package service import ( "context" "time" "hyapp/services/room-service/internal/room/outbox" "hyapp/services/room-service/internal/room/rank" "hyapp/services/room-service/internal/room/state" roomv1 "hyapp.local/api/proto/room/v1" ) // SendGift 先同步调用 wallet,再在 Room Cell 内完成热度、榜单和房间事件落地。 func (s *Service) SendGift(ctx context.Context, req *roomv1.SendGiftRequest) (*roomv1.SendGiftResponse, error) { return s.sendGift(ctx, req, giftSendOptions{}) } // SendGiftBatch 是多人送礼展示入口;它复用送礼账务、幸运礼物和 Room Cell 提交流程, // 但只在 batch 分支生成 RoomGiftBatchSent 展示消息,避免普通 SendGift 被 display_mode 隐式改变行为。 func (s *Service) SendGiftBatch(ctx context.Context, req *roomv1.SendGiftRequest) (*roomv1.SendGiftResponse, error) { return s.sendGiftBatch(ctx, req) } func (s *Service) sendGiftBatch(ctx context.Context, req *roomv1.SendGiftRequest) (*roomv1.SendGiftResponse, error) { return s.sendGift(ctx, req, giftSendOptions{BatchDisplay: true}) } type giftSendOptions struct { Robot robotGiftOptions BatchDisplay bool } func (s *Service) sendGift(ctx context.Context, req *roomv1.SendGiftRequest, options giftSendOptions) (*roomv1.SendGiftResponse, error) { ctx = contextFromMeta(ctx, req.GetMeta()) flow := newGiftFlow(s, ctx, req, options) result, err := s.mutateRoom(ctx, flow.cmd, true, func(now time.Time, current *state.RoomState, localRank *rank.LocalRank) (mutationResult, []outbox.Record, error) { // 阶段顺序保持原 sendGift 同步主链路:先校验房间态,再取房间事实,再扣费/抽奖,最后提交 Room Cell 状态和构造事件。 if err := flow.validateBeforeDebit(current); err != nil { return mutationResult{}, nil, err } if err := flow.prepareRoomFacts(); err != nil { return mutationResult{}, nil, err } if err := flow.debitAndSettle(now); err != nil { return mutationResult{}, nil, err } if err := flow.applyRoomState(now, current, localRank); err != nil { return mutationResult{}, nil, err } return flow.buildMutationResult(now, current) }) if err != nil { return nil, err } return &roomv1.SendGiftResponse{ Result: commandResult(result.applied, result.snapshot.GetVersion(), s.clock.Now()), BillingReceiptId: result.billingReceiptID, RoomHeat: result.roomHeat, GiftRank: result.giftRank, Room: result.snapshot, Rocket: result.snapshot.GetRocket(), LuckyGift: result.luckyGift, LuckyGifts: result.luckyGifts, CoinBalanceAfter: result.coinBalanceAfter, GiftIncomeBalanceAfter: result.giftIncomeBalanceAfter, BatchDisplay: result.batchDisplay, }, nil }