hyapp-server/docs/幸运礼物重构第一步-配置契约与单位.md
2026-05-27 22:12:16 +08:00

157 lines
6.5 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.

# 幸运礼物重构第一步:配置契约与单位
本文档定义幸运礼物重构第一步。第一步只解决配置、单位、规则版本和后台发布校验,不实现新抽奖算法。当前处于开发阶段,不兼容旧代码,不保留旧的 `target_rtp_ppm=95`、旧 `tiers.weight=0` 自动权重模型,也不继续把 `lucky_gift_rules.gift_id` 当作奖池 ID 使用。
## 目标
| 目标 | 说明 |
| --- | --- |
| 消灭单位歧义 | 后台展示百分比和倍数,服务端持久化 ppm |
| 明确规则 owner | `activity-service` 拥有幸运礼物规则、奖档、RTP 参数和版本 |
| 按阶段配置体验 | `novice/normal/advanced` 各自拥有独立奖档概率 |
| 发布前校验 | 配置无法满足成本或体验边界时拒绝发布 |
| 为第二步抽奖引擎铺路 | 输出可直接被 v2 抽奖引擎读取的规则快照 |
## 删除旧模型
| 旧模型 | 新处理 |
| --- | --- |
| 后台直接提交 `target_rtp_ppm=95` | 改为后台提交 `target_rtp_percent=95`,服务端转换为 `950000` |
| 后台提交倍数 `0.3` 后直接存 `0.3` | 服务端保存 `multiplier_ppm=300000` |
| `lucky_gift_rules.gift_id` 表示奖池 | 新表使用显式 `pool_id` |
| 单一 `multiplier_ppms` 生成三个阶段相同奖档 | 三个阶段分别配置奖档和权重 |
| `tiers.weight=0`,运行时自动权重 | 后台必须配置基础概率,抽奖引擎只做控制调整 |
## 新配置字段
### 规则主表
建议新建 `lucky_gift_rule_versions`
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `app_code` | string | App 编码 |
| `pool_id` | string | 幸运礼物奖池 ID |
| `rule_version` | int64 | 规则版本,每次发布递增 |
| `enabled` | bool | 是否启用 |
| `target_rtp_ppm` | int64 | 基础返奖目标,`95% = 950000` |
| `pool_rate_ppm` | int64 | 每笔扣费进入基础奖池比例 |
| `settlement_window_wager` | int64 | RTP 结算窗口流水,单位金币 |
| `control_band_ppm` | int64 | 正常波动带,`10000 = ±1%` |
| `gift_price_reference` | int64 | 配置参考礼物价格 |
| `effective_from_ms` | int64 | 生效时间UTC epoch ms |
| `created_by_admin_id` | int64 | 发布人 |
| `created_at_ms` | int64 | 创建时间 |
### 阶段奖档表
建议新建 `lucky_gift_stage_tiers`
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `app_code` | string | App 编码 |
| `pool_id` | string | 奖池 ID |
| `rule_version` | int64 | 规则版本 |
| `stage` | string | `novice/normal/advanced` |
| `tier_id` | string | 奖档 ID |
| `multiplier_ppm` | int64 | 倍率,`1x = 1000000` |
| `base_weight_ppm` | int64 | 正常模式基础概率 |
| `reward_source` | string | `base_rtp/activity_subsidy/presentation_only` |
| `high_water_only` | bool | 是否高水位才开放 |
| `broadcast_level` | string | `none/room/region/global` |
| `enabled` | bool | 是否启用 |
## 后台输入格式
后台对运营展示和提交应使用易懂单位:
```json
{
"pool_id": "lucky_100",
"enabled": true,
"target_rtp_percent": 95,
"pool_rate_percent": 96,
"settlement_window_wager": 1000000,
"control_band_percent": 1,
"gift_price_reference": 100,
"stages": [
{
"stage": "novice",
"tiers": [
{"tier_id": "novice_none", "multiplier": 0, "probability_percent": 30},
{"tier_id": "novice_0_3x", "multiplier": 0.3, "probability_percent": 22},
{"tier_id": "novice_0_5x", "multiplier": 0.5, "probability_percent": 18}
]
}
]
}
```
服务端保存前转换:
```text
target_rtp_percent=95 -> target_rtp_ppm=950000
pool_rate_percent=96 -> pool_rate_ppm=960000
control_band_percent=1 -> control_band_ppm=10000
multiplier=0.3 -> multiplier_ppm=300000
probability_percent=22 -> base_weight_ppm=220000
```
## 发布校验
| 校验项 | 规则 | 失败处理 |
| --- | --- | --- |
| RTP 单位 | `100000 <= target_rtp_ppm <= 990000` | 拒绝发布 |
| 入池比例 | `pool_rate_ppm >= target_rtp_ppm``pool_rate_ppm <= 1000000` | 拒绝发布 |
| 波动带 | `0 < control_band_ppm <= 50000` | 拒绝发布 |
| 参考价格 | `gift_price_reference > 0` | 拒绝发布 |
| 阶段完整 | 必须有 `novice/normal/advanced` 三个阶段 | 拒绝发布 |
| 阶段概率 | 每个阶段 `sum(base_weight_ppm) == 1000000` | 拒绝发布 |
| 倍率合法 | `multiplier_ppm >= 0` | 拒绝发布 |
| 基础奖档 | 每个阶段至少有一个 `base_rtp` 奖档 | 拒绝发布 |
| 未中奖档 | 每个阶段必须显式配置 `0x`,除非标记为“每抽必返” | 拒绝发布或强确认 |
| 高倍档 | `high_water_only=true` 或有明确单次上限 | 拒绝发布 |
## API 调整
后台接口只暴露业务单位:
| 接口 | 行为 |
| --- | --- |
| `GET /api/v1/admin/activity/lucky-gifts/config?pool_id=...` | 返回百分比、倍数和阶段奖档 |
| `PUT /api/v1/admin/activity/lucky-gifts/config?pool_id=...` | 发布新版本,服务端转换 ppm 并校验 |
| `GET /api/v1/admin/activity/lucky-gifts/configs` | 列出所有奖池和当前版本 |
内部 gRPC 可以继续使用 ppm但字段名必须明确
| 字段 | 说明 |
| --- | --- |
| `target_rtp_ppm` | 内部字段,后台 HTTP 不直接要求运营填写 |
| `pool_rate_ppm` | 内部字段 |
| `multiplier_ppm` | 内部字段 |
| `base_weight_ppm` | 内部字段 |
## 实施任务
1. 新增 v2 proto规则、阶段、奖档、发布请求、读取响应。
2. 新增 MySQL v2 表。
3. 改后台 lucky gift handler只接收百分比和倍数。
4.`activity-service` 增加配置转换和发布校验。
5. 删除或旁路旧 `multiplier_ppms -> tiers(weight=0)` 归一化模型。
6. 增加配置单元测试和 schema 初始化。
## 验收标准
| 验收项 | 标准 |
| --- | --- |
| 单位转换 | 后台提交 `target_rtp_percent=95`DB 保存 `target_rtp_ppm=950000` |
| 倍率转换 | 后台提交 `multiplier=0.3`DB 保存 `multiplier_ppm=300000` |
| 概率转换 | 后台提交 `probability_percent=22`DB 保存 `base_weight_ppm=220000` |
| 错误 RTP | 后台提交等价 `target_rtp_ppm=95` 的配置必须被拒绝 |
| 阶段概率 | 任一阶段概率和不是 100% 时必须被拒绝 |
| 旧字段隔离 | 新规则表不再使用 `gift_id` 表示 `pool_id` |
| 版本不可变 | 发布新配置只新增 `rule_version`,不覆盖历史版本 |
| 测试 | `go test ./services/activity-service/internal/service/luckygift ./services/activity-service/internal/storage/mysql ./server/admin/internal/modules/luckygift` 通过 |
第一步完成后,系统只具备“正确配置和发布规则”的能力,不要求新抽奖引擎上线。