hyapp-server/docs/幸运礼物线上落地文档.md
2026-05-26 03:06:02 +08:00

8.8 KiB
Raw Blame History

幸运礼物线上落地文档

本文档定义当前幸运礼物在 activity-service 的线上落地边界、后台参数、抽奖事务和验证方式。幸运礼物只拥有活动决策、RTP 窗口、奖池和抽奖事实;金币扣费和返奖入账仍归 wallet-service,房间状态和房间表现仍归 room-service

Current Scope

本次落地包含:

Scope Status Notes
LuckyGiftService.CheckLuckyGift done 房间送礼前检查礼物是否启用,并返回当前体验池
LuckyGiftService.ExecuteLuckyGiftDraw done 扣费成功后执行抽奖决策,写抽奖事实和 outbox
AdminLuckyGiftService.GetLuckyGiftConfig done 后台按 pool_id 读取幸运礼物奖池规则;未配置时返回默认草稿
AdminLuckyGiftService.UpsertLuckyGiftConfig done 后台按 pool_id 发布独立幸运礼物奖池规则版本
AdminLuckyGiftService.ListLuckyGiftDraws done 后台审计抽奖记录
三层基础奖池 done platform / room / gift
RTP 双窗口 done global + gift
风控计数 done user hour/day、device day、room hour、anchor day
房间气氛池 done 独立预算,不污染基础 RTP
活动预算池 done 独立补贴,不污染基础 RTP
返奖入账 done activity_outbox worker 调 wallet-service.CreditLuckyGiftReward,钱包 outbox 继续触发余额私信
房间中奖 IM done activity_outbox worker 投递腾讯云 IM 群消息 lucky_gift_drawn
App 检查接口 done gateway 暴露 POST /api/v1/activities/lucky-gifts/check

activity-service 抽奖事务内仍不调用钱包、房间 RPC 或腾讯云 IM。抽奖事务只写事实和 activity_outbox;事务外由 lucky gift worker 抢占 outbox先幂等返奖再投递房间中奖 IM最后标记 draw 和 outbox 状态。

Call Flow

room-service SendGift
  1. CheckLuckyGift(meta, user_id, room_id, gift_id, pool_id)
  2. wallet-service 扣费成功
  3. ExecuteLuckyGiftDraw(command_id, user_id, target_user_id, gift_count, device_id, room_id, anchor_id, gift_id, pool_id, coin_spent, paid_at_ms)
  4. activity-service 本地事务写 draw fact + outbox
  5. lucky gift worker 幂等调用 wallet-service.CreditLuckyGiftReward
  6. wallet-service 写 WalletBalanceChangednotice-service 投递私信余额通知
  7. lucky gift worker 投递房间群 IM lucky_gift_drawn
  8. activity-service 标记 reward_status=granted、outbox=delivered

command_id 是抽奖幂等键。重复 ExecuteLuckyGiftDraw 会返回同一条 lucky_draw_records,不会重复消耗 RTP、奖池或预算。

后台按 pool_id 维护多份互相独立的幸运礼物奖池规则,实际抽奖仍记录用户发送的真实 gift_id。后台不暴露单抽消耗;服务端保存固定内部换算单位,不同幸运礼物实际消耗不同,抽奖事务按实际 coin_spent 等比缩放基础奖档,并按实际 coin_spent 累计 RTP 目标和奖池入账。同一个 gift_id 可以请求不同 pool_id,不同 pool_id 的 RTP 窗口、基础奖池、房间气氛池、活动预算、用户阶段和风控计数互不共享。

Transaction Boundary

ExecuteLuckyGiftDraw 使用一个 MySQL 本地事务,事务内必须完成:

  1. command_id 锁定幂等记录。
  2. 锁定 lucky_gift_rulespool_id 对应规则。
  3. 锁定或创建用户体验状态 lucky_user_states
  4. 锁定或创建全站 RTP 窗口、幸运礼物整体子窗口。
  5. 锁定或创建平台、房间、幸运礼物整体三层基础奖池。
  6. 锁定或创建房间气氛池。
  7. 锁定或创建活动总预算桶和 UTC 日预算桶。
  8. 锁定用户、设备、房间、主播风控计数。
  9. 抽前过滤所有不可支付候选。
  10. 执行一次服务端安全随机。
  11. 更新 RTP 窗口、奖池、预算、风控计数、用户状态。
  12. 插入 lucky_draw_records
  13. 必要时插入 activity_outbox

事务内不得调用 wallet-serviceroom-service 或腾讯云 IM避免外部 RPC 卡住奖池行锁。

RTP Accounting

基础返奖只使用 base_reward_coins

base_rtp = sum(base_reward_coins) / sum(coin_spent)

用户可见返奖使用:

