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/请求参数/返回参数/字典列表/公共错误码
Torna
官网:https://gitee.com/durcframework/torna
企业接口文档解决方案,目标是让文档管理变得更加方便、快捷。Torna采用团队协作的方式管理和维护项目API文档,将不同形式的文档纳入进来,形成一个统一的维护方式。
Torna弥补了传统文档生成工具(如swagger)的不如之处,在保持原有功能的前提下丰富并增强了一些实用的功能。
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编排文件。
Idea中Maven插件中执行
在idea编辑器中找到maven插件下的如下执行,双击执行,据说可能出现乱码,不推荐。
案例演示
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编排文件生成接口文档即可。
文档效果
更多的文档功能请自行探索~