package voiceroomrocket import ( "context" "net/http" "strconv" "strings" "time" "chatapp3-golang/internal/integration" "chatapp3-golang/internal/model" ) // GetKingRanking 查询火箭王榜单。 func (s *Service) GetKingRanking(ctx context.Context, user AuthUser, roomID int64, level int, roundNo int, cursor int, limit int) (*KingResponse, error) { if roomID <= 0 { return nil, NewAppError(http.StatusBadRequest, "missing_room_id", "roomId is required") } if level <= 0 || level > defaultMaxLevel { return nil, NewAppError(http.StatusBadRequest, "invalid_level", "level must be between 1 and 6") } cursor, limit = normalizePage(cursor, limit) snapshot, err := s.loadConfig(ctx, user.SysOrigin) if err != nil { return nil, err } local := resolveLocation(snapshot.Timezone) now := time.Now().In(local) statusRow, err := s.loadOrInitStatus(ctx, nil, snapshot.SysOrigin, roomID, dayKey(now, local), now) if err != nil { return nil, err } if roundNo <= 0 { roundNo = statusRow.RoundNo } sourceRoundNo := roundNo records, err := s.rankRecords(ctx, snapshot.SysOrigin, roomID, statusRow.DayKey, sourceRoundNo, level, cursor, limit) if err != nil { return nil, err } if len(records) == 0 && roundNo == statusRow.RoundNo && level > statusRow.CurrentLevel && roundNo > 1 { sourceRoundNo = roundNo - 1 records, err = s.rankRecords(ctx, snapshot.SysOrigin, roomID, statusRow.DayKey, sourceRoundNo, level, cursor, limit) if err != nil { return nil, err } } resp := &KingResponse{ RoomID: roomID, DayKey: statusRow.DayKey, RoundNo: roundNo, SourceRoundNo: sourceRoundNo, Level: level, Records: records, } if len(records) > 3 { resp.Podium = append([]KingRecord(nil), records[:3]...) } else { resp.Podium = append([]KingRecord(nil), records...) } if len(records) == 0 { resp.EmptyReason = "未达到火箭等级,没有火箭王" } return resp, nil } func (s *Service) topKingRecords(ctx context.Context, sysOrigin string, roomID int64, day string, roundNo int, level int, limit int) ([]KingRecord, error) { if limit <= 0 { limit = 3 } return s.rankRecords(ctx, sysOrigin, roomID, day, roundNo, level, 1, limit) } func (s *Service) rankRecords(ctx context.Context, sysOrigin string, roomID int64, day string, roundNo int, level int, cursor int, limit int) ([]KingRecord, error) { cursor, limit = normalizePage(cursor, limit) var rows []model.VoiceRoomRocketContribution if err := s.repo.DB.WithContext(ctx). Where("sys_origin = ? AND room_id = ? AND day_key = ? AND round_no = ? AND level = ?", sysOrigin, roomID, day, roundNo, level). Order("score_energy DESC, last_contribute_time ASC, user_id ASC"). Offset((cursor - 1) * limit). Limit(limit). Find(&rows).Error; err != nil { return nil, err } profiles := s.mapUserProfiles(ctx, contributionUserIDs(rows)) records := make([]KingRecord, 0, len(rows)) baseRank := (cursor-1)*limit + 1 for index, row := range rows { profile := profiles[row.UserID] record := userProfileToKingRecord(row.UserID, baseRank+index, row.ScoreEnergy, profile) if strings.TrimSpace(record.Account) == "" { record.Account = strconv.FormatInt(row.UserID, 10) } if strings.TrimSpace(record.UserNickname) == "" { record.UserNickname = record.Account } records = append(records, record) } return records, nil } func contributionUserIDs(rows []model.VoiceRoomRocketContribution) []int64 { result := make([]int64, 0, len(rows)) seen := map[int64]struct{}{} for _, row := range rows { if row.UserID <= 0 { continue } if _, ok := seen[row.UserID]; ok { continue } seen[row.UserID] = struct{}{} result = append(result, row.UserID) } return result } func (s *Service) mapUserProfiles(ctx context.Context, userIDs []int64) map[int64]integration.UserProfile { result := make(map[int64]integration.UserProfile, len(userIDs)) if s.userProfiles == nil { return result } for _, userID := range userIDs { profile, err := s.userProfiles.GetUserProfile(ctx, userID) if err == nil { result[userID] = profile } } return result } func rankRedisKey(sysOrigin string, roomID int64, day string, roundNo int, level int) string { return "voice_room:rocket:rank:" + strings.ToUpper(strings.TrimSpace(sysOrigin)) + ":" + strconv.FormatInt(roomID, 10) + ":" + day + ":" + strconv.Itoa(roundNo) + ":" + strconv.Itoa(level) } func roomLockKey(sysOrigin string, roomID int64) string { return "voice_room:rocket:lock:" + strings.ToUpper(strings.TrimSpace(sysOrigin)) + ":" + strconv.FormatInt(roomID, 10) }