effective_reward_coins = base_reward_coins
                       + room_atmosphere_reward_coins
                       + activity_subsidy_coins

房间气氛池和活动预算只能提高 effective_rtp,不能用于补平基础 RTP。

Admin Parameters

后台参数分为 6 类。

后台 HTTP 入口:

Method Path Permission Meaning
GET /api/v1/admin/activity/lucky-gifts/config?pool_id=pool_95 lucky-gift:view 读取指定奖池规则;未配置返回默认草稿
PUT /api/v1/admin/activity/lucky-gifts/config?pool_id=pool_95 lucky-gift:update 发布指定奖池的新规则版本
GET /api/v1/admin/activity/lucky-gifts/draws lucky-gift:view 查询抽奖审计记录
Group Fields Meaning
RTP 成本 target_rtp_ppm, pool_rate_ppm, global_window_draws, gift_window_draws 控制基础 RTP 和收敛窗口
三层池 platform_pool_weight_ppm, room_pool_weight_ppm, gift_pool_weight_ppm, initial_*_pool, *_reserve 控制平台、房间、礼物各自承担的基础返奖能力
倍率配置 multiplier_ppms, generated tiers 运营只配置用户可能命中的倍率;服务按 RTP 窗口、水位和风控自动生成实时概率
体验池 novice_draw_limit, intermediate_draw_limit 控制新手/中级/高级阶段,不直接决定成本
高倍 high_multiplier, high_water_pool_multiple, large_tier_enabled 控制 100x/500x 等高倍倍率是否可进入候选
风控 max_single_payout, user_hourly_payout_cap, user_daily_payout_cap, device_daily_payout_cap, room_hourly_payout_cap, anchor_daily_payout_cap 控制单用户、设备、房间、主播关联风险敞口
娱乐补贴 room_atmosphere_rate_ppm, room_atmosphere_initial, room_atmosphere_reserve, activity_budget, activity_daily_limit 控制房间爆点和活动补贴

调整方向:

Goal Primary Change
降成本 target_rtp_ppm,降补贴预算,提高高水位倍数
增刺激 增加高倍倍率,增房间气氛和活动预算
新手更友好 增加 0.2x/0.5x/1x 等小倍率,延长 novice_draw_limit
防套利 降低用户/设备/房间/主播上限,关闭高倍
RTP 更稳 缩小 RTP 窗口
体验更自然 放大 RTP 窗口,但接受短周期波动

Data Tables

Table Owner Data
lucky_gift_rules pool_id 保存幸运礼物奖池规则和后台可调参数
lucky_rtp_windows pool_id 保存 RTP 控制窗口
lucky_pools pool_id 保存 platform / room / gift 三层基础奖池
lucky_room_atmosphere_pools pool_id + room_id 保存房间气氛预算
lucky_activity_budgets pool_id 保存活动补贴预算;budget_day=__total__ 控总额UTC 日期行控日释放
lucky_user_states 用户在单个 pool_id 内的累计付费抽数
lucky_risk_counters pool_id 保存风控窗口计数
lucky_draw_records 每次抽奖事实和审计快照

Verification

Proto 变更后必须运行:

PATH=/tmp/codex-protoc/bin:$HOME/go/bin:$PATH make proto

服务编译验证:

go test ./services/activity-service/...

模型验证:

go run ./tools/lucky-gift-v2-demo \
  -users 1000 \
  -devices 900 \
  -rooms 50 \
  -draws-min 0 \
  -draws-max 100000 \
  -unique-draws \
  -base-rtp 95 \
  -initial 1000000000 \
  -cost 500 \
  -global-window 100000 \
  -gift-window 100000

验收口径:

Check Requirement
100000 paid draws 基础 RTP 偏差 < 1%
1000000 paid draws 基础 RTP 偏差 < 0.5%
lucky gift RTP 幸运礼物整体子窗口独立收敛
pool safety 三层池不低于 reserve
subsidy isolation 房间气氛/活动补贴不进入基础 RTP
idempotency 重复 command_id 返回同一 draw

Rollout

建议上线顺序:

  1. 后台创建规则草稿,enabled=false
  2. 跑模型模拟并审核参数。
  3. 发布 enabled=true 到全局幸运礼物配置。
  4. 只开放内部房间或白名单房间。
  5. 观察 base_rtpeffective_rtp、pool balance、risk reject、outbox pending。
  6. 稳定后逐步开放更多房间和礼物。

必须保留的开关:

Switch Effect
enabled=false 全站幸运礼物停止抽奖
large_tier_enabled=false 关闭高倍奖档
activity_budget=0 关闭活动补贴
room_atmosphere_rate_ppm=0 关闭房间气氛补贴
降低各风控 cap 快速压缩风险敞口