|
@@ -0,0 +1,643 @@
|
|
|
|
+/**
|
|
|
|
+ * 根据 Swagger json 自动生成接口服务文件
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+const config = require('./config');
|
|
|
|
+
|
|
|
|
+// 输出文件及目录定义
|
|
|
|
+const ROOT_PATH = './src/services';
|
|
|
|
+const ENUM_FILE = `${ROOT_PATH}/enums.ts`;
|
|
|
|
+const TYPING_D = `${ROOT_PATH}/typing.d.ts`;
|
|
|
|
+const API_PATH = `${ROOT_PATH}/apis`;
|
|
|
|
+
|
|
|
|
+/** 数值类型映射 */
|
|
|
|
+const IntegerMapping = {
|
|
|
|
+ int16: 'number',
|
|
|
|
+ int32: 'number',
|
|
|
|
+ int64: 'string',
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** 返回结果类型映射 */
|
|
|
|
+const ResultMapping = {
|
|
|
|
+ Int16: 'number',
|
|
|
|
+ Int32: 'number',
|
|
|
|
+ Int64: 'string',
|
|
|
|
+ Boolean: 'boolean',
|
|
|
|
+ Object: 'any',
|
|
|
|
+ String: 'string',
|
|
|
|
+ DateTime: 'string',
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** 统一注释 */
|
|
|
|
+const UnifyComment = `// @ts-ignore\n/* eslint-disable */\n\n// 该文件自动生成,请勿手动修改!`;
|
|
|
|
+
|
|
|
|
+// ----------------------------------------
|
|
|
|
+
|
|
|
|
+// 执行外部命令
|
|
|
|
+const execSync = require('child_process').execSync;
|
|
|
|
+// 文件操作
|
|
|
|
+const fs = require('fs');
|
|
|
|
+
|
|
|
|
+// ----------------------------------------
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 下划线中横线分隔字符串转换为驼峰格式
|
|
|
|
+ * @param {string} value 待转换字符串
|
|
|
|
+ * @param {boolean} [capitalize] 首字母大写
|
|
|
|
+ * @returns
|
|
|
|
+ */
|
|
|
|
+const kebabToCamelCase = (value, capitalize = false) => {
|
|
|
|
+ let n = value.replace(/[_-][a-zA-z]/g, (str) => str.substr(-1).toUpperCase()).trim() || '';
|
|
|
|
+ if (capitalize && n.length != 0) {
|
|
|
|
+ n = n.slice(0, 1).toUpperCase() + n.slice(1);
|
|
|
|
+ }
|
|
|
|
+ return n;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 根据路径生成操作名称
|
|
|
|
+ * @param {string} path 接口路径
|
|
|
|
+ * @returns 取路径最后一级的名称
|
|
|
|
+ */
|
|
|
|
+const getActionName = (path) => {
|
|
|
|
+ const pp = path.split('/');
|
|
|
|
+ if (pp.length == 0) {
|
|
|
|
+ return '';
|
|
|
|
+ }
|
|
|
|
+ const n = pp[pp.length - 1];
|
|
|
|
+ let an = kebabToCamelCase(n);
|
|
|
|
+ if (['import', 'delete'].includes(an.toLocaleLowerCase())) {
|
|
|
|
+ an = `${an}Action`;
|
|
|
|
+ }
|
|
|
|
+ return an;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 生成控制器名称
|
|
|
|
+ * @param {string} name Swagger的tag名称
|
|
|
|
+ * @returns
|
|
|
|
+ */
|
|
|
|
+const getControllerName = (name) => {
|
|
|
|
+ const n = kebabToCamelCase(name, true);
|
|
|
|
+ return `${n}Controller`;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 清除文件
|
|
|
|
+ * @param {*} dir 需要清空的目录
|
|
|
|
+ */
|
|
|
|
+const clearFiles = (dir) => {
|
|
|
|
+ const oldFiles = fs.readdirSync(dir);
|
|
|
|
+ oldFiles.forEach((f) => {
|
|
|
|
+ const filePath = `${dir}/${f}`;
|
|
|
|
+ const s = fs.statSync(filePath);
|
|
|
|
+ if (s.isDirectory()) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ fs.unlinkSync(filePath);
|
|
|
|
+ });
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** 获取引用类型 */
|
|
|
|
+const getRefEntity = ($ref) => {
|
|
|
|
+ const rs = $ref.split('/');
|
|
|
|
+ if (rs.length == 0) {
|
|
|
|
+ return 'any';
|
|
|
|
+ }
|
|
|
|
+ return rs[rs.length - 1];
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** 获取简单类型 */
|
|
|
|
+const getSimpleType = ({ type, format }) => {
|
|
|
|
+ if (!type) {
|
|
|
|
+ return 'any';
|
|
|
|
+ }
|
|
|
|
+ const ltype = type.toLowerCase();
|
|
|
|
+ if (['string', 'boolean', 'number'].includes(type)) {
|
|
|
|
+ return ltype;
|
|
|
|
+ }
|
|
|
|
+ if (ltype == 'integer') {
|
|
|
|
+ return IntegerMapping[format] ?? 'number';
|
|
|
|
+ }
|
|
|
|
+ return 'any';
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** 获取响应结果类型 */
|
|
|
|
+const getResultType = (type) => {
|
|
|
|
+ const t = ResultMapping[type];
|
|
|
|
+ if (t) {
|
|
|
|
+ return t;
|
|
|
|
+ }
|
|
|
|
+ return `API.${type}`;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** 获取实体类型 */
|
|
|
|
+const getEntityType = (name, schema) => {
|
|
|
|
+ const props = schema.properties;
|
|
|
|
+
|
|
|
|
+ let entity = '';
|
|
|
|
+ const desc = schema.description;
|
|
|
|
+ if (desc) {
|
|
|
|
+ entity = `/** ${desc} */\n`;
|
|
|
|
+ }
|
|
|
|
+ entity = `${entity}type ${name} = {\n`;
|
|
|
|
+
|
|
|
|
+ const requiredList = schema.required ?? [];
|
|
|
|
+
|
|
|
|
+ for (const pname in props) {
|
|
|
|
+ const prop = props[pname];
|
|
|
|
+ let member = '';
|
|
|
|
+ const memberDesc = prop.description;
|
|
|
|
+ if (memberDesc) {
|
|
|
|
+ member = `/** ${memberDesc} */\n`;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let memberType = '';
|
|
|
|
+ if (prop['$ref']) {
|
|
|
|
+ memberType = `${getRefEntity(prop['$ref'])}`;
|
|
|
|
+ } else if (prop['items']) {
|
|
|
|
+ if (prop['items']['$ref']) {
|
|
|
|
+ memberType = `${getRefEntity(prop['items']['$ref'])}[]`;
|
|
|
|
+ } else {
|
|
|
|
+ memberType = `${getSimpleType(prop['items'])}[]`;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ memberType = `${getSimpleType(prop)}`;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const required = requiredList.includes(pname) || (prop.nullable !== undefined && !prop.nullable);
|
|
|
|
+
|
|
|
|
+ member = `${member}${pname}${required ? '' : '?'}: ${memberType};\n`;
|
|
|
|
+ entity = `${entity}${member}`;
|
|
|
|
+ }
|
|
|
|
+ entity = `${entity}}\n`;
|
|
|
|
+ return entity;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** 获取请求类型 */
|
|
|
|
+const getRequestBodyType = (reqBody) => {
|
|
|
|
+ if (reqBody['content']['application/json']) {
|
|
|
|
+ const schema = reqBody['content']['application/json']['schema'];
|
|
|
|
+ let tps = '';
|
|
|
|
+ if (schema['$ref']) {
|
|
|
|
+ tps = getRefEntity(schema['$ref']);
|
|
|
|
+ } else if (schema['items']) {
|
|
|
|
+ if (schema['items']['$ref']) {
|
|
|
|
+ tps = getRefEntity(schema['items']['$ref']);
|
|
|
|
+ } else {
|
|
|
|
+ tps = getSimpleType(schema['items']);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ tps = getSimpleType(schema);
|
|
|
|
+ }
|
|
|
|
+ if (schema['type'] == 'array') {
|
|
|
|
+ return `${tps}[]`;
|
|
|
|
+ }
|
|
|
|
+ return tps;
|
|
|
|
+ }
|
|
|
|
+ if (reqBody['content']['multipart/form-data']) {
|
|
|
|
+ return 'FormData';
|
|
|
|
+ }
|
|
|
|
+ return 'any';
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** 获取统一返回类型 */
|
|
|
|
+const getXnRestfulResultType = (propRef) => {
|
|
|
|
+ const rtype = getRefEntity(propRef).replaceAll('XnRestfulResult_', '');
|
|
|
|
+ const ps = rtype.split('_');
|
|
|
|
+ if (ps.length == 1) {
|
|
|
|
+ return getResultType(ps);
|
|
|
|
+ }
|
|
|
|
+ if (ps[0] == 'List') {
|
|
|
|
+ return `${getResultType(ps[1])}[]`;
|
|
|
|
+ }
|
|
|
|
+ if (ps[0] == 'PageResult') {
|
|
|
|
+ return `API.PageResponse<${getResultType(ps[1])}>`;
|
|
|
|
+ }
|
|
|
|
+ // return getResultType(ps[1]);
|
|
|
|
+ // return getResultType(ps[0]);
|
|
|
|
+ return getResultType(rtype);
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** 获取响应类型 */
|
|
|
|
+const getResponseType = (res) => {
|
|
|
|
+ const schema = res['200']?.['content']?.['application/json']?.['schema'];
|
|
|
|
+ if (!schema) {
|
|
|
|
+ return 'any';
|
|
|
|
+ }
|
|
|
|
+ if (schema['$ref']) {
|
|
|
|
+ return getXnRestfulResultType(schema['$ref']);
|
|
|
|
+ } else if (schema['items']) {
|
|
|
|
+ if (schema['items']['$ref']) {
|
|
|
|
+ return getXnRestfulResultType(schema['items']['$ref']);
|
|
|
|
+ } else {
|
|
|
|
+ return getSimpleType(schema['items']);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ return getSimpleType(schema);
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** 获取查询参数类型 */
|
|
|
|
+const getParamsType = (parameters) => {
|
|
|
|
+ let pb = '';
|
|
|
|
+ for (const p of parameters) {
|
|
|
|
+ const pdesc = p.description ?? p.name;
|
|
|
|
+ pb = `${pb}/** ${pdesc} */\n${p.name}${p.required ? '' : '?'}:`;
|
|
|
|
+
|
|
|
|
+ const prop = p.schema;
|
|
|
|
+ let ptype = '';
|
|
|
|
+ if (prop['$ref']) {
|
|
|
|
+ ptype = `${getRefEntity(prop['$ref'])}`;
|
|
|
|
+ } else if (prop['items']) {
|
|
|
|
+ if (prop['items']['$ref']) {
|
|
|
|
+ ptype = `${getRefEntity(prop['items']['$ref'])}[]`;
|
|
|
|
+ } else {
|
|
|
|
+ ptype = `${getSimpleType(prop['items'])}[]`;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ ptype = `${getSimpleType(prop)}`;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pb = `${pb}${ptype};\n`;
|
|
|
|
+ }
|
|
|
|
+ return `{\n${pb}}`;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** 判断是否简单类型 */
|
|
|
|
+const isSimpleType = (type) => {
|
|
|
|
+ return ['string', 'number', 'boolean', 'object', 'any', 'formdata'].includes(type.toLowerCase());
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** 获取 tag 描述字典 */
|
|
|
|
+const getTagDescDict = (tags) => {
|
|
|
|
+ let tagDict = {};
|
|
|
|
+ for (const tag of tags) {
|
|
|
|
+ tagDict[tag.name] = tag.description ?? tag.name;
|
|
|
|
+ }
|
|
|
|
+ return tagDict;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// ----------------------------------------
|
|
|
|
+
|
|
|
|
+/** 生成枚举 */
|
|
|
|
+const generateEnums = (schemas) => {
|
|
|
|
+ const enums = [];
|
|
|
|
+ const importEnums = [];
|
|
|
|
+ for (let ek in schemas) {
|
|
|
|
+ if (schemas[ek].enum) {
|
|
|
|
+ const desc = schemas[ek].description;
|
|
|
|
+ if (!desc) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ const s1 = desc.replaceAll('<br />', '').split(' ');
|
|
|
|
+ if (s1 && s1.length > 1) {
|
|
|
|
+ const enumComment = s1?.[0];
|
|
|
|
+ const items = [];
|
|
|
|
+ for (let j = 1; j < s1.length; j++) {
|
|
|
|
+ const s2 = s1[j].split(' ');
|
|
|
|
+ if (s2.length > 3) {
|
|
|
|
+ const itemComent = s2[0];
|
|
|
|
+ const itemCode = s2[1];
|
|
|
|
+ const itemValue = s2[3];
|
|
|
|
+ items.push(`\n /** ${itemComent} */\n ${itemCode} = ${itemValue},`);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ enums.push(`\n/** ${enumComment} */\nexport enum ${ek} {${items.join('')}\n}`);
|
|
|
|
+ importEnums.push(ek);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const fs = require('fs');
|
|
|
|
+ const es = enums.join('\n');
|
|
|
|
+ const ies = importEnums.map((t) => ` ${t}`).join(',\n');
|
|
|
|
+ fs.writeFileSync(ENUM_FILE, `${UnifyComment}\n\n${es}\n\nexport default {\n${ies},\n};\n`);
|
|
|
|
+ execSync(`prettier --write ${ENUM_FILE}`);
|
|
|
|
+ return importEnums;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** 生成类型定义文件 */
|
|
|
|
+const generateEntity = (schemas, enums) => {
|
|
|
|
+ // 生成类型
|
|
|
|
+ let entities = [];
|
|
|
|
+ for (const name in schemas) {
|
|
|
|
+ const schema = schemas[name];
|
|
|
|
+ if (
|
|
|
|
+ name.includes('XnRestfulResult_') ||
|
|
|
|
+ name.includes('PageResult_') ||
|
|
|
|
+ schema.enum ||
|
|
|
|
+ !schema.properties
|
|
|
|
+ ) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // const entity = getEntityType(name.split('_')[0], schema);
|
|
|
|
+ // const ns = name.split('_');
|
|
|
|
+ // const entity = getEntityType(ns[ns.length - 1], schema);
|
|
|
|
+ const entity = getEntityType(name, schema);
|
|
|
|
+ entities.push(entity);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const fs = require('fs');
|
|
|
|
+ const ies = enums.map((t) => ` ${t}`).join(',\n');
|
|
|
|
+ const es = entities.join('\n');
|
|
|
|
+ fs.writeFileSync(
|
|
|
|
+ TYPING_D,
|
|
|
|
+ `${UnifyComment}
|
|
|
|
+
|
|
|
|
+ import {
|
|
|
|
+ ${ies},
|
|
|
|
+ } from './enums';
|
|
|
|
+
|
|
|
|
+ declare global {
|
|
|
|
+ declare namespace API {
|
|
|
|
+ /** 分页查询请求参数基类型 */
|
|
|
|
+ type PageQueryType = {
|
|
|
|
+ /** 当前页值 */
|
|
|
|
+ pageIndex: number;
|
|
|
|
+ /** 每页大小 */
|
|
|
|
+ pageSize: number;
|
|
|
|
+
|
|
|
|
+ /** 搜索值 */
|
|
|
|
+ searchValue?: string;
|
|
|
|
+ /** 搜索开始时间 */
|
|
|
|
+ searchBeginTime?: string;
|
|
|
|
+ /** 搜索结束时间 */
|
|
|
|
+ searchEndTime?: string;
|
|
|
|
+ /** 排序字段 */
|
|
|
|
+ sortField?: string;
|
|
|
|
+ /** 排序方法,默认升序,否则降序(配合antd前端,约定参数为 Ascend,Dscend) */
|
|
|
|
+ sortOrder?: string;
|
|
|
|
+ /** 降序排序(不要问我为什么是descend不是desc,前端约定参数就是这样) */
|
|
|
|
+ descStr?: string;
|
|
|
|
+ /** 复杂查询条件 */
|
|
|
|
+ searchParameters?: Condition[];
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ /** 后端服务请求返回参数 */
|
|
|
|
+ type ResponseType<T = any> = {
|
|
|
|
+ /** 执行成功 */
|
|
|
|
+ success?: boolean;
|
|
|
|
+ /** 状态码 */
|
|
|
|
+ code?: number;
|
|
|
|
+ /** 错误码 */
|
|
|
|
+ errorCode?: number;
|
|
|
|
+ /** 错误信息 */
|
|
|
|
+ errorMessage?: any;
|
|
|
|
+ /** 消息显示类型 */
|
|
|
|
+ showType?: number;
|
|
|
|
+ /** 数据 */
|
|
|
|
+ data?: T;
|
|
|
|
+ /** 附加数据 */
|
|
|
|
+ extras?: any;
|
|
|
|
+ /** 时间戳 */
|
|
|
|
+ timestamp?: number;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ /** 分页数据对象 */
|
|
|
|
+ type PageResponse<T = any> = {
|
|
|
|
+ /** 当前页值 */
|
|
|
|
+ pageIndex: number;
|
|
|
|
+ /** 每页大小 */
|
|
|
|
+ pageSize: number;
|
|
|
|
+ /** 数据总数 */
|
|
|
|
+ totalCount: number;
|
|
|
|
+ /** 总页数 */
|
|
|
|
+ totalPage?: number;
|
|
|
|
+ /** 数据行 */
|
|
|
|
+ items?: T[];
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ ${es}}
|
|
|
|
+ }`,
|
|
|
|
+ );
|
|
|
|
+ execSync(`prettier --write ${TYPING_D}`);
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** 生成控制器文件 */
|
|
|
|
+const generateController = (paths, tagDict, enums) => {
|
|
|
|
+ // 构造控制器
|
|
|
|
+ let controllers = {};
|
|
|
|
+ for (const apiPath in paths) {
|
|
|
|
+ const actionName = getActionName(apiPath);
|
|
|
|
+ const api = paths[apiPath];
|
|
|
|
+ for (const verb in api) {
|
|
|
|
+ // 只处理 post 和 get,其它忽略
|
|
|
|
+ if (!['post', 'get'].includes(verb)) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ const action = api[verb];
|
|
|
|
+ const actionDesc = action.summary ?? actionName;
|
|
|
|
+ // tag 作为控制器名称,如果没有 tag 则跳过
|
|
|
|
+ const tags = action.tags ?? [];
|
|
|
|
+ if (tags.length < 1) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ for (const tag of tags) {
|
|
|
|
+ const controllerName = getControllerName(tag);
|
|
|
|
+ if (!controllers.hasOwnProperty(controllerName)) {
|
|
|
|
+ controllers[controllerName] = {
|
|
|
|
+ description: tagDict[tag],
|
|
|
|
+ actions: [],
|
|
|
|
+ actionNames: [],
|
|
|
|
+ enums: [],
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ let paramsType = '';
|
|
|
|
+ let dataType = '';
|
|
|
|
+ let responseType = '';
|
|
|
|
+ if (action.parameters) {
|
|
|
|
+ paramsType = getParamsType(action.parameters);
|
|
|
|
+ }
|
|
|
|
+ if (action.requestBody) {
|
|
|
|
+ dataType = getRequestBodyType(action.requestBody);
|
|
|
|
+ }
|
|
|
|
+ if (action.responses) {
|
|
|
|
+ responseType = getResponseType(action.responses);
|
|
|
|
+ }
|
|
|
|
+ const es = enums.filter(t => paramsType.indexOf(t) !== -1 || dataType.indexOf(t) !== -1 || responseType.indexOf(t) !== -1);
|
|
|
|
+ if (es.length > 0) {
|
|
|
|
+ controllers[controllerName].enums.push(es[0]);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const actionFullDesc = `/** ${actionDesc} ${verb.toUpperCase()} ${apiPath} */\n`;
|
|
|
|
+ let actionBody = actionFullDesc;
|
|
|
|
+ // export api
|
|
|
|
+ if (actionName.toLowerCase().includes('export') || actionName.toLowerCase().includes('download')) {
|
|
|
|
+ actionBody = `${actionFullDesc}export async function ${actionName}(`;
|
|
|
|
+ if (verb == 'get') {
|
|
|
|
+ if (paramsType != '') {
|
|
|
|
+ actionBody = `${actionBody}params:${paramsType},`;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ if (paramsType != '') {
|
|
|
|
+ actionBody = `${actionBody}params:${paramsType},`;
|
|
|
|
+ }
|
|
|
|
+ if (dataType != '') {
|
|
|
|
+ const prefix = isSimpleType(dataType) ? '' : 'API.';
|
|
|
|
+ actionBody = `${actionBody}data:${prefix}${dataType},`;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ actionBody = `${actionBody}options?:{[key:string]:any}){\n`;
|
|
|
|
+ actionBody = `${actionBody}const url='${apiPath}';\n`;
|
|
|
|
+ actionBody = `${actionBody}const config={method:'${verb.toUpperCase()}',`;
|
|
|
|
+ if (verb == 'get' && paramsType != '') {
|
|
|
|
+ actionBody = `${actionBody}params,`;
|
|
|
|
+ }
|
|
|
|
+ if (verb == 'post') {
|
|
|
|
+ let pbv = '';
|
|
|
|
+ if (paramsType != '') {
|
|
|
|
+ pbv = 'params,';
|
|
|
|
+ }
|
|
|
|
+ if (dataType != '') {
|
|
|
|
+ pbv = `${pbv}data,`;
|
|
|
|
+ }
|
|
|
|
+ actionBody = `${actionBody}${pbv}`;
|
|
|
|
+ }
|
|
|
|
+ actionBody = `${actionBody}...(options||{}), responseType: 'blob', getResponse: true} as RequestOptions;\n`;
|
|
|
|
+ actionBody = `${actionBody}const res = await request(url, config);\n`;
|
|
|
|
+ actionBody = `${actionBody}const hcd = res.request.getResponseHeader('Content-Disposition');\n`;
|
|
|
|
+ actionBody = `${actionBody}let fileName = '';\n`;
|
|
|
|
+ actionBody = `${actionBody}const cd = contentDisposition.parse(hcd)\n`;
|
|
|
|
+ actionBody = `${actionBody}if (cd?.parameters?.filename) { fileName = cd?.parameters?.filename; }\n`;
|
|
|
|
+ actionBody = `${actionBody}return { fileName, data:res.data };\n}\n`;
|
|
|
|
+ }
|
|
|
|
+ // other api
|
|
|
|
+ else {
|
|
|
|
+ actionBody = `${actionFullDesc}export async function ${actionName}(`;
|
|
|
|
+ if (verb == 'get') {
|
|
|
|
+ if (paramsType != '') {
|
|
|
|
+ actionBody = `${actionBody}params:${paramsType},`;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ if (paramsType != '') {
|
|
|
|
+ actionBody = `${actionBody}params:${paramsType},`;
|
|
|
|
+ }
|
|
|
|
+ if (dataType != '') {
|
|
|
|
+ const prefix = isSimpleType(dataType) ? '' : 'API.';
|
|
|
|
+ actionBody = `${actionBody}data:${prefix}${dataType},`;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ actionBody = `${actionBody}options?:{[key:string]:any}){\n`;
|
|
|
|
+ actionBody = `${actionBody}const url='${apiPath}';\n`;
|
|
|
|
+ actionBody = `${actionBody}const config={method:'${verb.toUpperCase()}',`;
|
|
|
|
+ if (verb == 'get' && paramsType != '') {
|
|
|
|
+ actionBody = `${actionBody}params,`;
|
|
|
|
+ }
|
|
|
|
+ if (verb == 'post') {
|
|
|
|
+ let pbv = '';
|
|
|
|
+ if (paramsType != '') {
|
|
|
|
+ pbv = 'params,';
|
|
|
|
+ }
|
|
|
|
+ if (dataType != '') {
|
|
|
|
+ pbv = `${pbv}data,`;
|
|
|
|
+ }
|
|
|
|
+ actionBody = `${actionBody}${pbv}`;
|
|
|
|
+ }
|
|
|
|
+ actionBody = `${actionBody}...(options||{})};\n`;
|
|
|
|
+ actionBody = `${actionBody}const res = await request<API.ResponseType<${responseType}>>(url, config);`;
|
|
|
|
+ actionBody = `${actionBody}return res?.data;\n}\n`;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ controllers[controllerName].actions.push(actionBody);
|
|
|
|
+ controllers[controllerName].actionNames.push(`${actionFullDesc}${actionName},\n`);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 生成控制器文件
|
|
|
|
+ let csnames = [];
|
|
|
|
+ for (let ck in controllers) {
|
|
|
|
+ const c = controllers[ck];
|
|
|
|
+ const es = [...new Set(c.enums)].map((t) => `${t}, `);
|
|
|
|
+ let importEnums = '';
|
|
|
|
+ if (es.length > 0) {
|
|
|
|
+ importEnums = `\nimport { ${es.join('')} } from '../enums';`;
|
|
|
|
+ }
|
|
|
|
+ csnames.push({ name: ck, description: c.description });
|
|
|
|
+ const controllerFile = `${API_PATH}/${ck}.ts`;
|
|
|
|
+
|
|
|
|
+ const isBolb = c.actionNames.findIndex(t => t.includes('export') || t.includes('download')) !== -1;
|
|
|
|
+
|
|
|
|
+ fs.writeFileSync(
|
|
|
|
+ controllerFile,
|
|
|
|
+ `${UnifyComment}
|
|
|
|
+
|
|
|
|
+ // --------------------------------------------------------------------------
|
|
|
|
+ // ${c.description}
|
|
|
|
+ // --------------------------------------------------------------------------
|
|
|
|
+
|
|
|
|
+ import { request${isBolb ? ', RequestOptions' : ''} } from '@umijs/max';${importEnums}
|
|
|
|
+ ${isBolb ? 'import contentDisposition from \'content-disposition\';' : ''}
|
|
|
|
+
|
|
|
|
+ ${c.actions.join('\n')}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ export default {
|
|
|
|
+ ${c.actionNames.join('')}
|
|
|
|
+ };
|
|
|
|
+ `,
|
|
|
|
+ );
|
|
|
|
+ execSync(`prettier --write ${controllerFile}`);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 生成index.ts
|
|
|
|
+ const ecs = csnames.map((t) => `import * as ${t.name} from './${t.name}';\n`);
|
|
|
|
+ const edefaults = csnames.map((t) => `/** ${t.description} */\n${t.name},\n`);
|
|
|
|
+ const indexFile = `${API_PATH}/index.ts`;
|
|
|
|
+ fs.writeFileSync(
|
|
|
|
+ indexFile,
|
|
|
|
+ `${UnifyComment}
|
|
|
|
+
|
|
|
|
+ ${ecs.join('')}
|
|
|
|
+
|
|
|
|
+ export default {
|
|
|
|
+ ${edefaults.join('')}
|
|
|
|
+ };
|
|
|
|
+ `,
|
|
|
|
+ );
|
|
|
|
+ execSync(`prettier --write ${indexFile}`);
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// ----------------------------------------
|
|
|
|
+
|
|
|
|
+/** 生成接口服务 */
|
|
|
|
+const generate = (error, response, body) => {
|
|
|
|
+ // OpenApi 描述文件
|
|
|
|
+ const swagger = JSON.parse(body);
|
|
|
|
+ // 实体模型
|
|
|
|
+ const schemas = swagger['components']['schemas'];
|
|
|
|
+ // API路径
|
|
|
|
+ const apiPaths = swagger['paths'];
|
|
|
|
+
|
|
|
|
+ // 开始处理
|
|
|
|
+ console.info('生成后台接口文件');
|
|
|
|
+ console.info(`-----------------\n${new Date().toLocaleString()}\n-----------------\n清理文件`);
|
|
|
|
+ clearFiles(ROOT_PATH);
|
|
|
|
+ if (!fs.existsSync(API_PATH)) {
|
|
|
|
+ fs.mkdirSync(API_PATH);
|
|
|
|
+ } else {
|
|
|
|
+ clearFiles(API_PATH);
|
|
|
|
+ }
|
|
|
|
+ console.info('清理完成!');
|
|
|
|
+
|
|
|
|
+ console.log('-----------------\n开始生成');
|
|
|
|
+
|
|
|
|
+ // 生成枚举文件
|
|
|
|
+ console.log('> 生成枚举文件');
|
|
|
|
+ const enums = generateEnums(schemas);
|
|
|
|
+
|
|
|
|
+ // 生成实体类型文件
|
|
|
|
+ console.log('> 生成实体类型文件');
|
|
|
|
+ generateEntity(schemas, enums);
|
|
|
|
+
|
|
|
|
+ // 生成控制器文件
|
|
|
|
+ console.log('> 生成控制器文件');
|
|
|
|
+ generateController(apiPaths, getTagDescDict(swagger['tags'] ?? []), enums);
|
|
|
|
+
|
|
|
|
+ console.info('生成完成!');
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const request = require('request');
|
|
|
|
+request.get(config.swaggerJson, generate);
|