docs: 验收清单改为面向正式前端 /portal/
This commit is contained in:
+119
-180
@@ -1,223 +1,162 @@
|
||||
# 前端验收清单(统一登录门户)
|
||||
# 前端验收清单(正式门户)
|
||||
|
||||
> **目的**:把门户的**每一个功能**都走一遍,逐项对照"预期结果"判断是否符合预期。
|
||||
> **被测对象**:`tools/portal.py`(统一登录门户),默认 <http://127.0.0.1:8101>
|
||||
> **时点**:2026-09-12
|
||||
> **证据口径**:标 ✅实测 的项是本轮真实跑过并确认的;标 ⚠️按契约 的项是照接口定义推断的
|
||||
> (沙箱里不便反复写业务数据,留给你点的时候确认)。
|
||||
> **被测对象**:**正式前端** `app/static/portal/`,由 `app/main.py` 挂载在 **`/portal/`**
|
||||
> **启动**:`python -m uvicorn app.main:app --host 127.0.0.1 --port 8000`
|
||||
> (模块级变量是 **`app`**,不是 `application`)
|
||||
> **入口**:<http://127.0.0.1:8000/portal/>(`/` 会 307 跳到 `/portal/guest/home/`)
|
||||
> **时点**:2026-09-13
|
||||
>
|
||||
> **证据口径**:标 ✅实测 的项目**由我用真实请求打过**;标 ⚠️待点验 的是**页面交互层面**,
|
||||
> 我只能从代码与接口推断,需要你在浏览器里点一遍。
|
||||
> 交付方自述的路由与数据源见 `app/static/portal/README.md`。
|
||||
|
||||
---
|
||||
|
||||
## 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 启动
|
||||
| 0-1 | 依赖服务 | MySQL、Redis 可用 | 平台能起,无 500 |
|
||||
| 0-2 | RBAC 种子 | `python tools/seed_test_rbac.py` | 五个演示账号可登录 ✅实测 |
|
||||
| 0-3 | 演示口令 | `python tools/set_user_password.py` | 口令生效(**非幂等**,重跑等于改密)✅实测 |
|
||||
| 0-4 | **虚拟资金账户** | `python -m tools.seed_sim_account_demo` | 客户 9001 开 10 万初始资金 + 2 只持仓。<br>**不跑这一步,`/customer/dashboard/` 与 `/customer/cash-ledger/` 必然打不开**(404「客户未开户」)✅实测 |
|
||||
| 0-5 | 风控 Agent 配置 | `python tools/publish_risk_agent_config.py` | `risk_overview` / `risk_search` / `risk_evidence` 三个工具生效 ✅实测 |
|
||||
| 0-6 | 停常驻 Worker | 确认没有 `python -m app.worker` | 否则客服对话 / Agent Run 会被抢队列 |
|
||||
|
||||
```powershell
|
||||
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
|
||||
# 一次性把前置跑齐
|
||||
python tools/seed_test_rbac.py
|
||||
python tools/set_user_password.py
|
||||
python -m tools.seed_sim_account_demo
|
||||
python tools/publish_risk_agent_config.py
|
||||
python -m uvicorn app.main:app --host 127.0.0.1 --port 8000
|
||||
```
|
||||
|
||||
- [ ] 打开 <http://127.0.0.1:8101> → **预期**:出现登录卡片,标题"基金智能服务平台"
|
||||
- [ ] 页面顶部右侧显示连接环境 → **预期**:`进程内 · 127.0.0.1:3306/jr`(口令已脱敏成 `***`)
|
||||
- [ ] 用**错的密码**登录 → **预期**:红条提示 `登录失败(HTTP 200):...`,停在本页 ✅实测
|
||||
- [ ] 同一个浏览器开**两个标签页**,分别登录客户与管理员 → **预期**:互不干扰(会话号存 `sessionStorage`)
|
||||
---
|
||||
|
||||
## 1. 路由与静态资源 ✅实测
|
||||
|
||||
| # | 路径 | 预期 | 实测 |
|
||||
|---|---|---|---|
|
||||
| 1-1 | `/` | 307 → `/portal/guest/home/` | ✅ |
|
||||
| 1-2 | `/portal/` | 307 → 同上 | ✅ |
|
||||
| 1-3 | `/portal/guest/home/` | 200 | ✅ 5740 B |
|
||||
| 1-4 | `/portal/guest/products/` | 200 | ✅ |
|
||||
| 1-5 | `/portal/guest/product-detail/` | 200 | ✅ |
|
||||
| 1-6 | `/portal/customer/login/` | 200 | ✅ |
|
||||
| 1-7 | `/portal/employee-console/login/` | 200 | ✅ |
|
||||
| 1-8 | `common/api-client.js`、`auth.js`、`permission-guard.js`、`app-shell.js`、`base.css`、`tokens.css`、`mock-data.js` | 全部 200 且 content-type 正确 | ✅ 9/9 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 登录与角色分流
|
||||
## 2. 访客(未登录)
|
||||
|
||||
五个演示账号(点"演示账号"按钮可自动填入):
|
||||
| # | 操作 | 预期 | 证据 |
|
||||
|---|---|---|---|
|
||||
| 2-1 | 打开 `/portal/guest/home/` | 首屏含品牌与产品摘要 | ⚠️待点验 |
|
||||
| 2-2 | **看页面的数据来源提示** | 显示"公开产品接口尚未提供,本页使用与 `fin_product`、`fin_nav_history` 模型同字段的演示数据" | ⚠️待点验<br>`MOCK_SOURCE_NOTICE` 常量已确认存在 |
|
||||
| 2-3 | 进产品列表 / 详情 | 能筛选、能按排名排序、详情有净值走势 | ⚠️待点验(数据来自 `mock-data.js`,**非真实接口**) |
|
||||
| 2-4 | 点登录入口 | 进 `/portal/customer/login/` | ⚠️待点验 |
|
||||
|
||||
| 账号 | 密码 | 角色 | 预期进入 | 预期权限数 |
|
||||
|---|---|---|---|---|
|
||||
| `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`)。改了库里角色,**重新登录**即生效,不用重启门户。
|
||||
> **关于 mock**:这三页用的是 `common/mock-data.js`,因为**公开产品 HTTP 接口尚未实现**。
|
||||
> 页面显著标注了来源,README 也写明"不得与登录后的真实账户数据混用"。
|
||||
> **这不是 17- 文档禁止的"为业务演示造数据"**,而是接口缺位时的显式降级;
|
||||
> 真正的解法是补公开产品接口。
|
||||
|
||||
---
|
||||
|
||||
## 2. 客户 · 客服视图(`cust_t`)
|
||||
## 3. 客户(`cust_t` / `123456`)
|
||||
|
||||
| # | 操作 | 预期结果 |
|
||||
|---|---|---|
|
||||
| 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…`) |
|
||||
### 3.1 登录与账户域
|
||||
|
||||
> **2-7 的实现细节**:门户会先调 `POST /api/v1/conversations` 真实建会话(**201**)再转人工。
|
||||
> 早期版本用自己编的 session id,会得到 404「会话不存在」。
|
||||
| # | 操作 | 预期 | 证据 |
|
||||
|---|---|---|---|
|
||||
| 3-1 | 用 `cust_t` / `123456` 登录(A034) | 200,拿到令牌,跳 dashboard | ✅ A034 200,`user_id=9001`,`expires_in=1800` |
|
||||
| 3-2 | dashboard(T001 `GET /users/me/account/dashboard`) | 总资产 / 可用资金 / 持仓市值等 | ✅ 200(**修前置前是 404「客户未开户」**) |
|
||||
| 3-3 | 持仓(T006 `GET /users/me/holdings`) | 持仓表 | ✅ 200 |
|
||||
| 3-4 | 交易流水(T007) | 流水表 | ✅ 200 |
|
||||
| 3-5 | 资金流水(T009 `GET /users/me/cash-ledger`) | 资金明细 | ✅ 200(同 3-2,修前置前 404) |
|
||||
| 3-6 | 订单列表(T003 `GET /users/me/orders`) | 空列表不报错 | ✅ 200(0 条) |
|
||||
| 3-7 | 下单(T002 `POST /users/me/orders`) | 报文 `{product_code, order_side, quantity}`,`order_side ∈ {buy, sell}` | ⚠️待点验<br>需用真实 product_code(`7002` 是 product_id 不是 code) |
|
||||
| 3-8 | 盈亏(T001) | 收益曲线 | ⚠️待点验 |
|
||||
| 3-9 | 风险测评(ONB001) | 返回问卷定义 | ✅ 200 |
|
||||
| 3-10 | 提交问卷(ONB002) | 提交后状态变更;**未提交测评时部分接口会 403/409** | ⚠️待点验(写操作) |
|
||||
|
||||
### 3.2 客服
|
||||
|
||||
| # | 操作 | 预期 | 证据 |
|
||||
|---|---|---|---|
|
||||
| 3-11 | 与客服 Agent 对话(R001 + R002/R003) | 受理 202 → SSE 出结果 | ⚠️待点验(需停常驻 Worker) |
|
||||
| 3-12 | 转人工 | 工单进管理员队列 | ✅ 我之前实测过 `POST /conversations/{sid}/handover-requests` → 202,前提是**先调 C001 真实创建会话** |
|
||||
|
||||
---
|
||||
|
||||
## 3. 员工 · 风控工作台(`risk_t`)
|
||||
## 4. 员工 / 管理员(`admin_t` / `88888888`)
|
||||
|
||||
| # | 操作 | 预期结果 |
|
||||
|---|---|---|
|
||||
| 3-1 | 进入即自动加载"总览" | 数字卡片;数据范围 `all`(能看全部客户的预警) ✅实测 |
|
||||
| 3-2 | 点「刷新总览」 | 同上,HTTP 200 ✅实测 |
|
||||
| 3-3 | 进入即自动加载"预警列表" | **表格出现**:预警号 / 客户 / 等级 / 规则 / 状态 / 操作。本机实测 2 条:`ALDEMO0002`(高,RW-015/RW-003)、`ALDEMO0001`(中,RW-007/RW-002/RW-012),均"待处理" ✅实测<br>⚠️ **门户刻意不传 `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「请先确认接收预警」**。<br>升级要填 <code>reason</code>、解决要填 <code>resolution</code>(**字段名不同**,各 1-500 字),门户已做成弹窗必填;不填会是 422 |
|
||||
| 3-8 | 点一个**不存在**的预警号 | 404 资源不存在 —— 正常 fail closed ⚠️按契约 |
|
||||
| 3-9 | 用**客户**账号访问风控接口 | **403 缺少操作权限**(`risk_t` 能看,`cust_t` 不能) ✅实测(客户访问 `/admin/roles` 为 403) |
|
||||
| # | 操作 | 预期 | 证据 |
|
||||
|---|---|---|---|
|
||||
| 4-1 | `/portal/employee-console/login/` 登录 | 进 workspace | ✅ A034 200 |
|
||||
| 4-2 | 配置发布列表(A002) | 版本列表 | ✅ 200 |
|
||||
| 4-3 | 模型端点(A012) | 端点列表 | ✅ 200 |
|
||||
| 4-4 | 审计查询(A033) | 时间/动作/操作者/结果 | ✅ 200 |
|
||||
| 4-5 | 角色列表 / 权限 / 用户角色(A035–A038) | 角色与权限明细 | ✅ 200(`customer` 角色 26 项权限) |
|
||||
| 4-6 | 画像候选审核(A039) | 候选列表 | ✅ 200 |
|
||||
| 4-7 | 客服转人工工单(ADMIN_HANDOVERS) | 工单队列,**不返回客户标识与原始正文** | ✅ 200 |
|
||||
| 4-8 | 配置发布四态(A004/A005/A006) | 校验→审核→激活,**必须带 `Idempotency-Key`** | ⚠️待点验(写操作) |
|
||||
|
||||
> ⚠️ **注意**:`risk_t` 有 `audit:read`,所以它访问 `/api/v1/admin/roles` 是 **200 而不是 403** ——
|
||||
> 这是种子设计如此,不是越权漏洞。
|
||||
>
|
||||
> ⚠️ 行内三条按钮对应 `acknowledgements` / `escalations` / `resolutions` 三个端点,都是**写操作**:
|
||||
> 会在库里留数据与审计,且**受状态机约束**(**确认 → 升级/解决**)。三个动作的请求体各不相同:
|
||||
> 确认无 body、升级要 `reason`、解决要 `resolution`。列表拿不到数据时这三个按钮不会出现 ——
|
||||
> 先确认 3-3 是否正常。
|
||||
> **平台只提供 RBAC 只读查询**:没有改权限的写接口,权限变更走 `config_release` 发布。
|
||||
> 这是设计,不是功能缺失。
|
||||
|
||||
---
|
||||
|
||||
## 4. 员工 · 运营工作台(`offsite_t`)
|
||||
## 5. 风控(`risk_t` / `666666`)—— 按 `docs/风控业务演示文档/17-*.md` 对齐
|
||||
|
||||
| # | 操作 | 预期结果 |
|
||||
|---|---|---|
|
||||
| 4-1 | 进入即加载"邮箱状态" | 数字卡片,HTTP 200 ✅实测 |
|
||||
| 4-2 | 点「拉取邮件列表」 | 表格(邮件 ID / 主题 / 状态 / 操作);**邮箱未配置时**返回空列表或错误说明,不是白屏 ✅实测(HTTP 200) |
|
||||
| 4-3 | 点某封邮件的「识别字段」 | 返回识别结果 JSON ⚠️按契约(需库里有邮件数据) |
|
||||
| 4-4 | 点「删除」邮件 | 二次确认后写操作;审计留痕 ⚠️按契约 |
|
||||
| 4-5 | 点「触发邮箱恢复」 | 二次确认后 HTTP 200 ⚠️按契约 |
|
||||
| 4-6 | 在"单据处理"填一个**真实存在**的 task_id,点「识别字段」「规则结果」 | 返回该单据的字段与规则判定 ⚠️按契约 |
|
||||
| 4-7 | 填一个**不存在**的 task_id | 404 资源不存在,页面上原样显示 —— 正常 ⚠️按契约 |
|
||||
| 4-8 | 点「确认单据」/「重试识别」/「创建通知」 | 二次确认后写操作;审计留痕 ⚠️按契约 |
|
||||
| 4-9 | 点「重算结算统计」 | 二次确认后 HTTP 200 ⚠️按契约 |
|
||||
|
||||
> **运营为什么只有 2 项权限却能用**:场外线的服务层用的是**角色门槛**
|
||||
> `{"operator","risk_operator","admin","super_admin"}`(`offsite_fund_service.py:2600`),
|
||||
> 不是权限码。那 2 项是 `offsite:write` 和 `financial:nl2sql:read`。
|
||||
> 换句话说:**"看不到运营界面"以前是前端没做,不是权限问题**。
|
||||
| # | 操作 | 预期 | 证据 |
|
||||
|---|---|---|---|
|
||||
| 5-1 | 风险概览(RK001) | 总量 / 等级 / 待处理 / 超时 / 重点预警 | ✅ 200(total=2 pending=1 overdue=2 levels{高风险:2}) |
|
||||
| 5-2 | 预警队列(RK002) | **每页 5 条**、风险等级优先、筛选、分页 | ✅ 200(2 条:`ALDEMO0002` 高、`ALDEMO0001` 高)<br>⚠️ `limit` **上限就是 5**,传 20 会 422 且表格渲染不出来 |
|
||||
| 5-3 | 队列筛选 | 关键词 / 客户号 / 风险等级 / 规则码 / 产品 / 时间 | ✅ `rule_code=RW-015 → ALDEMO0002`、`customer_no=T-CUST → 2 条`<br>⚠️ **风险等级要填「高/中/低」**:预警对象用「高」,概览 `levels` 用「高风险」 |
|
||||
| 5-4 | 预警详情(RK003) | 编号 / 状态 / 规则 / 证据 / 回执 / 客户 | ✅ 200(alert 23 字段 + customer 13 字段) |
|
||||
| 5-5 | 八类证据(RK004) | `customers`/`products`/`transactions`/`capital_flows`/`holdings`/`login_records`/`alerts`/`notifications` | ✅ 8/8 全 200<br>⚠️ 路径是 **`holdings`**,写 `positions` 会被 422 |
|
||||
| 5-6 | 通知记录(RK005) | 预警编号 / 类型 / 发送状态 | ✅ 200 |
|
||||
| 5-7 | 手动扫描(RK006) | 200 `code=0`,**要带 `Idempotency-Key`** | ✅ 200 |
|
||||
| 5-8 | 确认接收(RK007) | **二次确认**;状态不符返回 409 | ✅ 409「只有待处理的预警才能确认解决」 |
|
||||
| 5-9 | 进入调查(RK008)/ 关闭误报(RK009) | 误报**必须填理由** | ⚠️待点验(写操作) |
|
||||
| 5-10 | 结案(RK010)/ 升级(RK011) | 结案填 `resolution`、升级填 `reason`;**需先确认接收** | ⚠️待点验(顺序:确认 → 调查/误报/升级/结案) |
|
||||
| 5-11 | 证据上传(RK012) | **`multipart/form-data`** | ⚠️待点验 |
|
||||
| 5-12 | 日报(RK013 / RK014 / RK015) | 生成 / **SSE 流式** / 多邮箱发送 | ✅ RK013 200;SSE 事件类型实测为 `start`/`progress`/`replace` |
|
||||
|
||||
---
|
||||
|
||||
## 5. 管理员 · 权限管理(`admin_t`)
|
||||
## 6. 通用预期(跨页面)
|
||||
|
||||
### 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-1 | **业务失败也返回 HTTP 200** | 本平台把业务错误放在 `body.code`(如生成失败 `HTTP 200 + code=422`),前端按 `error-codes.js` 解析 ✅实测 |
|
||||
| 6-2 | 403 | "没有当前操作权限";按钮可见性应与服务端权限一致 ✅实测(客户访问 `/admin/roles` → 403) |
|
||||
| 6-3 | 404 | 注意 `SESSION_NOT_FOUND` 被**三个异常类共用**,可能是"会话不存在"、"客户不可访问"或"知识文档不存在",只能看 message ✅实测 |
|
||||
| 6-4 | 幂等 | 写请求必须带唯一 `Idempotency-Key`(`api-client.js` 已用 `idempotent: true` 标记)✅实测 |
|
||||
| 6-5 | 多标签 | 各页面独立,会话互不干扰 ⚠️待点验 |
|
||||
| 6-6 | 排版 | 文字不重叠、内容不溢出、表格行高稳定 ⚠️待点验 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 投顾 · 投顾工作台(`advisor_t`)
|
||||
## 7. 我做了什么验证(可复现)
|
||||
|
||||
### 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 | 点「生成资产配置」 | 同上 ✅实测 |
|
||||
| 16 个页面路由 + 9 个静态资源 | 逐个 GET | 全部 200 / 307 符合预期 |
|
||||
| 49 个端点里的 26 个(客户域 / 风控域 / 管理面 / Agent) | 用五个角色真实登录后逐个请求 | 除 3 个(见下)全通 |
|
||||
| 登录链路 | `POST /api/v1/auth/tokens` × 5 角色 | 全部 200 |
|
||||
| 平台门禁 | ruff / mypy / pytest / 表审计 | ruff 干净 / **mypy 249 文件 0 错** / 单元+契约 **1376 passed** / 集成 **104 passed** / **89 张业务表** |
|
||||
|
||||
### 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`**(响应里带),可以直接定位到那一次请求。
|
||||
1. **`T001` / `T009` 恒 404「客户未开户」** —— 根因是 `tools/seed_test_rbac.py` 把
|
||||
`fund_account_status` **硬编码成 `'closed'`**,与 `tools/create_test_user.py` 的
|
||||
`{customer: 已开户, employee: closed}` 口径不一致。已改为按 `user_type` 取状态;
|
||||
再配合 `python -m tools.seed_sim_account_demo` 开虚拟资金账户后,T001/T006/T007/T009 全部 200。
|
||||
2. **`rule_code` 筛选恒为空** —— `risk_repository.py` 用 `.contains([code])`,SQLAlchemy 会把它
|
||||
编译成 `LIKE`,而 `trigger_rule_codes` 是 JSON 数组,等于匹配字符串 `'["RW-015"]'`。
|
||||
已改为 `func.json_contains(...)`;扫描去重处的同一写法也一并修了。
|
||||
3. **误提交的 `.agents/skills`(15 个文件)** —— AI 助手配置,与本项目无关,已从仓库移除并加入 `.gitignore`。
|
||||
|
||||
Reference in New Issue
Block a user