自己实现自动化生成api

228 阅读6分钟

分析api-docs

目前公司不同BU的swagger文档格式大致如下

详细规范可参考:

swagger.io/docs/specif…

mock/next.js

module.exports = {
    "swagger": "2.0",
    "info": {
        "description": "xxxx swagger Application.",
        "version": "1.0.0",
        "title": "xxxx swagger Application",
        "termsOfService": "TERMS OF SERVICE URL",
        "contact": {
            "name": "xxxx",
            "url": "http://xxxx.com/",
            "email": "xxxx.com"
        },
        "license": {
            "name": "MIT License",
            "url": "LICENSE URL"
        }
    },
    "host": "xxxx",
    "basePath": "xxx",
    "tags": [
        {
            "name": "product",
            "description": "Product Controller"
        }
    ],
    "paths": {
        "/product/createOrUpdate": {
            "post": {
                "tags": [
                    "product"
                ],
                "summary": "创建或编辑商品",
                "operationId": "createOrUpdateUsingPOST",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "*/*"
                ],
                "parameters": [
                    {
                        "in": "body",
                        "name": "vo",
                        "description": "vo",
                        "required": true,
                        "schema": {
                            "originalRef": "CreateOrUpdateProductVo",
                            "$ref": "#/definitions/CreateOrUpdateProductVo"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "originalRef": "SimpleResponse«boolean»",
                            "$ref": "#/definitions/SimpleResponse«boolean»"
                        }
                    },
                    "201": {
                        "description": "Created"
                    },
                    "401": {
                        "description": "Unauthorized"
                    },
                    "403": {
                        "description": "Forbidden"
                    },
                    "404": {
                        "description": "Not Found"
                    }
                },
                "deprecated": false
            }
        }
    },
    "definitions": {
        "CreateOrUpdateProductVo": {
            "type": "object",
            "properties": {
                "displayOriginalPrice": {
                    "type": "string",
                    "description": "原价(单位:元)"
                },
                "firstImageUrl": {
                    "type": "string",
                    "description": "商品头图url"
                },
                "firstOrderPrice": {
                    "type": "string",
                    "description": "首单优惠价(单位:元),可不传"
                },
                "id": {
                    "type": "integer",
                    "format": "int64",
                    "description": "商品id,创建时传入0",
                    "minimum": 0.0,
                    "exclusiveMinimum": false
                },
                "imageUrls": {
                    "type": "array",
                    "description": "商品详情图片url列表,最多5张",
                    "items": {
                        "type": "string"
                    }
                },
                "limitAmount": {
                    "type": "integer",
                    "format": "int32",
                    "description": "限购数量",
                    "minimum": 1.0,
                    "exclusiveMinimum": false
                },
                "orderNumber": {
                    "type": "integer",
                    "format": "int32",
                    "description": "排序号",
                    "minimum": 1.0,
                    "exclusiveMinimum": false
                },
                "productName": {
                    "type": "string",
                    "description": "商品名称,最长20字符"
                },
                "productState": {
                    "type": "string",
                    "description": "商品状态,1-下架,2-上架",
                    "enum": [
                        "UN_KNOWN",
                        "UN_SALE",
                        "SALE_ING"
                    ]
                },
                "unitPrice": {
                    "type": "string",
                    "description": "单位(单位:元)"
                }
            },
            "title": "CreateOrUpdateProductVo"
        },
        "SimpleResponse«boolean»": {
            "type": "object",
            "properties": {
                "data": {
                    "type": "boolean"
                },
                "errorCode": {
                    "type": "integer",
                    "format": "int32"
                },
                "errorMessage": {
                    "type": "string"
                },
                "status": {
                    "type": "boolean"
                }
            },
            "title": "SimpleResponse«boolean»"
        },
        "enumDto": {
            "type": "object",
            "properties": {
                "key": {
                    "type": "integer",
                    "format": "int32"
                },
                "value": {
                    "type": "string"
                }
            },
            "title": "enumDto"
        },
        "WhitelistEnumsDto": {
            "type": "object",
            "properties": {
                "applyStateEnums": {
                    "type": "array",
                    "items": {
                        "originalRef": "enumDto",
                        "$ref": "#/definitions/enumDto"
                    }
                }
            },
            "title": "WhitelistEnumsDto"
        }
    }
}

