bindbox-game/docs/任务中心领取风险审查与测试计划.md
2026-02-27 16:07:12 +08:00

63 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 任务中心领取逻辑风险审查与测试计划
## 背景与目标
- **风险点**:用户可直接领取、或依赖任务上线前的历史数据领取新的任务奖励,尤其是在 `window` 未配置或默认 `lifetime` 的档位上。
- **目标**:明确代码审查要点、需要补强的校验以及自动化/手工测试,确保任务中心的进度统计与领取逻辑默认受限于任务 `StartTime ~ EndTime`
## 现状速览
- **窗口处理**`normalizeWindow` + `computeTimeWindow` 对非法窗口统一回退 `lifetime``WindowActivityPeriod` 才会套用任务起止时间(`internal/service/task_center/service.go:200-525`)。未配置或设置 `lifetime` 的档位默认统计全部历史。
- **进度统计**`GetUserProgress``(window, activity_id)` 分组统计 `TierProgressMap`,并回退到全局订单/邀请(`service.go:528-668`)。
- **领取校验**`ClaimTier` 先读取 `TierProgressMap`,再按 metric/operator 判断;含活动的档位使用 Redis `tc:claim_lock:{user_id}:{activity_id}` 加锁,并做跨任务阈值校验(`service.go:670-939`)。全局档位没有锁,任务级限额只在总量层面控制。
## 审查与增强要点
### 1. 窗口与时间范围
1. 将“任务设置了 `StartTime`/`EndTime` 但 window=空或 `lifetime`”纳入巡检:建议在 `normalizeWindow` or `GetUserProgress` 中,当 `task.StartTime` 不为空时自动将窗口裁剪到 `[StartTime, EndTime]`,并在单测覆盖空/NULL 窗口场景。
2. 明确 `since_registration` 行为:若不允许历史数据,应在 `computeTimeWindow` 中将起点设为 `max(user.RegistrationTime, task.StartTime)`
3. 验证 `TierProgressMap``SubProgress` 是否正确过滤 `start_time`/`end_time`:针对 `daily/weekly/activity_period/lifetime` 构造订单在边界日/周的案例,确保窗口重置与任务期重叠判断符合预期。
### 2. 领取校验扩展
1.`ClaimTier` 中新增“窗口开始 < task.StartTime判断 `tierProgress` 返回数据完全来自任务上线前需拒绝并提示任务尚未开始需重新累计”。
2. 复核跨任务阈值 `calculateCrossTaskConsumedThreshold`目前按任务创建时间 + 窗口重叠过滤但若 `siblingRows` 包含旧任务的 `lifetime` 档位仍可能消耗全部历史需要配合第 1 点的窗口裁剪
3. Redis 锁范围对于 `activity_id=0` 的档位可增加 `tc:claim_lock_task:{user_id}:{task_id}`防止并发重复领取
### 3. 配置与数据守护
1. **巡检 SQL**示例
```sql
SELECT tiers.id, tiers.task_id, tiers.window, tasks.start_time, tasks.end_time
FROM task_center_task_tiers tiers
JOIN task_center_tasks tasks ON tasks.id = tiers.task_id
WHERE (tiers.window IS NULL OR tiers.window = '' OR tiers.window = 'lifetime')
AND tasks.start_time IS NOT NULL;
```
对结果逐条评估是否需改成 `activity_period` 或自定义窗口。
2. 若需让运营显式配置“允许历史数据”,可在 `task_center_task_tiers` 增加 `allow_legacy_data TINYINT`,并在 `admin` Upsert 接口透出。当前阶段以代码默认裁剪为主。
## 自动化测试计划
| 编号 | 场景 | 步骤 | 预期 |
| --- | --- | --- | --- |
| UT-1 | `lifetime` + StartTime 剪裁 | 任务 StartTime=T0插入 T0-1/T0+1 订单,调用 `GetUserProgress` | 仅统计 T0+1 数据;`TierProgressMap` 中的数量=1 |
| UT-2 | 空 Window | 档位 `window=''`,任务 StartTime=T0同上 | 行为等同 UT-1 |
| UT-3 | `since_registration` | 构造用户注册时间 Treg < T0验证窗口起点为 `max(Treg, T0)` | 统计以 `T0` 为准 |
| IT-1 | 历史数据领取阻断 | 先插入历史订单,使 `TierProgressMap` 达标;上线任务后 `ClaimTier` | 返回“任务条件未达成” |
| IT-2 | 新订单后可领 | 在 IT-1 基础上插入新订单超过阈值再 `ClaimTier` | 领取成功 |
| IT-3 | 跨任务占用 | 旧任务(已领 50 单) + 新任务阈值 60 单,新订单 15 单 | `ClaimTier` 拒绝,日志输出 `cross-task threshold`;再补 10 单 → 成功 |
| IT-4 | Redis 锁 | 并发触发 `ClaimTier`activityID>0 与 activityID=0 场景分别验证 | 仅一次成功,其余提示“操作频繁”或“已领取” |
> 自动化测试可基于现有 SQLite Repo`mysql.NewSQLiteRepoForTest`)快速构造数据,参考 `service_test.go`、`invite_logic_test.go` 的写法。
## 手工/灰度验证
1. **SQL 巡检**:执行上文 SQL导出需要修正的档位配合运营确认并批量更新 `window`。
2. **模拟接口回放**:通过 `/admin/task_center/simulate/order_paid`、`/simulate/invite_success` 重放旧流水,再调整任务时间并调用 `/tasks/{id}/claim/{user}`,观察日志(`ClaimTier: cross-task threshold...`、`任务尚未开始` 等)。
3. **小程序体验**:发布新任务后,用老用户登录 `pages-user/tasks`,确认显示进度清零、领取按钮禁用;完成新订单后刷新 → 按预期解锁。
## 风险与假设
- 默认业务需求为“任务上线前的历史数据不可复用”,如需白名单例外需另开配置。
- Redis/数据库资源允许新增少量锁与巡检脚本,不影响现有性能。
- 若需要对现网数据批量改 `window`,需评估是否会影响已经配置为 `lifetime` 的任务并提前同步运营
## 下一步
1. 根据本计划完成代码 PoC窗口剪裁领取校验锁扩展)。
2. 提交自动化测试用例覆盖表格中的 UT/IT 场景
3. 运行 SQL 巡检 + 手工验证记录整改项
4. 如需引入允许历史数据配置评估 schema 与前端/运营端改造影响再单独立项