抽奖模块-backup
一、模块定位
抽奖模块是足球季活动的激励模块,用户每日完成投票后获得抽奖机会,可抽取实物奖品和虚拟权益,保持活动期间的持续参与热情。
关于活动整体背景和全局规则,请见 足球季活动全局。
二、功能需求清单
2.1 抽奖机会获取
| 获取方式 | 说明 |
|---|---|
| 每日投票 | 完成当日投票后自动获得 1 次抽奖机会 |
| 分享活动 | 分享活动页面可额外获得抽奖机会,每人每天至多额外获得 5 次 |
- 抽奖机会与投票绑定:必须先完成投票才能抽奖
- 分享额外获得的抽奖机会,不要求当日投票即可使用
抽奖机会生命周期规则
| 机会类型 | 发放触发 | 有效期 | 上限 | 过期处理 |
|---|---|---|---|---|
| 投票机会 | 用户完成当日投票 | 仅当日有效,00:00 重置 | 每日 1 次 | 活动结束后失效 |
| 分享机会 | 用户从活动页面跳转出去 | 活动期内有效,保留至活动结束 | 每日最多发放 5 次;每日最多消耗 5 次 | 活动结束后失效 |
消耗优先级:可用机会 = 投票机会 + 分享机会。扣减时先扣投票机会(当日过期浪费代价小),投票机会为 0 时再扣分享机会。
分享机会保留规则:分享获得的未使用抽奖机会保留至活动结束,不在次日 00:00 清零;但分享机会的发放仍按自然日限制,每人每天最多额外获得 5 次,且单日最多消耗 5 次分享机会。
幂等保证:同一用户当日多次投票只发 1 次机会;当日分享机会达到 5 次上限后不再发放。
2.2 抽奖操作
- 用户进入抽奖区域,消耗 1 次抽奖机会执行抽奖
- 抽奖结果即时展示(中奖/未中奖)
- 中奖后展示奖品信息和领取提示
2.3 奖品类型
| 奖品类型 | 说明 | 数量限制 |
|---|---|---|
| 公仔玩偶 | 限定足球冠军叫叫公仔挂件(实物) | 限量 430 个 |
| 体验课 | 叫叫体验课(虚拟权益) | 不限量 |
| 叫叫口算VIP月卡 | 叫叫口算VIP月卡(虚拟权益) | 激活码池(约 10 万),每用户至多中奖 1 次 |
| 叫叫金片 | 叫叫金片(实物奖品,真金子制作) | 限量(数量待定),每用户至多中奖 1 次 |
- 实物奖品:公仔玩偶(430个)和叫叫金片(数量待定)需严格控制发放节奏,中奖后需收集用户收货地址
- 不限量奖品:体验课无总数限制
- 激活码奖品:叫叫口算VIP月卡通过预置激活码池发放(约 10 万个激活码),激活码耗尽则该奖品不可再中
- 用户维度限制:公仔玩偶、叫叫金片、叫叫口算VIP月卡均为每用户活动期间最多中奖 1 次
2.4 奖品发放
奖品按类型采用不同的发放方式:
| 奖品类型 | 发放方式 | 说明 |
|---|---|---|
| 公仔玩偶(实物) | 中奖后填写收货地址,活动结束后运营人工发货 | 详见 收货地址模块 |
| 叫叫金片(实物) | 中奖后填写收货地址,活动结束后运营人工发货 | 同上 |
| 叫叫口算VIP月卡(虚拟权益) | 中奖后分配激活码 + 自动复制 + 跳转兑换页 | 详见 2.4.1 虚拟权益领取流程 |
| 体验课(虚拟权益) | 用户中奖后点击「立即领取」按钮跳转领取 | 详见 2.4.1 虚拟权益领取流程 |
| 冠军皮肤装扮 | 活动结束后后台手动发放 | 详见 发奖与数据导出模块 |
- 实物奖品不通过系统自动发放,活动结束后运营人员导出数据人工发货
- 虚拟权益奖品由用户自主领取:口算VIP月卡通过激活码发放,体验课通过跳转链接领取
- 详见 发奖与数据导出模块
2.4.1 虚拟权益领取流程
虚拟权益奖品采用用户自主领取方式。口算VIP月卡通过「激活码 + 跳转兑换页」模式发放,体验课通过跳转链接领取。
叫叫口算VIP月卡(激活码模式):
系统预先在数据库中导入约 10 万个激活码。用户中奖后,系统从激活码池中分配一个未使用的激活码给该用户。
领取流程:
- 用户中奖后,在中奖弹窗或「我的奖品」页面看到奖品名称、激活码(可直接复制)及「立即领取」按钮
- 系统自动将激活码复制到用户剪贴板,并提示"激活码已复制"
- 点击「立即领取」按钮,通过 Bridge
openBrowser方法在系统浏览器中打开兑换页面 - 用户在兑换页面中粘贴激活码,自主完成兑换
- 本系统仅负责激活码分配和跳转,兑换流程由目标页面承接,不在本活动范围内
领取链接(固定):
| 奖品 | 领取链接 | 备注 |
|---|---|---|
| 叫叫口算VIP月卡 | https://act.cdssylkj.com/activeCode/index | 已确认 |
技术要点:
- 激活码在中奖时由后端从激活码池原子分配(CAS 取码),确保不重复发放
- 前端收到激活码后自动复制到剪贴板,并通过 Bridge
openBrowser在系统浏览器打开兑换页- 激活码总量约 10 万,耗尽后该奖品自动不可中(概率归入未中奖)
- 已中过口算VIP月卡的用户,其激活码在中奖记录和「我的奖品」中始终可查看和复制
体验课(跳转链接模式):
领取流程:
- 用户中奖后,在中奖弹窗或「我的奖品」页面看到该奖品及「立即领取」按钮
- 点击「立即领取」按钮,跳转到对应的领取页面链接(新页面打开)
- 用户在跳转后的页面中自主完成兑换/领取操作
- 本系统仅负责跳转,后续的兑换流程由目标页面承接,不在本活动范围内
领取链接配置:
| 奖品 | 领取链接 | 备注 |
|---|---|---|
| 体验课 | https://act.tinman.cn/actPage?linkId=12876260&channelNo=1ABFED57C3CE7E4A&bizLinkId=18892 | 已确认,后台可配置 |
技术要点:链接通过后台配置,运营可随时修改。前端「立即领取」按钮读取后台返回的链接地址进行跳转即可。
2.5 限量奖品节奏控制
- 公仔玩偶和叫叫金片为限量奖品,核心目标是保证活动全程都有奖品可抽,避免提前抽完
- 不要求绝对均匀出奖,允许参与高的日子多抽、参与低的日子少抽,自然节奏即可
- 未消耗的奖品配额不浪费,累积到后续天数的奖池中继续抽
- 奖池策略需确保:活动最后一天仍有库存,不可提前抽完
- 后台需支持配置奖品总量、每日注入基准、利用率等参数
2.6 抽奖算法
2.6.1 核心思路:累计释放 + 最终日保留 + 参与人数动态概率
抽奖算法采用「累计释放 + 最终日保留 + 每日上限 + 参与人数动态概率」策略:
- 累计释放:限量奖品按活动进度逐步释放,可抽数量 = 累计已释放量 - 已中奖量
- 最终日保留:活动最后一天前,公仔和金片各保留一部分库存,确保最后一天仍有奖品可抽
- 每日上限:每个限量奖品每天设置中奖上限,防止某个高流量日集中消耗多天库存
- 动态概率:中奖概率根据当前可用量和抽奖人次实时计算,人越多概率越低,人越少概率越高
2.6.2 活动参数
| 参数 | 默认值 | 说明 |
|---|---|---|
| 活动期 | 6 月 15 日 - 7 月 20 日(36 天) | 后台可配置 |
| 公仔玩偶总量 | 430 个 | 后台可配置 |
| 叫叫金片总量 | 60 个 | 后台可配置 |
| 基准抽奖量 | 1000 | 防止低人次时概率过高 |
| 利用率 R | 0.8 | 后台可配置 |
| 单类实物概率上限 P_max | 0.05 | 后台可配置 |
| 公仔权重比例 | 0.6 | 后台可配置 |
| 金片权重比例 | 0.4 | 后台可配置 |
| 公仔每日上限 | 20 | 后台可配置 |
| 金片每日上限 | 3 | 后台可配置 |
| 公仔最后一天保留量 | 20 | 后台可配置;当库存较小时自动按库存收缩 |
| 金片最后一天保留量 | 3 | 后台可配置;当库存较小时自动按库存收缩 |
| 口算 VIP 月卡概率 | 0.03 | 后台可配置,码池耗尽后归零 |
| 体验课概率 | 0.10 | 后台可配置 |
2.6.3 限量奖品释放机制
限量奖品(公仔、金片)不一次性放入可抽池,而是按活动进度累计释放。
关键定义:
- 活动天序:活动第 N 天,首日为 1
- 最后一天前可释放总量:
总库存 - 最后一天保留量 - 累计释放量:
- 活动最后一天前:
floor(最后一天前可释放总量 × 活动天序 / (总天数 - 1)) - 活动最后一天:释放全部剩余库存
- 活动最后一天前:
- 当前可抽数量:
min(累计释放量 - 已中奖量, 每日上限 - 今日已中奖量)
关键规则:
- 非最后一天应用每日上限,超过后该奖品当天不再可抽
- 最后一天释放全部剩余库存,不再用每日上限卡住发放
- 如果活动期内没有完全消耗完,少量库存剩余可接受
- 总库存永远不能超发
2.6.4 参与人数动态概率
中奖概率不是固定值,而是根据当前奖池存量和参与抽奖人次实时计算。
人次基准(双基准取大值):
人次基准 = max(昨日全天抽奖总人次, 今日已抽奖人次)
- 昨日人次作为保底分母,防止凌晨参与人极少时概率畸高
- 今日人次超过昨日时自动切换,保证高峰日概率自适应降低
- 活动第 1 天无昨日数据,使用后台配置的预估日均人次兜底
核心公式:
人次基准 = max(yesterday_draws, today_draws, 保底人次)
目标消耗量 = 当前奖池存量 × 利用率 R(后台可配,默认 0.8)
限量奖品基础概率 = 目标消耗量 / 人次基准
限量奖品最终概率 = min(基础概率 × 奖品权重比例, 单次上限 P_max)
各奖品概率计算规则:
| 奖品类型 | 概率计算方式 | 是否受参与人数影响 |
|---|---|---|
| 公仔玩偶(限量) | min(目标消耗量 × 公仔权重比例 / 人次基准, P_max) | 是:人越多概率越低 |
| 叫叫金片(限量) | min(目标消耗量 × 金片权重比例 / 人次基准, P_max) | 是:人越多概率越低 |
| 体验课(不限量) | 固定概率 W3(后台可配,如 10%) | 否:不限量,概率固定 |
| 叫叫口算VIP月卡(激活码) | 固定概率 W4(后台可配,如 3%);激活码耗尽后概率归零 | 否:固定概率,激活码总量有限 |
| 未中奖(兜底) | 见下方概率归一化规则 | 自动计算,保证总概率 = 100% |
概率安全校验(防止概率总和超过 100%):
运营可独立调整概率参数,但后台保存配置时必须校验最大中奖概率:
最大中奖概率 = P_max × 2 + VIP 概率 + 体验课概率
- 若最大中奖概率
≤ 1,允许保存 - 若最大中奖概率
> 1,拒绝保存并提示运营调低参数 - 管理端需展示概率预览:最大中奖概率、实物奖最大概率、最小未中奖概率
- 系统不自动归一化运营输入的概率,避免保存值与实际概率不一致导致解释困难
概率自适应机制说明:
- 参与人少时:人次基准小(由昨日保底),目标消耗量/小 = 概率高 → 鼓励消耗奖池积累
- 参与人多时:人次基准大(今日超过昨日),目标消耗量/大 = 概率低 → 保护库存不被快速抽完
- 奖池充裕时:当前奖池存量大,目标消耗量大 → 概率自然提高
- 奖池紧张时:当前奖池存量小,目标消耗量小 → 概率自然降低
单次抽奖上限 P_max(后台可配,如 5%):
- 防止在参与极少时概率过高(比如凌晨只有 1 人抽奖时不可能 80% 中公仔)
- 限量奖品单次概率不超过 P_max,超出部分自动截断
不限量奖品的概率:
- 口算VIP月卡使用后台配置的固定概率值(如 3%),不受参与人数影响
- 口算VIP月卡通过激活码池发放(约 10 万个),激活码耗尽后该奖品概率自动归零,归零的概率归入未中奖
- 口算VIP月卡受用户维度限制(每用户最多中 1 次),已中过的用户该奖品概率归零,归零的概率归入未中奖(不重新分配给其他奖品)
2.6.5 抽奖流程
用户发起抽奖请求
│
├─ 1. 幂等校验:检查 requestId 是否已处理,是则返回首次抽奖结果
│
├─ 2. 前置校验
│ ├─ 活动是否进行中? → 否:返回"活动已结束"
│ ├─ 用户是否有抽奖机会? → 否:返回"今日已无抽奖机会"
│ └─ 校验通过 → 扣减 1 次抽奖机会
│
├─ 3. 构建当前可用奖池
│ ├─ 根据累计释放量、已发数量、今日已发数量计算限量奖品可用量
│ └─ 根据用户限次移除已中过的限次奖品
│
├─ 4. 计算各奖品概率
│
├─ 5. 加权随机抽取(按概率命中奖品或"未中奖")
│
├─ 6. 记录抽奖、中奖和机会消耗
│
└─ 7. 返回抽奖结果 + 奖品信息 + 最新活动状态
2.6.6 安全阀:防止库存集中耗尽
| 规则 | 说明 |
|---|---|
| 最终日保留 | 活动最后一天前保留指定数量的公仔和金片,确保最后一天仍有奖品 |
| 每日抽取上限 | 非最后一天每个限量奖品每天最多发放配置数量,超过后当天不再可抽 |
| 最后一天释放 | 最后一天释放全部剩余库存,不再用每日上限卡住,尽量减少剩余 |
| 单次概率上限 | 限量奖品单次中奖概率不超过 P_max(如 5%),防止低参与时概率过高 |
| 总库存硬约束 | 已中奖数量不能超过总库存 |
2.6.7 用户维度限次逻辑
| 奖品 | 用户限次规则 | 实现方式 |
|---|---|---|
| 公仔玩偶 | 每用户活动期间最多中 1 个 | 查询用户中奖记录,已中过则从奖池移除 |
| 叫叫金片 | 每用户活动期间最多中 1 个 | 查询用户中奖记录,已中过则从奖池移除 |
| 叫叫口算VIP月卡 | 每用户活动期间最多中 1 次 | 查询用户中奖记录,已中过则从奖池移除 |
| 体验课 | 无限制 | 始终参与抽奖 |
2.6.8 防刷与安全
| 措施 | 说明 |
|---|---|
| 服务端校验 | 抽奖机会校验、扣减、开奖全部在服务端完成,前端仅展示结果 |
| 幂等设计 | 每次抽奖请求携带唯一 requestId,同一用户同一 requestId 重试返回首次结果,不重复扣机会 |
| 事务保护 | 抽奖机会扣减、抽奖记录、中奖记录在后端同一次 repository update 内完成,MySQL 模式下由事务和更新锁保护 |
| 库存保护 | 每次抽奖前根据最新已发数量、累计释放量、每日上限重新计算可用奖池,防止超发 |
| 参数保护 | 后台保存配置时校验概率范围、最终日保留量、每日上限和最大中奖概率 |
三、业务规则与约束
3.1 抽奖节奏控制
| 规则 | 说明 |
|---|---|
| 活动结束前奖品策略 | 限量奖品(公仔、叫叫金片)必须确保活动结束前不抽完,最后一天仍有库存 |
| 每日抽奖次数 | 每日投票后获得 1 次,分享活动页面最多额外 +5 次(每人每天上限,次日重置) |
| 抽奖机会是否累积 | 未使用的抽奖机会保留至活动结束 |
| 重复中奖 | 公仔每用户限 1 个,金片每用户限 1 个,口算VIP月卡每用户限 1 次,体验课不限 |
3.2 奖品概率与库存
- 限量奖品(公仔、叫叫金片)采用「滚动奖池」机制,每天动态注入,未消耗的累积到后续天数(详见 2.6.3)
- 中奖概率与参与人数动态关联:分母取 max(昨日人次, 今日人次),人越多概率越低(详见 2.6.4)
- 不限量奖品(体验课)使用后台配置的固定权重,不受参与人数影响
- 激活码奖品(口算VIP月卡)使用固定概率,激活码池耗尽后概率归零
- 公仔、金片、口算VIP月卡均受用户维度限制,每用户最多中 1 次(详见 2.6.7)
3.3 前置条件
- 用户必须已完成当日投票,才有抽奖入口
- 分享额外机会无需投票前提
3.4 边界场景
| 场景 | 处理方式 |
|---|---|
| 用户无抽奖机会时点击抽奖 | 提示"今日已无抽奖机会,完成投票可获得" |
| 限量奖品奖池当日耗尽 | 该奖品当天不再可抽;次日注入后恢复(若总库存仍 > 0) |
| 限量奖品触发每日抽取上限 | 该奖品当天不再可抽,次日恢复 |
| 限量奖品总库存耗尽 | 该奖品永久从奖池移除,活动剩余天数内不再出现 |
| 网络中断导致抽奖请求失败 | 提示"抽奖失败,请重试",不消耗抽奖机会 |
| 用户中奖后未查看奖品信息 | 奖品记录保存,后台可查 |
| 活动已结束,用户尝试抽奖 | 入口关闭或提示"活动已结束" |
| 用户已中过叫叫口算VIP月卡 | 该用户后续抽奖不再中此奖品 |
| 用户已中过公仔玩偶 | 该用户后续抽奖不再中公仔(其他奖品不受影响) |
| 用户已中过叫叫金片 | 该用户后续抽奖不再中金片(其他奖品不受影响) |
| 用户当日分享机会已用完 | 提示"今日分享奖励已领完,明天再来吧" |
| 并发重复提交 | 幂等设计 + 分布式锁,只处理一次,返回缓存结果 |
2.6.9 参数配置方式
抽奖模块中所有标注“后台可配”的参数(利用率 R、P_max、公仔/金片权重比例、不限量奖品固定概率、保底人次、体验课/口算VIP月卡概率等)均需支持在活动后台配置。运营或开发可在后台调整后保存生效,配置变更需记录更新时间;若后端需要额外保护,可限制参数范围并拒绝不合法配置。