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

112 lines
5.1 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.
## ADDED Requirements
### Requirement: API 请求全量采集
系统 SHALL 通过 `ApiRequestLogMiddleware` 记录经 FastAPI 处理的 API 请求,并写入 `sys_api_request_log`。每条记录 SHALL 包含 HTTP 方法、路径(接口地址)、Query 字符串、请求体、响应体、HTTP 状态码、耗时、IP、User-Agent、请求时间(`create_at`)、用户 ID、用户账号(`user_no`)及用户姓名(`nick_name` → `create_name`)。
#### Scenario: 记录业务 API 入参与出参
- **WHEN** 已认证用户调用 JSON 业务接口(如 `POST /api/web/crm/customer/update`)
- **THEN** 系统持久化 `path`、`query_string`、`request_body`、`response_body`
- **AND** `create_at` 为请求完成时刻
- **AND** `create_name` 为 JWT 中的 `real_name`
#### Scenario: 敏感字段脱敏
- **WHEN** 请求或响应 JSON 包含 `password`、`token`、`authorization` 等敏感键
- **THEN** 落库值 SHALL 为 `***` 而非明文
#### Scenario: 跳过高噪与健康检查路径
- **WHEN** 请求为 `OPTIONS` 或路径为 `/health`、`/docs`、`/openapi.json`
- **THEN** 系统 SHALL 不写入 `sys_api_request_log`
#### Scenario: 文件上传不记录文件体
- **WHEN** 请求 Content-Type 为 `multipart/form-data`
- **THEN** `request_body` SHALL 标记为省略说明而非文件二进制内容
### Requirement: 业务语义字段落库
系统 SHALL 在 `sys_api_request_log` 存储业务语义列:`tenant_no`、`business_title`、`business_type`、`business_id`、`oper_summary`。写操作接口 SHOULD 通过 `@oper_log` 声明上述字段;未声明时系统 SHALL 使用路径与方法推断规则填充 `business_title` 与 `business_type`(`business_id` 与 `oper_summary` 可为空)。
#### Scenario: 装饰器声明修改客户
- **WHEN** 带 `@oper_log(title="CRM客户管理", business_type=2, business_id_param="id", summary="修改客户【{name}】")` 的更新接口被调用且 ContextVar 已设置
- **THEN** 落库 `business_title=CRM客户管理`、`business_type=2`
- **AND** `business_id` 为请求中的客户 id
- **AND** `oper_summary` 为可读摘要文本
#### Scenario: 只读列表无装饰器
- **WHEN** 用户调用客户列表 GET 接口且路由无 `@oper_log`
- **THEN** 系统仍记录 path 与入参出参
- **AND** `business_title` 由路径推断(如 `CRM客户管理`)
- **AND** `business_type` 为 `0`(其他)
#### Scenario: business_type 枚举
- **WHEN** 系统写入 `business_type`
- **THEN** 值 SHALL 属于:0其他、1新增、2修改、3删除、5导出、6导入(与 `sys_oper_type` 字典一致)
### Requirement: 租户隔离
操作日志查询 SHALL 按当前用户 `tenant_no` 过滤 `sys_api_request_log.tenant_no`。中间件落库时 SHALL 从 JWT 写入 `tenant_no`。
#### Scenario: 租户数据不可见
- **WHEN** 租户 A 的管理员查询操作日志
- **THEN** 结果 SHALL 不包含租户 B 的记录
### Requirement: 操作日志查询 API
系统 SHALL 提供 `POST /api/web/monitor/operlog/list` 与 `POST /api/web/monitor/operlog/detail`,将日志适配为 RuoYi `OperLogVO` 结构:`operUrl`(path)、`operParam`(request_body)、`jsonResult`(response_body)、`operName`(create_name 优先)、`operTime`、`title`(business_title)、`businessType`。
#### Scenario: 列表筛选业务实体
- **WHEN** 请求体包含 `businessId` 与可选 `businessTitle`
- **THEN** 返回匹配 `business_id` 的日志分页列表
#### Scenario: 详情含完整入参出参
- **WHEN** 管理员请求某条日志详情
- **THEN** 响应 SHALL 包含完整 `operParam` 与 `jsonResult`(受库内截断上限约束)
#### Scenario: 数据权限
- **WHEN** 非 super_admin 用户查询列表
- **THEN** 结果 SHALL 受数据权限约束(按操作人 `user_id` 范围)
#### Scenario: 访问控制
- **WHEN** 非 admin/super_admin 用户调用 operlog API
- **THEN** 系统 SHALL 拒绝访问
### Requirement: 操作日志管理页
web-admin SHALL 提供操作日志标准列表页(界面模式 P01),支持按模块、操作类型、操作人、状态、时间范围筛选;详情展示接口地址、入参、出参与摘要。按钮与表格 SHALL 符合 Fresh Mint 与 `web-admin-crud-dialog` 规范。
#### Scenario: 管理员查看日志列表
- **WHEN** 已授权的 admin 用户打开操作日志页
- **THEN** 展示分页列表,列含模块、类型、操作人、时间、摘要、状态
- **AND** 可打开详情查看 `operParam` 与 `jsonResult`
### Requirement: CRM 业务详情操作日志 Tab
CRM 客户详情与合同详情中的操作日志 Tab SHALL 按 `business_id` 调用 operlog 列表,不再拉取全量日志后前端过滤。
#### Scenario: 客户详情日志
- **WHEN** 用户在某客户详情打开「操作日志」Tab
- **THEN** 仅展示 `business_id` 等于该客户 id 的日志
### Requirement: 配置与生产开关
`API_REQUEST_LOG_ENABLED` 在生产环境 SHALL 为 `true`。body 截断上限 SHALL 保持可配置(`API_REQUEST_LOG_MAX_BODY`、`API_REQUEST_LOG_MAX_BODY_BYTES`)。
#### Scenario: 关闭采集
- **WHEN** `API_REQUEST_LOG_ENABLED=false`
- **THEN** 中间件 SHALL 不写入数据库