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.
 
 
 
 
 

5.5 KiB

10 修复:P2 健壮性批次(P2-1 ~ P2-7)

Type: task Status: resolved Blocked by: 04

Question

按 ticket 04 的裁决修复 P2 确认项(判误报/已实现的跳过,仅在清单记录):

  • P2-1 领取并发:复核 CAS 抢占失败返回明确"已被他人认领"错误码(前端 toast 依据);缺则补。
  • P2-2 数值校验:按 04 以原型原文裁决的语义对齐 savePool 校验(daily ≥0 整数、0=无限;hold 按 PRD/原型结论);LeadPoolServiceImplTest 既有断言与新语义冲突时以裁决为准调整。
  • P2-3 池名全局唯一savePool() 校验唯一(注意软删行不计入/规避重复)。
  • P2-4 部门负责人归属只读:按 04 结论(含用户拍板的实现方式)落地——操作者为部门负责人时忽略/拒绝对 team/dept/province 归属字段的变更。
  • P2-5 批量角色隔离:按 04 查明的既有权限实践给 delete-batch/批量分配等端点补权限点校验(纯 hasAuthority + 种子机制,不发明新机制;若需新增权限点种子,走 PermissionInitializer 既有 seam)。
  • P2-6 反馈派生字段:按 04 结论修正列表回填(最新 SUBMITTED 反馈,不被 DRAFT 污染)。
  • P2-7 状态白名单兜底:按 04 结论复核/补齐 release/feedback/convert 的后端状态白名单(若 guard 已兜底则只补单测)。

验收:每项针对性单测 + mvn test(crm-rule/crm-lead)全绿 + BOM 扫描。

Answer

判定汇总

编号 裁决 处理
P2-1 误报 CAS 幂等已落地(eq status=PENDING + checkCas 65010),无需修复
P2-2 误报(hold=0 留白已解决) validatePool 四项校验全齐(ticket 06 已修 holdLimit≥0);本票修正 DTO/实体注释"≥0,0=不可领取"
P2-3 误报 池名唯一已校验 + @TableLogic 软删行不计入 + deleteKey 复用键,无需修复
P2-4 实现遗漏 已修复——savePool 增加角色拦截守卫
P2-5 实现遗漏*(用户拍板 B) 按用户决策关闭——维持 fail-open,不种子化 button 权限点(记最终报告风险项)
P2-6 误报 反馈派生字段=主表快照列,草稿明确不刷快照,无污染路径,无需修复
P2-7 误报 guard 声明式守卫 + editLead 终态锁已完备,无需修复

P2-4 修复详情(savePool 角色拦截)

文件crm-rule/src/main/java/com/crm/rule/service/impl/LeadPoolServiceImpl.javasavePool() 方法

变更

  1. 方法开头获取 isAdmin = SecurityUtils.hasRole(AuthConstants.ROLE_CODE_ADMIN) + exist pool(编辑路径)
  2. 非管理员编辑时(!isCreate && !isAdmin):
    • deptId 变更 → 抛 CODE_POOL_INVALID("部门负责人不可修改归属部门,仅超级管理员可变更")
    • 地理范围变更(省份/城市 ID 列表集合比较,忽略顺序) → 抛 CODE_POOL_INVALID("部门负责人不可修改负责区域,仅超级管理员可变更")
  3. 管理员编辑时:守卫跳过,可自由修改所有字段
  4. 新建时:守卫跳过(isCreate=true

设计决策

  • validatePool 前获取 exist,避免非管理员发送不同 deptId 时唯一性校验误拦
  • 地理范围用 Set<Long> 集合比较(null 归一化为空集),忽略列表顺序差异
  • 角色判定用 SecurityUtils.hasRole(service 层业务规则,非 controller 层 @PreAuthorize)

P2-2 注释修正

  • LeadPoolDTO.java@Schema(description = "个人持有上限(≥0,0=不可领取)")
  • LeadPool.java@Comment("个人持有上限(≥0,0=不可领取)")
  • validatePoolholdLimit≥0 校验已在 ticket 06 修正,此处仅同步注释

P2-5 用户决策

用户拍板 B:维持 fail-open——不推翻"不种子化 button 权限点"决策。P2-5 按既有决策关闭,记入最终报告风险项。P0-7/P1-2/P2-4 的 service 内角色/管理员判定仍按 PRD 定稿做(属业务规则非 button 权限点,走 SecurityUtils + 角色查询,不走 hasAuthority + 种子)。

单测

新增 4 个 P2-4 测试LeadPoolServiceImplTest.java):

测试 场景 验证
savePool_nonAdminChangeDept_rejected 非管理员编辑改 deptId 抛 CODE_POOL_INVALID,不执行 updateById
savePool_nonAdminChangeGeography_rejected 非管理员编辑改 geography 抛 CODE_POOL_INVALID,不执行 updateById
savePool_nonAdminNoOwnershipChange_passes 非管理员编辑不改归属字段 放行,执行 updateById + publishEvent
savePool_adminChangeDept_passes 管理员编辑改 deptId 放行(守卫跳过),执行 updateById + publishEvent

适配 1 个既有测试savePool_update_publishesEvent — 编辑路径现在进入守卫,用 mockStatic(SecurityUtils.class)hasRole 返回 true(管理员路径),保持原有"编辑成功 + 发布事件"验证意图不变。

mockStatic 模式:try-with-resources 局部 scope,与 @Mock 字段不冲突,MockitoExtension strict stubbing 无误报。

验证

  • BOM 扫描:4 个修改文件全绿(LeadPoolServiceImpl.java / LeadPoolDTO.java / LeadPool.java / LeadPoolServiceImplTest.java)
  • mvn testmvn test -pl crm-rule,crm-lead -am → BUILD SUCCESS
    • crm-rule:30 tests(含 LeadPoolServiceImplTest 22 tests = 18 原有 + 4 新增)
    • crm-lead:97 tests(含 ticket 09 新增的 PoolChangedEventListenerTest / LeadMaintenanceJobTest / 更新的 LeadViewQueryImplTest)