- Introduced `convert_meta_api` endpoint to fetch the latest NAV date for conversion processes, restricted to users with the "risk_officer" role. - Updated `created_at` field in `AgentMessage` to use UTC timezone for consistency in timestamp handling. - Added `get_max_product_nav_date` method in `CoreReadOnlyRepository` to support the new API functionality. - Enhanced Milvus template loading in `MilvusTemplateVectorStore` to ensure collections are loaded when they exist. This update improves the API's capability to handle conversion metadata and ensures accurate timestamp management across the application.
12 KiB
顾问前端 + 风控新功能(基金转换 T+1)+ 风控前端 端到端测试(第三轮)
状态:已执行完毕(2026-09-12)。本文件是实施计划的入库副本,勾选状态为实际结果。 设计说明见
docs/superpowers/specs/2026-09-12-agent-frontend-e2e-design.md; 产出报告见docs/memory/tests/2026-09-12-agent-frontend-e2e/TEST-LOG-2026-09-12-FE-001.md。本 plan 只做测试与报告,不改任何业务代码 —— 实际执行亦如此(
app/与web/src/零改动)。
Context
前两轮都是 API-only,且都被后续合并改写了基线:
| 轮次 | 包 | 覆盖 | 现状 |
|---|---|---|---|
| 第一轮 | docs/memory/tests/2026-09-12-advisor-agent-e2e/ |
顾问 API 全量 | 已失真:结论写于 03fce88 之前 → 本轮已改写为 v1.1 |
| 第二轮 | docs/memory/tests/2026-09-11-risk-agent-e2e/ |
风控 API(进程内 TestClient) | 基线有效,但未覆盖基金转换 |
| 本轮 | docs/memory/tests/2026-09-12-agent-frontend-e2e/ |
顾问前端 + 风控新功能 + 风控前端 | 已完成 |
驱动本轮的三件事:
- 顾问前端已就绪 —— 四页此前从未在真实浏览器里点过。
- 风控新功能已合入 ——
03fce88把「基金转换 T+1」整条线并入merger(110 文件、仅后端);c09b987补上RiskConvertPage前端与迁移。该功能从未做过端到端。 - 基线被合并改写 —— 当前工作区在
merger@c09b987。
开工前必须知道的三条基线订正(已实测)
- D1 已修 ——
app/repository/core_ro.py:839,851✅ 实测转绿(见 Task 4) - D2 未修 ——
app/model/entities.py:116的created_at仍无 Python 侧 default ✅ 实测仍 500 - D3 定性存疑 —— 设计时的假设「高层
MilvusClient自动 load,所以不是未 load 的问题」被服务端日志证伪,详见 Task 4 结果
Goal
对 A(顾问前端)· B(风控转换 + RBAC)· C(风控前端)三块做真实端到端(真浏览器 / 真 HTTP / 真 MySQL / 真 DeepSeek)验证,产出可复现的通过矩阵与缺陷清单,先报告、确认后再修。
Architecture
Part A/C 浏览器跑道 Part B API 跑道
──────────────────────── ────────────────────────
Chromium(channel:chrome) httpx.Client
↓ http://localhost:5173 ↓ http://127.0.0.1:8000
Vite dev server (proxy /api) uvicorn app.main:app
↓ ↓
└──────── 同一套 MySQL + Redis + DeepSeek ────────┘
浏览器跑道必须经 Vite(app/ 无 CORS 中间件)。
Tech Stack
Playwright 1.63.0(Node,仓库外)+ 系统 Chrome · Python 3.13 + httpx 0.28 · MySQL 8 双库 + Redis 6380 + Milvus Lite + Ollama bge-m3
Global Constraints(全部遵守 ✅)
- ✅ 不改任何业务代码 ——
app/、web/src/零改动 - ✅ 不修 D1–D4,只报告
- ✅ 不改
web/package.json—— 浏览器脚本留在仓库外 - ✅ 不修
sandbox_risk_test.py:325-326—— 只作为「测试资产腐化」上报 - ✅ 不动
run_demo_smoke.ps1—— 同上 - ✅ 未执行任何 DROP 重建(保留取证数据)
- ✅ 凭据未逐字写入交付文档
- ✅ KYC / 话术两页如实标 FAIL 并附页面取证
File map(实际)
| 文件 | 动作 | 结果 |
|---|---|---|
C:/Users/Windows/e2e-jinrong/harness.mjs |
改 | ✅ ROUTES 补页 + clearAuth / waitRows / cjk;login() 加 reload:false |
C:/Users/Windows/e2e-jinrong/09-advisor-pages.mjs |
建 | ✅ 17 用例 · 15 PASS / 2 FAIL |
C:/Users/Windows/e2e-jinrong/10-risk-pages.mjs |
建 | ✅ 21 用例 · 19 PASS / 2 FAIL |
C:/Users/Windows/e2e-jinrong/11-cross-role.mjs |
建 | ✅ 9 用例 · 4 PASS / 5 FAIL |
scripts/dev/risk_e2e_smoke.py |
建 | ✅ 64 用例 · 63 PASS / 0 FAIL / 1 SKIP · 退出码 0 |
scripts/dev/advisor_e2e_smoke.py |
复用不改 | ✅ 重跑取当前基线 · 63 用例 · 60 PASS / 3 FAIL |
docs/memory/tests/2026-09-12-advisor-agent-e2e/TEST-LOG-...-ADV-001.md |
改 | ✅ 升 v1.1 · 关闭 D1 |
docs/memory/tests/2026-09-12-agent-frontend-e2e/ |
建 | ✅ README + FE-001 + _raw/ |
docs/superpowers/specs/2026-09-12-agent-frontend-e2e-design.md |
建 | ✅ |
docs/superpowers/plans/2026-09-12-agent-frontend-e2e.md |
建 | ✅ 本文件 |
docs/memory/MEMORY.md · TODO.md |
改 | ✅ 已同步 |
Task 0 · 环境前置自检 ✅
- 确认树:
merger@c09b987 - 只读探测演示数据未过期(未 DROP)
- 记录基线:NAV 最新
2026-09-11·core_product_nav覆盖 14 产品 - 抓到 merge 前的旧 uvicorn 进程(PID 27348,启动于 16:55:24,早于代码落地 17:52–17:59)→ 重启,否则全部转换用例假失败
结果:前置通过。新增前置项:/openapi.json 必须含转换路径 —— 已写入报告 §3。
Task 1 · 起服务 ✅
python -m uvicorn app.main:app --host 127.0.0.1 --port 8000(无--reload)/health返回{"status":"ok"}cd web && npm run dev→http://localhost:5173- Vite 代理
/api生效
Task 2 · Part B:风控 API 真实 HTTP E2E ✅
- 新建
scripts/dev/risk_e2e_smoke.py,照搬advisor_e2e_smoke.py骨架(Ctx/ok/bad/--base-url/--only/--report/ 退出码 0·1·2) - R1 鉴权负例 · R2 台账 RBAC · R3 处置状态机 · R4 适当性 · R5 AML · R6 转换线
- 顺序依赖:先
simulate/trade+aml/scan造 alert,再做 R2/R3
结果:63 PASS / 0 FAIL / 1 SKIP,退出码 0。
过程中的三条假 FAIL(全是脚本自身的 bug,已定位并修)
| 假 FAIL | 真因 | 修法 |
|---|---|---|
转换受理 可用份额不足:申请 30000,可用 0(已扣除在途转换占用) |
反复跑测把 CUST-1001 持有的 30000 份 PROD-110022 占满 |
换仍有 80000 份余量的 CUST-9527 |
accept_date=2026/09/15 期 400 |
FastAPI 查询校验 pre-handler 就拦下,实为 422 | 期望改 422 REQUEST_VALIDATION_FAILED |
撤单 409 CANCEL_NOT_ALLOWED |
我自己的顺序错:先跑了 confirm_batch,单子已进 nav_pending(不可撤) |
撤单挪到 confirm 之前,并另开一笔专用转换 |
三条都不是产品缺陷 —— 已用独立探针脚本逐条证实。
Task 3 · Part A/C:浏览器 E2E ✅
harness.mjs补路由 + 抽clearAuth/waitRows/cjk- 登录回归:去掉
reload兜底仍能登录 → A6.1 PASS(BUG-1 回归确认) - Part A 顾问:首页 / 合规 / 话术模板 / KYC / 市场异动 → 15 PASS / 2 FAIL(D2 · D3)
- Part C 风控:首页 / 台账 / 适当性 / AML / 模拟交易 / 转换页 → 19 PASS / 2 FAIL(F1 · F2)
- 跨角色:4 PASS / 5 FAIL(F3)
- 明确排除:两个 SSE 页
结果:47 用例 · 38 PASS / 9 FAIL。
AntD 6 的坑(本轮踩到,已固化)
| 坑 | 实际 |
|---|---|
.ant-tabs-tabpane-active 匹配 0 元素 |
活跃面板是 .ant-tabs-content-active |
.ant-select-selection-item 取不到值 |
值节点是 .ant-select-content |
.ant-table-row 匹配 0 元素 |
用 .ant-table-tbody tr |
| CJK 之间被插入空格 | 加 cjk() 辅助函数生成 \s* 间隔正则 |
| 非活跃 Tab 面板仍挂载且隐藏 | 所有交互必须 scope 到 pane(),否则选到隐藏元素而超时 |
Task 4 · 改写第一轮顾问报告 ✅
- 重跑
advisor_e2e_smoke.py - §3 被测版本 →
merger @ c09b987 - 关闭 D1(附
core_ro.py:839,851证据 + G5 10/10 实测) - §4 用例数按实跑口径(63 用例)
- D3 按本轮实测重新定性 —— 见下
- §11 结论改写 · §12 追加 v1.1 行
D1 结论:✅ 已关闭。v1.0 为 3 PASS / 2 FAIL / 5 SKIP,本轮 G5 10/10 PASS,scanned=14 created=2 used_fallback=False。
D3 结论 —— 我的设计期假设被证伪:
- plan 假设「高层
MilvusClient在 search 时自动 load,所以不是未 load 的问题」。这个假设是错的。 - uvicorn 日志里
MilvusException (code=101, message=Collection 'kb_script_templates' is in state 'released'; call load() before search/get/query)出现 10 次,且无其它 Milvus 错误类型。 - 第二层观察:集合目录 5.0K / 2 文件,而已灌集合为 50K / 5 文件 ⇒ 疑似同时是空集。仅补
load()未必恢复召回。 - 如实记入报告,不掩饰假设被推翻。
pymilvus探针两次失败:①Illegal uri(legacyconnections在 import 期读MILVUS_URI);②Open local milvus failed(运行中的 uvicorn 持有 Milvus Lite 文件锁)。最终改用服务端自身日志作为判别依据 —— 这比进程外探针更直接,且不需要停止服务。
Task 5 · 产出本轮报告包 ✅
- 建包:
README.md+TEST-LOG-2026-09-12-FE-001.md(12 节_TEMPLATE.md结构) - 缺陷四类归属:实现缺陷(F1/F2/F4)· 纵深防御缺口(F3)· 承接未修(D2/D3)· 测试资产腐化(T1)
- 每个缺陷给:现象 / 复现 / 期望-实际 / 根因
file:line/ 修复方向(只写方向) - 建 superpowers spec + plan 入库副本
- 同步
MEMORY.md/TODO.md
Task 6 · 收口与复现性 ✅
- 报告附复现三连
- 记录浏览器脚本在仓库外的事实与代价,挂到
TODO.md的「E2E 最小集进仓库」P0 - 列出未测/不可判定项及原因
结果总览
| 跑道 | 用例 | PASS | FAIL | SKIP |
|---|---|---|---|---|
| 顾问 API | 63 | 60 | 3 | 0 |
| 风控 API | 64 | 63 | 0 | 1 |
| 顾问前端 | 17 | 15 | 2 | 0 |
| 风控前端 | 21 | 19 | 2 | 0 |
| 跨角色 | 9 | 4 | 5 | 0 |
| 合计 | 174 | 161 | 12 | 1 |
收敛 6 缺陷:D2(承接)· D3(承接,定性更正)· F1 转换页预置跨主体必失败 · F2 alert_id/alert_ids 字段名不匹配 · F3 无路由级角色守卫 · F4 确认日默认越界
+ 1 项测试资产腐化 T1:sandbox_risk_test.py:325-326 · run_demo_smoke.ps1
交叉印证成立:浏览器侧失败与 API 侧同名用例同因同果,无额外分歧。
建议修复顺序:F1 → F2(演示可见、改动极小)→ D2 → F4 → F3 → D3
验证方式(总)· 复现三连 ✅
# 1) 后端(WinError 10013 时退 18000,并同步改下面所有 URL)
python -m uvicorn app.main:app --host 127.0.0.1 --port 8000
# 2) 前端 —— 必须用 localhost:5173(app/ 无 CORS,浏览器只能走 Vite 代理)
cd web && npm run dev
# 3) 三支脚本
python scripts/dev/risk_e2e_smoke.py # 退出码 0 = 全过 / 1 = 有 FAIL / 2 = 前置失败
python scripts/dev/advisor_e2e_smoke.py
cd C:/Users/Windows/e2e-jinrong && node 09-advisor-pages.mjs && node 10-risk-pages.mjs && node 11-cross-role.mjs
本机特有状态依赖(换机复现须知):
- 浏览器三脚本不在仓库内 —— 需手工拷贝
C:/Users/Windows/e2e-jinrong/ CUST-9527的PROD-110022可用份额会被转换用例消耗,耗尽后需重灌种子- Milvus
kb_script_templates本轮刻意未灌(写操作,且会掩盖 D3 的待修状态) - NAV 需覆盖到
2026-09-11(决定确认批处理的可用日期区间)
不在本 plan 范围
- 不修 D1–D4 及任何业务代码
- 不改
web/package.json - 不修
sandbox_risk_test.py失效断言、不修run_demo_smoke.ps1—— 只上报 - 不测两个 SSE 对话页、不测
agent_tools.py/agent_graph.py - 不收敛既有
test_sprint*红项 - 不执行
prepare_all.ps1式双库 DROP 重建 - 不做性能/压力测试;不做响应式与可访问性专项