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.
104 lines
14 KiB
104 lines
14 KiB
|
16 hours ago
|
# Handoff: a7-parity 执行票 07→12 串行实施(2026-09-07)
|
||
|
|
|
||
|
|
> 注:OS 临时目录不可写(沙箱限制),按 .scratch 临时区惯例落盘于此。
|
||
|
|
|
||
|
|
## 会话状态
|
||
|
|
|
||
|
|
- effort:`.scratch/a7-parity/`(蓝湖 A7 后台管理原型-代码对齐)
|
||
|
|
- **Map 决策阶段已收官**:票 01-06 全 resolved,拍板存档见 `issues/06-gap-verdict.md` + `gap-report.md`
|
||
|
|
- 用户指令:**串行执行执行票 07→12**(每票 claim→实现→resolve;最后票 12 收官验收)
|
||
|
|
- **票 07 已 claim、侦察完毕、设计定案、尚未写一行代码**——本文档核心就是把侦察成果完整传递
|
||
|
|
- Todo:t07 IN_PROGRESS;t08-t12 PENDING
|
||
|
|
|
||
|
|
## 不重复的既有素材(直接引用,勿重读)
|
||
|
|
|
||
|
|
| 素材 | 路径 |
|
||
|
|
|---|---|
|
||
|
|
| 票面 07-12 | `.scratch/a7-parity/issues/07-a71-a72-light-endpoints.md` ~ `12-bruno-regen-baseline.md` |
|
||
|
|
| 四张盘点矩阵 | 同目录 `matrix-a7-1-a7-2.md` / `matrix-a7-3-upper.md` / `matrix-a7-3-lower.md` / `matrix-a7-4-a7-5.md` |
|
||
|
|
| 缺口总报告 | `gap-report.md`(缺接口 9 组/语义差异 6/tag 12 处) |
|
||
|
|
| Map(含全部拍板行) | `map.md` |
|
||
|
|
| 原型文本库 | `.scratch/opportunity-bugfix/lanhu-pages/A7-*.md`(34 文件) |
|
||
|
|
| 票 08 素材 | matrix-a7-3-upper §推送规则;参照实现=`crm-rule` 的 OpportunitySchemeCardTemplate*(版本化 8 端点同构) |
|
||
|
|
| 票 09 素材 | matrix-a7-3-lower §查重;原型 `A7-3-2-4-1_查重规则(详情页).md`(602 行 4 步骤向导) |
|
||
|
|
| 票 10 素材 | 原型 `A7-3-4_项目规则.md`(264 行 5 区块);LeadPool 现状=crm-rule LeadPoolController + PoolMemberStore |
|
||
|
|
| 票 11 素材 | tag 8 处清单在票面;种子参照 `crm-dict/.../config/DictPermissionInitializer.java`(permission-seeder seam 先例) |
|
||
|
|
| AGENTS.md 纪律 | BOM 剥离脚本 / 孤立单大写字母 @Column 钉名 / ADR-0017 表单绑定 |
|
||
|
|
|
||
|
|
## 全局拍板(票 06 已定,勿再问用户)
|
||
|
|
|
||
|
|
① P0 全建,重接口(同步任务 5 端点、全局日志 5 端点+AOP)记账不建;② 新规则 Controller 全进 crm-rule;③ 日志形态 A;④ 数据共享不纳入。次级默认值:资源删除语义按原型改(级联→禁删)、LeadPool 部门成员=显式成员表 DEPT_MEMBER、查重「执行次数」=引用次数、幻影 3 端点保留挂运维 tag。全程不提交 git(后端仓+文档仓 E:\code\crm-api-docs 均留用户审查)。
|
||
|
|
|
||
|
|
## 票 07 侦察结论(本会话独有,重读代价高,务必信任)
|
||
|
|
|
||
|
|
### 代码事实
|
||
|
|
|
||
|
|
- **crm-auth/pom.xml**:依赖 crm-base+crm-file;easyexcel 4.0.3 在 crm-base(compile scope 可传递);StringRedisTemplate 直接注入有先例(SessionStoreImpl/CorpAccessTokenProviderImpl)
|
||
|
|
- **SystemController**(156 行):`/api/system` 下已有 menus/tree、depts/tree|save|delete、users/assign-roles(单用户全量替换,@RequestParam userId+roleIds 逗号串)、users/assign-depts、users/page、users/stats、sync/dingtalk、users/admin-role;底部有 `splitIds(String)` 私有工具
|
||
|
|
- **RoleController**(80 行):`/api/roles` page/saveOrUpdate/detail/assign-resources/delete;@PreAuthorize 用 `hasAuthority('crm:role:*')`;tag「A7 后台管理/权限管理/角色管理」✅ 不用动
|
||
|
|
- **ResourceController**(55 行):`/api/resources` list/saveOrUpdate/delete/icon/upload;**类级 @PreAuthorize("hasRole('ADMIN')")**;4 个端点 tag 均为「A7 后台管理/权限点管理」→ 待补「权限管理」一级
|
||
|
|
- **SysRole 实体**:仅 roleName/roleCode/sort/remark/builtin——**缺 roleType/status 两列**
|
||
|
|
- **SysMenu 实体**:status 列已存在(varchar(16) default 'enabled'),但只有 button 类型写值;**catalog/menu 存量行 status=null → 一律视为 enabled**
|
||
|
|
- **AuthUser 实体**:username/mobile/email/avatar/enabled/lastLoginTime/deptId/employmentStatus/title/account——筛选列全齐,无需 DDL
|
||
|
|
- **UserPageParam**:deptId/employmentStatus/loginFrom/loginTo(+BaseParam keyword/current/size/orderBy/asc)
|
||
|
|
- **RoleParam**:空壳(仅继承 BaseParam)
|
||
|
|
- **UserStatsDTO**:@AllArgsConstructor **4 参**(deptCount/activeCount/resignedCount/unassignedCount)——加 3 字段后须同步改 UserQueryServiceImpl.stats() 的**两处 new 调用**(约 144 行空集分支、157 行正常分支)
|
||
|
|
- **UserQueryServiceImpl**(216 行):pageUserList 的 scopedUserIds 子树预筛→wrapper→批量拼装部门/角色名模式照抄扩展;stats() 计数模式
|
||
|
|
- **AuthUserServiceImpl**(168 行):assignRoles=先删后插全量替换;**configureAdminRoles=增量先例**(查已有行,仅插缺失/仅删命中)——批量增量照此模式
|
||
|
|
- **SysRoleServiceImpl**(192 行):pageRoles/saveRole(builtin 保护+code 唯一)/saveRoleWithScopes/getRoleDetail/assignResources(祖先补全)/deleteRoleCascade(builtin 保护+级联三表)——deleteRoleCascade 需**补「在职使用人数=0 才可删」校验**(矩阵三#7)
|
||
|
|
- **ResourceServiceImpl**(296 行):**delete 现状=子节点检查+级联清理 sys_role_menu(103-127 行,要改写)**;save 校验链完整;applyToEntity 只对 button 写 status
|
||
|
|
- **RoleScopeStore**:load(roleId)/save(roleId, moduleScopes)/delete(roleId)——copy 角色复制数据范围档位用它
|
||
|
|
- **IDtOrgSyncService**:只有 syncOrg();**lastSyncTime 无任何现成存储**→定案见下
|
||
|
|
- **AuthConstants 错误码 61xxx**:已用至 61018(61009 是 ResourceServiceImpl 私有常量 CODE_RESOURCE_INVALID)→新码从 **61019** 起
|
||
|
|
- **权限码**:crm:role:list/detail/save/delete/assign 已由 DataInitializer 种子化(ButtonSeed×5)。**定案:新角色端点复用现有码**(stats→crm:role:list;copy/enable/disable→crm:role:save),不改 DataInitializer
|
||
|
|
- **跨域 seam 先例**:PermissionSeeder 接口在 **crm-base.service**、各业务模块实现 → UserTransferPort 同位放 crm-base.service
|
||
|
|
- **三域 owner 字段**:Lead.ownerUserId(pool_id/dept_id/owner_user_id 三层)、Customer.ownerUserId(+owner_dept_id)、Opportunity.ownerUserId(+owner_dept_id+owner_name_snapshot);**「项目」域=同表 oppStatus=5(已转项目)**
|
||
|
|
- **crm-opportunity 已有 E9 移交 transition**(CAS,但只允许状态 2/3)→ **定案:port 实现直接批量 update 直改 owner(低频管理动作,覆盖所有状态),域内轨迹不做、依赖 sys_user_transfer_log 全局快照**(轻票定位,Answer 记录)
|
||
|
|
- 业务模块全部依赖 crm-auth → port 实现可注入 AuthUserMapper(拿接收人 deptId,**owner_dept_id 跟人走**,Opportunity 注释明示「移交跟人走」)与 ISysDeptService
|
||
|
|
- **导出无先例**(全仓无 export 端点);UserListDTO 有嵌套 List 不能直接喂 EasyExcel
|
||
|
|
|
||
|
|
### 设计定案(写码时照此执行)
|
||
|
|
|
||
|
|
1. **批量分配角色=增量语义**:⚠️ 票面写「多用户全量替换」与原型 u296 原文「提交后为 2 名员工增加"销售人员"角色;**不会覆盖其已有角色**,也不会修改钉钉资料」矛盾——**按原型增量实现**(对每个 userId 查已有 roleId 集,仅插缺失行;configureAdminRoles 同款),在票 07 Answer 记录此偏差及依据
|
||
|
|
2. **UserTransferPort**(crm-base 新建):`String domain()` / `long countOwned(Long userId)` / `int transferOwned(Long fromUserId, Long toUserId)`;4 个实现:crm-lead×1(lead)、crm-customer×1(customer)、crm-opportunity×2(opportunity=oppStatus!=5 / project=oppStatus=5)
|
||
|
|
3. **sys_user_transfer_log 新表**(SysUserTransferLog 实体+Mapper):fromUserId/toUserId/domainsJson(各域计数快照)/operatorUserId/transferTime;命名过孤立单大写检查(from_user_id/domains_json ✅)
|
||
|
|
4. **UserTransferService(crm-auth 新)**:注入 `List<UserTransferPort>`(ObjectProvider 防空);`preview(userId)`→List<TransferDomainCountDTO>(domain/domainName/count);`transfer(userId, receiverId)`→校验接收人存在在职启用+≠userId→逐 port transferOwned→写 transfer log(事务)
|
||
|
|
5. **角色 status/roleType**:SysRole 加 `roleType varchar(20)`(存 code:SYS/MANAGE/BIZ/FUNC,服务层校验 4 值)+ `status varchar(16) default 'enabled'`(对齐 SysMenu 先例);role_type ✅ 无命名坑
|
||
|
|
6. **copyRole**:复制 roleType/remark/功能权限(sys_role_menu 全套)/数据范围(RoleScopeStore.load→save);**不复制人员**;生成 **status=disabled 停用草稿**;roleCode 服务端生成(原码+后缀或时间戳,唯一即可)
|
||
|
|
7. **enableRole 校验**(原型 u163-u186):≥1 个 menu 类型授权(resourceIds 含 menu 节点)+ 四域(lead/opportunity/customer/project)数据范围档位齐全,否则报错;disableRole 直改 status
|
||
|
|
8. **userCount(在职使用人数)**:批量算——page 出参对当页 roleIds:selectList(sys_user_role in roleIds)→userIds→AuthUserMapper selectList(id in, employment_status='active')→按 role 归组计数;detail 同步填
|
||
|
|
9. **RoleStatsDTO**:roleCount/enabledCount/disabledCount/zeroUsageCount(active 使用人数=0 的角色数)
|
||
|
|
10. **资源 enable/disable/references**:disable 时查 sys_role_menu 中引用该资源且**角色 status=enabled** 的行>0→禁止停用(61020);references(id)→List<ResourceReferenceDTO>(roleId/roleName/roleStatus/activeUserCount);enable/disable 后 apiPermissionCache.invalidate()
|
||
|
|
11. **资源 delete 语义改写**(拍板项):删掉级联清理 sys_role_menu 代码;校验链=①无子节点②**仅 status=disabled 可删**(null 视为 enabled)③**有任何 sys_role_menu 引用→禁止删除并提示引用方**;发现现网调用方依赖级联语义则停下报票 06 复议(票面原文)
|
||
|
|
12. **lastSyncTime**:IDtOrgSyncService 加 `LocalDateTime getLastSyncTime()`;impl 注入 StringRedisTemplate,syncOrg() 成功尾部写 key `crm:auth:sync:last-sync-time`(ISO 字符串),读时反解析;注释挂账「任务化时迁表」
|
||
|
|
13. **stats 三时间戳**:UserStatsDTO +lastSyncTime(来自 Redis)/lastTransferTime/lastTransferUser(查 sys_user_transfer_log 最新一条,transferUser=接收人名)
|
||
|
|
14. **导出**:新建 UserExportRow 平铺 DTO(@ExcelProperty:姓名/账号/职务/手机号/主部门/兼职部门逗号串/在职状态/角色逗号串/最后登录时间);EasyExcel 写 ByteArrayOutputStream→`FileApi.upload(bytes, "用户列表-yyyyMMddHHmmss.xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "system")`→返回 `Result<FileInfoDTO>`,前端拿 fileId 走既有下载端点
|
||
|
|
15. **UserPageParam 4 筛选落地**:username/mobile/title=like;roleId=先 selectList(sys_user_role where role_id in)拿 userIds→wrapper.in(id, userIds)(空集短路返空页,同 scopedUserIds 模式)
|
||
|
|
16. **tag 修正(票 07 范围 5 处)**:ResourceController 4 处→「A7 后台管理/权限管理/权限点管理」;sync/dingtalk→「A7 后台管理/用户管理/同步任务」;**幻影 3 项**:depts/save、depts/delete 的 tag 从「部门管理」改挂「A7 后台管理/运维」,users/admin-role 已是「运维」不动;depts/tree 票面未列→保留「部门管理」不动
|
||
|
|
17. **新错误码**:61019 移交参数非法 / 61020 资源被启用角色引用禁操作 / 61021 角色启用校验不过(enable 校验失败)
|
||
|
|
|
||
|
|
### 待写文件清单(票 07 剩余工作)
|
||
|
|
|
||
|
|
**新建**:crm-base `service/UserTransferPort.java`;crm-auth `domain/entity/SysUserTransferLog.java`、`mapper/SysUserTransferLogMapper.java`、`domain/dto/TransferDomainCountDTO.java`、`domain/dto/UserTransferResultDTO.java`(可并入 Service 返回 Map 或简化)、`domain/dto/RoleStatsDTO.java`、`domain/dto/ResourceReferenceDTO.java`、`domain/dto/UserExportRow.java`、`service/UserTransferService.java` + `service/impl/UserTransferServiceImpl.java`
|
||
|
|
|
||
|
|
**修改**:UserPageParam(+4 筛选)、UserStatsDTO(+3 字段)、IUserQueryService/UserQueryServiceImpl(筛选落地+stats 时间戳+改两处 new)、IAuthUserService/AuthUserServiceImpl(assignRolesBatch 增量)、SysRole(+roleType/status)、RoleParam(+roleType/status)、RoleDTO(+roleType/status/userCount,fromEntity/toEntity 补映射)、ISysRoleService/SysRoleServiceImpl(roleStats/copyRole/enableRole/disableRole+delete 补人数校验+pageRoles 补筛选+userCount 批量)、IResourceService/ResourceServiceImpl(enable/disable/references+delete 改语义)、IDtOrgSyncService/DtOrgSyncServiceImpl(lastSyncTime)、AuthConstants(+61019/61020/61021)、SystemController(+4 端点+tag)、RoleController(+4 端点)、ResourceController(+3 端点+4 tag)、业务模块 4 个 port 实现(crm-lead×1/crm-customer×1/crm-opportunity×2,放各自 config 或 service 包,@Component 注册)
|
||
|
|
|
||
|
|
### 票 07 验收与收尾
|
||
|
|
|
||
|
|
- 全仓 *.java BOM 扫描 0(AGENTS.md 有 PowerShell 剥离脚本);`mvn compile` 过;单测全绿(若 crm-auth 既有测试受 SysRole 新列/构造签名变化影响需同步修)
|
||
|
|
- SearchReplace 票 07:Status→resolved + Answer(含增量语义偏差记录、级联→禁删已改、记账项);map.md Decisions 追加票 07 行;TodoWrite t07 COMPLETE
|
||
|
|
- 票 08→12 依次推进;票 12 收官时跑 bruno-sync 全量重生成(文档仓 E:\code\crm-api-docs,bare collection 布局)+对账双向零差+基线验收
|
||
|
|
|
||
|
|
## 环境注意(踩过的坑)
|
||
|
|
|
||
|
|
- PowerShell 环境:`python -c` 内联与脚本执行不可靠(ExitCode 1);文件名清单用 `Get-ChildItem | Out-File -Encoding utf8`;代码检索一律用 Grep/Glob/Read 工具(ripgrep,可靠)
|
||
|
|
- mvn 编译若报 `illegal character: '\uFEFF'` = 文件带 BOM,按 AGENTS.md 脚本剥离
|
||
|
|
- 大段 CJK Markdown 编辑走 Write 新文件或小步 SearchReplace,避免超大单块替换
|
||
|
|
- 每票收工时对照 AGENTS.md「新模块接口接入 checklist」(URL 扁平动词/深 tag/bruno 配置)
|
||
|
|
|
||
|
|
## Suggested skills
|
||
|
|
|
||
|
|
- **implement**(票 08-12 大块实现时可直接按票面+本文档定案实施,无需再侦察)
|
||
|
|
- **bruno-sync**(票 12 收官必用:全量重生成+对账)
|
||
|
|
- 无需 grilling(拍板已全部完成,遇到票面与原型冲突按原型+Answer 记录偏差处理;若发现现网依赖级联删除语义则停下报票 06 复议)
|