Files
group_fqcd_jr/docs/40-前端验收清单.md
T
lzf_0626 76923d7e8b fix(portal): 补齐写操作的必填 body(风控升级/解决、场外六个写接口)
- 风控:escalations 要 reason、resolutions 要 resolution(字段名不同),
  且真实顺序是 先确认接收 才能升级/解决(否则 409 请先确认接收预警)
- 场外:六个写接口都必填 operator_id(防伪校验,须等于当前登录用户),
  confirmations 还要 decision(中文枚举)、notifications 还要 notification_type、
  recalculate 要 fund_code+application_date;门户自动带当前 user_id
- 实测:recalculate 200 code=0;不传 operator_id 必得 422(证明该字段必须)
- 清单 §3/§4 更新为实测结果并列出各写接口的必填字段表
2026-09-12 16:08:38 +08:00

15 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)

# 操作 预期结果
3-1 进入即自动加载"总览" 数字卡片;数据范围 all(能看全部客户的预警) ✅实测
3-2 点「刷新总览」 同上,HTTP 200 ✅实测
3-3 进入即自动加载"预警列表" 表格出现:预警号 / 客户 / 等级 / 规则 / 状态 / 操作。本机实测 2 条:ALDEMO0002(高,RW-015/RW-003)、ALDEMO0001(中,RW-007/RW-002/RW-012),均"待处理" ✅实测
⚠️ 门户刻意不传 limit:该接口 limit 上限是 5,传 20 会得到 422 query.limit: Input should be less than or equal to 5,整张表格渲染不出来(行内按钮也随之消失)
3-4 点「触发一次扫描」 HTTP 200,body.code=0(这条以前会因缺幂等头报 422,已修) ✅实测
3-5 点「生成日报」 HTTP 200,返回日报内容 ⚠️按契约
3-6 对某条预警点「确认」 二次确认后返回业务结果。✅实测:POST .../acknowledgements(无必填 body)→ 409「只有待处理的预警才能确认解决」 —— 该动作有状态前置条件,不是任意状态都能点
3-7 点「升级」/「解决」 ✅实测:必须先「确认」接收预警,否则两者都返回 409「请先确认接收预警」。
升级要填 reason、解决要填 resolution(字段名不同,各 1-500 字),门户已做成弹窗必填;不填会是 422
3-8 点一个不存在的预警号 404 资源不存在 —— 正常 fail closed ⚠️按契约
3-9 用客户账号访问风控接口 403 缺少操作权限(risk_t 能看,cust_t 不能) ✅实测(客户访问 /admin/roles 为 403)

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

⚠️ 行内三条按钮对应 acknowledgements / escalations / resolutions 三个端点,都是写操作: 会在库里留数据与审计,且受状态机约束(确认 → 升级/解决)。三个动作的请求体各不相同: 确认无 body、升级要 reason、解决要 resolution。列表拿不到数据时这三个按钮不会出现 —— 先确认 3-3 是否正常。


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(响应里带),可以直接定位到那一次请求。