几个主要的对象:

  • tags
  • paths
  • definitions

实现步骤

  • 实现可配置apiconfigJSON,生成存放api的文件夹
  • 提取接口相关json
  • 提取接口相关type以及enum
  • 输出type 以及 enum 文件,输出接口文件

实现功能

1.配置apiconfig 创建api文件夹

在项目中创建config/apis/api-config.js文件

const next = require('./next')

module.exports = {
  serviceTemplate: `import http from "./../../http"
    import { AxiosPromise, AxiosRequestConfig } from 'axios';
    `,//新增serviceTemplate,用于后续生成的api文件内部引入
  outputDir: '/src/common/api',
  apiList: [
    {
      //swaggerUrl: next,  //演示地址
      swaggerUrl: 'http://xxxx/api-docs', //实际地址
      outputDir: '/src/common/api/next',
      isCreate: true,
    },
  ],
}
  • outputDir:生成的输入文件夹路径
  • apiList(可配置多个):
    • swaggerUrl:api doc地址(需免登),本项目演示用本地json代替
    • outputDir :该api 对应输出文件夹路径
    • isCreate: 用于开启或关闭检索生成

在项目config/apis文件夹中创建auto-port.js

const apiConfig = require('./api-config')
const fs = require('fs')
const path = require('path')
/**
 * 创建文件
 * @param {*} dirname 
 * @returns 
 */
const mkdir = (dirname) => {
    if (fs.existsSync(dirname)) {
        return true
    }
    else {
        // 去掉文件名,返回目录 path.dirname ,当存在目录开始进行mkdirSync
        if (mkdir(path.dirname(dirname))) {
            fs.mkdirSync(dirname);
            return true;
        }
    }
}

/**
 * mkdirSync 创建目录要一层一层创建
 */
const checkOutputDirExit = () => {
    mkdir(path.resolve(process.cwd() + apiConfig.outputDir))
}
/**
 * 自动生成api
 */
const createApi = () => {
    checkOutputDirExit()
}
createApi()

配置package.json

  "scripts": {
    "prettier": "prettier --write "src/**/**/*.{js,jsx,vue,tsx,json,css,scss,less,md}"",
    "api": "node ./config/apis/auto-port.js && npm run prettier "
  },

运行命令 yarn api ,此时生成src/common/api的目录,后续用于存放自动生成的api文件

2.解析接口JSON(将paths转为JSON)

原始json

分析下swagger paths的结构

"paths": {
        "/aggregatedCreditPayment/switchAvailableState": {
            "post": {
                "tags": [
                    "riskMerchant"
                ],
                "summary": "xxxxxxxxxx",
                "operationId": "switchAvailableStateUsingPOST",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "*/*"
                ],
                "parameters": [
                   {
                        "in": "body",
                        "name": "vo",
                        "description": "vo",
                        "required": true,
                        "schema": {
                            "originalRef": "MerchantCreditPayLimitVo",
                            "$ref": "#/definitions/MerchantCreditPayLimitVo"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "originalRef": "SimpleResponse«boolean»",
                            "$ref": "#/definitions/SimpleResponse«boolean»"
                        }
                    },
                    "201": {
                        "description": "Created"
                    },
                    "401": {
                        "description": "Unauthorized"
                    },
                    "403": {
                        "description": "Forbidden"
                    },
                    "404": {
                        "description": "Not Found"
                    }
                },
                "deprecated": false
            }
        }
    },
  • tags 该接口对应模块
  • post/get 该接口对应请求方式
  • summary 注释
  • parameters - in params类型
    • formData
    • query
    • body
  • responses 返回结果
  • schema 源数据对象
    • originalRef 原始数据类型
    • $ref 数据类型
  • type 类型

希望的得到的结构

