smart-doc + Torna 行业领先的文档生成和管理解决方案

常见文档管理解决方案

Swagger

swagger主要原理是利用JAVA的注解和反射机制去生成文档。如果项目文档要比较清晰就必须使用大量的注解。 注解和业务代码强绑定,最终构建产出的部署包里也就必须包含swagger的依赖了。

Swagger缺点

缺点蛮多,最大的问题就是~

  • 代码侵入性太强、项目中需要依赖与业务无关的jar包、需要编写大量与业务无关的注解和参数,需要开发人员熟悉注解用途,增加开发成本,降低代码可读性。
  • 界面巨丑,排版不人性化。
  • 文档的展示依赖于项目的部署启动。不便于前端脱离后端进行接口调试,降低开发效率。
  • 添加文档备注很不方便,需要更改源代码,不能添加截图备注/不适合添加大段的备注等。
Showdoc
优点
  • 界面非常友好,阅读起来清爽。
  • 客户端RunApi可以直接调试,类似postman。
  • 不跟特定的语言耦合,因此它的使用范围相当广泛,可以支持c++、java、php、node等等常见的主流语言。
缺点
  • 生成文档比较耗时,要么通过桌面客户端RunApi手动进行填充,要么通过方法上编排特定语法,然后执行shell脚本自动生成文档,比较耗时。
  • 需要使用官网在线服务或者私有化部署服务支持。

语法案例:

/**
    * showdoc
    * @catalog 测试文档/用户相关
    * @title 用户登录
    * @description 用户登录的接口
    * @method get
    * @url https://www.showdoc.cc/home/user/login
    * @header token 可选 string 设备token 
    * @param username 必选 string 用户名 
    * @param password 必选 string 密码  
    * @param name 可选 string 用户昵称  
    * @return {"error_code":0,"data":{"uid":"1","username":"12154545","name":"吴系挂","groupid":2}}
    * @return_param groupid int 用户组id
    * @return_param name string 用户昵称
    * @remark 这里是备注信息
    * @number 99
    */

参考官方教程:https://www.showdoc.com.cn/page/741656402509783

smart-doc + Torna(本文)

smart-doc + Torna实现文档全流程自动化

如果您使用Java语言,推荐使用smart-doc + Torna

smart-doc + Torna 组成行业领先的文档生成和管理解决方案,使用smart-doc无侵入完成Java源代码和注释提取生成API文档,自动将文档推送到Torna企业级接口文档管理平台。

通过这套组合您可以实现:只需要写完Java注释就能把接口信息推送到Torna平台,从而实现接口预览、接口调试。

推送的内容有:接口名称/author/Path参数/Header/请求参数/返回参数/字典列表/公共错误码

容器化部署 与服务器操作系统 doctor容器化部署_容器化部署 与服务器操作系统

Torna

官网:https://gitee.com/durcframework/torna

企业接口文档解决方案,目标是让文档管理变得更加方便、快捷。Torna采用团队协作的方式管理和维护项目API文档,将不同形式的文档纳入进来,形成一个统一的维护方式。

Torna弥补了传统文档生成工具(如swagger)的不如之处,在保持原有功能的前提下丰富并增强了一些实用的功能。

容器化部署 与服务器操作系统 doctor容器化部署_加载_02

docker运行torna

docker环境请自行安装。

使用现有MySQL数据库创建torna库,导入数据基础数据,执行mysql.sql

下载公共镜像

docker pull tanghc2020/latest

默认docker启动没有指定配置文件,可以手动指定,方法如下:

在/opt/torna/config下创建一个空文件application.properties

执行脚本多加一句-v /opt/torna/config:/torna/config,最终变为:

docker run --name torna --restart=always \
  -p 7700:7700 \
  -e JAVA_OPTS="-Xms256m -Xmx256m" \
  -e MYSQL_HOST="172.16.60.102:3306" \
  -e MYSQL_SCHEMA="torna" \
  -e MYSQL_USERNAME="root" \
  -e MYSQL_PASSWORD="root" \
  -v /opt/torna/config:/torna/config \
  -d tanghc2020/torna:latest

需改更改的部分:

  • MYSQL_HOST:MySQL服务器地址
  • MYSQL_SCHEMA:数据库名称,默认不用改
  • MYSQL_USERNAME:MySQL用户名,要求账号能运行DDL/DML/ALTER语句
  • MYSQL_PASSWORD:MySQL密码

浏览器访问http://ip:7700,ip对应docker宿主机器ip,非docker容器ip。

