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.
9.2 KiB
9.2 KiB
Ticket 02: 资源树 perms 三缺口加固(对齐 v29 A7-2-2)
状态: resolved · 优先级: P2 · 来源: 20260831 ResourceServiceImplTest.save_editCatalog_noButtonFields 红灯裁决(用户拍板「三缺口加固票一起开」)· 同日用户确认两处票内拍板(见 Comments)
背景
- 红灯裁决(20260831):
ResourceServiceImplTest.save_editCatalog_noButtonFields红灯根因 = commit 5ac7aba(2026-08-28,message "commit" 大杂烩提交)把ResourceServiceImpl#applyToEntity的 perms 从「仅 button 写入 / 非 button 清 null」改为三类统一写入(删除 else 分支setPerms(null)),漏带测试。行为方向与 v29 A7-2-2 批注一致(三类资源权限编码必填、全局唯一、编辑只读),非误改。测试已于 20260831 同日修正(perms 断言反转为跟随入参 + 补 denyBehavior/status 断言),crm-auth 单测 206/0/0 全绿,红灯闭环。 - 授权语义零影响:
PermissionResolverImpl权限码并集只收集menuType=BUTTON且 status=enabled 的 perms(ADR-0011);catalog/menu 的 perms 是纯数据字段(资源树展示 + 全局唯一性),不进 authority。 - 但该行为变更只完成了「写入侧」,与 v29 对照残留三个缺口,本票收口。
依据(v29 A7-2-2 权限点管理批注,.scratch/opportunity-bugfix/lanhu-pages/A7-2-2_权限点管理.md)
- 资源层级(u194)第 6 条:「权限编码在三种资源类型之间全局唯一」
- 新增通用规则(u200)第 3 条:「权限编码必填、全局唯一、至少 3 位,只允许字母、数字和下划线」
- 编辑规则(u203)第 2 条:「资源类型和权限编码只读」
- 保存校验(u206)6.1:「新建时发现权限编码已被任一类型资源使用,不创建资源」
任务(三缺口)
- 必填校验:
ResourceServiceImpl#validateRequiredFields对 CATALOG/MENU 补 perms 非空校验(现状只校验 BUTTON),错误沿用 61009,提示「catalog/menu 类型必须提供权限编码(perms)」。 - 全局唯一校验:新增模式下校验 perms 在 sys_menu 全表跨类型唯一,重复拒 61009(提示沿用 v29 6.1 口径「已被其他资源使用」)。资源树量级百级,list 全表内存比对即可,无需加索引。编辑模式不校验(任务 3 保证 perms 不变更,天然无冲突)。
- 编辑只读锁:编辑模式下忽略
node.getPerms()(保留 DB 原值),不采用「传不同值报错」的拒绝形态——「只读」语义是「不可改」而非「改了报错」,宽容形态对误传前端无感。实现位置与现有 type 编辑锁(save 编辑分支 L74-77)相邻,注释注明 v29 u203 依据。
约束(含一项已拍板的豁免)
- 字符格式规则豁免(本票拍板):v29 4.1 第 3 条「至少 3 位、只允许字母数字下划线」与本仓库既有权限码冒号风格(
crm:user:list,DataInitializer 种子 / PermissionSeederImpl / ticket-01 全集下发全用冒号,且冒号是 Spring Security authority 命名惯例)冲突。本票只采纳必填 + 全局唯一,不做字符格式校验;若要统一命名风格(下划线化),涉及存量种子与前端权限点全面迁移,另立票全局裁决,不在本票。 - 不动
PermissionResolverImpl/PermissionGrant(授权链路与 perms 存储解耦)。 - 不动 ResourceNodeDTO 结构(无新 IO 对象,ADR-0017 无涉)。
- 单测沿用纯 Mockito 形态(
ResourceServiceImplTest36 用例扩容),全表唯一校验通过既有@Mock ISysMenuService的 list/lambdaQuery 桩扩展,不引集成测试依赖。 - 契约影响提示:新增模式 perms 必填是行为收紧——若资源树新增/编辑有 .bru 契约文档(bruno collection 后台管理文件夹),实现后同步约束描述(只碰 meta 含 generated 的文件,走 bruno-sync)。
验收
- crm-auth 单测全绿,新增负路径覆盖:catalog/menu 缺 perms → 61009;新增 perms 与既有其他节点重复 → 61009;编辑 catalog 传不同 perms → 保留 DB 原值(captor 断言)。
- 运行时冒烟(verify profile 起服 + debug token,脚本 Python requests):新增 catalog 无 perms → 61009;带合法 perms 保存成功;编辑传不同 perms → 返回与 DB 均为原值;新增 perms 与既有 button 重复 → 61009。
- 三类统一存储口径不回退(编辑 catalog 保存 perms 跟随入参的既有断言保持)。
- BOM 扫描干净。
关联
- 红灯修正:本票「背景」节 +
ResourceServiceImplTest.save_editCatalog_noButtonFields(20260831 已修,206/0/0) - v29 原型批注:
.scratch/opportunity-bugfix/lanhu-pages/A7-2-2_权限点管理.md(u194/u200/u203/u206) - 行为变更来源 commit:5ac7aba(2026-08-28)
- ADR-0011(统一权限资源树)、ADR-0016(PermissionSeeder seam)
- ticket-01(权限码全集下发——同一 perms 体系的消费端)
Answer
已完成(20260831)。三缺口(必填 / 全局唯一 / 编辑只读锁)落地,crm-auth 单测 210/0/0,运行时冒烟 5/5,BOM 干净。
交付内容
- 必填校验:
validateRequiredFields末尾新增——仅新增模式(node.getId() == null)对 CATALOG/MENU 校验 perms 非空,61009 消息「catalog/menu 类型必须提供权限码(perms)」;MENU 的 route 既有校验在前不受影响。 - 全局唯一校验:新私有方法
validatePermsUnique(save 流程中 validateHierarchy 之后调用)——仅新增模式且 perms 非空时sysMenuService.list()全表内存比对,重复抛 61009「权限码已被其他资源使用:{perms}」;编辑模式直接跳过(只读锁保证不变更,天然无冲突)。 - 编辑只读锁:
applyToEntity的setPerms收进新增分支(node.getId() == null)——编辑忽略入参保留 DB 原值;button 专用字段清空逻辑不变。
测试(ResourceServiceImplTest 36→40)
- 既有适配 7 例:新增正路径 4 例(newCatalog / newMenuUnderCatalog / catalogAsRoot / menuAsRoot)补 perms 入参 +
list()空桩(唯一性校验依赖);newButtonUnderMenu 补 list 空桩;层级负路径 2 例(catalogUnderMenu / menuUnderMenu)补 perms 入参使必填放行后由层级拦截(消息断言不变)。 save_editCatalog_noButtonFields改造:断言由「perms 跟随入参」改为「perms 只读保留 DB 原值」(与任务 3 对齐,冲突裁决见透明记录)。- 新增 4 例:新增 catalog 缺 perms 61009 / 新增 menu 缺 perms 61009 / 新增跨类型撞 perms 61009(menu 撞 catalog)/ 编辑不参与唯一性校验(verify never list)。
验收对照
- ✅ 单测全绿:crm-auth 210/0/0(surefire 汇总,ResourceServiceImplTest 40/0);BOM 扫描两文件干净。
- ✅ 运行时冒烟
t02-verify.py5/5(verify profile 起服 + ADMIN debug token):A1 新增 catalog 无 perms → 61009;A2 新增带 perms 成功且跟随入参;A3 跨类型撞码(menu 撞 catalog)→ 61009;U 编辑传不同 perms → 返回保留原值;D 测试节点清理(t02hp_ 前缀防撞远程库,已删)。脚本落盘.scratch/auth-perms/t02-verify.py。 - ⚠ 透明记录:票文验收 3 与任务 3 的断言冲突——验收 3 原文「编辑 catalog 保存 perms 跟随入参的既有断言保持」与任务 3「编辑只读忽略入参」互斥(验收 1 已要求「编辑传不同 perms 保留 DB 原值」)。实现按任务 3 为准:编辑保留原值,「跟随入参」断言移至新增路径(save_newCatalog_success + 冒烟 A2)承载三类统一存储口径。票文缺陷记录于此,不改写原文。
存量盘点(Comments 承诺回填)
t02-perms-audit.py(list 树递归展开,只读):BUTTON 15/15 perms 全有值;CATALOG 3/3、MENU 12/12 共 15 节点 perms 全 null(系统管理/线索管理/商机管理三目录 + 角色管理/菜单管理/部门管理/数据字典/公海/我的线索/我的关注/线索管理/公海池配置/公海与提醒规则/商机阶段设置/方案卡模板设置十二菜单,均出自各 PermissionInitializer 种子建点不设 perms)。欠账影响面(v29 三类必填口径):鉴权零影响(并集只收 button);编辑只读锁对 null 原值放行;全局唯一比对 null 不参与。补数涉及 15 节点命名(撞冒号/下划线风格裁决)+ 各种子初始化器改造,本票不补(拍板「不强制补数」),留全局命名风格票一并裁决。
Comments
- 拍板记录(20260831,已闭环):两处票内拍板获用户确认,必填校验随后追加确认保留(用户口径「是的必填」)。终态口径:采纳必填 + 全局唯一,豁免字符格式校验;存量数据缺口维持「实现时盘点回填 Comments、不强制补数」。三缺口范围(必填 / 全局唯一 / 编辑只读锁)全部确认,同日实现完成(见 Answer)。
- (待实现后回填:存量种子 catalog/menu perms=null 盘点结论——若 null 存量存在,按 v29 必填口径属欠账,但编辑只读锁不会触发必填校验,仅影响新增同名校验的记忆性比对;是否补数在实现时记录。)