跳到主要内容

抽奖模块

一、模块定位

抽奖模块是足球季活动的激励模块,用户每日完成投票后获得抽奖机会,可抽取实物奖品和虚拟权益,保持活动期间的持续参与热情。

关于活动整体背景和全局规则,请见 足球季活动全局


二、功能需求清单

2.1 抽奖机会获取

获取方式说明
每日投票完成当日投票后自动获得 1 次抽奖机会
分享活动分享活动页面可额外获得抽奖机会,每人每天至多额外获得 5 次
  • 抽奖机会与投票绑定:必须先完成投票才能抽奖
  • 分享额外获得的抽奖机会,不要求当日投票即可使用

抽奖机会生命周期规则

机会类型发放触发有效期上限过期处理
投票机会用户完成当日投票仅当日有效,00:00 重置每日 1 次活动结束后失效
分享机会用户从活动页面跳转出去仅当日有效,00:00 重置每日最多 5 次活动结束后失效

消耗优先级:可用机会 = 投票机会 + 分享机会。扣减时先扣投票机会(当日过期浪费代价小),投票机会为 0 时再扣分享机会。

幂等保证:同一用户当日多次投票只发 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 万个激活码。用户中奖后,系统从激活码池中分配一个未使用的激活码给该用户。

领取流程

  1. 用户中奖后,在中奖弹窗或「我的奖品」页面看到奖品名称、激活码(可直接复制)及「立即领取」按钮
  2. 系统自动将激活码复制到用户剪贴板,并提示"激活码已复制"
  3. 点击「立即领取」按钮,通过 Bridge openBrowser 方法在系统浏览器中打开兑换页面
  4. 用户在兑换页面中粘贴激活码,自主完成兑换
  5. 本系统仅负责激活码分配和跳转,兑换流程由目标页面承接,不在本活动范围内

领取链接(固定)

奖品领取链接备注
叫叫口算VIP月卡https://act.cdssylkj.com/activeCode/index已确认

技术要点

  • 激活码在中奖时由后端从激活码池原子分配(CAS 取码),确保不重复发放
  • 前端收到激活码后自动复制到剪贴板,并通过 Bridge openBrowser 在系统浏览器打开兑换页
  • 激活码总量约 10 万,耗尽后该奖品自动不可中(概率归入未中奖)
  • 已中过口算VIP月卡的用户,其激活码在中奖记录和「我的奖品」中始终可查看和复制

体验课(跳转链接模式)

领取流程

  1. 用户中奖后,在中奖弹窗或「我的奖品」页面看到该奖品及「立即领取」按钮
  2. 点击「立即领取」按钮,跳转到对应的领取页面链接(新页面打开)
  3. 用户在跳转后的页面中自主完成兑换/领取操作
  4. 本系统仅负责跳转,后续的兑换流程由目标页面承接,不在本活动范围内

领取链接配置

奖品领取链接备注
体验课待补充链接格式相同,后续确认后填入

技术要点:链接通过后台配置,运营可随时修改。前端「立即领取」按钮读取后台返回的链接地址进行跳转即可。

2.5 限量奖品节奏控制

  • 公仔玩偶叫叫金片为限量奖品,核心目标是保证活动全程都有奖品可抽,避免提前抽完
  • 不要求绝对均匀出奖,允许参与高的日子多抽、参与低的日子少抽,自然节奏即可
  • 未消耗的奖品配额不浪费,累积到后续天数的奖池中继续抽
  • 奖池策略需确保:活动最后一天仍有库存,不可提前抽完
  • 后台需支持配置奖品总量、每日注入基准、利用率等参数

2.6 抽奖算法

2.6.1 核心思路:滚动奖池 + 参与人数动态概率

抽奖算法采用「滚动奖池 + 参与人数动态概率」策略:

  • 滚动奖池:限量奖品每天按计算注入一定数量到「可抽奖池」,当天未抽完的保留在池中,累积到后续天数继续抽
  • 动态概率:中奖概率不是写死的,而是根据当前奖池存量和参与抽奖人次实时计算。参与人越多,单次中奖概率自动降低;参与人越少,概率自动提高
  • 自然节奏:人多的日子多抽、人少的日子少抽,避免库存过早耗尽,同时减少活动结束后大量奖品剩余

