方案 B 详细设计:下游接口明细对齐 API 调试(ApiTest)
决策:采用方案 B(大改),传输层(Query / Body / Header)与映射层(Mapping Body)分离,页面与数据模型对齐 /api/apiDebugger。
范围:itfx-master 后端 + oms0414 前端 + 数据迁移 + 映射/发布/导入导出/授权联动。
关联文档:下游接口明细改造方案-小改vs大改.md
目录
- 目标与原则
- 概念模型前后对比
- 表结构前后对比
- 枚举与常量前后对比
- 接口契约前后对比
- 转发与映射逻辑前后对比
- 前端页面与数据流前后对比
- 关联能力改动清单
- 数据迁移方案
- 兼容策略与版本节奏
- 实施阶段建议
1. 目标与原则
1.1 目标
| # |
目标 |
| G1 |
下游接口明细 Request 区与 ApiTest 一致:Query / Body / Header 为 KV 表,Mapping Body 为参数树 |
| G2 |
解决 Form 字段值为 JSON 时,上游映射应对 JSON 内部结构(Mapping Body),表单字段用 $FromMappingBody 引用 |
| G3 |
运行时 按 FieldPosition 把表单写入 URL Query、Header、Form Body |
| G4 |
接口映射、发布、导入导出与新区分方式一致 |
1.2 设计原则
- 传输层(真实 HTTP):
t_parameter_form + FieldPosition(Query / Body / Header)
- 映射层(上游 → 下游):
t_parameter + ParaKind = MappingBody
- Json 下游接口:Mapping Body 树 同时 作为映射目标与
SerializationBody 的数据源(无 Form Body KV 或 Body KV 为空)
- 接口级 FixedParameter:逐步收敛——Header/Query 迁入 Form;Fixed 保留 服务商级、Url 路径占位、脚本动态写入 场景
- 兼容一期:
GetInterfaceById 仍返回 formParameterList / inParameterList(聚合旧字段),SaveInterface 同时接受新旧字段,服务端以新字段为准
2. 概念模型前后对比
2.1 改造前(现状)
┌─────────────────────────────────────────────────────────────┐
│ 下游接口明细 (InterfaceDetail) │
├─────────────────────────────────────────────────────────────┤
│ Query Tab → formParameterList (t_parameter_form) │
│ ⚠️ 后端全部当作 FormUrlEncoded Body,非 URL Query │
├─────────────────────────────────────────────────────────────┤
│ Body Tab → inParameterList (t_parameter, IsInPara) │
│ ⚠️ 同时承担:Json HTTP Body + 上游映射目标 │
├─────────────────────────────────────────────────────────────┤
│ 固定配置 Tab → FixedParameter 独立 API (t_parameter_fixed) │
│ Header / Query / Body / Url │
├─────────────────────────────────────────────────────────────┤
│ cURL Tab → inParams 字符串示例 │
├─────────────────────────────────────────────────────────────┤
│ Response Tab → outParameterList │
└─────────────────────────────────────────────────────────────┘
2.2 改造后(方案 B)
┌─────────────────────────────────────────────────────────────┐
│ 下游接口明细 (对齐 ApiTest) │
├─────────────────────────────────────────────────────────────┤
│ Query Tab → queryFormParameterList │
│ FieldPosition = Query → 运行时 URL Query │
├─────────────────────────────────────────────────────────────┤
│ Body Tab → bodyFormParameterList │
│ FieldPosition = Body → FormUrlEncoded 等 │
│ 值可为 $FromMappingBody / $TimeStamp / 留空 │
├─────────────────────────────────────────────────────────────┤
│ Header Tab → headerFormParameterList │
│ FieldPosition = Header → 请求头 │
├─────────────────────────────────────────────────────────────┤
│ Mapping Body Tab → mappingBodyParameterList │
│ ParaKind = MappingBody → 仅上游映射 + 序列化源 │
├─────────────────────────────────────────────────────────────┤
│ cURL Tab → inParams(不变) │
├─────────────────────────────────────────────────────────────┤
│ Response Tab → outParameterList(不变) │
└─────────────────────────────────────────────────────────────┘
(可选)服务商级 / Url 固定项 → 仍在 t_parameter_fixed,接口明细页「固定配置」可隐藏或仅高级入口
2.3 职责对照表
| 概念 |
改造前 |
改造后 |
| URL Query 键值 |
主要靠 FixedFieldList(Query)或没有 |
bodyForm 之外:queryFormParameterList |
| 请求头 |
FixedFieldList(Header) |
headerFormParameterList + 服务商级 Fixed |
| Form 业务字段 |
全部 formParameterList → 进 Form Body |
bodyFormParameterList,按 ContentType 组装 |
| 上游映射目标(下游侧) |
inParameterList(IsInPara) |
mappingBodyParameterList(ParaKind=MappingBody) |
| Json HTTP Body |
inParameterList 序列化 |
mappingBodyParameterList 序列化(Json 接口) |
| 表单里塞整段 JSON |
$FromBody(语义混) |
$FromMappingBody(明确指向映射结果) |
3. 表结构前后对比
3.1 t_parameter(参数树)
改造前
| 列 |
类型 |
说明 |
| Id |
int PK |
|
| InterfaceId |
int |
|
| PId |
int |
父节点 |
| Name / Alias / CnName |
string |
|
| Type |
int |
ParameterType |
| IsRequired |
bool |
|
| IsInPara |
bool |
true=入参,false=出参 |
| Description / Sort |
|
|
| 审计字段 |
|
|
语义:IsInPara=true 的记录 = 入参树 = 映射目标 +(Json 时)HTTP Body 结构。
改造后
| 列 |
类型 |
说明 |
变更 |
| (原有列) |
|
|
保留 |
| ParaKind |
int NOT NULL DEFAULT 0 |
见下表 |
新增 |
ParaKind 枚举(建议)
| 值 |
名称 |
说明 |
| 0 |
Normal |
默认;出参仅 Normal;上游接口入参仍为 Normal + IsInPara |
| 1 |
MappingBody |
仅下游:映射体树;IsInPara 仍为 true |
| 2 |
TransportBody |
可选,若 Json 接口要把「HTTP Body 结构」与「映射体」拆成两棵树时使用;一期可不用 |
约束(逻辑)
IF ParaKind = MappingBody → 必须 IsInPara = true,且仅用于下游接口 (IsUpStream=false)
IF IsInPara = false → ParaKind 必须为 Normal(出参)
DDL 示例
ALTER TABLE t_parameter
ADD ParaKind INT NOT NULL DEFAULT 0 COMMENT '0=Normal,1=MappingBody,2=TransportBody(预留)';
CREATE INDEX IX_t_parameter_InterfaceId_ParaKind
ON t_parameter (InterfaceId, ParaKind, IsInPara)
WHERE IsDeleted = 0;
3.2 t_parameter_form(表单 KV)
改造前
| 列 |
说明 |
| Id, InterfaceId |
|
| Name, Value, Description |
Value 为占位符或常量 |
| 审计字段 |
|
| 无位置字段 |
全部视为 FormUrlEncoded 的 Body 字典 |
改造后
| 列 |
说明 |
变更 |
| (原有列) |
|
保留 |
| FieldPosition |
int NOT NULL DEFAULT 2 |
新增;对齐 FieldPosition 枚举 |
FieldPosition 使用范围(form 表)
| 值 |
枚举 |
用于 form 表 |
| 1 |
Header |
✅ |
| 2 |
Body |
✅(历史数据默认值) |
| 3 |
Query |
✅ |
| 4 |
Url |
❌ 仍用 Fixed(路径替换) |
| 5 |
Nothing |
❌ |
DDL 示例
ALTER TABLE t_parameter_form
ADD FieldPosition INT NOT NULL DEFAULT 2 COMMENT '1=Header,2=Body,3=Query';
CREATE INDEX IX_t_parameter_form_InterfaceId_Position
ON t_parameter_form (InterfaceId, FieldPosition)
WHERE IsDeleted = 0;
3.3 t_parameter_fixed(固定参数)
改造前
| 列 |
说明 |
| ServiceProviderId / InterfaceId |
二选一或兼有 |
| FieldName, FieldValue |
可含 $FromBody 等 |
| FieldPositions |
逗号分隔多位置:Header,Body,Query,Url |
接口明细页通过独立 API FixedParameter/MaintainParameter 维护。
改造后(策略)
| 场景 |
改造后归属 |
说明 |
| 接口级 Header / Query 固定 KV |
迁入 t_parameter_form |
与 ApiTest 一致,随 SaveInterface 保存 |
| 接口级 Body 固定路径绑定 |
可迁入 form(Body)或保留 Fixed(Body) |
若值为 $FromMappingBody 路径片段,优先 Fixed Body 逻辑保留 |
| 服务商级 通用 Header/签名 |
保留 t_parameter_fixed |
ServiceProviderId 有值 |
Url 路径 {id} 替换 |
保留 Fixed(Url) |
form 表不支持 Url |
脚本运行时 AddHeader |
保留 Fixed 动态能力 |
ForwardCoreContext.AddHeader |
一期可不删表、不删 API,仅接口明细 UI 隐藏「固定配置」;数据迁移把 InterfaceId IS NOT NULL 且 FieldPositions 含 Header/Query 的行复制到 form 表。
3.4 t_interface / 其它表
| 表 |
变更 |
t_interface |
无结构变更(ContentType、MappedParamType 仍控制序列化) |
t_interface_map / t_interface_map_detail |
无结构变更;JOIN 参数时增加 ParaKind 条件 |
t_interface_return_config |
无变更 |
3.5 ER 关系(改造后)
erDiagram
t_interface ||--o{ t_parameter : "出参 Normal"
t_interface ||--o{ t_parameter : "MappingBody IsInPara"
t_interface ||--o{ t_parameter_form : "Query Body Header"
t_interface ||--o{ t_parameter_fixed : "服务商级 Url"
t_service_provider ||--o{ t_parameter_fixed : "服务商级"
t_parameter {
int ParaKind "0 Normal 1 MappingBody"
bool IsInPara
}
t_parameter_form {
int FieldPosition "1 Header 2 Body 3 Query"
}
4. 枚举与常量前后对比
4.1 新增 / 扩展
| 类型 |
改造前 |
改造后 |
| ParameterPlaceholder |
FromAccount, FromBody, TimeStamp, DateTime, Url, Token |
+ FromMappingBody → "$FromMappingBody" |
| GlobalConst |
FromBody 等 |
+ FromMappingBody |
| ParaKind(新枚举) |
无 |
Normal=0, MappingBody=1, TransportBody=2(可选) |
| FormParameterItem |
id,name,value,description |
+ fieldPosition |
| InterfaceFormFieldDto(运行时) |
Name, Value, Description |
+ FieldPosition |
4.2 Dictionary API
GET api/Dictionary/GetList?type=ParameterPlaceholder
改造后 返回增加一项:
{ "key": 7, "value": "$FromMappingBody" }
(具体 key 值以实现为准,前后端 itfxEnums.ts 同步。)
4.3 占位符语义(改造后定稿)
| 占位符 |
作用域 |
替换时机 |
替换结果 |
$FromMappingBody |
Form(Query/Body/Header) |
ReplacePlaceholder / SetContentType |
OriginalInterfaceMappedInput 按 ContentType 序列化后的字符串(整段) |
$FromBody |
Form / Fixed |
兼容保留 |
Form:同现行(整段 TargetInterfaceInput 或映射体,与现网一致);Fixed:从 OriginalInterfaceMappedInput 按 FieldName 路径取值 |
$FromAccount |
Form / Fixed |
ReplacePlaceholder |
账户字段 |
$TimeStamp / $DateTime |
Form / Fixed |
ReplacePlaceholder |
时间 |
$Url |
Form / Fixed |
ReplacePlaceholder |
请求地址 |
$token |
Fixed 为主 |
ReplacePlaceholder |
授权 token |
推荐:新配置 JSON 业务字段统一用 $FromMappingBody;$FromBody 仅兼容老数据。
5. 接口契约前后对比
5.1 GET Interface/GetInterfaceById
改造前响应(节选)
{
"id": 1001,
"name": "1688回调",
"contentType": 1,
"mappedParamType": 3,
"inParameterList": [ { "id": 1, "pId": 0, "name": "order", "alias": "order", "type": 3 } ],
"outParameterList": [ ],
"formParameterList": [
{ "id": 10, "name": "notifyBizEventDTO", "value": "$FromBody", "description": "..." }
]
}
改造后响应(完整约定)
{
"id": 1001,
"name": "1688回调",
"contentType": 1,
"mappedParamType": 3,
"queryFormParameterList": [
{ "id": 11, "name": "_aop_timestamp", "value": "$TimeStamp", "description": "ms", "fieldPosition": 3 }
],
"bodyFormParameterList": [
{ "id": 10, "name": "notifyBizEventDTO", "value": "$FromMappingBody", "description": "", "fieldPosition": 2 }
],
"headerFormParameterList": [
{ "id": 12, "name": "Content-Type", "value": "application/x-www-form-urlencoded", "fieldPosition": 1 }
],
"mappingBodyParameterList": [
{ "id": 1, "pId": 0, "name": "order", "alias": "order", "type": 3, "isRequired": true, "paraKind": 1 }
],
"outParameterList": [ ],
"formParameterList": [ ],
"inParameterList": [ ]
}
兼容字段(只读聚合,便于老客户端)
| 字段 |
聚合规则 |
formParameterList |
query + body + header 三个列表合并 |
inParameterList |
若 mappingBodyParameterList 非空 → 等于其副本;否则沿用 DB 中 ParaKind=Normal & IsInPara(迁移期) |
5.2 POST Interface/SaveInterface
改造前请求(节选)
{
"optionType": 3,
"id": 1001,
"isUpStream": false,
"serviceProviderId": 5,
"name": "1688回调",
"contentType": 1,
"inParameterList": [ { "optionType": 3, "id": 1, "pId": 0, "name": "order", "alias": "order", "type": 3 } ],
"outParameterList": [ ],
"formParameterList": [ { "optionType": 0, "id": 10, "name": "notifyBizEventDTO", "value": "$FromBody" } ],
"inParams": "curl ..."
}
改造后请求(标准)
{
"optionType": 3,
"id": 1001,
"isUpStream": false,
"serviceProviderId": 5,
"name": "1688回调",
"contentType": 1,
"mappedParamType": 3,
"queryFormParameterList": [
{ "optionType": 0, "id": 11, "name": "_aop_timestamp", "value": "$TimeStamp", "description": "ms", "fieldPosition": 3 }
],
"bodyFormParameterList": [
{ "optionType": 0, "id": 10, "name": "notifyBizEventDTO", "value": "$FromMappingBody", "fieldPosition": 2 }
],
"headerFormParameterList": [
{ "optionType": 0, "id": 12, "name": "X-Custom", "value": "fixed", "fieldPosition": 1 }
],
"mappingBodyParameterList": [
{ "optionType": 3, "id": 1, "pId": 0, "name": "order", "alias": "order", "type": 3, "isRequired": true }
],
"outParameterList": [ ],
"inParams": "curl ..."
}
FormParameterInput 扩展
public class FormParameterInput : FormParameterItem
{
public OptionType OptionType { get; set; }
public FieldPosition FieldPosition { get; set; } // 新增;也可由所在列表推断
}
ParameterInput 扩展
// ParameterInput 已有字段基础上
public ParaKind ParaKind { get; set; } // 映射体列表传入 MappingBody;出参传 Normal
保存时服务端合并规则(改造后)
1. mappingBodyParameterList → 写入 t_parameter (IsInPara=true, ParaKind=MappingBody)
2. outParameterList → 写入 t_parameter (IsInPara=false, ParaKind=Normal)
3. query/body/header 三列表 → 合并写入 t_parameter_form(带 FieldPosition)
4. 若仍收到 formParameterList(旧前端)→ 全部 FieldPosition=Body,且不与 body 列表重复 id
5. 若仍收到 inParameterList(旧前端)→ 当作 mappingBodyParameterList(迁移期)
6. Json 接口且 mappingBody 为空但 in 有值 → 同 5,并打日志提示升级
5.3 GET FormParameter/GetParameterList
改造前
GET FormParameter/GetParameterList?interfaceId=1001
→ List<FormParameterItem> // 无 position
改造后(二选一)
选项 1(推荐):扩展原接口
GET FormParameter/GetParameterList?interfaceId=1001&fieldPosition=3
→ 按 position 过滤;不传则返回全部(带 fieldPosition 字段)
选项 2:废弃,统一走 GetInterfaceById。
5.4 POST FixedParameter/MaintainParameter
| 维度 |
改造前 |
改造后 |
| 接口明细页 |
主要维护入口 |
隐藏;Header/Query 改走 SaveInterface |
| 服务商管理 / 高级配置 |
使用 |
保留 |
| 行为 |
写 t_parameter_fixed |
不变;转发时与 form 合并(见 §6.3) |
5.5 映射相关接口
GET InterfaceMap/EditMapDetails / GetMapParameterCascadedList
| 维度 |
改造前 |
改造后 |
| 下游入参映射目标 |
t_parameter 且 IsInPara=true(全部入参) |
ParaKind = MappingBody(无则回退全部 IsInPara,迁移期) |
| 上游系统接口入参 |
IsInPara=true |
不变(上游无 Mapping Body 概念) |
POST InterfaceMap/SaveParameterMaps(保存映射明细)
- 映射明细仍引用
t_parameter.Id;Mapping Body 节点 id 为新 id 空间,与旧 in 参 id 可能变化,迁移需重导映射或跑 id 映射脚本。
5.6 上游接口 SaveInterface(IsUpStream=true)
| 字段 |
改造后 |
mappingBodyParameterList |
不使用(或忽略) |
inParameterList |
仍为上游业务入参树(Normal) |
query/body/headerForm |
可选;一般上游为空 |
| UI |
上游 InterfaceDetail 不展示 Mapping Body Tab(或展示但禁用) |
5.7 TypeScript 类型(前端 typings.d.ts 目标)
type FormParameterItem = {
id?: number;
name?: string;
value?: string;
description?: string;
fieldPosition?: 1 | 2 | 3; // Header Body Query
};
type InterfaceParameterOutputValueObject = {
// ...existing
paraKind?: 0 | 1 | 2;
};
type InterfaceOutput = {
// ...existing
queryFormParameterList?: FormParameterItem[];
bodyFormParameterList?: FormParameterItem[];
headerFormParameterList?: FormParameterItem[];
mappingBodyParameterList?: InterfaceParameterOutputValueObject[];
/** @deprecated 聚合字段,勿写入 Save */
formParameterList?: FormParameterItem[];
/** @deprecated 聚合字段 */
inParameterList?: InterfaceParameterOutputValueObject[];
};
type SaveInterfaceInput = {
// ...existing
queryFormParameterList?: FormParameterInput[];
bodyFormParameterList?: FormParameterInput[];
headerFormParameterList?: FormParameterInput[];
mappingBodyParameterList?: ParameterInput[];
};
6. 转发与映射逻辑前后对比
6.1 总流程
改造前
OriginalInterfaceInput
→ InParamRestructure(InParamTreeList from 下游全部 IsInPara)
→ OriginalInterfaceMappedInput
→ ReplacePlaceholder(Fixed + Form)
→ SerializationBody (按 ContentType)
→ RequestAsync
Fixed → Url / Query / Header
Form → 仅 FormUrlEncoded 整包 Body
改造后
OriginalInterfaceInput
→ InParamRestructure(MappingBodyTreeList) ← 仅 ParaKind=MappingBody
→ OriginalInterfaceMappedInput
→ ReplacePlaceholder(Fixed + Form 分位置)
→ SerializationBody
Json/Xml: 序列化 OriginalInterfaceMappedInput
FormUrlEncoded: 见 6.2
→ RequestAsync
Fixed → Url(保留)
Form(Query) → UriBuilder.Query
Form(Header) → msg.Headers
Form(Body) + Fixed(Body) → SetContentType
6.2 SetContentType(FormUrlEncoded)前后对比
改造前
// 所有 FormFieldList 打成一个字典作为 Body
if (context.FormFieldList.Count != 0) {
var field = context.FormFieldList.FirstOrDefault(x => x.Value == GlobalConst.FromBody);
if (field != null) field.Value = body; // body = TargetInterfaceInput
nameValueCollection = context.FormFieldList.ToDictionary(x => x.Name, x => x.Value);
}
改造后
var bodyForms = context.FormFieldList.Where(x => x.FieldPosition == FieldPosition.Body).ToList();
// 替换 $FromMappingBody / $FromBody
foreach (var item in bodyForms) {
if (item.Value == GlobalConst.FromMappingBody)
item.Value = JsonConvert.SerializeObject(context.OriginalInterfaceMappedInput);
// ... 其它占位符
}
msg.Content = new FormUrlEncodedContent(bodyForms.ToDictionary(x => x.Name, x => x.Value));
// Query / Header 已在 RequestAsync 前文处理,不进入此字典
6.3 RequestAsync 前后对比
| 步骤 |
改造前 |
改造后 |
| Url 替换 |
FixedFieldList Url |
不变 |
| Query |
FixedFieldList Query |
Form Query + Fixed Query(合并,见下) |
| Header |
FixedFieldList Header |
Form Header + Fixed Header |
| Body |
SetContentType |
仅 Form Body + Fixed Body 注入映射路径 |
合并优先级(建议写死到代码与文档)
同一 FieldName + 同一位置:
接口级 Form > 接口级 Fixed > 服务商级 Fixed
6.4 ReplacePlaceholder 前后对比
| 处理对象 |
改造前 |
改造后 |
FixedFieldList |
$FromBody 路径取值 |
保留;可增加 $FromMappingBody 整段序列化 |
FormFieldList |
仅 TimeStamp/DateTime/Url/FromAccount |
+ $FromMappingBody;Query/Header 同样支持 TimeStamp 等 |
Form 中 $FromBody |
在 SetContentType 用 TargetInterfaceInput 替换 |
建议标记过时,逻辑保留 |
6.5 InterfaceForwardQuery 构建上下文
改造前
FormFieldList = await GetFormFieldListAsync(interfaceId); // 全部 form,无 position
InParamTreeList = JsonHelper.BuildTreeList(items); // 全部 IsInPara 映射项
改造后
FormFieldList = await GetFormFieldListAsync(interfaceId); // 含 FieldPosition
InParamTreeList = JsonHelper.BuildTreeList(
items.Where(x => x.ParaKind == ParaKind.MappingBody)); // 映射配置来自 MappingBody
授权接口 GetFormFieldListAsync / InParamTreeList:授权若依赖表单,按授权接口自身配置;InParamTreeList = null 保持不变。
6.6 InParamRestructure 数据源
| 维度 |
改造前 |
改造后 |
| 树来源 |
下游映射 SQL 拉取的 全部 IsInPara |
仅 MappingBody 节点 |
| Json 接口 HTTP Body |
同一棵树序列化 |
同一棵 MappingBody 树序列化 |
| 出参映射 |
OutParamTreeList |
不变 |
6.7 ContentType 分支(产品规则)
| ContentType |
Query Tab |
Body Tab |
Mapping Body |
HTTP Body 来源 |
| Json (3) |
可选 |
通常空 |
必填(映射+序列化) |
Serialize(OriginalInterfaceMappedInput) |
| FormUrlEncoded (1) |
常用 |
常用(含 $FromMappingBody) |
必填(映射) |
Form Body 字典 |
| Xml (4) |
可选 |
可选 |
必填 |
Xml 序列化映射体 |
| FormData (2) |
二期 |
二期 |
必填 |
暂可抛不支持 |
7. 前端页面与数据流前后对比
7.1 InterfaceDetail Request 区 Tab
| Tab |
改造前 |
改造后 |
| Query |
form 表(误标) |
KV 表 → queryFormParameterList |
| Body |
参数树 inParameterList |
KV 表 → bodyFormParameterList |
| Header |
无(在固定配置) |
KV 表 → headerFormParameterList |
| Mapping Body |
无 |
参数树 → mappingBodyParameterList |
| 固定配置 |
FixedParameter API |
默认隐藏 / 高级入口 |
| cURL |
有 |
有;智能填充拆到 Q/H/B/Mapping |
7.2 ServiceProviderInterfaceModal 加载/保存
改造前
queryParams.value = interfaceData.formParameterList || [];
requestParams.value = interfaceData.inParameterList || [];
// save: formParameterList from query table only
// save: inParameterList from request tree
改造后
queryParams.value = interfaceData.queryFormParameterList || [];
bodyFormParams.value = interfaceData.bodyFormParameterList || [];
headerParams.value = interfaceData.headerFormParameterList || [];
mappingBodyParams.value = interfaceData.mappingBodyParameterList || [];
// save: 四个列表分别组装 optionType
// 不再把 query 写入 inParameterList
7.3 curlToInterfaceFill 智能填充
| cURL 部分 |
改造前 |
改造后 |
?a=1&b=2 |
进 queryParams(实为 form) |
queryFormParameterList |
-H |
部分进 Fixed / 忽略 |
headerFormParameterList |
--data-raw JSON |
解析进 inParameterList 树 |
结构进 mappingBodyParameterList;若 FormUrlEncoded 单字段则 body 表一项 $FromMappingBody |
| JSON 形态 query 值 |
$FromBody |
$FromMappingBody |
7.4 与 ApiTest 组件复用(建议)
components/
RequestTabs/
QueryKeyValueTable.vue ← 从 ApiTest 抽
HeaderKeyValueTable.vue
BodyKeyValueTable.vue
MappingBodyTreeTable.vue ← 从 InterfaceDetail 原 Body 树抽
8. 关联能力改动清单
| 模块 |
文件/服务 |
改造要点 |
| 接口保存/读取 |
InterfaceService |
拆分列表、兼容聚合 |
| 表单参数 |
FormParameterService |
FieldPosition CRUD、按位置查询 |
| 树形参数 |
ParameterService |
ParaKind、MappingBody 维护 |
| 转发 |
DefaultForwardFlow |
RequestAsync、SetContentType、ReplacePlaceholder |
| 上下文加载 |
InterfaceForwardQuery |
Form 带 position;InParamTreeList 过滤 MappingBody |
| 映射 SQL |
InterfaceMapRepository |
JOIN 参数加 ParaKind |
| 映射编辑 API |
InterfaceMapService.Details |
级联列表过滤 |
| 导入导出 |
ServiceProviderImportExportService |
form position + paraKind |
| 授权 |
ServiceProviderAuthService |
确认入参列表来源(MappingBody 或仍为原 in) |
| 发布/缓存 |
ForwardCoreContextCache |
DTO 增加 FieldPosition、ParaKind |
| 字典 |
ParameterPlaceholder 枚举 |
+FromMappingBody |
| 前端枚举 |
itfxEnums.ts |
同步 |
| 文档 |
itfx枚举说明.md |
更新 |
| 上游接口页 |
UpstreamInterfaceModal |
不展示 Mapping Body |
| 映射配置页 |
upstreamInterfaceConfig* |
下游侧展示 MappingBody 别名 |
| 历史页 |
newPort.vue |
同步或废弃 |
9. 数据迁移方案
9.1 迁移步骤(建议顺序)
Step 1 DDL:t_parameter.ParaKind、t_parameter_form.FieldPosition
Step 2 备份相关表
Step 3 form:历史行 FieldPosition = 2 (Body)
Step 4 parameter:下游接口 IsInPara=true 且 ContentType=FormUrlEncoded
→ ParaKind = MappingBody(原 in 树整体迁移)
Step 5 parameter:下游接口 ContentType=Json
→ 原 in 树 ParaKind = MappingBody(同时作为 HTTP body 定义)
Step 6 fixed:InterfaceId NOT NULL 且 FieldPositions 含 Header/Query
→ INSERT INTO t_parameter_form(去重策略按 FieldName+Position)
Step 7 验证映射明细 MappedParaId 仍指向有效 parameter.Id
Step 8 发布缓存刷新 / 全量发布后回归
9.2 迁移 SQL 示意
-- form 默认 Body
UPDATE t_parameter_form SET FieldPosition = 2 WHERE FieldPosition IS NULL OR FieldPosition = 0;
-- 下游入参 → MappingBody(需按接口表 IsUpStream=0 关联,示意)
UPDATE p SET p.ParaKind = 1
FROM t_parameter p
INNER JOIN t_interface i ON p.InterfaceId = i.Id
WHERE p.IsInPara = 1 AND p.IsDeleted = 0 AND i.IsUpStream = 0;
-- Fixed → Form(Header=1, Query=3)需脚本逐行解析 FieldPositions,不建议单条 SQL 粗暴合并
9.3 回滚
- 保留
ParaKind、FieldPosition 列,回滚代码读旧逻辑:忽略新列,按 IsInPara + 全量 form 聚合。
- 迁移前务必备份
t_parameter、t_parameter_form、t_interface_map_detail。
10. 兼容策略与版本节奏
10.1 API 版本
| 阶段 |
后端 |
前端 |
| V1 并行 |
Save 接受新旧字段;Get 返回新旧字段 |
新页面只发新字段 |
| V2 |
Save 对新字段必填(下游);旧字段 deprecated 日志 |
移除旧 Tab |
| V3 |
可选移除 formParameterList 聚合 |
— |
10.2 行为兼容矩阵
| 场景 |
未迁移数据 |
已迁移数据 |
| GetById |
inParameterList 有值,mapping 空 → 前端用 in 填 Mapping Tab |
四列表 + mapping 正常 |
| Save 旧前端 |
仅 form + in → 服务端按 §5.2 规则 4、5 拆分 |
— |
| 转发 |
ParaKind 全 0 → 等同改造前 |
新逻辑 |
11. 实施阶段建议
| 阶段 |
内容 |
产出 |
| P0 |
DDL + 枚举 + Get/Save 契约 + 单元测试 |
可存可取 |
| P1 |
DefaultForwardFlow + InterfaceForwardQuery + 占位符 |
可转发 |
| P2 |
映射 SQL + 发布缓存 + 导入导出 |
可发布 |
| P3 |
前端 InterfaceDetail 重写 + Modal 保存 + curl 填充 |
可配置 |
| P4 |
数据迁移 + 选生产接口回归 |
上线 |
| P5 |
隐藏固定配置、文档、培训 |
收尾 |
附录 A:改造前后单接口完整示例(1688 风格 FormUrlEncoded)
改造前
库内
t_parameter:order.{field} 树,IsInPara=1
t_parameter_form:notifyBizEventDTO=$FromBody,无 position
运行时
- 映射到 order 树 → 序列化 → 填入 notifyBizEventDTO → 与其它 form 字段一起 POST
改造后
库内
t_parameter:order 树,ParaKind=1(MappingBody)
t_parameter_form:(notifyBizEventDTO, $FromMappingBody, Body=2),(timestamp, $TimeStamp, Query=3)
运行时
- 映射到 MappingBody 树 →
OriginalInterfaceMappedInput → $FromMappingBody 填入 Body 表单字段 → Query 拼 URL → POST
附录 B:后端类改动速查
| 类 |
变更 |
FormParameterEntity |
+FieldPosition |
ParameterEntity |
+ParaKind |
FormParameterItem/Input |
+FieldPosition |
ParameterInput / InterfaceParameterOutputValueObject |
+ParaKind |
InterfaceOutput |
+3 form 列表 + mappingBodyParameterList |
SaveInterfaceInput |
同上 |
InterfaceFormFieldDto |
+FieldPosition |
GlobalConst |
+FromMappingBody |
ParameterPlaceholder |
+FromMappingBody |
文档版本:2026-06-02 · 方案 B 定稿详细设计