143 lines
3.6 KiB
Go
143 lines
3.6 KiB
Go
package voiceroomrocket
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"chatapp3-golang/internal/model"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
// ProcessDueLaunches 扫描进度满 10 秒后的火箭发射任务。
|
|
func (s *Service) ProcessDueLaunches(ctx context.Context, now time.Time, limit int64) (int, error) {
|
|
if limit <= 0 {
|
|
limit = int64(defaultWorkerBatchSize)
|
|
}
|
|
processed := 0
|
|
if s.repo.Redis != nil {
|
|
statusIDs, err := s.repo.Redis.ZRangeByScore(ctx, s.launchDueZSetKey(), &redis.ZRangeBy{
|
|
Min: "-inf",
|
|
Max: strconv.FormatInt(now.Unix(), 10),
|
|
Count: limit,
|
|
}).Result()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
for _, rawID := range statusIDs {
|
|
statusID, parseErr := strconv.ParseInt(strings.TrimSpace(rawID), 10, 64)
|
|
if parseErr != nil || statusID <= 0 {
|
|
_ = s.repo.Redis.ZRem(context.Background(), s.launchDueZSetKey(), rawID).Err()
|
|
continue
|
|
}
|
|
done, err := s.processDueLaunchStatus(ctx, statusID, now)
|
|
if err != nil {
|
|
return processed, err
|
|
}
|
|
if done {
|
|
if err := s.repo.Redis.ZRem(context.Background(), s.launchDueZSetKey(), rawID).Err(); err != nil {
|
|
return processed, err
|
|
}
|
|
processed++
|
|
}
|
|
}
|
|
}
|
|
backfilled, err := s.processStaleLaunchingStatuses(ctx, now, limit)
|
|
if err != nil {
|
|
return processed, err
|
|
}
|
|
return processed + backfilled, nil
|
|
}
|
|
|
|
func (s *Service) processStaleLaunchingStatuses(ctx context.Context, now time.Time, limit int64) (int, error) {
|
|
if limit <= 0 {
|
|
limit = int64(defaultWorkerBatchSize)
|
|
}
|
|
var rows []model.VoiceRoomRocketStatus
|
|
if err := s.repo.DB.WithContext(ctx).
|
|
Where("status = ?", statusLaunching).
|
|
Order("update_time ASC").
|
|
Limit(int(limit)).
|
|
Find(&rows).Error; err != nil {
|
|
return 0, err
|
|
}
|
|
processed := 0
|
|
for _, row := range rows {
|
|
done, err := s.processDueLaunchStatus(ctx, row.ID, now)
|
|
if err != nil {
|
|
return processed, err
|
|
}
|
|
if done {
|
|
processed++
|
|
}
|
|
}
|
|
return processed, nil
|
|
}
|
|
|
|
func (s *Service) processDueLaunchStatus(ctx context.Context, statusID int64, now time.Time) (bool, error) {
|
|
var launch model.VoiceRoomRocketLaunch
|
|
done := false
|
|
var launched bool
|
|
err := s.repo.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
var statusRow model.VoiceRoomRocketStatus
|
|
err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
|
|
Where("id = ?", statusID).
|
|
First(&statusRow).Error
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
done = true
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if statusRow.Status != statusLaunching || statusRow.CurrentEnergy < statusRow.NeedEnergy {
|
|
done = true
|
|
return nil
|
|
}
|
|
var round model.VoiceRoomRocketLevelRound
|
|
err = tx.Clauses(clause.Locking{Strength: "UPDATE"}).
|
|
Where(
|
|
"sys_origin = ? AND room_id = ? AND day_key = ? AND round_no = ? AND level = ?",
|
|
statusRow.SysOrigin, statusRow.RoomID, statusRow.DayKey, statusRow.RoundNo, statusRow.CurrentLevel,
|
|
).
|
|
First(&round).Error
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if round.ReachTime == nil || round.IgniteUserID == nil || *round.IgniteUserID <= 0 {
|
|
return nil
|
|
}
|
|
due := round.ReachTime.Add(time.Duration(defaultLaunchDelaySeconds) * time.Second)
|
|
if now.Before(due) {
|
|
return nil
|
|
}
|
|
result, err := s.launchLocked(ctx, tx, statusRow, *round.IgniteUserID, now)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
launch = result.Launch
|
|
launched = result.Launched
|
|
done = true
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if launched {
|
|
_ = s.notifyLaunch(context.Background(), launch)
|
|
}
|
|
return done, nil
|
|
}
|
|
|
|
func (s *Service) launchDueZSetKey() string {
|
|
return "voice_room:rocket:launch_due"
|
|
}
|