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.
13 KiB
13 KiB
确认清单 — P1(缺陷报告 P1-1 ~ P1-8 三方判定)
方法:原型条款(research/原型依据基准.md)× PRD v1.2 × 代码现状(2026-08-19 逐行复核)+ ADR-0019/0023/0021/0022 定稿口径。 语义优先级:PRD > 原型 > 缺陷报告;ADR 为架构定稿,与 PRD 同级引用。 判定口径:真实缺陷 = 已有代码路径在特定场景产生错误结果(越权/脏数据/竞态);实现遗漏 = 定稿要求的逻辑从未实现;误报 = 代码已满足 PRD/原型。
一、汇总
| 编号 | 裁决 | 一句话结论 | 修复票 |
|---|---|---|---|
| P1-1 | 实现遗漏 | applyFeedback 提交后未清除同 (user_id, lead_id) 的 DRAFT 行 |
08 |
| P1-2 | 真实缺陷 | deleteLead 权限不受控:isCreator 死变量 + "DataScope 兜底"假设不成立,同部门销售可互删 |
08 |
| P1-3 | 实现遗漏 | EDIT 历史只记 leadName 单值,无字段级 old→new diff | 08 |
| P1-4 | 误报 | 守卫已下沉状态机(FeedbackCmd 白名单 + requiresOwner + 枚举校验),报告还误把 VOID 当禁区 | —(票 08 仅复核单测覆盖) |
| P1-5 | 误报 | PoolChangedEventListener 已完整实现 §4.1.1 联动(未分发/待领取 dept_id 跟池走) |
—(无需修复) |
| P1-6 | 实现遗漏 | N/M 变更后存量 deadline 重算完全缺失(M 有注释自认"暂不刷新",N 连注释都无) | 09 |
| P1-7 | 真实缺陷 | countStats selectList 内存循环,违反 ADR-0023 D3"按 status 分组计数"定稿 |
09 |
| P1-8 | 真实缺陷 | 失效/回收是两个独立 @Scheduled(02:00/02:05)软间隔,无 ADR-0019 要求的顺序硬保证,存在竞态窗口 | 09 |
裁决分布:实现遗漏 3 / 真实缺陷 3 / 误报 2 / 需求变更 0。 缺陷报告 P1 段可信度:8 项中 6 项属实、2 项误报(P1-4 部分失实+建议与 PRD 冲突、P1-5 报告自身"需确认"的疑虑经查已实现)。
二、分项证据链
P1-1 反馈提交后未清理草稿(DRAFT)行 — 实现遗漏
- 原型:无草稿条款(基准 §P1-1:报告引文"A2-1-2-1 反馈分草稿/提交"在原型中不存在,实为 PRD §6.4 内容——引文失实但结论方向由 PRD 支撑)。
- PRD:§6.4【保存】副作用第 4 条:"清除同 (user_id, lead_id) 的草稿行(转为 SUBMITTED)"。
- 代码:LeadTransitionImpl.applyFeedback() L226-237 仅
leadFeedbackMapper.insert(fb)写入新 SUBMITTED 行,无任何 DRAFT 行的 DELETE/UPDATE。用户"先存草稿再提交"时 DRAFT 行永久残留。 - 影响修正:Controller 仅有
/feedback-draftPOST(保存草稿),无草稿查询回显接口——残留当前无用户可见影响,属数据层脏行 + 未来回显接口的坑。 - 修复方向(票 08):提交事务内清除同 (user_id, lead_id) 的 DRAFT 行。PRD 措辞"转为 SUBMITTED"——按现表结构建议:有 DRAFT 则 UPDATE 该行为 SUBMITTED(内容=提交内容),无则 INSERT,保证 (user_id, lead_id, DRAFT) 唯一。
P1-2 删除线索未真正校验创建人/角色权限 — 真实缺陷
- 原型:A2-1-4"仅管理员角色可见;过期失效、未分发、待领取可删除,已领取/已转商机删除增加二次强确认"——已被 PRD §3.3 推翻(需求变更背景,修复按 PRD 不按原型)。
- PRD:§3.3 删除权限全局规则(2026-08-13 定稿):
删除 = (状态 ≠ 已转商机) AND (管理员 OR 创建人·强确认);已转商机是唯一删除禁区;过期失效/作废无下游依赖仍可删。§9 定稿写权由"权限点 + service"守。 - 代码:LeadServiceImpl.deleteLead()——L193-195 已拦截 CONVERTED ✅;L197-199 计算
isCreator后从未使用(死变量);L200-201 注释声称"管理员判定由 DataScope 兜底(ALL_VISIBLE 能查到即可删)"——该假设不成立:@DataScope 是 SELECT 读过滤(四档天花板 SELF/DEPT/DEPT_AND_CHILDREN/ALL),getById查得到 ≠ 是管理员——SELF/DEPT 档的普通销售查得到本部门他人线索即可删除,写操作无任何守卫。 - 影响:同部门销售可互删他人线索;删除行为仅剩 CONVERTED 一道闸。
- 修复方向(票 08):service 内显式落地
(状态≠CONVERTED) AND (管理员 OR 创建人);管理员判定机制与票 07(P0-7 角色分支)共用同一实现保持一致;死变量激活或移除;DataScope 兜底注释更正。
P1-3 编辑线索未记录字段级变更历史 — 实现遗漏
- 原型:A2-1-2-1 历史记录"操作类型标签(领取/反馈/释放/转商机/编辑)、完整操作详情文本""永久留存"→ 基准 §P1-3 ✅(引文真实)。
- PRD:§6.9 副作用"写 lead_history EDIT 记录,detail 含变更字段清单(旧值→新值)";§6.10 EDIT detail=变更字段清单(旧值→新值)。可编辑字段=基础 7(名称/电话/微信/邮箱/省/市/地址)+ 业务来源 4(渠道/品牌/需求产品/需求场景)+ 核心 2(咨询内容/是否加急)。
- 代码:LeadServiceImpl.editLead() L185-186 仅
historyRecorder.record(id, EDIT, userId, "leadName", update.getLeadName())——只记单字段且只有新值、无旧值→新值格式。 - 修复方向(票 08):editLead 对 §6.9 可编辑字段全集做 old→new diff,变更项按
LeadHistoryRecorderkv 机制写入(字段名/旧值/新值);无变更时是否仍写 EDIT 由票 08 定并注明依据。
P1-4 提交反馈缺少入口级状态校验 — 误报
- 原型:A2-1-2-1"反馈仅【未过期失效】线索可见"→ 基准 §P1-4 ✅(引文真实)。
- PRD:§3.3 矩阵反馈行 = 已领取 ✓(持有人) / 跟进中 ✓(持有人) / 线索作废 ✓(持有人)(边 #16 作废恢复),其余 ✗。
- 代码:守卫已完整下沉状态机——TransitionCmd.FeedbackCmd
allowedFromStatuses()={CLAIMED, FOLLOWING, VOID}+requiresOwner()=true;guard() 统一执行(非法态抛 65003、非持有人抛 65004);applyFeedback() L196 拒绝 feedbackStatus ∉ {有效,无效}("反馈情况必须为有效或无效")。 - 报告失实点:①"未校验 feedbackStatus 枚举"——L196 已校验;②"未拦截 EXPIRED/CONVERTED"——guard 白名单已拦;③建议"拦截 VOID 反馈"——与 PRD §3.3 冲突(作废线索被分配后新持有人反馈=有效是唯一复活路径,边 #16)。
- 处理:无需修复。票 08 仅复核单测对 {EXPIRED, CONVERTED, PENDING, UNDISTRIBUTED} 拒反馈、非持有人拒反馈、feedbackStatus=0 拒绝三个场景的覆盖,缺则补测。
P1-5 池归属变更后存量线索归属未刷新 — 误报
- 原型:A7-3-1"联动更新:所有线索模块公海池下拉、领取按钮、上限拦截逻辑同步刷新"——引文真实但语义=前端刷新(基准 §P1-5 ⚠),非存量 dept_id 刷新。
- PRD:§4.1.1 定稿:未分发/待领取 dept_id 跟池走(批量 UPDATE);已领取/跟进中不动;终态不动。§4.2 池改名时批量刷 pool_name_snapshot。
- 代码:PoolChangedEventListener 已实现且与 §4.1.1 完全一致——①池名快照全量刷(L33-36);②
in(Lead::getStatus, 1, 2)恰为未分发/待领取刷 deptId+teamDeptId(L38-43),其余态天然不动。事件发布侧:savePool编辑时发布(LeadPoolServiceImpl L128-130)✅。 - 报告失实点:"需确认是否同步刷新"——已刷新;标题中"省份"维度系过度推度(PRD 无"线索省份跟池走"条款,线索 provinceCode 是客户属性与池无关)。
- 处理:无需修复。票 09 按此结论跳过 P1-5(监听器扩展仅做 P1-6 的 deadline 重算)。
P1-6 回收天数 N / 失效天数 M 修改后存量 deadline 未刷新 — 实现遗漏
- 原型:A7-3-1"修改领取上限、回收天数、失效天数后,已领取/存量线索次日起按照新规则执行,存量已超时线索当日不批量回溯处理"→ 基准 §P1-6 ✅。
- PRD:§5.3 定稿刷新公式——N 改动:已领取
recycle_deadline = claim_time + new_N、跟进中= last_feedback_time + new_N(即 lead.feedback_time 快照);M 改动:expire_deadline = create_time + new_M WHERE status ≠ 已转商机;当日不批量清算已超时行(只改截止时刻,状态转移交次日定时任务)。 - 代码现状(已有什么/缺什么——票 09 形状输入):
- 已有:
PoolChangedEventListener骨架(池名快照 + dept_id 联动,见 P1-5)。 - 缺:N 重算完全无代码(监听器无 recycle_deadline 任何逻辑);M 重算注释自认预留(L45-46"expireDeadline 刷新……此处暂不批量刷新(需自定义 SQL 做 DATE_ADD)")。
- 已有:
- 修复方向(票 09):监听器补两条批量重算(自定义 SQL DATE_ADD,LambdaUpdateWrapper 表达不了列间运算):N 按 §5.3 分状态两段 UPDATE;M 一段 UPDATE(status≠CONVERTED)。"不回溯清算"由"只改 deadline 不改状态"天然满足。
P1-7 视图统计走内存循环 — 真实缺陷(性能)
- 原型:A2-1-1/A2-1-3/A2-1-4"实时统计当前筛选条件下对应数据"——只约束实时性,不约束实现方式(基准 §P1-7)。
- ADR:ADR-0023 D3 定稿:"与 /page 吃完全相同的 viewType + 筛选 + @DataScope……只把「取一页」换成「按 status 分组计数」"。
- 代码:LeadViewQueryImpl.countStats() L87
leadMapper.selectList(...)全量拉回后 L90-116 Java 内存循环累加——方法注释自称"改分组计数"但实现名实不符,违反 ADR-0023 D3 定稿。 - 影响:数据量增长时全结果集进内存(当前演示数据量无感,属随规模退化的性能缺陷)。
- 修复方向(票 09):改 SQL
GROUP BY status聚合(status + feedbackStatus + 今日新增三维度),复用buildViewWrapper保口径一致;注意 H2/MySQL 兼容(测试跑 H2,曾有缺列翻车教训)。
P1-8 定时任务"先失效后回收"顺序 — 真实缺陷
- 原型:无直接条款(权威 = PRD §6.7 + ADR-0019)。
- PRD/ADR:§6.7 grill #4 定稿 + ADR-0019(accepted):统一每日 02:00 全库扫,失效先、回收后;ADR-0019 Consequences 明文:"若将来两个 Job 拆成独立调度/独立进程,必须保留「失效先完成、回收后开始」的顺序保证,否则本决策失效"。
- 代码:LeadExpireJob
@Scheduled(cron="0 0 2 * * ?")与 LeadRecycleJob@Scheduled(cron="0 5 2 * * ?")——两个独立 @Scheduled 靠 5 分钟 cron 软间隔,无"失效先完成"硬保证。失效 Job 执行超 5 分钟时(大数据量)回收 Job 并发启动:双 deadline 过期线索先被回收(清 owner)再被失效(保留 owner 语义被破坏)——owner 被误清,激活找回原持有人的后路丢失,正是 ADR-0019 要防的场景。 - 报告核实:报告的竞态担忧属实(其引用的是 PRD v1.1 grill,v1.2 §6.7 + ADR-0019 已定稿同义)。
- 修复方向(票 09):合并为单 Job——一个
@Scheduled(cron="0 0 2 * * ?"),方法内先executeExpire(now)后executeRecycle(now)串行(完全对齐 PRD"同一次扫描内先失效后回收"原文);两个 Job 类合一或保留类仅留单调度入口。ADR-0019 语义不变无需修订。
三、待用户确认
无。P1 段无"PRD 与原型均无定论"的产品分歧(P1-2 管理员判定机制为工程决策,票 07 选型后票 08 复用;P1-3 无变更是否写历史、P1-6 daily 计数数据源均为票内工程决策)。
四、结论
- P1-1~P1-8 判定完毕:实现遗漏 3(P1-1/3/6)、真实缺陷 3(P1-2/7/8)、误报 2(P1-4/5)。
- 两个真实缺陷均有 ADR 级依据:P1-7 违反 ADR-0023 D3 定稿、P1-8 违反 ADR-0019 Consequences 的顺序硬保证。
- 修复映射:票 08(P1-1 草稿清理、P1-2 删除权限、P1-3 字段级 diff;P1-4 误报仅补单测)→ 票 09(P1-6 N/M 重算、P1-7 SQL 聚合、P1-8 Job 合并;P1-5 误报跳过,监听器现状保留)。
- 判定口径与 P0 清单一致:P1-2 的"原型仅管理员"被 PRD §3.3 推翻记录为需求变更背景,代码修复一律按 PRD。