- 新增系统称号模板与效果配置表及相关CRUD接口 - 实现用户称号分配与抽奖效果应用逻辑 - 优化抽奖接口支持用户ID参数以应用称号效果 - 新增称号管理前端页面与分配功能 - 修复Windows时区错误与JSON字段初始化问题 - 移除无用管理接口代码并更新文档说明
3.5 KiB
抽奖控制策略选择与执行
背景与约束
-
不改变算法逻辑(承诺→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 次不出现”的意图;只要在承诺前已固化,抽后不会被判定为操纵。
执行建议(不改代码)
- 选定期次与目标奖品,设置前置配置:
quantity=0或weight=0。 - 生成承诺(
commit_random)并上线;开始计数抽奖日志,达到 N 次后恢复配置并生成新承诺。 - 记录操作与版本切换,必要时在活动规则中说明奖池/期的切换策略。
参考位置
-
参与判定与选取:
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