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

6.5 KiB
Raw Blame History

幸运礼物重构第一步:配置契约与单位

本文档定义幸运礼物重构第一步。第一步只解决配置、单位、规则版本和后台发布校验,不实现新抽奖算法。当前处于开发阶段,不兼容旧代码,不保留旧的 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 是否启用

后台输入格式

后台对运营展示和提交应使用易懂单位:

{
  "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}
      ]
    }
  ]
}

服务端保存前转换:

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_ppmpool_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=95DB 保存 target_rtp_ppm=950000
倍率转换 后台提交 multiplier=0.3DB 保存 multiplier_ppm=300000
概率转换 后台提交 probability_percent=22DB 保存 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 通过

第一步完成后,系统只具备“正确配置和发布规则”的能力,不要求新抽奖引擎上线。