{
    "product": [
        {
            "name": "createOrUpdate",
            "method": "post",
            "url": "/product/createOrUpdate",
            "summary": "创建或编辑商品",
            "params": [
                {
                    "name": "vo",
                    "desc": "vo",
                    "type": "CreateOrUpdateProductVo",
                    "originType": {
                        "originalRef": "CreateOrUpdateProductVo",
                        "$ref": "#/definitions/CreateOrUpdateProductVo"
                    },
                    "required": true,
                    "simpleType": false
                }
            ],
            "returnData": {
                "type": "SimpleResponse«boolean»",
                "originType": {
                    "originalRef": "SimpleResponse«boolean»",
                    "$ref": "#/definitions/SimpleResponse«boolean»"
                },
                "simpleType": false
            }
        }
    ]
}
2.1 处理paths对象,生成基本机构JSON

在自动生成api方法中提取swaggerJSON

/**
 * 自动生成api
 */
const createApi = () => {
    checkOutputDirExit()
  apiConfig.apiList.forEach(async api => {
        if (api.isCreate) {
            //真实请求swaggerUrl地址
            const result = await axios.get(api.swaggerUrl) 
            const data = result.data
            
            // const data = api.swaggerUrl  // 当前项目本地文件演示
            parseData(data, api.outputDir)
        }
    })
}
/**
 * 解析swagger
 * @param {*} swaggerJson 
 * @param {*} dirname 
 */
const parseData = (swaggerJson, dirname) => {
    const paths = handlePath(swaggerJson.paths)
    console.log('paths', JSON.stringify(paths))
}
  • 实现json基本机构
/**
 * 处理paths对象
 * @param {*} path 
 */
const handlePath = (path) => {
    let apiOptions = {};
    const apiPaths = Object.keys(path)
    apiPaths && apiPaths.forEach(apiItem => {
        const apiInfo = {
            name: apiItem.split('/').pop(),
            method: path[apiItem].get ? 'get' : 'post',
            url: apiItem,
            summary: '',
            params: '',
            returnData: {
                type: '',
                originType: '',
                simpleType: true,
            },
        }
        
        const apiInfoBody = path[apiItem][apiInfo.method]
        apiInfo.summary = apiInfoBody.summary

        //分模块
        const belongModule = getBelong(apiInfoBody.tags)

        if (!apiOptions[belongModule]) {
            apiOptions[belongModule] = []
        }
            apiOptions[belongModule].push(apiInfo)
    })
    return apiOptions
}
/** 获取所属模块名 */
const getBelong = (str) => {
    if (str.length === 0) {
        throw new Error('无所属模块')
    } else {
        return str[0]
    }
}

此时得到的json数据结果如下

{
    "riskMerchant": [
        {
            "name": "switchAvailableState",
            "method": "post",
            "url": "/aggregatedCreditPayment/switchAvailableState",
            "summary": "xxxxxx",
            "params": "",
            "returnData": {
                "type": "",
                "originType": "",
                "simpleType": true
            }
        }
    ]
}

但是此时该方法的params以及returnData中的数据未做处理

  • 通过解析parameters字段,实现getParams方法
//handlePath中添加
apiInfo.params = apiInfoBody.parameters ? getParams(apiInfoBody.parameters) : []

/**
 * 获取api params
 * @param {*} params 
 */
const getParams = (params) => {
    const arr = []
    params.forEach(paramsItem => {
        if (['formData', 'query', 'body'].includes(paramsItem.in)) {
            const _item = {
                name: paramsItem.name,
                desc: paramsItem.description || '描述缺失',
                type: null, //类型
                originType:null, //源类型
                required: paramsItem.schema ? true : paramsItem.required, //是否必须
                simpleType: paramsItem.schema ? false : true, //是否基础类型
            }
              //处理paths中
            if (paramsItem.schema) {
                const _schema = paramsItem.schema
                _schema.$ref = _schema.$ref && splitRealType(_schema.$ref)
                _schema.originType = _schema.originType && splitRealType(_schema.originType)
                _item.originType = _schema || paramsItem.type
            }
            _item.type = paramsItem.schema
                ? getRealType(paramsItem.schema)
                : paramsItem.format == 'int64'
                    ? 'string'
                    : toTypescriptType(paramsItem.type)

            arr.push(_item)
        }
    })

    return arr
}
  • 解析responses字段,实现returnData
//handlePath中添加
apiInfo.returnData = getResponses(apiInfoBody.responses)

