All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hn.doc.xyj.XyjUtils Maven / Gradle / Ivy

There is a newer version: 1.0.18
Show newest version
package com.hn.doc.xyj;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.hn.doc.xyj.annotation.Api;
import com.hn.doc.xyj.domain.Doc;
import com.hn.doc.xyj.domain.DocContent;
import com.hn.doc.xyj.domain.DocData;
import com.hn.doc.xyj.parse.DomainParse;
import com.hn.doc.xyj.parse.MethodParse;
import com.hn.doc.xyj.parse.ParseUtils;
import com.hn.utils.AssertUtils;
import com.hn.utils.ClassUtil;
import org.springframework.web.bind.annotation.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

/**
 * 描述: 小幺鸡通过注解生成接口类
 *
 * @author fei
 * @since 2019-11-21 13:17
 */
public class XyjUtils {
    private static final Log log = LogFactory.get();

    private static List basicObjects = new ArrayList();
    private static List objectMethods = new ArrayList<>();

    static {
        basicObjects.add(Integer.class);
        basicObjects.add(Long.class);
        basicObjects.add(String.class);
        basicObjects.add(Byte.class);
        basicObjects.add(Boolean.class);
        basicObjects.add(Double.class);
        basicObjects.add(Float.class);
        basicObjects.add(BigDecimal.class);
        basicObjects.add(BigInteger.class);
        basicObjects.add(long.class);
        basicObjects.add(int.class);
        basicObjects.add(boolean.class);
        basicObjects.add(double.class);
        basicObjects.add(float.class);
        basicObjects.add(Date.class);
    }

    static {
        Class objectClass = Object.class;
        Method[] methods1 = objectClass.getMethods();
        for (Method method : methods1) {
            objectMethods.add(method.getName());
        }
        objectMethods.add("initBinder");
    }

    private Xyj xyj;

    private XyjUtils(Xyj xyj) {
        this.xyj = xyj;
    }

    /**
     * 初始化XyjUtils
     *
     * @param url      小幺鸡地址
     * @param email    邮箱账号
     * @param password 密码
     * @return XyjUtils
     */
    public static XyjUtils create(String url, String email, String password) {
        return new XyjUtils(new Xyj(url, email, password));
    }

    /**
     * 初始化XyjUtils
     *
     * @param url    小幺鸡地址
     * @param cookie cookie
     * @return XyjUtils
     */
    public static XyjUtils create(String url, String cookie) {
        return new XyjUtils(new Xyj(url, cookie));
    }

    public String project(String projectName, String projectUrl) {
        String projectId = xyj.createProject(projectName);
        // 初始化全局变量
        xyj.createGlobal(projectId, projectUrl);
        return projectId;
    }

    private static Map responseMap;
    private static String dataKey;
    private static String dataDesc;

    /**
     * 自定义返回结果 result
     *
     * @param response response
     * @param key      dataKey
     * @param desc     dataDesc
     */
    public void customizeResult(Map response, String key, String desc) {
        responseMap = response;
        dataKey = key;
        dataDesc = desc;
    }

    /**
     * 根据包名创建文档
     *
     * @param packageName 包名
     * @param projectId   项目ID
     */
    public void docByPackageName(String packageName, String projectId) {
        List> classes = ClassUtil.getClasses(packageName);
        for (Class clazz : classes) {
            doc(clazz, projectId);
        }
    }

    /**
     * 创建文档
     *
     * @param className 包名+类名
     * @param projectId 项目ID
     * @throws ClassNotFoundException 找不到类异常
     */
    public void doc(String className, String projectId) throws ClassNotFoundException {
        Class clazz = Class.forName(className);
        doc(clazz, projectId, null);
    }

    /**
     * 创建文档
     *
     * @param clazz     class
     * @param projectId 项目ID
     */
    public void doc(Class clazz, String projectId) {
        doc(clazz, projectId, null);
    }

