# 01 — 创建 PermissionSeeder seam **What to build:** crm-base 中定义 `PermissionSeeder` interface、`PermissionModuleDescriptor` 和 `ButtonSeed` record。crm-auth 中实现 `PermissionSeederImpl`,吸收当前 `DataInitializer` 和 `DictPermissionInitializer` 重复的 find-or-create + bind 逻辑——按 `name+parentId` 查目录和菜单、按 `perms` 查按钮(幂等键)、按 `roleCode` 查角色并绑定。硬编码 `sys_menu` 的 schema 默认值(`menuType`、`denyBehavior="hide"`、`status="enabled"`、`visible=true`),调用方不感知这些字段。 **Blocked by:** None — 可立即开始 **Status:** ready-for-agent - [ ] `PermissionSeeder` interface 存在于 crm-base,含单个 `seedModule(PermissionModuleDescriptor)` 方法 - [ ] `PermissionModuleDescriptor` record 存在于 crm-base domain.dto,字段:catalogName, menuName, menuPath, menuComponent, menuSort, List\ buttons, List\ bindToRoleCodes - [ ] `ButtonSeed` record 存在于 crm-base domain.dto,字段:name, perms, apiUrl, sort - [ ] `PermissionSeederImpl` 存在于 crm-auth service.impl,`@Service` 注解,直接使用 SysMenuMapper / SysRoleMapper / SysRoleMenuMapper - [ ] seedModule 实现 find-or-create:目录(type=1, parentId=0, by name)、菜单(type=2, parentId=catalogId, by name)、按钮(type=3, parentId=menuId, by perms) - [ ] seedModule 实现 bind:按 roleCode 查 sys_role,找到则 bindIfAbsent,找不到 warn 跳过 - [ ] seedModule 幂等:同一描述符调用两次不产生重复记录 - [ ] seedModule 目录共享:两次调用同一 catalogName 只创建一次目录 - [ ] 硬编码默认值:denyBehavior="hide", status="enabled", visible=true, menuType 枚举值——描述符中不含这些字段 - [ ] `PermissionSeederImplTest`(H2 集成测试)通过,覆盖以上所有行为 - [ ] 现有 `DataInitializer` 和 `DictPermissionInitializer` 保持不变,现有测试仍然通过