const getResponses = (responses) => {
    const res = responses['200']
    if (res.schema) {
        const _item = {
            type: null,
            originType: res.schema,
            simpleType: false,
        }
        _item.type = getRealType(res.schema)
        return _item
    } else {
        return {
            type: res.type,
            originType: res.type,
            simpleType: false,
        }
    }
}

/**
 * 
 * @param {*} schema 
 */
const getRealType = (schema) => {
    if (schema) {
        return splitRealType(schema.originalRef || schema.$ref?.split('#/definitions/')[1])
    } else {
        return 'any'
    }

}

/**
 *  const _name = name.split("«")[0]// 处理类 SimpleResponse«List«ChannelFeeConfigDto»»  => ChannelFeeConfigDto【tip:2022118调整】
 * @param {*} name
 * @returns
 */
const splitRealType = (name) => {
  if (!name) {
    return ''
  }
  return name?.split('«').join('»').split('»').filter(item => item).at(-1)
}

// /**
//  *  const _name = name.split("«")[0]//
//    处理类 SimpleResponse«List«ChannelFeeConfigDto»»  => SimpleResponseListChannelFeeConfigDto
//  * @param {*} name
//  * @returns
//  */
//    const splitRealType = (name) => {
//     if (!name) {
//         return ''
//     }
//     return name?.split('«').join('').split('»').reduce((a, b) => { return `${a}${b}` })
// }

此刻运行 yarn api方法,就能得到目标json对象

2.2 处理 definitions,提取typeMap
/**
 * 处理definitions
 * @param {*} definitions 
 */
const handleDefinitions = (definitions) => {
    let typeMap = {};
    const _enumMap = []
    const apiDefinitions = Object.keys(definitions)
    apiDefinitions.map(definitionsKey => {
        const definitionsItem = definitions[definitionsKey]
        const { title, type, description, properties } = definitionsItem
        typeMap[title] = {
            name: title,
            type: type,
            description: description || '缺少注释',
            property: properties
        }
    })
    return typeMap
}

得到下面结果的typaMap json

{
    "CreateOrUpdateProductVo": {
        "name": "CreateOrUpdateProductVo",
        "type": "object",
        "description": "缺少注释",
        "property": {
            "displayOriginalPrice": {
                "type": "string",
                "description": "原价(单位:元)"
            },
            "firstImageUrl": {
                "type": "string",
                "description": "商品头图url"
            },
            "firstOrderPrice": {
                "type": "string",
                "description": "首单优惠价(单位:元),可不传"
            },
            "id": {
                "type": "integer",
                "format": "int64",
                "description": "商品id,创建时传入0",
                "minimum": 0,
                "exclusiveMinimum": false
            },
            "imageUrls": {
                "type": "array",
                "description": "商品详情图片url列表,最多5张",
                "items": {
                    "type": "string"
                }
            },
            "limitAmount": {
                "type": "integer",
                "format": "int32",
                "description": "限购数量",
                "minimum": 1,
                "exclusiveMinimum": false
            },
            "orderNumber": {
                "type": "integer",
                "format": "int32",
                "description": "排序号",
                "minimum": 1,
                "exclusiveMinimum": false
            },
            "productName": {
                "type": "string",
                "description": "商品名称,最长20字符"
            },
            "productState": {
                "type": "string",
                "description": "商品状态,1-下架,2-上架",
                "enum": [
                    "UN_KNOWN",
                    "UN_SALE",
                    "SALE_ING"
                ]
            },
            "unitPrice": {
                "type": "string",
                "description": "单位(单位:元)"
            }
        }
    },
    "SimpleResponse«boolean»": {
        "name": "SimpleResponse«boolean»",
        "type": "object",
        "description": "缺少注释",
        "property": {
            "data": {
                "type": "boolean"
            },
            "errorCode": {
                "type": "integer",
                "format": "int32"
            },
            "errorMessage": {
                "type": "string"
            },
            "status": {
                "type": "boolean"
            }
        }
    },
    "enumDto": {
        "name": "enumDto",
        "type": "object",
        "description": "缺少注释",
        "property": {
            "key": {
                "type": "integer",
                "format": "int32"
            },
            "value": {
                "type": "string"
            }
        }
    },
    "WhitelistEnumsDto": {
        "name": "WhitelistEnumsDto",
        "type": "object",
        "description": "缺少注释",
        "property": {
            "applyStateEnums": {
                "type": "array",
                "items": {
                    "originalRef": "enumDto",
                    "$ref": "#/definitions/enumDto"
                }
            }
        }
    }
}

