341 lines
28 KiB
Markdown
341 lines
28 KiB
Markdown
# 企业级测试日志 · TEST-2026-09-12-FE-001
|
||||
|
|
|
|||
|
|
> 顾问前端 · 风控新功能(基金转换 T+1)· 风控前端 —— 真实浏览器 + 真实 HTTP 端到端验收(第三轮)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 文档元数据
|
|||
|
|
|
|||
|
|
| 字段 | 值 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| **测试记录编号** | TEST-2026-09-12-FE-001 |
|
|||
|
|
| **缺陷/变更标题** | 顾问前端四页 + 风控转换线 + 风控前端六页端到端:174 用例 · 161 PASS / 12 FAIL / 1 SKIP · 收敛 6 缺陷 |
|
|||
|
|
| **文档版本** | **v1.1** |
|
|||
|
|
| **创建日期** | 2026-09-12 |
|
|||
|
|
| **最后更新** | 2026-09-12(**v1.1** · 结果明细入库 + D3 计数口径更正) |
|
|||
|
|
| **关联分支** | **`merger`** |
|
|||
|
|
| **被测版本** | `merger` @ **`c09b987`**(`feat(web): 基金转换演示页与 agent 镜像表迁移; Core seed 修复`) |
|
|||
|
|
| **关联拍板 / TODO** | `docs/superpowers/specs/2026-09-12-advisor-agent-frontend-design.md` · `docs/memory/TODO.md`「E2E 最小集进仓库」P0 |
|
|||
|
|
| **风险等级** | **MEDIUM**(无 P1 阻断主流程;2 项降级 + 3 项前端缺陷 + 1 项纵深防御缺口) |
|
|||
|
|
| **缺陷类型** | 前端默认值越界 ×1 · 前端字段名不匹配 ×1 · 路由守卫缺失 ×1 · 承接 D2/D3 ×2 · 测试资产腐化 ×1 |
|
|||
|
|
| **发现阶段** | 真实浏览器 + 真实 HTTP 端到端验收(第三轮) |
|
|||
|
|
| **修复阶段** | **未修复** —— 本轮定性「先报告、确认后再修」,`app/` 与 `web/src/` **零改动** |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 组织与责任
|
|||
|
|
|
|||
|
|
| 字段 | 值 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| **所属系统** | JinRong 金融四 Agent 智能管家 |
|
|||
|
|
| **所属模块** | 投资顾问 Agent 前端 · 风控 Agent(基金转换 T+1 新功能 + 前端) |
|
|||
|
|
| **子模块 / 服务** | `web/src/pages/advisor/*`(4 页)· `web/src/pages/risk/*`(6 页)· `/api/simulate/trade*` · `/api/admin/convert/confirm` · `/api/risk/*` |
|
|||
|
|
| **模块负责人** | (见 `docs/项目框架设计/理财顾问Agent-合并说明.md`) |
|
|||
|
|
| **发现人** | Andrew |
|
|||
|
|
| **修改人** | (未修改 · 待确认后再修) |
|
|||
|
|
| **测试执行人** | Andrew |
|
|||
|
|
| **评审人** | (待模块负责人确认) |
|
|||
|
|
| **发布建议** | **不阻断主线**:鉴权 / 合规 / 门闸 / 模板 / 台账 / 适当性 / AML 全绿;建议按下方优先级排修后再做演示 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 环境与基线
|
|||
|
|
|
|||
|
|
| 字段 | 值 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| **测试环境** | development · 本机 Windows 11 · Python 3.13 · Node(Playwright 1.63.0) |
|
|||
|
|
| **被测版本** | `merger` @ `c09b987` · `git status` 干净 |
|
|||
|
|
| **后端** | `python -m uvicorn app.main:app --host 127.0.0.1 --port 8000`(真实进程 · 无 `--reload`) |
|
|||
|
|
| **前端** | `cd web && npm run dev` → **必须 `http://localhost:5173`**(`app/` 无 CORS 中间件,浏览器只能走 Vite 的 `/api` 代理;且 WebCrypto 需安全上下文) |
|
|||
|
|
| **浏览器** | 系统 Chrome(`channel:'chrome'`·headless)· 视口 1440×1000 |
|
|||
|
|
| **数据库** | MySQL `jinrong_agent` + `jinrong_core` · **NAV 最新 `2026-09-11`** · `core_product_nav` 覆盖 14 产品 |
|
|||
|
|
| **Redis / Milvus / Ollama** | Docker 6380 · Milvus Lite(`kb_script_templates` **疑似空集**,见 D3) · bge-m3 |
|
|||
|
|
| **DeepSeek** | 有效 key · 异动解读实测 `used_fallback=False latency≈1415ms`(真实 LLM 路径) |
|
|||
|
|
| **前置自检(关键)** | 复跑前确认 :8000 上**无 merge 前的旧进程**:`/openapi.json` 必须含 `/api/admin/convert/confirm` 等转换路径。**本轮开工时曾抓到旧进程**(PID 27348 启动于 16:55:24,早于代码落地 17:52–17:59),若未重启,全部转换用例会**假失败** |
|
|||
|
|
| **基线对照** | 顾问 API 首轮(v1.0)为 53 PASS / 5 FAIL / 5 SKIP,本轮复跑已改写至 60 PASS / 3 FAIL / 0 SKIP,见 `2026-09-12-advisor-agent-e2e/` |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 测试目标与方法
|
|||
|
|
|
|||
|
|
| 字段 | 值 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| **目标** | 三块:**A** 顾问前端四页(真浏览器点按)· **B** 风控转换线 + RBAC(真 HTTP)· **C** 风控前端六页(真浏览器点按) |
|
|||
|
|
| **方法** | 两条独立跑道:PyPlaywright→Chrome→Vite 代理→uvicorn / httpx→uvicorn。断言不看截图,逐用例判定 |
|
|||
|
|
| **关键工具改进** | 新增 `clearAuth(page)`(切角色前清 `localStorage`,否则 `#/login` 会重定向回上个角色)与 `waitRows(page)`(**轮询**等真实行,取代固定 `waitForTimeout` 冒充空表判定) |
|
|||
|
|
| **用例总数** | **174** = 顾问 API 63 + 风控 API 64 + 浏览器 47 |
|
|||
|
|
| **账号** | 顾问 `STAFF-10086` · 风控专员 `STAFF-30001` · 风控经理 `STAFF-31001` · 合规 `STAFF-40001` · 客户 `CUST-9527` |
|
|||
|
|
| **未覆盖(明示)** | 两个 SSE 对话页(`/app/advisor/chat`、`/app/risk/chat`,依赖实时 LLM 不确定)· `agent_tools.py`/`agent_graph.py`(无 HTTP 入口) |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. 结果总览
|
|||
|
|
|
|||
|
|
| 跑道 | 用例 | PASS | FAIL | SKIP | 退出码 |
|
|||
|
|
| --- | --- | --- | --- | --- | --- |
|
|||
|
|
| 顾问 API `advisor_e2e_smoke.py` | 63 | 60 | 3 | 0 | 1 |
|
|||
|
|
| 风控 API `risk_e2e_smoke.py` | 64 | **63** | **0** | 1 | 0 |
|
|||
|
|
| 顾问前端 `09-advisor-pages.mjs` | 17 | 15 | 2 | 0 | — |
|
|||
|
|
| 风控前端 `10-risk-pages.mjs` | 21 | 19 | 2 | 0 | — |
|
|||
|
|
| 跨角色 `11-cross-role.mjs` | 9 | 4 | 5 | 0 | — |
|
|||
|
|
| **合计** | **174** | **161** | **12** | **1** | |
|
|||
|
|
|
|||
|
|
| 业务面 | 状态 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| 顾问:鉴权 / 合规 / 复制门闸 / 模板 / 输入防护 / 跨线回归 | ✅ 全绿 |
|
|||
|
|
| 顾问:**市场异动**(D1) | ✅ **本轮转绿**(API 10/10 · 浏览器 4/4) |
|
|||
|
|
| 顾问:KYC 采集(D2) | ❌ 500(API 2 FAIL · 浏览器 1 FAIL) |
|
|||
|
|
| 顾问:话术语义检索(D3) | ⚠️ 静默降级(API 1 FAIL · 浏览器 1 FAIL) |
|
|||
|
|
| 风控:台账 / 处置 / 适当性 / AML / 模拟交易 / 转换线 | ✅ 全绿(除下列前端渲染缺陷) |
|
|||
|
|
| 前端:转换页一键预置(F1)· 模拟交易预警编号(F2)· 路由守卫(F3)· 确认日默认值(F4) | ❌ 见 §6 |
|
|||
|
|
|
|||
|
|
**唯一 SKIP**:风控 API `R4 阻断文案 advice/notice` —— 该客户/产品组合未触发阻断,**非失败**。
|
|||
|
|
|
|||
|
|
**复现确认(报告定稿前实跑)**:两条 API 跑道按 §「复现三连」重放,**逐字命中** ——
|
|||
|
|
`advisor_e2e_smoke.py` → `60 PASS / 3 FAIL / 0 SKIP`(退出码 1,失败项与 §7.5 完全一致)·
|
|||
|
|
`risk_e2e_smoke.py` → `63 PASS / 0 FAIL / 1 SKIP`(退出码 0)。
|
|||
|
|
浏览器跑道(47 用例)为**当时截图留档**,未重放 —— 见 §3 复核提示。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. 缺陷描述(6 条 · 均未修复)
|
|||
|
|
|
|||
|
|
> **归属分类**:F1/F2/F4 = **实现缺陷(前端)** · F3 = **纵深防御缺口** · D2/D3 = **承接顾问线未修项** · T1 = **测试资产腐化**
|
|||
|
|
|
|||
|
|
### F1 · 转换页「演示全转」一键预置是跨主体组合,**必然失败**(P2)
|
|||
|
|
|
|||
|
|
| 字段 | 内容 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| **现象** | 风控前端「基金转换」受理 Tab 的**唯一**预置场景,点一下提交即 400 |
|
|||
|
|
| **复现** | `web` 起后进 `#/app/risk/convert` → 受理 Tab → 直接点「提交受理」 |
|
|||
|
|
| **期望 / 实际** | 演示预置应能一键走通 → 202 / **HTTP 400 `CROSS_ENTITY_NOT_SUPPORTED`** |
|
|||
|
|
| **根因** | `web/src/pages/risk/RiskConvertPage.tsx:16-27` 的 `ACCEPT_PRESETS[0].values` 把 `PROD-110022`(华夏模拟基金 · TA-CN-001)配给 `PROD-161725`(易方模拟基金 · TA-CN-002)。业务规则要求**同销售机构 + 同管理人 + 同 TA**,违约即 400(`app/service/convert/errors.py:75-78`) |
|
|||
|
|
| **实测** | 浏览器 B6.1/B6.2:预置文案 `演示全转(CUST-1001 · 110022→161725 · 30000 份)` → HTTP 400,页面报 `CROSS_ENTITY_NOT_SUPPORTED`;API 侧 R6 同步复现 |
|
|||
|
|
| **影响** | 演示路径的**唯一**入口是不可用路径 —— 演示时必卡在第一步 |
|
|||
|
|
| **修复方向(只写方向)** | 把预置 `to_product_id` 换成与 `PROD-110022` 同管理人同 TA 的产品(实测 `PROD-000001` 可 202);**不动手** |
|
|||
|
|
| **佐证(改对后可通)** | 同页手工改为 `CUST-9527 / 110022→000001 / 4000` → **202** + `convert_group_id=CNV-…`,查询/撤单全链路通 |
|
|||
|
|
|
|||
|
|
### F2 · 模拟交易页「预警编号」字段名不匹配,恒显示「—」(P2)
|
|||
|
|
|
|||
|
|
| 字段 | 内容 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| **现象** | A-3 大额演示**确实生成了预警**,但页面「预警编号」永远是 `—` |
|
|||
|
|
| **复现** | `#/app/risk/simulate` → 预置 A-3(50 万)→ 提交 |
|
|||
|
|
| **期望 / 实际** | 显示刚生成的预警号 / **`—`** |
|
|||
|
|
| **根因** | `web/src/pages/risk/RiskSimulatePage.tsx:138` 取 `key: 'alert_id'`(**单数**),而 `POST /api/simulate/trade` 返回的是 `alert_ids`(**复数数组**)|
|
|||
|
|
| **实测取证** | 同一次请求:响应体 `alert_ids=["ALT-20260912-A021D01E"]`、`triggered_rules=["RISK-001","RISK-002","RISK-003","RISK-004"]`,而页面渲染 `预警编号 —` |
|
|||
|
|
| **影响** | 演示时「造预警」这一步**看起来失败**(实际成功),会误导演示者重复操作、堆重复预警 |
|
|||
|
|
| **修复方向(只写方向)** | 改取 `alert_ids`(数组,多命中时并列展示);**不动手** |
|
|||
|
|
| **注意** | **不是后端缺陷** —— 后端已正确返回;浏览器用例 B5.1 对后端断言 **PASS**,B5.2 对渲染断言 FAIL,二者分离记录 |
|
|||
|
|
|
|||
|
|
### F3 · 无路由级角色守卫,菜单隔离仅「隐藏」不「拦截」(P2 · 纵深防御缺口)
|
|||
|
|
|
|||
|
|
| 字段 | 内容 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| **现象** | 换角色后**手输 URL** 可打开另一条业务线的页面 |
|
|||
|
|
| **复现** | 以风控 `STAFF-30001` 登录,地址栏直接输 `#/app/advisor/compliance`(或 kyc / templates / market-alerts);反向以顾问登录输 `#/app/risk/convert` |
|
|||
|
|
| **期望 / 实际** | 应重定向回本角色首页 / **页面正常渲染** |
|
|||
|
|
| **根因** | `web/src/layouts/AppLayout.tsx:20` 的 `allowedPaths` **只**用于计算 Menu 的 `selectedKey`(`:23`),**未参与任何路由守卫**;`web/src/routes/menus.ts` 的 `buildMenuGroups(roleLabel)` 只做菜单隐藏 |
|
|||
|
|
| **实测** | C1.2–C1.5(风控→顾问四页)、C2.2(顾问→转换页)**全部 `打开=true`**;菜单侧 C1.1/C2.1 隔离**正常** |
|
|||
|
|
| **定性:不是数据越权** | 后端 RBAC 仍然生效:C1.6 以风控身份在顾问页实际点「检测」→ **HTTP 403 `/api/advisor-agent/compliance/content-check`**;C2.3 顾问点开预警台账 → 页面级 `AUTH_403_AGENT_MISMATCH`。**缺口只在 UI 可见性** |
|
|||
|
|
| **为何仍要修** | ①演示时误入他人页面会看到空表/报错,观感像故障;②纵深防御应双端一致;③不做守卫时,未来若新增「前端隐式信任、后端未覆盖」的接口即成真越权 |
|
|||
|
|
| **修复方向(只写方向)** | 在 `AppLayout` 或路由层加基于 `roleLabel` 的 `allowedPaths` 守卫,越界重定向到本角色首页;**不动手** |
|
|||
|
|
|
|||
|
|
### F4 · 转换页确认批处理默认日期越界,**默认即触发降级路径**(P3)
|
|||
|
|
|
|||
|
|
| 字段 | 内容 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| **现象** | 「确认批处理」Tab 的默认日期落在 NAV 区间之外 |
|
|||
|
|
| **复现** | `#/app/risk/convert` → 确认批处理 Tab → 不改日期直接点「触发 confirm_batch」 |
|
|||
|
|
| **期望 / 实际** | 默认值应落在可用区间(`2026-09-11` 及之前)/ 默认 `2026-09-15`,**区间外** |
|
|||
|
|
| **根因** | `web/src/pages/risk/RiskConvertPage.tsx:37` 硬编码 `useState('2026-09-15')`;而 `core_product_nav` 最新 `nav_date` = `2026-09-11` |
|
|||
|
|
| **实测对比** | 默认 `2026-09-15`(区间外)→ `scanned=8 confirmed=0 nav_pending=8`(全落 `nav_pending`,**零确认**);改 `2026-09-11`(区间内)→ `scanned=0`(因 `accept_date` 解析到下一交易日) |
|
|||
|
|
| **口径提醒** | `scanned` / `nav_pending` 是**当时存量**(随每轮新增受理单增长,复跑时会变大,如 API 侧复现为 `scanned=10`)。**判据看 `confirmed` 恒为 0**,不看绝对数 |
|
|||
|
|
| **定性** | **降级不是缺陷,默认值才是** —— `scanned=8 confirmed=0 nav_pending=8` 是 `accept_date` 解析为未来交易日所致(属已文档化行为);缺陷在于**页面把不可用日期设为默认**,使用者一进来就走到零确认路径 |
|
|||
|
|
| **修复方向(只写方向)** | 默认值改为由数据驱动(取 `core_product_nav` 最新 `nav_date`)或退化为 `2026-09-11`;**不动手** |
|
|||
|
|
|
|||
|
|
### D2(承接)· KYC 对话写 `agent_message` 时 `created_at` 显式 NULL(P1)
|
|||
|
|
|
|||
|
|
| 字段 | 内容 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| **状态** | **未修复**(顾问线首轮即报,本轮前端侧独立复现) |
|
|||
|
|
| **现象** | KYC 页点「发送采集」→ 页面 `INTERNAL_ERROR` |
|
|||
|
|
| **前端取证** | `09-advisor-pages.mjs` A4.2:页面渲染 `INTERNAL_ERROR internal server error trace_id: trc-6417ef20507a4d10`;同页 A4.1「创建会话」**成功**(唯采集一步挂) |
|
|||
|
|
| **根因** | `app/model/entities.py:116` 的 `created_at` 无 Python 侧 default,`app/repository/kyc_session_repository.py` 用 ORM 写且不传该字段 ⇒ 显式 NULL 覆盖 DDL 默认值 → `IntegrityError 1048` |
|
|||
|
|
| **完整定性** | 见 `../2026-09-12-advisor-agent-e2e/TEST-LOG-2026-09-12-ADV-001.md` §6 D2 |
|
|||
|
|
|
|||
|
|
### D3(承接)· Milvus 集合 `released` 未 load,语义检索静默降级(P2)
|
|||
|
|
|
|||
|
|
| 字段 | 内容 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| **状态** | **未修复**(根因已由服务端日志坐实,详见顾问线报告 v1.1) |
|
|||
|
|
| **前端取证** | `09-advisor-pages.mjs` A3.2:搜「亏损」返回 4 行,`match_type` **全为 `keyword`**(`["keyword","keyword","keyword","keyword"]`) |
|
|||
|
|
| **决定性证据** | uvicorn 日志 `MilvusException (code=101, message=Collection 'kb_script_templates' is in state 'released'; call load() before search/get/query)` —— **6 次独立异常**(12 行提及 = 6 条 RPC-error + 6 条 traceback),**全 session 无其它 Milvus 错误类型**。原文摘录见 `_raw/server-log-evidence.md` §2 |
|
|||
|
|
| **口径更正** | ADV-001 v1.1 与本文首版均写作「出现 **10** 次」—— 该数字实为**日志行数**(5 次异常 × 2 行),非异常次数。次数**随每次语义检索查询累加**(ADV-001 定稿时 5 次,本包 18:55 复现 +1)。**结论不变,只是计数口径写错了** |
|
|||
|
|
| **第二层** | 集合目录 5.0K/2 文件 vs 已灌集合 50K/5 文件 ⇒ **疑似空集**,仅补 `load()` 未必恢复召回 |
|
|||
|
|
| **完整定性** | 见顾问线报告 v1.1 §6 D3 |
|
|||
|
|
|
|||
|
|
### T1 · 测试资产腐化(**非产品缺陷** · 只上报)
|
|||
|
|
|
|||
|
|
| 资产 | 问题 | 建议方向(不动手) |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| `scripts/dev/sandbox_risk_test.py:325-326` | 仍断言 `convert` → **400 `BAD_REQUEST`**;而 `03fce88` 之后合法 convert **受理为 202**。该断言如今**必然失败** | 把 `expect(..., 400, "BAD_REQUEST")` 改为断言 202 受理,或换成真正的非法组合(如本报告 F1 的跨主体对)以保留负例覆盖 |
|
|||
|
|
| `scripts/demo/run_demo_smoke.ps1` | 仍调 `/api/v1/*` 并用 `username`/`password` 登录、断言 `code=="00000"`;`/api/v1` 在 `app/` 中**已无任何路由**,按当前代码必然 404 | 改到 canonical 路径 + `POST /api/auth/login {"actor_id","token_type"}` |
|
|||
|
|
| `docs/演示文档/功能演示版启动手册.md` 等 | 残留 `alembic upgrade head` 与 `/api/v1` 指引 | 与现状对齐 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. 测试执行记录(明细)
|
|||
|
|
|
|||
|
|
### 7.1 Part A · 顾问前端(`09-advisor-pages.mjs` · 15 PASS / 2 FAIL)
|
|||
|
|
|
|||
|
|
| 用例 | 结果 | 证据 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| A1.1 首页渲染 + 名册表有行 | PASS | `rows=10 canvas=2` |
|
|||
|
|
| A1.2 首页无 HTTP≥400 | PASS | 0 条 |
|
|||
|
|
| A2.1 合规检测「违规承诺」→ BLOCK | PASS | 命中 BLOCK |
|
|||
|
|
| A2.2 复制门闸 Tab1 检测后**自动回填**(有序依赖) | PASS | `summary="本产品保本保收益,绝对不会亏。" level=BLOCK` |
|
|||
|
|
| A2.3 复制门闸 BLOCK 内容 → **拒绝导出** | PASS | `content cannot be copied or exported` |
|
|||
|
|
| A2.4 复制门闸 INFO 内容 → 放行出 `use_id` | PASS | 回执出现 |
|
|||
|
|
| A2.5 输入防护「注入样例」→ `prompt_injection` | PASS | 命中 `prompt injection` |
|
|||
|
|
| A3.1 话术模板搜「亏损」有行 | PASS | `rows=4` |
|
|||
|
|
| **A3.2 话术模板 `match_type`** | **FAIL(D3)** | 全为 `keyword` |
|
|||
|
|
| A3.3 话术模板引用 → `use_id` | PASS | `use_id=use_ab4e…` |
|
|||
|
|
| A4.1 KYC 创建会话 | PASS | 创建成功 |
|
|||
|
|
| **A4.2 KYC 发送采集** | **FAIL(D2)** | 页面 `INTERNAL_ERROR` + `trace_id` |
|
|||
|
|
| A5.1 市场异动查净值 000001(**D1 回归**) | PASS | 净值快照渲染 |
|
|||
|
|
| A5.2 市场异动扫描 + 列表 | PASS | `rows=5` |
|
|||
|
|
| A5.3 生成解读 → **四段式** | PASS | `["【异动概述】","【原因分析】","【当前建议】","【风险提示】"] len=403` |
|
|||
|
|
| A5.4 采纳(adopt) | PASS | `PUT …/feedback` **HTTP 200** |
|
|||
|
|
| A6.1 **BUG-1 回归**:点击登录(无 `reload` 兜底) | PASS | 未出现 ErrorBoundary,直入 `/app/advisor/home` |
|
|||
|
|
|
|||
|
|
### 7.2 Part B/C · 风控前端(`10-risk-pages.mjs` · 19 PASS / 2 FAIL)
|
|||
|
|
|
|||
|
|
| 用例 | 结果 | 证据 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| B1.1 首页 stats 渲染 | PASS | 待处理指标可见 |
|
|||
|
|
| B2.1–B2.5 台账列表 / 筛选 / 处置 Modal / 提交 200 / 自动重载 | PASS | `POST /api/risk/alerts/{id}/handle` → 200 |
|
|||
|
|
| B3.1 A-1 阻断 → 「阻断 · CODE」 | PASS | `阻断 · SUIT_RISK_MISMATCH 客户风险等级与产品最低等级不匹配` |
|
|||
|
|
| B3.2 阻断解释文案 | PASS | 同上 |
|
|||
|
|
| B4.1–B4.3 AML 扫描 / 计数 / 幂等 | PASS | `扫描客户 33 人 · 命中 1 人 · 新发预警 0 条 · 跳过已有 1 人` |
|
|||
|
|
| B5.1 A-3 大额 → **后端**触发预警 | PASS | `alert_ids=["ALT-20260912-A021D01E"]` |
|
|||
|
|
| **B5.2 页面「预警编号」渲染** | **FAIL(F2)** | 页面 `预警编号 —` |
|
|||
|
|
| B6.1 预置可见 | PASS | `演示全转(CUST-1001 · 110022→161725 · 30000 份)` |
|
|||
|
|
| **B6.2 内置预置一键受理** | **FAIL(F1)** | HTTP 400 `CROSS_ENTITY_NOT_SUPPORTED` |
|
|||
|
|
| B6.3 改对后受理 → 202 | PASS | `gid=CNV-20260912-602D8732` |
|
|||
|
|
| B6.4 查询框自动回填 | PASS | 回填一致 |
|
|||
|
|
| B6.5 查询 → 200 + 状态 | PASS | `status accepted` |
|
|||
|
|
| B6.6 撤单 → `cancelled` | PASS | `status cancelled` |
|
|||
|
|
| B6.7 确认批处理默认 `2026-09-15`(区间外) | PASS | `scanned 8 confirmed 0 nav_pending 8` |
|
|||
|
|
| B6.8 确认批处理 `2026-09-11`(区间内) | PASS | `scanned 0 confirmed 0 nav_pending 0` |
|
|||
|
|
|
|||
|
|
### 7.3 Part C · 跨角色(`11-cross-role.mjs` · 4 PASS / 5 FAIL)
|
|||
|
|
|
|||
|
|
| 用例 | 结果 | 证据 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| C1.1 风控菜单不含顾问项 | PASS | 菜单已隔离 |
|
|||
|
|
| **C1.2–C1.5 风控直达顾问四页** | **FAIL ×4(F3)** | 4 页全部 `打开=true`,无重定向 |
|
|||
|
|
| C1.6 ★ 风控身份在顾问页实际提交 | PASS | **HTTP 403** `/api/advisor-agent/compliance/content-check` |
|
|||
|
|
| C2.1 顾问菜单不含风控项 | PASS | 菜单已隔离 |
|
|||
|
|
| **C2.2 顾问直达 `/app/risk/convert`** | **FAIL(F3)** | `打开=true` |
|
|||
|
|
| C2.3 顾问直达 `/app/risk/alerts` | PASS | 页面级 `AUTH_403_AGENT_MISMATCH` |
|
|||
|
|
|
|||
|
|
### 7.4 Part B · 风控 API(`risk_e2e_smoke.py` · 63 PASS / 0 FAIL / 1 SKIP)
|
|||
|
|
|
|||
|
|
| 分组 | 覆盖 | 结果 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| R1 鉴权负例 | 缺 `X-Agent-Type` → 401 · 域外值 → 400 · 跨域 → 403 | 全 PASS |
|
|||
|
|
| R2 台账 RBAC | officer 全量 / manager 只读 / compliance 强制 `aml` / 越界 403 | 全 PASS |
|
|||
|
|
| R3 处置状态机 | 200 迁移 · 重复 409 `STATE_CONFLICT` · 404 · 超长 422 | 全 PASS |
|
|||
|
|
| R4 适当性 | owner 200 / 非归属 403 `AUTH_403_SCOPE` · **每次落 `audit_log`** | 全 PASS(1 SKIP:未触发阻断) |
|
|||
|
|
| R5 AML | 200 · `skipped_existing` 幂等 · 非 officer 403 | 全 PASS |
|
|||
|
|
| R6 **基金转换线** | 202 受理 · 幂等重放同 `group_id` · 未确认单不含 `_CONFIRMED_ONLY_FIELDS` · 撤单/重复撤单 409 · 确认批处理双日期 · 参数 422 · RBAC 403 | 全 PASS |
|
|||
|
|
|
|||
|
|
**R6 关键口径(易误判,已固化)**
|
|||
|
|
- 合法受理 = **HTTP 202** + `convert_group_id` + `status='accepted'`;幂等重放**也是 202**,且同 `group_id`、`idempotent=true`(首跑 `false`)。
|
|||
|
|
- 适当性阻断 = **HTTP 200 + `blocked=true`**,**不是 202**。
|
|||
|
|
- `accept_date` 格式非法(`2026/09/15`)由 FastAPI 查询校验拦下 → **422 `REQUEST_VALIDATION_FAILED`**,**不是 400**。
|
|||
|
|
- 撤单必须在 `confirm_batch` **之前**:确认会把单推进到 `nav_pending`,而 `nav_pending` 不可撤(409 `CANCEL_NOT_ALLOWED`)。
|
|||
|
|
|
|||
|
|
### 7.5 Part A · 顾问 API(`advisor_e2e_smoke.py` · 60 PASS / 3 FAIL)
|
|||
|
|
|
|||
|
|
| 分组 | v1.1 | 备注 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| G1 鉴权与封套(5) | 5 PASS | |
|
|||
|
|
| G2 合规检测(14) | 14 PASS | |
|
|||
|
|
| G3 复制门闸(5) | 5 PASS | |
|
|||
|
|
| G4 话术模板(12) | 11 PASS / **1 FAIL** | D3 |
|
|||
|
|
| **G5 市场异动(10)** | **10 PASS** | ✅ **由 D1 修复转绿**(v1.0 为 3 PASS / 2 FAIL / 5 SKIP) |
|
|||
|
|
| G6 KYC(10) | 8 PASS / **2 FAIL** | D2 |
|
|||
|
|
| G7 输入防护(3) | 3 PASS | |
|
|||
|
|
| G8 桩路由(2) | 2 PASS | |
|
|||
|
|
| G9 跨线回归(2) | 2 PASS | |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. 非缺陷澄清(易误判项 · 本轮实测踩过)
|
|||
|
|
|
|||
|
|
| 项 | 结论 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| 复制门闸对 BLOCK 内容返回 `content cannot be copied or exported` | **非缺陷,正是门闸生效**。初版用例误期「成功」,已改为断言「拒绝导出」,并另加 INFO 内容放行用例形成正反对照 |
|
|||
|
|
| 「预警编号」页面显示 `—` | **是缺陷(F2),但不在后端**。后端 `alert_ids` 正确返回;浏览器用例已把「后端 PASS」与「渲染 FAIL」拆成 B5.1/B5.2 两条,避免把渲染问题记成接口问题 |
|
|||
|
|
| 确认批处理 `scanned=8 confirmed=0 nav_pending=8` | **降级是已文档化行为,非缺陷**。`accept_date` 解析到下一交易日(未来)故不能确认;缺陷在**页面默认值越界**(F4),不在批处理逻辑 |
|
|||
|
|
| 风控身份能打开顾问页面 | **是缺陷(F3),但不是数据越权**。后端 403 仍然拦住(C1.6 实证);缺口只在 UI 可见性 |
|
|||
|
|
| 风控身份点「合规检测」返回 200 | **非缺陷**。`app/api/deps.py:48-58` 的 `AGENT_ACCESS_MATRIX["advisor"].roles` **含 `compliance`**,属设计内的跨线权限。初版用例误记为缺陷,已改为真正的负例(`risk_officer` 声明 `advisor` 域 → 403) |
|
|||
|
|
| 台账 `?status=nope` 返回 200 空集 | **低危观察,不列缺陷**。未做枚举校验;前端用固定 Select,实际不可达 |
|
|||
|
|
| 转换受理「失败」于 `可用份额不足:申请 30000,可用 0(已扣除在途转换占用)` | **非缺陷,是用例自污染**。反复跑测把 `CUST-1001` 持有的 30000 份 `PROD-110022` 占满;改用仍有 80000 份余量的 `CUST-9527` 后即 202。**本项曾是本轮 3 条假 FAIL 的根因之一** |
|
|||
|
|
| advisor JWT 通道够不到 `NOT_ASSIGNED`/`NOT_OWNER` 分支 | **渠道差异,非缺陷**。advisor 不在 risk 域,矩阵层先行拦截(403 `AUTH_403_AGENT_MISMATCH`);sandbox 里那两条断言只能走 dev debug 通道(`X-Debug-Role`,不过矩阵)。已在报告中标注该**渠道分叉** |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. 变更清单
|
|||
|
|
|
|||
|
|
| 类型 | 路径 | 说明 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| 脚本 | `scripts/dev/risk_e2e_smoke.py` | **新增** · 风控 API 真实 HTTP E2E(64 用例 · R1–R6 · `--only` / `--report` / 退出码 0·1·2) |
|
|||
|
|
| 脚本 | `C:/Users/Windows/e2e-jinrong/harness.mjs` | **改**(**仓库外**)· `ROUTES.advisor` 补 4 页、`ROUTES.risk` 补 `/app/risk/convert`;新增 `clearAuth` / `waitRows` / `cjk`;`login()` 加 `reload:false` 选项供 BUG-1 回归 |
|
|||
|
|
| 脚本 | `C:/Users/Windows/e2e-jinrong/09-advisor-pages.mjs` | **新增**(仓库外)· 顾问前端四页(17 用例) |
|
|||
|
|
| 脚本 | `C:/Users/Windows/e2e-jinrong/10-risk-pages.mjs` | **新增**(仓库外)· 风控前端六页(21 用例) |
|
|||
|
|
| 脚本 | `C:/Users/Windows/e2e-jinrong/11-cross-role.mjs` | **新增**(仓库外)· 跨角色隔离(9 用例) |
|
|||
|
|
| 文档 | `docs/memory/tests/2026-09-12-agent-frontend-e2e/` | **新增** · 本包(README + 本日志 + `_raw/` 结果明细) |
|
|||
|
|
| 数据 | `…/2026-09-12-agent-frontend-e2e/_raw/` | **新增** · **逐用例原始记录**:`advisor-api.md`(63) · `risk-api.md`(64) · `browser-{advisor,risk,cross-role}.json`(17/21/9) · `shots/`(12 张截图) · `README.md`(来源与口径说明) |
|
|||
|
|
| 文档 | `docs/memory/tests/2026-09-12-advisor-agent-e2e/TEST-LOG-2026-09-12-ADV-001.md` | **改** · 升 v1.1,按 `merger @ c09b987` 重写结论、关闭 D1 |
|
|||
|
|
| 文档 | `docs/superpowers/specs/2026-09-12-agent-frontend-e2e-design.md` · `docs/superpowers/plans/2026-09-12-agent-frontend-e2e.md` | **新增** · 履约 superpowers 约定 |
|
|||
|
|
| 文档 | `docs/memory/MEMORY.md` · `docs/memory/TODO.md` | **改** · 同步本轮结论与未修项 |
|
|||
|
|
| **业务代码** | `app/` · `web/src/` | **零改动** ✅ |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 10. 数据准备与副作用
|
|||
|
|
|
|||
|
|
| 数据集 | 是否重灌 | 说明 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| `jinrong_core` | **否** | 保留取证数据;**未**执行 `prepare_all.ps1` / `reset.ps1`(会 `DROP DATABASE`) |
|
|||
|
|
| 脚本副作用 | —— | 每轮新增:`core_trade` · `convert_request` · `risk_alert` · `audit_log` · 模板使用日志 等行;**全部保留** |
|
|||
|
|
| `CUST-9527` 持仓 | **被占用** | 转换用例按 `CONVERT_QTY_*` 消耗 `PROD-110022` 可用份额(在途占用),**重跑前若份额耗尽需重灌** |
|
|||
|
|
| Milvus `kb_script_templates` | **未灌** | 本轮**刻意不执行** `sync_template_vectors.py`(写操作,且会掩盖 D3 的待修状态) |
|
|||
|
|
| 凭据 | —— | 本包**不含**任何明文密码;账号一律以 `STAFF-*` / `CUST-*` 标识 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 11. 结论与剩余风险
|
|||
|
|
|
|||
|
|
| 字段 | 结论 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| **需求是否覆盖** | 顾问前端四页 + 风控前端六页(含新增 `/app/risk/convert`)+ 转换线四端点 + 风控四端点 RBAC 全部触达;SSE 两页与 `agent_tools`/`agent_graph` 明示排除 |
|
|||
|
|
| **旧功能是否破坏** | 未破坏 —— 顾问 API 跨线回归(G9)双绿;风控台账/适当性/AML 全绿 |
|
|||
|
|
| **D1 状态** | **已关闭** —— `app/repository/core_ro.py:839,851` 补齐方法,实测市场异动 API 10/10 + 浏览器 4/4 转绿 |
|
|||
|
|
| **剩余风险** | ① **F1 转换页预置必失败** ⇒ 演示第一步即卡(**建议优先修,一行数据改动**);② **F2 预警编号恒 `—`** ⇒ 演示「造预警」观感失败(**建议优先修**);③ **F3 无路由守卫** ⇒ 纵深防御单端;④ **F4 确认日默认越界** ⇒ 默认零确认;⑤ **D2 KYC 采集 500**;⑥ **D3 语义检索静默降级**(且疑似空集);⑦ `sandbox_risk_test.py:325-326` 与 `run_demo_smoke.ps1` 已腐化,**会持续报假红/假绿**;⑧ 浏览器脚本在仓库外,他人不可复现 |
|
|||
|
|
| **建议修复顺序** | F1 → F2(均为演示可见、改动面极小) → D2 → F4 → F3 → D3(需先确认向量是否已灌) |
|
|||
|
|
| **建议人工再验** | 修完 F1/F2 后重跑 `node 10-risk-pages.mjs`,目标 **21/21 PASS**;修完 D2 后目标 `advisor_e2e_smoke.py` **0 FAIL** |
|
|||
|
|
| **是否阻断发布** | **不阻断主线**(鉴权 / 合规 / 门闸 / 模板 / 台账 / 适当性 / AML 全绿);**演示前建议先修 F1、F2** |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 12. 修订历史
|
|||
|
|
|
|||
|
|
| 版本 | 日期 | 作者 | 说明 |
|
|||
|
|
| --- | --- | --- | --- |
|
|||
|
|
| v1.0 | 2026-09-12 | Andrew | 首版 · 174 用例 · 161 PASS / 12 FAIL / 1 SKIP · 收敛 6 缺陷 + 1 项测试资产腐化 · 业务代码零改动 |
|
|||
|
|
| **v1.1** | **2026-09-12** | Andrew | **结果明细入库 + 计数口径更正。** ①**新增 `_raw/` 逐用例原始记录**:`browser-*.json`×3(脚本原产物原样搬运)· `shots/`×12 张截图 · `README.md`(来源与口径说明)②**新增 `_raw/server-log-evidence.md`** —— 把 D2/D3 赖以定性的 uvicorn 日志摘录入库(原件在临时目录会被清理),**结论由「不可被第三方查阅」转为可查阅** ③**D3 计数口径订正**:原记「日志出现 **10 次**」实为**日志行数**(5 次异常 × 2 行),正确表述为 **6 次独立异常**;**D3 结论未变** ④**D2 由「推断」升级为「日志直证」**:补 `IntegrityError (1048, "Column 'created_at' cannot be null")` 逐字报文,失败 SQL 明确列出 `created_at` 列且参数为 `None` ⑤新增交叉印证:全 session **8 次 500 全部出自 KYC 采集,市场异动线 0 次** —— D1 已修的独立佐证 |
|