    /**
     * 创建文档
     * @param clazz      class
     * @param projectId  项目ID
     * @param methodName 方法名
     */
    public void doc(Class clazz, String projectId, String methodName) {
        RequestMapping requestMapping = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
        if (requestMapping == null) {
            return;
        }
        Api clazzApi = (Api) clazz.getAnnotation(Api.class);
        if (clazzApi == null) {
            return;
        }

        // 需要登录
        boolean authFlag = true;
        Annotation[] clazzAnnotations = clazz.getAnnotations();
        for (Annotation clazzAnnotation : clazzAnnotations) {
            Class aClass = clazzAnnotation.annotationType();
            String simpleName = aClass.getSimpleName();
            if ("NotAuth".equals(simpleName)) {
                authFlag = false;
            }
        }
        System.out.println(requestMapping.value()[0]);
        Method[] methods = clazz.getMethods();
        String preUrl = "$prefix$" + requestMapping.value()[0];

        // 创建文件目录
        String parentId = xyj.createFolder(clazzApi.value(), projectId);
        for (Method method : methods) {
            if (objectMethods.contains(method.getName())) {
                continue;
            }
            if (StrUtil.isNotBlank(methodName) && !methodName.equals(method.getName())) {
                continue;
            }

            DocContent docContent = new DocContent();
            int flag = 0;
            Api api = null;
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                Class annotClass = annotation.annotationType();
                String simpleName = annotClass.getSimpleName();
                if (simpleName.endsWith("Mapping")) {
                    flag++;
                    setReqMethod(docContent, preUrl, annotation, simpleName);
                }
                if (authFlag) {
                    if ("NotAuth".equals(simpleName)) {
                        authFlag = false;
                    }
                }
                if (Api.class.equals(annotClass)) {
                    api = (Api) annotation;
                }
            }

            if (flag == 0) {
                continue;
            }

            if (api != null) {
                // 创建文档
                String docId = xyj.createDoc(parentId, api.value(), projectId);
                System.out.println("apiResult:" + api.result());

                // 请求方法
                if (isJson(method)) {
                    docContent.setDataType("JSON");
                }

                // 请求头
                if (authFlag) {
                    docContent.setRequestHeaderArg(DocData.create("token", "用户令牌"));
                }
                // 接口描述
                docContent.setDescription(api.description());
                // 请求参数
                setRequestParams(method, docContent);
                // 返回参数
                setResponseParams(method, api.result(), docContent);

                System.out.println("docContent:" + JSONUtil.toJsonStr(docContent));

                // 更新文档
                Doc doc = new Doc()
                        .setDocId(docId)
                        .setContent(docContent);
                xyj.updateDoc(doc);
            }
        }
    }

    private void setResponseParams(Method method, Class result, DocContent docContent) {
        Class returnClass = method.getReturnType();
        String returnName = returnClass.getSimpleName();
        DocData data = null;
        if ("void".equals(returnName)) {
            log.info("无返回值");
        } else if ("TableDataInfo".equals(returnName)) {
            docContent.setResponseArgs(DocData.create("total", "总记录数"),
                    DocData.create("code", "消息状态码"),
                    DocData.create("msg", "消息内容"));
            data = DocData.create("rows", "列表数据");
        } else if ("AjaxResult".equals(returnName)) {
            // 响应头
            docContent.setResponseArgs(DocData.create("code", "消息状态码"),
                    DocData.create("msg", "消息内容"));
            data = DocData.create("data", "数据");
        } else {
            Set> entries = responseMap.entrySet();
            for (Map.Entry entry : entries) {
                docContent.setResponseArg(DocData.create(entry.getKey(), entry.getValue()));
            }
            data = DocData.create(dataKey, dataDesc);
        }

        if (data == null) {
            return;
        }

        if (void.class.equals(result)) {
            docContent.setResponseArg(data);
            return;
        }
        data.addChildren(getDocData(result));
        docContent.setResponseArg(data);
    }

    private void setReqMethod(DocContent docContent, String preUrl, Annotation annotation, String simpleName) {
        String url = preUrl;
        String requestMethod = simpleName.replace("Mapping", "").toUpperCase();
        docContent.setRequestMethod(requestMethod);
        String[] values = null;
        if (requestMethod.equals("POST")) {
            PostMapping postMapping = (PostMapping) annotation;
            values = postMapping.value();

        } else if ("GET".equals(requestMethod)) {
            GetMapping postMapping = (GetMapping) annotation;
            values = postMapping.value();

        } else if ("DELETE".equals(requestMethod)) {
            DeleteMapping postMapping = (DeleteMapping) annotation;
            values = postMapping.value();
        } else if ("PUT".equals(requestMethod)) {
            PutMapping postMapping = (PutMapping) annotation;
            values = postMapping.value();
        }
        if (values != null && values.length > 0) {
            String value = values[0];
            url += value.startsWith("/") ? value : "/" + value;
        }

        docContent.setUrl(url);
        log.info("请求方法:{},请求地址:{}", requestMethod, url);
    }

    private void setRequestParams(Method method, DocContent docContent) {
        Class[] parameterTypes = method.getParameterTypes();
        for (Class parameterType : parameterTypes) {
            log.info("参数类型:{}", parameterType);
            // 判断是否为基本包装类
            // 或者是基本类型
            if (basicObjects.contains(parameterType)) {
                // 判断是否带 @PathVariable 注解
                boolean pathVariable = isPathVariable(method);
                if(!pathVariable){
                }
                continue;
            }
            docContent.setRequestArgs(getDocData(parameterType));
        }
    }

    public List getDocData(Class clazz) {
        DomainParse domainParse = ParseUtils.domain(clazz);
        AssertUtils.notNull(domainParse, "实体类解析失败");
        List fieldList =
                AssertUtils.notNull(domainParse.getFieldList(), "实体类解析失败");
        List docDataList = new ArrayList<>();
        for (DomainParse.Field parseField : fieldList) {
            String name = parseField.getName();
            String comment = parseField.getComment();

            Field field = ReflectUtil.getField(clazz, name);
            Class type = field.getType();
            DocData docData = DocData.create(name, comment);
            // 是基本对象
            if (basicObjects.contains(type)) {
                docDataList.add(docData);
                continue;
            }
            // 不是基本对象
            // list 还是 普通对象
            if (List.class.equals(type)) {
                // 如果是List类型,得到其Generic的类型
                Type genericType = field.getGenericType();
                if (genericType == null) {
                    docDataList.add(docData);
                    continue;
                }
                // 如果是泛型参数的类型
                if (genericType instanceof ParameterizedType) {
                    ParameterizedType pt = (ParameterizedType) genericType;
                    //得到泛型里的class类型对象
                    Class genericClazz = (Class) pt.getActualTypeArguments()[0];
                    if(!clazz.equals(genericClazz)){
                        // 可能会出现递归死循环
                        docData.addChildren(getDocData(genericClazz));
                    }
                    docData.setType("array[object]");
                }
            } else {
                // 可能会出现递归死循环
                if(!clazz.equals(field.getType())){
                    docData.addChildren(getDocData(field.getType()));
                }
                docData.setType("object");
            }
            docDataList.add(docData);
        }
        return docDataList;
    }

    /**
     * 获取给 "方法参数" 进行注解的值
     *
     * @param method 要获取参数名的方法
     * @return 按参数顺序排列的参数名列表
     */
    private boolean isJson(Method method) {
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        if (parameterAnnotations == null || parameterAnnotations.length == 0) {
            return false;
        }
        for (Annotation[] parameterAnnotation : parameterAnnotations) {
            for (Annotation annotation : parameterAnnotation) {
                if (annotation instanceof RequestBody) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 获取给 "方法参数" 进行注解的值
     *
     * @param method 要获取参数名的方法
     * @return 按参数顺序排列的参数名列表
     */
    private boolean isPathVariable(Method method) {
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        if (parameterAnnotations == null || parameterAnnotations.length == 0) {
            return false;
        }
        for (Annotation[] parameterAnnotation : parameterAnnotations) {
            for (Annotation annotation : parameterAnnotation) {
                if (annotation instanceof PathVariable) {
                    return true;
                }
            }
        }
        return false;
    }
}