init smart-doc

This commit is contained in:
oppofind 2019-09-10 09:39:04 +08:00
commit 9f4ee124f2
32 changed files with 3478 additions and 0 deletions

25
.gitignore vendored Normal file
View File

@ -0,0 +1,25 @@
#Compiled class file
*.class
.idea
*.iml
target
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.war
*.ear
*.zip
*.tar.gz
*.rar
*.jar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

213
README.md Normal file
View File

@ -0,0 +1,213 @@
smart-doc是一个java restful api文档生成工具smart-doc颠覆了传统类似swagger这种大量采用注解侵入来生成文档的实现方法。
smart-doc完全基于接口源码分析来生成接口文档完全做到零注解侵入你只需要按照java标准注释的写就能得到一个标准的markdown接口文档。
如果你已经厌倦了swagger等文档工具的注解和强侵入污染那请拥抱smart-doc吧
**重点** smart-doc已成功被开源中国收录并且被一加、iflytek等知名公司采用
# smart-doc和其他工具对比
没有对比就没有伤害,如果不是为了解决问题又何必发明新的轮子。
# smart-doc使用
## 1.导入smart-doc工具的依赖包
```
<dependency>
<groupId>com.github.shalousun</groupId>
<artifactId>smart-doc</artifactId>
<version>1.6</version>
<scope>test</scope>
</dependency>
```
## 2.编写一个单元测试类
妈妈再也不用担心我不会用了So easy!
```
/**
* Description:
* ApiDoc测试
*
* @author yu 2018/06/11.
*/
public class ApiDocTest {
/**
* 简单型接口不需要指定请求头并且项目是maven的.
*
*/
@Test
public void testBuilderControllersApiSimple(){
//将生成的文档输出到d:\md目录下严格模式下api-doc会检测Controller的接口注释
ApiDocBuilder.builderControllersApi("d:\\md",true);
}
/**
* 包括设置请求头,缺失注释的字段批量在文档生成期使用定义好的注释
*/
@Test
public void testBuilderControllersApi() {
ApiConfig config = new ApiConfig();
config.setStrict(true);
config.setAllInOne(true);//true则将所有接口合并到一个AllInOne中markdown中错误码合并到最后
config.setOutPath("d:\\md");
// @since 1.2,如果不配置该选项则默认匹配全部的controller,
// 如果需要配置有多个controller可以使用逗号隔开
config.setPackageFilters("com.power.doc.controller.app");
//默认是src/main/java,maven项目可以不写
config.setSourcePaths(
SourcePath.path().setDesc("本项目代码").setPath("src/test/java"),
SourcePath.path().setPath("E:\\Test\\Mybatis-PageHelper-master\\src\\main\\java"),
SourcePath.path().setDesc("加载项目外代码").setPath("E:\\ApplicationPower\\ApplicationPower\\Common-util\\src\\main\\java")
);
//设置请求头,如果没有请求头,可以不用设置
config.setRequestHeaders(
ApiReqHeader.header().setName("access_token").setType("string").setDesc("Basic auth credentials"),
ApiReqHeader.header().setName("user_uuid").setType("string").setDesc("User Uuid key")
);
//对于外部jar的类api-doc目前无法自动获取注释
//如果有这种场景则自己添加字段和注释api-doc后期遇到同名字段则直接给相应字段加注释
config.setCustomResponseFields(
CustomRespField.field().setName("success").setDesc("成功返回true,失败返回false"),
CustomRespField.field().setName("message").setDesc("接口响应信息"),
CustomRespField.field().setName("data").setDesc("接口响应数据"),
CustomRespField.field().setName("code").setValue("00000").setDesc("响应代码")
);
//设置项目错误码列表,设置自动生成错误列表
List<ApiErrorCode> errorCodeList = new ArrayList<>();
for(ErrorCodeEnum codeEnum:ErrorCodeEnum.values()){
ApiErrorCode errorCode = new ApiErrorCode();
errorCode.setValue(codeEnum.getValue()).setDesc(codeEnum.getDesc());
errorCodeList.add(errorCode);
}
//不是必须
config.setErrorCodes(errorCodeList);
ApiDocBuilder.builderControllersApi(config);
}
}
```
# smart-doc的缺点
万物有痕,所以美!
## 1.泛型推导不完美
目前api-doc在泛型的字段推导上是不完美的。例如
**样例1**
```
public class Teacher<T,M,K> {
private T data;
private Object data3
private K data1;
private M data2;
/**
* 年龄
*/
private int age;
}
```
介于当前的推导算法。泛型字段属性的顺序和Teacher泛型参数顺序一致否则api-doc推导的json数据会出现张冠李戴的情况。当然你只要调整一下属性的顺序就可以了。
**样例2**
```
public class Teacher<T,M,K> {
private T data;
private Object data3
private K data1;
private M data2;
/**
* 年龄
*/
private int age;
}
```
上面这种在泛型属性中突然插入了一个Object的属性因为泛型本身在编译后会被擦除因此这种在中间插入的情况会导致目前算法推导出错同样实现数据张冠李戴因此推荐如果前面泛型的字段的前面字段不属于基本类型(String,int,Double等)的都移动到泛型后面去。
## 2.form data数据请求处理不完美
这并不是smart-doc不具备能力来生成处理form data请求式接口文档而是目前作者不知道怎么来做这个模板尤其是请求示例数据的模板怎么提供。但是api-doc还是提供了基本类型请求参数的文档生成。
所以真诚的希望您可以提供一个form data请求式的模板然后smart-doc在后续的版本更新中完善form data请求参数的处理。
## 3. 对于继承的属性为推导到文档
由于api-doc目前采用qdoc来实现了代码的加载由于qdoc在这方面没有提供直接支持并且在泛型处理上也太好考虑后期会去使用其他框架来作为api-doc
的底层实现,因此这项先搁浅,如果使用者遇到这样问题可以定义一个返回包装类解决。
## api-doc 测试demo
https://github.com/shalousun/api-doc-test
## 效果图[markdown截图]
### 接口头部效果图
![输入图片说明](https://images.gitee.com/uploads/images/2018/0905/173104_abcf4345_144669.png "1.png")
### 请求参数示例效果图
![请求参数示例](https://images.gitee.com/uploads/images/2018/0905/172510_853735b9_144669.png "2.png")
### 响应参数示例效果图
![响应参数示例](https://images.gitee.com/uploads/images/2018/0905/172538_1918820c_144669.png "3.png")
## smart-doc版本
版本小于1.0都属于使用版正式1.0起始发布将会等到文中提到的问题解决后才发布。
### 版本号0.1
- 更新日期2018-06-25
- 更新内容:
1. 手册将api-doc发布到中央仓库
### 版本号0.2
- 更新日期2018-07-07
- 更新内容:
1. 修改api-doc泛型推导的bug.
#### 版本号0.3
- 更新日期2018-07-10
- 更新内容:
1. api-doc增加对jackson和fastjson注解的支持可根据注解定义来生成返回信息。
#### 版本号0.4
- 更新日期2018-07-11
- 更新内容:
1. 修改api-doc对类继承属性的支持。
#### 版本号0.5
- 更新日期2018-08-04
- 更新内容:
1. 修改api-doc对各种字段的解析错误bug。
#### 版本号0.5
- 更新日期2018-08-23
- 更新内容:
1. 将api-doc重命名为smart-doc并发布到中央仓库
#### 版本号1.0
- 更新日期2018-08-25
- 更新内容:
1. smart-doc增加将所有文档导出归档到一个markdown中件的功能
2. 参考阿里开发手册将直接提升到1.0,之前的版本主要是个人内部测试
#### 版本号1.1
- 更新日期2018-08-30
- 更新内容:
1. 修改PostMapping和GetMapping value为空报错的bug
2. 增强时间字段的mock数据创建
3. 修改smart-doc解析自引用对象出错的bug
#### 版本号1.2
- 更新日期2018-09-04
- 更新内容:
1. 根据用户反馈增加controller报名过滤功能该功能为可选项
#### 版本号1.3
- 更新日期2018-09-15
- 更新内容:
1. 增加PutMapping和DeleteMapping支持
2. 添加字符串date和Date类型时间的模拟值生成
# 问题反馈
目前已经能够支持许多复杂和返回类型和类结构推导,但是由于目前做采用的底层工具处理能力不是很好,所以有些功能做的不够彻底。
当然基于目前的版如果在使用中发现问题欢迎及时的提出反馈的建议。

136
doc/List.md Normal file
View File

@ -0,0 +1,136 @@
关于list结构的返回json数据测试
# List<String>结构
api-doc对于List中返回基础数据类型都是支持的
```
/**
* List<String>
*
* @return
*/
@GetMapping(value = "listString")
public List<String> testList() {
return null;
}
```
api-doc生成的响应数据
```
[ "ivvqah","isrz5x"]
```
# List<Map<String,String>>结构
```
/**
*
* @return
*/
@GetMapping(value = "/map/Primitive")
public List<Map<String,String>> testMap() {
return null;
}
```
api-doc生成的响应数据
```
[{
"mapKey1": "o9mibj",
"mapKey2": "3dnnrn"
}]
```
# List<Map<String,T>>结构
```
@GetMapping(value = "/map/Primitive")
public List<Map<String,Student>> testMap() {
return null;
}
```
相应数据省略
# 测试List<T>结构
```
/**
* 测试List<T>结构
* @return
*/
@GetMapping(value = "/map/Primitive")
public List<Teacher> testMap() {
return null;
}
```
# List<T<M,N>>结构
```
/**
* 测试List<T<M,N>>结构
* @return
*/
@GetMapping(value = "/map/Primitive")
public List<Teacher<User,User>> testMap() {
return null;
}
```
# List<Map<M,N<P,k>>>超复杂结构
```
/**
* 测试List<Map<M,N<P,k>>>超复杂结构
* @return
*/
@GetMapping(value = "/map/Primitive")
public List<Map<String,Teacher<User,User>>> testMap() {
return null;
}
```
api-doc自动返回的数据
```
[{
"mapKey": {
"data": {
"userName": "lxh2yi",
"userAddress": "6jfp3h",
"userAge": 741
},
"data1": {
"userName": "1wp54g",
"userAddress": "8ul6m4",
"userAge": 550
},
"age": 10
}
}]
```
# List<T<List<M>,List<M>,List<M>>>超复杂结构
```
/**
* List<T<List<M>,List<M>,List<M>>>
* @return
*/
@GetMapping(value = "listString")
public List<Teacher<List<User>,List<User>,List<User>>> testListString(){
return null;
}
```
# 其他复杂结构
```
/**
* List<T<List<M>,List<M>,List<M>>>
*
* @return
*/
@GetMapping(value = "listString")
public List<Teacher<Teacher<User,User,User>,User,User>> testListString() {
return null;
}
@GetMapping(value = "listString")
public List<Teacher<Teacher<User,User,User>,Teacher<User,User,User>,Teacher<User,User,User>>> testListString() {
return null;
}
```
**注意:** api-doc为了传入的复杂泛型结构数据做了许多情况的测试目前基本能兼容系统开发中95%以上的List返回接口
也提供了一些能够处理的很复杂的泛型结构,但是这种复杂的泛型结构在开发中是不被推荐的。

153
doc/Map.md Normal file
View File

@ -0,0 +1,153 @@
Api-doc对于api中map结构数据的json化处理多组测试用例,对于map返回的json结构
目前基本仅仅支持String类型的key。
**基础数据类型:** json支持的基本java数据类型(不包含byte,包含String)
# map使用基础数据类型
```
/**
* 测试map使用基础数据类型
* @return
*/
@GetMapping(value = "/map/Primitive")
public Map<String,Integer> testMap() {
return null;
}
```
api-doc 生成的json:
```
{
"mapKey1": 721,
"mapKey2": 280
}
```
# map使用Object
因为api-doc使用的是无侵入静态分析生成api文档因此对于直接使用Object做map value的接口api-doc无法准确的生成json。
所以api-doc返回是会在默认json中加一段警告使用者需要自己去修改返回数据或者是使用显示的类型数据结构。
```
/**
* 测试map使用基础数据类型
* @return
*/
@GetMapping(value = "/map/Primitive")
public Map<String,Object> testMap() {
return null;
}
```
api-doc 生成的json:
```
{
"mapKey": {
"waring": "You may use java.util.Object for Map value;Api-doc can't be handle."
}
}
```
# map中属于自己定义的简单数据结构
User对象的属性仅仅是基本数据类型
```
/**
* 测试map使用自定义数据结构
* @return
*/
@GetMapping(value = "/map/Primitive")
public Map<String,User> testMap() {
return null;
}
```
api-doc 生成的json:
```
{
"mapKey": {
"userName": "7t2ccy",
"userAddress": "3ipy7g",
"userAge": 280
}
}
```
# map中属于自己定义的复杂数据结构
Student对象的属性有基本类型又有User类型和Map类型的属性。
```
/**
* 测试map使用自定义数据结构
* @return
*/
@GetMapping(value = "/map/Primitive")
public Map<String,Student> testMap() {
return null;
}
```
api-doc 生成的json:
```
{
"mapKey": {
"stuName": "9cwzml",
"stuAge": 792,
"stuAddress": "rdfmtx",
"user": {
"userName": "fjglql",
"userAddress": "yy6vkf",
"userAge": 398
},
"userMap": {
"mapKey": {
"userName": "paw90w",
"userAddress": "mnmz42",
"userAge": 937
}
},
"user1": {
"userName": "rr3v6g",
"userAddress": "rbeorq",
"userAge": 399
}
}
}
```
# Map<M,N<P,k>>复杂结构
```
{
"mapKey":{
"data":{
"userName":"tumrit",
"userAddress":"v8fvdi",
"userAge":465
},
"data1":{
"userName":"f7wbwk",
"userAddress":"brdh8j",
"userAge":345
},
"age":194
}
}
```
# Map<String,T<List<M>,N>超复杂结构
```
/**
* Map<String,T<List<M>,N>超复杂结构
* @return
*/
@GetMapping(value = "/map/Primitive")
public Map<String,Teacher<List<User>,User>> testMap() {
return null;
}
```
# Map其他复杂结构
对于map的key采用多泛型的情况目前api-doc也是支持的。
```
/**
* Map<String,T<List<M>,N>超复杂结构
* @return
*/
public Map<String,Teacher<Map<String,User>,Map<String,User>,Map<String,User>>> testMap() {
return null;
}
```
**注意:** api-doc为了传入的复杂泛型结构数据做了许多情况的测试目前基本能兼容系统开发中95%以上的Map返回接口
也提供了一些能够处理的很复杂的泛型结构,但是这种复杂的泛型结构在开发中是不被推荐的。

31
doc/error.md Normal file
View File

@ -0,0 +1,31 @@
api-doc对Spring mvc或者SpringBoot应用的Controller接口返回做了一些强制规约一旦在代码中使用
这些被api-doc不推荐的接口返回类型api-doc将会直接报错。
# 违反规约的实例
## 直接返回Object
```
/**
* 返回object
* @return
*/
@GetMapping("/test/Object")
public Object getMe(){
return null;
}
```
报错提示Please do not return java.lang.Object directly in api interface.
## 将非String对象作为Map的key然后将map作为接口中返回
```
/**
* 测试object的作为map的key
* @return
*/
@GetMapping("/test/map")
public Map<Object,Object> objectMap(){
return null;
}
```

141
pom.xml Normal file
View File

@ -0,0 +1,141 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<groupId>com.github.shalousun</groupId>
<modelVersion>4.0.0</modelVersion>
<artifactId>smart-doc</artifactId>
<packaging>jar</packaging>
<version>1.6.1</version>
<name>smart-doc</name>
<url>https://github.com/shalousun/ApplicationPower.git</url>
<description>ApplicationPower smart-doc</description>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<scm>
<url>https://github.com/shalousun/ApplicationPower.git</url>
<connection>scm:https://github.com/shalousun/ApplicationPower.git</connection>
<developerConnection>scm:https://github.com/shalousun/ApplicationPower.git</developerConnection>
</scm>
<developers>
<developer>
<name>shalousun</name>
<email>836575280@qq.com</email>
<url>https://github.com/shalousun</url>
</developer>
</developers>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.qdox</groupId>
<artifactId>qdox</artifactId>
<version>2.0-M10</version>
</dependency>
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>com.github.shalousun</groupId>
<artifactId>common-util</artifactId>
<version>1.8.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--compiler-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- Source -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- JavaDoc -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<finalName>smart-doc</finalName>
</build>
<profiles>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<distributionManagement>
<snapshotRepository>
<id>oss</id>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</snapshotRepository>
<repository>
<id>oss</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
</profile>
</profiles>
</project>

BIN
screen/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

BIN
screen/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

BIN
screen/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

View File

@ -0,0 +1,116 @@
package com.power.doc.builder;
import com.power.common.util.CollectionUtil;
import com.power.common.util.DateTimeUtil;
import com.power.common.util.FileUtil;
import com.power.common.util.StringUtil;
import com.power.doc.model.ApiConfig;
import com.power.doc.model.ApiDoc;
import com.power.doc.model.ApiErrorCode;
import com.power.doc.utils.BeetlTemplateUtil;
import org.beetl.core.Template;
import java.util.List;
public class ApiDocBuilder {
public static final String FILE_SEPARATOR = System.getProperty("file.separator");
/**
* 生成所有controller的api文档
*
* @param outPath 代码输出路径
* @param isStrict 是否启用严格模式
*/
public static void builderControllersApi(String outPath, boolean isStrict) {
SourceBuilder sourceBuilder = new SourceBuilder(isStrict);
List<ApiDoc> apiDocList = sourceBuilder.getControllerApiData();
buildApiDoc(apiDocList, outPath);
}
/**
* @param config 配置
*/
public static void builderControllersApi(ApiConfig config) {
if (null == config) {
throw new NullPointerException("ApiConfig can't be null");
}
if (StringUtil.isEmpty(config.getOutPath())) {
throw new RuntimeException("doc output path can't be null or empty");
}
SourceBuilder sourceBuilder = new SourceBuilder(config);
List<ApiDoc> apiDocList = sourceBuilder.getControllerApiData();
if (config.isAllInOne()) {
buildAllInOne(apiDocList, config);
} else {
buildApiDoc(apiDocList, config.getOutPath());
buildErrorCodeDoc(config.getErrorCodes(), config.getOutPath());
}
}
/**
* 生成单个controller的api文档
*
* @param outPath 代码输出路径
* @param controllerName controller 名称
*/
public static void buildSingleControllerApi(String outPath, String controllerName) {
FileUtil.mkdirs(outPath);
SourceBuilder sourceBuilder = new SourceBuilder(true);
ApiDoc doc = sourceBuilder.getSingleControllerApiData(controllerName);
Template mapper = BeetlTemplateUtil.getByName("ApiDoc.btl");
mapper.binding("desc", doc.getDesc());
mapper.binding("name", doc.getName());
mapper.binding("list", doc.getList());//类名
FileUtil.writeFileNotAppend(mapper.render(), outPath + FILE_SEPARATOR + doc.getName() + "Api.md");
}
/**
* 公共生成controller api 文档
*
* @param apiDocList
* @param outPath
*/
private static void buildApiDoc(List<ApiDoc> apiDocList, String outPath) {
FileUtil.mkdirs(outPath);
for (ApiDoc doc : apiDocList) {
Template mapper = BeetlTemplateUtil.getByName("ApiDoc.btl");
mapper.binding("desc", doc.getDesc());
mapper.binding("name", doc.getName());
mapper.binding("list", doc.getList());//类名
FileUtil.nioWriteFile(mapper.render(), outPath + FILE_SEPARATOR + doc.getName() + "Api.md");
}
}
/**
* 合并所有接口文档到一个文档中
*
* @param apiDocList
*/
private static void buildAllInOne(List<ApiDoc> apiDocList, ApiConfig config) {
String outPath = config.getOutPath();
FileUtil.mkdirs(outPath);
Template tpl = BeetlTemplateUtil.getByName("AllInOne.btl");
tpl.binding("apiDocList", apiDocList);
tpl.binding("errorCodeList", config.getErrorCodes());
tpl.binding("revisionLogList", config.getRevisionLogs());
String version = DateTimeUtil.long2Str(System.currentTimeMillis(), "yyyyMMddHHmm");
FileUtil.nioWriteFile(tpl.render(), outPath + FILE_SEPARATOR + "AllInOne-V" + version + ".md");
}
/**
* 构建错误码列表
*
* @param errorCodeList 错误列表
* @param outPath
*/
private static void buildErrorCodeDoc(List<ApiErrorCode> errorCodeList, String outPath) {
if (CollectionUtil.isNotEmpty(errorCodeList)) {
Template mapper = BeetlTemplateUtil.getByName("ErrorCodeList.btl");
mapper.binding("list", errorCodeList);//类名
FileUtil.nioWriteFile(mapper.render(), outPath + FILE_SEPARATOR + "ErrorCodeList.md");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
package com.power.doc.constants;
/**
* @author yu 2018/12/15.
*/
public class GlobalConstants {
/**
* controller注解全名称
*/
public static final String CONTROLLER_FULLY = "org.springframework.stereotype.Controller";
/**
* rest controller注解全名称
*/
public static final String REST_CONTROLLER_FULLY = "org.springframework.web.bind.annotation.RestController";
}

View File

@ -0,0 +1,149 @@
package com.power.doc.model;
import com.power.common.util.CollectionUtil;
import java.util.List;
/**
* Description:
* Api配置
*
* @author yu 2018/06/18.
*/
public class ApiConfig {
/**
* 应用请求base路径
*/
private String serverUrl;
/**
* 是否采用严格模式
*/
private boolean isStrict;
/**
* 是否将markdown全输出合并到一个文件
*/
private boolean allInOne;
/**
* 输出路径
*/
private String outPath;
/**
* source path
*/
private List<SourcePath> sourcePaths;
/**
* 请求头
*/
private List<ApiReqHeader> requestHeaders;
/**
* 自定义字段
*/
private List<CustomRespField> customResponseFields;
/**
* 错误码code列表
* @return
*/
private List<ApiErrorCode> errorCodes;
/**
* controller包过滤
*/
private String packageFilters;
/**
* 接口变更日志
*/
private List<RevisionLog> revisionLogs;
public String getServerUrl() {
return serverUrl;
}
public void setServerUrl(String serverUrl) {
this.serverUrl = serverUrl;
}
public boolean isStrict() {
return isStrict;
}
public void setStrict(boolean strict) {
isStrict = strict;
}
public String getOutPath() {
return outPath;
}
public void setOutPath(String outPath) {
this.outPath = outPath;
}
public List<ApiReqHeader> getRequestHeaders() {
return requestHeaders;
}
public void setRequestHeaders(ApiReqHeader... requestHeaders) {
this.requestHeaders = CollectionUtil.asList(requestHeaders);
}
public List<CustomRespField> getCustomResponseFields() {
return customResponseFields;
}
public void setCustomResponseFields(CustomRespField... customResponseFields) {
this.customResponseFields = CollectionUtil.asList(customResponseFields);
}
public List<ApiErrorCode> getErrorCodes() {
return errorCodes;
}
public void setErrorCodes(List<ApiErrorCode> errorCodes) {
this.errorCodes = errorCodes;
}
public List<SourcePath> getSourcePaths() {
return sourcePaths;
}
public void setSourcePaths(SourcePath... sourcePaths) {
this.sourcePaths = CollectionUtil.asList(sourcePaths);
}
public boolean isAllInOne() {
return allInOne;
}
public void setAllInOne(boolean allInOne) {
this.allInOne = allInOne;
}
public String getPackageFilters() {
return packageFilters;
}
public void setPackageFilters(String packageFilters) {
this.packageFilters = packageFilters;
}
public void setRevisionLogs(RevisionLog... revisionLogs){
this.revisionLogs = CollectionUtil.asList(revisionLogs);
}
public List<RevisionLog> getRevisionLogs() {
return revisionLogs;
}
}

View File

@ -0,0 +1,48 @@
package com.power.doc.model;
import java.util.List;
public class ApiDoc {
/**
* 类名
*/
private String name;
/**
* 方法文档列表
*/
private List<ApiMethodDoc> list;
/**
* 类注解描述
*/
private String desc;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ApiMethodDoc> getList() {
return list;
}
public void setList(List<ApiMethodDoc> list) {
this.list = list;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}

View File

@ -0,0 +1,39 @@
package com.power.doc.model;
/**
* Description:
* restful api错误码
*
* @author yu 2018/06/25.
*/
public class ApiErrorCode {
/**
* 错误码
*/
private String value;
/**
* 错误描述
*/
private String desc;
public String getValue() {
return value;
}
public ApiErrorCode setValue(String value) {
this.value = value;
return this;
}
public String getDesc() {
return desc;
}
public ApiErrorCode setDesc(String desc) {
this.desc = desc;
return this;
}
}

View File

@ -0,0 +1,100 @@
package com.power.doc.model;
import java.io.Serializable;
/**
* api文档
*/
public class ApiMethodDoc implements Serializable {
private String desc;
private String url;
private String type;
private String headers;
private String contentType = "application/x-www-form-urlencoded";
private String requestParams;
private String requestUsage;
private String responseUsage;
private String responseParams;
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getRequestParams() {
return requestParams;
}
public void setRequestParams(String requestParams) {
this.requestParams = requestParams;
}
public String getResponseUsage() {
return responseUsage;
}
public void setResponseUsage(String responseUsage) {
this.responseUsage = responseUsage;
}
public String getResponseParams() {
return responseParams;
}
public void setResponseParams(String responseParams) {
this.responseParams = responseParams;
}
public String getRequestUsage() {
return requestUsage;
}
public void setRequestUsage(String requestUsage) {
this.requestUsage = requestUsage;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public String getHeaders() {
return headers;
}
public void setHeaders(String headers) {
this.headers = headers;
}
}

View File

@ -0,0 +1,55 @@
package com.power.doc.model;
/**
* Description:
* 请求头
*
* @author yu 2018/06/18.
*/
public class ApiReqHeader {
/**
* 请求头的名称
*/
private String name;
/**
* 请求头类型
*/
private String type;
/**
* 请求头描述
*/
private String desc;
public static ApiReqHeader header(){
return new ApiReqHeader();
}
public String getName() {
return name;
}
public ApiReqHeader setName(String name) {
this.name = name;
return this;
}
public String getType() {
return type;
}
public ApiReqHeader setType(String type) {
this.type = type;
return this;
}
public String getDesc() {
return desc;
}
public ApiReqHeader setDesc(String desc) {
this.desc = desc;
return this;
}
}

View File

@ -0,0 +1,70 @@
package com.power.doc.model;
/**
* Description:
* Api 自动义字段修正
*
* @author yu 2018/06/18.
*/
public class CustomRespField {
/**
* 字段名
*/
private String name;
/**
* 字段描述
*/
private String desc;
/**
* 字段隶属类
*/
private String ownerClassName;
/**
* 默认值
*/
private Object value;
public static CustomRespField field(){
return new CustomRespField();
}
public String getName() {
return name;
}
public CustomRespField setName(String name) {
this.name = name;
return this;
}
public String getDesc() {
return desc;
}
public CustomRespField setDesc(String desc) {
this.desc = desc;
return this;
}
public String getOwnerClassName() {
return ownerClassName;
}
public CustomRespField setOwnerClassName(String ownerClassName) {
this.ownerClassName = ownerClassName;
return this;
}
public Object getValue() {
return value;
}
public CustomRespField setValue(Object value) {
this.value = value;
return this;
}
}

View File

@ -0,0 +1,84 @@
package com.power.doc.model;
/**
* 接口文档修订日志
* @author yolanda0608 2018/12/15
*/
public class RevisionLog {
/**
* 修订版本
*/
private String version;
/**
* 状态
*/
private String status;
/**
* 作者
*/
private String author;
/**
* 修订时间
*/
private String revisionTime;
/**
* 备注
*/
private String remarks;
public String getVersion() {
return version;
}
public RevisionLog setVersion(String version) {
this.version = version;
return this;
}
public String getStatus() {
return status;
}
public RevisionLog setStatus(String status) {
this.status = status;
return this;
}
public String getAuthor() {
return author;
}
public RevisionLog setAuthor(String author) {
this.author = author;
return this;
}
public String getRevisionTime() {
return revisionTime;
}
public RevisionLog setRevisionTime(String revisionTime) {
this.revisionTime = revisionTime;
return this;
}
public String getRemarks() {
return remarks;
}
public RevisionLog setRemarks(String remarks) {
this.remarks = remarks;
return this;
}
public static RevisionLog getLog(){
return new RevisionLog();
}
}

View File

@ -0,0 +1,39 @@
package com.power.doc.model;
/**
* @author yu 2018/7/14.
*/
public class SourcePath {
/**
* Source path
*/
private String path;
/**
* path description
*/
private String desc;
public static SourcePath path() {
return new SourcePath();
}
public String getPath() {
return path;
}
public SourcePath setPath(String path) {
this.path = path;
return this;
}
public String getDesc() {
return desc;
}
public SourcePath setDesc(String desc) {
this.desc = desc;
return this;
}
}

View File

@ -0,0 +1,69 @@
package com.power.doc.utils;
import com.power.common.util.FileUtil;
import org.beetl.core.Configuration;
import org.beetl.core.GroupTemplate;
import org.beetl.core.Template;
import org.beetl.core.resource.ClasspathResourceLoader;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 获取模板
*
* @author sunyu on 2016/12/6.
*/
public class BeetlTemplateUtil {
public static Template getByName(String templateName) {
try {
ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader("/template/");
Configuration cfg = Configuration.defaultConfiguration();
GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
return gt.getTemplate(templateName);
} catch (IOException e) {
throw new RuntimeException("获取模板异常");
}
}
/**
*
* @param path path
* @param params params
* @return map
*/
public static Map<String, String> getTemplatesRendered(String path, Map<String, Object> params) {
Map<String, String> templateMap = new HashMap<>();
File[] files = FileUtil.getResourceFolderFiles(path);
GroupTemplate gt = getGroupTemplate(path);
for (File f : files) {
if (f.isFile()) {
String fileName = f.getName();
Template tp = gt.getTemplate(fileName);
if (null != params) {
tp.binding(params);
}
templateMap.put(fileName, tp.render());
}
}
return templateMap;
}
/**
*
* @param path
* @return
*/
private static GroupTemplate getGroupTemplate(String path) {
try {
ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader("/"+path+"/");
Configuration cfg = Configuration.defaultConfiguration();
GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
return gt;
} catch (IOException e) {
throw new RuntimeException("获取模板异常");
}
}
}

View File

@ -0,0 +1,390 @@
package com.power.doc.utils;
import java.util.ArrayList;
import java.util.List;
/**
* Description:
* class的工具
*
* @author yu 2018//14.
*/
public class DocClassUtil {
/**
* Check if it is the basic data type of json data
*
* @param type0 java class name
* @return boolean
*/
public static boolean isPrimitive(String type0) {
String type = type0.contains("java.lang") ? type0.substring(type0.lastIndexOf(".") + 1, type0.length()) : type0;
type = type.toLowerCase();
switch (type) {
case "integer":
return true;
case "int":
return true;
case "long":
return true;
case "double":
return true;
case "float":
return true;
case "short":
return true;
case "bigdecimal":
return true;
case "char":
return true;
case "string":
return true;
case "boolean":
return true;
case "byte":
return true;
case "java.sql.timestamp":
return true;
case "java.util.date":
return true;
case "java.time.localdatetime":
return true;
case "localdatetime":
return true;
case "localdate":
return true;
case "java.time.localdate":
return true;
case "java.math.bigdecimal":
return true;
case "java.math.biginteger":
return true;
default:
return false;
}
}
/**
* get class names by generic class name
*
* @param returnType generic class name
* @return array of string
*/
public static String[] getSimpleGicName(String returnType) {
if (returnType.contains("<")) {
String pre = returnType.substring(0, returnType.indexOf("<"));
if (DocClassUtil.isMap(pre)) {
return getMapKeyValueType(returnType);
}
String type = returnType.substring(returnType.indexOf("<") + 1, returnType.lastIndexOf(">"));
if (DocClassUtil.isCollection(pre)) {
return type.split(" ");
}
String[] arr = type.split(",");
return classNameFix(arr);
} else {
return returnType.split(" ");
}
}
/**
* Get a simple type name from a generic class name
*
* @param gicName Generic class name
* @return String
*/
public static String getSimpleName(String gicName) {
if (gicName.contains("<")) {
return gicName.substring(0, gicName.indexOf("<"));
} else {
return gicName;
}
}
/**
* Automatic repair of generic split class names
*
* @param arr arr of class name
* @return array of String
*/
private static String[] classNameFix(String[] arr) {
List<String> classes = new ArrayList<>();
List<Integer> indexList = new ArrayList<>();
int globIndex = 0;
for (int i = 0; i < arr.length; i++) {
if (classes.size() > 0) {
int index = classes.size() - 1;
if (!DocUtil.isClassName(classes.get(index))) {
globIndex = globIndex + 1;
if (globIndex < arr.length) {
indexList.add(globIndex);
String className = classes.get(index) + "," + arr[globIndex];
classes.set(index, className);
}
} else {
globIndex = globIndex + 1;
if (globIndex < arr.length) {
if (DocUtil.isClassName(arr[globIndex])) {
indexList.add(globIndex);
classes.add(arr[globIndex]);
} else {
if (!indexList.contains(globIndex) && !indexList.contains(globIndex + 1)) {
indexList.add(globIndex);
classes.add(arr[globIndex] + "," + arr[globIndex + 1]);
globIndex = globIndex + 1;
indexList.add(globIndex);
}
}
}
}
} else {
if (DocUtil.isClassName(arr[i])) {
indexList.add(i);
classes.add(arr[i]);
} else {
if (!indexList.contains(i) && !indexList.contains(i + 1)) {
globIndex = i + 1;
classes.add(arr[i] + "," + arr[globIndex]);
indexList.add(i);
indexList.add(i + 1);
}
}
}
}
return classes.toArray(new String[classes.size()]);
}
/**
* get map key and value type name populate into array.
*
* @param gName generic class name
* @return array of string
*/
public static String[] getMapKeyValueType(String gName) {
if(gName.contains("<")){
String[] arr = new String[2];
String key = gName.substring(gName.indexOf("<") + 1, gName.indexOf(","));
String value = gName.substring(gName.indexOf(",") + 1, gName.lastIndexOf(">"));
arr[0] = key;
arr[1] = value;
return arr;
}else {
return new String[0];
}
}
/**
* Convert the parameter types exported to the api document
*
* @param javaTypeName java simple typeName
* @return String
*/
public static String processTypeNameForParams(String javaTypeName) {
if (javaTypeName.length() == 1) {
return "object";
}
if(javaTypeName.contains("[]")){
return "array";
}
switch (javaTypeName) {
case "java.lang.String":
return "string";
case "string":
return "string";
case "char":
return "char";
case "java.util.List":
return "array";
case "list":
return "array";
case "java.lang.Integer":
return "int";
case "integer":
return "int";
case "int":
return "int";
case "short":
return "int";
case "java.lang.Short":
return "int";
case "double":
return "number";
case "java.lang.Long":
return "number";
case "long":
return "number";
case "java.lang.Float":
return "number";
case "bigdecimal":
return "number";
case "biginteger":
return "number";
case "float":
return "number";
case "java.lang.Boolean":
return "boolean";
case "boolean":
return "boolean";
case "java.util.Byte":
return "string";
case "byte":
return "string";
case "map":
return "map";
case "date":
return "string";
case "localdatetime":
return "string";
case "localdate":
return "string";
default:
return "object";
}
}
/**
* validate java collection
*
* @param type java typeName
* @return boolean
*/
public static boolean isCollection(String type) {
switch (type) {
case "java.util.List":
return true;
case "java.util.LinkedList":
return true;
case "java.util.ArrayList":
return true;
case "java.util.Set":
return true;
case "java.util.TreeSet":
return true;
case "java.util.HashSet":
return true;
case "java.util.SortedSet":
return true;
case "java.util.Collection":
return true;
case "java.util.ArrayDeque":
return true;
case "java.util.PriorityQueue":
return true;
default:
return false;
}
}
/**
* Check if it is an map
*
* @param type java type
* @return boolean
*/
public static boolean isMap(String type) {
switch (type) {
case "java.util.Map":
return true;
case "java.util.SortedMap":
return true;
case "java.util.TreeMap":
return true;
case "java.util.LinkedHashMap":
return true;
case "java.util.HashMap":
return true;
case "java.util.concurrent.ConcurrentHashMap":
return true;
case "java.util.Properties":
return true;
case "java.util.Hashtable":
return true;
default:
return false;
}
}
/**
* check array
* @param type type name
* @return boolean
*/
public static boolean isArray(String type){
return type.contains("[]");
}
/**
* check JSR303
* @param annotationSimpleName annotation name
* @return boolean
*/
public static boolean isJSR303Required(String annotationSimpleName) {
switch (annotationSimpleName) {
case "NotNull":
return true;
case "NotEmpty":
return true;
case "NotBlank":
return true;
case "Required":
return true;
default:
return false;
}
}
/**
* custom tag
* @param tagName custom field tag
* @return boolean
*/
public static boolean isRequiredTag(String tagName){
switch (tagName) {
case "required":
return true;
default:
return false;
}
}
/**
* ignore tag request field
* @param tagName custom field tag
* @return boolean
*/
public static boolean isIgnoreTag(String tagName){
switch (tagName) {
case "ignore":
return true;
default:
return false;
}
}
/**
* ignore param of spring mvc
* @param paramType param type name
* @return boolean
*/
public static boolean isMvcIgnoreParams(String paramType){
switch (paramType){
case "org.springframework.ui.Model":
return true;
case "org.springframework.ui.ModelMap":
return true;
case "org.springframework.web.servlet.ModelAndView":
return true;
case "org.springframework.validation.BindingResult" :
return true;
case "javax.servlet.http.HttpServletRequest":
return true;
case "javax.servlet.http.HttpServletResponse":
return true;
default:
return false;
}
}
}

View File

@ -0,0 +1,159 @@
package com.power.doc.utils;
import com.github.javafaker.Faker;
import com.power.common.util.DateTimeUtil;
import com.power.common.util.IDCardUtil;
import com.power.common.util.RandomUtil;
import com.power.common.util.StringUtil;
import java.util.*;
import java.util.regex.Pattern;
/**
* Description:
* DocUtil
*
* @author yu 2018/06/11.
*/
public class DocUtil {
private static Faker faker = new Faker(new Locale("zh-CN"));
private static Faker enFaker = new Faker(new Locale("en-US"));
private static Map<String,String> fieldValue = new LinkedHashMap<>();
static {
fieldValue.put("uuid-string", UUID.randomUUID().toString());
fieldValue.put("uid",UUID.randomUUID().toString());
fieldValue.put("nickname-string",enFaker.name().username());
fieldValue.put("name-string",faker.name().username());
fieldValue.put("url-string",faker.internet().url());
fieldValue.put("username-string",faker.name().username());
fieldValue.put("age-int",String.valueOf(RandomUtil.randomInt(0,70)));
fieldValue.put("age-integer",String.valueOf(RandomUtil.randomInt(0,70)));
fieldValue.put("email-string",faker.internet().emailAddress());
fieldValue.put("domain-string",faker.internet().domainName());
fieldValue.put("phone-string",faker.phoneNumber().cellPhone());
fieldValue.put("mobile-string",faker.phoneNumber().cellPhone());
fieldValue.put("telephone-string",faker.phoneNumber().phoneNumber());
fieldValue.put("address-string",faker.address().fullAddress().replace(",",""));
fieldValue.put("ip-string",faker.internet().ipV4Address());
fieldValue.put("ipv4-string",faker.internet().ipV4Address());
fieldValue.put("ipv6-string",faker.internet().ipV6Address());
fieldValue.put("company-string",faker.company().name());
fieldValue.put("timestamp-long",String.valueOf(System.currentTimeMillis()));
fieldValue.put("timestamp-string",DateTimeUtil.dateToStr(new Date(),DateTimeUtil.DATE_FORMAT_SECOND));
fieldValue.put("time-long",String.valueOf(System.currentTimeMillis()));
fieldValue.put("time-string",DateTimeUtil.dateToStr(new Date(),DateTimeUtil.DATE_FORMAT_SECOND));
fieldValue.put("birthday-string", DateTimeUtil.dateToStr(new Date(),DateTimeUtil.DATE_FORMAT_DAY));
fieldValue.put("birthday-long",String.valueOf(System.currentTimeMillis()));
fieldValue.put("code-string",String.valueOf(RandomUtil.randomInt(100,99999)));
fieldValue.put("message-string","success,fail".split(",")[RandomUtil.randomInt(0,1)]);
fieldValue.put("date-string",DateTimeUtil.dateToStr(new Date(),DateTimeUtil.DATE_FORMAT_DAY));
fieldValue.put("date-date",DateTimeUtil.dateToStr(new Date(),DateTimeUtil.DATE_FORMAT_DAY));
fieldValue.put("state-int",String.valueOf(RandomUtil.randomInt(0,10)));
fieldValue.put("state-integer",String.valueOf(RandomUtil.randomInt(0,10)));
fieldValue.put("flag-int",String.valueOf(RandomUtil.randomInt(0,10)));
fieldValue.put("flag-integer",String.valueOf(RandomUtil.randomInt(0,10)));
fieldValue.put("flag-boolean","true");
fieldValue.put("flag-Boolean","false");
fieldValue.put("idcard-string", IDCardUtil.getIdCard());
fieldValue.put("sex-int",String.valueOf(RandomUtil.randomInt(0,1)));
fieldValue.put("sex-integer",String.valueOf(RandomUtil.randomInt(0,1)));
fieldValue.put("gender-int",String.valueOf(RandomUtil.randomInt(0,1)));
fieldValue.put("gender-integer",String.valueOf(RandomUtil.randomInt(0,1)));
}
/**
* 随机生成json值
* @param type0 type name
* @return string
*/
public static String jsonValueByType(String type0){
String type = type0.contains(".")?type0.substring(type0.lastIndexOf(".")+1,type0.length()):type0;
String value = RandomUtil.randomValueByType(type);
if("Integer".equals(type)||"int".equals(type)||"Long".equals(type)||"long".equals(type)
||"Double".equals(type)||"double".equals(type)|| "Float".equals(type)||"float".equals(type)||
"BigDecimal".equals(type)||"boolean".equals(type)||"Boolean".equals(type)||
"Short".equals(type)||"BigInteger".equals(type)){
return value;
}else{
StringBuilder builder = new StringBuilder();
builder.append("\"").append(value).append("\"");
return builder.toString();
}
}
/**
* 根据字段字段名和type生成字段值
* @param type0 类型
* @param filedName 字段名称
* @return string
*/
public static String getValByTypeAndFieldName(String type0,String filedName){
String type = type0.contains("java.lang")?type0.substring(type0.lastIndexOf(".")+1,type0.length()):type0;
String key = filedName.toLowerCase()+"-"+type.toLowerCase();
String value = null;
for(Map.Entry<String,String> entry:fieldValue.entrySet()){
if(key.contains(entry.getKey())){
value = entry.getValue();
break;
}
}
if(null == value){
return jsonValueByType(type0);
}else{
if("string".equals(type.toLowerCase())){
StringBuilder builder = new StringBuilder();
builder.append("\"").append(value).append("\"");
return builder.toString();
}else{
return value;
}
}
}
/**
* 是否是合法的java类名称
* @param className class nem
* @return boolean
*/
public static boolean isClassName(String className){
if(StringUtil.isEmpty(className)){
return false;
}
if(className.contains("<")&&!className.contains(">")){
return false;
}else if(className.contains(">")&&!className.contains("<")){
return false;
}else{
return true;
}
}
/**
* match controller package
* @param packageFilters package filter
* @param controllerName controller name
* @return boolean
*/
public static boolean isMatch(String packageFilters,String controllerName){
if(StringUtil.isNotEmpty(packageFilters)){
String[] patterns = packageFilters.split(",");
for (String str : patterns) {
if (str.endsWith("*")) {
String name = str.substring(0, str.length() - 2);
if (controllerName.contains(name)) {
return true;
}
} else {
if(controllerName.contains(str)){
return true;
}
}
}
}
return false;
}
}

View File

@ -0,0 +1,26 @@
package com.power.doc.utils;
import com.power.common.util.StringUtil;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
public class PathUtil {
/**
* 获取java类名
* @param parentDir parent dir
* @param className 类名
* @return string
*/
public static String javaFilePath(String parentDir, String className) {
if (StringUtil.isEmpty(parentDir)) {
parentDir = "java.io.tmpdir";
}
if (!StringUtils.endsWith(parentDir, File.separator)) {
parentDir += File.separator;
}
className = className.replaceAll("\\.", "\\" + File.separator);
return parentDir + className+".java";
}
}

View File

@ -0,0 +1,2 @@
DELIMITER_STATEMENT_START=<%
DELIMITER_STATEMENT_END=%>

View File

@ -0,0 +1,75 @@
<%if(isNotEmpty(revisionLogList)){%>
版本 | 时间 | 状态 | 作者 | 备注
------|--------|-----|------| -------
<%
for(revisionLog in revisionLogList){
%>
${revisionLog.version}|${revisionLog.revisionTime}|${revisionLog.status}|${revisionLog.author}|${revisionLog.remarks}
<%}%>
<%}%>
<%
for(api in apiDocList){
%>
# ${api.desc}
<%
for(doc in api.list){
%>
## ${doc.desc}
**URL:** ${doc.url}
**Type:** ${doc.type}
**Content-Type:** ${doc.contentType}
<%if(isNotEmpty(doc.headers)){%>
**Request-headers:**
Name | Type|Description
---|---|---
${doc.headers}
<%}%>
<%if(isNotEmpty(doc.requestParams)){%>
**Request-parameters:**
Parameter | Type|Description|Required
---|---|---|---
${doc.requestParams}
<%}%>
<%if(isNotEmpty(doc.requestUsage)){%>
**Request-example:**
```
${doc.requestUsage}
```
<%}%>
<%if(isNotEmpty(doc.responseParams)){%>
**Response-fields:**
Field | Type|Description
---|---|---
${doc.responseParams}
<%}%>
<%if(isNotEmpty(doc.responseUsage)){%>
**Response-example:**
```
${doc.responseUsage}
```
<%}%>
<% } %>
<% } %>
<%if(isNotEmpty(errorCodeList)){%>
# Error code list
Error code |Description
---|---
<%
for(error in errorCodeList){
%>
${error.value}|${error.desc}
<%}%>
<%}%>

View File

@ -0,0 +1,50 @@
# ${desc}
<%
for(doc in list){
%>
## ${doc.desc}
**URL:** ${doc.url}
**Type:** ${doc.type}
**Content-Type:** ${doc.contentType}
<%if(isNotEmpty(doc.headers)){%>
**Request-headers:**
Name | Type|Description
---|---|---
${doc.headers}
<%}%>
<%if(isNotEmpty(doc.requestParams)){%>
**Request-parameters:**
Parameter | Type|Description|Required
---|---|---|---
${doc.requestParams}
<%}%>
<%if(isNotEmpty(doc.requestUsage)){%>
**Request-example:**
```
${doc.requestUsage}
```
<%}%>
<%if(isNotEmpty(doc.responseParams)){%>
**Response-fields:**
Field | Type|Description
---|---|---
${doc.responseParams}
<%}%>
<%if(isNotEmpty(doc.responseUsage)){%>
**Response-example:**
```
${doc.responseUsage}
```
<%}%>
<% } %>

View File

@ -0,0 +1,10 @@
# Error code list
Error code |Description
---|---
<%
for(error in list){
%>
${error.value}|${error.desc}
<%}%>

View File

@ -0,0 +1,71 @@
package com.power.doc;
import com.power.common.util.DateTimeUtil;
import com.power.doc.builder.ApiDocBuilder;
import com.power.doc.model.*;
import org.junit.Test;
/**
* Description:
* ApiDoc测试
*
* @author yu 2018/06/11.
*/
public class ApiDocTest {
/**
* 简单型接口不需要指定请求头并且项目是maven的.
*/
/*
@Test
public void testBuilderControllersApiSimple() {
//将生成的文档输出到d:\md目录下严格模式下api-doc会检测Controller的接口注释
ApiDocBuilder.builderControllersApi("d:\\md",true);
}
*/
/**
* 包括设置请求头缺失注释的字段批量在文档生成期使用定义好的注释
*/
@Test
public void testBuilderControllersApi() {
ApiConfig config = new ApiConfig();
config.setServerUrl("http://localhost:8080");
//config.setStrict(true);
config.setAllInOne(true);
config.setOutPath("d:\\md2");
//不指定SourcePaths默认加载代码为项目src/main/java下的
config.setSourcePaths(
//SourcePath.path().setDesc("本项目代码").setPath("src/test/java")
SourcePath.path().setPath("F:\\Personal\\project\\smart\\src\\main\\java")
//SourcePath.path().setDesc("加载项目外代码").setPath("E:\\ApplicationPower\\ApplicationPower\\Common-util\\src\\main\\java")
);
//设置请求头如果没有请求头可以不用设置
/* config.setRequestHeaders(
ApiReqHeader.header().setName("access_token").setType("string").setDesc("Basic auth credentials"),
ApiReqHeader.header().setName("user_uuid").setType("string").setDesc("User Uuid key")
);*/
//对于外部jar的类api-doc目前无法自动获取注释
//如果有这种场景则自己添加字段和注释api-doc后期遇到同名字段则直接给相应字段加注释
/* config.setCustomResponseFields(
// CustomRespField.field().setName("success").setDesc("成功返回true,失败返回false"),
// CustomRespField.field().setName("message").setDesc("接口响应信息"),
// CustomRespField.field().setName("data").setDesc("接口响应数据"),
CustomRespField.field().setName("code").setValue("00000")
//.setDesc("响应代码")
);*/
//非必须只有当setAllInOne设置为true时文档变更记录才生效https://gitee.com/sunyurepository/ApplicationPower/issues/IPS4O
config.setRevisionLogs(
RevisionLog.getLog().setRevisionTime("2018/12/15").setAuthor("chen").setRemarks("测试").setStatus("创建").setVersion("V1.0"),
RevisionLog.getLog().setRevisionTime("2018/12/16").setAuthor("chen2").setRemarks("测试2").setStatus("修改").setVersion("V2.0")
);
long start = System.currentTimeMillis();
ApiDocBuilder.builderControllersApi(config);
long end = System.currentTimeMillis();
DateTimeUtil.printRunTime(end, start);
}
}

View File

@ -0,0 +1,46 @@
package com.power.doc;
import com.power.common.model.CommonResult;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Main {
public static void exeCmd(String commandStr) {
BufferedReader br = null;
try {
Process p = Runtime.getRuntime().exec(commandStr);
br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line + "\n");
}
System.out.println(sb.toString());
} catch (Exception e) {
e.printStackTrace();
}
finally
{
if (br != null)
{
try {
br.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws Exception {
Class cls = CommonResult.class;
System.out.println("path:"+cls.getResource(""));
String path = cls.getResource("").getPath();
String commandStr = String.format("javap -classpath %s -private CommonResult",path);
String cmd = "java -jar d:/procyon-decompiler-0.5.30.jar D:/ProgramFiles/mvnrepository/repository/com/boco/sp/Common-util/1.0-SNAPSHOT/Common-util-1.0-20180105.062727-5.jar -o out";
//String commandStr = "ipconfig";
Main.exeCmd(cmd);
}
}

View File

@ -0,0 +1,27 @@
package com.power.doc.util;
import com.power.doc.utils.DocClassUtil;
import org.junit.Test;
/**
* Description:
* DocUtil单元测试
*
* @author yu 2018/06/16.
*/
public class DocClassUtilTest {
@Test
public void testGetSimpleGicName(){
char me = 'k';
String className = "com.power.doc.controller.Teacher<com.power.doc.controller.Teacher<com.power.doc.controller.User,com.power.doc.controller.User,com.power.doc.controller.User>,com.power.doc.controller.Teacher<com.power.doc.controller.User,com.power.doc.controller.User,com.power.doc.controller.User>,com.power.doc.controller.Teacher<com.power.doc.controller.User,com.power.doc.controller.User,com.power.doc.controller.User>>";
String[] arr = DocClassUtil.getSimpleGicName(className);
// System.out.println("arr:"+ JSON.toJSONString(arr));
}
@Test
public void testIsPrimitive(){
String typeName = "java.time.LocalDateTime";
System.out.println(DocClassUtil.isPrimitive(typeName));
}
}

View File

@ -0,0 +1,16 @@
package com.power.doc.util;
import com.power.doc.utils.DocUtil;
import org.junit.Test;
/**
* @author yu 2018/12/10.
*/
public class DocUtilTest {
@Test
public void test(){
String str = DocUtil.getValByTypeAndFieldName("LocalDateTime","createTime");
System.out.println(str);
}
}