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

com.pm.japi.sacnner.ApiDocumentationScanner Maven / Gradle / Ivy

The newest version!
package com.pm.japi.sacnner;

import com.pm.japi.annotations.Api;
import com.pm.japi.annotations.ApiMethod;
import com.pm.japi.annotations.ApiNote;
import com.pm.japi.annotations.ApiParam;
import com.pm.japi.model.*;
import com.pm.japi.resolver.ResolverType;
import com.pm.japi.spring.handler.WebFluxRequestHandler;
import com.pm.japi.spring.handler.WebMvcRequestHandler;
import com.pm.japi.spring.provider.WebFluxRequestHandlerProvider;
import com.pm.japi.spring.provider.WebMvcRequestHandlerProvider;
import com.pm.japi.utils.PathUtils;
import org.apache.commons.lang3.StringUtils;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.util.pattern.PathPattern;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;


public class ApiDocumentationScanner {

    @Autowired(required = false)
    private WebMvcRequestHandlerProvider webMvcRequestHandlerProvider;

    @Autowired(required = false)
    private WebFluxRequestHandlerProvider webFluxRequestHandlerProvider;

    private ApiConfigInfo apiConfigInfo;

    public ApiDocumentationScanner(ApiConfigInfo configInfo) {
        this.apiConfigInfo = configInfo;
    }

    public ApiDocument scanDocument() {
        ApiDocument apiDocument = new ApiDocument();


        //处理method
        Map apiMap = new HashMap();
        ModelProvider modelProvider = new ModelProvider();
        scanWebMvc(apiMap, modelProvider);
        scanWebFlux(apiMap, modelProvider);

        // 在document中,添加接口参数的数据模型
        Map moduleMap = new HashMap();

        //把所有api,组装到module中
        apiMap.entrySet().forEach(p -> {
            ApiInfo apiInfo = p.getValue();
            Module module = moduleMap.get(apiInfo.getModule());
            if (module == null) {
                module = new Module();
                module.setName(apiInfo.getModule());
                moduleMap.put(module.getName(), module);

                apiDocument.getModuleList().add(module);
            }
            module.getApiList().add(apiInfo);
        });


        this.moduleSort(apiDocument.getModuleList());
        this.tryAddSysMarkDown(apiDocument);

        apiDocument.setDefines(modelProvider.getTypeMap());
        apiDocument.setConfig(apiConfigInfo);
        return apiDocument;
    }


    private void scanWebMvc(Map apiMap, ModelProvider modelProvider) {
        if (webMvcRequestHandlerProvider == null) {
            return;
        }

        List list = webMvcRequestHandlerProvider.getRequestHandlers();
        list.stream().filter(p -> p.isAnnotatedWith(ApiMethod.class)).forEach(p -> {

            //方法上的注解
            ApiMethod apiMethod = p.getHandlerMethod().getMethod().getAnnotation(ApiMethod.class);
            org.springframework.web.servlet.mvc.method.RequestMappingInfo requestMapping = p.getRequestMapping();
            String[] urls = requestMapping.getPatternsCondition().getPatterns().toArray(new String[0]);

            //由于spring注解中,path和value是别名关系,防止开发人员2种方式都在写,所以需要utils帮忙2边都有值


            //获取方法对应的url
            if (urls == null || urls.length == 0) {
                return;
            }

            Method method = new Method();
            Set mds = requestMapping.getMethodsCondition().getMethods();
            String mdStr = "";
            for (RequestMethod rm : mds) {
                if (StringUtils.isNotBlank(mdStr)) {
                    mdStr = mdStr + "," + rm.name();
                } else {
                    mdStr = mdStr + "  " + rm.name();
                }
            }

            method.setType(mdStr);

            method.setName(apiMethod.value());
            method.setMarkDown(apiMethod.markDown());
            method.setNote(apiMethod.note());
            method.setOrder(apiMethod.order());

            //请求的参数
            List apiParamList = Arrays.asList(apiMethod.params());
            List paramList = parse(modelProvider, apiParamList);
            method.setParamList(paramList);

            //返回的参数
            List apiResultList = Arrays.asList(apiMethod.result());
            List resultList = parse(modelProvider, apiResultList);
            method.setResultList(resultList);

            //Controller上的api描述
            Api api = AnnotatedElementUtils.findMergedAnnotation(p.getHandlerMethod().getBeanType(), Api.class);
            String apiClassName = p.getHandlerMethod().getBeanType().getName();
            ApiInfo apiInfo = apiMap.get(apiClassName);
            if (apiInfo == null) {
                apiInfo = new ApiInfo();
                apiInfo.setModule(api.module());
                apiInfo.setName(api.value());
                apiInfo.setMarkDown(api.markDown());
                apiInfo.setHidden(api.hidden());
                apiInfo.setTags(api.tags());
                apiInfo.setOrder(api.order());

                apiMap.put(apiClassName, apiInfo);
            } else {
                apiMap.get(apiClassName);
            }


            //返回参数类型处理
            Type returnType = p.getHandlerMethod().getMethod().getGenericReturnType();
            modelProvider.addType(returnType, null);
            method.setReturnType(returnType.getTypeName());

            //处理请求的参数类型
            if (!ApiMethod.class.equals(apiMethod.paramType())) {
                //不是默认的,说明是设置过的,则需要处理
                modelProvider.addType(apiMethod.paramType(), null);
                method.setParamType(apiMethod.paramType().getTypeName());
            }

            for (String url : urls) {
                Method fMethod = method.clone();
                fMethod.setPath(url);
                //方法接口地址叠加
                apiInfo.getMethodList().add(fMethod);
            }
        });
    }


