Files
group_fqcd_jr/app/static/portal/README.md
T
lzf_0626 9cb0f6e474 feat(portal): 公开产品接口(P001)落地,访客三页改读真实数据
访客首页推荐、产品列表、产品详情此前读的是前端手写的
`app/static/portal/common/mock-data.js`:只有 8 只,且**其中 6 只根本不在
`fin_product` 里**(159915 / 512100 / 513100 / 511360 / 159645 / 159925),
还把海富通的 `511360` 标成"南方短融ETF"、把 `510500` 净值写成 6.742(真实 7.6027)。
`README.md` 把它记为"公开产品 HTTP 接口尚未实现"的临时方案。

## 接口

新增 `GET /api/v1/products`(编号 **P001**,已在 `docs/05` §19 总目录与 §19 说明中登记):

- **要求有效令牌但不校验权限码**:访客令牌的角色是 `visitor`、不带任何权限,
  这与 `/api/v1/agent-runs`、`/api/v1/conversations` 面向访客的口径一致;
  产品信息本身是公开信息。数据面只暴露 `fin_product`(`status='上市'`)与
  `fin_market_price` 的最新一行,**不含任何账户/客户字段**(由 integration 测试守着)。
- 字段与 `mock-data.js` 对齐,因此前端渲染与筛选逻辑**一行未改**。
- `change_pct` **可能是 `null`**:当日涨跌需要两个交易日的收盘价,行情只同步过一天时
  算不出来。前端对 `null` 显示"暂无" —— `formatPercent(null)` 会渲染成 `+0.00%`,
  那等于对客户说"今天平盘",是编出来的结论。

## 前端

- 新增 `common/visitor-token.js`:访客令牌的**唯一实现**(这段逻辑原先只写在客服浮窗里,
  现在四处要用;复制四份的话存储 key 与过期判断迟早不一致);`widget.js` 改为复用它。
- 新增 `common/product-notes.js`:产品级披露文案。510300"非本公司发行"那条是**合规披露**,
  不能随 mock 一起删掉。
- 三个访客页改读接口,并显式区分 loading / error 状态。
- **删除 `common/mock-data.js`**。
- 产品详情页**不再画走势图**:`fin_nav_history` 目前 0 行,此前那条曲线是 mock 里
  12 个编造点位 —— 走势图最容易被当成真数据,宁可不画,并显式说明"尚未接入"。

## 顺带修正

`min_amount` 在库里全是 0.00(那本是场外"最小认购金额"的概念,对场内按手交易的 ETF
不适用),页面不再显示"¥0.00"(会被读成零元起购),改为"1 手(100 份)起"。

验证:接口实测 `count=20`;ruff 通过;mypy 251 文件 0 错;unit+contract 1387 passed;
integration 106 passed;e2e 冒烟 40/40。
2026-09-13 22:57:48 +08:00

53 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Portal 路由与角色映射
正式门户使用原生 JavaScript、原生 CSS 与同源 `fetch`,静态资源统一从 `/static/portal/` 加载。
| 路由 | 角色 | 数据源 |
|---|---|---|
| `/portal/guest/home/` | 访客 / 全部 | P001(推荐位取前 3 只) |
| `/portal/guest/products/` | 访客 / 全部 | P001 |
| `/portal/guest/product-detail/?code=` | 访客 / 全部 | P001(**不含历史净值曲线**) |
| `/portal/customer/login/` | 未登录客户 | A034 |
| `/portal/customer/dashboard/` | customer / admin | T001、T002 |
| `/portal/customer/holdings/` | customer / admin | T006 |
| `/portal/customer/profit-loss/` | customer / admin | T001 |
| `/portal/customer/orders/` | customer / admin | T003 |
| `/portal/customer/transactions/` | customer / admin | T007 |
| `/portal/customer/cash-ledger/` | customer / admin | T009 |
| `/portal/customer/risk-questionnaire/` | customer | ONB001、ONB002 |
| `/portal/employee-console/login/` | 未登录员工 / 管理员 | A034 |
| `/portal/employee-console/workspace/` | admin / super_admin | A002-A006、A012、A033、A035-A040、客服转人工管理接口 |
| `/portal/employee-risk/dashboard/` | risk_operator / admin / super_admin | `/api/v1/risk/**`、R001-R003 |
| `/portal/employee-advisor/dashboard/` | advisor / admin / super_admin | `/api/v1/advisor/recommendations/published`(本人 + 名下归属客户的**已发布**交付物) |
| `/portal/employee-operations/dashboard/` | operator / admin / super_admin | `/api/v1/offsite-fund/mails`、`/api/v1/offsite-fund/mailbox-status` |
> 投顾页的数据口径:接口按「本人 + `sys_customer_assignment` 里名下归属客户」过滤,且同时覆盖
> `investment_goal_book`(方案书,发布后 `review_status='published'`)与
> `advisor_recommendation_plan`(推荐方案,审核后 `review_status='approved'`)两类内容。
> 方案书的**审核与发布都要求管理员**(`investment-goal:review` / `publish` 都带 `admin=True`),
> 投顾自己发不出来 —— 这是有意设计的复核环节,不是缺陷。
客服浮窗由公开首页、基金产品页和客户工作台统一挂载。访客使用 `/api/v1/visitor-tokens`
获取短期令牌(角色 `visitor`,权限只有 `agent:run` + `knowledge:query`),因此**必须**走
`query_knowledge` 这个工具名;登录客户走 `search_knowledge`。两者都要出现在发布配置的
`agent_tools/customer_service:<intent>` 白名单里,缺哪一条,对应人群就一问即失败。
公开产品数据来自 **`GET /api/v1/products`(编号 P001,见 `docs/05` §19)**,
产品与净值取自 `fin_product`(`status='上市'`)、行情取自 `fin_market_price` 的最新一行。
访客页面通过 `common/visitor-token.js` 取短期访客令牌后调用;该文件是访客令牌的**唯一实现**
(客服浮窗也用它),不要在页面里另写一份,否则存储 key 与过期判断迟早不一致。
**公开产品数据不得与登录后的真实账户数据混用。**
三点口径(改前端前先读):
1. **`change_pct` 可能是 `null`**(行情只同步过一个交易日时算不出涨跌)。
调用方必须显示"暂无",**不得当成 `0`** —— `formatPercent` 收到 `null` 会渲染成 `+0.00%`,
那等于告诉客户"今天平盘"。
2. **历史净值曲线没有数据源**:`fin_nav_history` 目前 0 行,详情页因此**不画走势图**,
并显式说明"尚未接入"。此前那条曲线是 mock 里 12 个编造点位 —— 走势图最容易被当成真数据。
3. **产品级披露在 `common/product-notes.js`**(如 510300 的"同指数参考产品,非本公司发行")。
`fin_product` 没有这个字段,所以它留在前端;新增需要披露的产品时改那一份。
凡渲染公开产品的页面都要挂 `data-source-notice` 说明数据来源。
客户页面均由 `common/auth.js` 执行入口守卫,接口路径只在 `common/api-client.js` 的端点表登记。