/* eslint-disable import/extensions */ /* eslint-disable @typescript-eslint/no-var-requires */ const common = require('./common.js') const { cl, request, files } = common const convert = { /** 特殊替换时使用 */ _placeholder: '_', _maps: { baseTypes: ['integer', 'int', 'long', 'bigdecimal', 'string'], /** 需要替换的类型 */ dataTypes: { number: ['integer', 'int', 'long', 'bigdecimal'], Array: ['Collection', 'List'], Record: ['Map'], IPage: ['Page'], IPageParams: ['分页请求参数'], AnyObject: ['object', 'JSONObject'], }, scheamas: {}, /** 完整替换的类型 */ completes: Object.entries({ 'Array': ['array'], any: ['Result', '返回值'], 'Record': ['Record', 'Map'], 'IPageParams': ['IPageParams'], }), }, /** 修正被占位符替换破坏的类型修饰 */ correct(str) { /** 修正被替换的基础类型后面的, */ convert._maps.baseTypes.forEach((f) => { const cs = `${f}${convert._placeholder}` if (str.includes(cs)) { str = str.replace(cs, `${f}, `) } }) return str }, /** 转换类型定义 */ definitions(str) { str = str.replace('#/definitions/', '') str = convert.getSafeStr(str) str = convert.correct(str) str = convert.dataTypesMap(str, 1) str = str.replace(/«/g, '<') str = str.replace(/»/g, '>') for (const [k, v] of convert._maps.completes) { v.forEach((f) => { if (str === f) { str = k } }) } return str // ms = ['返回值','List','Ipage'] // str = '返回值«IPage«UserAccount对象»»' // function ffff(str) { // const rg = new RegExp(`([${ms.join('|')}])«(.+)»`) // const strm = str.match(rg) // if (strm && strm.length == 3) { // switch (strm[1]) { // case 'List': // str = `${strm[2]}[]` // defa // } // ffff(strm[2]) // console.log(strm) // } // } }, /** 类型替换 * type 0:字符串完全替换,1:正则替换 */ dataTypesMap(str, type = 0) { if (!str) return '' for (const [k, v] of Object.entries(this._maps.dataTypes)) { v.forEach((f) => { if (type === 0) { str = str.replace(f, k) } if (type === 1) { // 脱壳 str = str.replace(/^返回值«(.+)»/, '$1') str = str.replace(/^Result«(.+)»/, '$1') // 取出被«»包裹的类型 const rg = new RegExp(`(^|«+)${f}([,«»]+|$)`) if (rg.test(str)) { // 替换这种类型 str = str.replace(rg, `$1${k}$2`) // // 被特殊替换了的, 替换回来 // const srp = `${k}${convert._placeholder}` // if (str.includes(srp)) { // str = str.replace(srp, `${k}, `) // } } } }) } return str }, _dirtyChars: ['-', ',', '、', '/', '(', ')', ','], getSafeStr(str) { this._dirtyChars.forEach((f) => { str = str.replaceAll(f, convert._placeholder) }) return str }, main({ name, url, basePath }) { basePath = basePath.replace(/^\//, '') return request .get(url) .then((res) => { // const tt = files.getIFPath(basePath + '_raw') // files.write(tt.webApiPath, res) // try { const data = res this.scheama({ name, basePath, data }) this.webapi({ name, basePath, data }) // } catch (e) { // return Promise.resolve({ errInfo: e }) // } }) .catch((e) => { cl.logw(`${name}-${basePath}:${e}`) return Promise.resolve({ errInfo: e }) }) }, /** 生成 scheama文件 .d.ts */ scheama({ name, basePath, data }) { const curps = files.getIFPath(basePath) const tss = Object.values(data.definitions) // 忽略嵌套类型 .filter((f) => !f.title.includes('«')) .map((m) => { let descriptions = '' if (m.properties) { descriptions = '\n' for (const [k, v] of Object.entries(m.properties)) { let curType = '' if (v.$ref) { curType = convert.definitions(v.$ref) } else if (v.type === 'array' && v.items.$ref) { curType = convert.definitions(v.items.$ref) + '[]' } else { curType = convert.dataTypesMap(v.type) } descriptions += ` /** ${v.description} */\n ${k}?: ${curType}\n` } } return `interface ${convert.getSafeStr(m.title)} {${descriptions}}` }) files.write(curps.schemaPath, `/** ${name} */\n${tss.join('\n')}\n`) }, /** 生成接口配置信息 */ webapi({ name, basePath, data }) { const curps = files.getIFPath(basePath) const paths = data.paths let urlsStr = '' const urlNameMap = {} const getUrlName = (url) => { url = url.replace(/.+\//, '') if (urlNameMap.hasOwnProperty(url)) { urlNameMap[url]++ } else { urlNameMap[url] = 0 } return url + (urlNameMap[url] ? '_' + urlNameMap[url] : '') } for (const [k, v] of Object.entries(paths)) { const curPostInfo = v.post if (!curPostInfo) continue // const urlName = curPostInfo.operationId.replace('UsingPOST', '') if (k.includes('{')) continue const urlName = getUrlName(k) let isUploadStr = '' let reqTypeStr = '' let resTypeStr = '' // 存在入参定义 if (curPostInfo.parameters && curPostInfo.parameters.length) { /** body类型入参 */ const bodyParameter = curPostInfo.parameters.find((f) => f.in === 'body') if (bodyParameter) { const curSchema = bodyParameter.schema if (curSchema && curSchema.$ref) { reqTypeStr = ` reqType: {} as ${convert.definitions(curSchema.$ref)},` } } /** query类型入参 */ const queryParameters = curPostInfo.parameters.filter((f) => f.in === 'query') if (queryParameters.length) { const curReqType = queryParameters .map( (m) => `\n /** ${m.description} */\n ${m.name}${m.required ? '' : '?'}: ${convert.dataTypesMap(m.type)}`, ) .join('') reqTypeStr = ` reqType: {} as {${curReqType}\n },` } /** formData类型入参-- 上传文件 */ const formDataParameters = curPostInfo.parameters.find((f) => f.in === 'formData') if (formDataParameters) { isUploadStr = '\n isUpload: true,' // reqTypeStr = ` reqType: {} as {${`\n /** ${formDataParameters.description} */\n ${formDataParameters.name}${formDataParameters.required ? '' : '?'}: File`}\n },` } } if ( curPostInfo.responses && curPostInfo.responses[200] && curPostInfo.responses[200].schema && curPostInfo.responses[200].schema.$ref ) { /** 出参 类型 */ const resTypeSchema = convert.definitions(curPostInfo.responses[200].schema.$ref) resTypeStr = ` resType: {} as ${resTypeSchema},` } // urlsStr += `\n /** ${curPostInfo.summary || curPostInfo.summary} */\n ${urlName}: {\n realUrl: '${k}',${isUploadStr}\n${reqTypeStr}\n${resTypeStr}\n },` urlsStr += `\n /** ${curPostInfo.summary || curPostInfo.summary} */\n ${urlName}: {\n realUrl: '${k}' as const,${isUploadStr}\n${reqTypeStr}\n${resTypeStr}\n },` } files.write( curps.webApiPath, `export default {${urlsStr}\n}\n`, // `const ${basePath} = {${urlsStr}\n}\nexport default ${basePath}\n`, ) }, } const main = () => { request.get('/json/group.json').then((res) => { // res= [{ // "name": "账户中心", // "url": "/account/v2/api-docs", // "swaggerVersion": "2.0", // "location": "/account/v2/api-docs", // "basePath": "/account" // }] // 获取所有控制台参数 const args = process.argv.slice(2) // 去掉第一个和第二个参数(node路径和脚本路径) // 分组名称-模糊 const gn = args[0] const groups = gn ? res.filter((f) => f.basePath.includes(gn)) : res let importStr = '' let exportStr = '' Promise.all( groups.map((f) => { f.basePath = f.basePath.replace('/', '') return convert.main(f).then((res) => { if (res && res.errInfo) { cl.loge(`${f.name}-${f.basePath}:导入失败×`) return } else { cl.logs(`${f.name}-${f.basePath}:导入成功√`) } importStr += `import ${f.basePath} from './${f.basePath}'\n` exportStr += ` /** ${f.name} */\n ${f.basePath},\n` }) }), ).then(() => { // 根据名称模糊搜索 不写入index.ts if (gn) return // 写入 src\utils\config\interFaces\index.ts const exportFileStr = `${importStr}\nexport default {\n${exportStr}}\n` const exportsPath = files.getIFPath('index') files.write(exportsPath.webApiPath, exportFileStr) }) }) } main() const test = () => { // 获取所有控制台参数 const args = process.argv.slice(2) // 去掉第一个和第二个参数(node路径和脚本路径) } // test()