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

191 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 幸运礼物线上落地文档
本文档定义当前幸运礼物在 `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
```text
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_rules``pool_id` 对应规则。
3. 锁定或创建用户体验状态 `lucky_user_states`
4. 锁定或创建全站 RTP 窗口、幸运礼物整体子窗口。
5. 锁定或创建平台、房间、幸运礼物整体三层基础奖池。
6. 锁定或创建房间气氛池。
7. 锁定或创建活动总预算桶和 UTC 日预算桶。
8. 锁定用户、设备、房间、主播风控计数。
9. 抽前过滤所有不可支付候选。
10. 执行一次服务端安全随机。
11. 更新 RTP 窗口、奖池、预算、风控计数、用户状态。
12. 插入 `lucky_draw_records`
13. 必要时插入 `activity_outbox`
事务内不得调用 `wallet-service``room-service` 或腾讯云 IM避免外部 RPC 卡住奖池行锁。
## RTP Accounting
基础返奖只使用 `base_reward_coins`
```text
base_rtp = sum(base_reward_coins) / sum(coin_spent)
```
用户可见返奖使用:
```text
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 变更后必须运行:
```bash
PATH=/tmp/codex-protoc/bin:$HOME/go/bin:$PATH make proto
```
服务编译验证:
```bash
go test ./services/activity-service/...
```
模型验证:
```bash
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_rtp``effective_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 | 快速压缩风险敞口 |