4.2 KiB
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 写入链路
- 请求进入 → 路由
@oper_log在 handler 执行前/包装器内设置ContextVar call_next完成 → 中间件finally读 ContextVar,与 JWT 字段一并_persist_log- 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
- 执行
docs/sql/migrations/2026-06-29-operation-audit.sql - 部署后端(中间件 + 装饰器 + API)
- 部署前端(管理页 + CRM Tab)
- 确认生产
API_REQUEST_LOG_ENABLED=true - 为核心 CRM 写接口补
@oper_log
Open Questions
- 日志在线保留天数(默认 90 天归档,待客户确认)
- 响应体是否额外脱敏手机号/身份证(默认保持现状,仅 password/token 类)