Files
group_fqcd_jr/docs/40-前端验收清单.md
T

17 KiB
Raw Blame History

前端验收清单(统一登录门户)

目的:把门户的每一个功能都走一遍,逐项对照"预期结果"判断是否符合预期。 被测对象:tools/portal.py(统一登录门户),默认 http://127.0.0.1:8101 时点:2026-09-12 证据口径:标 ✅实测 的项是本轮真实跑过并确认的;标 ⚠️按契约 的项是照接口定义推断的 (沙箱里不便反复写业务数据,留给你点的时候确认)。


0. 启动与前置

0.1 前置(不满足会直接显示原因,不会静默)

# 项 命令 / 检查 预期
0-1 依赖服务 MySQL、Redis 可用(Docker Desktop 要在跑,Milvus 才可用) 门户顶部不出现"平台初始化失败"红条
0-2 演示账号 python tools/seed_test_rbac.py 然后 python tools/set_user_password.py 五个账号都能登录;口令脚本非幂等,重复执行等于重设密码
0-3 停常驻 Worker 确认没有 python -m app.worker 在跑 否则客服对话会被抢队列,页面一直转圈

0.2 启动

D:\conda\envs\jr_py313\python.exe tools\portal.py                 # 进程内直挂平台,走真实鉴权栈
D:\conda\envs\jr_py313\python.exe tools\portal.py --base-url http://127.0.0.1:8000
  • 打开 http://127.0.0.1:8101 → 预期:出现登录卡片,标题"基金智能服务平台"
  • 页面顶部右侧显示连接环境 → 预期:进程内 · 127.0.0.1:3306/jr(口令已脱敏成 ***)
  • 用错的密码登录 → 预期:红条提示 登录失败(HTTP 200):...,停在本页 ✅实测
  • 同一个浏览器开两个标签页,分别登录客户与管理员 → 预期:互不干扰(会话号存 sessionStorage)

1. 登录与角色分流

五个演示账号(点"演示账号"按钮可自动填入):

账号 密码 角色 预期进入 预期权限数
cust_t 123456 customer 客服 20
risk_t 666666 risk_operator 风控工作台 10
offsite_t offsite123 operator 运营工作台 2
admin_t 88888888 admin 权限管理 50
advisor_t abc12345 advisor 投顾工作台 28
  • 逐个登录 → 预期:顶栏显示"用户名(user_id)"与角色徽章,选项卡标题与上表一致 ✅实测
  • 多角色账号 → 预期:选项卡按最高权限界面进入,其余已具备的界面也可切换(便于一次演示)
  • 点"退出登录" → 预期:回到登录卡片

角色是每次请求现查库的(令牌里只有 sub)。改了库里角色,重新登录即生效,不用重启门户。


2. 客户 · 客服视图(cust_t)

# 操作 预期结果
2-1 输入"赎回基金多久到账?"发送 气泡里出现回答(如"货币基金T+0或T+1到账…QDII T+7、T+10"),下方标签显示意图(如 faq) ✅实测
2-2 看回答末尾 附合规提示:"本内容仅为投资风险参考,不构成任何直接投资建议…" ✅实测
2-3 问一个答不了的(如"帮我下单") Agent 引导拨打客服热线;若判定需人工,标签显示已转人工 ✅实测(问"赎回费怎么算"得到引导话术)
2-4 点「我的画像」 返回当前客户的记忆画像(HTTP 200) ✅实测
2-5 点「我的画像候选」 返回候选列表;没有候选时显示"暂无候选(HTTP 200)"而不是报错 ⚠️按契约
2-6 候选里点「确认」/「拒绝」 HTTP 200,状态变更;再刷新列表状态已更新 ⚠️按契约(本机暂无候选数据)
2-7 点「转人工」 弹出说明输入框 → 确认后 HTTP 202,返回 handover_id 与 status=pending ✅实测
2-8 转人工后,用管理员看"客服转人工工单" 新工单出现在队列里 ✅实测(ticket-d8c526a9…)

2-7 的实现细节:门户会先调 POST /api/v1/conversations 真实建会话(201)再转人工。 早期版本用自己编的 session id,会得到 404「会话不存在」。


3. 员工 · 风控工作台(risk_t)

