373 lines
17 KiB
Markdown
373 lines
17 KiB
Markdown
# 下游接口明细改造方案:小改 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 代码梳理*
|