2.6.2 活动参数

参数
活动期6 月 15 日 - 7 月 20 日(36 天)
公仔玩偶总量430 个
叫叫金片总量待定(算法相同,以 N 表示)
利用率 R(后台可配)0.8(即每天希望消耗当日注入量的 80%)
公仔权重比例(后台可配)0.6(公仔占限量奖品概率份额的 60%)
金片权重比例(后台可配)0.4(金片占限量奖品概率份额的 40%)

2.6.3 滚动奖池机制

限量奖品(公仔、金片)各维护一个「当前可抽奖池」,池中的数量 = 累积未抽完的奖品。

关键定义

  • 剩余总库存 = 奖品总配额 - 已被抽走的数量(不含奖池中未抽走的)。即本次活动该奖品尚未发放给用户的剩余额度

每日注入规则

  • 每天 00:00,系统计算当日应注入数量:当日注入量 = max(1, floor(剩余总库存 / 剩余天数))
  • max(1, ...) 保底注入:即使剩余库存 < 剩余天数(如库存剩 5 个、还剩 10 天),每天也至少注入 1 个,避免奖池长时间为零
  • 注入后:当前奖池 = 昨日剩余 + 当日注入量
  • 剩余总库存和剩余天数相应更新
  • 当剩余总库存为 0 时,不再注入,该奖品永久移除

举例(公仔玩偶,总量 430,36 天)

天数剩余库存剩余天数当日注入奖池起始说明
第1天430361111初始注入
第2天42335129第1天抽了5个,剩余4个累积,注入12
第10天350261328累积较多,奖池充裕
第30天8061322库存减少但天数也少
第36天1211212最后一天,剩余全部注入

上表为示例,实际数据取决于每天的抽奖消耗。

关键规则

  • 未消耗累积:当天没抽完的奖品留在池中,不会浪费
  • 动态注入:注入量 = max(1, 剩余库存 / 剩余天数),自动适应当前消耗节奏,且保底每天至少注入 1 个
  • 总库存硬约束:奖池数量永远不超过剩余总库存
  • 最后一天保底:最后一天 剩余天数=1,注入量=全部剩余库存,确保最后一天有奖品可抽

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%)

计算完所有奖品概率后,可能出现概率之和 > 1 的情况(如凌晨参与极少 + 奖池充裕 + 不限量奖品固定概率叠加)。处理规则:

  1. 计算 所有奖品概率之和 S
  2. S ≤ 1:未中奖概率 = 1 - S,正常出奖
  3. S > 1:采用分步归一化,避免限量奖品被 P_max 截断和缩放双重惩罚:
    • a. 锁定已达 P_max 上限的限量奖品,其概率固定为 P_max
    • b. 计算剩余概率空间:剩余空间 = 1 - 已锁定奖品概率之和
    • c. 未锁定的奖品(含不限量奖品和未达 P_max 的限量奖品)按比例缩放至填满剩余空间
    • d. 缩放后若有限量奖品概率超过 P_max,截断至 P_max,超出部分归入未中奖
    • e. 未中奖概率 = 0(概率已填满)

此机制确保限量奖品在 P_max 允许范围内优先分配概率份额,不因归一化而被过度压缩。

概率自适应机制说明

  • 参与人少时:人次基准小(由昨日保底),目标消耗量/小 = 概率高 → 鼓励消耗奖池积累
  • 参与人多时:人次基准大(今日超过昨日),目标消耗量/大 = 概率低 → 保护库存不被快速抽完
  • 奖池充裕时:当前奖池存量大,目标消耗量大 → 概率自然提高
  • 奖池紧张时:当前奖池存量小,目标消耗量小 → 概率自然降低

单次抽奖上限 P_max(后台可配,如 5%):

  • 防止在参与极少时概率过高(比如凌晨只有 1 人抽奖时不可能 80% 中公仔)
  • 限量奖品单次概率不超过 P_max,超出部分自动截断