使用docker-compose部署

教程:https://gitee.com/durcframework/torna/tree/master/torna-docker-compose

smart-doc

官网:https://smart-doc-group.github.io/#/zh-cn/

一个无侵入式的api接口文档生成器。

概述

smart-doc在业内率先提出基于JAVA泛型定义推导的理念, 完全基于接口源码来分析生成接口文档,不采用任何注解侵入到业务代码中。 你只需要按照java-doc标准编写注释, smart-doc就能帮你生成一个简易明了的Markdown、HTML5、Postman Collection2.0+、OpenAPI 3.0+的文档。

  • smart-doc是一款同时支持JAVA RESTful API和Apache Dubbo RPC接口文档生成的工具。
  • 完全基于注释生成文档,做到零侵入。

特性

  • 零注解、零学习成本、只需要写标准JAVA注释。
  • 基于源代码接口定义自动推导,强大的返回结构推导。
  • 支持Spring MVC、Spring Boot、Spring Boot Web Flux(Controller书写方式)、Feign。
  • 支持Callable、Future、CompletableFuture等异步接口返回的推导。
  • 支持JavaBean上的JSR303参数校验规范,包括分组验证。
  • 对JSON请求参数的接口能够自动生成模拟JSON参数。
  • 对一些常用字段定义能够生成有效的模拟值。
  • 支持生成JSON返回值示例。
  • 支持从项目外部加载源代码来生成字段注释(包括标准规范发布的jar包)。
  • 支持生成多种格式文档:Markdown、HTML5、Asciidoctor、Postman Collection、OpenAPI 3.0。 开放文档数据,可自由实现接入文档管理系统。
  • 支持导出错误码和定义在代码中的各种字典码到接口文档。
  • 支持Maven、Gradle插件式轻松集成。
  • 支持Apache Dubbo RPC接口文档生成。
  • debug接口调试html5页面完全支持文件上传,下载(@download tag标记下载方法)测试。

Maven单模块项目中使用插件

添加插件

pom.xml中添加插件。

<plugin>
				<groupId>com.github.shalousun</groupId>
				<artifactId>smart-doc-maven-plugin</artifactId>
				<version>2.2.3</version>
				<configuration>
					<!--指定生成文档的使用的配置文件,配置文件放在自己的项目中-->
					<configFile>./src/main/resources/smart-doc.json</configFile>
					<!--指定项目名称-->
					<projectName>测试</projectName>
					<!--smart-doc实现自动分析依赖树加载第三方依赖的源码,如果一些框架依赖库加载不到导致报错,这时请使用excludes排除掉-->
					<excludes>
						<!--格式为:groupId:artifactId;参考如下-->
						<!--也可以支持正则式如:com.alibaba:.* -->
						<exclude>com.alibaba:fastjson</exclude>
					</excludes>
					<!--includes配置用于配置加载外部依赖源码,配置后插件会按照配置项加载外部源代码而不是自动加载所有,因此使用时需要注意-->
					<!--smart-doc能自动分析依赖树加载所有依赖源码,原则上会影响文档构建效率,因此你可以使用includes来让插件加载你配置的组件-->
					<includes>
						<!--格式为:groupId:artifactId;参考如下-->
						<!--也可以支持正则式如:com.alibaba:.* -->
						<include>com.alibaba:fastjson</include>
						<!-- 如果配置了includes的情况下, 使用了mybatis-plus的分页需要include所使用的源码包 -->
						<include>com.baomidou:mybatis-plus-extension</include>
						<!-- 如果配置了includes的情况下, 使用了jpa的分页需要include所使用的源码包 -->
						<include>org.springframework.data:spring-data-commons</include>
					</includes>
				</configuration>
			</plugin>
添加配置文件

/src/main/resources路径下添加smart-doc.json配置文件。

{
  "serverUrl": "http://{{server}}:8080/hzt-api",
  "isStrict": false,
  "allInOne": true,
  "coverOld": true,
  "packageFilters": "com.github.modules.*",
  "projectName": "XXX项目接口文档",
  "appToken": "2c89d5c9c29a4113ae5e7586fb8ad693",
  "openUrl": "http://localhost:7700/api",
  "isReplace":true,
  "debugEnvName":"测试环境",
  "debugEnvUrl":"http://127.0.0.1:8080/hzt-api",
  "style":"xt256",
  "outPath":"doc"
}

注意:该配置中appToken为torna平台appToken,后文中会讲到。