但是现在还是存在一些问题,提取并且处理enum,property中仍是一个对象,存在大量的参数,仍需要单独提取出来方便后续处理

处理property和enum

/**
 * 处理获取typeMap中的properties 和枚举Info
 * @param {*} properties 
 * @returns 
 */
const getPropertyAndEnumInfo = (properties) => {
    const propertyArr = []

    let enumInfo = {}
    const propertyInfo = Object.keys(properties)
    propertyInfo.forEach(propertyKey => {
        const _item = properties[propertyKey]
        const { type, format, description, items } = _item;
        const propertyItem = {
            name: propertyKey,
            type: format == 'int64'
                ? 'string'
                : toTypescriptType(type),
            description: description || '注释丢失',
        }
        propertyArr.push(propertyItem)
        if (_item.enum) {
            enumInfo[`${propertyKey}Enum`] = _item.enum
        }
    })
    return { propertyArr, enumInfo }
}

修改handleDefinitions方法

/**
 * 处理definitions
 * @param {*} definitions 
 */
const handleDefinitions = (definitions) => {
    const typeMap = {}
    const apiDefinitions = Object.keys(definitions)
    const _enumMap = []
    apiDefinitions.map(definitionsKey => {
        const definitionsItem = definitions[definitionsKey]
        const { title, type, description, properties } = definitionsItem
        if (properties) {
            const { propertyArr, enumInfo } = getPropertyAndEnumInfo(properties)
            typeMap[title] = {
                name: title,
                type: type,
                description: description || '缺少注释',
                property: properties ? propertyArr : []
            }
            if (isEmptyObj(enumInfo)) {
                // 重复枚举去重
                const check = _enumMap.every(item => Object.keys(item)[0] !== Object.keys(enumInfo)[0])
                if (check) {
                    _enumMap.push(enumInfo)
                }

            }
        }
    })
    return { typeMap, _enumMap }
}
/**
 * 判断是否空对象
 * @param {*} obj 
 * @returns 
 */
const isEmptyObj = (obj) => {
    for (let item in obj) {
        return true
    }
    return false
}

此时运行yarn api,得到预期的机构如下:

//typeMap
{
    "CreateOrUpdateProductVo": {
        "name": "CreateOrUpdateProductVo",
        "type": "object",
        "description": "缺少注释",
        "property": [
            {
                "name": "displayOriginalPrice",
                "type": "string",
                "description": "原价(单位:元)"
            },
            {
                "name": "firstImageUrl",
                "type": "string",
                "description": "商品头图url"
            },
            {
                "name": "firstOrderPrice",
                "type": "string",
                "description": "首单优惠价(单位:元),可不传"
            },
            {
                "name": "id",
                "type": "string",
                "description": "商品id,创建时传入0"
            },
            {
                "name": "imageUrls",
                "description": "商品详情图片url列表,最多5张"
            },
            {
                "name": "limitAmount",
                "type": "number",
                "description": "限购数量"
            },
            {
                "name": "orderNumber",
                "type": "number",
                "description": "排序号"
            },
            {
                "name": "productName",
                "type": "string",
                "description": "商品名称,最长20字符"
            },
            {
                "name": "productState",
                "type": "string",
                "description": "商品状态,1-下架,2-上架"
            },
            {
                "name": "unitPrice",
                "type": "string",
                "description": "单位(单位:元)"
            }
        ]
    },
    "SimpleResponse«boolean»": {
        "name": "SimpleResponse«boolean»",
        "type": "object",
        "description": "缺少注释",
        "property": [
            {
                "name": "data",
                "type": "boolean",
                "description": "注释丢失"
            },
            {
                "name": "errorCode",
                "type": "number",
                "description": "注释丢失"
            },
            {
                "name": "errorMessage",
                "type": "string",
                "description": "注释丢失"
            },
            {
                "name": "status",
                "type": "boolean",
                "description": "注释丢失"
            }
        ]
    },
    "enumDto": {
        "name": "enumDto",
        "type": "object",
        "description": "缺少注释",
        "property": [
            {
                "name": "key",
                "type": "number",
                "description": "注释丢失"
            },
            {
                "name": "value",
                "type": "string",
                "description": "注释丢失"
            }
        ]
    },
    "WhitelistEnumsDto": {
        "name": "WhitelistEnumsDto",
        "type": "object",
        "description": "缺少注释",
        "property": [
            {
                "name": "applyStateEnums",
                "type": "enumDto",
                "description": "注释丢失"
            }
        ]
    }
} 
//enumMap
[
    {
        "productStateENUM": [
            "UN_KNOWN",
            "UN_SALE",
            "SALE_ING"
        ]
    }
]
2.3 处理提取的typeMap以及enumMap
2.3.1处理输出enumMap.ts
/**
 * 生成EnumCode
 * @param {*} enumMap 
 */
