You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
50 lines
4.1 KiB
50 lines
4.1 KiB
|
3 weeks ago
|
# 09 修复:P1 联动/任务/统计批次(P1-5/6/7/8)
|
||
|
|
|
||
|
|
Type: task
|
||
|
|
Status: resolved
|
||
|
|
Blocked by: 03, 08
|
||
|
|
|
||
|
|
## Question
|
||
|
|
|
||
|
|
按 ticket 03 的裁决修复(形状取决于 03 对 PoolChangedEvent 现状的照亮结果):
|
||
|
|
|
||
|
|
- **P1-5 池归属变更联动**(PRD §4.1.1):池 dept 变更后,`未分发/待领取` 线索 `dept_id` 批量跟池走(`UPDATE lead SET dept_id=new WHERE pool_id=? AND status IN (未分发,待领取)`);已领取/跟进中不动。落地载体(既有 PoolChangedEvent 监听器扩展 / 新建监听)按 03 结论。
|
||
|
|
- **P1-6 N/M 变更重算 deadline**(PRD §5.3 公式):N 改动 → 已领取 `recycle_deadline=claim_time+new_N`、跟进中 `=last_feedback_time+new_N`;M 改动 → `expire_deadline=create_time+new_M`(status≠已转商机)。**当日不批量清算已超时行**——只改截止时刻,状态转移交给次日定时任务。
|
||
|
|
- **P1-7 统计 SQL 聚合**:`countStats()` 从 selectList+内存循环改为 SQL `GROUP BY status` 聚合(口径仍=当前视图筛选 + @DataScope,抽 `buildViewWrapper` 复用);注意 H2/MySQL 兼容(本仓测试跑 H2,曾因缺列翻车——见 common_pitfalls 记忆)。
|
||
|
|
- **P1-8 定时任务顺序**(PRD §6.7/ADR-0019):失效先、回收后,同一 02:00 调度内串行一次扫描;消除两个独立 `@Scheduled` 的竞态(若 03 查明已满足则仅补单测/注释)。
|
||
|
|
|
||
|
|
**验收**:每项新增单测(联动刷新范围正确/公式正确/不回溯/聚合结果与原实现等价/先失效后回收)+ `mvn test`(crm-lead,必要时 crm-rule)全绿 + BOM 扫描。
|
||
|
|
|
||
|
|
## Answer
|
||
|
|
|
||
|
|
四项全部修复完成,`mvn test -pl crm-lead -am` 全绿(97 tests, 0 failures, 0 errors),BOM 扫描通过。
|
||
|
|
|
||
|
|
### P1-5 池归属变更联动
|
||
|
|
- **文件**:`PoolChangedEventListener.java`
|
||
|
|
- **修复**:将硬编码 `in(status, 1, 2)` 替换为 `LeadConstants.STATUS_UNDISTRIBUTED, STATUS_PENDING` 常量引用,消除魔法数字。
|
||
|
|
- **单测**:`PoolChangedEventListenerTest.deptUpdate_targetsUndistributedAndPendingOnly`——验证 dept 联动更新 WHERE 含 `status IN` 子句。
|
||
|
|
|
||
|
|
### P1-6 N/M 变更重算 deadline
|
||
|
|
- **文件**:`PoolChangedEventListener.java`
|
||
|
|
- **修复**:替换原"暂不批量刷新"注释,新增 3 条 `setSql("DATE_ADD(...)")` 更新:
|
||
|
|
- CLAIMED:`recycle_deadline = DATE_ADD(claim_time, INTERVAL N DAY)`
|
||
|
|
- FOLLOWING:`recycle_deadline = DATE_ADD(feedback_time, INTERVAL N DAY)`
|
||
|
|
- 非CONVERTED:`expire_deadline = DATE_ADD(create_time, INTERVAL M DAY)`
|
||
|
|
- 当日不批量清算已超时行——只改截止时刻,状态转移交给次日定时任务。
|
||
|
|
- **单测**:3 个测试分别验证 CLAIMED 用 claim_time、FOLLOWING 用 feedback_time、expire 用 create_time 且 WHERE 含 `<>`(排除 CONVERTED)。
|
||
|
|
|
||
|
|
### P1-7 统计 SQL GROUP BY 聚合
|
||
|
|
- **文件**:`LeadViewQueryImpl.java`
|
||
|
|
- **修复**:`countStats()` 从 `selectList + Java 循环` 重构为 3 次 `selectMaps` + GROUP BY 聚合:
|
||
|
|
1. `SELECT status, count(*) as cnt ... GROUP BY status` → 状态计数
|
||
|
|
2. `SELECT feedback_status, count(*) as cnt ... GROUP BY feedback_status` → 反馈计数
|
||
|
|
3. `SELECT count(*) as cnt ... WHERE DATE(create_time) = CURRENT_DATE` → 今日新增
|
||
|
|
- 原 `buildViewWrapper` 重构为 `applyViewFilters(LambdaQueryWrapper, ...)`,让 `QueryWrapper.lambda()` 能复用同一套 WHERE 条件。
|
||
|
|
- `pageLeads` 和 `countStats` 共享 `applyViewFilters`,确保「统计口径 = 列表口径」。
|
||
|
|
- **单测**:2 个原 countStats 测试更新为 mock `selectMaps`(返回值等价);新增 `countStats_usesGroupByAggregation` 验证 `selectMaps` 被调用 3 次、`selectList` 从不被调用、第 1 次 SQL 含 `GROUP BY`。
|
||
|
|
|
||
|
|
### P1-8 定时任务合并
|
||
|
|
- **文件**:删除 `LeadExpireJob.java` + `LeadRecycleJob.java`,新建 `LeadMaintenanceJob.java`;更新 `LeadTransition.java` javadoc 引用。
|
||
|
|
- **修复**:合并两个独立 `@Scheduled`(02:00 expire + 02:05 recycle)为单一 `@Scheduled(cron = "0 0 2 * * ?")`,串行调用 `executeExpire` → `executeRecycle`,消除竞态。
|
||
|
|
- **单测**:`LeadMaintenanceJobTest.execute_expireBeforeRecycle`——用 Mockito `InOrder` 验证 expire 先于 recycle 执行。
|