添加编排文件

外层工程目录下放Makefile编排文件。

# 注意:window环境下先安装MinGW,idea中Makefile Support插件
# Makefile 命令开头必须为tab键

# 生成hzt-api的文档,生成文档推送到Torna平台
hzt-api@torna-rest:
	mvn -X smart-doc:torna-rest -Dfile.encoding=UTF-8

注意:Makefile编排文件运行需要idea安装Makefile插件。

Maven多模块项目中使用插件

├─parent
├──common
│   pom.xml
├──web1
│   pom.xml
├──web2
│   pom.xml
└─pom.xml

web1和web2都依赖于common, 这种情况下如果跑到web1下或者web2目录下直接执行mvn命令来编译 都是无法完成的。需要在根目录上去执行命令编译命令才能通过,而smart-doc插件会通过类加载器去加载用户配置的一些类,因此需要调用编译的和执行命令是一样的。这种情况下建议你将smart-doc-maven-plugin放到根pom.xml中,在web1和web2中放置各自的smart-doc.json配置。 然后通过-pl去指定让smart-doc生成指定 模块的文档。

操作命令如下:

# 生成web1模块的api文档
mvn smart-doc:markdown -Dfile.encoding=UTF-8  -pl :web1 -am
# 生成web2模块的api文档
mvn smart-doc:markdown -Dfile.encoding=UTF-8  -pl :web2 -am
添加插件

父模块的pom.xml中添加插件。

<plugin>
                <groupId>com.github.shalousun</groupId>
                <artifactId>smart-doc-maven-plugin</artifactId>
                <version>2.2.3</version>
                <configuration>
                    <!--指定生成文档的使用的配置文件,配置文件放在自己的项目中-->
                    <configFile>${basedir}/src/main/resources/smart-doc.json</configFile>
                    <!--指定项目名称-->
                    <projectName>测试</projectName>
                    <!--smart-doc实现自动分析依赖树加载第三方依赖的源码,如果一些框架依赖库加载不到导致报错,这时请使用excludes排除掉-->
                    <excludes>
                        <!--格式为:groupId:artifactId;参考如下-->
                        <!--也可以支持正则式如:com.alibaba:.* -->
                        <exclude>com.alibaba:fastjson</exclude>
                    </excludes>
                    <!--includes配置用于配置加载外部依赖源码,配置后插件会按照配置项加载外部源代码而不是自动加载所有,因此使用时需要注意-->
                    <!--smart-doc能自动分析依赖树加载所有依赖源码,原则上会影响文档构建效率,因此你可以使用includes来让插件加载你配置的组件-->
                    <includes>
                        <!--格式为:groupId:artifactId;参考如下-->
                        <!--也可以支持正则式如:com.alibaba:.* -->
                        <include>com.alibaba:fastjson</include>
                        <!-- 如果配置了includes的情况下, 使用了mybatis-plus的分页需要include所使用的源码包 -->
                        <include>com.baomidou:mybatis-plus-extension</include>
                        <!-- 如果配置了includes的情况下, 使用了jpa的分页需要include所使用的源码包 -->
                        <include>org.springframework.data:spring-data-commons</include>
                    </includes>
                </configuration>
            </plugin>
添加配置文件

在每一个需要生成文档的子模块的/src/main/resources路径下添加smart-doc.json配置文件。

{
  "serverUrl": "http://{{server}}:8183/api/",
  "isStrict": false,
  "allInOne": true,
  "coverOld": true,
  "packageFilters": "com.abc.*",
  "projectName": "XXX项目接口文档",
  "appToken": "2c89d5c9c29a4113ae5e7586fb8ad693",
  "openUrl": "http://localhost:7700/api",
  "isReplace":true,
  "debugEnvName":"测试环境",
  "debugEnvUrl":"http://127.0.0.1:8080",
  "style":"xt256",
  "outPath":"doc"
}
添加编排文件

父模块根路径下放Makefile编排文件。

# Maven多模块中使用插件:https://smart-doc-group.github.io/#/zh-cn/plugins/maven_plugin?id=maven%e5%a4%9a%e6%a8%a1%e5%9d%97%e4%b8%ad%e4%bd%bf%e7%94%a8%e6%8f%92%e4%bb%b6
# 多模块怎么构建文档?https://smart-doc-group.github.io/#/zh-cn/QA?id=%e5%a4%9a%e6%a8%a1%e5%9d%97%e6%80%8e%e4%b9%88%e6%9e%84%e5%bb%ba%e6%96%87%e6%a1%a3%e5%95%8a%ef%bc%9f

