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.
11 KiB
11 KiB
确认清单 — P0(缺陷报告 P0-1 ~ P0-7 三方判定)
方法:原型条款(research/原型依据基准.md,蓝湖 MCP 实时版 2026-08-15)× PRD v1.2(
.scratch/clue-module/线索业务-PRD.md)× 代码现状(2026-08-19 逐行复核)。 语义优先级:PRD > 原型 > 缺陷报告(用户 2026-08-19 拍板)。 裁决四类:真实缺陷(代码有错)/ 实现遗漏(需求定稿但从未实现)/ 需求变更(PRD 推翻原型,按 PRD)/ 误报(代码已正确实现)。
一、汇总
| 编号 | 裁决 | 一句话结论 | 修复票 |
|---|---|---|---|
| P0-1 | 实现遗漏 | deletePool 无"池下无非终态线索"守卫,ADR-0023 刻意预留的决策债 |
05 |
| P0-2 | 实现遗漏 | claimLead 无 hold_limit / daily_claim_limit 校验 |
06 |
| P0-3 | 实现遗漏 | assignToUser 无被分配人上限校验 |
06 |
| P0-4 | 实现遗漏 | createLead claimOnCreate 分支绕过全部领取校验(含 ADMIN_ONLY) |
06 |
| P0-5 | 误报 | claimLead L217-219 已拦截 ADMIN_ONLY 并抛 65005,且有单测覆盖 |
—(无需修复) |
| P0-6 | 实现遗漏 | PUBLIC_POOL 视图硬编码 status=PENDING,违反 PRD §7(E) 可配置状态集 |
07 |
| P0-7 | 实现遗漏 | createLead 无角色分支:销售可建"未分发"、可任意指定池 |
07 |
裁决分布:实现遗漏 6 / 误报 1 / 真实缺陷 0 / 需求变更 0。 缺陷报告 P0 段可信度:7 项指控 6 项属实、1 项误报(P0-5),无误引原型条款。
二、分项证据链
P0-1 删除公海池未校验"存在活跃线索" — 实现遗漏
- 原型:A7-3-1"已存在线索绑定的公海池禁止删除"(单条)、"若任意勾选公海池内存在有效线索,弹出拦截提示禁止批量删除"(批量)→ 基准 §P0-1 ✅
- PRD:§4.3"删除软删且必须池下无非终态线索"——非终态 = status ∉ {5 已转商机, 6 过期失效, 7 作废},即
status IN (1,2,3,4)。 - 代码:LeadPoolServiceImpl.deletePool() 直接软删(deleted=1 + delete_key=id)+ 级联硬删关联表,无占用检查;
deletePoolBatch()L150-168 逐条self.deletePool同样无检查。PoolBatchFailReasonHAS_ACTIVE_LEAD枚举已定义,注释自认"本期保留,单条删除与批量删除均不产出,待独立 ADR/issue 落地";RuleConstants.CODE_POOL_HAS_ACTIVE_LEAD=64005从未被业务逻辑抛出。 - 报告微瑕:称 64005"从未被引用"——实际在枚举定义处被引用,但运行时从未产出,指控实质成立。
- 修复方向(票 05):软删前
SELECT COUNT(*) FROM lead WHERE pool_id=? AND status IN (1,2,3,4),>0 抛 64005。跨模块 seam:crm-rule 不得依赖 crm-lead(铁律),需出 ADR——参照 crm-leadOpportunityCreationPort的 outbound port 模式,crm-rule 定义占用查询 port、crm-lead 实现。批量口径:单条守卫落地后逐条独立事务自然部分成功(与 ADR-0023 一致);原型"整批拦截"由前端预检实现,后端不破坏 ADR-0023。
P0-2 领取未校验"个人持有上限 / 每日领取上限" — 实现遗漏
- 原型:A7-3-1 其他设置(0=无限仅明说于每日上限、达到上限禁止领取)+ A2-1-1"领取超限"弹窗 → 基准 §P0-2 ✅
- PRD:§6.2 前置 2:
daily_claim_limit(0=无限,"每日"=自然日);前置 3:hold_limit,计数口径 = 仅已领取+跟进中(已转商机/过期失效/作废不占名额)。 - 代码:LeadServiceImpl.claimLead() 仅 ADMIN_ONLY 拦截;
LeadTransitionImpl.applyClaim()L118-131 仅状态 CAS(PENDING→CLAIMED);crm-lead 全模块 grepholdLimit|dailyClaimLimit0 匹配;错误码 65006(每日超限)/65007(持有超限)已在LeadConstantsL55-56 定义、从未抛出。 - 修复方向(票 06):claimLead 按目标池参数补两上限校验。hold 计数 =
owner_user_id=? AND pool_id=? AND status IN (3,4);daily 计数数据源(lead_history 当日 CLAIM 日志 vs 冗余计数)为票 06 内部工程决策。hold_limit=0 语义:原型仅授权 daily=0 无限,hold 校验按hold_limit>=1强制(PRD §4.3 默认 200、validatePool 已要求 ≥1,代码口径一致)。
P0-3 管理员分配未校验被分配人上限 — 实现遗漏
- 原型:无直接条款(分配后=已领取语义支持);权威 = PRD §6.3。
- PRD:§6.3 grill #7——分配到销售时两上限都校验(与自领同一套约束),额度归属=被指派销售(非管理员),按目标线索所属池上限值算;批量分配部分成功;仅分配到池(无 owner)不占上限。
- 代码:LeadServiceImpl.assignToUser() 仅做 pool 归属预检 + recycleDeadline 预算,无上限校验;
applyAssignToUser仅 CAS。批量assignToUserBatch走 runBatch 部分成功框架(与 PRD 口径兼容)。 - 修复方向(票 06):以被分配人为主体复用 P0-2 同一校验器(同一深模块,避免两处口径漂移)。基准附加发现 #2"分配可不指定销售人员"在现有接口形态(userId 必传)下不存在,无需处理。
P0-4 新增线索勾选"是否领取"绕过校验 — 实现遗漏
- 原型:A2-1-2-2"勾选则新增完成后自动领取该线索"→ 基准 §P0-4 ✅;超限处理原型留白。
- PRD:§6.1 副作用"勾'是否领取'则一步转已领取(边 #3)";§6.2 领取前置语义适用于一切领取行为。用户 2026-08-19 拍板:超限或池规则禁自领时,整个新增失败报错,用户只能改选"否"再提交。
- 代码:LeadServiceImpl.createLead() claimOnCreate 分支直接置 CLAIMED + owner + claimTime + recycleDeadline,未走任何校验链——ADMIN_ONLY 池也能通过"新增即领取"自领(报告"后门"指控属实)。
- 修复方向(票 06):claimOnCreate=true 时先跑完整领取校验链(ADMIN_ONLY + hold + daily;lead 未落库,计数天然不含本条),不满足则整个新增事务失败(@Transactional 回滚,用户拍板语义)。
P0-5 领取规则"管理员可分配"未拦截销售自领 — 误报
- 原型:A7-3-1 选项 2"销售仅能查看公海线索,无自主领取按钮"→ 基准 §P0-5 ✅
- PRD:§6.2 前置 1:claim_rule=2 时销售不可自领。
- 代码:LeadServiceImpl.claimLead() L217-219 已有
pool.getClaimRule() == RuleConstants.CLAIM_RULE_ADMIN_ONLY→ 抛CODE_CLAIM_RULE_DENIED(65005)"该公海池仅管理员可分配,不可自助领取";单测覆盖(LeadServiceImplTestL241 断言 code=65005)。 - 裁决依据:报告指控"claimLead 未读取 claim_rule"与代码事实不符。前端置灰属展示层,后端兜底已存在——原型条款与 PRD 均已满足。
- 处理:无需修复;票 06 落地上限校验时保持该拦截不变(校验链顺序:claimRule → hold → daily)。
P0-6 线索公海硬编码只展示"待领取" — 实现遗漏
- 原型:A2-1-1 列表混合展示(状态四态标识 + 领取人列 + 防撞单文案 + 已领取行示例)→ 基准 §P0-6 ✅
- PRD:§7 已定稿(E)(grill #9)——公海视图混合展示(待领取 + 已领取/跟进中只读观察态,防撞单),展示范围作为可配置项,不硬编码:后端以"公海可见状态集"参数(默认
{待领取, 已领取, 跟进中})驱动过滤;领取按钮仅对status='待领取'行可点。 - 代码:LeadViewQueryImpl.buildViewWrapper() L130
case PUBLIC_POOL -> wrapper.eq(Lead::getStatus, STATUS_PENDING)硬编码;L138 的 statusIn 筛选与之 AND 叠加(交集),即使前端传 {PENDING,CLAIMED,FOLLOWING} 也被压回仅 PENDING。 - 修复方向(票 07):PUBLIC_POOL 分支改为"公海可见状态集"驱动(默认 {2,3,4};配置载体——系统参数/字典——票 07 内定)。statusIn 与状态集 AND(下钻收窄)语义保留。领取操作安全性天然成立:claimLead 的 CAS 仅 PENDING 起始可成功。领取人列回显走
ownerNameSnapshot快照字段(已随实体返回,无需新查询)。
P0-7 新增线索缺少角色/归属约束 — 实现遗漏
- 原型:A2-1-2-2"线索公海池自动回填当前销售所属区域公海池,不可编辑";A2-1-4 / A2-1-4-1(08-15 新页,强化):管理侧新增默认未分发、超管可切换任意池、选人即分配(已领取)、销售无入口 → 基准 §P0-7 ✅
- PRD:§6.1 grill #6 定稿——自动带池 = 本人主部门的池(不给选、不可编辑;兼职部门有池也只落主部门池);主部门无池 → 报错拦截("本部门尚未配置公海池,请联系管理员");"未分发/空池"是管理员导入专属中间态,销售不得创建。
- 代码:LeadServiceImpl.createLead() L110-133——
poolId==null一律置 UNDISTRIBUTED(无角色判定,销售也能建未分发);poolId!=null时前端传什么用什么(无销售强制回填、无管理员可切换分支)。Controller 层create接口同样无角色约束(claimOnCreate/followOnCreate 由前端任意传)。 - 修复方向(票 07):createLead 增加角色分支——销售:忽略前端 poolId,强制回填主部门池(经 crm-auth 主部门 →
getPoolByDept),主部门无池抛错拦截;管理员:可指定任意池,poolId 可空 = 未分发(对齐 A2-1-4-1)。角色判定来源(SecurityUtils 会话角色)在票 07 内核对现有 crm-auth 接口。
三、待用户确认(不阻塞本判定,阻塞修复票 07)
- 管理侧新增(A2-1-4-1)的"选人即分配"是否纳入本期修复范围:原型已画(选中人员→创建即已领取),PRD §6.1 未定义管理侧新增流程(属 PRD 留白、原型有页)。若做,语义应为"分配"(走 §6.3 上限校验,额度归被选销售)而非"自领"。票 07 开工前拍板。
四、结论
- P0-1~P0-7 判定完毕:6 项实现遗漏、1 项误报(P0-5),无真实缺陷、无需求变更。
- 缺陷报告 P0 段可信度良好:唯一失实指控为 P0-5(claim_rule 拦截已存在)。
- 修复映射:票 05(P0-1 池删除守卫 + 跨模块 seam ADR)→ 票 06(P0-2/3/4 共用领取额度校验器;P0-5 保持现状)→ 票 07(P0-6 可配置状态集 + P0-7 角色分支)。
- 所有"实现遗漏"的 PRD 依据均已定稿(§4.3 / §6.1 / §6.2 / §6.3 / §7(E)),无"PRD 与原型均无定论"的产品分歧;唯一范围级待确认项见 §三。