    private void scanWebFlux(Map apiMap, ModelProvider modelProvider) {
        if (webFluxRequestHandlerProvider == null) {
            return;
        }

        List list = webFluxRequestHandlerProvider.getRequestHandlers();
        list.stream().filter(p -> p.isAnnotatedWith(ApiMethod.class)).forEach(p -> {

            //方法上的注解
            ApiMethod apiMethod = p.getHandlerMethod().getMethod().getAnnotation(ApiMethod.class);
            org.springframework.web.reactive.result.method.RequestMappingInfo requestMapping = p.getRequestMapping();
            PathPattern[] urls = requestMapping.getPatternsCondition().getPatterns().toArray(new PathPattern[0]);

            //由于spring注解中,path和value是别名关系,防止开发人员2种方式都在写,所以需要utils帮忙2边都有值


            //获取方法对应的url
            if (urls == null || urls.length == 0) {
                return;
            }

            Method method = new Method();
            Set mds = requestMapping.getMethodsCondition().getMethods();
            String mdStr = "";
            for (RequestMethod rm : mds) {
                if (StringUtils.isNotBlank(mdStr)) {
                    mdStr = mdStr + "," + rm.name();
                } else {
                    mdStr = mdStr + "  " + rm.name();
                }
            }

            method.setType(mdStr);

            method.setName(apiMethod.value());
            method.setMarkDown(apiMethod.markDown());
            method.setNote(apiMethod.note());
            method.setOrder(apiMethod.order());

            //请求的参数
            List apiParamList = Arrays.asList(apiMethod.params());
            List paramList = parse(modelProvider, apiParamList);
            method.setParamList(paramList);

            //返回的参数
            List apiResultList = Arrays.asList(apiMethod.result());
            List resultList = parse(modelProvider, apiResultList);
            method.setResultList(resultList);

            //Controller上的api描述
            Api api = AnnotatedElementUtils.findMergedAnnotation(p.getHandlerMethod().getBeanType(), Api.class);
            String apiClassName = p.getHandlerMethod().getBeanType().getName();
            ApiInfo apiInfo = apiMap.get(apiClassName);
            if (apiInfo == null) {
                apiInfo = new ApiInfo();
                apiInfo.setModule(api.module());
                apiInfo.setName(api.value());
                apiInfo.setMarkDown(api.markDown());
                apiInfo.setHidden(api.hidden());
                apiInfo.setTags(api.tags());
                apiInfo.setOrder(api.order());

                apiMap.put(apiClassName, apiInfo);
            } else {
                apiMap.get(apiClassName);
            }


            //返回参数类型处理
            Type returnType = p.getHandlerMethod().getMethod().getGenericReturnType();
            // 【重要】去掉Mono Just
            returnType = putOff(modelProvider, returnType);
            modelProvider.addType(returnType, null);
            method.setReturnType(returnType.getTypeName());

            //处理请求的参数类型
            if (!ApiMethod.class.equals(apiMethod.paramType())) {
                //不是默认的,说明是设置过的,则需要处理
                modelProvider.addType(apiMethod.paramType(), null);
                method.setParamType(apiMethod.paramType().getTypeName());
            }

            for (PathPattern url : urls) {
                Method fMethod = method.clone();
                fMethod.setPath(url.getPatternString());
                //方法接口地址叠加
                apiInfo.getMethodList().add(fMethod);
            }
        });
    }