# 注意:window环境下先安装MinGW,idea中Makefile Support插件
# Makefile 命令开头必须为tab键

# 生成demo-a模块的文档
demo-a@torna-rest:
	mvn -X smart-doc:torna-rest -Dfile.encoding=UTF-8  -pl :demo-a -am

# 生成demo-b模块的文档
demo-b@torna-rest:
	mvn -X smart-doc:torna-rest -Dfile.encoding=UTF-8  -pl :demo-b -am

# 生成demo-c模块的文档
demo-c@torna-rest:
	mvn -X smart-doc:torna-rest -Dfile.encoding=UTF-8  -pl :demo-c -am

# 生成demo-d模块的文档
demo-d@torna-rest:
	mvn -X smart-doc:torna-rest -Dfile.encoding=UTF-8  -pl :demo-d -am

注意:Makefile编排文件运行需要idea安装Makefile插件。

生成文档

执行命令

添加好插件和配置文件后可以直接运行Maven命令生成文档。

//生成html
mvn -Dfile.encoding=UTF-8 smart-doc:html
//生成markdown
mvn -Dfile.encoding=UTF-8 smart-doc:markdown
//生成adoc
mvn -Dfile.encoding=UTF-8 smart-doc:adoc
//生成postman json数据
mvn -Dfile.encoding=UTF-8 smart-doc:postman
// 生成 Open Api 3.0+,Since smart-doc-maven-plugin 1.1.5
mvn -Dfile.encoding=UTF-8 smart-doc:openapi
// 生成文档推送到Torna平台
mvn -Dfile.encoding=UTF-8 smart-doc:torna-rest

// Apache Dubbo RPC文档
// Generate html
mvn -Dfile.encoding=UTF-8 smart-doc:rpc-html
// Generate markdown
mvn -Dfile.encoding=UTF-8 smart-doc:rpc-markdown
// Generate adoc
mvn -Dfile.encoding=UTF-8 smart-doc:rpc-adoc

// 生成dubbo接口文档推送到torna
mvn -Dfile.encoding=UTF-8 smart-doc:torna-rpc
执行编排文件(推荐)

利用idea插件Makefile执行Makefile编排文件。

容器化部署 与服务器操作系统 doctor容器化部署_配置文件_03

Idea中Maven插件中执行

在idea编辑器中找到maven插件下的如下执行,双击执行,据说可能出现乱码,不推荐。

容器化部署 与服务器操作系统 doctor容器化部署_配置文件_04

案例演示

