一键生成系统Word接口文档的基本思路和实现

基本思路

  1. 可以利用swagger提供的/v2/api-docs接口返回的数据结构,来获取系统接口信息,包括:请求参数、请求Url、响应数据、相应码等;
  2. 对返回的数据去做解析,来组装符合Freemarker模板的数据;
  3. 通过Freemarker生成系统接口文档;
  4. 为了让接口swagger规范,可以提供代码模板,一键生成代码。

基本实现

  1. 设计Word接口文档模板的基本板式和格式,在resources/templates/word中;
  2. 设计满足Word接口文档模板的实体类格式,相关Bean在/document/dto中;
  3. 使用代码模板一键生成系统功能接口,执行ApplicationTests.codeGeneration()方法即可
  4. 启动系统,解析/v2/api-docs接口返回数据,组装数据使其符合步骤1设计的文档模板
  5. 将word接口文档模板重命名为2003版的.xml文件;
  6. 用notepad++打开xml文件,点击 插件 -> XMl tools-> Pretty print(libXML),格式化xml文件;
  7. 对数据进行变量替换,并将文件重名为.ftl文件
  8. 保持系统启动状态,ApiDocumentGenerateTest.handler()一键生成系统接口文档;
  9. 到resources/word下,查看生成的word接口文档
  10. 更新文档目录,补充接口描述,补充接口实例等
  11. github源码链接

数据解析工具类SwaggerApiDataHandlerUtil

