Files
agent/mock/interface.mock.ts
2026-02-16 12:46:37 +08:00

455 lines
12 KiB
TypeScript

import type {
InterfaceChangeLogItem,
InterfaceDocInfo,
InterfaceExportResponse,
InterfaceFormValues,
InterfaceGatewayFormValues,
InterfaceGatewayPolicy,
InterfaceIntegrationInfo,
InterfaceItem,
InterfaceListParams,
InterfaceMockInfo,
InterfaceRoutePreview,
} from '@/pages/InterfaceManagement/data';
import type { Request, Response } from 'express';
import type { ParamsDictionary } from 'express-serve-static-core';
type InterfaceListQuery = Omit<InterfaceListParams, 'current' | 'pageSize'> & {
current?: string;
pageSize?: string;
};
const interfaceList: InterfaceItem[] = [
{
id: 'api-1001',
name: '用户列表查询',
method: 'GET',
path: '/api/users',
owner: '李娜',
status: 'active',
version: 'v1',
description: '分页获取用户列表',
createdAt: '2024-10-01 09:30:00',
updatedAt: '2024-10-10 18:20:00',
},
{
id: 'api-1002',
name: '新增订单',
method: 'POST',
path: '/api/orders',
owner: '王彬',
status: 'active',
version: 'v2',
description: '创建新的订单记录',
createdAt: '2024-09-18 13:10:00',
updatedAt: '2024-10-05 11:05:00',
},
{
id: 'api-1003',
name: '库存调整',
method: 'PUT',
path: '/api/inventory/adjust',
owner: '赵敏',
status: 'deprecated',
version: 'v1',
description: '旧库存调整接口,计划下线',
createdAt: '2024-08-02 08:45:00',
updatedAt: '2024-09-21 16:40:00',
},
{
id: 'api-1004',
name: '删除文章',
method: 'DELETE',
path: '/api/articles/:id',
owner: '陈曦',
status: 'disabled',
version: 'v1',
description: '文章删除功能已暂时关闭',
createdAt: '2024-07-12 10:20:00',
updatedAt: '2024-08-20 19:10:00',
},
];
const interfaceDocMap: Record<string, InterfaceDocInfo> = {
'api-1001': {
docUrl: 'https://docs.example.com/interfaces/api-1001',
exampleText:
'curl -X GET https://api.example.com/api/users?page=1&pageSize=10',
updatedAt: '2024-10-10 18:20:00',
owner: '李娜',
},
'api-1002': {
docUrl: 'https://docs.example.com/interfaces/api-1002',
exampleText: 'curl -X POST https://api.example.com/api/orders -d "{}"',
updatedAt: '2024-10-05 11:05:00',
owner: '王彬',
},
};
const gatewayPolicyMap: Record<string, InterfaceGatewayPolicy> = {
'api-1001': {
routeId: 'route-api-1001',
rateLimit: '2000 次/分钟',
authType: 'OAuth2 + 签名校验',
timeoutMs: 3000,
},
'api-1002': {
routeId: 'route-api-1002',
rateLimit: '1200 次/分钟',
authType: 'API Key',
timeoutMs: 5000,
},
};
const mockInfoMap: Record<string, InterfaceMockInfo> = {
'api-1001': {
requestSample:
'curl -X GET https://api.example.com/api/users?page=1&pageSize=10',
responseSample: '{"success":true,"data":[{"id":"1"}],"total":1}',
latency: '120ms',
mockEnabled: true,
mockUrl: 'https://mock.example.com/interfaces/api-1001',
},
'api-1002': {
requestSample: 'curl -X POST https://api.example.com/api/orders -d "{}"',
responseSample: '{"success":true,"data":{"id":"100"}}',
latency: '210ms',
mockEnabled: false,
mockUrl: 'https://mock.example.com/interfaces/api-1002',
},
};
const integrationInfoMap: Record<string, InterfaceIntegrationInfo> = {
'api-1001': {
partner: '物流平台 A',
channel: 'API Key + IP 白名单',
status: '已接入',
consoleUrl: 'https://console.example.com/integrations/api-1001',
},
'api-1002': {
partner: '支付平台 B',
channel: 'JWT + 双向 TLS',
status: '对接中',
consoleUrl: 'https://console.example.com/integrations/api-1002',
},
};
const changeLogMap: Record<string, InterfaceChangeLogItem[]> = {
'api-1001': [
{
id: 'log-1',
time: '2024-10-10 18:20',
description: '调整响应字段,新增可选字段 note',
operator: '李娜',
},
{
id: 'log-2',
time: '2024-09-18 13:10',
description: '接口首次发布',
operator: '王彬',
},
],
'api-1002': [
{
id: 'log-3',
time: '2024-10-05 11:05',
description: '补充校验规则与异常码',
operator: '王彬',
},
],
};
const parseNumber = (value: string | undefined, fallback: number) => {
const parsed = Number(value);
return Number.isNaN(parsed) ? fallback : parsed;
};
const getInterfaceItem = (id: string) =>
interfaceList.find((item) => item.id === id);
const ensureGatewayPolicy = (id: string): InterfaceGatewayPolicy => {
if (!gatewayPolicyMap[id]) {
gatewayPolicyMap[id] = {
routeId: `route-${id}`,
rateLimit: '2000 次/分钟',
authType: 'OAuth2 + 签名校验',
timeoutMs: 3000,
};
}
return gatewayPolicyMap[id];
};
const ensureMockInfo = (id: string): InterfaceMockInfo => {
if (!mockInfoMap[id]) {
const item = getInterfaceItem(id);
mockInfoMap[id] = {
requestSample: `curl -X ${item?.method ?? 'GET'} https://api.example.com${
item?.path ?? '/api/example'
}`,
responseSample: '{"success":true}',
latency: '120ms',
mockEnabled: false,
mockUrl: `https://mock.example.com/interfaces/${id}`,
};
}
return mockInfoMap[id];
};
export default {
'GET /api/interfaces': (
req: Request<ParamsDictionary, unknown, unknown, InterfaceListQuery>,
res: Response,
) => {
const { current, pageSize, name, method, path, owner, status, version } =
req.query;
const currentPage = parseNumber(current, 1);
const size = parseNumber(pageSize, 10);
let dataSource = [...interfaceList];
if (name) {
dataSource = dataSource.filter((item) => item.name.includes(name));
}
if (method) {
dataSource = dataSource.filter((item) => item.method === method);
}
if (path) {
dataSource = dataSource.filter((item) => item.path.includes(path));
}
if (owner) {
dataSource = dataSource.filter((item) => item.owner.includes(owner));
}
if (status) {
dataSource = dataSource.filter((item) => item.status === status);
}
if (version) {
dataSource = dataSource.filter((item) => item.version.includes(version));
}
const start = (currentPage - 1) * size;
const end = start + size;
res.json({
success: true,
data: dataSource.slice(start, end),
total: dataSource.length,
});
},
'POST /api/interfaces': (
req: Request<ParamsDictionary, InterfaceItem, InterfaceFormValues>,
res: Response,
) => {
const now = new Date().toISOString();
const newItem: InterfaceItem = {
id: `api-${Date.now()}`,
createdAt: now,
updatedAt: now,
...req.body,
};
interfaceList.unshift(newItem);
res.json(newItem);
},
'PUT /api/interfaces/:id': (
req: Request<{ id: string }, InterfaceItem, InterfaceFormValues>,
res: Response,
) => {
const { id } = req.params;
const index = interfaceList.findIndex((item) => item.id === id);
if (index >= 0) {
interfaceList[index] = {
...interfaceList[index],
...req.body,
updatedAt: new Date().toISOString(),
};
res.json(interfaceList[index]);
return;
}
res.status(404).json({
success: false,
data: interfaceList,
total: interfaceList.length,
});
},
'DELETE /api/interfaces/:id': (
req: Request<{ id: string }>,
res: Response,
) => {
const { id } = req.params;
const index = interfaceList.findIndex((item) => item.id === id);
if (index >= 0) {
interfaceList.splice(index, 1);
res.json({ success: true });
return;
}
res.status(404).json({ success: false });
},
'GET /api/interfaces/:id/doc': (
req: Request<{ id: string }>,
res: Response<InterfaceDocInfo>,
) => {
const { id } = req.params;
const item = getInterfaceItem(id);
if (!item) {
res.status(404).end();
return;
}
const docInfo: InterfaceDocInfo = interfaceDocMap[id] ?? {
docUrl: `https://docs.example.com/interfaces/${id}`,
exampleText: `curl -X ${item.method} https://api.example.com${item.path}`,
updatedAt: item.updatedAt,
owner: item.owner,
};
interfaceDocMap[id] = docInfo;
res.json(docInfo);
},
'GET /api/interfaces/:id/gateway': (
req: Request<{ id: string }>,
res: Response<InterfaceGatewayPolicy>,
) => {
const { id } = req.params;
res.json(ensureGatewayPolicy(id));
},
'PUT /api/interfaces/:id/gateway': (
req: Request<
{ id: string },
InterfaceGatewayPolicy,
InterfaceGatewayFormValues
>,
res: Response<InterfaceGatewayPolicy>,
) => {
const { id } = req.params;
const currentPolicy = ensureGatewayPolicy(id);
const nextPolicy: InterfaceGatewayPolicy = {
...currentPolicy,
rateLimit: req.body.rateLimit,
authType: req.body.authType,
timeoutMs: req.body.timeoutMs,
};
gatewayPolicyMap[id] = nextPolicy;
res.json(nextPolicy);
},
'GET /api/interfaces/:id/gateway/preview': (
req: Request<{ id: string }>,
res: Response<InterfaceRoutePreview>,
) => {
const { id } = req.params;
const item = getInterfaceItem(id);
if (!item) {
res.status(404).end();
return;
}
const policy = ensureGatewayPolicy(id);
const preview: InterfaceRoutePreview = {
routeId: policy.routeId,
upstream: 'https://gateway.example.com',
path: item.path,
method: item.method,
timeoutMs: policy.timeoutMs,
rateLimit: policy.rateLimit,
authType: policy.authType,
};
res.json(preview);
},
'GET /api/interfaces/:id/mock': (
req: Request<{ id: string }>,
res: Response<InterfaceMockInfo>,
) => {
const { id } = req.params;
res.json(ensureMockInfo(id));
},
'POST /api/interfaces/:id/debug': (
req: Request<{ id: string }>,
res: Response<{ success: boolean; message: string }>,
) => {
const { id } = req.params;
const mockInfo = ensureMockInfo(id);
mockInfo.mockEnabled = true;
mockInfo.latency = '98ms';
res.json({ success: true, message: 'debug-started' });
},
'GET /api/interfaces/:id/integrations': (
req: Request<{ id: string }>,
res: Response<InterfaceIntegrationInfo>,
) => {
const { id } = req.params;
const info =
integrationInfoMap[id] ??
({
partner: '待配置',
channel: '未配置',
status: '未接入',
consoleUrl: 'https://console.example.com/integrations',
} as InterfaceIntegrationInfo);
integrationInfoMap[id] = info;
res.json(info);
},
'POST /api/interfaces/:id/integrations': (
req: Request<
{ id: string },
InterfaceIntegrationInfo,
Pick<InterfaceIntegrationInfo, 'partner' | 'channel'>
>,
res: Response<InterfaceIntegrationInfo>,
) => {
const { id } = req.params;
const info: InterfaceIntegrationInfo = {
partner: req.body.partner,
channel: req.body.channel,
status: '已接入',
consoleUrl: `https://console.example.com/integrations/${id}`,
};
integrationInfoMap[id] = info;
res.json(info);
},
'POST /api/interfaces/:id/integrations/key': (
req: Request<{ id: string }>,
res: Response<InterfaceIntegrationInfo>,
) => {
const { id } = req.params;
const info = integrationInfoMap[id] ?? {
partner: '待配置',
channel: '未配置',
status: '未接入',
consoleUrl: `https://console.example.com/integrations/${id}`,
};
res.json(info);
},
'GET /api/interfaces/:id/changelog': (
req: Request<{ id: string }>,
res: Response<InterfaceChangeLogItem[]>,
) => {
const { id } = req.params;
res.json(changeLogMap[id] ?? []);
},
'GET /api/interfaces/:id/changelog/export': (
req: Request<{ id: string }>,
res: Response<InterfaceExportResponse>,
) => {
const { id } = req.params;
res.json({
downloadUrl: `https://download.example.com/interfaces/${id}/changelog.csv`,
fileName: `interface-${id}-changelog.csv`,
});
},
};