getInterFace.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /* eslint-disable import/extensions */
  2. /* eslint-disable @typescript-eslint/no-var-requires */
  3. const common = require('./common.js')
  4. const { cl, request, files } = common
  5. // const urlNamesCount = {}
  6. const convert = {
  7. /** 特殊替换时使用 */
  8. _placeholder: '_',
  9. _maps: {
  10. baseTypes: ['integer', 'int', 'long', 'bigdecimal', 'string'],
  11. /** 需要替换的类型 */
  12. dataTypes: {
  13. number: ['integer', 'int', 'long', 'bigdecimal'],
  14. Array: ['Collection', 'List'],
  15. Record: ['Map'],
  16. IPage: ['Page'],
  17. IPageParams: ['分页请求参数'],
  18. AnyObject: ['object', 'JSONObject'],
  19. },
  20. scheamas: {},
  21. /** 完整替换的类型 */
  22. completes: Object.entries({
  23. 'Array<any>': ['array'],
  24. any: ['Result', '返回值'],
  25. 'Record<any, any>': ['Record', 'Map'],
  26. 'IPageParams<any>': ['IPageParams'],
  27. }),
  28. },
  29. /** 修正被占位符替换破坏的类型修饰 */
  30. correct(str) {
  31. /** 修正被替换的基础类型后面的, */
  32. convert._maps.baseTypes.forEach((f) => {
  33. const cs = `${f}${convert._placeholder}`
  34. if (str.includes(cs)) {
  35. str = str.replace(cs, `${f}, `)
  36. }
  37. })
  38. return str
  39. },
  40. /** 转换类型定义 */
  41. definitions(str) {
  42. str = str.replace('#/definitions/', '')
  43. str = convert.getSafeStr(str)
  44. str = convert.correct(str)
  45. str = convert.dataTypesMap(str, 1)
  46. str = str.replace(/«/g, '<')
  47. str = str.replace(/»/g, '>')
  48. for (const [k, v] of convert._maps.completes) {
  49. v.forEach((f) => {
  50. if (str === f) {
  51. str = k
  52. }
  53. })
  54. }
  55. return str
  56. // ms = ['返回值','List','Ipage']
  57. // str = '返回值«IPage«UserAccount对象»»'
  58. // function ffff(str) {
  59. // const rg = new RegExp(`([${ms.join('|')}])«(.+)»`)
  60. // const strm = str.match(rg)
  61. // if (strm && strm.length == 3) {
  62. // switch (strm[1]) {
  63. // case 'List':
  64. // str = `${strm[2]}[]`
  65. // defa
  66. // }
  67. // ffff(strm[2])
  68. // console.log(strm)
  69. // }
  70. // }
  71. },
  72. /** 类型替换
  73. * type 0:字符串完全替换,1:正则替换
  74. */
  75. dataTypesMap(str, type = 0) {
  76. if (!str) return ''
  77. for (const [k, v] of Object.entries(this._maps.dataTypes)) {
  78. v.forEach((f) => {
  79. if (type === 0) {
  80. str = str.replace(f, k)
  81. }
  82. if (type === 1) {
  83. // 脱壳
  84. str = str.replace(/^返回值«(.+)»/, '$1')
  85. str = str.replace(/^Result«(.+)»/, '$1')
  86. // 取出被«»包裹的类型
  87. const rg = new RegExp(`(^|«+)${f}([,«»]+|$)`)
  88. if (rg.test(str)) {
  89. // 替换这种类型
  90. str = str.replace(rg, `$1${k}$2`)
  91. // // 被特殊替换了的, 替换回来
  92. // const srp = `${k}${convert._placeholder}`
  93. // if (str.includes(srp)) {
  94. // str = str.replace(srp, `${k}, `)
  95. // }
  96. }
  97. }
  98. })
  99. }
  100. return str
  101. },
  102. _dirtyChars: ['-', ',', '、', '/', '(', ')', ','],
  103. getSafeStr(str) {
  104. this._dirtyChars.forEach((f) => {
  105. str = str.replaceAll(f, convert._placeholder)
  106. })
  107. return str
  108. },
  109. main({ name, url, basePath }) {
  110. basePath = basePath.replace(/^\//, '')
  111. return request
  112. .get(url)
  113. .then((res) => {
  114. // const tt = files.getIFPath(basePath + '_raw')
  115. // files.write(tt.webApiPath, res)
  116. // try {
  117. const data = res
  118. this.scheama({ name, basePath, data })
  119. this.webapi({ name, basePath, data })
  120. // } catch (e) {
  121. // return Promise.resolve({ errInfo: e })
  122. // }
  123. })
  124. .catch((e) => {
  125. cl.logw(`${name}-${basePath}:${e}`)
  126. return Promise.resolve({ errInfo: e })
  127. })
  128. },
  129. /** 生成 scheama文件 .d.ts */
  130. scheama({ name, basePath, data }) {
  131. const curps = files.getIFPath(basePath)
  132. const tss = Object.values(data.definitions)
  133. // 忽略嵌套类型
  134. .filter((f) => !f.title.includes('«'))
  135. .map((m) => {
  136. let descriptions = ''
  137. if (m.properties) {
  138. descriptions = '\n'
  139. for (const [k, v] of Object.entries(m.properties)) {
  140. let curType = ''
  141. if (v.$ref) {
  142. curType = convert.definitions(v.$ref)
  143. } else if (v.type === 'array' && v.items.$ref) {
  144. curType = convert.definitions(v.items.$ref) + '[]'
  145. } else {
  146. curType = convert.dataTypesMap(v.type)
  147. }
  148. descriptions += ` /** ${v.description} */\n ${k}?: ${curType}\n`
  149. }
  150. }
  151. return `interface ${convert.getSafeStr(m.title)} {${descriptions}}`
  152. })
  153. files.write(curps.schemaPath, `/** ${name} */\n${tss.join('\n')}\n`)
  154. },
  155. /** 生成接口配置信息 */
  156. webapi({ name, basePath, data }) {
  157. const curps = files.getIFPath(basePath)
  158. const paths = data.paths
  159. let urlsStr = ''
  160. const urlNameMap = {}
  161. const getUrlName = (url) => {
  162. url = url.replace(/.+\//, '')
  163. url = url.replace('-', '_')
  164. if (urlNameMap.hasOwnProperty(url)) {
  165. urlNameMap[url]++
  166. } else {
  167. urlNameMap[url] = 0
  168. }
  169. return url + (urlNameMap[url] ? '_' + urlNameMap[url] : '')
  170. }
  171. for (const [k, v] of Object.entries(paths)) {
  172. const curPostInfo = v.post
  173. if (!curPostInfo) continue
  174. // const urlName = curPostInfo.operationId.replace('UsingPOST', '')
  175. if (k.includes('{')) continue
  176. const urlName = getUrlName(k)
  177. // if (!urlNamesCount[urlName]) {
  178. // urlNamesCount[urlName] = 1
  179. // } else {
  180. // urlNamesCount[urlName] += 1
  181. // }
  182. let isUploadStr = ''
  183. let reqTypeStr = ''
  184. let resTypeStr = ''
  185. // 存在入参定义
  186. if (curPostInfo.parameters && curPostInfo.parameters.length) {
  187. /** body类型入参 */
  188. const bodyParameter = curPostInfo.parameters.find((f) => f.in === 'body')
  189. if (bodyParameter) {
  190. const curSchema = bodyParameter.schema
  191. if (curSchema && curSchema.$ref) {
  192. reqTypeStr = ` reqType: {} as ${convert.definitions(curSchema.$ref)},`
  193. }
  194. }
  195. /** query类型入参 */
  196. const queryParameters = curPostInfo.parameters.filter((f) => f.in === 'query')
  197. if (queryParameters.length) {
  198. const curReqType = queryParameters
  199. .map(
  200. (m) =>
  201. `\n /** ${m.description} */\n ${m.name}${m.required ? '' : '?'}: ${convert.dataTypesMap(m.type)}`,
  202. )
  203. .join('')
  204. reqTypeStr = ` reqType: {} as {${curReqType}\n },`
  205. }
  206. /** formData类型入参-- 上传文件 */
  207. const formDataParameters = curPostInfo.parameters.find((f) => f.in === 'formData')
  208. if (formDataParameters) {
  209. isUploadStr = '\n isUpload: true,'
  210. // reqTypeStr = ` reqType: {} as {${`\n /** ${formDataParameters.description} */\n ${formDataParameters.name}${formDataParameters.required ? '' : '?'}: File`}\n },`
  211. }
  212. }
  213. if (
  214. curPostInfo.responses &&
  215. curPostInfo.responses[200] &&
  216. curPostInfo.responses[200].schema &&
  217. curPostInfo.responses[200].schema.$ref
  218. ) {
  219. /** 出参 类型 */
  220. const resTypeSchema = convert.definitions(curPostInfo.responses[200].schema.$ref)
  221. resTypeStr = ` resType: {} as ${resTypeSchema},`
  222. }
  223. urlsStr += `\n /** ${curPostInfo.summary} */\n ${urlName}: {\n realUrl: '${k}',${isUploadStr}\n${reqTypeStr}\n${resTypeStr}\n },`
  224. // urlsStr += `\n /** ${curPostInfo.summary || curPostInfo.summary} */\n ${urlName}: {\n realUrl: '${k}' as const,${isUploadStr}\n${reqTypeStr}\n${resTypeStr}\n },`
  225. }
  226. files.write(
  227. curps.webApiPath,
  228. `export default {${urlsStr}\n}\n`,
  229. // `const ${basePath} = {${urlsStr}\n}\nexport default ${basePath}\n`,
  230. )
  231. },
  232. }
  233. const main = () => {
  234. request.get('/json/group.json').then((res) => {
  235. // res= [{
  236. // "name": "账户中心",
  237. // "url": "/account/v2/api-docs",
  238. // "swaggerVersion": "2.0",
  239. // "location": "/account/v2/api-docs",
  240. // "basePath": "/account"
  241. // }]
  242. // 获取所有控制台参数
  243. const args = process.argv.slice(2) // 去掉第一个和第二个参数(node路径和脚本路径)
  244. // 分组名称-模糊
  245. const gn = args[0]
  246. const groups = gn ? res.filter((f) => f.basePath.includes(gn)) : res
  247. let importStr = ''
  248. let exportStr = ''
  249. Promise.all(
  250. groups.map((f) => {
  251. f.basePath = f.basePath.replace('/', '')
  252. return convert.main(f).then((res) => {
  253. if (res && res.errInfo) {
  254. cl.loge(`${f.name}-${f.basePath}:导入失败×`)
  255. return
  256. } else {
  257. cl.logs(`${f.name}-${f.basePath}:导入成功√`)
  258. }
  259. importStr += `import ${f.basePath} from './${f.basePath}'\n`
  260. exportStr += ` /** ${f.name} */\n ${f.basePath},\n`
  261. })
  262. }),
  263. ).then(() => {
  264. // 根据名称模糊搜索 不写入index.ts
  265. if (gn) return
  266. // 写入 src\utils\config\interFaces\index.ts
  267. const exportFileStr = `${importStr}\nexport default {\n${exportStr}}\n`
  268. const exportsPath = files.getIFPath('index')
  269. files.write(exportsPath.webApiPath, exportFileStr)
  270. // 接口名称重复的次数
  271. // const urlNamesCountRepeat = {}
  272. // Object.keys(urlNamesCount).forEach((k) => {
  273. // if (urlNamesCount[k] > 1) urlNamesCountRepeat[k] = urlNamesCount[k]
  274. // })
  275. // files.write(
  276. // files.getPath('temp/urlNamesCountRepeat.json'),
  277. // JSON.stringify(urlNamesCountRepeat),
  278. // )
  279. })
  280. })
  281. }
  282. main()
  283. const test = () => {
  284. // 获取所有控制台参数
  285. const args = process.argv.slice(2) // 去掉第一个和第二个参数(node路径和脚本路径)
  286. }
  287. // test()