95 lines
4.2 KiB
Markdown
95 lines
4.2 KiB
Markdown
## Context
|
||
|
||
已有 `ApiRequestLogMiddleware` 全量采集 Web API 至 `sys_api_request_log`(path、query、request_body、response_body、user_id、username、create_at 等)。`operlog/list` 将上述数据适配为 RuoYi `OperLogVO`,权限为 admin/super_admin,CRM 详情 Tab 已调用但未按业务实体过滤。
|
||
|
||
约束:MySQL 5.7、租户隔离、数据权限、Fresh Mint UI(P01 列表)、中间件与 `ApiCryptoMiddleware` 顺序不变(Crypto 外层、RequestLog 内层,记录明文 JSON)。
|
||
|
||
## Goals / Non-Goals
|
||
|
||
**Goals:**
|
||
|
||
- 客户可见:姓名、时间、地址、入参、出参
|
||
- 业务语义可筛、可按 `business_id` 关联单据
|
||
- 写操作通过 `@oper_log` 声明;未声明接口保留路径推断兜底
|
||
- 全局操作日志管理页
|
||
|
||
**Non-Goals:**
|
||
|
||
- 前端 UI 行为埋点(不发 API 的操作)
|
||
- 独立审计子系统(MQ/ES)
|
||
- 与 CRM Java 仓 `sys_oper_log` 数据合并
|
||
- 本期全量 `@oper_log` 覆盖所有写接口
|
||
- multipart 文件内容入库
|
||
|
||
## Decisions
|
||
|
||
### D1 方案 A + 语义字段(不新建 sys_oper_log)
|
||
|
||
在 `sys_api_request_log` 扩展列,中间件继续全量采集;语义由 `@oper_log` + ContextVar 注入。理由:基础设施已存在,改动面可控。
|
||
|
||
### D2 语义字段集
|
||
|
||
| 列 | 说明 |
|
||
|----|------|
|
||
| `tenant_no` | 租户隔离 |
|
||
| `business_title` | 模块,如 `CRM客户管理` |
|
||
| `business_type` | tinyint,对齐 `sys_oper_type`(0其他 1新增 2修改 3删除 5导出 6导入) |
|
||
| `business_id` | 业务主键,可空 |
|
||
| `oper_summary` | 人读摘要,如 `修改客户【杭州某某化工】` |
|
||
|
||
展示优先级:`oper_summary` 用于列表摘要;`title`/`businessType` 映射 operlog API 字段。
|
||
|
||
### D3 写入链路
|
||
|
||
1. 请求进入 → 路由 `@oper_log` 在 handler 执行前/包装器内设置 `ContextVar`
|
||
2. `call_next` 完成 → 中间件 `finally` 读 ContextVar,与 JWT 字段一并 `_persist_log`
|
||
3. ContextVar 为空时:`business_title`/`business_type` 由 `oper_log.py` 现有路径规则在**读时**兜底(落库可为 NULL,或中间件落库时同步写推断值——实现取**落库时写推断值**以减少读时计算)
|
||
|
||
### D4 请求人姓名
|
||
|
||
JWT payload 已有 `real_name`(nick_name)。中间件 `_claims_from_auth` 扩展解析并写入 `create_name`;`username` 仍为 `user_no`。
|
||
|
||
### D5 oper_summary 模板
|
||
|
||
`{动词}{对象}【{显示名或编号}】`,动词与 `business_type` 一致。装饰器可传 `summary` 或模板参数;Service 层可在执行业务后更新摘要(二期可选)。
|
||
|
||
### D6 查询与权限
|
||
|
||
- `operlog/list`:DB 层筛选 `tenant_no`、语义字段;数据权限仍按 `user_id`
|
||
- 管理页菜单:`system:operlog:list`;仅 admin/super_admin(与现 monitor API 一致)
|
||
- CRM Tab:`business_id` + 可选 `business_title` 精确过滤
|
||
|
||
### D7 记录范围(默认)
|
||
|
||
- **记录**:经 FastAPI 的业务 API(含 GET 列表)
|
||
- **跳过**:OPTIONS、/health、/docs、/openapi.json(与现中间件一致)
|
||
- **不完整**:multipart、超大 body、敏感字段脱敏(保持现逻辑)
|
||
|
||
客户若后续要求「仅写操作」,通过 `API_REQUEST_LOG_EXCLUDE_PATHS` 配置扩展(本期仅预留 settings 字段,默认空)。
|
||
|
||
### D8 归档
|
||
|
||
表 `sys_api_request_log_his` 已存在;本期迁移脚本扩展同名列。定时归档任务记入 tasks 后续项,不阻塞主功能。
|
||
|
||
## Risks / Trade-offs
|
||
|
||
| 风险 | 缓解 |
|
||
|------|------|
|
||
| 日志量膨胀 | 后续归档 + 可选排除只读路径 |
|
||
| 未注解接口语义弱 | 路径推断兜底 + 逐步补注解 |
|
||
| 响应体含 PII | 保持脱敏;详情仅授权角色可见 |
|
||
| ContextVar 在子线程丢失 | 落库用 `asyncio.to_thread` 时传入已收集的 dict,不依赖子线程读 ContextVar |
|
||
|
||
## Migration Plan
|
||
|
||
1. 执行 `docs/sql/migrations/2026-06-29-operation-audit.sql`
|
||
2. 部署后端(中间件 + 装饰器 + API)
|
||
3. 部署前端(管理页 + CRM Tab)
|
||
4. 确认生产 `API_REQUEST_LOG_ENABLED=true`
|
||
5. 为核心 CRM 写接口补 `@oper_log`
|
||
|
||
## Open Questions
|
||
|
||
- 日志在线保留天数(默认 90 天归档,待客户确认)
|
||
- 响应体是否额外脱敏手机号/身份证(默认保持现状,仅 password/token 类)
|