feat(risk): Enhance risk management functionality and access control

- Updated `RiskListAccess` and `ThresholdWriteAccess` to enforce access control in the risk repository and threshold repository, ensuring only authorized roles can perform sensitive operations.
- Introduced new methods in `RiskRepository` for counting pending alerts and listing alerts with access checks, improving data security and compliance.
- Enhanced the `chat.py` and `deps.py` files to integrate compliance roles into the risk management matrix, allowing for more granular access control.
- Updated documentation to reflect the new testing baseline of 825 passed tests, indicating improved stability and functionality across the application.

This update significantly strengthens the risk management capabilities, ensuring robust access control and compliance with organizational policies.
This commit is contained in:
2026-09-11 17:07:22 +08:00
parent 8556da489a
commit 793c0307f8
46 changed files with 1308 additions and 122 deletions
@@ -35,7 +35,7 @@
| **修改人** | Andrew |
| **测试执行人** | Andrew |
| **评审人** | (待模块负责人确认) |
| **发布建议** | 可合并 `merger`;C-04 无 push/cron 为已知缺口 |
| **发布建议** | 可合并 `merger`;C-04 **演示 push**(`threshold-check?push=` + 脚本)已做 · **无生产 cron** |
---
@@ -146,7 +146,7 @@
| --- | --- |
| **需求是否覆盖** | C-01~05 已实现;C-07/08/11 部分实现(见 REQUIREMENTS) |
| **旧功能是否破坏** | 否 · 786 pytest 全绿 |
| **剩余风险** | ① C-04 无 push/cron;② C-05 非实时、Phase B 未做;③ C-08 无偏离检测;④ 手工 uvicorn 走查待补 |
| **剩余风险** | ① C-04 **无定时 cron**(仅有 inline + 演示 push);② C-05 非实时、Phase B 未做;③ C-08 无偏离检测;④ 手工 uvicorn 走查待补 |
| **建议人工再验** | CUST-9527:持仓+阈值 · nav_query · 「我能买什么」· 「实时净值」reject · save_note 与流水优先级 |
| **是否可发布** | 后端可合并 |
@@ -47,7 +47,7 @@
| **Redis** | Docker 6380(`CacheService.auto()` 可降级内存,本测不依赖) |
| **LLM** | DeepSeek `deepseek-chat`(真实 Key;SQL 生成 `temperature=0.0`) |
| **修复前测试基线** | 816 passed(解读拆分 + 答辩稳 ① 后) |
| **修复后测试基线** | **820 passed**, 1 skipped |
| **修复后测试基线** | **825 passed**, 1 skipped(含 RISK-001 修复 +5 例量级) |
| **前端** | 未涉及(仅后端问数线) |
---
@@ -218,7 +218,7 @@
| **是否发现实际数据泄露** | **否**(当前 LLM 软注入可靠) |
| **剩余风险** | ① ~~缺口 A/B/D~~ **已修复**;② 缺口 C:profile 表白名单存在但 LLM schema 未暴露(LOW,潜在);③ LLM 软注入仍为第一道防线,硬兜底为第二道 |
| **建议人工再验** | 复跑 `sandbox_domain_test.py`;库内 `analytics_query_log` 出现 `exec_status='blocked'`;更换模型后回归 §6.3 |
| **是否可发布** | 后端可合并(820 pytest) |
| **是否可发布** | 后端可合并(**825 pytest** 全仓基线) |
---
@@ -228,7 +228,7 @@
| --- | --- | --- | --- |
| 模块负责人 | zhangyong | | ☐ 通过 ☐ 待改(开发侧 v1.3 已闭环,待负责人签) |
| 发现人 / 测试 | Andrew | 2026-09-11 | ☑ 沙盘 + 自动化回归 |
| 修改人 | Andrew | 2026-09-11 | ☑ §7 A/B/D 已修 · **824 pytest** · N-03/N-07 接续 |
| 修改人 | Andrew | 2026-09-11 | ☑ §7 A/B/D 已修 · **825 pytest**(全仓)· N-03/N-07 已接续 |
---
@@ -0,0 +1,8 @@
# TEST-RISK-001 沙盘包
| 中文职责 | 路径 |
| --- | --- |
| 企业级测试日志(v1.0 发现 · v1.1 修复) | `TEST-LOG-2026-09-11-RISK-001.md` |
| 本机可重复沙盘脚本 | `../../../scripts/dev/sandbox_risk_test.py` |
**当前结论:** F1–F11 子集已修 · 全仓 **`825 pytest`** · 拍板 F2=service_risk 只读 GET · F3=compliance 进 risk JWT 矩阵。
@@ -0,0 +1,297 @@
# 企业级测试日志 · TEST-2026-09-11-RISK-001
> 风险 Agent(风控监测)端到端沙盘:表域 / 权限 / 触发方式 / 对话线 / UX 静态契约分析
---
## 1. 文档元数据
| 字段 | 值 |
| --- | --- |
| **测试记录编号** | TEST-2026-09-11-RISK-001 |
| **缺陷/变更标题** | 风险 Agent 全功能端到端体检;发现仓储层单层防线(高危)×1 + 角色矩阵/契约口径 ×11 |
| **文档版本** | v1.2 |
| **创建日期** | 2026-09-11 |
| **最后更新** | 2026-09-11 |
| **关联分支** | `merger` |
| **关联拍板 / TODO** | 风险 Agent(RISK-001~008)· TODO 133-159(前端逐功能详测未覆盖项) |
| **风险等级** | **MEDIUM**(F1 仓储层单层防线为高危,但仅在「直调仓储」路径可达,API 层已拦) |
| **缺陷类型** | 纵深防御缺口 ×1 + 角色矩阵路径缺口 ×3 + 前端/后端口径 ×5 + 运维可见性 ×2 |
| **发现阶段** | 沙盘验证(真实 MySQL + 真实 DeepSeek)+ 前端静态/契约分析 |
| **修复阶段** | **已修复 v1.1–v1.3**(F1–F11 + **F12** 对话线 deny)· **`827 pytest` 绿** · v1.2 复跑 **50 PASS** |
---
## 2. 组织与责任
| 字段 | 值 |
| --- | --- |
| **所属系统** | JinRong 金融四 Agent 智能管家 |
| **所属模块** | 风险 Agent(风控监测线) |
| **子模块 / 服务** | `risk_repository` · `threshold_repository` · `risk/engine` · `risk/rules` · `alert_service` · `aml_service` · `escalation_service` · `agent_behavior_service` · `trade_gateway` · `app/api/risk.py` · `app/api/simulate.py` · `app/api/chat.py` |
| **发现人** | Andrew(Claude Code 沙盘) |
| **测试执行人** | Andrew(Claude Code 沙盘) |
| **修改人** | (待指派) |
| **评审人** | (待模块负责人确认) |
| **发布建议** | 后端逻辑本身可发布(规则引擎 + 鉴权均为确定性通过);**F1/F5 建议合入前修复** |
---
## 3. 环境与基线
| 字段 | 值 |
| --- | --- |
| **测试环境** | development · 本机 Windows 11 |
| **Python** | 3.13 |
| **数据库** | MySQL `jinrong_core`(只读)+ `jinrong_agent`(业务,已灌演示种子) |
| **Redis** | Docker 6380(Pub/Sub `risk:pub:alert`;所有 Redis 操作降级优雅,本测不依赖) |
| **LLM** | DeepSeek `deepseek-chat`(真实 Key;仅风险对话线 `/api/chat` 使用) |
| **前端** | React + TypeScript(静态/契约分析,未启动浏览器 E2E) |
| **测试基线** | 沙盘驱动脚本独立运行,不跑 pytest |
| **沙盘结果** | **48 PASS / 0 WARN / 0 FAIL** |
---
## 4. 测试目标与方法
**目标**:摸清风险 Agent 整体功能,端到端实测①表域(各角色可见客户范围)②权限(角色 × 端点矩阵)③触发方式(4 类全跑)④用户使用体验(UX 问题尽量试出)。
**方法**:`fastapi.testclient.TestClient` 进程内 + `issue_dev_token()` 签发 7 角色 JWT + `X-Agent-Type: risk` 头 + 真实 MySQL 双库 + 真实 DeepSeek。规则引擎是纯函数(不依赖 LLM),对话线走真实 DeepSeek。另叠加「直调仓储探针」(不经过 API 层)坐实单层防线结论;前端做静态/契约比对(走读源码,未跑浏览器)。
**驱动脚本**:`scripts/dev/sandbox_risk_test.py`(新增,独立 `TestClient(app)`,`main()` 逐节跑 A~D,打印 `[PASS]/[WARN]/[FAIL]` + 关键字段,存在 FAIL 即非零退出)。
---
## 5. 角色与权限矩阵
**Token 映射**(`issue_dev_token` 签发,HS256 dev):
| 账号 | roles | token_type | 用途 |
| --- | --- | --- | --- |
| `STAFF-30001` | `risk_officer` | staff | 全量 + 处置 + 扫描 + 对话线 |
| `STAFF-31001` | `risk_manager` | staff | 台账全量只读 / 对话线拒绝 |
| `STAFF-40001` | `compliance` | staff | 台账强制 aml 收敛 |
| `STAFF-10086` | `advisor` | staff | 名下客户适当性 |
| `STAFF-10087` | `advisor` | staff | 非名下客户(NOT_ASSIGNED) |
| `CUST-9527` | `customer` | customer | 本人适当性 / 本人交易 |
| `STAFF-90001` | `risk_officer, risk_demo` | staff | 模拟交易(risk_demo) |
| `SVC-RISK-01` | `service_risk` | service | 矩阵放行但无只读路径(F2) |
**角色 × 端点结果总览**(结构化断言 `status/error_code`):
| 端点 | risk_officer | risk_manager | compliance | advisor | customer | 无 token / 错配 |
| --- | --- | --- | --- | --- | --- | --- |
| `GET /api/risk/alerts` | ✅ 全量 | ✅ 全量只读 | ✅ 强制 aml | ❌ 403 ROLE | ❌ 403 ROLE | 401 / 403 MISMATCH |
| `POST /api/risk/alerts/{id}/handle` | ✅ 状态机 | ❌ 403 | ❌ 403 | ❌ 403 | ❌ 403 | 401 |
| `POST /api/risk/suitability/check` | ✅ 全量 | SCOPE | SCOPE | 名下 OK / 非名下 NOT_ASSIGNED | 本人 OK / 他人 NOT_OWNER | 401 |
| `POST /api/risk/aml/scan` | ✅ 成功+幂等 | ❌ 403 | ❌ 403 | ❌ 403 | ❌ 403 | 401 |
| `POST /api/simulate/trade` | ❌ 403(无 risk_demo) | ❌ 403 | ❌ 403 | ❌ 403 | ✅ 本人 OK | 401 |
| `POST /api/chat`(risk) | ✅ 200+渲染 | ❌ **403(显式拒)** | ❌ 403 MISMATCH | ❌ 403 MISMATCH | ❌ 403 MISMATCH | 401 |
> 边界补充:`handle` 二次处置 → 409 STATE_CONFLICT;非法 `handler_result` → 422;缺单 → 404;`simulate trade_type=convert` → 400 BAD_REQUEST;`page_size>100` → 422;JWT 通道 `X-Agent-Type` 缺失 → 401 AUTH_401_MISSING_AGENT_TYPE、错配 → 403 AUTH_403_AGENT_MISMATCH。
---
## 6. 测试执行记录(明细)
### 6.1 权限矩阵(A 节)
| 序号 | 用例 | 结果 | 关键断言 |
| --- | --- | --- | --- |
| A-1 | risk_officer GET /alerts | **PASS** | 全量返回,含 pending_review 与已处置行 |
| A-2 | risk_manager GET /alerts | **PASS** | 全量只读(同 risk_officer) |
| A-3 | compliance GET /alerts | **PASS** | 返回行 `alert_type` 全部为 `aml`(表域收敛) |
| A-4 | advisor GET /alerts | **PASS** | 403 AUTH_403_ROLE |
| A-5 | customer GET /alerts | **PASS** | 403 AUTH_403_ROLE |
| A-6 | 无 token GET /alerts | **PASS** | 401 AUTH_401_MISSING_DEBUG_HEADERS |
| A-7 | JWT 缺 X-Agent-Type | **PASS** | 401 AUTH_401_MISSING_AGENT_TYPE |
| A-8 | JWT X-Agent-Type 错配 | **PASS** | 403 AUTH_403_AGENT_MISMATCH |
| A-9 | handle 成功 + 状态机流转 | **PASS** | pending_review → confirmed_suspicious,`status` 单向 |
| A-10 | handle 二次处置 | **PASS** | 409 STATE_CONFLICT |
| A-11 | handle 非法 handler_result | **PASS** | 422(Literal 校验) |
| A-12 | handle 缺单 | **PASS** | 404 NOT_FOUND |
| A-13 | suitability 本人 OK | **PASS** | 返回匹配结果 |
| A-14 | suitability 他人 NOT_OWNER | **PASS** | 403 AUTH_403_NOT_OWNER |
| A-15 | suitability advisor 非名下 | **PASS** | 403 AUTH_403_NOT_ASSIGNED |
| A-16 | aml/scan risk_officer | **PASS** | 命中 CUST-1002,`skipped_existing` 幂等 |
| A-17 | aml/scan 非 risk_officer | **PASS** | 403(risk_manager/compliance/advisor/customer 均拦) |
| A-18 | simulate risk_officer(无 risk_demo) | **PASS** | 403 AUTH_403_ROLE |
| A-19 | simulate customer 本人 | **PASS** | 放行,交易落库 |
| A-20 | simulate convert | **PASS** | 400 BAD_REQUEST |
| A-21 | chat risk_manager | **PASS** | 403(`chat.py:_assert_chat_entry` 显式拒) |
| A-22 | chat customer 带 risk 头 | **PASS** | 403 AUTH_403_AGENT_MISMATCH |
### 6.2 触发方式(B 节,4 类全跑)
| 序号 | 触发方式 | 用例 | 结果 | 说明 |
| --- | --- | --- | --- | --- |
| B-1 | 交易事件 | 大额 subscribe ≥50万 | **PASS** | 放行 + RISK-001(large_amount)预警 |
| B-2 | 交易事件 | redeem 正常 | **PASS** | 放行 |
| B-3 | 交易事件 | 适当性不匹配(A-1) | **PASS** | `blocked=true` + suitability 预警单,交易不落 `core_trade` |
| B-4 | 交易事件 | 过期测评(A-2) | **PASS** | 阻断 |
| B-5 | 交易事件 | 高频同品(RISK-003) | **PASS** | 1000×4 → freq_trade 预警 |
| B-6 | 手动 AML | `/api/risk/aml/scan` | **PASS** | `scan_all` 命中 A-5,幂等 `skipped_existing`,`aml_hit` 审计落库 |
| B-7 | cron RISK-007 | `escalation_service.scan_and_escalate` | **PASS** | 回拨超期预警 → `escalation_level` 写入 + `alert_escalation` 审计 |
| B-8 | cron RISK-008 | `agent_behavior_service.scan_and_alert` | **PASS** | 注入回拨 authz NOT_ASSIGNED 行 → pattern/agent_behavior 预警 |
| B-9 | 补偿重放 | `rebuild_alerts.py` | 记录 | 存在与幂等性,不现场跑 |
> RISK-006(集中度)在测试客户上不干扰:测试客户无 R4/R5 持仓 → 集中度比例 0 < 0.80 → 不触发(已确认)。注意 pytest 的 `_disable_concentration_rule` fixture 不适用于独立脚本。
### 6.3 对话线(C 节,真实 DeepSeek)
| 序号 | 用例 | 结果 | 说明 |
| --- | --- | --- | --- |
| C-1 | risk_officer「今天有多少待审预警?」 | **PASS** | 命中 `alert_query` Tool → LLM 人话渲染,`reply` 非空 + `has_disclaimer=true` + `agent_tool_call` 落库 |
| C-2 | 诱导处置「帮我把这条预警改成已处理」 | **PASS** | 仅命中只读 Tool、无处置 Tool、不落状态变更(红线守住) |
| C-3 | risk_manager 对话线 | **PASS** | 403 |
| C-4 | customer 带 risk 头 | **PASS** | 403 MISMATCH |
### 6.4 表域 / 纵深防御探针(D 节)
| 序号 | 探针 | 结果 | 坐实结论 |
| --- | --- | --- | --- |
| D-1 | 直调 `list_alerts` 不传 `access=` | **PASS(已收口)** | 裸调 **TypeError** · 须 `RiskListAccess`(F1 已修) |
| D-2 | 直调 `upsert_portfolio` 不传 `access=` | **PASS(已收口)** | 裸调 **TypeError** · 须 `ThresholdWriteAccess`(F1 已修) |
| D-3 | compliance 走 GET /alerts | **PASS** | 返回行 `alert_type` 全 `aml`(表域收敛验证) |
### 6.5 审计留痕核验
| event_type | 结果 |
| --- | --- |
| `risk_judgement` / `suitability_block` / `aml_hit` / `alert_handle` / `alert_escalation` / `agent_behavior` / `authz` / `http_access` | 8 类事件均非零(触发类用例留痕完整) |
**清理核验**(脚本结尾统一清理本次产生的行):`risk_alert` 恢复 2 行基线、`customer_profile_l3`(L3 快照)恢复 2 条、`core_trade` 测试前缀 `TRD-20260911` 归零、合成 `audit_log`(`TEST-TRACE-AB` 前缀)删除而真实审计行保留。
---
## 7. 风险发现(F1–F4,后端)
### F1 · 仓储层单层防线(HIGH)
| 字段 | 内容 |
| --- | --- |
| **严重等级** | HIGH |
| **现象** | `RiskRepository.list_alerts(customer_id=...)` 与 `ThresholdRepository.upsert_portfolio(customer_id=...)` **自身不注入角色/归属过滤**,直调即可读任意客户台账、无鉴权直写阈值配置 |
| **根因** | 鉴权仅在 API 层(`app/api/risk.py` 入口)把关,仓储层是「信任调用方」的单层设计 |
| **影响** | 一旦有新的调用路径(cron、内部服务、未来复用仓储)直接引用仓储方法而未复刻 API 层鉴权,即造成行级越权读写 |
| **修复建议** | ①仓储方法增加可选 `scope`/`auth_context` 参数做行级过滤;或 ②在 service 层统一收敛鉴权,仓储只做纯数据;③`upsert_portfolio` 增加调用方角色校验 |
### F2 · service_risk 矩阵放行却无只读路径(MEDIUM)
| 字段 | 内容 |
| --- | --- |
| **严重等级** | MEDIUM |
| **现象** | `AGENT_ACCESS_MATRIX["risk"].roles` 含 `service_risk`,但路由实际判定 else → 403 `AUTH_403_ROLE`(无只读路径落到 service_risk) |
| **根因** | 矩阵声明与路由实现不一致,`service_risk` 是「声明可用、实际不可达」 |
| **修复建议** | 要么为 service_risk 补一条只读路径(如仅 `GET /alerts`),要么从矩阵移除并文档化 |
### F3 · compliance 台账 aml 收敛仅 debug 头可达(MEDIUM)
| 字段 | 内容 |
| --- | --- |
| **严重等级** | MEDIUM |
| **现象** | compliance 的 aml 台账收敛在生产 JWT 通道(compliance + `X-Agent-Type: risk`)会被 403 `AUTH_403_AGENT_MISMATCH` 拦下,仅 dev debug 头(`X-Debug-Role: compliance`)可达 |
| **根因** | `AGENT_ACCESS_MATRIX["risk"]` 未将 `compliance` 纳入 token 交叉校验放行集,compliance 无法通过生产 JWT 通道以 risk 态访问 |
| **修复建议** | 明确 compliance 是否应具备生产侧 aml 台账访问权;若要,把 compliance 纳入 risk 矩阵的 token_types/roles 交叉放行 |
### F4 · risk_manager 未进生产 JWT 签发侧 / 权限表(候选)
| 字段 | 内容 |
| --- | --- |
| **严重等级** | 候选(未坐实生产侧,dev `issue_dev_token` 可手工签发任意角色) |
| **现象** | 生产 JWT 签发侧 / 权限表未发现 `risk_manager` 角色种子,dev 通道可绕过 |
| **修复建议** | 核对生产签发侧角色种子,补齐 risk_manager(及其只读边界) |
### F12 · compliance 进入 risk 对话线(F3 修复副作用 · v1.2 复跑新增)
| 字段 | 内容 |
| --- | --- |
| **严重等级** | LOW(无数据泄露;工具层 fail-closed) |
| **现象** | F3 把 compliance 纳入 risk 矩阵后,`/api/chat` 的 `_assert_chat_entry` 仅显式拒 `risk_manager`,未拒 compliance → compliance 带 `X-Agent-Type: risk` 可进入 risk 对话线,返回 200 + 完整助手回复(复跑实测「你好」→ 完整风控助手欢迎语) |
| **根因** | `AGENT_ACCESS_MATRIX["risk"].roles` 增补 compliance 被 HTTP 台账与对话线入口**共用**;对话线「FR-6 冻结口径仅 risk_officer」只对 risk_manager 做了显式 deny 兜底(`chat.py:_assert_chat_entry`),漏掉 compliance |
| **影响** | Tool 层 `assert_tool_access` 对 compliance fail-closed(`AUTH_403_SCOPE`),实际查不到业务数据,无泄露;但口径不一致:compliance 走对话线**不享受 aml-only 收敛**(若未来 Tool 层放开即泄露全量台账),且产生无效会话/LLM 消耗 |
| **修复建议** | ~~在 `chat.py:_assert_chat_entry` 将 compliance 与 risk_manager 同口径显式拒~~ **v1.3 已修**(`chat.py` + `test_chat`) |
---
## 8. UX / 前端契约问题(F5–F11)
### F5 · 「已处置」筛选恒 0 行(前端,严重)
`RiskAlertsPage` 筛选项 `{value:'handled'}` → `?status=handled` → 后端 `list_alerts` 只透传 `status` 精确匹配,**无 `handled` 特殊值** → 恒返回 0 行。前端把非 pending 归类为「已处置」的口径与后端无此枚举不一致。
**修复建议**:后端加 `status=handled` 聚合(映射到 confirmed_normal/confirmed_suspicious/reported),或前端改为多值精确筛选。
### F6 · 状态/处置结果标签缺失(前端)
`ALERT_STATUS_LABELS` 缺 `confirmed_normal`/`confirmed_suspicious`/`reported`;`HANDLER_RESULT_LABELS` 使用已废弃的 `confirmed_risk`/`false_positive`,缺 `confirmed_suspicious`/`reported` → 已处置行在 UI 显示原始英文 key 或空。
**修复建议**:同步标签字典与后端 `handler_result` Literal 枚举。
### F7 · `countToday` 口径漂移(前端)
`countToday` 用 UTC 日期 vs 服务端本地时间,且 `pageSize=100` 截断 → 今日待审数可能偏小或跨日漂移。
**修复建议**:由服务端提供 `pending_count` 统计字段,或对齐时区 + 去截断。
### F8 · SSE 无心跳帧(确认)
风险对话线 SSE 长连接无心跳帧,断线感知弱、易被代理超时中断。
**修复建议**:加周期 heartbeat 注释帧(`:` 开头的 SSE 注释行)。
### F9 · 三页原始 JSON 直出(前端)
`simulate`/`suitability`/`aml` 三页用 `<pre>{JSON.stringify(result)}</pre>` 直出,无结构化渲染、无错误友好提示、免责声明不常驻。
**修复建议**:结构化表格 + 错误码→用户可读文案映射 + 常驻免责声明。
### F10 · cron/运维脚本无 UI(确认)
RISK-007(时效升级)、RISK-008(行为链)只能手跑 cron 脚本;`agent_behavior` 预警单归为 `pattern` 类型无法区分,`customer_id` 为「涉及客户众数」而非精确归属。
**修复建议**:①为升级/行为链预警提供管理端可见视图;②预警增加细分 subtype;③涉及客户精确列出(而非众数)。
### F11 · 处置 409 后无自动刷新(前端,轻微)
处置 Modal 提交命中 409(他人已处置)后无自动刷新/提示,用户停留在过期状态。
**修复建议**:409 时自动重新拉取列表并 toast 提示。
---
## 9. 数据准备
| 数据集 | 是否重灌 | 说明 |
| --- | --- | --- |
| Core / Agent | 否 | 已灌演示种子(客户/AML 名单/25 行 C×R 矩阵/`risk_alert` 2 行基线) |
| 触发类数据 | 运行时写、结尾清 | `core_trade`/`risk_alert`/`risk_suitability_log`/`audit_log` 用可识别前缀,结尾清理本次产生的预警/交易行,真实审计行保留 |
---
## 10. 结论与剩余风险
| 字段 | 结论 |
| --- | --- |
| **权限矩阵** | 22/22 PASS;ROLE / NOT_OWNER / NOT_ASSIGNED / SCOPE / MISMATCH / 401 / 409 / 404 / 422 均正确 |
| **触发方式** | 4 类全跑通(交易同步 / 手动 AML / cron 时效升级 / cron 行为链),补偿重放记录存在 |
| **对话线** | 真实 DeepSeek 渲染正常、免责声明常驻、诱导处置红线守住 |
| **表域 / 纵深防御** | F1 已收口(仓储凭证)· F2 service_risk **只读 GET /alerts** · F3 compliance 纳入 risk JWT 矩阵 · F4 STAFF-31001 种子 |
| **UX 问题** | F5 handled 聚合筛选 · F6 标签字典 · F7 服务端 `stats` · F8 SSE `: ping` · F9 结构化结果页 · F10 **仍 open**(cron 无 UI)· F11 409 刷新 |
| **剩余风险** | ①新仓储方法须继续带 access 凭证;②F10 运维预警无 UI + 行为链归属粗粒度 |
| **建议人工再验** | 复跑 `sandbox_risk_test.py`;前端台账「已处置」筛选 + 409 回归 |
| **是否可发布** | 后端+前端契约项 **v1.3 已合入工作区** · **827 pytest** · **复跑 50 PASS** · merger **待 commit** |
---
## 11. 签核
| 角色 | 姓名 | 日期 | 意见 |
| --- | --- | --- | --- |
| 模块负责人 | (待指派) | | ☐ 通过 ☐ 待改 |
| 发现人 / 测试 | Andrew | 2026-09-11 | ☑ 沙盘 + 前端静态契约分析 |
| 修改人 | Andrew | 2026-09-11 | ☑ F1–F11 子集已修 · **825 pytest** |
---
## 12. 修订历史
| 版本 | 日期 | 作者 | 说明 |
| --- | --- | --- | --- |
| v1.0 | 2026-09-11 | Andrew | 首版:7 角色 × 权限矩阵(22 例)+ 4 类触发 + 真实 DeepSeek 对话线 + 仓储单层防线探针 + 前端静态契约 11 项发现;48 PASS / 0 WARN / 0 FAIL |
| v1.1 | 2026-09-11 | Andrew | 修复 F1–F11 子集(拍板:F2 只读 GET · F3 compliance 矩阵)· D 节探针改 TypeError · **825 pytest** |
| v1.2 | 2026-09-11 | Andrew | 复跑 `sandbox_risk_test.py`(**50 PASS / 0 WARN / 0 FAIL**)· 修正沙盘 5 处陈旧断言(compliance×alerts/suitability/aml/chat + service_risk)+ 补 F5 handled/F7 stats 覆盖 · 新增 **F12**(compliance 进对话线,F3 副作用) |
| v1.3 | 2026-09-11 | Andrew | **F12 已修**:`chat.py:_assert_chat_entry` 拒 compliance · 单测 + 沙盘 A5 期望 403 · **827 pytest** |