Swagger MCP Server
by tuskermanshu
Verified
- swagger-mcp-server
- src
- generators
/**
* API客户端代码生成器
*/
import { promises as fs } from 'fs';
import path from 'path';
import { OpenAPIV3 } from 'openapi-types';
import { BaseCodeGenerator, CodeGenerationResult, CodeGeneratorOptions } from './code-generator';
import { OptimizedSwaggerApiParser } from '../optimized-swagger-parser';
import type { ApiOperation } from '../optimized-swagger-parser';
/**
* API客户端生成器选项
*/
export interface ApiClientGeneratorOptions extends CodeGeneratorOptions {
/**
* Swagger/OpenAPI文档URL
*/
swaggerUrl: string;
/**
* API客户端技术栈
* @default "axios"
*/
clientType?: 'axios' | 'fetch' | 'react-query';
/**
* 是否生成类型导入
* @default true
*/
generateTypeImports?: boolean;
/**
* 类型导入路径
* @default "../types"
*/
typesImportPath?: string;
/**
* 分组方式
* @default "tag"
*/
groupBy?: 'tag' | 'path' | 'none';
/**
* 请求头信息
*/
headers?: Record<string, string>;
/**
* 过滤tag列表
*/
includeTags?: string[];
/**
* 排除tag列表
*/
excludeTags?: string[];
/**
* 自定义模板路径
*/
templatePath?: string;
/**
* 是否使用缓存
* @default true
*/
useCache?: boolean;
/**
* 缓存有效期(分钟)
* @default 60
*/
cacheTTLMinutes?: number;
/**
* 是否跳过验证
* @default false
*/
skipValidation?: boolean;
/**
* 是否启用懒加载
* @default true
*/
lazyLoading?: boolean;
/**
* 进度回调函数
*/
progressCallback?: (progress: number, message: string) => void;
}
/**
* API客户端代码生成器类
*/
export class ApiClientGenerator extends BaseCodeGenerator<ApiClientGeneratorOptions> {
constructor() {
super(
'api-client-generator',
'Generates API client code from Swagger/OpenAPI specification'
);
}
/**
* 验证选项
*/
validateOptions(options: ApiClientGeneratorOptions): boolean {
if (!options.swaggerUrl) {
return false;
}
if (options.clientType && !['axios', 'fetch', 'react-query'].includes(options.clientType)) {
return false;
}
if (options.groupBy && !['tag', 'path', 'none'].includes(options.groupBy)) {
return false;
}
if (options.includeTags && options.excludeTags) {
console.warn('Both includeTags and excludeTags are defined. includeTags will take precedence.');
}
return true;
}
/**
* 生成API客户端代码
*/
async generate(options: ApiClientGeneratorOptions): Promise<CodeGenerationResult> {
try {
// 确保选项有效
if (!this.validateOptions(options)) {
return {
files: [],
success: false,
error: 'Invalid options. swaggerUrl is required.'
};
}
// 设置默认值
const outputDir = options.outputDir || './generated/api';
const overwrite = options.overwrite || false;
const filePrefix = options.filePrefix || '';
const fileSuffix = options.fileSuffix || '';
const clientType = options.clientType || 'axios';
const generateTypeImports = options.generateTypeImports !== false;
const typesImportPath = options.typesImportPath || '../types';
const groupBy = options.groupBy || 'tag';
const useCache = options.useCache !== false;
const cacheTTLMinutes = options.cacheTTLMinutes || 60;
const skipValidation = options.skipValidation || false;
const lazyLoading = options.lazyLoading !== false;
// 创建输出目录
await this.ensureDirectoryExists(outputDir);
// 解析Swagger文档
console.log(`[ApiClientGenerator] 解析Swagger文档: ${options.swaggerUrl}`);
// 设置进度日志
const logProgress = (progress: number, message: string) => {
console.log(`[ApiClientGenerator] 进度 ${Math.round(progress * 100)}%: ${message}`);
if (options.progressCallback) {
options.progressCallback(progress, message);
}
};
// 使用优化的解析器
const parser = new OptimizedSwaggerApiParser({
url: options.swaggerUrl,
headers: options.headers,
useCache,
cacheTTL: cacheTTLMinutes * 60 * 1000, // 转换为毫秒
skipValidation,
lazyLoading,
progressCallback: logProgress
});
logProgress(0.1, '开始获取API文档');
const api = await parser.fetchApi();
logProgress(0.3, 'API文档获取完成,开始获取操作列表');
const operations = await parser.getAllOperations();
if (!operations || operations.length === 0) {
return {
files: [],
success: false,
error: 'No API operations found in Swagger document'
};
}
logProgress(0.4, `找到 ${operations.length} 个API操作,准备生成代码`);
// 过滤操作
const filteredOperations = this.filterOperations(operations, options.includeTags, options.excludeTags);
logProgress(0.5, `过滤后剩余 ${filteredOperations.length} 个API操作`);
// 分组操作
const groupedOperations = this.groupOperations(filteredOperations, groupBy);
// 生成客户端代码
const generatedFiles: string[] = [];
const warnings: string[] = [];
// 获取所有用到的模式
logProgress(0.6, '加载模式定义');
const schemas = await parser.getAllSchemas();
// 为每个分组生成文件
let fileCount = 0;
const totalGroups = Object.keys(groupedOperations).length;
for (const [groupName, operations] of Object.entries(groupedOperations)) {
try {
// 格式化分组名称
const formattedGroupName = this.formatGroupName(groupName);
// 生成文件名
const fileName = `${filePrefix}${formattedGroupName}${fileSuffix}.ts`;
const filePath = path.join(outputDir, fileName);
// 检查文件是否存在
const fileExists = await this.fileExists(filePath);
if (fileExists && !overwrite) {
warnings.push(`跳过已存在的文件: ${fileName}`);
continue;
}
// 生成客户端代码
const clientCode = this.generateClientCode({
groupName: formattedGroupName,
operations,
schemas,
clientType,
generateTypeImports,
typesImportPath
});
// 写入文件
await fs.writeFile(filePath, clientCode, 'utf8');
generatedFiles.push(filePath);
// 更新进度
fileCount++;
const progress = 0.6 + (0.3 * fileCount / totalGroups);
logProgress(progress, `生成文件 ${fileCount}/${totalGroups}: ${fileName}`);
} catch (err) {
warnings.push(`无法处理分组 ${groupName}: ${err instanceof Error ? err.message : String(err)}`);
}
}
// 生成索引文件
if (generatedFiles.length > 0) {
const indexContent = this.generateIndexFile(generatedFiles, outputDir);
const indexPath = path.join(outputDir, 'index.ts');
await fs.writeFile(indexPath, indexContent, 'utf8');
generatedFiles.push(indexPath);
logProgress(0.95, `已生成索引文件: ${indexPath}`);
}
// 生成基础客户端配置文件
if (clientType === 'axios' || clientType === 'fetch') {
const configFileName = `${clientType}-client.ts`;
const configFilePath = path.join(outputDir, configFileName);
if (!await this.fileExists(configFilePath) || overwrite) {
const configCode = this.generateClientConfigCode(clientType);
await fs.writeFile(configFilePath, configCode, 'utf8');
generatedFiles.push(configFilePath);
logProgress(0.98, `已生成客户端配置文件: ${configFilePath}`);
}
}
logProgress(1.0, `代码生成完成,共生成 ${generatedFiles.length} 个文件`);
return {
files: generatedFiles,
success: true,
warnings: warnings.length > 0 ? warnings : undefined
};
} catch (error) {
return {
files: [],
success: false,
error: error instanceof Error ? error.message : String(error)
};
}
}
/**
* 确保目录存在
*/
private async ensureDirectoryExists(dir: string): Promise<void> {
try {
await fs.access(dir);
} catch {
await fs.mkdir(dir, { recursive: true });
}
}
/**
* 检查文件是否存在
*/
private async fileExists(filePath: string): Promise<boolean> {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
/**
* 过滤API操作
*/
private filterOperations(
operations: ApiOperation[],
includeTags?: string[],
excludeTags?: string[]
): ApiOperation[] {
if (includeTags && includeTags.length > 0) {
// 只包含指定tag的操作
return operations.filter(op =>
op.tags && op.tags.some((tag: string) => includeTags.includes(tag))
);
} else if (excludeTags && excludeTags.length > 0) {
// 排除指定tag的操作
return operations.filter(op =>
!op.tags || !op.tags.some((tag: string) => excludeTags.includes(tag))
);
}
return operations;
}
/**
* 分组API操作
*/
private groupOperations(
operations: ApiOperation[],
groupBy: 'tag' | 'path' | 'none'
): Record<string, ApiOperation[]> {
const grouped: Record<string, ApiOperation[]> = {};
if (groupBy === 'none') {
grouped['api'] = operations;
return grouped;
}
for (const operation of operations) {
let groupNames: string[] = [];
if (groupBy === 'tag') {
// 按tag分组
groupNames = operation.tags && operation.tags.length > 0
? operation.tags
: ['default'];
} else if (groupBy === 'path') {
// 按路径第一级分组
const pathParts = operation.path.split('/').filter(Boolean);
const firstPath = pathParts.length > 0 ? pathParts[0] : 'default';
groupNames = [firstPath];
}
// 添加到所有相关分组
for (const name of groupNames) {
if (!grouped[name]) {
grouped[name] = [];
}
grouped[name].push(operation);
}
}
return grouped;
}
/**
* 格式化分组名称
*/
private formatGroupName(name: string): string {
// 移除非法字符
let formattedName = name.replace(/[^a-zA-Z0-9_-]/g, '');
// 转换为驼峰命名
formattedName = formattedName
.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
.replace(/^([A-Z])/, (_, letter) => letter.toLowerCase());
// 确保名称不以数字开头
if (/^[0-9]/.test(formattedName)) {
formattedName = 'api' + formattedName;
}
return formattedName;
}
/**
* 生成索引文件
*/
private generateIndexFile(files: string[], baseDir: string): string {
const imports: string[] = [];
for (const file of files) {
// 排除索引文件本身和客户端配置文件
const basename = path.basename(file);
if (basename === 'index.ts' || basename.endsWith('-client.ts')) {
continue;
}
// 获取相对路径
const relativePath = path.relative(baseDir, file);
// 移除.ts扩展名
const importPath = './' + relativePath.replace(/\.ts$/, '');
imports.push(`export * from '${importPath}';`);
}
// 导出客户端配置
imports.push(`export * from './axios-client';`);
return imports.join('\n') + '\n';
}
/**
* 生成客户端代码
*/
private generateClientCode(options: {
groupName: string;
operations: ApiOperation[];
schemas: Record<string, OpenAPIV3.SchemaObject>;
clientType: 'axios' | 'fetch' | 'react-query';
generateTypeImports: boolean;
typesImportPath: string;
}): string {
const { groupName, operations, schemas, clientType, generateTypeImports, typesImportPath } = options;
// 收集使用到的类型
const usedTypes = new Set<string>();
// 生成导入语句
let imports = '';
if (clientType === 'axios') {
imports += `import { axiosInstance } from './axios-client';\n\n`;
} else if (clientType === 'fetch') {
imports += `import { fetchWithConfig } from './fetch-client';\n\n`;
} else if (clientType === 'react-query') {
imports += `import { useQuery, useMutation } from '@tanstack/react-query';\n`;
imports += `import { axiosInstance } from './axios-client';\n\n`;
}
// 分析操作,收集使用到的类型
for (const operation of operations) {
this.collectTypes(operation, schemas, usedTypes);
}
// 生成类型导入
if (generateTypeImports && usedTypes.size > 0) {
const typeImports = Array.from(usedTypes).join(', ');
imports += `import { ${typeImports} } from '${typesImportPath}';\n\n`;
}
// 生成API函数
const apiFunctions = operations.map(operation =>
this.generateApiFunction(operation, clientType)
).join('\n\n');
return `${imports}${apiFunctions}\n`;
}
/**
* 收集使用到的类型
*/
private collectTypes(
operation: ApiOperation,
schemas: Record<string, OpenAPIV3.SchemaObject>,
usedTypes: Set<string>
): void {
// 检查参数类型
if (operation.parameters) {
for (const param of operation.parameters) {
if ('schema' in param) {
this.collectSchemaTypes(param.schema as OpenAPIV3.SchemaObject | undefined, schemas, usedTypes);
}
}
}
// 检查请求体类型
if (operation.requestBody?.content) {
for (const [_, mediaType] of Object.entries(operation.requestBody.content)) {
this.collectSchemaTypes((mediaType as any).schema as OpenAPIV3.SchemaObject | undefined, schemas, usedTypes);
}
}
// 检查响应类型
if (operation.responses) {
for (const [_, response] of Object.entries(operation.responses)) {
if ((response as any).content) {
for (const [__, mediaType] of Object.entries((response as any).content)) {
this.collectSchemaTypes((mediaType as any).schema, schemas, usedTypes);
}
}
}
}
}
/**
* 从模式中收集类型
*/
private collectSchemaTypes(
schema: OpenAPIV3.SchemaObject | undefined,
schemas: Record<string, OpenAPIV3.SchemaObject>,
usedTypes: Set<string>
): void {
if (!schema) return;
// 处理引用
if ('$ref' in schema && typeof schema.$ref === 'string') {
const refParts = schema.$ref.split('/');
const refName = refParts[refParts.length - 1];
usedTypes.add(refName);
// 递归处理引用的模式
if (schemas[refName]) {
this.collectSchemaTypes(schemas[refName], schemas, usedTypes);
}
}
// 处理数组
if (schema.type === 'array' && schema.items) {
this.collectSchemaTypes(schema.items as OpenAPIV3.SchemaObject, schemas, usedTypes);
}
// 处理对象属性
if (schema.properties) {
for (const [_, propSchema] of Object.entries(schema.properties)) {
this.collectSchemaTypes(propSchema as OpenAPIV3.SchemaObject, schemas, usedTypes);
}
}
// 处理联合类型
if (schema.oneOf || schema.anyOf) {
const subSchemas = schema.oneOf || schema.anyOf || [];
for (const subSchema of subSchemas) {
this.collectSchemaTypes(subSchema as OpenAPIV3.SchemaObject, schemas, usedTypes);
}
}
// 处理交叉类型
if (schema.allOf) {
for (const subSchema of schema.allOf) {
this.collectSchemaTypes(subSchema as OpenAPIV3.SchemaObject, schemas, usedTypes);
}
}
}
/**
* 生成API函数
*/
private generateApiFunction(
operation: ApiOperation,
clientType: 'axios' | 'fetch' | 'react-query'
): string {
const { operationId, method, path, summary, description, parameters, requestBody, responses } = operation;
// 生成函数名
const functionName = this.formatOperationId(operationId);
// 生成注释
const comments = [];
if (summary) comments.push(summary);
if (description) comments.push(description);
// 生成路径参数映射
const pathParams = parameters?.filter((p: OpenAPIV3.ParameterObject) => (p as any).in === 'path') || [];
// 生成查询参数映射
const queryParams = parameters?.filter((p: OpenAPIV3.ParameterObject) => (p as any).in === 'query') || [];
// 生成其他参数
const headerParams = parameters?.filter((p: OpenAPIV3.ParameterObject) => (p as any).in === 'header') || [];
// 处理请求体类型
let requestBodyType = 'any';
let requestBodyRequired = requestBody?.required || false;
if (requestBody?.content) {
const contentType = Object.keys(requestBody.content)[0];
if (contentType) {
const schema = (requestBody.content[contentType] as any).schema;
if (schema) {
if (schema.$ref) {
const refParts = schema.$ref.split('/');
requestBodyType = refParts[refParts.length - 1];
} else if (schema.type === 'array') {
requestBodyType = 'any[]';
} else {
requestBodyType = this.mapSwaggerTypeToTS(schema);
}
}
}
}
// 处理响应类型
let responseType = 'any';
const successResponse = responses?.['200'] || responses?.['201'] || responses?.['204'];
if (successResponse && (successResponse as any).content) {
const contentType = Object.keys((successResponse as any).content)[0];
if (contentType) {
const schema = (successResponse as any).content[contentType].schema;
if (schema) {
if (schema.$ref) {
const refParts = schema.$ref.split('/');
responseType = refParts[refParts.length - 1];
} else if (schema.type === 'array') {
let itemType = 'any';
if (schema.items) {
const itemSchema = schema.items as OpenAPIV3.SchemaObject;
if ('$ref' in itemSchema && typeof itemSchema.$ref === 'string') {
const refParts = itemSchema.$ref.split('/');
itemType = refParts[refParts.length - 1];
} else {
itemType = this.mapSwaggerTypeToTS(itemSchema);
}
}
responseType = `${itemType}[]`;
} else {
responseType = this.mapSwaggerTypeToTS(schema);
}
}
}
}
// 生成参数接口
const paramInterfaceName = `${this.capitalize(functionName)}Params`;
let paramInterface = `interface ${paramInterfaceName} {\n`;
// 添加路径参数
for (const param of pathParams) {
const paramType = this.getParamType(param);
paramInterface += ` /** ${(param as any).description || (param as any).name} */\n`;
paramInterface += ` ${(param as any).name}: ${paramType};\n`;
}
// 添加查询参数
if (queryParams.length > 0) {
paramInterface += ` /** 查询参数 */\n`;
paramInterface += ` queryParams?: {\n`;
for (const param of queryParams) {
const paramType = this.getParamType(param);
const isRequired = (param as any).required ? '' : '?';
paramInterface += ` /** ${(param as any).description || (param as any).name} */\n`;
paramInterface += ` ${(param as any).name}${isRequired}: ${paramType};\n`;
}
paramInterface += ` };\n`;
}
// 添加请求体
if (requestBody) {
paramInterface += ` /** 请求数据 */\n`;
paramInterface += ` data${requestBodyRequired ? '' : '?'}: ${requestBodyType};\n`;
}
// 添加头部参数
if (headerParams.length > 0) {
paramInterface += ` /** 头部参数 */\n`;
paramInterface += ` headers?: {\n`;
for (const param of headerParams) {
const paramType = this.getParamType(param);
const isRequired = (param as any).required ? '' : '?';
paramInterface += ` /** ${(param as any).description || (param as any).name} */\n`;
paramInterface += ` ${(param as any).name}${isRequired}: ${paramType};\n`;
}
paramInterface += ` };\n`;
}
paramInterface += `}\n\n`;
// 生成注释文档
let commentDoc = '/**\n';
for (const comment of comments) {
commentDoc += ` * ${comment}\n`;
}
commentDoc += ` * @param params 请求参数\n`;
commentDoc += ` * @returns ${responseType} 响应结果\n`;
commentDoc += ` */\n`;
// 根据不同的客户端类型生成不同的API函数
if (clientType === 'axios') {
return this.generateAxiosFunction(
functionName,
method,
path,
paramInterfaceName,
responseType,
paramInterface,
commentDoc
);
} else if (clientType === 'fetch') {
return this.generateFetchFunction(
functionName,
method,
path,
paramInterfaceName,
responseType,
paramInterface,
commentDoc
);
} else if (clientType === 'react-query') {
return this.generateReactQueryFunction(
functionName,
method,
path,
paramInterfaceName,
responseType,
paramInterface,
commentDoc
);
}
return '';
}
/**
* 生成Axios API函数
*/
private generateAxiosFunction(
functionName: string,
method: string,
path: string,
paramInterfaceName: string,
responseType: string,
paramInterface: string,
commentDoc: string
): string {
// 构建URL模板
const urlTemplate = this.generateUrlTemplate(path);
// 构建函数体
const functionBody = `
${paramInterface}${commentDoc}export async function ${functionName}(params: ${paramInterfaceName}): Promise<${responseType}> {
// 替换URL中的路径参数
let url = \`${urlTemplate}\`;
// 设置请求配置
const config: any = {
method: '${method.toUpperCase()}',
url,
};
// 设置查询参数
if (params.queryParams) {
config.params = params.queryParams;
}
// 设置请求体
if (params.data) {
config.data = params.data;
}
// 设置头部参数
if (params.headers) {
config.headers = params.headers;
}
// 发送请求
const response = await axiosInstance(config);
return response.data;
}
`;
return functionBody.trim();
}
/**
* 生成Fetch API函数
*/
private generateFetchFunction(
functionName: string,
method: string,
path: string,
paramInterfaceName: string,
responseType: string,
paramInterface: string,
commentDoc: string
): string {
// 构建URL模板
const urlTemplate = this.generateUrlTemplate(path);
// 构建函数体
const functionBody = `
${paramInterface}${commentDoc}export async function ${functionName}(params: ${paramInterfaceName}): Promise<${responseType}> {
// 替换URL中的路径参数
let url = \`${urlTemplate}\`;
// 构建查询参数
if (params.queryParams) {
const queryString = new URLSearchParams(
Object.entries(params.queryParams)
.filter(([_, value]) => value !== undefined)
.map(([key, value]) => [key, String(value)])
).toString();
if (queryString) {
url += \`?\${queryString}\`;
}
}
// 设置请求选项
const options: RequestInit = {
method: '${method.toUpperCase()}',
};
// 设置请求体
if (params.data) {
options.body = JSON.stringify(params.data);
options.headers = {
...options.headers,
'Content-Type': 'application/json',
};
}
// 设置头部参数
if (params.headers) {
options.headers = {
...options.headers,
...params.headers,
};
}
// 发送请求
const response = await fetchWithConfig(url, options);
return response.json();
}
`;
return functionBody.trim();
}
/**
* 生成React Query函数
*/
private generateReactQueryFunction(
functionName: string,
method: string,
path: string,
paramInterfaceName: string,
responseType: string,
paramInterface: string,
commentDoc: string
): string {
// 构建URL模板
const urlTemplate = this.generateUrlTemplate(path);
// 判断是否是查询方法
const isQuery = ['get'].includes(method.toLowerCase());
let functionBody = `${paramInterface}`;
// 首先生成基础API函数
functionBody += `${commentDoc}export async function ${functionName}Raw(params: ${paramInterfaceName}): Promise<${responseType}> {
// 替换URL中的路径参数
let url = \`${urlTemplate}\`;
// 设置请求配置
const config: any = {
method: '${method.toUpperCase()}',
url,
};
// 设置查询参数
if (params.queryParams) {
config.params = params.queryParams;
}
// 设置请求体
if (params.data) {
config.data = params.data;
}
// 设置头部参数
if (params.headers) {
config.headers = params.headers;
}
// 发送请求
const response = await axiosInstance(config);
return response.data;
}\n\n`;
// 然后根据HTTP方法生成React Query钩子
if (isQuery) {
// 为GET请求生成useQuery钩子
functionBody += `/**
* 使用React Query的${functionName}查询钩子
* @param params 请求参数
* @param options React Query选项
*/
export function use${this.capitalize(functionName)}(
params: ${paramInterfaceName},
options?: UseQueryOptions<${responseType}, Error>
) {
return useQuery<${responseType}, Error>(
// 查询键,用于缓存和依赖更新
['${functionName}', params],
// 查询函数
() => ${functionName}Raw(params),
// 附加选项
options
);
}\n`;
} else {
// 为非GET请求生成useMutation钩子
functionBody += `/**
* 使用React Query的${functionName}变更钩子
* @param options React Query选项
*/
export function use${this.capitalize(functionName)}(
options?: UseMutationOptions<${responseType}, Error, ${paramInterfaceName}>
) {
return useMutation<${responseType}, Error, ${paramInterfaceName}>(
// 变更函数
(params) => ${functionName}Raw(params),
// 附加选项
options
);
}\n`;
}
return functionBody.trim();
}
/**
* 生成URL模板,将路径参数替换为模板变量
*/
private generateUrlTemplate(path: string): string {
// 替换路径参数,从{paramName}格式转换为ES6模板字符串${params.paramName}
return path.replace(/{([^}]+)}/g, '${params.$1}');
}
/**
* 获取参数类型
*/
private getParamType(param: any): string {
if (!param.schema) return 'any';
const schema = param.schema;
// 处理引用
if (schema.$ref) {
const refParts = schema.$ref.split('/');
return refParts[refParts.length - 1];
}
return this.mapSwaggerTypeToTS(schema);
}
/**
* 映射Swagger类型到TypeScript类型
*/
private mapSwaggerTypeToTS(schema: OpenAPIV3.SchemaObject): string {
switch (schema.type) {
case 'integer':
case 'number':
return 'number';
case 'boolean':
return 'boolean';
case 'string':
if (schema.format === 'date' || schema.format === 'date-time') {
return 'string'; // 或者可以使用 'Date'
}
return 'string';
case 'array':
if (schema.items) {
const itemSchema = schema.items as OpenAPIV3.SchemaObject;
return `${this.mapSwaggerTypeToTS(itemSchema)}[]`;
}
return 'any[]';
case 'object':
if (schema.properties) {
const props = Object.entries(schema.properties).map(([key, prop]) => {
const isRequired = schema.required?.includes(key) ? '' : '?';
return `${key}${isRequired}: ${this.mapSwaggerTypeToTS(prop as OpenAPIV3.SchemaObject)}`;
});
return `{ ${props.join('; ')} }`;
}
return 'Record<string, any>';
default:
return 'any';
}
}
/**
* 格式化操作ID
*/
private formatOperationId(operationId: string): string {
// 移除空格和特殊字符
let formatted = operationId.replace(/[^a-zA-Z0-9]/g, ' ');
// 转换为驼峰命名
formatted = formatted
.split(' ')
.filter(Boolean)
.map((word, index) =>
index === 0
? word.toLowerCase()
: word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
)
.join('');
return formatted;
}
/**
* 首字母大写
*/
private capitalize(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1);
}
/**
* 生成客户端配置代码
*/
private generateClientConfigCode(clientType: 'axios' | 'fetch'): string {
if (clientType === 'axios') {
return `import axios from 'axios';
/**
* 全局Axios实例配置
*/
export const axiosInstance = axios.create({
baseURL: process.env.API_BASE_URL || '',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
});
/**
* 请求拦截器
*/
axiosInstance.interceptors.request.use(
(config) => {
// 在发送请求前做些什么
// 例如,添加身份验证令牌
// const token = localStorage.getItem('token');
// if (token) {
// config.headers.Authorization = \`Bearer \${token}\`;
// }
return config;
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
/**
* 响应拦截器
*/
axiosInstance.interceptors.response.use(
(response) => {
// 对响应数据做些什么
return response;
},
(error) => {
// 对响应错误做些什么
// 例如,处理401未授权错误
// if (error.response && error.response.status === 401) {
// // 重定向到登录页面或刷新令牌
// }
return Promise.reject(error);
}
);`;
} else if (clientType === 'fetch') {
return `/**
* 全局Fetch配置
*/
const defaultConfig: RequestInit = {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
credentials: 'same-origin',
};
/**
* 扩展的Fetch函数,包含默认配置和错误处理
*/
export async function fetchWithConfig(
url: string,
options?: RequestInit
): Promise<Response> {
// 合并配置
const config = {
...defaultConfig,
...options,
headers: {
...defaultConfig.headers,
...options?.headers,
},
};
// 添加身份验证令牌
// const token = localStorage.getItem('token');
// if (token) {
// config.headers.Authorization = \`Bearer \${token}\`;
// }
// 添加基础URL
const baseUrl = process.env.API_BASE_URL || '';
const fullUrl = url.startsWith('http') ? url : \`\${baseUrl}\${url}\`;
// 发送请求
const response = await fetch(fullUrl, config);
// 检查响应状态
if (!response.ok) {
// 处理HTTP错误
const error = await response.text();
throw new Error(\`HTTP error \${response.status}: \${error}\`);
}
return response;
}`;
}
return '';
}
}