123 lines
4.1 KiB
Go
123 lines
4.1 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"strings"
|
||
|
||
roomv1 "hyapp.local/api/proto/room/v1"
|
||
"hyapp/pkg/appcode"
|
||
"hyapp/pkg/roomid"
|
||
"hyapp/pkg/xerr"
|
||
)
|
||
|
||
const (
|
||
// defaultRoomOnlineUsersPageSize 是在线用户弹窗的默认页大小,避免客户端不传 page_size 时一次拉全量。
|
||
defaultRoomOnlineUsersPageSize = 50
|
||
// maxRoomOnlineUsersPageSize 是在线用户列表单页硬上限,保护 presence 读模型查询。
|
||
maxRoomOnlineUsersPageSize = 100
|
||
)
|
||
|
||
// ListRoomOnlineUsers 分页读取房间在线用户列表。
|
||
// 该接口用于右上角在线人数弹窗,不能让客户端用完整 RoomSnapshot 做分页。
|
||
func (s *Service) ListRoomOnlineUsers(ctx context.Context, req *roomv1.ListRoomOnlineUsersRequest) (*roomv1.ListRoomOnlineUsersResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
|
||
roomID := req.GetRoomId()
|
||
viewerUserID := req.GetViewerUserId()
|
||
if !roomid.ValidStringID(roomID) {
|
||
// 在线用户查询必须绑定真实房间 ID,不能让空 room_id 触发宽查询。
|
||
return nil, xerr.New(xerr.InvalidArgument, "room_id is invalid")
|
||
}
|
||
if viewerUserID <= 0 {
|
||
// viewer_user_id 来自 gateway 鉴权态,用于限制只有房内用户可看在线列表。
|
||
return nil, xerr.New(xerr.InvalidArgument, "viewer_user_id is required")
|
||
}
|
||
|
||
// 先用用户当前房间读模型做轻量权限判断,避免为了弹窗分页读取完整 RoomSnapshot。
|
||
presence, exists, err := s.repository.GetCurrentRoomPresence(ctx, viewerUserID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if !exists || presence.RoomID != roomID || presence.Status != roomPresenceStatusActive {
|
||
// 不在该房间的用户不能借分页接口枚举房间内成员。
|
||
return nil, xerr.New(xerr.PermissionDenied, "viewer is not in room")
|
||
}
|
||
|
||
page := int(req.GetPage())
|
||
if page <= 0 {
|
||
// page 从 1 开始,0 或负数按第一页处理,保持客户端容错。
|
||
page = 1
|
||
}
|
||
pageSize := int(req.GetPageSize())
|
||
if pageSize <= 0 {
|
||
// 缺省页大小走服务端默认,避免前端每个入口复制同一数值。
|
||
pageSize = defaultRoomOnlineUsersPageSize
|
||
}
|
||
if pageSize > maxRoomOnlineUsersPageSize {
|
||
// 单页上限在 service 层压实,repository 不接受无限制分页。
|
||
pageSize = maxRoomOnlineUsersPageSize
|
||
}
|
||
sortMode := strings.TrimSpace(req.GetSort())
|
||
if sortMode != "" && sortMode != "online" && sortMode != "gift_value" {
|
||
return nil, xerr.New(xerr.InvalidArgument, "sort is invalid")
|
||
}
|
||
|
||
snapshot, err := s.currentSnapshot(ctx, roomID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if snapshot == nil {
|
||
return nil, xerr.New(xerr.NotFound, "room not found")
|
||
}
|
||
adminUsers := make(map[int64]bool, len(snapshot.GetAdminUserIds()))
|
||
for _, userID := range snapshot.GetAdminUserIds() {
|
||
if userID > 0 && userID != snapshot.GetOwnerUserId() {
|
||
adminUsers[userID] = true
|
||
}
|
||
}
|
||
|
||
result, err := s.repository.ListRoomOnlineUsers(ctx, RoomOnlineUserQuery{
|
||
AppCode: appcode.FromContext(ctx),
|
||
RoomID: roomID,
|
||
Page: page,
|
||
PageSize: pageSize,
|
||
Sort: sortMode,
|
||
})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
users := make([]*roomv1.RoomUser, 0, len(result.Users))
|
||
items := make([]*roomv1.RoomOnlineUser, 0, len(result.Users))
|
||
for _, user := range result.Users {
|
||
// 在线列表只返回轻量房间用户态,完整用户资料仍由 user-service 或 gateway 聚合。
|
||
users = append(users, &roomv1.RoomUser{
|
||
UserId: user.UserID,
|
||
Role: user.Role,
|
||
JoinedAtMs: user.JoinedAtMS,
|
||
LastSeenAtMs: user.LastSeenAtMS,
|
||
})
|
||
roomRole := "normal"
|
||
if adminUsers[user.UserID] {
|
||
roomRole = "admin"
|
||
}
|
||
items = append(items, &roomv1.RoomOnlineUser{
|
||
UserId: user.UserID,
|
||
Role: user.Role,
|
||
RoomRole: roomRole,
|
||
GiftValue: user.GiftValue,
|
||
JoinedAtMs: user.JoinedAtMS,
|
||
LastSeenAtMs: user.LastSeenAtMS,
|
||
IsOwner: user.UserID == snapshot.GetOwnerUserId(),
|
||
})
|
||
}
|
||
|
||
return &roomv1.ListRoomOnlineUsersResponse{
|
||
Users: users,
|
||
Items: items,
|
||
Total: result.Total,
|
||
Page: int32(result.Page),
|
||
PageSize: int32(result.PageSize),
|
||
ServerTimeMs: s.clock.Now().UnixMilli(),
|
||
}, nil
|
||
}
|