240 lines
7.5 KiB
Go
240 lines
7.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 len(userIDs) == 0 {
|
|
return result
|
|
}
|
|
if s.repo.DB != nil {
|
|
var rows []model.UserBaseInfo
|
|
if err := s.repo.DB.WithContext(ctx).Where("id IN ?", userIDs).Find(&rows).Error; err == nil {
|
|
for _, row := range rows {
|
|
if row.ID <= 0 {
|
|
continue
|
|
}
|
|
result[row.ID] = userBaseInfoToProfile(row)
|
|
}
|
|
}
|
|
}
|
|
if s.userProfiles == nil {
|
|
return result
|
|
}
|
|
for _, userID := range userIDs {
|
|
current := result[userID]
|
|
if userProfileComplete(current) {
|
|
continue
|
|
}
|
|
profile, err := s.userProfiles.GetUserProfile(ctx, userID)
|
|
if err != nil {
|
|
if int64(current.ID) == 0 {
|
|
current.ID = integration.Int64Value(userID)
|
|
result[userID] = current
|
|
}
|
|
continue
|
|
}
|
|
result[userID] = mergeUserProfile(current, profile, userID)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func userBaseInfoToProfile(row model.UserBaseInfo) integration.UserProfile {
|
|
return integration.UserProfile{
|
|
ID: integration.Int64Value(row.ID),
|
|
Account: strings.TrimSpace(row.Account),
|
|
UserAvatar: strings.TrimSpace(row.UserAvatar),
|
|
UserNickname: strings.TrimSpace(row.UserNickname),
|
|
OriginSys: strings.TrimSpace(row.OriginSys),
|
|
CountryCode: strings.TrimSpace(row.CountryCode),
|
|
CountryName: strings.TrimSpace(row.CountryName),
|
|
}
|
|
}
|
|
|
|
func userProfileComplete(profile integration.UserProfile) bool {
|
|
return int64(profile.ID) > 0 &&
|
|
strings.TrimSpace(profile.Account) != "" &&
|
|
strings.TrimSpace(profile.UserAvatar) != "" &&
|
|
strings.TrimSpace(profile.UserNickname) != "" &&
|
|
strings.TrimSpace(profile.CountryCode) != ""
|
|
}
|
|
|
|
func mergeUserProfile(base integration.UserProfile, fallback integration.UserProfile, userID int64) integration.UserProfile {
|
|
if int64(base.ID) == 0 {
|
|
if int64(fallback.ID) > 0 {
|
|
base.ID = fallback.ID
|
|
} else {
|
|
base.ID = integration.Int64Value(userID)
|
|
}
|
|
}
|
|
if strings.TrimSpace(base.Account) == "" {
|
|
base.Account = strings.TrimSpace(fallback.Account)
|
|
} else {
|
|
base.Account = strings.TrimSpace(base.Account)
|
|
}
|
|
if strings.TrimSpace(base.UserAvatar) == "" {
|
|
base.UserAvatar = strings.TrimSpace(fallback.UserAvatar)
|
|
} else {
|
|
base.UserAvatar = strings.TrimSpace(base.UserAvatar)
|
|
}
|
|
if strings.TrimSpace(base.UserNickname) == "" {
|
|
base.UserNickname = strings.TrimSpace(fallback.UserNickname)
|
|
} else {
|
|
base.UserNickname = strings.TrimSpace(base.UserNickname)
|
|
}
|
|
if int64(base.CountryID) == 0 {
|
|
base.CountryID = fallback.CountryID
|
|
}
|
|
if strings.TrimSpace(base.CountryCode) == "" {
|
|
base.CountryCode = strings.TrimSpace(fallback.CountryCode)
|
|
} else {
|
|
base.CountryCode = strings.TrimSpace(base.CountryCode)
|
|
}
|
|
if strings.TrimSpace(base.CountryName) == "" {
|
|
base.CountryName = strings.TrimSpace(fallback.CountryName)
|
|
} else {
|
|
base.CountryName = strings.TrimSpace(base.CountryName)
|
|
}
|
|
if strings.TrimSpace(base.RegionCode) == "" {
|
|
base.RegionCode = strings.TrimSpace(fallback.RegionCode)
|
|
} else {
|
|
base.RegionCode = strings.TrimSpace(base.RegionCode)
|
|
}
|
|
if strings.TrimSpace(base.OriginSys) == "" {
|
|
base.OriginSys = strings.TrimSpace(fallback.OriginSys)
|
|
} else {
|
|
base.OriginSys = strings.TrimSpace(base.OriginSys)
|
|
}
|
|
if strings.TrimSpace(base.AccountStatus) == "" {
|
|
base.AccountStatus = strings.TrimSpace(fallback.AccountStatus)
|
|
} else {
|
|
base.AccountStatus = strings.TrimSpace(base.AccountStatus)
|
|
}
|
|
return base
|
|
}
|
|
|
|
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)
|
|
}
|