火箭结算不正确问题
This commit is contained in:
parent
3ec7cc6a62
commit
adcc722284
@ -399,6 +399,9 @@ func TestInRoomRewardWorkerSnapshotsSelectsAndGrants(t *testing.T) {
|
||||
if err := rdb.SAdd(ctx, onlineRoomKey("LIKEI", roomID), userID).Err(); err != nil {
|
||||
t.Fatalf("sadd online: %v", err)
|
||||
}
|
||||
if err := rdb.Set(ctx, userRoomKey("LIKEI", userID), strconv.FormatInt(roomID, 10), time.Minute).Err(); err != nil {
|
||||
t.Fatalf("set user room: %v", err)
|
||||
}
|
||||
if err := rdb.Set(ctx, userSeenKey("LIKEI", userID), strconv.FormatInt(time.Now().UnixMilli(), 10), time.Minute).Err(); err != nil {
|
||||
t.Fatalf("set seen: %v", err)
|
||||
}
|
||||
@ -471,6 +474,92 @@ func TestInRoomRewardWorkerSnapshotsSelectsAndGrants(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInRoomRewardWorkerSkipsUsersNoLongerInRoom(t *testing.T) {
|
||||
gateway := newFakeRocketGateway()
|
||||
service, db, rdb, mr := newTestServiceWithRedis(t, gateway)
|
||||
defer mr.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
now := time.Now()
|
||||
seedRocketConfig(t, db)
|
||||
seedLevelConfigs(t, db, []int64{100, 200, 300, 400, 500, 600})
|
||||
seedRewardConfig(t, db, 1, rewardSceneTop1, rewardTypeGold, 10)
|
||||
seedRewardConfig(t, db, 1, rewardSceneIgnite, rewardTypeGold, 5)
|
||||
seedRewardConfigWithCopies(t, db, 1, rewardSceneInRoom, rewardTypeGold, 1, 4)
|
||||
|
||||
roomID := int64(9001)
|
||||
for _, userID := range []int64{1001, 1002} {
|
||||
if err := rdb.SAdd(ctx, onlineRoomKey("LIKEI", roomID), userID).Err(); err != nil {
|
||||
t.Fatalf("sadd active online: %v", err)
|
||||
}
|
||||
if err := rdb.Set(ctx, userRoomKey("LIKEI", userID), strconv.FormatInt(roomID, 10), time.Minute).Err(); err != nil {
|
||||
t.Fatalf("set active room: %v", err)
|
||||
}
|
||||
if err := rdb.Set(ctx, userSeenKey("LIKEI", userID), strconv.FormatInt(now.UnixMilli(), 10), time.Minute).Err(); err != nil {
|
||||
t.Fatalf("set active seen: %v", err)
|
||||
}
|
||||
}
|
||||
staleUsers := []int64{1003, 1004}
|
||||
for _, userID := range staleUsers {
|
||||
if err := rdb.SAdd(ctx, onlineRoomKey("LIKEI", roomID), userID).Err(); err != nil {
|
||||
t.Fatalf("sadd stale online: %v", err)
|
||||
}
|
||||
if err := rdb.Set(ctx, userSeenKey("LIKEI", userID), strconv.FormatInt(now.UnixMilli(), 10), time.Minute).Err(); err != nil {
|
||||
t.Fatalf("set stale seen: %v", err)
|
||||
}
|
||||
}
|
||||
if err := rdb.Set(ctx, userRoomKey("LIKEI", staleUsers[1]), strconv.FormatInt(roomID+1, 10), time.Minute).Err(); err != nil {
|
||||
t.Fatalf("set stale room: %v", err)
|
||||
}
|
||||
|
||||
payload := `{
|
||||
"trackId":"track-launch-in-room-stale",
|
||||
"sysOrigin":"LIKEI",
|
||||
"sendUserId":"1001",
|
||||
"roomId":"9001",
|
||||
"quantity":1,
|
||||
"giftConfig":{"id":"11","giftCandy":100,"type":"GOLD","giftTab":"NORMAL"},
|
||||
"createTime":` + strconvFormatMillis(now) + `
|
||||
}`
|
||||
resp, err := service.ProcessGiftPayload(ctx, payload)
|
||||
if err != nil {
|
||||
t.Fatalf("ProcessGiftPayload() error = %v", err)
|
||||
}
|
||||
if !resp.Processed {
|
||||
t.Fatalf("response = %+v, want processed", resp)
|
||||
}
|
||||
if _, err := service.ProcessDueLaunches(ctx, now.Add(11*time.Second), 10); err != nil {
|
||||
t.Fatalf("ProcessDueLaunches() error = %v", err)
|
||||
}
|
||||
if _, err := service.ProcessDueInRoomRewards(ctx, now.Add(30*time.Second), 10); err != nil {
|
||||
t.Fatalf("ProcessDueInRoomRewards() error = %v", err)
|
||||
}
|
||||
|
||||
var snapshotCount int64
|
||||
if err := db.Model(&model.VoiceRoomRocketAudienceSnapshot{}).Count(&snapshotCount).Error; err != nil {
|
||||
t.Fatalf("count snapshot: %v", err)
|
||||
}
|
||||
if snapshotCount != 2 {
|
||||
t.Fatalf("snapshot count = %d, want 2 active users only", snapshotCount)
|
||||
}
|
||||
for _, userID := range staleUsers {
|
||||
var userSnapshotCount int64
|
||||
if err := db.Model(&model.VoiceRoomRocketAudienceSnapshot{}).Where("user_id = ?", userID).Count(&userSnapshotCount).Error; err != nil {
|
||||
t.Fatalf("count stale snapshot: %v", err)
|
||||
}
|
||||
if userSnapshotCount != 0 {
|
||||
t.Fatalf("stale user %d snapshot count = %d, want 0", userID, userSnapshotCount)
|
||||
}
|
||||
var rewardCount int64
|
||||
if err := db.Model(&model.VoiceRoomRocketRewardRecord{}).Where("user_id = ? AND reward_scene = ?", userID, rewardSceneInRoom).Count(&rewardCount).Error; err != nil {
|
||||
t.Fatalf("count stale reward: %v", err)
|
||||
}
|
||||
if rewardCount != 0 {
|
||||
t.Fatalf("stale user %d in-room reward count = %d, want 0", userID, rewardCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessPendingRewardsGrantsBadgeThroughBadgeGateway(t *testing.T) {
|
||||
gateway := newFakeRocketGateway()
|
||||
service, db := newTestService(t)
|
||||
|
||||
@ -33,9 +33,14 @@ func (s *Service) listOnlineRoomUsers(ctx context.Context, sysOrigin string, roo
|
||||
continue
|
||||
}
|
||||
seen[userID] = struct{}{}
|
||||
seenAt, active := s.loadActiveRoomSeenTime(ctx, sysOrigin, roomID, userID, now)
|
||||
if !active {
|
||||
_ = s.repo.Redis.SRem(ctx, onlineRoomKey(sysOrigin, roomID), userID).Err()
|
||||
continue
|
||||
}
|
||||
users = append(users, roomAudienceUser{
|
||||
UserID: userID,
|
||||
LastSeenTime: s.loadUserSeenTime(ctx, sysOrigin, userID, now),
|
||||
LastSeenTime: seenAt,
|
||||
})
|
||||
}
|
||||
sort.Slice(users, func(i, j int) bool {
|
||||
@ -44,6 +49,43 @@ func (s *Service) listOnlineRoomUsers(ctx context.Context, sysOrigin string, roo
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (s *Service) loadActiveRoomSeenTime(ctx context.Context, sysOrigin string, roomID, userID int64, now time.Time) (time.Time, bool) {
|
||||
if s.repo.Redis == nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
rawRoomID, err := s.repo.Redis.Get(ctx, userRoomKey(sysOrigin, userID)).Result()
|
||||
if err != nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
currentRoomID, err := strconv.ParseInt(strings.TrimSpace(rawRoomID), 10, 64)
|
||||
if err != nil || currentRoomID != roomID {
|
||||
return time.Time{}, false
|
||||
}
|
||||
seenAt, ok := s.loadUserSeenTimeStrict(ctx, sysOrigin, userID, now)
|
||||
if !ok {
|
||||
return time.Time{}, false
|
||||
}
|
||||
if ttl := s.roomPresenceTTL(); ttl > 0 && now.Sub(seenAt) > ttl {
|
||||
return time.Time{}, false
|
||||
}
|
||||
return seenAt, true
|
||||
}
|
||||
|
||||
func (s *Service) loadUserSeenTimeStrict(ctx context.Context, sysOrigin string, userID int64, fallback time.Time) (time.Time, bool) {
|
||||
if s.repo.Redis == nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
raw, err := s.repo.Redis.Get(ctx, userSeenKey(sysOrigin, userID)).Result()
|
||||
if err != nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
millis, err := strconv.ParseInt(strings.TrimSpace(raw), 10, 64)
|
||||
if err != nil || millis <= 0 {
|
||||
return time.Time{}, false
|
||||
}
|
||||
return resolveMillisTime(millis, fallback), true
|
||||
}
|
||||
|
||||
func (s *Service) loadUserSeenTime(ctx context.Context, sysOrigin string, userID int64, fallback time.Time) time.Time {
|
||||
if s.repo.Redis == nil {
|
||||
return fallback
|
||||
@ -59,10 +101,22 @@ func (s *Service) loadUserSeenTime(ctx context.Context, sysOrigin string, userID
|
||||
return resolveMillisTime(millis, fallback)
|
||||
}
|
||||
|
||||
func (s *Service) roomPresenceTTL() time.Duration {
|
||||
seconds := s.cfg.VoiceRoomRedPacket.PresenceTTLSeconds
|
||||
if seconds <= 0 {
|
||||
seconds = defaultPresenceTTLSeconds
|
||||
}
|
||||
return time.Duration(seconds) * time.Second
|
||||
}
|
||||
|
||||
func onlineRoomKey(sysOrigin string, roomID int64) string {
|
||||
return fmt.Sprintf("voice_room:online:%s:%d", strings.ToUpper(strings.TrimSpace(sysOrigin)), roomID)
|
||||
}
|
||||
|
||||
func userRoomKey(sysOrigin string, userID int64) string {
|
||||
return fmt.Sprintf("voice_room:user_room:%s:%d", strings.ToUpper(strings.TrimSpace(sysOrigin)), userID)
|
||||
}
|
||||
|
||||
func userSeenKey(sysOrigin string, userID int64) string {
|
||||
return fmt.Sprintf("voice_room:user_seen:%s:%d", strings.ToUpper(strings.TrimSpace(sysOrigin)), userID)
|
||||
}
|
||||
|
||||
@ -32,6 +32,7 @@ const (
|
||||
defaultWorkerBatchSize = 100
|
||||
defaultWorkerInterval = time.Second
|
||||
defaultRoomLockTTL = 10 * time.Second
|
||||
defaultPresenceTTLSeconds = 60
|
||||
|
||||
statusCharging = "CHARGING"
|
||||
statusLaunching = "LAUNCHING"
|
||||
|
||||
@ -429,6 +429,7 @@ func seedPresence(ctx context.Context, rdb *redis.Client, roomID int64, users []
|
||||
key := fmt.Sprintf("voice_room:online:LIKEI:%d", roomID)
|
||||
for _, userID := range users {
|
||||
must(rdb.SAdd(ctx, key, userID).Err(), "seed online room")
|
||||
must(rdb.Set(ctx, fmt.Sprintf("voice_room:user_room:LIKEI:%d", userID), strconv.FormatInt(roomID, 10), time.Minute).Err(), "seed user room")
|
||||
must(rdb.Set(ctx, fmt.Sprintf("voice_room:user_seen:LIKEI:%d", userID), strconv.FormatInt(time.Now().UnixMilli(), 10), time.Minute).Err(), "seed user seen")
|
||||
}
|
||||
must(rdb.Expire(ctx, key, time.Minute).Err(), "expire online room")
|
||||
|
||||
@ -109,6 +109,9 @@ type verifySummary struct {
|
||||
Top1PopupBeforeAck int `json:"top1PopupBeforeAck"`
|
||||
Top1PopupAfterAck int `json:"top1PopupAfterAck"`
|
||||
IgnitePopupCount int `json:"ignitePopupCount"`
|
||||
LeftUserID int64 `json:"leftUserId,string"`
|
||||
LeftUserSnapshotCount int64 `json:"leftUserSnapshotCount"`
|
||||
LeftUserRewardCount int64 `json:"leftUserRewardCount"`
|
||||
RewardRecordCount int64 `json:"rewardRecordCount"`
|
||||
SnapshotCount int64 `json:"snapshotCount"`
|
||||
SelectedAudienceCount int64 `json:"selectedAudienceCount"`
|
||||
@ -162,6 +165,11 @@ func main() {
|
||||
cleanup(ctx, db, rdb, room.RoomID, day)
|
||||
seedConfig(ctx, service, db)
|
||||
seedPresence(ctx, rdb, room.RoomID, append([]int64{room.UserID, igniteUserID}, otherUsers[1:]...))
|
||||
leftUserID := otherUsers[2]
|
||||
must(rdb.Del(ctx,
|
||||
fmt.Sprintf("voice_room:user_room:LIKEI:%d", leftUserID),
|
||||
fmt.Sprintf("voice_room:user_seen:LIKEI:%d", leftUserID),
|
||||
).Err(), "simulate left user presence")
|
||||
|
||||
initial, err := service.GetStatus(ctx, user, room.RoomID)
|
||||
must(err, "initial status")
|
||||
@ -179,7 +187,17 @@ func main() {
|
||||
secondPayload := mqEnvelope(giftPayload("LOCAL_VERIFY_TRACK_2", igniteUserID, room.RoomID, 80, now.Add(time.Second)))
|
||||
second, err := service.ProcessGiftPayload(ctx, secondPayload)
|
||||
must(err, "process second gift")
|
||||
assert(second.Processed && second.AcceptedEnergy == 40 && second.Launched && second.LaunchNo != "", "second gift result mismatch")
|
||||
assert(second.Processed && second.AcceptedEnergy == 40 && !second.Launched, "second gift result mismatch")
|
||||
|
||||
launched, err := service.ProcessDueLaunches(ctx, now.Add(11*time.Second), 10)
|
||||
must(err, "process due launches")
|
||||
assert(launched == 1, "due launch count mismatch")
|
||||
var launch model.VoiceRoomRocketLaunch
|
||||
must(db.WithContext(ctx).
|
||||
Where("room_id = ? AND day_key = ?", room.RoomID, day).
|
||||
Order("create_time DESC").
|
||||
First(&launch).Error, "load launch")
|
||||
assert(strings.TrimSpace(launch.LaunchNo) != "", "launch no missing")
|
||||
|
||||
finalStatus, err := service.GetStatus(ctx, user, room.RoomID)
|
||||
must(err, "final status")
|
||||
@ -213,7 +231,7 @@ func main() {
|
||||
must(err, "top1 reward records")
|
||||
assert(len(rewardRecords.Records) >= 1, "top1 reward record count mismatch")
|
||||
|
||||
dueProcessed, err := service.ProcessDueInRoomRewards(ctx, time.Now().Add(2*time.Second), 10)
|
||||
dueProcessed, err := service.ProcessDueInRoomRewards(ctx, now.Add(30*time.Second), 10)
|
||||
must(err, "process due in-room rewards")
|
||||
assert(dueProcessed == 1, "due in-room reward count mismatch")
|
||||
|
||||
@ -233,10 +251,22 @@ func main() {
|
||||
must(db.WithContext(ctx).Model(&model.VoiceRoomRocketAudienceSnapshot{}).
|
||||
Where("room_id = ?", room.RoomID).
|
||||
Count(&snapshotCount).Error, "count snapshots")
|
||||
assert(snapshotCount == 3, "snapshot count mismatch")
|
||||
var selectedCount int64
|
||||
must(db.WithContext(ctx).Model(&model.VoiceRoomRocketAudienceSnapshot{}).
|
||||
Where("room_id = ? AND selected = ?", room.RoomID, true).
|
||||
Count(&selectedCount).Error, "count selected snapshots")
|
||||
assert(selectedCount == 3, "selected snapshot count mismatch")
|
||||
var leftSnapshotCount int64
|
||||
must(db.WithContext(ctx).Model(&model.VoiceRoomRocketAudienceSnapshot{}).
|
||||
Where("room_id = ? AND user_id = ?", room.RoomID, leftUserID).
|
||||
Count(&leftSnapshotCount).Error, "count left user snapshots")
|
||||
assert(leftSnapshotCount == 0, "left user snapshot mismatch")
|
||||
var leftRewardCount int64
|
||||
must(db.WithContext(ctx).Model(&model.VoiceRoomRocketRewardRecord{}).
|
||||
Where("room_id = ? AND day_key = ? AND user_id = ?", room.RoomID, day, leftUserID).
|
||||
Count(&leftRewardCount).Error, "count left user rewards")
|
||||
assert(leftRewardCount == 0, "left user reward mismatch")
|
||||
var successCount int64
|
||||
must(db.WithContext(ctx).Model(&model.VoiceRoomRocketRewardRecord{}).
|
||||
Where("room_id = ? AND day_key = ? AND grant_status = ?", room.RoomID, day, "SUCCESS").
|
||||
@ -258,13 +288,16 @@ func main() {
|
||||
FirstAcceptedEnergy: first.AcceptedEnergy,
|
||||
SecondRawEnergy: 80,
|
||||
SecondAcceptedEnergy: second.AcceptedEnergy,
|
||||
LaunchNo: second.LaunchNo,
|
||||
LaunchNo: launch.LaunchNo,
|
||||
StatusLevel: finalStatus.CurrentLevel,
|
||||
StatusEnergy: finalStatus.CurrentEnergy,
|
||||
KingRecords: len(king.Records),
|
||||
Top1PopupBeforeAck: len(top1Popups.Records),
|
||||
Top1PopupAfterAck: len(top1AfterAck.Records),
|
||||
IgnitePopupCount: len(ignitePopups.Records),
|
||||
LeftUserID: leftUserID,
|
||||
LeftUserSnapshotCount: leftSnapshotCount,
|
||||
LeftUserRewardCount: leftRewardCount,
|
||||
RewardRecordCount: rewardCount,
|
||||
SnapshotCount: snapshotCount,
|
||||
SelectedAudienceCount: selectedCount,
|
||||
@ -312,6 +345,7 @@ func seedPresence(ctx context.Context, rdb *redis.Client, roomID int64, users []
|
||||
key := fmt.Sprintf("voice_room:online:LIKEI:%d", roomID)
|
||||
for _, userID := range users {
|
||||
must(rdb.SAdd(ctx, key, userID).Err(), "seed online room")
|
||||
must(rdb.Set(ctx, fmt.Sprintf("voice_room:user_room:LIKEI:%d", userID), strconv.FormatInt(roomID, 10), time.Minute).Err(), "seed user room")
|
||||
must(rdb.Set(ctx, fmt.Sprintf("voice_room:user_seen:LIKEI:%d", userID), strconv.FormatInt(time.Now().UnixMilli(), 10), time.Minute).Err(), "seed user seen")
|
||||
}
|
||||
must(rdb.Expire(ctx, key, time.Minute).Err(), "expire online room")
|
||||
@ -373,7 +407,7 @@ func seedConfig(ctx context.Context, service *voiceroomrocket.Service, db *gorm.
|
||||
Rewards: []voiceroomrocket.RewardConfigPayload{
|
||||
{Level: 1, RewardScene: "TOP1", RewardType: "GOLD", RewardName: "Local Verify Top1 Gold", RewardAmount: 10, Enabled: true, Sort: 1},
|
||||
{Level: 1, RewardScene: "IGNITE", RewardType: "GOLD", RewardName: "Local Verify Ignite Gold", RewardAmount: 5, Enabled: true, Sort: 1},
|
||||
{Level: 1, RewardScene: "IN_ROOM", RewardType: "GOLD", RewardName: "Local Verify In Room Gold", RewardAmount: 1, Enabled: true, Sort: 1},
|
||||
{Level: 1, RewardScene: "IN_ROOM", RewardType: "GOLD", RewardName: "Local Verify In Room Gold", RewardAmount: 1, RewardCopies: 3, Enabled: true, Sort: 1},
|
||||
},
|
||||
})
|
||||
must(err, "save rewards")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user