package com.ocean.angel.tool.doucment.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ocean.angel.tool.doucment.dto.*;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public class SwaggerApiDataHandlerUtil {

    /**
     * 将swagger /v2/api-docs接口返回的数据转化成标准数据
     * @param documentData
     * @param jsonString
     */
    public static void dataHandler(DocumentData documentData, String jsonString) {

        try {
            // 将字符串转换为JSON对象
            JSONObject jsonObject = JSONObject.parseObject(jsonString);

            // 文档标题
            if(StrUtil.isEmpty(documentData.getTitle())) {
                String title = jsonObject.getJSONObject("info").getString("title");
                documentData.setTitle(title);
            }

            // swagger definitions 数据
            JSONObject definitionsJSONObject = jsonObject.getJSONObject("definitions");

            // swagger paths 数据
            JSONObject paths = jsonObject.getJSONObject("paths");

            // 接口tags数据,用于定义文档的二级目录
            List<ApiTag> tags = tagsHandler(paths, definitionsJSONObject);
            documentData.setTags(tags);

        } catch (Exception e) {
            log.error("SwaggerApiDataHandlerUtil.dataHandler() error. {}", e.getMessage(), e);
        }
    }

    /**
     * Tags数据处理
     * @param pathsJSONObject
     * @return
     */
    private static List<ApiTag> tagsHandler(JSONObject pathsJSONObject, JSONObject definitionsJSONObject) {

        List<ApiTag> tags = new ArrayList<>();

        for (String path : pathsJSONObject.keySet()) {

            ApiData apiData = new ApiData();

            // 请求url
            apiData.setPath(path);

            JSONObject apiJSONObject = pathsJSONObject.getJSONObject(path);
            for(String type: apiJSONObject.keySet()) {

                // 请求方式
                apiData.setType(type);
                JSONObject apiDataJSONObject = apiJSONObject.getJSONObject(type);

                // 接口名称
                apiData.setName(apiDataJSONObject.getString("description"));

                // 接口tag
                apiData.setTag(apiDataJSONObject.getJSONArray("tags").getString(0));

                // 请求参数数据处理
                JSONArray parametersJSONArray = apiDataJSONObject.getJSONArray("parameters");
                requestParamHandler(parametersJSONArray, definitionsJSONObject, apiData);

                // 响应数据和响应码数据处理
                JSONObject responsesJSONObject = apiDataJSONObject.getJSONObject("responses");
                responseHandler(responsesJSONObject, definitionsJSONObject, apiData);

                // 数据组装,将api划归到tag下
                tagsDataHandler(tags, apiData);
            }
        }

        return tags;
    }

    /**
     * 请求参数处理
     * @param parametersJSONArray
     * @param definitionsJSONObject
     * @return
     */
    private static void requestParamHandler(JSONArray parametersJSONArray, JSONObject definitionsJSONObject, ApiData apiData) {

        List<ApiParam> list = new ArrayList<>();
        for (Object object : parametersJSONArray) {
            JSONObject jsonObject = (JSONObject) object;
            JSONObject schemaJSONObject = jsonObject.getJSONObject("schema");
            if(null == schemaJSONObject) {

                // 普通参数
                ApiParam apiParam = JSONObject.parseObject(JSON.toJSONString(object), ApiParam.class);
                list.add(apiParam);
            } else {

                // 对象参数
                String ref = schemaJSONObject.getString("$ref").replace("#/definitions/", "");
                JSONObject propertiesJSONObject = definitionsJSONObject.getJSONObject(ref).getJSONObject("properties");

                // 对象参数转换,对象参数不能再嵌套
                for (String name : propertiesJSONObject.keySet()) {
                    ApiParam apiParam = new ApiParam();
                    apiParam.setName(name);
                    apiParam.setType(propertiesJSONObject.getJSONObject(name).getString("type"));
                    apiParam.setDescription(propertiesJSONObject.getJSONObject(name).getString("description"));
                    apiParam.setRequired(false);
                    list.add(apiParam);
                }
            }
        }
        // 更新接口参数
        apiData.setParameters(list);
    }

    private static void responseHandler(JSONObject responsesJSONObject, JSONObject definitionsJSONObject, ApiData apiData) {

        // 响应码列表
        List<ApiResponseCode> codes = new ArrayList<>();
        for (String code : responsesJSONObject.keySet()) {

            // 响应码
            ApiResponseCode apiResponseCode = new ApiResponseCode();
            apiResponseCode.setCode(code);
            JSONObject codeJSONObject = responsesJSONObject.getJSONObject(code);
            apiResponseCode.setMsg(codeJSONObject.getString("description"));
            codes.add(apiResponseCode);

            // 响应数据
            if("200".equals(code)) {

                JSONObject schemaJSONObject = codeJSONObject.getJSONObject("schema");
                if(null != schemaJSONObject) {
                    // 响应数据逻辑
                    List<ApiParam> responses = new ArrayList<>();
                    String ref = schemaJSONObject.getString("$ref").replace("#/definitions/", "");
                    // 引用对象参数处理
                    List<ApiParam> params = refParamHandler(ref, definitionsJSONObject);
                    if(CollUtil.isNotEmpty(params)) {
                        boolean flag = true;
                        while (flag) {
                            responses.addAll(params);
                            flag = false;

                            List<ApiParam> itemParams = new ArrayList<>();
                            for (ApiParam param : params) {
                                if(StrUtil.isNotEmpty(param.getRef())) {
                                    itemParams.addAll(refParamHandler(param.getRef(), definitionsJSONObject));
                                }
                            }

                            if(itemParams.size() > 0) {
                                params = itemParams;
                                flag = true;
                            }
                        }
                    }
                    // 更新接口响应数据
                    apiData.setResponses(responses);
                }
            }
        }
        // 更新接口响应码
        apiData.setCodes(codes);
    }

    /**
     * 将引用对象,转化为apiParam列表
     * @param ref
     * @param definitionsJSONObject
     * @return
     */
    private static List<ApiParam> refParamHandler(String ref, JSONObject definitionsJSONObject) {

        JSONObject propertiesJSONObject = definitionsJSONObject.getJSONObject(ref).getJSONObject("properties");

        List<ApiParam> params = new ArrayList<>();
        for (String name : propertiesJSONObject.keySet()) {

            ApiParam apiParam = new ApiParam();
            apiParam.setName(name);
            apiParam.setType(propertiesJSONObject.getJSONObject(name).getString("type"));
            String description = propertiesJSONObject.getJSONObject(name).getString("description");
            apiParam.setDescription(description);

            JSONObject dataJSONObject = propertiesJSONObject.getJSONObject(name);
            if(null != dataJSONObject && "data".equals(name)) {
                String itemRef = dataJSONObject.getString("$ref");
                apiParam.setRef(itemRef);
                continue;
            }

            JSONObject rowsJSONObject = propertiesJSONObject.getJSONObject(name);
            if(null != rowsJSONObject && "rows".equals(name)) {
                JSONObject itemsJSONObject = rowsJSONObject.getJSONObject("items");
                String itemRef = itemsJSONObject.getString("$ref").replace("#/definitions/", "");
                apiParam.setRef(itemRef);
            }

            params.add(apiParam);

        }

        return params;
    }

    /**
     * 接口API分类处理,划归到制定tag下
     * @param tags
     * @param apiData
     */
    private static void tagsDataHandler(List<ApiTag> tags, ApiData apiData) {

        if (CollUtil.isNotEmpty(tags)) {
            for (ApiTag tag : tags) {
                if(apiData.getTag().equals(tag.getName())) {
                    tag.getApis().add(apiData);
                    return;
                }
            }
        }

        List<ApiData> apis = new ArrayList<>();
        apis.add(apiData);

        ApiTag apiTag = new ApiTag();
        apiTag.setApis(apis);
        apiTag.setName(apiData.getTag());

        tags.add(apiTag);
    }

}

注意:如果小伙们的接口返回数据结构跟本系统不一致,在解析接口响应数据时,要根据自己接口返回的数据结构去解析。

使用指南

  1. 去github上下载本系统的源码
  2. 初始化sql脚本
  3. 修改application.yml的数据库连接配置,并启动服务
  4. 保持系统启动状态,执行ApiDocumentGenerateTest.handler()方法

使用的freemarker 变量替换的语句:

  1. 接口文档基本信息
    ${data.title}
    ${data.date}
    ${data.version}
    ${data.notes}
  1. TAG基本信息
    <#list data.tags as tag>
        ${tag.name}
    </#list>
  1. 接口基本信息
    <#list tag.apis as api>
        ${api.name}
        ${api.path}
        ${api.type}
    </#list>
  1. 接口参数信息
    <#list api.parameters as param>
        ${param.name}
        ${param.type}
        ${param.requiredStr}
        ${param.description}
    </#list>
  1. 响应参数信息
    <#list api.responses as param>
        ${param.name}
        ${param.type}
        ${param.requiredStr}
        ${param.description}
    </#list>
  1. 响应码信息
    <#list api.code as item>
        ${item.code}
        ${item.msg}
    </#list>

一键生成的接口文档截图

在这里插入图片描述

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值