修复机器人送礼
This commit is contained in:
parent
23a44d5f6f
commit
ef3dacd1a3
@ -282,9 +282,10 @@ func (s *Service) sendGift(ctx context.Context, req *roomv1.SendGiftRequest, rob
|
||||
}
|
||||
|
||||
records := make([]outbox.Record, 0, len(giftEvents)+4)
|
||||
robotRecords := make([]outbox.Record, 0, len(giftEvents)+3)
|
||||
if cmd.RobotGift {
|
||||
// 机器人送礼只保留客户端展示所需事实,并写入 robot outbox,由独立 worker/topic 补偿投递。
|
||||
robotRecords := make([]outbox.Record, 0, len(giftEvents)+5)
|
||||
// RobotWalletGift 只表示这笔礼物由机器人钱包结算;真实房机器人仍会同步写房间贡献、火箭和麦位热度,但展示事件必须走独立 outbox,避免机器人流量抢占真人主 outbox。
|
||||
robotOutboxGift := cmd.RobotWalletGift
|
||||
if robotOutboxGift {
|
||||
robotRecords = append(robotRecords, giftEvents...)
|
||||
} else {
|
||||
records = append(records, giftEvents...)
|
||||
@ -306,27 +307,35 @@ func (s *Service) sendGift(ctx context.Context, req *roomv1.SendGiftRequest, rob
|
||||
if err != nil {
|
||||
return mutationResult{}, nil, err
|
||||
}
|
||||
if cmd.RobotGift {
|
||||
// 房间热度和麦位收礼热度仍要给客户端展示,但走机器人 outbox,不能占用真人房间主流程。
|
||||
if robotOutboxGift {
|
||||
// 房间热度和麦位收礼热度仍要给客户端展示;是否真实计入统计由 RobotGift 语义单独控制,不能再拿投递通道反推业务真实性。
|
||||
robotRecords = append(robotRecords, heatEvent, rankEvent)
|
||||
} else {
|
||||
records = append(records, heatEvent, rankEvent)
|
||||
}
|
||||
if !cmd.RobotGift && !cmd.RobotWalletGift {
|
||||
// 火箭只属于真人礼物主业务链路,机器人展示礼物不能触发火箭燃料 outbox。
|
||||
if !cmd.RobotGift {
|
||||
// 真实房间礼物才广播火箭进度;真人房机器人送礼使用机器人钱包结算,但仍是真实房间贡献,所以火箭事件跟随机器人通道投递。
|
||||
if rocketApply.progressEvent != nil {
|
||||
progressEvent, err := outbox.Build(current.RoomID, "RoomRocketFuelChanged", current.Version, now, rocketApply.progressEvent)
|
||||
if err != nil {
|
||||
return mutationResult{}, nil, err
|
||||
}
|
||||
records = append(records, progressEvent)
|
||||
if robotOutboxGift {
|
||||
robotRecords = append(robotRecords, progressEvent)
|
||||
} else {
|
||||
records = append(records, progressEvent)
|
||||
}
|
||||
}
|
||||
if rocketApply.ignited != nil {
|
||||
ignitedEvent, err := outbox.Build(current.RoomID, "RoomRocketIgnited", current.Version, now, rocketApply.ignited)
|
||||
if err != nil {
|
||||
return mutationResult{}, nil, err
|
||||
}
|
||||
records = append(records, ignitedEvent)
|
||||
if robotOutboxGift {
|
||||
robotRecords = append(robotRecords, ignitedEvent)
|
||||
} else {
|
||||
records = append(records, ignitedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
if cmd.SyntheticLuckyGift && luckyGift != nil {
|
||||
@ -351,7 +360,7 @@ func (s *Service) sendGift(ctx context.Context, req *roomv1.SendGiftRequest, rob
|
||||
if err != nil {
|
||||
return mutationResult{}, nil, err
|
||||
}
|
||||
if cmd.RobotGift {
|
||||
if robotOutboxGift {
|
||||
robotRecords = append(robotRecords, drawEvent)
|
||||
} else {
|
||||
records = append(records, drawEvent)
|
||||
|
||||
@ -153,6 +153,98 @@ func TestRobotGiftSkipsMainHeatAndRankOutbox(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRealRoomRobotGiftKeepsRoomContributionAndUsesRobotOutbox(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repository := mysqltest.NewRepository(t)
|
||||
now := &fixedRoomRocketClock{now: time.Date(2026, 6, 8, 11, 30, 0, 0, time.UTC)}
|
||||
wallet := &rocketTestWallet{debits: []*walletv1.DebitGiftResponse{{
|
||||
BillingReceiptId: "receipt-real-room-robot",
|
||||
CoinSpent: 100,
|
||||
ChargeAmount: 100,
|
||||
HeatValue: 100,
|
||||
ChargeAssetType: "ROBOT_COIN",
|
||||
GiftTypeCode: "normal",
|
||||
}}}
|
||||
leaderboard := &recordingRoomGiftLeaderboard{}
|
||||
svc := roomservice.New(roomservice.Config{
|
||||
NodeID: "node-real-room-robot-outbox",
|
||||
LeaseTTL: 10 * time.Second,
|
||||
RankLimit: 20,
|
||||
SnapshotEveryN: 100,
|
||||
Clock: now,
|
||||
RoomGiftLeaderboard: leaderboard,
|
||||
}, router.NewMemoryDirectory(), repository, wallet, integration.NewNoopRoomEventPublisher(), integration.NewNoopOutboxPublisher())
|
||||
writeRocketConfig(t, ctx, repository, rocketConfigForTest(100, 1_000))
|
||||
|
||||
roomID := "real-room-robot-outbox"
|
||||
senderID := int64(64001)
|
||||
targetID := int64(64002)
|
||||
createRocketRoom(t, ctx, svc, roomID, senderID, 9001)
|
||||
joinRocketRoom(t, ctx, svc, roomID, targetID)
|
||||
if _, err := svc.MicUp(ctx, &roomv1.MicUpRequest{
|
||||
Meta: rocketMeta(roomID, targetID, "real-room-robot-target-up"),
|
||||
SeatNo: 1,
|
||||
}); err != nil {
|
||||
t.Fatalf("target mic up failed: %v", err)
|
||||
}
|
||||
beforeMain := outboxEventCounts(t, ctx, repository)
|
||||
beforeRobot := robotOutboxEventCounts(t, ctx, repository)
|
||||
|
||||
resp, err := svc.RobotSendGift(ctx, roomservice.RobotSendGiftInput{
|
||||
Meta: rocketMeta(roomID, senderID, "real-room-robot-gift"),
|
||||
TargetUserID: targetID,
|
||||
GiftID: "real-room-robot-gift",
|
||||
GiftCount: 1,
|
||||
PoolID: "human_robot_super_lucky_display",
|
||||
RobotUserIDs: []int64{senderID, targetID},
|
||||
SyntheticMultiplierPPM: 5_000_000,
|
||||
RealRoomHeat: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("real room robot gift failed: %v", err)
|
||||
}
|
||||
|
||||
if resp.GetRoomHeat() != 100 {
|
||||
t.Fatalf("real room robot gift must update room heat, got %d", resp.GetRoomHeat())
|
||||
}
|
||||
if user := onlineUserByID(resp.GetRoom(), targetID); user == nil || user.GetGiftValue() != 100 {
|
||||
t.Fatalf("real room robot gift must update target gift value: %+v", user)
|
||||
}
|
||||
if seat := seatByNo(resp.GetRoom(), 1); seat == nil || seat.GetGiftValue() != 100 {
|
||||
t.Fatalf("real room robot gift must update mic gift value: %+v", seat)
|
||||
}
|
||||
if len(resp.GetGiftRank()) == 0 || resp.GetGiftRank()[0].GetUserId() != senderID || resp.GetGiftRank()[0].GetGiftValue() != 100 {
|
||||
t.Fatalf("real room robot gift must update room gift rank: %+v", resp.GetGiftRank())
|
||||
}
|
||||
if resp.GetRocket() == nil || len(resp.GetRocket().GetPendingLaunches()) != 1 {
|
||||
t.Fatalf("real room robot gift must fuel and ignite room rocket: %+v", resp.GetRocket())
|
||||
}
|
||||
if len(leaderboard.increments) != 1 || leaderboard.increments[0].RoomID != roomID || leaderboard.increments[0].CoinSpent != 100 {
|
||||
t.Fatalf("real room robot gift must keep cross-room contribution increment: %+v", leaderboard.increments)
|
||||
}
|
||||
|
||||
afterMain := outboxEventCounts(t, ctx, repository)
|
||||
for _, eventType := range []string{"RoomGiftSent", "RoomHeatChanged", "RoomRankChanged", "RoomRobotLuckyGiftDrawn", "RoomRocketFuelChanged", "RoomRocketIgnited"} {
|
||||
if delta := afterMain[eventType] - beforeMain[eventType]; delta != 0 {
|
||||
t.Fatalf("real room robot gift must not write %s to main outbox, delta=%d counts=%+v", eventType, delta, afterMain)
|
||||
}
|
||||
}
|
||||
afterRobot := robotOutboxEventCounts(t, ctx, repository)
|
||||
wantRobotDeltas := map[string]int{
|
||||
"RoomGiftSent": 1,
|
||||
"RoomHeatChanged": 1,
|
||||
"RoomRankChanged": 1,
|
||||
"RoomRobotLuckyGiftDrawn": 1,
|
||||
"RoomRocketFuelChanged": 1,
|
||||
"RoomRocketIgnited": 1,
|
||||
}
|
||||
for eventType, want := range wantRobotDeltas {
|
||||
if delta := afterRobot[eventType] - beforeRobot[eventType]; delta != want {
|
||||
t.Fatalf("real room robot gift robot outbox %s delta=%d want=%d counts=%+v", eventType, delta, want, afterRobot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoomUserGiftValueRecoversFromCommandLog(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repository := mysqltest.NewRepository(t)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user