8.5 KiB
02 全局日志 crm-log 模块(A7-4,新建模块)
Type: task Status: resolved
Question
按一期票 06 拍板(形态 A:新全局表 + AOP 采集;域内三表不动),新建 crm-log 轻模块,补齐 A7-4 日志三页 5 端点。素材:matrix-a7-4-a7-5.md §一 + 文本库 A7-4-1_用户操作日志.md / A7-4-2_登录日志.md / A7-4-3_批量操作日志.md。
- 新模块 crm-log:pom + crm-app 依赖 + CONTEXT.md(挂 CONTEXT-MAP.md);轻模块不并入 crm-auth(票 06 拍板)。
- 表设计(三表贴合原型三页列结构,票内可微调):
- 操作日志:op_time / operator_name / dept_name / account / ip / op_type(登录/查询/新增/修改/删除/处理/导入/导出 8 枚举)/ content / result(成功/失败)
- 登录日志:op_time / operator_name / dept_name / account / ip / status / op_type(登录/退出)
- 批量操作日志:公共列 + op_type(导入/导出/修改/删除)/ page(操作页面)/ result(成功/失败/部分成功)/ 计数明细文本 / 结果文件 id
- 采集机制(票内定 AOP 切面 vs 内联埋点;仓库现无 @Aspect 先例,AOP 依赖需核实):写端点 POST + 登录/退出(crm-auth AuthController)+ 批量操作(crm-base BatchResult 写侧,LeadPool 导入导出已有调用点)。⚠️ 原型枚举含「查询」——全量 GET 采集代价大,覆盖面(全采 vs 写操作+登录/批量先行)为本票显式拍板项。采集失败不阻塞业务主流程。
- 批量结果文件:复用 crm-file/MinIO 落盘;90 天保留期(GET 过期返「文件已过期」)+ 过期清理定时任务(参照 OrphanChunkCleanupTask)。
- 5 端点(
/api/log下,扁平动词 + 查询参数规范 ADR-0017):POST /operation/page、POST /login/page、POST /batch/page(三 PageParam 逐字段对齐矩阵 §一筛选清单:所属组织模糊/操作人模糊/时间范围/opType/批量 result)/ POST /batch/download(ids[] 打包下载)/ GET /batch/file?id=。 - 权限口径:仅系统管理员及审计权限角色可见(原型三页一致);权限种子 @Order(26) + 菜单「A7 后台管理/日志管理」(票 01 用 25,两票并行不撞号)。
- tag:「A7 后台管理/日志管理/用户操作日志」「…/登录日志」「…/批量操作日志」。
- bruno-sync:sourceRoots 加 crm-log;menuBindings 加日志管理菜单;文档 5 份新增。
- 错误码 65xxx 起(如文件过期 65001)。
验收基线:编译过 + BOM 0 + crm-log 单测全绿 + 全仓回归。
Answer
开工前两问拍板(2026-09-08,先于建表写码)
① 采集机制 = 混合:AOP 注解切面兜底写端点 + 内联埋点采登录/退出/批量(SPI 缝合)
依赖核实:全仓 9 模块 pom 均无 spring-boot-starter-aop/aspectjweaver,代码无 @Aspect 先例;但 @Transactional 代理基建已在用(AuthServiceImpl self 注入注释明说「走 Spring AOP 事务代理」,spring-aop 随 spring-context 在 classpath),启用注解切面仅需补 spring-boot-starter-aop(引入 aspectjweaver,版本由 spring-boot-parent 3.3.5 管),风险低。
利弊摆完:
- 纯 AOP 拿不到两类关键上下文:登录失败行(A7-4-2 原型明确有失败行)——异常路径只有错误码无操作人;批量日志的操作页面/计数明细/结果文件 id——导出还是 void 写 response 切不到返回值。这两类只能内联。
- 纯内联:全仓 188 个 POST 写端点逐个埋点侵入大、漏埋率高、新增端点易忘。
- 混合各取所长:AOP 兜底切面自动覆盖全 POST(URL 关键词映射 op_type:save/create→新增、update/edit→修改、delete→删除、import→导入、export→导出、其余→处理,content=URI 摘要),
@OpLog注解可覆写语义内容;登录/退出/批量走 crm-base SPI(OpLogPort,UserTransferPort同构先例)内联埋点。
依赖方向零循环:crm-base 定义 OpLogPort;crm-log 实现 SPI + 切面 + 三表/端点,依赖 crm-auth(部门名解析,crm-rule 先例)与 crm-file(批量结果文件);crm-auth / crm-rule 仅依赖 crm-base 接口埋点,不反向依赖 crm-log。切面排除 /api/auth/**(登录走内联埋点更精确,避免重复采)与 /api/log/**(日志查询自产自销)。
② 采集覆盖面 = 写侧先行(写 POST + 登录/退出 + 批量),不采「查询」
量化:全仓 GET 81 / POST 188。全采「查询」= 81 个 GET 全挂 + 每次列表页刷新产行,行数爆炸、审计价值密度低;写侧先行覆盖审计核心诉求(谁改了什么/谁登了/批量结果),op_type 枚举仍定义原型 8 项(筛选下拉不缺项),采集侧现阶段不产「查询」行,未来要采零改表。符合 map.md 纪律「对齐原型操作类型枚举即可,不追求全端点全采」。
实现清单
| 票面项 | 落地 |
|---|---|
| 1 新模块 | crm-log(pom + root pom modules/dependencyManagement + crm-app 依赖 + CONTEXT.md 挂 CONTEXT-MAP) |
| 2 三表 | sys_log_operation / sys_log_login / sys_log_batch(Hibernate columnDefinition 管 DDL + MyBatis-Plus BaseMapper 管 CRUD,均继承 BaseEntity;op_time 建索引) |
| 3 采集机制(拍板①混合) | OpLogAspect(全仓首个 @Aspect,切 execution(* com.crm..*Controller.*(..)),只采 POST,URL 关键词映射 op_type,@OpLog 注解可覆写;排除 /api/auth/ /api/log/ /api/file/)+ OperationLogCollector(OpLogPort 实现:快照补全/IP/结果文件落盘,全入口自吞异常) |
| 3 内联埋点 | crm-auth AuthServiceImpl 5 钩子(login 成功 / confirmLogin 顶号成功 / doLoginInTx 账号禁用失败 / verifyOrgMembership 非组织成员匿名失败 / logout 退出);crm-rule LeadPoolServiceImpl 3 处(批量删除 / 导入含失败明细 / 导出含 Excel 留档);埋点方一律 ObjectProvider<OpLogPort> 容忍缺 Bean,依赖方向零循环 |
| 4 结果文件 | collector 统一落盘(导出留档载荷优先,failures 自动生成错误报告 Excel,FileApi.upload bizDomain=log)+ 下载时校验 90 天(66001)+ LogFileCleanupTask(cron crm.log.cleanup-cron 默认 3:30,与 crm.file 3:00 错峰;删文件 + 置空 fileId 防重扫,日志行长期保留) |
| 5 端点 | 5 端点全部落地(三页 POST 分页 + POST /batch/download zip 打包重名去重 + GET /batch/file 流式下载对称 FileController) |
| 6 权限 | LogController 类级 @PreAuthorize("hasRole('ADMIN')");LogPermissionInitializer @Order(26) 种子(菜单「A7 后台管理/日志管理」path=/log,仅 ROLE_ADMIN) |
| 7 tag | 三组深 tag 按 ADR-0024 挂齐(方法级多值 tags) |
| 8 bruno-sync | sourceRoots + menuBindings 三页配置;文档 5 份已生成到文档仓 A7 后台管理/日志管理/(三页文件夹) |
| 9 错误码 | 66001 文件已过期 / 66002 无可下载结果文件(偏差:66xxx 段,见下) |
单测:crm-log 34 用例(aspect 12 / collector 7 / service 11 / cleanup 4);全仓回归 1273 用例全绿(含 crm-auth AuthServiceImplTest 18、crm-rule LeadPoolServiceImplTest 28 埋点兼容回归)。实现过程修复一处自暴露缺陷:MP 条件方法 boolean 前缀不阻止参数求值,startOfDay(null) NPE → startOfDay/endOfDay 改空安全。
偏差
- 错误码 65xxx → 66xxx:票面 9 与 map.md 预核均为 65xxx 起,但 README 错误码表证实 65001~65999 已被 crm-lead 占用 → 改取 66xxx 段(66001/66002),README 已补登记行。map.md 预核笔误一并勘误:62xxx=crm-file、65xxx=crm-lead(预核写反)。
- 切面排除清单多一项
/api/file/**:拍板时只列 auth/log 两项,实现时补排除(分片上传高频 POST,非业务写操作),CONTEXT.md 已记录。 - 登录失败行仅覆盖两种可定位场景(账号禁用、非组织成员-匿名行):其余失败形态(钉钉调用异常等)无本地操作人,不落库。
- 批量埋点本票只接线 LeadPool 一处(票面指定调用点):其余模块未来新增批量导入/导出时按 BatchOpLog SPI 补埋,零改表。
- 菜单粒度:「日志管理」一级菜单(path=/log,component=log/index),三页为前端 tab;未为三页各建菜单行(bruno 文档仍按三页深 tag 分文件夹)。
- 「查询」枚举不产行(拍板②写侧先行的自然结果):op_type 8 枚举保留定义,筛选下拉不缺项,采集侧现阶段不产「查询」行。