front/docs/下游接口明细改造方案-小改vs大改.md
2026-06-04 19:15:28 +08:00

373 lines
17 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.

# 下游接口明细改造方案:小改 vs 大改
> 场景:下游接口页(`/api/downstreamInterface`)保存接口明细时,需要更好支持 **FormUrlEncoded / 表单类接口** 中「某个字段的值是一整段 JSON」的情况。
> 目标:为上游映射提供独立的 **Mapping Body(映射体)** 结构树,表单字段用 **`$FromMappingBody`** 引用映射结果。
> 参考 UI:`/api/apiDebugger`(ApiTest:Query / Request(Body) / Header)。
---
## 1. 现状速览(为何需要改)
| 层级 | 现状 | 问题 |
|------|------|------|
| **表** | `t_parameter`(`IsInPara`)= 入参树;`t_parameter_form`(无位置)= 表单 KV;`t_parameter_fixed`(`FieldPositions`)= 固定 Header/Query/Body | 前端「Query」Tab 实际写的是 `formParameterList`,后端却只把 Form 当 **整段 x-www-form-urlencoded Body**,不是 URL Query |
| **页面** | InterfaceDetail:Query(表单) + Body(参数树) + 固定配置 + cURL | 与 ApiTest 的 Query/Body/Header 不一致;**Body 树**同时承担「HTTP JSON 结构」和「上游映射主体」,表单里 JSON 字段只能塞 `$FromBody`,语义混 |
| **转发** | `InParamRestructure` ← 下游 `IsInPara` 树;`FormFieldList` ← 全部 form;Query/Header ← `FixedFieldList` | 映射树与传输层表单未分离 |
```text
上游 JSON ──► InParamRestructure(下游 IsInPara 树) ──► OriginalInterfaceMappedInput
│
▼
ReplacePlaceholder / 序列化
│
▼
FormUrlEncoded(form 全进 Body)+ Fixed(Query/Header)
```
---
## 2. 方案总览
| 维度 | **方案 A:小改(推荐优先)** | **方案 B:大改(完全对齐 ApiTest)** |
|------|---------------------------|-------------------------------------|
| **核心思路** | 只新增 **Mapping Body** 参数树 + **`$FromMappingBody`**,其余结构基本不动 | Query/Body/Header 表单分表存储 + 转发按位置组装;Mapping Body 独立;逐步弱化「固定配置」与旧 form 语义 |
| **表变更** | 1 张表加 1 个字段(或 1 个枚举列) | 至少 2 张表扩展 + 数据迁移 + 行为重构 |
| **API 变更** | `SaveInterface` / `GetInterfaceById` 增字段;占位符枚举增 1 项 | 契约重组(多列表/统一带 position);Fixed 与 Form 职责重划 |
| **转发逻辑** | 映射树数据源切换 + 占位符分支(约数十行级) | `RequestAsync` / `SetContentType` / 缓存 / 映射 SQL 全链路调整 |
| **前端** | InterfaceDetail 增 1 个 Tab,保存多传一个列表 | 整页 Request 区重写,对齐 ApiTest;curl 填充、映射页联动 |
| **风险** | 低,可灰度、可回滚 | 中高,影响所有下游接口与发布数据 |
| **工期(粗估)** | 后端 2~4 人日 + 前端 3~5 人日 | 后端 8~15 人日 + 前端 10~18 人日 + 联调/迁移 |
---
## 3. 方案 A:小改(Mapping Body 增量)
### 3.1 要解决什么
**只解决**:Query/Body **表单**里某字段值为 JSON(或应填 `$FromBody`)时,上游映射应对的是 **JSON 内部结构**,而不是把整段 form 或错误树当映射目标。
**不解决**(保持现状):
- Form 仍整体作为 `FormUrlEncoded` Body(不拆真正的 URL Query 表单 Tab 行为)
- Header 仍主要走 **固定配置**(`FixedParameter`)
- `ContentType=Json` 的下游接口仍用现有 `inParameterList` 作 Body + 映射(可不显示 Mapping Body Tab)
### 3.2 表结构
| 表 | 变更 | 说明 |
|----|------|------|
| `t_parameter` | 新增 `ParaKind`(int)或 `IsMappingBody`(bit) | `0=Normal`(现有入参/出参逻辑),`1=MappingBody`(仅映射,不参与 Json 序列化 Body 时可配置策略) |
| `t_parameter_form` | **不改** | 仍 flat KV;JSON 字段 value 改为 `$FromMappingBody` |
| `t_parameter_fixed` | **不改** | Header/Query 固定值照旧 |
**迁移**:历史数据 `ParaKind=0`;原 `inParameterList` 若实为映射树,可脚本把「仅用于 FormUrlEncoded 映射」的接口标为 MappingBody,或 **读时兼容**(见下)。
### 3.3 接口契约
#### `GET Interface/GetInterfaceById`
```json
{
"inParameterList": [],
"mappingBodyParameterList": [],
"formParameterList": [],
"outParameterList": []
}
```
**兼容规则**:
- 若 `mappingBodyParameterList` 为空 → 用 `inParameterList` 填充(老数据零感知)
- 新保存的 FormUrlEncoded 接口:映射树写 `mappingBodyParameterList`,`inParameterList` 可留空或仅存「真实 HTTP JSON Body 结构」(Json 类型仍用 in)
#### `POST Interface/SaveInterface`
```json
{
"mappingBodyParameterList": [{ "optionType": 1, "id": 1, "pId": 0, "name": "order", "alias": "order", "type": 3 }],
"inParameterList": [],
"formParameterList": [
{ "name": "notifyBizEventDTO", "value": "$FromMappingBody", "optionType": 0 }
]
}
```
`inParameterList` / `outParameterList` / `formParameterList` 字段 **保留**,老前端、导入导出可继续用。
#### 其它接口
| 接口 | 变更 |
|------|------|
| `Dictionary/GetList?type=ParameterPlaceholder` | 后端枚举加 `FromMappingBody` → 展示 `$FromMappingBody` |
| `InterfaceMap/*`(映射编辑) | 下游入参映射 SQL/列表 **优先** `ParaKind=MappingBody`,无则回退 `IsInPara` |
| `FixedParameter/*` | 无 |
| `FormParameter/GetParameterList` | 无 |
### 3.4 后端逻辑改动点
| 模块 | 改动量 | 内容 |
|------|--------|------|
| `ParameterService` / `InterfaceService` | 小 | 保存/读取时按 `ParaKind` 过滤;维护 MappingBody 列表 |
| `InterfaceForwardQuery.GetParameterMapListAsync` | 小 | JOIN `t_parameter` 增加 `ParaKind=MappingBody` 条件(入参映射) |
| `InterfaceForwardQuery` 构建 `InParamTreeList` | 小 | 有 MappingBody 用其建树,否则用原 `IsInPara` |
| `DefaultForwardFlow.ReplacePlaceholder` | 小 | `FixedFieldList` / `FormFieldList` 增加 `$FromMappingBody` → `Serialize(OriginalInterfaceMappedInput)` |
| `DefaultForwardFlow.SetContentType` | 小 | `FormUrlEncoded` 里对 `$FromMappingBody` 与 `$FromBody` 同样替换为序列化结果(可共用实现) |
| `DefaultForwardFlow.RequestAsync` | **无或极小** | 不拆 Query/Header 表单 |
| `ServiceProviderImportExportService` | 小 | 导出/导入带上 `ParaKind` |
| 发布缓存 `ForwardCoreContextCache` | 小 | 缓存参数列表带 `ParaKind` |
**`$FromBody` vs `$FromMappingBody`(小改语义建议)**
| 占位符 | 小改建议语义 |
|--------|----------------|
| `$FromBody` | **保持现有行为**(兼容老配置) |
| `$FromMappingBody` | **明确**指向 `OriginalInterfaceMappedInput` 的序列化(映射完成后、写进 form 字段) |
### 3.5 前端改动点
| 文件/模块 | 改动量 |
|-----------|--------|
| `InterfaceDetail` | 中:Request 区增加 **Mapping Body** Tab(复用现有 Body 树表格);原 **Body** Tab 可改名为「传输 Body」或保留树仅给 Json 类型显示 |
| `ServiceProviderInterfaceModal.saveInterface` | 小:多组装 `mappingBodyParameterList`;`formParameterList` 仍只来自 Query 表 |
| `curlToInterfaceFill` | 小:JSON 类 form 字段推断 `$FromMappingBody` |
| `upstreamInterfaceConfig` 映射页 | 小:下游参数源改读 mapping 列表(后端改即可) |
| `itfxEnums` / 文档 | 小 |
| `ApiTest` | 无 |
### 3.6 方案 A 优点 / 缺点
**优点**
- 改动面可控,**只增加概念**,不推翻 Form/Fixed 分工
- 老接口、老映射、老占位符可继续跑
- 符合「多的映射 body 只解决表单里还有 json」的诉求
**缺点**
- UI 仍与 ApiTest **不完全一致**(Query 名不副实、Header 仍在固定配置)
- 技术债仍在:`formParameterList` ≠ URL Query
---
## 4. 方案 B:大改(完全对齐 API 调试)
### 4.1 目标结构(与 ApiTest 一致)
```text
┌─ 基本信息(地址、Method、ContentType…)────────────────┐
├─ Query KV 表 → 真实 URL Query │
├─ Body KV 表 → Form Body / 或 Raw(按 ContentType) │
├─ Header KV 表 → 请求头 │
├─ Mapping Body 参数树 → 仅上游映射主体($FromMappingBody) │
├─ Response 参数树 + 返回配置(保持) │
└─ cURL 示例(可选) ┘
```
传输层(Query/Body/Header)与映射层(Mapping Body)**彻底分离**。
### 4.2 表结构
| 表 | 变更 | 说明 |
|----|------|------|
| `t_parameter_form` | 新增 **`FieldPosition`**(int,enum:Header=1, Body=2, Query=3) | 一条 form 记录明确归属;历史数据默认 `Body=2` |
| `t_parameter` | 新增 **`ParaKind`**(Normal / MappingBody / 可选 TransportBody) | MappingBody 只用于映射;Json 接口的 HTTP Body 树可用 Normal 或单独 Kind |
| `t_parameter_fixed` | **可选废弃或降级** | Header/Query 迁入 form 后,Fixed 仅保留服务商级 / Url 路径 / 脚本写入项;需迁移工具 |
| `t_interface` | 一般不改 | — |
**数据迁移**
- 旧 `formParameterList` → `FieldPosition=Body`
- 旧 `inParameterList`(Form 场景)→ 拆到 `MappingBody` + 清空 in 或保留 Json 专用
- 旧 `FixedParameter`(Header/Query)→ 迁入 `t_parameter_form` 或双写过渡期
### 4.3 接口契约(重组)
#### `GetInterfaceById`(示例)
```json
{
"queryFormParameterList": [],
"bodyFormParameterList": [],
"headerFormParameterList": [],
"mappingBodyParameterList": [],
"inParameterList": [],
"outParameterList": [],
"formParameterList": []
}
```
`formParameterList` 兼容期 = 三表合并;**新前端只认分列表**。
#### `SaveInterface`
- 必传:`queryFormParameterList` + `bodyFormParameterList` + `headerFormParameterList` + `mappingBodyParameterList` + `outParameterList`
- `inParameterList`:仅 **ContentType=Json** 时作为 HTTP JSON Schema(或合并进 mapping,产品需二选一)
#### 其它
| 能力 | 大改影响 |
|------|----------|
| `FixedParameter/MaintainParameter` | 接口明细页可能 **移除** 或仅服务商维度保留 |
| 授权 `ServiceProviderAuth` | 入参列表来源可能改为 MappingBody |
| 接口映射发布/对比 | 参数树 diff 逻辑调整 |
| 导入导出 | 全量适配 position + ParaKind |
| 前置脚本 Skill | `FormFieldList` 分 position;文档重写 |
### 4.4 后端逻辑改动点
| 模块 | 改动量 | 内容 |
|------|--------|------|
| `FormParameterService` | **大** | CRUD 带 `FieldPosition`;按 position 查询 |
| `InterfaceService` Save/Get | **大** | 多列表合并/拆分 |
| `DefaultForwardFlow.RequestAsync` | **大** | `FormFieldList` 按 Query → `UriBuilder.Query`;Header → `msg.Headers`;Body → `SetContentType` |
| `DefaultForwardFlow` 与 Fixed 合并 | **大** | 同名冲突策略、服务商级覆盖规则 |
| `InterfaceForwardQuery` | **大** | 映射、授权、缓存多处 |
| `InterfaceMapRepository` SQL | **中** | 下游参数过滤 MappingBody |
| `ServiceProviderImportExportService` | **大** | |
| 单元/集成测试 | **大** | 需覆盖 Json / FormUrlEncoded / 混合 Header |
### 4.5 前端改动点
| 模块 | 改动量 |
|------|--------|
| `InterfaceDetail` | **很大**:Request 区 Tabs 与 ApiTest 同构;删除或隐藏「固定配置」;三份 KV 表 + Mapping Body 树 |
| `ServiceProviderInterfaceModal` | **大**:保存/加载拆 4 类参数 |
| `ApiTest` | **中**:可抽公共组件 `RequestTabs` 复用 |
| `curlToInterfaceFill` | **大**:-H→header,?query→query,--data→body form 或 mapping |
| `downstreamInterface/index` | **中** |
| `upstreamInterface` | **小~中**:上游是否展示 Mapping Body |
| `newPort.vue` 等历史页 | **中**:废弃或同步 |
### 4.6 方案 B 优点 / 缺点
**优点**
- 产品、开发、运维认知与 **API 调试** 一致,培训成本低
- Query/Header 行为正确,不再名不副实
- 映射与传输分层清晰,后续扩展 FormData、Raw Body 更自然
**缺点**
- **改动面大**:表 + 转发 + 映射 + 导入导出 + 多页面
- 必须做 **数据迁移与发布回归**,线上 Form 接口需逐个验证
- 与「只为表单 JSON 加一个映射体」相比,**投入不成比例**(若 80% 接口是 Json Body)
---
## 5. 改动量对照(文件级清单)
### 5.1 后端 `itfx-master`(约)
| 路径/模块 | 方案 A | 方案 B |
|-----------|--------|--------|
| `ParameterEntity` + 迁移 SQL | ● | ●● |
| `FormParameterEntity` + 迁移 | — | ●● |
| `SaveInterfaceInput` / `InterfaceOutput` | ● | ●● |
| `InterfaceService` | ● | ●● |
| `ParameterService` | ● | ●● |
| `FormParameterService` | — | ●● |
| `DefaultForwardFlow` | ● | ●●● |
| `InterfaceForwardQuery` | ● | ●● |
| `InterfaceMapRepository` / `InterfaceMapService` | ● | ●● |
| `ServiceProviderImportExportService` | ● | ●● |
| `GlobalConst` / `ParameterPlaceholder` | ● | ● |
| `ServiceProviderAuthService` | ○ | ● |
| 测试项目 | ● | ●●● |
图例:— 无 ○ 可选 ● 必改 ●● 重度 ●●● 重构
### 5.2 前端 `oms0414`(约)
| 路径/模块 | 方案 A | 方案 B |
|-----------|--------|--------|
| `InterfaceDetail/index.vue` | ●● | ●●● |
| `ServiceProviderInterfaceModal.vue` | ● | ●● |
| `curlToInterfaceFill.ts` | ● | ●● |
| `constants/itfxEnums.ts` + `docs/itfx枚举说明.md` | ● | ●● |
| `api/InterfaceServices/typings.d.ts` | ● | ●● |
| 上游映射配置页 | ● | ●● |
| `ApiTest/index.vue`(抽组件) | — | ●● |
| `downstreamInterface/newPort.vue` | ○ | ●● |
---
## 6. 行为对比(典型:1688 / FormUrlEncoded)
| 步骤 | 现状 | 方案 A | 方案 B |
|------|------|--------|--------|
| 定义 JSON 订单结构 | 写在 Body 参数树 | 写在 **Mapping Body** 树 | 同 A |
| 表单字段 `notifyBizEventDTO` | `$FromBody` | `$FromMappingBody` | `$FromMappingBody`(在 **Body 表单** Tab) |
| URL `?a=1` | Fixed Query 或没有 | 仍 Fixed / 没有 | **Query 表单** Tab |
| Header `sign` | 固定配置 + 脚本 | 同左 | **Header 表单** Tab |
| 上游映射配置 | 对下游 in 树 alias | 对 **MappingBody** alias | 同 A |
---
## 7. 选型建议
```text
表单 JSON 映射问题
│
┌───────────────┴───────────────┐
│ │
仅 FormUrlEncoded 等少数接口 希望全站 UI/行为
需要拆映射体 与 ApiTest 完全一致
│ │
▼ ▼
【方案 A 小改】 【方案 B 大改】
优先落地、可迭代 单独立项、版本窗口
```
| 若你的情况是… | 建议 |
|--------------|------|
| 主要痛点是「form 里 JSON 字段 + 上游映射对不上」 | **方案 A** |
| 同时要强改 Query/Header 配置方式、去掉固定配置双轨 | **方案 B**(或 A 上线后再做 B 的第二阶段) |
| 资源紧、要先上线 | **A**;B 里「form 分 position」可单独立项 |
**推荐路径**:**先做方案 A** → 验证 `$FromMappingBody` + 映射发布 → 若仍纠结 Query/Header 体验,再开 **方案 B 第二阶段**(只迁 `t_parameter_form.FieldPosition` + `RequestAsync`,Mapping Body 不再动)。
---
## 8. 方案 A 实施 checklist(可直接拆任务)
- [ ] DB:`t_parameter.ParaKind` + 默认值
- [ ] 实体/DTO/枚举:`ParameterPlaceholder.FromMappingBody`
- [ ] `GetInterfaceById` / `SaveInterface` 增加 `mappingBodyParameterList` + 兼容
- [ ] `InParamTreeList` / 映射 SQL 使用 MappingBody
- [ ] `DefaultForwardFlow` 占位符
- [ ] 导入导出
- [ ] 前端 Mapping Body Tab + 保存字段
- [ ] curl 智能填充推断 `$FromMappingBody`
- [ ] 文档 `itfx枚举说明.md` 更新
- [ ] 选 1~2 个 FormUrlEncoded 生产接口回归 + 发布
---
## 9. 方案 B 实施 checklist(摘要)
- [ ] 产品定稿:Json 接口 in 树 vs MappingBody 分工;Fixed 是否保留
- [ ] DB 迁移脚本 + 回滚脚本
- [ ] Form 分 position 全链路
- [ ] `RequestAsync` 三分支组装
- [ ] 接口契约 v2(可 `/Interface/SaveInterfaceV2` 并行一期)
- [ ] 前端 InterfaceDetail 重写 + 组件抽取
- [ ] 映射/授权/导出/发布全量回归
- [ ] 灰度:按服务商或接口标签切换
---
## 10. 附录:与 API 调试(ApiTest)结构对齐度
| 能力 | ApiTest | 现状 | 方案 A | 方案 B |
|------|---------|------|--------|--------|
| Query KV | ✅ | ⚠️ 名是 Query 实为 form | ⚠️ | ✅ |
| Body KV / Raw | ✅ | ⚠️ 树在 Body | ⚠️ form 仍在 Query 表 | ✅ |
| Header KV | ✅ | ⚠️ 在固定配置 | ⚠️ | ✅ |
| 映射 JSON 结构 | — | ⚠️ 混在 in 树 | ✅ Mapping Body | ✅ |
| `$FromMappingBody` | — | ❌ | ✅ | ✅ |
---
*文档版本:2026-06-02 · 基于 `itfx-master` + `oms0414` 下游接口/ApiTest 代码梳理*