# 任务中心领取逻辑风险审查与测试计划 ## 背景与目标 - **风险点**:用户可直接领取、或依赖任务上线前的历史数据领取新的任务奖励,尤其是在 `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 与前端/运营端改造影响,再单独立项。