const generateEnumCode = (enumMap) => {
    const enumCodeArr = []
    enumMap.forEach(enumItem => {
        const key = Object.keys(enumItem)[0]
        enumCodeArr.push(` export enum ${key} {
            ${enumItem[key].join(',\n')}
         }`)
    })
    return enumCodeArr.join('\n')
}
/**
 * 写入文件
 * @param {*} name 
 * @param {*} data 
 * @param {*} dirname 
 */
const writeFile = (name, data, dirname) => {
    const _path = process.cwd() + dirname
    mkdir(_path)
    const filePath = path.resolve(_path, `${name}.ts`)
    fs.writeFileSync(filePath, data)
}

 const enumCode = generateEnumCode(enumMap)
    if (enumCode) {
        writeFile('enum', enumCode, dirname)
    }

此时得到文件以及对应枚举结构

export enum productStateEnum {
  UN_KNOWN,
  UN_SALE,
  SALE_ING,
}
2.3.2处理输出typeMap.ts 并引入enum.ts
 const typeCode = generateTypeCode(typeMap, enumMap)
    if (typeCode) {
        writeFile('type', typeCode, dirname)
    }


/**
 * 生成typeCode 引入enum.ts
 * @param {*} typeCode 
 * @param {*} enumMap 
 * @returns 
 */