本节按 docs/风控业务演示文档/17-风控模块-前端合并提示词与验收约束.md 重写过,功能清单与交互约束都对齐了那份文档。

# 操作 预期结果
3-1 进入即自动加载"风险概览" 五张指标卡:预警总量 / 待处理 / 已超时 / 高风险 / 重点预警 ✅实测(total=2 pending=1 overdue=2 高风险=2 重点=2)
3-2 看"预警队列" 每页 5 条、按风险等级优先、行高固定且超长省略 ✅实测
本机 2 条:ALDEMO0002(高,RW-015/RW-003)、ALDEMO0001(高,RW-007/RW-002/RW-012)
⚠️ 该接口 limit 上限就是 5,传 20 会 422 且整张表渲染不出来
3-3 用筛选栏逐项筛 8 个条件:关键词、客户号、风险等级、规则码、产品代码、产品名、起止时间 ✅实测
rule_code=RW-015 → ALDEMO0002、customer_no=T-CUST → 2 条、keyword=ALDEMO0002 → 1 条
⚠️ 风险等级必须填「高/中/低」:预警对象用「高」,而概览 levels 用「高风险」,填「高风险」会 422
3-4 点「下一页 / 上一页」 走游标分页(meta.next_cursor + has_more),页脚显示"第 N 页 · 每页 5 条" ✅实测(本机 2 条 → 已到末页)
3-5 点某行「详情」 弹窗显示:预警编号、类型、等级与优先级分、处置状态、回执状态、证据归档、规则、到期时间、结案原因、证据摘要、证据快照、客户(行为分/投资者类型/风险标签)✅实测(alert 23 字段、customer 13 字段)
3-6 弹窗里点「确认接收」 二次确认后提交。✅实测:POST .../acknowledgements(无 body)→ 409「只有待处理的预警才能确认解决」,说明该动作有状态前置
3-7 弹窗里点「进入调查」 二次确认后 POST .../investigations(无 body)⚠️按契约
3-8 弹窗里点「关闭误报」 必须填写理由(1-500 字),空值会被前端与后端双重拒绝 → POST .../exclusions {reason} ✅实测(body 字段已核对)
3-9 弹窗里点「升级」 必须先「确认接收」,否则 409「请先确认接收预警」;填 reason → POST .../escalations ✅实测
3-10 弹窗里点「完成结案」 填 resolution(字段名不是 reason)→ POST .../resolutions ✅实测
3-11 点「手动扫描」 二次确认后 POST /alerts/scan → HTTP 200, code=0 ✅实测
3-12 点「生成日报」 弹窗内流式生成(SSE:start / progress / replace),生成后可编辑内容再填收件人发送 ✅实测(事件类型已核对)
3-13 点「八类证据」 八个页签:客户 / 产品 / 交易 / 资金 / 持仓 / 登录 / 预警 / 通知,各带关键词、行为分等级、发送状态、起止时间筛选 ✅实测(8/8 全部 HTTP 200)
⚠️ 正确路径是 holdings,写成 positions 会被 422 拒绝
3-14 点「通知记录」 表格:预警编号 / 类型 / 发送状态 / 时间 ✅实测(HTTP 200)
3-15 用客户账号访问风控接口 403 缺少操作权限 ✅实测

⚠️ 注意:risk_t 有 audit:read,所以它访问 /api/v1/admin/roles 是 200 而不是 403 —— 这是种子设计如此,不是越权漏洞。

⚠️ 五个处置动作都是写操作,会在库里留数据与审计,且受状态机约束(先确认 → 再调查/误报/升级/结案)。 各自动作的请求体不同:确认与进入调查无 body、误报与升级要 reason、结案要 resolution。 列表拿不到数据时行内按钮不会出现 —— 先确认 3-2 是否正常。

⚠️ 排序局限:接口没有排序参数,门户只在当前页内按风险等级+优先级分排序;跨页整体排序需要服务端支持。

⚠️ 本轮顺带修掉一个平台 bug:rule_code 筛选此前恒返回 0 条(risk_repository.py 用 .contains([code]),SQLAlchemy 把它编译成 LIKE,而列是 JSON 数组,等于匹配字符串 '["RW-015"]')。已改为 func.json_contains(col, json.dumps(code)),扫描去重处的同一写法也一并修了。


