front/src/utils/interfaceParameterModel.ts
zzh b9702cd0bc
All checks were successful
Build and Deploy Front / deploy (push) Successful in 5m47s
fix: 订单详情编辑体验、接口参数与价格表批量填价
优化运单详情抽屉操作栏与申报表编辑观感,限制我的订单入库后不可编辑;完善接口参数全量替换/脏检查与全局 Message;修复价格表批量填价选中被清与弹窗被遮挡。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-10 16:27:22 +08:00

785 lines
27 KiB
TypeScript
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.

import { ContentType, FieldPosition } from '@/constants/itfxEnums';
/** 参数树节点种类:0=普通,1=Mapping Body(仅 FormUrlEncoded 映射体) */
export const ParaKind = {
Normal: 0,
MappingBody: 1,
} as const;
export type InterfaceParameterMode = 'upstream' | 'downstream';
/** 字典 / 表单可能返回 string,统一为数字再比较 */
export function normalizeContentType(contentType: unknown): number | undefined {
if (contentType === '' || contentType === undefined || contentType === null) {
return undefined;
}
const n = Number(contentType);
return Number.isNaN(n) ? undefined : n;
}
export function isNoneContentType(contentType?: unknown): boolean {
return normalizeContentType(contentType) === ContentType.None;
}
export function isFormUrlEncodedContentType(contentType?: unknown): boolean {
return normalizeContentType(contentType) === ContentType.FormUrlEncoded;
}
export function isJsonOrXmlContentType(contentType?: unknown): boolean {
const ct = normalizeContentType(contentType);
return ct === ContentType.Json || ct === ContentType.Xml;
}
/** FormUrlEncoded 与 none:需 Mapping Body Tab(Query 等可 $FromMappingBody) */
export function isMappingBodyContentType(contentType?: unknown): boolean {
return isFormUrlEncodedContentType(contentType) || isNoneContentType(contentType);
}
/** 从 GetInterfaceById 响应拆成 InterfaceDetail 各 Tab 数据 */
export function hydrateDetailParamsFromInterface(apiData: Record<string, any>) {
const contentType = apiData.contentType as number | undefined;
const isForm = isFormUrlEncodedContentType(contentType);
const isNone = isNoneContentType(contentType);
return {
queryParams: apiData.queryFormParameterList ?? apiData.formParameterList ?? [],
headerParams: apiData.headerFormParameterList ?? [],
responseParams: apiData.outParameterList ?? [],
requestParams: isForm || isNone
? []
: (apiData.requestBodyParameterList ?? apiData.inParameterList ?? []),
bodyFormParams: isForm ? (apiData.bodyFormParameterList ?? []) : [],
mappingBodyParams: isForm || isNone ? (apiData.mappingBodyParameterList ?? []) : [],
};
}
/** vxe 树表 fullData 常为带 children 的层级;转为扁平 id/pId 列表 */
export function flattenTreeTableRows(arr: any[]): any[] {
if (!Array.isArray(arr) || !arr.length) return [];
return arr.reduce((acc: any[], val: any) => {
const { children, ...rest } = val;
acc.push(rest);
if (Array.isArray(children) && children.length) {
acc.push(...flattenTreeTableRows(children));
}
return acc;
}, []);
}
function flattenTree(arr: any[]): any[] {
return flattenTreeTableRows(arr);
}
/**
* 读取树表完整扁平数据。
* 已传入表格 fullData(含清空后的 [])时必须以表格为准;
* 旧逻辑在 grid 为空时回退 props,会把已删除行「复活」,导致清空后保存仍报重名。
*/
export function getTreeTableFlatRows(fromGrid?: any[], propsData?: any[]): any[] {
if (Array.isArray(fromGrid)) {
return flattenTreeTableRows(fromGrid);
}
return flattenTreeTableRows(Array.isArray(propsData) ? propsData : []);
}
function getRowSeq(row: any, tableRef: any): string {
return `${tableRef?.xGrid?.getRowSeq(row)}` || '0';
}
function normalizeRowId(row: any): number {
const id = Number(row?.id ?? row?.Id);
return Number.isNaN(id) ? 0 : id;
}
function buildParamPathKey(pId: number, name: string): string {
return `${pId}:${String(name || '').trim().toLowerCase()}`;
}
type NormalizedParamRow = {
id: number;
pId: number;
name: string;
alias: string;
type: number;
isRequired: boolean;
cnName: string | null;
description: string | null;
sort: string;
};
function normalizeTreeParamRow(row: any): NormalizedParamRow {
return {
id: normalizeRowId(row),
pId: Number(row?.pId ?? row?.PId ?? 0),
name: String(row?.name ?? row?.Name ?? '').trim(),
alias: String(row?.alias ?? row?.Alias ?? '').trim(),
type: Number(row?.type ?? row?.Type ?? 1),
isRequired: !!(row?.isRequired ?? row?.IsRequired),
cnName: row?.cnName === '' ? null : (row?.cnName ?? row?.CnName ?? null),
description: row?.description ?? row?.Description ?? null,
sort: String(row?.sort ?? row?.Sort ?? ''),
};
}
function isSameTreeParamRow(row: any, baseline: NormalizedParamRow): boolean {
const current = normalizeTreeParamRow(row);
return (
current.name === baseline.name
&& current.pId === baseline.pId
&& current.type === baseline.type
&& current.isRequired === baseline.isRequired
&& (current.cnName ?? null) === (baseline.cnName ?? null)
&& (current.description ?? null) === (baseline.description ?? null)
);
}
export interface OriginalParamSnapshot {
ids: Set<number>;
pathToId: Map<string, number>;
rowById: Map<number, NormalizedParamRow>;
}
export interface OriginalParamIdSets {
query: OriginalParamSnapshot;
header: OriginalParamSnapshot;
bodyForm: OriginalParamSnapshot;
request: OriginalParamSnapshot;
mappingBody: OriginalParamSnapshot;
response: OriginalParamSnapshot;
}
export function createEmptyOriginalParamSnapshot(): OriginalParamSnapshot {
return {
ids: new Set<number>(),
pathToId: new Map<string, number>(),
rowById: new Map<number, NormalizedParamRow>(),
};
}
export function createEmptyOriginalParamIds(): OriginalParamIdSets {
const empty = createEmptyOriginalParamSnapshot();
return {
query: empty,
header: createEmptyOriginalParamSnapshot(),
bodyForm: createEmptyOriginalParamSnapshot(),
request: createEmptyOriginalParamSnapshot(),
mappingBody: createEmptyOriginalParamSnapshot(),
response: createEmptyOriginalParamSnapshot(),
};
}
/** 从加载时的参数行构建快照(id / 路径 / 原始字段) */
export function buildOriginalParamSnapshot(rows: any[]): OriginalParamSnapshot {
const snapshot = createEmptyOriginalParamSnapshot();
flattenTreeTableRows(rows || []).forEach((raw) => {
const row = normalizeTreeParamRow(raw);
if (row.id <= 0 || !row.name) return;
snapshot.ids.add(row.id);
snapshot.rowById.set(row.id, row);
snapshot.pathToId.set(buildParamPathKey(row.pId, row.name), row.id);
});
return snapshot;
}
function buildOriginalFormSnapshot(rows: any[]): OriginalParamSnapshot {
const snapshot = createEmptyOriginalParamSnapshot();
(rows || []).forEach((raw) => {
const rest = stripFormKvMeta(raw);
const id = normalizeRowId(rest);
const name = String(rest.name || rest.Name || '').trim();
if (id <= 0 || !name) return;
const row: NormalizedParamRow = {
id,
pId: 0,
name,
alias: String(rest.value || rest.Value || '').trim(),
type: 0,
isRequired: false,
cnName: null,
description: rest.description || rest.Description || null,
sort: '',
};
snapshot.ids.add(id);
snapshot.rowById.set(id, row);
snapshot.pathToId.set(buildParamPathKey(0, name), id);
});
return snapshot;
}
/** @deprecated 使用 buildOriginalParamSnapshot */
export function collectParamIds(rows: any[]): Set<number> {
return buildOriginalParamSnapshot(rows).ids;
}
export function buildOriginalParamIdsFromHydrated(source: {
queryParams?: any[];
headerParams?: any[];
bodyFormParams?: any[];
requestParams?: any[];
mappingBodyParams?: any[];
responseParams?: any[];
}): OriginalParamIdSets {
return {
query: buildOriginalFormSnapshot(source.queryParams || []),
header: buildOriginalFormSnapshot(source.headerParams || []),
bodyForm: buildOriginalFormSnapshot(source.bodyFormParams || []),
request: buildOriginalParamSnapshot(source.requestParams || []),
mappingBody: buildOriginalParamSnapshot(source.mappingBodyParams || []),
response: buildOriginalParamSnapshot(source.responseParams || []),
};
}
function mapTreeRow(item: any, optionType: number, tableRef: any, paraKind: number, serverId?: number) {
const id = serverId ?? normalizeRowId(item);
const pId = Number(item?.pId ?? item?.PId ?? 0);
const name = String(item?.name ?? item?.Name ?? '').trim();
return {
optionType,
id,
pId,
alias: optionType === 1 ? name : (item?.alias || item?.Alias || name || ''),
name,
cnName: item?.cnName === '' ? null : (item?.cnName ?? item?.CnName ?? null),
type: item?.type ?? item?.Type ?? 1,
isRequired: !!(item?.isRequired ?? item?.IsRequired),
sort: getRowSeq(item, tableRef),
description: item?.description ?? item?.Description ?? null,
paraKind,
};
}
function mapTreeChangeRows(
actionRows: { insertRecords?: any[]; removeRecords?: any[]; updateRecords?: any[] },
tableRef: any,
paraKind: number,
) {
const install = (actionRows.insertRecords || []).map((i: any) => {
if (!i.name) return null;
return mapTreeRow(i, 1, tableRef, paraKind);
}).filter(Boolean);
const remove: any[] = [];
(actionRows.removeRecords || []).forEach((i: any) => {
remove.push(mapTreeRow(i, 2, tableRef, paraKind));
if (i.children?.length) {
flattenTree(i.children).forEach((child: any) => {
remove.push(mapTreeRow(child, 2, tableRef, paraKind));
});
}
});
const update = (actionRows.updateRecords || []).map((i: any) => {
if (!i.name) return null;
return mapTreeRow(i, 3, tableRef, paraKind);
}).filter(Boolean);
return [...install, ...remove, ...update];
}
function mapTreeDeleteFromBaseline(
serverId: number,
baseline: NormalizedParamRow,
paraKind: number,
) {
return {
optionType: 2,
id: serverId,
pId: baseline.pId,
alias: baseline.alias || baseline.name,
name: baseline.name,
cnName: baseline.cnName,
type: baseline.type,
isRequired: baseline.isRequired,
sort: baseline.sort || '0',
description: baseline.description,
paraKind,
};
}
/**
* 编辑保存时对齐后端 MaintainParametersAsync:
* - 仅提交增/删/改增量
* - 未变更的已有参数不传(后端会自动补全 DB 节点)
* - 假 id + 已存在路径 → 按服务端 id 走修改,避免与 DB 补全节点重复
* - 解析全量导入会 reloadData(清空 removeRecords)且行 id 全是新假 id:走「删光快照 + 整树新增」
* - VXE removeRecords 丢失时,按「快照有、当前表无」补删(与 FormKV 一致)
*/
function buildTreeParameterList(
actionRows: { insertRecords?: any[]; removeRecords?: any[]; updateRecords?: any[] },
treeData: any[],
tableRef: any,
paraKind: number,
isEditMode: boolean,
original: OriginalParamSnapshot,
) {
const changes = mapTreeChangeRows(actionRows, tableRef, paraKind);
const changeById = new Map(changes.map((c) => [Number(c.id), c]));
const removedIds = new Set(
changes.filter((c) => c.optionType === 2).map((c) => Number(c.id)),
);
const flatCurrent = flattenTree(treeData).filter(
(item) => item?.name && String(item.name).trim() !== '',
);
if (!isEditMode) {
if (changes.length > 0) return changes;
return flatCurrent.map((item) => mapTreeRow(item, 1, tableRef, paraKind));
}
// 全量替换:当前行没有任何加载时的服务端 id(解析导入假 id / 或已清空整表)
const preservesAnyServerId = flatCurrent.some((item) =>
original.ids.has(normalizeRowId(item)),
);
if (original.ids.size > 0 && !preservesAnyServerId) {
const result: any[] = [];
original.ids.forEach((serverId) => {
const baseline = original.rowById.get(serverId);
if (!baseline?.name) return;
result.push(mapTreeDeleteFromBaseline(serverId, baseline, paraKind));
});
flatCurrent.forEach((item) => {
result.push(mapTreeRow(item, 1, tableRef, paraKind));
});
return result;
}
const result: any[] = [];
changes.filter((c) => c.optionType === 2).forEach((c) => result.push(c));
const presentServerIds = new Set<number>();
flatCurrent.forEach((item) => {
const id = normalizeRowId(item);
const pId = Number(item?.pId ?? item?.PId ?? 0);
const name = String(item?.name ?? item?.Name ?? '').trim();
const pathKey = buildParamPathKey(pId, name);
if (removedIds.has(id)) return;
const tracked = changeById.get(id);
if (tracked && tracked.optionType !== 2) {
result.push(tracked);
const trackedServerId = Number(tracked.id);
if (trackedServerId > 0 && original.ids.has(trackedServerId)) {
presentServerIds.add(trackedServerId);
}
return;
}
const serverIdByPath = original.pathToId.get(pathKey);
const serverId = original.ids.has(id) ? id : serverIdByPath;
const baseline = serverId ? original.rowById.get(serverId) : undefined;
if (serverId && baseline) {
presentServerIds.add(serverId);
if (!isSameTreeParamRow(item, baseline)) {
result.push(mapTreeRow(item, 3, tableRef, paraKind, serverId));
}
return;
}
if (!serverId) {
result.push(mapTreeRow(item, 1, tableRef, paraKind));
}
});
// VXE removeRecords 丢失时,仍按「原始快照有、当前表无」补删,避免库里残留
original.ids.forEach((serverId) => {
if (removedIds.has(serverId) || presentServerIds.has(serverId)) return;
const baseline = original.rowById.get(serverId);
if (!baseline?.name) return;
result.push(mapTreeDeleteFromBaseline(serverId, baseline, paraKind));
});
return result;
}
/** 客户端校验:同一 pId 下参数名不可重复(对齐后端 ValidateParameter,跳过删除项) */
export function validateTreeParameterPaths(rows: any[]): string | null {
const seen = new Set<string>();
for (const item of flattenTree(rows)) {
// 后端 ValidateParameter 会先排除 OptionType.删除;全量替换时「删旧 + 增同名」不能当重复
const optionType = Number(item?.optionType ?? item?.OptionType ?? 0);
if (optionType === 2) continue;
const name = String(item?.name ?? item?.Name ?? '').trim();
if (!name) continue;
const pId = Number(item?.pId ?? item?.PId ?? 0);
const key = buildParamPathKey(pId, name);
if (seen.has(key)) {
return `同一层级下不能存在重复参数名「${name}」(父节点 id=${pId || 0})`;
}
seen.add(key);
}
return null;
}
/** 编辑模式下无增量变更时不传该列表,避免空数组覆盖后端已有参数 */
function assignParameterList(
payload: Record<string, any>,
key: string,
list: any[],
isEditMode: boolean,
hadOriginalItems: boolean,
) {
if (isEditMode && list.length === 0 && hadOriginalItems) {
delete payload[key];
return;
}
payload[key] = list;
}
function mapFormKvRow(row: any, optionType: number, fieldPosition: number, serverId?: number) {
return {
id: serverId ?? normalizeRowId(row),
name: row.name || row.Name || '',
value: row.value || row.Value || '',
description: row.description || row.Description || '',
optionType,
fieldPosition,
};
}
function stripFormKvMeta(item: any) {
const { _X_ROW_CHILD, _X_ROW_KEY, children, Id, ...rest } = item || {};
return rest;
}
function isSameFormKvRow(row: any, baseline: NormalizedParamRow): boolean {
const rest = stripFormKvMeta(row);
return (
String(rest.name || rest.Name || '').trim() === baseline.name
&& String(rest.value || rest.Value || '').trim() === String(baseline.alias || '')
&& String(rest.description || rest.Description || '').trim() === String(baseline.description || '')
);
}
function isPersistableFormKvRow(p: any) {
// 删除只依赖 id;参数值允许留空(签名类由前置脚本写入)
if (Number(p?.optionType) === 2) {
return Number(p?.id) > 0;
}
return !!(p.name && String(p.name).trim() !== '');
}
function buildFormKvParameterList(
changes: any[],
tableData: any[],
fieldPosition: number,
isEditMode: boolean,
original: OriginalParamSnapshot,
) {
const changeById = new Map(changes.map((c) => [Number(c.id), c]));
const removedIds = new Set(
changes.filter((c) => c.optionType === 2).map((c) => Number(c.id)),
);
if (!isEditMode) {
if (changes.length) return changes.filter(isPersistableFormKvRow);
return (tableData || [])
.map((item) => mapFormKvRow(stripFormKvMeta(item), 1, fieldPosition))
.filter(isPersistableFormKvRow);
}
const result: any[] = [];
changes.filter((c) => c.optionType === 2).forEach((c) => result.push(c));
const presentServerIds = new Set<number>();
(tableData || []).forEach((item) => {
const rest = stripFormKvMeta(item);
const id = normalizeRowId(rest);
const name = String(rest.name || rest.Name || '').trim();
const pathKey = buildParamPathKey(0, name);
if (removedIds.has(id)) return;
const tracked = changeById.get(id);
if (tracked && tracked.optionType !== 2) {
result.push(tracked);
const trackedServerId = Number(tracked.id);
if (trackedServerId > 0) presentServerIds.add(trackedServerId);
return;
}
const serverIdByPath = original.pathToId.get(pathKey);
const serverId = original.ids.has(id) ? id : serverIdByPath;
const baseline = serverId ? original.rowById.get(serverId) : undefined;
if (serverId && baseline) {
presentServerIds.add(serverId);
if (!isSameFormKvRow(rest, baseline)) {
result.push(mapFormKvRow(rest, 3, fieldPosition, serverId));
}
return;
}
if (!serverId) {
result.push(mapFormKvRow(rest, 1, fieldPosition));
}
});
// VXE removeRecords 丢失时,仍按「原始快照有、当前表无」补删,避免库里残留
original.ids.forEach((serverId) => {
if (removedIds.has(serverId) || presentServerIds.has(serverId)) return;
const baseline = original.rowById.get(serverId);
if (!baseline?.name) return;
result.push({
id: serverId,
name: baseline.name,
value: baseline.alias || '',
description: baseline.description || '',
optionType: 2,
fieldPosition,
});
});
return result.filter(isPersistableFormKvRow);
}
function mapFormKvChanges(
actionRows: { insertRecords?: any[]; removeRecords?: any[]; updateRecords?: any[] },
fieldPosition: number,
) {
const mapRow = (row: any, optionType: number) => ({
id: normalizeRowId(row),
name: row.name || '',
value: row.value || '',
description: row.description || '',
optionType,
fieldPosition,
});
const list = [
...(actionRows.insertRecords || []).map((r) => mapRow(r, 1)),
...(actionRows.removeRecords || []).map((r) => mapRow(r, 2)),
...(actionRows.updateRecords || []).map((r) => mapRow(r, 3)),
];
return list.filter(isPersistableFormKvRow);
}
export interface InterfaceDetailSnapshot {
queryParams: any[];
headerParams: any[];
bodyFormParams: any[];
requestParams: any[];
mappingBodyParams: any[];
responseParams: any[];
queryActionRows: { insertRecords: any[]; removeRecords: any[]; updateRecords: any[] };
headerActionRows: { insertRecords: any[]; removeRecords: any[]; updateRecords: any[] };
bodyFormActionRows: { insertRecords: any[]; removeRecords: any[]; updateRecords: any[] };
requestActionRows: { insertRecords: any[]; removeRecords: any[]; updateRecords: any[] };
mappingBodyActionRows: { insertRecords: any[]; removeRecords: any[]; updateRecords: any[] };
responseActionRows: { insertRecords: any[]; removeRecords: any[]; updateRecords: any[] };
queryTableRef: any;
headerTableRef: any;
bodyFormTableRef: any;
requestTableRef: any;
mappingBodyTableRef: any;
responseTableRef: any;
requestExample: string;
originalParamIds?: OriginalParamIdSets;
}
export interface BuildSavePayloadOptions {
formData: Record<string, any>;
mode: InterfaceParameterMode;
isEditMode: boolean;
snapshot: InterfaceDetailSnapshot;
extraFields?: Record<string, any>;
}
function snapshotSize(snapshot: OriginalParamSnapshot): boolean {
return snapshot.ids.size > 0;
}
export function buildInterfaceSavePayload(options: BuildSavePayloadOptions) {
const { formData, mode, isEditMode, snapshot, extraFields = {} } = options;
const contentType = formData.contentType as number | undefined;
const original = snapshot.originalParamIds ?? createEmptyOriginalParamIds();
const queryChanges = mapFormKvChanges(snapshot.queryActionRows, FieldPosition.Query);
const headerChanges = mapFormKvChanges(snapshot.headerActionRows, FieldPosition.Header);
const bodyFormChanges = mapFormKvChanges(snapshot.bodyFormActionRows, FieldPosition.Body);
const queryFormParameterList = buildFormKvParameterList(
queryChanges,
snapshot.queryParams,
FieldPosition.Query,
isEditMode,
original.query,
);
const headerFormParameterList = buildFormKvParameterList(
headerChanges,
snapshot.headerParams,
FieldPosition.Header,
isEditMode,
original.header,
);
const bodyFormParameterList = buildFormKvParameterList(
bodyFormChanges,
snapshot.bodyFormParams,
FieldPosition.Body,
isEditMode,
original.bodyForm,
);
const outParameterList = buildTreeParameterList(
snapshot.responseActionRows,
snapshot.responseParams,
snapshot.responseTableRef,
ParaKind.Normal,
isEditMode,
original.response,
).filter((p) => p.name && String(p.name).trim() !== '');
const emptyLists = {
inParameterList: [] as any[],
requestBodyParameterList: [] as any[],
mappingBodyParameterList: [] as any[],
bodyFormParameterList: [] as any[],
};
let payload: Record<string, any> = {
...formData,
...extraFields,
inParams: snapshot.requestExample,
...emptyLists,
};
assignParameterList(payload, 'queryFormParameterList', queryFormParameterList, isEditMode, snapshotSize(original.query));
assignParameterList(payload, 'headerFormParameterList', headerFormParameterList, isEditMode, snapshotSize(original.header));
assignParameterList(payload, 'outParameterList', outParameterList, isEditMode, snapshotSize(original.response));
if (mode === 'upstream') {
const inParameterList = buildTreeParameterList(
snapshot.requestActionRows,
snapshot.requestParams,
snapshot.requestTableRef,
ParaKind.Normal,
isEditMode,
original.request,
).filter((p) => p.name && String(p.name).trim() !== '');
assignParameterList(payload, 'inParameterList', inParameterList, isEditMode, snapshotSize(original.request));
} else if (isFormUrlEncodedContentType(contentType)) {
assignParameterList(payload, 'bodyFormParameterList', bodyFormParameterList, isEditMode, snapshotSize(original.bodyForm));
const mappingBodyParameterList = buildTreeParameterList(
snapshot.mappingBodyActionRows,
snapshot.mappingBodyParams,
snapshot.mappingBodyTableRef,
ParaKind.MappingBody,
isEditMode,
original.mappingBody,
).filter((p) => p.name && String(p.name).trim() !== '');
assignParameterList(payload, 'mappingBodyParameterList', mappingBodyParameterList, isEditMode, snapshotSize(original.mappingBody));
} else if (isNoneContentType(contentType)) {
const mappingBodyParameterList = buildTreeParameterList(
snapshot.mappingBodyActionRows,
snapshot.mappingBodyParams,
snapshot.mappingBodyTableRef,
ParaKind.MappingBody,
isEditMode,
original.mappingBody,
).filter((p) => p.name && String(p.name).trim() !== '');
assignParameterList(payload, 'mappingBodyParameterList', mappingBodyParameterList, isEditMode, snapshotSize(original.mappingBody));
} else if (isJsonOrXmlContentType(contentType)) {
const requestBodyParameterList = buildTreeParameterList(
snapshot.requestActionRows,
snapshot.requestParams,
snapshot.requestTableRef,
ParaKind.Normal,
isEditMode,
original.request,
).filter((p) => p.name && String(p.name).trim() !== '');
assignParameterList(payload, 'requestBodyParameterList', requestBodyParameterList, isEditMode, snapshotSize(original.request));
}
if (isEditMode) {
payload.optionType = 3;
} else {
delete payload.id;
payload.optionType = 1;
}
return payload;
}
export function validateInterfaceSavePayload(
payload: Record<string, any>,
mode: InterfaceParameterMode,
): string | null {
const hasItems = (list?: any[]) => Array.isArray(list) && list.length > 0;
const mustEmpty = (list?: any[]) => !list || list.length === 0;
const isEdit = payload.optionType === 3;
const treeLists: any[][] = [];
if (mode === 'upstream') treeLists.push(payload.inParameterList || []);
else if (isFormUrlEncodedContentType(payload.contentType)) {
treeLists.push(payload.mappingBodyParameterList || []);
} else if (isNoneContentType(payload.contentType)) {
treeLists.push(payload.mappingBodyParameterList || []);
} else if (isJsonOrXmlContentType(payload.contentType)) {
treeLists.push(payload.requestBodyParameterList || []);
}
treeLists.push(payload.outParameterList || []);
for (const list of treeLists) {
const pathError = validateTreeParameterPaths(list);
if (pathError) return pathError;
}
if (mode === 'upstream') {
if (!isEdit && !hasItems(payload.inParameterList)) return '上游入参(In Parameters)不能为空';
if (!mustEmpty(payload.requestBodyParameterList)) return '上游不应包含 requestBodyParameterList';
if (!mustEmpty(payload.mappingBodyParameterList)) return '上游不应包含 mappingBodyParameterList';
if (!mustEmpty(payload.bodyFormParameterList)) return '上游不应包含 bodyFormParameterList';
return null;
}
const ct = payload.contentType;
if (isNoneContentType(ct)) {
if (!mustEmpty(payload.requestBodyParameterList)) return 'none 不应包含 requestBodyParameterList';
if (!mustEmpty(payload.bodyFormParameterList)) return 'none 不应包含 bodyFormParameterList';
if (!mustEmpty(payload.inParameterList)) return 'none 不应包含 inParameterList';
return null;
}
if (isFormUrlEncodedContentType(ct)) {
if (!isEdit && !hasItems(payload.bodyFormParameterList)) return 'FormUrlEncoded 须配置 Body 表单字段';
if (!isEdit && !hasItems(payload.mappingBodyParameterList)) return 'FormUrlEncoded 须配置 Mapping Body';
if (!mustEmpty(payload.inParameterList)) return '下游 FormUrlEncoded 不应包含 inParameterList';
if (!mustEmpty(payload.requestBodyParameterList)) return '下游 FormUrlEncoded 不应包含 requestBodyParameterList';
return null;
}
if (isJsonOrXmlContentType(ct)) {
if (!isEdit && !hasItems(payload.requestBodyParameterList)) return 'Json/Xml 下游须配置 Body 参数树';
if (!mustEmpty(payload.inParameterList)) return '下游 Json/Xml 不应包含 inParameterList';
if (!mustEmpty(payload.mappingBodyParameterList)) return '下游 Json/Xml 不应包含 mappingBodyParameterList';
if (!mustEmpty(payload.bodyFormParameterList)) return '下游 Json/Xml 不应包含 bodyFormParameterList';
return null;
}
return '不支持的 ContentType';
}
/** 映射页:下游目标参数树来源 */
export function resolveDownstreamMappingTargetList(interfaceData: Record<string, any>): any[] {
const ct = interfaceData.contentType;
if (isNoneContentType(ct)) {
return interfaceData.mappingBodyParameterList ?? [];
}
if (isFormUrlEncodedContentType(ct)) {
return interfaceData.mappingBodyParameterList ?? [];
}
if (isJsonOrXmlContentType(ct)) {
return interfaceData.requestBodyParameterList ?? interfaceData.inParameterList ?? [];
}
return interfaceData.requestBodyParameterList ?? interfaceData.inParameterList ?? [];
}