PRD-010 抽奖与寻宝
文档编号:PRD-010
版本:v1.0
状态:已通过
适用端:服务端 / 客户端
关联模块:SessionServer/Lottery.cpp、BillServer、tbl/LotteryPool.xml
1. 背景与目标
抽奖是主要的付费/消耗玩法,必须保证概率透明、结果可审计、保底可复现。当前存在十连结果与单抽概率不一致、保底计数丢失、客户端可预知结果等问题。本 PRD 统一种奖池、概率、保底、十连与公示。
2. 核心规则
2.1 奖池
| 类型 | 消耗 | 保底 | 说明 |
|---|---|---|---|
| 普通寻宝 | 金币 | 无 | 材料/消耗品 |
| 高级寻宝 | 元宝 | 30 抽必出稀有 | 时装/坐骑碎片 |
| 限定寻宝 | 限定道具 | 90 抽必出大奖 | 活动期限 |
- 奖池分权重组,组内按权重随机。
- 十连至少 1 个稀有(按配置保底)。
- 限定奖池结束,保底次数按配置继承/清零。
2.2 概率与保底
- 单抽概率公示在抽奖界面,可展开查看完整列表。
- 保底计数按角色 + 奖池记录,跨天不清空。
- 出大奖后保底计数重置。
- 十连按 10 次独立随机 + 保底修正,不是一次随机。
2.3 结果与发放
- 结果由服务端生成并落库,客户端只播放动画。
- 物品直接入包,背包满转邮件。
- 抽奖记录保留 180 天,可按角色/时间查询。
- 抽奖动画不提前暴露结果,避免抓包/改包。
3. 服务端设计
3.1 数据结构
struct LotteryPool {
uint32_t pool_id;
std::vector<LotteryItem> items; // item_id, weight, rare_level
uint32_t pity_threshold;
uint32_t ten_pull_guarantee;
};
struct LotteryState {
uint64_t role_id;
uint32_t pool_id;
uint32_t pity_count;
uint32_t total_count;
};
3.2 协议
| 方向 | 协议 | 说明 |
|---|---|---|
| C→S | CS_LotteryDraw | 单抽/十连 |
| C→S | CS_LotteryInfo | 奖池/概率/保底 |
| S→C | SC_LotteryResult | 结果列表(全部生成后下发) |
| S→C | SC_LotteryInfo | 保底/剩余次数 |
3.3 抽奖流程
CS_LotteryDraw(pool_id, count)
→ 校验活动时间/消耗/次数
→ 扣货币(事务)
→ 循环 count 次:按权重随机 → 更新保底
→ 十连保底修正
→ 生成结果列表 → 发放物品/邮件
→ 写 lottery_log(种子、结果、保底前后)
→ 返回 SC_LotteryResult
3.4 审计与风控
- 记录随机种子、奖池版本、概率表版本。
- 同一
request_id幂等,不重复扣费/发奖。 - 异常高频抽奖触发风控,人工复核。
- 概率表变更需版本号,历史记录可追溯。
4. 客户端设计
- 抽奖界面:奖池展示、单抽/十连按钮、消耗、保底进度。
- 概率公示:可展开完整概率表,支持搜索。
- 动画:翻牌/开箱,十连逐个展示,可跳过。
- 结果汇总:稀有物品高亮,可分享到聊天。
- 保底提示:剩余 N 抽必出。
5. 配置
LotteryPool.xml:pool_id、item_id、weight、rare_level、pity_threshold、ten_pull_guarantee、start_time、end_time、cost_type、cost_value。
6. 边界与异常
| 场景 | 处理 |
|---|---|
| 货币不足 | 拒绝,不扣费 |
| 背包满 | 物品转邮件 |
| 十连中服务器断线 | 结果已落库,重连补发/查询 |
| 保底计数异常 | 以 DB 为准,修正内存 |
| 奖池结束瞬间请求 | 以服务端时间校验,拒绝越界 |
| 重复请求 | 幂等 |
7. 验收标准
- 单抽/十连概率、保底、稀有保底符合配置。
- 结果与扣费事务一致,无扣费不发奖。
- 断线/重复请求不重复扣费/发奖。
- 概率公示与实际一致。
- 抽奖日志可按 request_id 审计。