4. 员工 · 运营工作台(offsite_t)

# 操作 预期结果
4-1 进入即加载"邮箱状态" 数字卡片,HTTP 200 ✅实测
4-2 点「拉取邮件列表」 表格(邮件 ID / 主题 / 状态 / 操作)。邮箱未配置时条数为 0、不白屏 ✅实测(HTTP 200,0 条)
4-3 点某封邮件的「识别字段」 返回识别结果 JSON ⚠️按契约(需库里有邮件数据)
4-4 点「删除」邮件 二次确认后写操作。门户自动带 operator_id(= 当前登录 user_id)✅实测(字段齐了才会进业务层)
4-5 点「触发邮箱恢复」 ✅实测:HTTP 200 + body.code=404「邮箱尚未初始化」 —— 请求体已合法,是本机没配邮箱,属正常
4-6 在"单据处理"填一个真实存在的 task_id,点「识别字段」「规则结果」 返回该单据的字段与规则判定 ⚠️按契约
4-7 填一个不存在的 task_id 422/404,页面上原样显示 —— 正常 ⚠️实测(假 task_id 得到 422)
4-8 点「确认单据」/「重试识别」/「创建通知」 见下方"三个写操作的必填字段",各自会弹窗要必填值 ⚠️按契约
4-9 点「重算结算统计」 ✅实测:要填 fund_code + application_date(门户已弹窗),HTTP 200 code=0 ok

场外写操作的三条硬约束(实测得出,门户已按此实现):

接口 必填字段
mailbox-status/recoveries、mails/{id}/deletions、documents/{id}/recognition-retries operator_id
documents/{id}/confirmations decision(中文枚举:确认无误 / 确认异常 / 未处理)+ operator_id
documents/{id}/notifications notification_type(risk / settlement / mail_return / normal_return / exception_return…)+ operator_id
settlement-statistics/recalculate fund_code + application_date(没有 operator_id)

operator_id 是防伪校验:平台会核对它是否等于当前登录用户(传别人的会被拒)。 实测不传它必得 422,所以门户一律自动带本次登录的 user_id,不让你手填。

运营为什么只有 2 项权限却能用:场外线的服务层用的是角色门槛 {"operator","risk_operator","admin","super_admin"}(offsite_fund_service.py:2600), 不是权限码。那 2 项是 offsite:write 和 financial:nl2sql:read。 换句话说:"看不到运营界面"以前是前端没做,不是权限问题。


5. 管理员 · 权限管理(admin_t)

5.1 角色与权限

# 操作 预期结果
5-1 进入即加载角色卡片 每个角色显示 权限数 / 角色名 / user_count ✅实测(GET /admin/roles 200)
5-2 点任一角色卡片 列出该角色的权限码(药丸标签)+ 角色详情 ✅实测(customer 20 项)
5-3 对照第 1 节的权限数 与登录时顶栏显示的权限数一致
5-4 输入 9001 点「查询该用户的角色」 返回 cust_t → customer 的解析结果 ✅实测

平台只提供只读查询:改权限要发布新的 config_release,没有直接写接口 —— 这是设计(配置受版本控制),不是功能没做完。页面上也这么写了。

5.2 审计与工单

# 操作 预期结果
5-5 点「刷新」审计流水 时间 / 动作 / 操作者 / 结果;做过写操作后能看到刚才那条 ✅实测(200)
5-6 点「刷新工单」 客户在第 2-7 步建的工单出现在这里(工单号 / 来源 / 优先级 / 原因 / 状态)✅实测
5-7 点「列出知识文档」 文档清单(ID / 标题 / 状态)⚠️按契约(需 KB 有数据)

5.3 推广材料审核

# 操作 预期结果
5-8 填投顾给你的任务单号,点「查任务」 返回任务详情,若已有 approved/sent 版本会自动填进版本号框 ✅实测
5-9 填 material_version_id,点「通过」 二次确认后 HTTP 200 code=0;此后投顾才能投递 ✅实测(版本 19 通过)
5-10 点「退回修改」/「拒绝」 同样 200,状态流转 ⚠️按契约

6. 投顾 · 投顾工作台(advisor_t)

6.1 投资目标与方案

