fix(portal): 照接口逐条核对前端,修掉 5 处「照着表结构写、看着像对」的缺陷
做法:把前端 `api-client.js` 注册的端点**按前端完全相同的方式**(同样的路径、参数、
身份)逐个调用,再拿真实返回去核对前端 render 用到的字段。
**这类问题纯读代码看不出来** —— 只有把真实返回和期望字段摆在一起才会暴露。
## 1. 知识库功能实际是坏的(K002 / K003 是裸信封)
`K002` 的成功体是 `{knowledge_ids, filename, chunk_count}`、`K003` 是 `{items, count}`,
**都没有 `data` 信封**。而 `request()` 默认取 `payload.data`(undefined),于是:
- 上传后前端显示「已入库 **0** 块」,而库里其实切了 23 块;
- 列表永远显示「知识库为空」。
端点表里标 `raw: true` 后(与 `V001` 同一做法)两者都正常。
实测:上传 4805 字符的产品手册 → 切 23 块并出现在列表里。
## 2. 委托/成交详情页有 5 行永远显示「--」
前端按 `fin_sim_order` / `fin_transaction` 的**建表字段**写了 `quote_source`、
`nav`、`fee_rate_snapshot`、`confirmed_at`、`auto_confirmed` ——
但这些字段**接口的返回视图没有带**(表里有、返回里没有)。已按实际返回重写字段表,
并在注释里写明"以接口返回为准,不要照表写"。
## 3. 配置项与路由规则的**编辑功能不可能成功**(接口缺口)
`PUT` 硬性要求 `If-Match`,校验的是该行内容的 digest;而这两个资源是 `detail=False`
—— **没有任何端点能返回这个 digest**(列表的 `meta` 只有 trace_id)。
乐观并发在"读不到版本"的前提下等于死锁:**首次编辑必然 409**。
(配置发布能用,是因为它有详情端点 `A003`。)
- 新增详情端点 `A048` / `A049`(`detail=True`),已登记 `docs/05` §19;
- 前端编辑前先 GET 详情取 etag,再带 `If-Match` 提交。
- 实测:编辑配置项与路由规则均 200;**不带 `If-Match` 仍返回 409**,
说明乐观并发没有被削弱。
## 4. 路由规则表单**必然提交失败**
前端固定写 `max_attempts: 2` 且 `fallbacks: []`,而后端要求
`max_attempts ≤ 端点总数`(主 + 兜底)→ 422「重试次数超过端点数量」。
改为 `1` 并注明约束。
## 5. 主端点手填 ID 会 422
后端对不存在/未激活的 `primary_endpoint_id` 直接 422「模型端点不存在或未激活」。
把输入框改成**下拉**,只列 `status='active'` 的端点(数据复用已有的端点列表)。
## 顺带
- `apiClient` 增加 `del()` / `put()`:发出的方法一直由端点表决定,所以 `post('K004')`
也能发 DELETE —— 语义太绕,现在意图与行为一致。
- 清理了测试期间上传的 31 条知识残留(客服会检索到它们),库内恢复到 23 条产品手册。
## 关于"逐条核对"的方法论
前两轮跑出来的 9 个和 5 个"失败"里,**多数是我测试脚本自己的假设错了**,不是前端问题:
`T001` 是 `{account, summary}` 嵌套、`RK002` 的字段叫 `risk_level`、
`RK002/RK004/RK005` 的 limit 上限是 5/10/10(前端传的正是 5/10/10)、
`AD011/A002/A047` 的 data 是裸 list。每一处都回到前端源码确认后才下结论 ——
**先把"我以为"改成"代码里写的"**,否则报告出去的就是假 bug。
验证:unit+contract **1397 passed**;integration **110 passed**;ruff 通过;
mypy 251 文件 0 错;§19 现 93 个端点无重复;e2e 冒烟 **40/40**。
前端等价测试:只读 31 项全绿、写操作(含 ETag 链路)9 项 8 绿 1 项因测试数据过短。
This commit is contained in:
+12
-1
@@ -1115,6 +1115,8 @@ GET /internal/metrics
|
||||
| A045 | `POST /api/v1/admin/advisor/recommendations/{content_id}/reviews` | `product-recommendation:review`(+`admin`) | 必须 | `200` | 推荐方案审核 |
|
||||
| A046 | `POST /api/v1/admin/advisor/recommendations/{content_id}/publications` | `product-recommendation:publish`(+`admin`) | 必须 | `200` | 推荐方案发布 |
|
||||
| A047 | `GET /api/v1/admin/advisor/pending-contents` | `product-recommendation:review`(+`admin`) | 否 | `200` | 否 |
|
||||
| A048 | `GET /api/v1/admin/config-releases/{release_id}/platform-config-items/{item_id}` | `config:read`(+`admin`) | 否 | `200` | 否 |
|
||||
| A049 | `GET /api/v1/admin/config-releases/{release_id}/model-routing-rules/{rule_id}` | `config:read`(+`admin`) | 否 | `200` | 否 |
|
||||
| AD001 | `POST /api/v1/advisor/investment-goals` | `investment-goal:write:self` / `:customer` | 必须 | `201` | 投资目标创建 |
|
||||
| AD002 | `GET /api/v1/advisor/investment-goals/current` | `investment-goal:read:self` | 否 | `200` | 否 |
|
||||
| AD003 | `GET /api/v1/advisor/customers/{customer_id}/investment-goals/current` | `investment-goal:read:self` / `:customer` | 否 | `200` | 否 |
|
||||
@@ -1210,7 +1212,16 @@ GET /internal/metrics
|
||||
|
||||
业务域接口 `/customer-service/handover-tickets/**`、`/advisory-plans/**`、`/sim-orders/**`、`/risk-scans/**` 和 `/risk-alerts/**` 的具体方法、请求体、领域状态机和错误码分别由对应业务文档登记;它们仍必须遵守本文第 3-5、11 和 12 节。
|
||||
|
||||
> **AD 段(投顾自用)与 A041–A046(投顾治理)的六点说明**:
|
||||
> **A048 / A049 为什么必须存在**:`platform-config-items` 与 `model-routing-rules`
|
||||
> 的**更新端点要求 `If-Match`**,校验的是该行内容的 digest;而这两个资源此前**没有详情端点**,
|
||||
> 列表的 `meta` 也不带 etag —— 客户端**无从取得当前 digest**,首次编辑必然
|
||||
> `409 RESOURCE_VERSION_CONFLICT`。乐观并发在"读不到版本"的前提下等于死锁。
|
||||
> 补上详情端点后,客户端 GET 详情(响应头 `ETag` + `meta.etag`)再 PUT 即可。
|
||||
>
|
||||
> ⚠️ 判据:**凡接受 `If-Match` 的资源,必须同时提供能返回该 etag 的读取路径** ——
|
||||
> 这是本次由前端等价测试(照接口逐个调用核对)才暴露出来的,纯看代码不容易发现。
|
||||
|
||||
> **AD 段(投顾自用)与 A041–A047(投顾治理)的六点说明**:
|
||||
>
|
||||
> 这批端点原先**只存在于代码中**,`§19` 一条都没登记(2026-09-13 补登)。当时 §12 写的
|
||||
> 入口是 `/api/v1/advisory-plans/**`,与实际路径 `/api/v1/advisor/**` **不符**,
|
||||
|
||||
Reference in New Issue
Block a user