    private Type putOff(ModelProvider modelProvider, Type type) {

        if (type.getTypeName().startsWith(Mono.class.getName())
                || type.getTypeName().startsWith(Flux.class.getName())) {
            if (type instanceof ParameterizedType) {
                type = ((ParameterizedType) type).getActualTypeArguments()[0];
            }
        }
        return type;
    }

    private void tryAddSysMarkDown(ApiDocument apiDocument) {

        if (StringUtils.isNotBlank(apiConfigInfo.getReadMe())) {
            Module module = new Module();
            module.setName("简介");
            module.setMarkDown("");
            apiDocument.getModuleList().add(0, module);

            ApiInfo apiInfo = new ApiInfo();
            apiInfo.setName("介绍");
            apiInfo.setMarkDown("");
            module.getApiList().add(apiInfo);

            Method method = new Method();
            method.setMarkDown(apiConfigInfo.getReadMe());
            method.setName("ReadMe");
            apiInfo.getMethodList().add(method);


        }
    }

    private void moduleSort(List moduleList) {

        moduleList.forEach(module -> {
            module.getApiList().forEach(api -> {
                //方法接口排序
                api.getMethodList().sort((a, b) -> {
                    return a.getOrder() - b.getOrder();
                });
            });
            //api排序
            module.getApiList().sort((a, b) -> {
                return a.getOrder() - b.getOrder();
            });
        });

        //module 排序
        moduleList.sort((a, b) -> {
            return a.getName().compareTo(b.getName());
        });
    }


    private List parse(ModelProvider modelProvider, List apiParamList) {

        apiParamList.sort((p1, p2) -> {
            return p1.value().compareTo(p2.value());
        });

        Map paramMap = new HashMap();
        List paramList = new ArrayList();

        for (ApiParam bean : apiParamList) {
            //剔除注解的默认对象值,value必须有值
            if (StringUtils.isBlank(bean.value())) {
                continue;
            }

            Param param = new Param();
            param.setTitle(bean.title());
            param.setFrom(bean.from());
            param.setNote(bean.note());
            param.setName(bean.value());

            String field = null;
            // 判断是否有引用的字段
            int filedIndex = param.getName().indexOf("$");
            if (filedIndex >= 0) {
                field = param.getName().substring(filedIndex + 1);
                param.setName(param.getName().replace("$", ""));
            }

            //处理名称
            if (param.getName().indexOf(".") > 0) {
                //说明是user.id方式;
                String parentName = PathUtils.parentName(param.getName(), ".");
                param.setName(PathUtils.getName(param.getName(), "."));
                Param pt = checkParent(paramMap, paramList, parentName, param);
                pt.getProperties().add(param);
            } else {
                //普通模式
                paramList.add(param);//放入结果中
            }


            // 处理type和引用的字段
            param.setType("$" + bean.type().getTypeName());

            if (StringUtils.isNotBlank(field)) {
                Field f = ResolverType.getField(bean.type(), field);
                if (StringUtils.isBlank(param.getNote())) {
                    ApiNote apiNote = f.getAnnotation(ApiNote.class);
                    if (apiNote != null) {
                        param.setNote(apiNote.value());
                    }
                }
                //设置真正的数据类型,如果是其他bean,不会递归处理,由界面处理
                param.setType("$" + f.getGenericType().getTypeName());
                modelProvider.addType(f.getGenericType(), null);

            } else {
                modelProvider.addType(bean.type(), null);
            }

            paramMap.put(param.getName(), param);
        }

        return paramList;
    }

    private Param checkParent(Map paramMap, List paramList, String path, Param child) {
        //是否有节点
        if (paramMap.get(path) == null) {
            //没有
            if (path.indexOf(".") > 0) {
                //如果还有父节点,继续处理
                String parentName = PathUtils.parentName(path, ".");
                Param pt = checkParent(paramMap, paramList, parentName, child);

                String name = PathUtils.getName(path, ".");
                Param parent = new Param();
                parent.setName(name);
                parent.setFrom(child.getFrom());
                parent.setType("$" + Object.class.getTypeName());

                pt.getProperties().add(parent);

                paramMap.put(path, parent);
                return parent;

            } else {
                //如果是定点
                Param parent = new Param();
                parent.setName(path);
                parent.setFrom(child.getFrom());
                parent.setType("$" + Object.class.getTypeName());

                paramMap.put(path, parent);
                paramList.add(parent);
                return parent;
            }
        } else {
            return paramMap.get(path);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy