112 lines
5.1 KiB
Markdown
112 lines
5.1 KiB
Markdown
## 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 不写入数据库
|