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
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.java — savePool() 方法
变更:
- 方法开头获取
isAdmin = SecurityUtils.hasRole(AuthConstants.ROLE_CODE_ADMIN)+existpool(编辑路径) - 非管理员编辑时(
!isCreate && !isAdmin):deptId变更 → 抛CODE_POOL_INVALID("部门负责人不可修改归属部门,仅超级管理员可变更")- 地理范围变更(省份/城市 ID 列表集合比较,忽略顺序) → 抛
CODE_POOL_INVALID("部门负责人不可修改负责区域,仅超级管理员可变更")
- 管理员编辑时:守卫跳过,可自由修改所有字段
- 新建时:守卫跳过(
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=不可领取)")validatePool的holdLimit≥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 test:
mvn 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)