package service import ( "context" "sort" roomv1 "hyapp.local/api/proto/room/v1" "hyapp/pkg/appcode" "hyapp/pkg/roomid" "hyapp/pkg/xerr" "hyapp/services/room-service/internal/room/state" ) const ( // defaultRoomBannedUsersPageSize 是房间黑名单面板默认页大小。 defaultRoomBannedUsersPageSize = 20 // maxRoomBannedUsersPageSize 限制单次查询量,避免管理面板一次拉完整治理状态。 maxRoomBannedUsersPageSize = 100 ) // ListRoomBannedUsers 分页读取房间当前仍有效的黑名单。 // 黑名单状态属于 Room Cell,用户头像、昵称、国家等展示资料由 gateway 批量查询 user-service。 func (s *Service) ListRoomBannedUsers(ctx context.Context, req *roomv1.ListRoomBannedUsersRequest) (*roomv1.ListRoomBannedUsersResponse, error) { ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) roomID := req.GetRoomId() viewerUserID := req.GetViewerUserId() if !roomid.ValidStringID(roomID) { return nil, xerr.New(xerr.InvalidArgument, "room_id is invalid") } if viewerUserID <= 0 { return nil, xerr.New(xerr.InvalidArgument, "viewer_user_id is required") } snapshot, err := s.currentSnapshot(ctx, roomID) if err != nil { return nil, err } if snapshot == nil || snapshot.GetRoomId() == "" { return nil, xerr.New(xerr.NotFound, "room not found") } if snapshot.GetStatus() != state.RoomStatusActive { return nil, xerr.New(xerr.RoomClosed, "room closed") } current := state.FromProto(snapshot) if err := requireManagerPresent(current, viewerUserID); err != nil { // 黑名单是房间治理数据,只允许当前仍在房间内的 owner/admin 查看。 return nil, err } nowMS := s.clock.Now().UnixMilli() items := activeBannedUsersFromState(current, nowMS) page, pageSize := normalizeRoomBannedUsersPage(req.GetPage(), req.GetPageSize()) total := int64(len(items)) start := (page - 1) * pageSize if start >= len(items) { items = nil } else { end := start + pageSize if end > len(items) { end = len(items) } items = items[start:end] } return &roomv1.ListRoomBannedUsersResponse{ Items: items, Total: total, Page: int32(page), PageSize: int32(pageSize), ServerTimeMs: nowMS, }, nil } func activeBannedUsersFromState(current *state.RoomState, nowMS int64) []*roomv1.RoomBannedUser { if current == nil || len(current.BanUsers) == 0 { return nil } items := make([]*roomv1.RoomBannedUser, 0, len(current.BanUsers)) for _, ban := range current.BanUsers { if !ban.ActiveAt(nowMS) { continue } remainingMS := int64(0) if ban.ExpiresAtMS > 0 { remainingMS = ban.ExpiresAtMS - nowMS } items = append(items, &roomv1.RoomBannedUser{ UserId: ban.UserID, ActorUserId: ban.ActorUserID, CreatedAtMs: ban.CreatedAtMS, UnbanAtMs: ban.ExpiresAtMS, RemainingMs: remainingMS, Permanent: ban.ExpiresAtMS == 0, }) } sort.Slice(items, func(i, j int) bool { if items[i].GetCreatedAtMs() != items[j].GetCreatedAtMs() { return items[i].GetCreatedAtMs() > items[j].GetCreatedAtMs() } return items[i].GetUserId() < items[j].GetUserId() }) return items } func normalizeRoomBannedUsersPage(rawPage int32, rawPageSize int32) (int, int) { page := int(rawPage) if page <= 0 { page = 1 } pageSize := int(rawPageSize) if pageSize <= 0 { pageSize = defaultRoomBannedUsersPageSize } if pageSize > maxRoomBannedUsersPageSize { pageSize = maxRoomBannedUsersPageSize } return page, pageSize }