123 lines
4.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}