bindbox-game/.trae/documents/哈希抽奖逻辑与控制策略说明.md
邹方成 8141a47690
Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 39s
feat(称号系统): 新增称号管理功能与抽奖效果集成
- 新增系统称号模板与效果配置表及相关CRUD接口
- 实现用户称号分配与抽奖效果应用逻辑
- 优化抽奖接口支持用户ID参数以应用称号效果
- 新增称号管理前端页面与分配功能
- 修复Windows时区错误与JSON字段初始化问题
- 移除无用管理接口代码并更新文档说明
2025-11-16 11:37:40 +08:00

75 lines
3.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.

# 抽奖控制策略选择与执行
## 背景与约束
* 不改变算法逻辑承诺→HMAC+拒绝采样→验算一致),仅通过“输入/配置/时机”达到运营控制。
* 验算用户能看到的是回执中池快照与承诺哈希一致性,不能证明你在抽后操纵,只能确认抽前配置。
## 方案A权重/库存门控(推荐)
* 做法:在承诺前将目标奖品 `weight=0``quantity=0`,前 N 次(或直到达到阈值)不进入抽取集合;到期后恢复权重/库存并重新承诺。
* 基于代码:
* 排除条件:`weight>0 && (quantity==-1 || quantity>0)` 才参与抽取(`internal/service/activity/draw_execute.go:31-35,56-58`)。
* 承诺快照:包含每个奖励的 `{id,name,weight,quantity_before}``internal/service/activity/random_commit.go:53-61,67-73`)。
* 优点:简单直接、无需改算法;前 N 次绝不命中;验算完全通过。
* 缺点:恢复后需生成新承诺(`state_version` 增加),不同时间段 `items_root` 不同,运维需记录策略切换。
## 方案B承诺版本切换
* 做法:用 `state_version` 管理期的承诺版本:
* v1不含目标奖品或其权重为 0 → 前 N 次抽使用 v1。
* v2目标奖品恢复权重/库存 → 达到 N 后切换到 v2。
* 基于代码:承诺生成与历史查询(`internal/service/activity/random_commit.go:74-97,121-146`)。
* 优点:语义清晰、审计友好;对不同用户批次可严格区分承诺。
* 缺点:运维复杂度稍高;用户若横向对比可能看到承诺变化,但单次验算仍通过。
## 方案C直接发放替代抽奖
* 做法:对需要“必中/避中”的个体,使用管理端发放接口 `POST /api/admin/users/:user_id/rewards/grant``internal/router/router.go:127`)。
* 优点:精确可控,零风险。
* 缺点:不产生抽奖回执;不适合需要“抽奖体验”的场景。
## 方案对比与推荐
* 目标“前 N 次不出现”且保留抽奖体验:优先选 **方案A权重/库存门控)**,用 `quantity=0``weight=0` 让奖品在 N 次前不参与集合;到期后恢复并重新承诺。
* 若需批次化与清晰审计边界:选 **方案B承诺版本切换**,以 `state_version` 驱动切换N 次阈值以抽奖日志计数实现运维。
* 个体定向控制:用 **方案C直接发放** 替代抽奖。
## 验算与用户感知
* 验算会确认:回执中的 `server_seed_hash/items_root/weights_total/selected_index/rand_proof` 与承诺一致(`internal/api/admin/verify_draw.go:50-66,104-138`)。
* 用户能“看到”:当次承诺的奖池快照与权重(若回执包含快照,管理端/APP均有`internal/api/admin/draw_receipt.go:55-73``internal/api/activity/draw_app.go:28-34`)。
* 用户“感知不到”:你通过前置配置与时机实现“前 N 次不出现”的意图;只要在承诺前已固化,抽后不会被判定为操纵。
## 执行建议(不改代码)
1. 选定期次与目标奖品,设置前置配置:`quantity=0``weight=0`
2. 生成承诺(`commit_random`)并上线;开始计数抽奖日志,达到 N 次后恢复配置并生成新承诺。
3. 记录操作与版本切换,必要时在活动规则中说明奖池/期的切换策略。
## 参考位置
* 参与判定与选取:`internal/service/activity/draw_execute.go:31-35,50-66,131-145`
* 承诺生成与版本:`internal/service/activity/random_commit.go:67-85,74-97,121-146`
* 管理端验证:`internal/api/admin/verify_draw.go:50-66,104-138`