# 操作 预期结果
6-1 客户 ID 填 9001,点「查投资目标」 200,返回目标(目标区间、基准)—— 前提是 9001 在你名下且已建过目标 ✅实测(4.5000 - 8.0000、沪深300)
6-2 改填一个不在你名下的客户 ID 404「客户不可访问」 —— 最小权限,data_scope=own_customers 生效 ✅实测设计如此
6-3 点「已发布方案」 200,返回已发布方案列表 ✅实测
6-4 点「跑组合分析」 二次确认后 200(请求体是空对象 {},多传字段会 422)✅实测
6-5 点「生成资产配置」 同上 ✅实测

6.2 推广材料(六步流程,顺序不能跳)

# 操作 预期结果
6-6 ① 点「创建任务」 200,返回 task_no(如 PM-20260912-0007)并自动填进单号框 ✅实测
6-7 ② 改一下结构化输入(六个块必填),点「保存输入」 200 code=0 ✅实测
6-8 ③ 点「生成 PPTX」 200 code=0,返回 material_version_id 与 status=pending_review,版本号自动填进投递框 ✅实测(真实产出 v1.pptx)
6-9 若没填费率就点生成 body.code=422「材料内容未通过合规校验」,findings 里是 fee_structure.incomplete(severity=block),任务被置为 compliance_failed ✅实测
6-10 ④ 让管理员在 5.3 审核通过 见 5-9
6-11 ⑤ 点「投递」(投顾 id 填 9020) 200 code=0 ✅实测
6-12 ⑥ 点「查询任务」 200,status="sent",含 material_version(版本号、pptx 路径)✅实测
6-13 没投递就查询 404「该材料尚未发送给当前投顾」 —— 这是合规设计,不是故障;页面会追加提示告诉你怎么走完 ✅实测
6-14 点「合规检查结果」 列出 findings(通过的会显示 overall.pass)✅实测

⚠️ 两条必须知道的约定:

  1. 费率七项必须非空(subscription_fee/purchase_fee/redemption_fee/sales_service_fee/ management_fee/custody_fee/client_maintenance_fee),否则合规规则阻断生成。 骨架里填的是「待填写」占位,真实材料必须换成真实费率。
  2. 骨架刻意不预填任何业绩数字:performance_info 的业绩字段全部可选且带 show_product_performance 开关,关掉即可 —— 编造业绩是红线。

7. 通用行为预期(跨视图)

# 情形 预期表现
7-1 业务失败也返回 HTTP 200 本平台把业务错误放在 body.code(如生成失败 HTTP 200 + code=422)。门户按 body.code 判成败并标红 ✅实测
7-2 403 "当前角色权限不足(平台按设计 fail closed)",原样显示不隐藏 ✅实测
7-3 404 显示 message。注意 SESSION_NOT_FOUND 被三个异常类共用,可能是"会话不存在"、"客户不可访问"或"知识文档不存在",只能看 message 区分 ✅实测
7-4 422 报文格式问题,error.field_errors 会指出具体字段 ✅实测
7-5 写操作 一律二次确认(confirm),避免误点改数据 ✅实测
7-6 令牌 只存在服务端,浏览器拿不到;前端只有一个随机会话号 ✅设计
7-7 空数据 显示"暂无…(HTTP xxx)"或原始返回,不白屏 ✅实测

8. 已知限制(先说明,免得当成 bug)

  1. 权限界面只读 —— 平台没有写接口,改权限走 config_release 发布;
  2. 运营工作台的"单据处理"需要一个真实的 task_id —— 本机没有场外单据数据时,识别/确认/通知只能看到 404,属正常;
  3. 组合分析与资产配置只对空请求体有效({},additionalProperties:false);
  4. 风控日报/处置类写操作会在库里留数据与审计,验收时建议用专用库或事后清理;
  5. 门户是单进程工具:会话存内存,重启门户需要重新登录。

9. 验收记录表

章节 项数 通过 不符合预期 备注
0 启动与前置 4
1 登录与分流 3
2 客户 · 客服 8
3 员工 · 风控 9
4 员工 · 运营 9
5 管理员 · 权限 10
6 投顾 · 投顾台 14
7 通用行为 7
合计 64

发现不符合预期的项,记下章节号 + 当时的 trace_id(响应里带),可以直接定位到那一次请求。