不限量奖品的概率

  • 口算VIP月卡使用后台配置的固定概率值(如 3%),不受参与人数影响
  • 口算VIP月卡通过激活码池发放(约 10 万个),激活码耗尽后该奖品概率自动归零,归零的概率归入未中奖
  • 口算VIP月卡受用户维度限制(每用户最多中 1 次),已中过的用户该奖品概率归零,归零的概率归入未中奖(不重新分配给其他奖品)

2.6.5 抽奖流程

用户发起抽奖请求

├─ 1. 幂等校验:检查 request_id 是否已处理,是则返回缓存结果

├─ 2. 前置校验
│ ├─ 活动是否进行中? → 否:返回"活动已结束"
│ ├─ 用户是否有抽奖机会? → 否:返回"今日已无抽奖机会"
│ └─ 校验通过 → 扣减 1 次抽奖机会

├─ 3. 获取抽奖锁(同一用户分布式锁,保证概率计算与扣减原子性)

├─ 4. 构建当前可用奖池(根据奖池存量、用户限次动态筛选)

├─ 5. 计算各奖品概率(根据 max(昨日人次,今日人次) 实时计算,见 2.6.4)

├─ 6. 加权随机抽取(按概率命中奖品或"未中奖")

├─ 7. 中奖后处理(仍在锁内)
│ ├─ 限量奖品:原子扣减奖池(CAS 操作,失败则重试步骤 5-7,最多 3 次)
│ ├─ 若重试后仍失败(如 daily_cap 已满或奖池耗尽):视为未中奖,归还抽奖机会
│ ├─ 扣减总库存 1 个
│ ├─ 记录中奖信息(用户ID、奖品类型、时间戳)
│ └─ 更新用户维度中奖记录

├─ 8. 释放抽奖锁

└─ 9. 返回抽奖结果 + 奖品信息

2.6.6 安全阀:防止库存集中耗尽

滚动奖池可能在极端情况下(如某天参与量突然激增)被快速抽空。安全阀规则:

规则说明
每日抽取上限每个限量奖品每天最多被抽走 daily_cap = max(3, 当日注入量 × 2) 个,超过后该奖品当天不再可抽。活动最后一天特殊处理daily_cap_last = ceil(当日注入量 / 2),防止剩余库存被一次性抽空
单次概率上限限量奖品单次中奖概率不超过 P_max(如 5%),防止低参与时概率过高
总库存硬约束奖池数量永远不会超过剩余总库存,双重保护

daily_cap 设 max(3, ...) 下限是为了避免注入量较小时 cap 过低(如注入 1 个时 cap=2 太少)。例如某天突然 10 万人参与,即使概率自适应降低,仍可能有较多中奖。daily_cap 作为绝对上限保证不会一天抽光多天的量。

最后一天 daily_cap 改用 ceil(注入量 / 2) 是因为最后一天注入量 = 全部剩余库存,若沿用 注入量 × 2 则 cap 远超实际库存,安全阀形同虚设。

2.6.7 用户维度限次逻辑

奖品用户限次规则实现方式
公仔玩偶每用户活动期间最多中 1 个查询用户中奖记录,已中过则从奖池移除
叫叫金片每用户活动期间最多中 1 个查询用户中奖记录,已中过则从奖池移除
叫叫口算VIP月卡每用户活动期间最多中 1 次查询用户中奖记录,已中过则从奖池移除
体验课无限制始终参与抽奖

2.6.8 防刷与安全

措施说明
服务端校验抽奖机会校验、扣减、开奖全部在服务端完成,前端仅展示结果
幂等设计每次抽奖请求携带唯一 request_id,防止重复提交
并发控制同一用户同时只能有 1 个抽奖请求在处理中(加分布式锁)
概率与扣减原子性概率计算、随机抽取、奖池扣减三步在同一把锁内完成,读取加锁后的最新奖池存量;CAS 扣减失败自动重试(最多 3 次)
奖池原子扣减限量奖品从奖池扣减通过原子操作完成,防止超发
异常回滚抽奖过程中任何环节失败,回滚已扣减的抽奖机会和奖池数量

三、业务规则与约束

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月卡概率等)均通过后端配置文件或环境变量设定,不需要管理后台界面。修改参数时由开发人员在后端配置中调整后重新部署即可。