You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

191 lines
8.0 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package com.dsideal.base;
import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.dsideal.base.Plugin.PostmanDocPlugin;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.jfinal.aop.Before;
import com.jfinal.core.Controller;
import com.jfinal.ext.interceptor.GET;
import com.jfinal.ext.interceptor.POST;
import io.github.yedaxia.apidocs.DocContext;
import io.github.yedaxia.apidocs.Docs;
import io.github.yedaxia.apidocs.DocsConfig;
import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* JApiDocs 无需额外注解的 API 文档生成工具
* <p>
* 源码 https://github.com/YeDaxia/JApiDocs
* 文档 https://japidocs.agilestudio.cn/#/zh-cn/
*
* @author zxd 2022-02-17
*/
public class JApiDocsGenerator {
private static String getHttpType(Before before) {
if (before.value().length > 0) {
for (Class<?> interceptClass : before.value()) {
if (interceptClass.equals(GET.class)) {
return "GET";
} else if (interceptClass.equals(POST.class)) {
return "POST";
}
}
}
return "UNKNOWN";
}
/**
* 获取指定目录下所有JAVA文件
* @param directory 指定目录
* @return 所有Java文件
*/
public static List<File> findJavaFiles(File directory) {
List<File> javaFiles = new ArrayList<>();
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
javaFiles.addAll(findJavaFiles(file)); // 递归遍历子目录
} else if (file.getName().endsWith(".java")) {
javaFiles.add(file); // 添加Java文件
}
}
}
return javaFiles;
}
/**
* 功能:获取所有 Controller 名称
*
* @param sourceJava JAVA目录
* @return Controller名称集合
*/
public static Map<String, String> getControllMap(String sourceJava) {
Map<String, String> res = new HashMap<>();
List<File> files = findJavaFiles(new File(sourceJava));
for (File file : files) {
String className = file.getAbsolutePath().replace("\\", "/").replace(sourceJava + "/", "");
className = className.replace("/", ".");
className = className.replace(".java", "");
try {
// 去掉.class后缀并加载类
Class<?> cls = Class.forName(className);
if (Controller.class.isAssignableFrom(cls) && !Modifier.isAbstract(cls.getModifiers())) {
// 获取类上的所有方法
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
// 检查方法上是否有@Before注解
var before = method.getAnnotation(Before.class);
if (before != null) {
String httpType = getHttpType(before);
res.put(cls.getSimpleName() + "." + method.getName(), httpType);
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return res;
}
/**
* JApiDocs 生成器
* 如果报错,做如下检查:
* 1 javadoc @param 后是否有注释
* 2 src.main.java 目录中非 .java 扩展名文件的内容要 // 注释起来
* 3 删除 config.setDocsPath 目录中的文件,再生成试试
* <p>
* 如果生成的 api 文档不是预期的,作如下检查:
* 1 必须在 configRoute(Routes me) 中已该方式 me.add("/xx/yy", xx.class, "/"); 定义 Controller
* 2 在需要生成 api 的 Controller 中添加 @ApiDoc 注解
* 3 如果要忽略某 action在 action 上添加 @Ignore
* https://jfinal.com/share/2528
* <p>
* 在需要生成Doc文档的Controller类上面加上@ApiDoc注解
* <p>
* https://japidocs.agilestudio.cn/#/zh-cn/
*/
public static void main(String[] args) {
//可以限制只生成哪个接口,数组内容为空,则表示生成全部
//String[] generateInterfacesList = {"getWxTj"};
String[] generateInterfacesList = {};
String projectPath = System.getProperty("user.dir");
projectPath = projectPath.replace("\\", "/");
String projectName = projectPath.substring(projectPath.lastIndexOf("/") + 1);
DocsConfig config = new DocsConfig();
String sourceJava = projectPath + "/src/main/java";
config.setProjectPath(sourceJava); // root project path
config.setProjectName(projectName); // project name
String version = "v1.0";
config.setApiVersion(version); // api version
String docPath = projectPath + "/Doc";
config.setDocsPath(docPath); // api docs target path
config.addPlugin(new PostmanDocPlugin());
config.setAutoGenerate(Boolean.FALSE); // auto generate
config.setMvcFramework("JFinal");
Docs.buildHtmlDocs(config); // execute to generate
// 获取项目路径
Map<String, String> map = getControllMap(sourceJava);
//修正一下 postman 的请求方式
String docFileName = String.format("%s-%s-api-docs.json", DocContext.getDocsConfig().getProjectName(), DocContext.getDocsConfig().getApiVersion());
String jsonPath = docPath + "/" + version + "/" + docFileName;
String jsonContent = FileUtil.readUtf8String(jsonPath);
JSONObject jo = JSONObject.parseObject(jsonContent);
//第一层item
for (Object item : jo.getJSONArray("item")) {
JSONObject j2 = (JSONObject) item;
//Controller类名
String className = j2.getString("name");
//第二层item
for (Object o : j2.getJSONArray("item")) {
JSONObject j3 = (JSONObject) o;
JSONObject jRequest = j3.getJSONObject("request");
String x = jRequest.getJSONObject("url").getString("raw");
x = x.substring(x.lastIndexOf("/") + 1);
jRequest.put("method", map.get(className + "." + x));
}
}
//限制只生成哪个接口
Set<String> _set = new HashSet<>();
Collections.addAll(_set, generateInterfacesList);
// 获取JSONArray
jo = jo.getJSONArray("item").getJSONObject(0);
JSONArray jsonArray = jo.getJSONArray("item");
if (!_set.isEmpty()) {
// 遍历JSONArray
for (int i = 0; i < jsonArray.size(); i++) {
// 获取当前的JSONObject
JSONObject obj = jsonArray.getJSONObject(i);
JSONObject jRequest = obj.getJSONObject("request");
// 检查条件,例如,如果名字不是"John",则删除这个条目
String x = jRequest.getJSONObject("url").getString("raw");
x = x.substring(x.lastIndexOf("/") + 1);
if (!_set.contains(x)) {
// 从JSONArray中移除当前条目
jsonArray.remove(i);
// 由于移除操作会改变数组的长度和索引所以i需要减1以保持正确的索引位置
i--;
}
}
// 将修改后的JSONArray重新设置回JSONObject
jo.put("item", jsonArray);
}
//美化JSON格式化保存
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String jsonString = gson.toJson(jo);
FileUtil.writeUtf8String(jsonString, jsonPath);
System.out.println("恭喜文档JSON处理完成");
}
}