2026-05-14 13:46:53 +08:00

143 lines
4.5 KiB
Go

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)
}