front/docs/方案B-下游接口对齐ApiTest-详细设计.md
2026-06-04 19:15:28 +08:00

32 KiB
Raw Blame History

方案 B 详细设计:下游接口明细对齐 API 调试(ApiTest)

决策:采用方案 B(大改),传输层(Query / Body / Header)与映射层(Mapping Body)分离,页面与数据模型对齐 /api/apiDebugger。
范围:itfx-master 后端 + oms0414 前端 + 数据迁移 + 映射/发布/导入导出/授权联动。
关联文档:下游接口明细改造方案-小改vs大改.md


目录

  1. 目标与原则
  2. 概念模型前后对比
  3. 表结构前后对比
  4. 枚举与常量前后对比
  5. 接口契约前后对比
  6. 转发与映射逻辑前后对比
  7. 前端页面与数据流前后对比
  8. 关联能力改动清单
  9. 数据迁移方案
  10. 兼容策略与版本节奏
  11. 实施阶段建议

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 定稿详细设计