bytefun
Version:
一个打通了原型设计、UI设计与代码转换、跨平台原生代码开发等的平台
350 lines (288 loc) • 11.2 kB
text/typescript
export function gen(filePath: string) {
// 输出目录
// const outputPath = 'F:\\project\\alice\\extract-ast\\src\\test\\other\\backendApi';
const outputPath = 'D:\\project\\alice\\extract-ast2\\src\\test\\other\\backendApi';
execGen(filePath, outputPath);
}
export function execGen(filePath: string, outputPath: string) {
let jsonAry: any[] = [];
try {
jsonAry = extractJsonFromFile(filePath);
} catch (e) {
console.error('解析JSON文件失败:', e);
jsonAry = [];
}
if (jsonAry.length < 1) {
return;
}
// 确保输出目录存在且包含 BaseApi.ts 文件
ensureDirAndBaseApi(outputPath);
// 创建或更新后端API使用说明.md文件
createOrUpdateApiDoc(outputPath, jsonAry);
// api ts 文件
processJsonArray(outputPath, jsonAry);
}
/**
* 确保目录存在并在其中创建 BaseApi.ts 文件(如果不存在)
* @param dirPath 文件夹路径
*/
export function ensureDirAndBaseApi(dirPath: string): void {
const fs = require('fs');
const path = require('path');
// 如果传入的文件夹路径对应的目录不存在,则创建对应的目录
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
// 检查 BaseApi.ts 文件是否存在,如果不存在则创建
const baseApiFilePath = path.join(dirPath, 'BaseApi.ts');
if (!fs.existsSync(baseApiFilePath)) {
const baseApiContent = 'export default class BaseApi {}\n';
fs.writeFileSync(baseApiFilePath, baseApiContent);
}
}
/**
* 处理文件夹路径和 jsonArray,生成对应的 TypeScript 文件
* @param dirPath 文件夹路径
* @param jsonArray extractJsonFromFile 函数的返回结果
*/
export function processJsonArray(dirPath: string, jsonArray: any[]): void {
const fs = require('fs');
const path = require('path');
// 如果传入的文件夹路径对应的目录不存在,则创建对应的目录
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
// 遍历 jsonArray 中的每个元素
jsonArray.forEach(jsonObj => {
// 获取 moduleEnName 作为子文件夹名称
const moduleEnName = jsonObj.moduleEnName;
const moduleDirPath = path.join(dirPath, moduleEnName);
// 创建子文件夹
if (!fs.existsSync(moduleDirPath)) {
fs.mkdirSync(moduleDirPath, { recursive: true });
}
// 生成 ts 文件内容
const tsContent = generateTsContent(jsonObj);
// 写入文件
const fileName = `${jsonObj.apiEnName}.ts`;
const filePath = path.join(moduleDirPath, fileName);
fs.writeFileSync(filePath, tsContent);
});
}
/**
* 创建或更新“后端API使用说明.md”文件
* @param dirPath 文件夹路径
* @param jsonArray extractJsonFromFile 函数的返回结果
*/
export function createOrUpdateApiDoc(dirPath: string, jsonArray: any[]): void {
const fs = require('fs');
const path = require('path');
// 如果传入的文件夹路径对应的目录不存在,则创建对应的目录
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
// 定义 md 文件路径 后端API使用说明.md->backendApiInfo.md
const mdFilePath = path.join(dirPath, 'backendApiInfo.md');
// 初始化 md 文件内容
let mdContent = '# 后端API使用说明\n\n';
// 按 moduleEnName 分组
const moduleGroups: { [key: string]: any[] } = {};
jsonArray.forEach(jsonObj => {
const moduleEnName = jsonObj.moduleEnName || '未命名模块';
if (!moduleGroups[moduleEnName]) {
moduleGroups[moduleEnName] = [];
}
moduleGroups[moduleEnName].push(jsonObj);
});
// 生成每个模块的内容
Object.keys(moduleGroups).forEach(moduleEnName => {
const moduleItems = moduleGroups[moduleEnName];
const firstItem = moduleItems[0];
// 添加模块标题
if (firstItem.moduleName) {
mdContent += `## src/backendApi/${moduleEnName}文件夹(${firstItem.moduleName})里面的\n`;
} else {
mdContent += `## src/backendApi/${moduleEnName}文件夹里面的\n`;
}
// 添加每个接口项
moduleItems.forEach((jsonObj, index) => {
const idx = index + 1;
mdContent += `${idx}. ${jsonObj.apiEnName}.ts: ${jsonObj.name}接口\n`;
});
mdContent += '\n';
});
// 写入文件
fs.writeFileSync(mdFilePath, mdContent);
}
/**
* 递归处理类型转换,包括array类型的元素处理
* @param typeInfo 类型信息对象
* @returns 转换后的类型字符串
*/
function processTypeRecursively(typeInfo: any): string {
if (typeof typeInfo === 'string') {
return typeInfo === 'integer' ? 'number' : typeInfo;
}
if (!typeInfo || !typeInfo.type) {
return 'any';
}
const baseType = typeInfo.type === 'integer' ? 'number' : typeInfo.type;
if (baseType === 'array') {
// 处理数组类型,递归处理元素类型
if (typeInfo.items) {
const itemType = processTypeRecursively(typeInfo.items);
if (typeInfo.items.type === 'object' && typeInfo.items.properties) {
// 对象数组使用 [{ ... }] 格式
return `[
${itemType}
]`;
} else {
// 基础类型数组使用 type[] 格式
return `${itemType}[]`;
}
}
return 'any[]';
}
if (baseType === 'object' && typeInfo.properties) {
// 处理对象类型,递归处理属性
let objectType = '{\n';
Object.keys(typeInfo.properties).forEach(key => {
const prop = typeInfo.properties[key];
const propType = processTypeRecursively(prop);
const description = prop.description || '';
objectType += ` ${key}: ${propType} // ${description}\n`;
});
objectType += ' }';
return objectType;
}
return baseType;
}
/**
* 将 integer 类型转换为 number 类型(保持向后兼容)
* @param type 原始类型
* @returns 转换后的类型
*/
function convertIntegerToNumber(type: string): string {
return type === 'integer' ? 'number' : type;
}
/**
* 根据 jsonObj 生成 ts 文件内容
* @param jsonObj JSON 对象
* @returns 生成的 ts 文件内容
*/
function generateTsContent(jsonObj: any): string {
const { moduleEnName, apiEnName, name, parameters, responses } = jsonObj;
// 生成请求数据接口
let requestInterface = `// ${moduleEnName}-${name}接口请求数据
export interface ${apiEnName}Request {
`;
if (parameters && Array.isArray(parameters)) {
parameters.forEach(param => {
const optional = param.required === 'true' ? '' : '?';
const processedType = processTypeRecursively(param);
const description = param.description || '';
requestInterface += ` ${param.name}${optional}: ${processedType} // ${description}
`;
});
}
requestInterface += `}
`;
// 生成返回数据接口
let responseInterface = `// ${moduleEnName}-${name}接口返回数据
export interface ${apiEnName}Response {
code: number
msg: string
data: `;
// 处理 data 属性
if (responses && responses.properties && responses.properties.data) {
const dataProp = responses.properties.data;
const processedType = processTypeRecursively(dataProp);
const description = dataProp.description || '';
if (dataProp.type === 'object' || dataProp.type === 'array') {
// 对象或数组类型,直接使用递归处理的结果
responseInterface += `${processedType}
`;
} else {
// 基础类型,添加注释
responseInterface += `${processedType} // ${description}
`;
}
} else {
responseInterface += `any
`;
}
responseInterface += `}
`;
// 生成失败返回数据接口
const failInterface = `// ${moduleEnName}-${name}接口失败返回数据
export interface apiFailInfo {
msg: string
code: number
}
`;
// 生成类定义
const classDefinition = `// ${moduleEnName}-${name}接口
export default class ${apiEnName} extends BaseApi {
public static request_ArtuX1d8a6A(
request: ${apiEnName}Request,
success: (res: ${apiEnName}Response) => void,
fail: (error: apiFailInfo) => void
): void { }
}
`;
// 组合所有内容
return `import BaseApi from '../BaseApi'
${requestInterface}${responseInterface}${failInterface}${classDefinition}`;
}
/**
* 从JSON配置文件中提取接口列表
* @param filePath JSON文件路径
* @returns 返回解析后的API数组,每个API包含模块信息
*/
export function extractJsonFromFile(filePath: string): any[] {
const fs = require('fs');
if (!fs.existsSync(filePath)) {
throw new Error(`文件不存在: ${filePath}`);
}
const content = fs.readFileSync(filePath, 'utf-8');
if (!content || content.length < 10) {
throw new Error(`文件内容为空: ${filePath}`);
}
try {
const configData = JSON.parse(content);
if (!configData.moduleList || !Array.isArray(configData.moduleList)) {
throw new Error('配置文件格式错误:缺少 moduleList 或 moduleList 不是数组');
}
const apiArray: any[] = [];
// 遍历每个模块
configData.moduleList.forEach((module: any) => {
const {
moduleFolder,
moduleEnName,
moduleCnName,
description: moduleDescription,
apiList
} = module;
if (!apiList || !Array.isArray(apiList)) {
console.warn(`模块 ${moduleEnName} 没有 apiList 或 apiList 不是数组`);
return;
}
// 遍历模块下的每个API
apiList.forEach((api: any) => {
// 将模块信息合并到API对象中
const apiWithModuleInfo = {
...api,
moduleFolder,
moduleEnName,
moduleName: moduleCnName, // 保持兼容性,使用 moduleName 字段
moduleCnName,
moduleDescription
};
apiArray.push(apiWithModuleInfo);
});
});
return apiArray;
} catch (e) {
throw new Error(`JSON解析失败: ${filePath}, error: ${(e as Error).message}`);
}
}