const generateTypeCode = (typeCode, enumMap) => {
    const typeCodeArr = []
    const typeCodeData = Object.keys(typeCode)
    typeCodeData.forEach(typeCodeDataKey => {
        const _item = typeCode[typeCodeDataKey]

        const { property, description, name } = _item

        const _name = splitRealType(name)
        typeCodeArr.push(`/** ${description} **/
        export interface ${_name} {
        ${property.map(propertyItem => {
            const { name, type, description } = propertyItem
            let _type = type

            // 替换枚举类型
            const check = enumMap.find(enumMapItem => Object.keys(enumMapItem)[0] === `${name}Enum`)
            if (check) {
                _type = `${name}Enum`
            }
            return `${name}:${_type}, //${description}`
        }).join('\n')}
        }`)
    })


    // 引入enum.ts文件
    const usedEnum = []
    enumMap.forEach(enumMapItem => {
        const type = Object.keys(enumMapItem)[0]
        if (!usedEnum.includes(type)) {
            usedEnum.push(type)
        }
    })

    if (usedEnum.length) {
        typeCodeArr.unshift(`
        import {${usedEnum.join(',')}} from './enum'
        `)
    }
    return typeCodeArr.join('\n')
}

此时就能正确输出type.ts文件并按需引入enum

import { productStateEnum } from "./enum";

/** 缺少注释 **/
export interface CreateOrUpdateProductVo {
  displayOriginalPrice: string; //原价(单位:元)
  firstImageUrl: string; //商品头图url
  firstOrderPrice: string; //首单优惠价(单位:元),可不传
  id: string; //商品id,创建时传入0
  imageUrls: []; //商品详情图片url列表,最多5张
  limitAmount: number; //限购数量
  orderNumber: number; //排序号
  productName: string; //商品名称,最长20字符
  productState: productStateEnum; //商品状态,1-下架,2-上架
  unitPrice: string; //单位(单位:元)
}
/** 缺少注释 **/
export interface SimpleResponseboolean {
  data: boolean; //注释丢失
  errorCode: number; //注释丢失
  errorMessage: string; //注释丢失
  status: boolean; //注释丢失
}
/** 缺少注释 **/
export interface enumDto {
  key: number; //注释丢失
  value: string; //注释丢失
}
/** 缺少注释 **/
export interface WhitelistEnumsDto {
  applyStateEnums: []; //注释丢失
}
2.4 整合type.ts以及enum.ts到接口中,生成接口文件

最后需要整合ype.ts以及enum.ts到接口文件中,需要实现一个generateApiType的方法

const codes = generateApiType(paths, typeMap, enumMap)
    Object.keys(codes).forEach(key => {
        writeFile(key, codes[key], dirname)
    })
2.4.1实现基本的接口格式
/**
 * 生成API文件
 * @param {*} paths 
 * @param {*} typeMap 
 * @param {*} enumMap 
 */
const generateApiType = (paths, typeMap, enumMap) => {
    const apiCodes = {}
    Object.keys(paths).forEach(path => {
        const apiText = []
        const usedType = []
        const moduleApiData = paths[path]

        moduleApiData.forEach(api => {
            const { summary, name, method, url, returnData, params } = api
            const endParam = [...api.params]
            endParam.push({
                name: 'options',
                type: 'AxiosRequestConfig | undefined',
                required: false,
                desc: '',
                originType: '',
                simpleType: false,
            })
            let paramToken = ''
            paramToken = `${params
                .map(paramsItem => {
                    return paramsItem.name
                })
                .join(',')}`
            if (method === 'get') {
                if (paramToken) {
                    paramToken = `,{params:{${paramToken}},...options}`
                } else {
                    paramToken = `,{...options}`
                }
            }
            else if (method === 'post') {
                if (params.length === 1) {
                    paramToken = params[0].simpleType ? `,{${params[0].name},options}` : `,${params[0].name},options`
                } else {
                    paramToken = `,{${paramToken}},options`
                }
            }
              // const _name = /^.*[A-Z]+.*/.test(name) ? name : url.split('/').reduce((pre, next) => `${pre}${titleCase(next)}`)   //存在同名的,一般是单个单词的
      // apiText.push(`
      //           /** ${summary} */
      //           export const ${_name} =(${endParam
      //     .map(a => `${a.name}${a.name == 'options' ? '?' : ''}:${a.type}`)
      //     .join(',')}): AxiosPromise<${returnData.type || null}> => {
      //               return http.${method}('${url}'${paramToken})
      //       }`)
      
      
         // 处理/dataImport/importTransactionAddress/{sign} 20221118修改
      const _name = /^.*[A-Z]+.*/.test(name) ? name : url.split('/{')[0].split('/').reduce((pre, next) => `${pre}${titleCase(next)}`)   //存在同名的,一般是单个单词的
      apiText.push(`
                /** ${summary} */
                export const ${_name} =(params:{
                  ${endParam
          .map(a => `${a.name}${(a.require === 'options' || !a.require) ? '?' : ''}:${a.type}`)  //补充require判断是否必填
          .join(',')}
                }): AxiosPromise<${returnData.type || null}> => {
                    return axios.${method}('${url}',{params})
            }`)

        })

        }
        apiText.unshift(apiConfig.serviceTemplate)
        apiCodes[path] = apiText.join('\n')
    })
    return apiCodes
}

此时生成的接口文件没有对应参数的type类型

2.4.2 引入接口的参数对应的type以及返回参数对应的type

在 generateApiType方法中添加以下逻辑

//api.params.forEach中添加
if (!param?.simpleType) {
  usedType.push(param.type)
}
})

if (!returnData?.simpleType) {
  usedType.push(returnData.type)
}
// moduleApiData.forEach中添加
const _usedType = []
usedType.forEach(usedTypeItem => {
  if (!_usedType.includes(usedTypeItem) && !!usedTypeItem) {
    _usedType.push(usedTypeItem)
  }
})

if (_usedType.length) {
  apiText.unshift(`
  import {${_usedType.join(',')}} from './type'
  `)
        }

此时再次运行yarn api,得到期待的结果

代码地址

jw-auto-port