# 头衔加概率与折扣的可验证设计

## 结论

* 采用“权重修饰”的方式为持有头衔的用户提升抽奖概率。

* 哈希算法（HMAC-SHA256 随机熵）保持不变；只扩展承诺与收据的字段以固化规则与有效权重，保证可验证性与不可篡改。

## 必要扩展

* 承诺扩展（IssueRandomCommitment）

  * 新增 `AlgoRulesHash`：对“权重修饰规则（由头衔配置确定的纯函数）”做哈希并随期承诺固化。服务端在抽取前不能更改规则。

* 收据扩展（Receipt）

  * 新增 `UserId`：明确本次抽取的用户。

  * 新增 `WeightsTotalEffective`：按用户头衔修饰后的总权重，用于位置采样与第三方复核。

  * 新增 `UserWeightFactorsRoot`：以 `reward_id -> factor` 做 Merkle 根，第三方可据此对 `Items(snapshot)` 重建有效权重。

* 编码消息扩展（不改算法，仅改输入）

  * 仍使用 `HMAC(serverSubSeed, encodeMessage(...))`；`encodeMessage` 增加 `UserId` 与 `WeightsTotalEffective` 字段（保持确定性）。

## 规则与确定性

* 规则来源：头衔模板配置（百分比或加点），以及叠加策略（none|max|multiply）。

* 纯函数：`factor = F(user_titles, reward_id)`，只依赖用户持有的头衔与静态规则；不依赖随机数或服务端临时状态。

* 可复核：第三方得到 `ItemsRoot`、`AlgoRulesHash`、`UserWeightFactorsRoot`、`UserId` 与收据内快照，即可重建每个 `reward_id` 的有效权重与 `WeightsTotalEffective`，据同样的消息编码与哈希熵验证 `SelectedIndex`。

## 核心流程（抽奖不变，仅插入修饰步骤）

1. 读取承诺：`GetIssueRandomCommit(...)`（`internal/service/activity/random_commit.go:99`）。
2. 载入奖励池快照：同现有实现（`internal/service/activity/draw_execute.go:21-35`）。
3. 计算用户权重修饰：`effective_weight[i] = base_weight[i] * factor(user, reward_id[i])`；汇总 `WeightsTotalEffective`。
4. 使用 HMAC-SHA256 生成熵（算法不变），对 `encodeMessage(..., UserId, WeightsTotalEffective)` 取样位点。
5. 按有效权重的累加区间选中 `SelectedIndex/SelectedItemId`。
6. 返回收据，并包含 `UserWeightFactorsRoot` 与证明材料。

## 折扣集成（独立不影响随机）

* 抽奖订单创建（新增）：以活动门票价 `PriceDraw`（`internal/repository/mysql/model/activities.gen.go:22`）为基价。

* 头衔折扣：按叠加策略计算 `discount_amount`，落到订单字段 `DiscountAmount/ActualAmount`（`internal/repository/mysql/model/orders.gen.go:22`）。

* 与优惠券/积分叠加：默认“优惠券优先 + 头衔折上折”，可通过 `stack_policy` 切到“取最大”。

## 接口与服务

* 管理端：头衔模板 CRUD + 发放；配置权重修饰参数与叠加策略。

* APP：

  * 抽奖订单创建接口（新）：返回计算后的金额。

  * 抽奖接口：保持路径不变（`internal/api/activity/draw_app.go:28`），服务内执行权重修饰。

* 模拟与校验：

  * 模拟接口增加 `user_id`，输出期望概率与观测分布。

  * 校验接口以收据扩展字段重建有效权重，验证 `SelectedIndex`。

## 验收标准

* 头衔发放后，持有用户的抽奖订单能正确展示并结算折扣。

* 抽奖收据包含新增字段，第三方使用承诺与收据可重放选中过程；不持有头衔的用户与持有者的分布差异符合规则。

* 哈希熵算法未变，安全与不可预测性不受影响。

## 说明：关于“加概率是否影响哈希算法”

* 不影响哈希算法本身（仍是 HMAC-SHA256）。

* 影响的是“采样空间”——由原始 `weights_total` 改为 `WeightsTotalEffective`，并且将该值与用户标识作为消息输入的一部分来固定采样过程；承诺和收据的扩展确保服务端无法事后调参。

