Files
shanxia2.0/openspec/changes/operation-audit-enhancement/design.md
T
2026-06-29 11:15:39 +08:00

95 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 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 类)