189 lines
5.9 KiB
Go
189 lines
5.9 KiB
Go
package voiceroomrocket
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"math"
|
|
"net/http"
|
|
"time"
|
|
|
|
"chatapp3-golang/internal/model"
|
|
"chatapp3-golang/internal/utils"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// GetStatus 查询房间当前火箭状态,懒初始化当天状态。
|
|
func (s *Service) GetStatus(ctx context.Context, user AuthUser, roomID int64) (*StatusResponse, error) {
|
|
if roomID <= 0 {
|
|
return nil, NewAppError(http.StatusBadRequest, "missing_room_id", "roomId is required")
|
|
}
|
|
snapshot, err := s.loadConfig(ctx, user.SysOrigin)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
local := resolveLocation(snapshot.Timezone)
|
|
now := time.Now().In(local)
|
|
status, err := s.loadOrInitStatus(ctx, nil, snapshot.SysOrigin, roomID, dayKey(now, local), now)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
levels, err := s.loadLevelConfigs(ctx, snapshot.SysOrigin, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
currentLevelConfig, err := s.loadLevelConfig(ctx, snapshot.SysOrigin, status.CurrentLevel)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rewards, err := s.loadRewardConfigs(ctx, snapshot.SysOrigin, status.CurrentLevel)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
percent, displayPercent := decimalPercent(status.CurrentEnergy, status.NeedEnergy)
|
|
shake := status.NeedEnergy > 0 &&
|
|
float64(status.CurrentEnergy)*100/float64(status.NeedEnergy) >= parsePercent(currentLevelConfig.ShakeThresholdPercent)
|
|
kings, _ := s.topKingRecords(ctx, snapshot.SysOrigin, roomID, status.DayKey, status.RoundNo, status.CurrentLevel, 3)
|
|
countdownSeconds := s.launchCountdownSeconds(ctx, status, time.Now())
|
|
return &StatusResponse{
|
|
RoomID: roomID,
|
|
DayKey: status.DayKey,
|
|
RoundNo: status.RoundNo,
|
|
DisplayRound: status.RoundNo > 1,
|
|
Status: status.Status,
|
|
CurrentLevel: status.CurrentLevel,
|
|
CurrentEnergy: status.CurrentEnergy,
|
|
NeedEnergy: status.NeedEnergy,
|
|
EnergyPercent: percent,
|
|
DisplayPercent: displayPercent,
|
|
LaunchCountdownSeconds: countdownSeconds,
|
|
Shake: shake,
|
|
PreviewRocketURL: currentLevelConfig.PreviewRocketURL,
|
|
RocketIconURL: currentLevelConfig.RocketIconURL,
|
|
RocketAnimationURL: currentLevelConfig.RocketAnimationURL,
|
|
Levels: buildLevelPayloads(levels),
|
|
RewardPreview: rewardPreviewPayload(rewards),
|
|
RocketKings: kings,
|
|
}, nil
|
|
}
|
|
|
|
func (s *Service) loadOrInitStatus(ctx context.Context, tx *gorm.DB, sysOrigin string, roomID int64, day string, now time.Time) (model.VoiceRoomRocketStatus, error) {
|
|
db := s.repo.DB.WithContext(ctx)
|
|
if tx != nil {
|
|
db = tx.WithContext(ctx)
|
|
}
|
|
var row model.VoiceRoomRocketStatus
|
|
err := db.Where("sys_origin = ? AND room_id = ? AND day_key = ?", sysOrigin, roomID, day).First(&row).Error
|
|
if err == nil {
|
|
return row, nil
|
|
}
|
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return model.VoiceRoomRocketStatus{}, err
|
|
}
|
|
levelConfig, err := s.loadLevelConfig(ctx, sysOrigin, 1)
|
|
if err != nil {
|
|
return model.VoiceRoomRocketStatus{}, err
|
|
}
|
|
id, err := utils.NextID()
|
|
if err != nil {
|
|
return model.VoiceRoomRocketStatus{}, err
|
|
}
|
|
row = model.VoiceRoomRocketStatus{
|
|
ID: id,
|
|
SysOrigin: sysOrigin,
|
|
RoomID: roomID,
|
|
DayKey: day,
|
|
RoundNo: 1,
|
|
CurrentLevel: 1,
|
|
CurrentEnergy: 0,
|
|
NeedEnergy: levelConfig.NeedEnergy,
|
|
Status: statusCharging,
|
|
LevelStartTime: now,
|
|
CreateTime: now,
|
|
UpdateTime: now,
|
|
}
|
|
if err := db.Create(&row).Error; err != nil {
|
|
if isDuplicateKeyErr(err) {
|
|
var existing model.VoiceRoomRocketStatus
|
|
if loadErr := db.Where("sys_origin = ? AND room_id = ? AND day_key = ?", sysOrigin, roomID, day).First(&existing).Error; loadErr != nil {
|
|
return model.VoiceRoomRocketStatus{}, loadErr
|
|
}
|
|
return existing, nil
|
|
}
|
|
return model.VoiceRoomRocketStatus{}, err
|
|
}
|
|
if err := s.ensureLevelRound(ctx, tx, sysOrigin, roomID, day, 1, 1, levelConfig.NeedEnergy, now); err != nil {
|
|
return model.VoiceRoomRocketStatus{}, err
|
|
}
|
|
return row, nil
|
|
}
|
|
|
|
func (s *Service) launchCountdownSeconds(ctx context.Context, status model.VoiceRoomRocketStatus, now time.Time) int {
|
|
if status.Status != statusLaunching {
|
|
return 0
|
|
}
|
|
var round model.VoiceRoomRocketLevelRound
|
|
err := s.repo.DB.WithContext(ctx).
|
|
Where(
|
|
"sys_origin = ? AND room_id = ? AND day_key = ? AND round_no = ? AND level = ?",
|
|
status.SysOrigin, status.RoomID, status.DayKey, status.RoundNo, status.CurrentLevel,
|
|
).
|
|
First(&round).Error
|
|
if err != nil || round.ReachTime == nil || round.ReachTime.IsZero() {
|
|
return defaultLaunchDelaySeconds
|
|
}
|
|
due := round.ReachTime.Add(time.Duration(defaultLaunchDelaySeconds) * time.Second)
|
|
remaining := due.Sub(now).Seconds()
|
|
if remaining <= 0 {
|
|
return 0
|
|
}
|
|
return int(math.Ceil(remaining))
|
|
}
|
|
|
|
func (s *Service) ensureLevelRound(ctx context.Context, tx *gorm.DB, sysOrigin string, roomID int64, day string, roundNo int, level int, needEnergy int64, now time.Time) error {
|
|
db := s.repo.DB.WithContext(ctx)
|
|
if tx != nil {
|
|
db = tx.WithContext(ctx)
|
|
}
|
|
var row model.VoiceRoomRocketLevelRound
|
|
err := db.Where(
|
|
"sys_origin = ? AND room_id = ? AND day_key = ? AND round_no = ? AND level = ?",
|
|
sysOrigin, roomID, day, roundNo, level,
|
|
).First(&row).Error
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return err
|
|
}
|
|
id, err := utils.NextID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
row = model.VoiceRoomRocketLevelRound{
|
|
ID: id,
|
|
SysOrigin: sysOrigin,
|
|
RoomID: roomID,
|
|
DayKey: day,
|
|
RoundNo: roundNo,
|
|
Level: level,
|
|
NeedEnergy: needEnergy,
|
|
FinalEnergy: 0,
|
|
Status: levelRoundStatusCharging,
|
|
CreateTime: now,
|
|
UpdateTime: now,
|
|
}
|
|
if err := db.Create(&row).Error; err != nil && !isDuplicateKeyErr(err) {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func buildLevelPayloads(rows []model.VoiceRoomRocketLevelConfig) []LevelConfigPayload {
|
|
result := make([]LevelConfigPayload, 0, len(rows))
|
|
for _, row := range rows {
|
|
result = append(result, levelPayload(row))
|
|
}
|
|
return result
|
|
}
|