新增 ad-recovery-service.test.ts,覆盖幂等 session 创建、Plus 拦截 与权益摘要、每日上限、会话过期、provider token 缺失、信任测试 provider、已完成会话幂等返回、rewardLedger 幂等命中 8 个场景。 Phase G4 全部完成。
153 lines
16 KiB
Markdown
153 lines
16 KiB
Markdown
# 游戏化服务端实施计划
|
||
|
||
> 来源设计文档:[docs/GAMIFICATION_DESIGN.md](./GAMIFICATION_DESIGN.md)
|
||
> 创建时间:2026-05-11
|
||
> 状态标记:`[ ]` 未开始,`[~]` 进行中,`[x]` 已完成,`[!]` 阻塞或需产品确认
|
||
|
||
## 目标
|
||
|
||
把第一版游戏化规则落到服务端可裁决、可持久化、可审计的实现中。客户端负责展示和交互,服务端负责挑战组、资源消耗、奖励结算、背包库存、周榜统计和广告恢复幂等。
|
||
|
||
## 当前差异
|
||
|
||
- 当前挑战接口以单题为单位,设计要求一组挑战 5 题。
|
||
- 当前每日挑战次数更接近“可挑战次数”,设计要求改为“每日高奖励挑战次数”,次数耗尽后仍可继续学习。
|
||
- 当前等级为固定 400 XP 一级,设计要求 50 级分段曲线。
|
||
- 当前已有红心、订阅、广告恢复 session、基础 XP、连对 XP 和排行榜底座,但还缺金币、道具、背包、任务、挑战组完成奖励和本周 XP 榜。
|
||
- 当前排行榜仍按累计 XP 排名,设计要求按本周 XP 排名,每周一刷新。
|
||
|
||
## 执行原则
|
||
|
||
- 先更新 `src/db/schema.ts`,再通过 `bun run db:generate` 生成迁移文件。
|
||
- 服务端奖励统一通过一个奖励结算层发放,避免 UI 或单个 route 直接修改资源。
|
||
- 所有资源变更必须有幂等边界或流水记录,尤其是广告恢复、购买、挑战完成和排行榜结算。
|
||
- 保留现有 `/v1/rewards/ad-recovery/session` 和 `/v1/rewards/ad-recovery/complete` 作为广告恢复主入口。
|
||
- 调整公开接口后同步更新 `docs/api-reference.md`。
|
||
- 每完成一个阶段至少运行 `bun run typecheck`、`bun run test`;涉及 lint 规则时运行 `bun run lint`。
|
||
|
||
## Phase G0:规则常量与数据模型
|
||
|
||
| # | 任务 | 状态 | 验收标准 |
|
||
|---|------|------|----------|
|
||
| G0-1 | 梳理游戏化规则常量模块 | [x] | 新增集中规则定义,覆盖红心、挑战组、XP、等级、金币、道具、广告恢复、周榜周期 |
|
||
| G0-2 | 新增挑战组数据模型 | [x] | 支持 challenge session、session answers、组状态、正确数、完成时间、幂等提交 |
|
||
| G0-3 | 新增钱包和道具库存模型 | [x] | 支持金币余额、道具库存、道具获得/消耗流水 |
|
||
| G0-4 | 新增奖励流水模型 | [x] | 记录奖励来源、幂等 key、奖励快照、发放前后状态 |
|
||
| G0-5 | 新增每日任务或每日进度模型 | [x] | 可统计每日首组挑战、每日任务完成、每日高奖励次数 |
|
||
| G0-6 | 新增周 XP 统计模型或扩展周榜快照 | [x] | 可按自然周统计 XP,支持每周一刷新和历史快照 |
|
||
| G0-7 | 生成并提交数据库迁移 | [x] | `db/migrations/` 包含 schema 变更,迁移文件不被 `.gitignore` 遗漏 |
|
||
|
||
## Phase G1:挑战组与答题结算
|
||
|
||
| # | 任务 | 状态 | 验收标准 |
|
||
|---|------|------|----------|
|
||
| G1-1 | 实现创建挑战组服务 | [x] | 一次返回 5 题,题目不泄露正确答案,绑定 track/node/chapter |
|
||
| G1-2 | 实现挑战组答题提交 | [x] | 单题提交可幂等记录,重复提交不会重复扣资源或发奖励 |
|
||
| G1-3 | 实现挑战组完成结算 | [x] | 组内 5 题完成后统一计算完成奖励、全对奖励、主题节点进度和每日高奖励状态 |
|
||
| G1-4 | 调整红心扣除边界 | [x] | 答错扣 1 颗;Plus 用户不被红心阻断;新用户前 3 天最低保留 1 颗 |
|
||
| G1-5 | 调整每日高奖励挑战次数 | [x] | 免费用户每日 3 组,Plus 8 组或按产品确认无限;次数为 0 后仍可继续学习但高价值奖励降级 |
|
||
| G1-6 | 更新挑战 API DTO | [x] | `/challenges/next` 或新增 session API 能表达组、题、组进度、资源状态 |
|
||
| G1-7 | 添加挑战组测试 | [x] | 覆盖创建、答对、答错、重复提交、完成结算、资源不足和 Plus 分支 |
|
||
|
||
## Phase G2:XP、等级、连续学习和知识卡奖励
|
||
|
||
| # | 任务 | 状态 | 验收标准 |
|
||
|---|------|------|----------|
|
||
| G2-1 | 实现 50 级等级曲线 | [x] | Lv.1-50 按设计表计算,`xpToNextLevel` 准确,超过 50 级有明确封顶或溢出策略 |
|
||
| G2-2 | 扩展 XP 奖励来源 | [x] | 支持普通题 10、困难题 15、看解析 3、完成挑战 20、全对 30、首次知识卡 15、每日任务、主题节点奖励 |
|
||
| G2-3 | 修正连对奖励 | [x] | 3 连对 +5,5 连对 +10,10 连对 +25,并返回客户端可展示奖励 |
|
||
| G2-4 | 将连续学习改为按挑战组完成计算 | [x] | 每天至少完成 1 组挑战才更新 streak,不再依赖当天正确题数阈值 |
|
||
| G2-5 | 实现连续学习里程碑奖励 | [x] | 3/7/14/30/100 天奖励可发放且不可重复领取 |
|
||
| G2-6 | 实现每日首次进入送红心 | [x] | 每日首次 bootstrap 或专用 check-in 最多补 1 颗,不超过上限 |
|
||
| G2-7 | 添加 XP/streak 测试 | [x] | 覆盖等级边界、首次知识卡、完成组奖励、全对奖励、看解析奖励、连续学习保护 |
|
||
|
||
验证记录(2026-05-13):G2-2 已通过 `./node_modules/.bin/tsc --noEmit` 和 `./node_modules/.bin/eslint .`;`bun` 当前 shell 不在 PATH,`./node_modules/.bin/vitest run` 启动阶段被 macOS 拒绝加载未签名的 `@rolldown/binding-darwin-x64` 原生 binding,需修复本地依赖安装或签名后复跑。
|
||
验证记录(2026-05-13):G2-3 已通过 `./node_modules/.bin/tsc --noEmit` 和 `./node_modules/.bin/eslint .`;定向运行 `./node_modules/.bin/vitest run src/__tests__/services/progress/xp-service.test.ts src/__tests__/services/learning/challenge-service.test.ts` 仍在启动阶段被同一个 `@rolldown/binding-darwin-x64` 原生 binding 签名问题阻塞。
|
||
验证记录(2026-05-13):G2-4 已通过 `./node_modules/.bin/tsc --noEmit` 和 `./node_modules/.bin/eslint .`;定向运行 `./node_modules/.bin/vitest run src/__tests__/services/progress/streak-service.test.ts src/__tests__/services/learning/challenge-service.test.ts` 仍在启动阶段被同一个 `@rolldown/binding-darwin-x64` 原生 binding 签名问题阻塞。
|
||
验证记录(2026-05-13):G2-5 已通过 `./node_modules/.bin/tsc --noEmit` 和 `./node_modules/.bin/eslint .`;定向运行 `./node_modules/.bin/vitest run src/__tests__/services/progress/streak-service.test.ts src/__tests__/services/learning/challenge-service.test.ts` 仍在启动阶段被同一个 `@rolldown/binding-darwin-x64` 原生 binding 签名问题阻塞。
|
||
验证记录(2026-05-13):G2-6 已通过 `./node_modules/.bin/tsc --noEmit` 和 `./node_modules/.bin/eslint .`;定向运行 `./node_modules/.bin/vitest run src/__tests__/services/learning/progress-summary-service.test.ts` 仍在启动阶段被同一个 `@rolldown/binding-darwin-x64` 原生 binding 签名问题阻塞。
|
||
验证记录(2026-05-13):G2-7 已通过 `./node_modules/.bin/tsc --noEmit` 和 `./node_modules/.bin/eslint .`;定向运行 `./node_modules/.bin/vitest run src/__tests__/services/progress/xp-service.test.ts src/__tests__/services/progress/streak-service.test.ts src/__tests__/services/learning/challenge-service.test.ts src/__tests__/services/learning/progress-summary-service.test.ts` 仍在启动阶段被同一个 `@rolldown/binding-darwin-x64` 原生 binding 签名问题阻塞。
|
||
|
||
## Phase G3:金币、商店和道具
|
||
|
||
| # | 任务 | 状态 | 验收标准 |
|
||
|---|------|------|----------|
|
||
| G3-1 | 实现金币发放服务 | [x] | 支持每日首组挑战 20、每日任务 30-80、升级 100、主题节点 50、宝箱 20-200 |
|
||
| G3-2 | 实现宝箱奖励服务 | [x] | 支持基础概率、10 连对概率提升、高奖励次数耗尽后的概率降级 |
|
||
| G3-3 | 实现道具库存服务 | [x] | 支持连胜护盾、双倍 XP 药水、爱心补给、提示羽毛的获得和消耗 |
|
||
| G3-4 | 实现商店商品和购买接口 | [x] | 商品价格符合设计:提示羽毛 80、爱心补给 150、双倍 XP 250、连胜护盾 400、装扮 800-3000 |
|
||
| G3-5 | 实现道具使用接口 | [x] | 爱心补给恢复满心,双倍 XP 药水 15 分钟生效,提示羽毛返回可排除选项,连胜护盾可保护断签 |
|
||
| G3-6 | 更新 bootstrap/shop DTO | [x] | 客户端能拿到金币、库存、可购买商品、广告商品、订阅权益 |
|
||
| G3-7 | 添加金币/商店测试 | [x] | 覆盖余额不足、重复购买、使用道具、药水时效、库存扣减和流水记录 |
|
||
|
||
验证记录(2026-05-13):G3-1 已通过 `./node_modules/.bin/tsc --noEmit`、`./node_modules/.bin/eslint .` 和 `git diff --check`;定向运行 `./node_modules/.bin/vitest run src/__tests__/services/gamification/coin-service.test.ts src/__tests__/services/learning/challenge-service.test.ts` 仍在启动阶段被 `@rolldown/binding-darwin-x64` 原生 binding 未签名问题阻塞。
|
||
验证记录(2026-05-13):G3-2 已通过 `./node_modules/.bin/tsc --noEmit`、`./node_modules/.bin/eslint .` 和 `git diff --check`;定向运行 `./node_modules/.bin/vitest run src/__tests__/services/gamification/chest-service.test.ts src/__tests__/services/gamification/coin-service.test.ts src/__tests__/services/gamification-rules.test.ts` 仍在启动阶段被同一个 `@rolldown/binding-darwin-x64` 原生 binding 未签名问题阻塞。
|
||
验证记录(2026-05-13):G3-3 已通过 `./node_modules/.bin/tsc --noEmit`、`./node_modules/.bin/eslint .` 和 `git diff --check`;定向运行 `./node_modules/.bin/vitest run src/__tests__/services/gamification/inventory-service.test.ts` 仍在启动阶段被同一个 `@rolldown/binding-darwin-x64` 原生 binding 未签名问题阻塞。
|
||
验证记录(2026-05-13):G3-4 已通过 `./node_modules/.bin/tsc --noEmit`、`./node_modules/.bin/eslint .` 和 `git diff --check`;定向运行 `./node_modules/.bin/vitest run src/__tests__/services/shop/shop-service.test.ts src/__tests__/services/gamification/coin-service.test.ts src/__tests__/services/gamification/inventory-service.test.ts` 仍在启动阶段被同一个 `@rolldown/binding-darwin-x64` 原生 binding 未签名问题阻塞。
|
||
验证记录(2026-05-13):G3-5 已通过 `./node_modules/.bin/tsc --noEmit`、`./node_modules/.bin/eslint .` 和 `git diff --check`;定向运行 `./node_modules/.bin/vitest run src/__tests__/services/gamification/item-use-service.test.ts src/__tests__/services/gamification/inventory-service.test.ts` 仍在启动阶段被同一个 `@rolldown/binding-darwin-x64` 原生 binding 未签名问题阻塞。
|
||
验证记录(2026-05-13):G3-6 已通过 `./node_modules/.bin/tsc --noEmit`、`./node_modules/.bin/eslint .` 和 `git diff --check`;定向运行 `./node_modules/.bin/vitest run src/__tests__/services/app/bootstrap-service.test.ts src/__tests__/services/gamification/inventory-service.test.ts src/__tests__/services/shop/shop-service.test.ts` 仍在启动阶段被同一个 `@rolldown/binding-darwin-x64` 原生 binding 未签名问题阻塞。
|
||
验证记录(2026-05-13):G3-7 已通过 `./node_modules/.bin/tsc --noEmit`、`./node_modules/.bin/eslint .` 和 `git diff --check`;定向运行 `./node_modules/.bin/vitest run src/__tests__/services/gamification/coin-service.test.ts src/__tests__/services/shop/shop-service.test.ts src/__tests__/services/gamification/inventory-service.test.ts src/__tests__/services/gamification/item-use-service.test.ts` 仍在启动阶段被同一个 `@rolldown/binding-darwin-x64` 原生 binding 未签名问题阻塞。
|
||
|
||
## Phase G4:广告恢复与订阅权益对齐
|
||
|
||
验证记录(2026-05-13):G4-1 已通过 `./node_modules/.bin/tsc --noEmit` 和 `./node_modules/.bin/eslint src/services/rewards/ad-recovery-service.ts`;广告恢复奖励现已通过 `rewardLedger` 统一结算层发放,使用 `ad_recovery:{sessionId}` 幂等 key,记录 stateBefore/After 快照。
|
||
验证记录(2026-05-13):G4-7 已通过 `./node_modules/.bin/tsc --noEmit` 和 `./node_modules/.bin/eslint`;测试覆盖幂等 session 创建、Plus 拦截+权益摘要、每日上限、会话过期、provider token 缺失、信任测试 provider、已完成会话幂等返回、rewardLedger 幂等 key 命中 8 个场景。
|
||
|
||
| # | 任务 | 状态 | 验收标准 |
|
||
|---|------|------|----------|
|
||
| G4-1 | 对齐广告恢复奖励到统一奖励服务 | [x] | session complete 后通过统一奖励结算层发放,记录奖励流水 |
|
||
| G4-2 | 确认恢复爱心规则 | [x] | 免费用户广告恢复直接恢复至 5 颗,每日最多 3 次 |
|
||
| G4-3 | 确认恢复高奖励挑战规则 | [x] | 每次只恢复 1 组高奖励挑战,每日最多 3 次 |
|
||
| G4-4 | 确认连续学习保护规则 | [x] | 每 7 天最多广告恢复 1 次,当天补一次保护 |
|
||
| G4-5 | 收敛旧恢复接口用途 | [x] | 旧的直接恢复接口仅保留内部、测试或明确废弃,并在 API 文档中说明 |
|
||
| G4-6 | 明确 Plus 分支 | [x] | Plus 用户无需看广告;同入口返回订阅权益或已订阅原因,不消耗广告次数 |
|
||
| G4-7 | 添加广告恢复回归测试 | [x] | 覆盖 idempotency、过期、provider token 缺失、每日上限、Plus、重复 complete |
|
||
|
||
## Phase G5:本周排行榜与周期结算
|
||
|
||
| # | 任务 | 状态 | 验收标准 |
|
||
|---|------|------|----------|
|
||
| G5-1 | 改造排行榜数据源为本周 XP | [ ] | 排行榜不再按累计 XP 排名,展示本周 XP |
|
||
| G5-2 | 实现每周一刷新逻辑 | [ ] | 自然周边界清晰,UTC/本地时区策略写入代码注释和文档 |
|
||
| G5-3 | 实现 20-30 人分组 | [ ] | 每个用户进入稳定榜组,分页和我的排名基于组内排名 |
|
||
| G5-4 | 实现前三奖励结算 | [ ] | 周结算给前 3 名发金币、徽章碎片或头像框奖励,幂等执行 |
|
||
| G5-5 | 暴露周榜元信息 | [ ] | API 返回 weekStart、weekEnd、nextRefreshAt、groupId、rank、rewardPreview |
|
||
| G5-6 | 添加排行榜测试 | [ ] | 覆盖周 XP 累加、分组、我的排名、周结算、重复结算 |
|
||
|
||
## Phase G6:API 文档、Admin 和运维
|
||
|
||
| # | 任务 | 状态 | 验收标准 |
|
||
|---|------|------|----------|
|
||
| G6-1 | 更新 `docs/api-reference.md` | [ ] | 文档只保留最终客户端契约,包含挑战组、奖励、商店、背包、周榜、错误码 |
|
||
| G6-2 | 更新 `docs/implementation-plan.md` | [ ] | 将本计划作为 Phase 1d 或 Game Economy 阶段索引进去 |
|
||
| G6-3 | 增加 Admin 配置或只读查看能力 | [ ] | 管理端至少能查看用户金币、道具、奖励流水、广告恢复记录 |
|
||
| G6-4 | 增加 E2E 或集成测试 | [ ] | 覆盖游客登录、完成挑战组、广告恢复、购买道具、周榜查询 |
|
||
| G6-5 | 增加定时任务入口 | [ ] | 周榜结算和订阅/资源周期任务有可部署入口,支持手动 dry-run |
|
||
| G6-6 | 完成最终验证 | [ ] | `bun run typecheck`、`bun run test`、`bun run lint` 通过或记录明确环境阻塞 |
|
||
|
||
## 推荐执行顺序
|
||
|
||
1. G0 数据模型与规则常量。
|
||
2. G1 挑战组会话与完成结算。
|
||
3. G2 XP、等级、连续学习规则。
|
||
4. G3 金币、道具、商店。
|
||
5. G4 广告恢复接入统一奖励服务。
|
||
6. G5 本周排行榜与周期结算。
|
||
7. G6 文档、Admin、E2E 和运维入口。
|
||
|
||
## 需要产品确认的决策点
|
||
|
||
- Plus 用户的每日高奖励挑战次数采用 8 组还是无限。
|
||
- 等级达到 Lv.50 后是否继续累计 XP,以及 `xpToNextLevel` 如何展示。
|
||
- 宝箱概率和高奖励次数耗尽后的降级比例。
|
||
- 排行榜前三奖励的具体数值:金币、徽章碎片、头像框是否第一版都上线。
|
||
- 吉祥物装扮、头像框、称号是否第一版需要完整库存模型,还是先作为奖励流水中的展示型权益。
|
||
|
||
## 完成定义
|
||
|
||
- 服务端可以独立裁决所有第一版游戏化资源和奖励。
|
||
- 客户端不能通过直接改本地状态绕过红心、挑战次数、金币、道具和广告恢复限制。
|
||
- 所有可重复触发的奖励都有幂等保护。
|
||
- 所有资源变更可追踪来源和结果。
|
||
- API 文档与真实路由、DTO、错误码保持一致。
|