MySQL建表
CREATE TABLE `app_user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `is_delete` int NOT NULL DEFAULT '0' COMMENT '是否删除(0否 1是)',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '用户名',
  `real_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '真实姓名',
  `pass_word` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '密码',
  `user_phone` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '手机号',
  `salt` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '盐',
  `email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '邮箱',
  `create_user_id` bigint NOT NULL DEFAULT '-1' COMMENT '创建者ID',
  `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态  0:禁用   1:正常',
  `openid` varchar(100) NOT NULL DEFAULT '' COMMENT '微信小程序用户唯一标识 openid',
  `is_wx_user` tinyint NOT NULL DEFAULT '0' COMMENT '是否是微信用户(0否,1是)',
  `nick_name` varchar(100) NOT NULL DEFAULT '' COMMENT '昵称',
  `avatar_url` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户头像URL地址',
  PRIMARY KEY (`id`,`openid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=61 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='App用户表';
生成代码

利用MybatisPlusGenerator(生成工具请前往文章查阅)工具生成Controller、service、serviceImpl、mapper、mapper.xml、entity等基础代码,此时的代码中已经包含有注释,像这样~

实体对象
package qgs.csmp.test.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;

/**
 * <p>
 * App用户表
 * </p>
 *
 * @author songfayuan
 * @since 2022-07-06 09:33:44
 */
@Data
@TableName("app_user")
public class AppUser implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 是否删除(0否 1是)
     */
    @TableField("is_delete")
    private Integer isDelete;

    /**
     * 创建时间
     */
    @TableField("create_time")
    private Date createTime;

    /**
     * 更新时间
     */
    @TableField("update_time")
    private Date updateTime;

    /**
     * 用户名
     */
    @TableField("user_name")
    private String userName;

    /**
     * 真实姓名
     */
    @TableField("real_name")
    private String realName;

    /**
     * 密码
     */
    @TableField("pass_word")
    private String passWord;

    /**
     * 手机号
     */
    @TableField("user_phone")
    private String userPhone;

    /**
     * 盐
     */
    @TableField("salt")
    private String salt;

    /**
     * 邮箱
     */
    @TableField("email")
    private String email;

    /**
     * 创建者ID
     */
    @TableField("create_user_id")
    private Long createUserId;

    /**
     * 状态  0:禁用   1:正常
     */
    @TableField("status")
    private Integer status;

    /**
     * 微信小程序用户唯一标识 openid
     */
    @TableId("openid")
    private String openid;

    /**
     * 是否是微信用户(0否,1是)
     */
    @TableField("is_wx_user")
    private Integer isWxUser;

    /**
     * 昵称
     */
    @TableField("nick_name")
    private String nickName;

    /**
     * 用户头像URL地址
     */
    @TableField("avatar_url")
    private String avatarUrl;
}
Controller类
package qgs.csmp.test.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import qgs.csmp.core.BaseController;
import qgs.csmp.test.entity.AppUser;
import qgs.csmp.test.service.AppUserService;
import qgs.csmp.util.CommonConstant;
import qgs.csmp.util.Query;
import qgs.csmp.util.ResponseUtils;

import java.util.Date;
import java.util.Map;

/**
 * <p>
 * App用户表 前端控制器
 * </p>
 *
 * @author songfayuan
 * @since 2022-07-06 09:33:44
 */
@RestController
@RequestMapping("/appUser")
public class AppUserController extends BaseController {

    @Autowired
    private AppUserService appUserService;

    /**
     * 通过ID查询App用户表
     *
     * @param id ID
     * @return com.github.common.utils.Response
     */
    @GetMapping("/{id}")
    public ResponseUtils get(@PathVariable Integer id) {
        if (id == null){
            return ResponseUtils.errorResponse("ID不能为空");
        }
        return ResponseUtils.success(this.appUserService.getById(id));
    }


    /**
     * 分页查询App用户表
     *
     * @param params
     * @return com.github.common.utils.Response
     */
    @RequestMapping("/page")
    public ResponseUtils page(@RequestParam Map<String, Object> params, AppUser appUser) {
        return ResponseUtils.success(this.appUserService.page(new Query<AppUser>().getPage(params), new QueryWrapper<AppUser>()
            .eq(CommonConstant.IS_DELETE, CommonConstant.INT_STATUS_NORMAL)));
    }

    /**
     * 添加App用户表
     * @param  appUser  实体
     * @return com.github.common.utils.Response<AppUser>
     */
    @PostMapping("/add")
    public ResponseUtils add(@RequestBody AppUser appUser) {
        //TODO 添加数据校验
        boolean res = this.appUserService.save(appUser);
        if (res){
            return ResponseUtils.successResponse("操作成功");
        }
        return ResponseUtils.errorResponse("操作失败");
    }

    /**
     * 删除App用户表
     * @param id ID
     * @return com.github.common.utils.Response<AppUser>
     */
    @DeleteMapping("/{id}")
    public ResponseUtils delete(@PathVariable Long id) {
        if (id == null){
            return ResponseUtils.errorResponse("ID不能为空");
        }
        AppUser appUser = new AppUser();
        appUser.setId(id);
        appUser.setUpdateTime(new Date());
        appUser.setIsDelete(CommonConstant.INT_STATUS_DEL);
        boolean res = this.appUserService.updateById(appUser);
        if (res){
            return ResponseUtils.successResponse("操作成功");
        }
        return ResponseUtils.errorResponse("操作失败");
    }

    /**
     * 编辑App用户表
     * @param  appUser  实体
     * @return com.github.common.utils.Response<AppUser>
     */
    @PutMapping("/edit")
    public ResponseUtils edit(@RequestBody AppUser appUser) {
        if (appUser.getId() == null){
            return ResponseUtils.errorResponse("ID不能为空");
        }
        //TODO 添加数据校验
        appUser.setUpdateTime(new Date());
        boolean res = this.appUserService.updateById(appUser);
        if (res){
            return ResponseUtils.successResponse("操作成功");
        }
        return ResponseUtils.errorResponse("操作失败");
    }

}

然后根据业务实现业务逻辑,根据Java-doc注释标准写好注释,运行Makefile编排文件生成接口文档即可。

文档效果

容器化部署 与服务器操作系统 doctor容器化部署_加载_05

更多的文档功能请自行探索~