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

shz.spring.api.ApiInfo Maven / Gradle / Ivy

There is a newer version: 10.2.5
Show newest version
package shz.spring.api;

import com.sun.javadoc.MethodDoc;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import shz.core.*;
import shz.core.api.doc.ApiDocDto;
import shz.core.api.doc.ApiDocDtoHandler;
import shz.core.api.doc.ApiDocType;
import shz.core.model.Uniquer;
import shz.core.msg.ServerFailureMsg;
import shz.core.tuple.Tuple3;
import shz.spring.BeanContainer;
import shz.spring.api.doc.DocDto;
import shz.spring.api.doc.DocHelp;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

public final class ApiInfo {
    private T data;
    private ApiInfo child;
    private Map> childes;

    private ApiInfo() {
    }

    public static  ApiInfo of() {
        ApiInfo apiInfo = new ApiInfo<>();
        apiInfo.childes = new HashMap<>();
        return apiInfo;
    }

    /**
     * 不保证线程安全,只用于项目初始化
     */
    public void put(String method, String path, T data) {
        ApiInfo apiInfo = childes.computeIfAbsent(method, k -> new ApiInfo<>());
        String[] array = format(path).split("/");
        int i = 0;
        for (; i < array.length; ++i) {
            String key = array[i];
            if (key.length() == 2 && key.charAt(0) == '{' && key.charAt(1) == '}') {
                if (apiInfo.child == null) apiInfo.child = new ApiInfo<>();
                apiInfo = apiInfo.child;
            } else {
                if (apiInfo.childes == null) apiInfo.childes = new HashMap<>();
                apiInfo = apiInfo.childes.computeIfAbsent(key, k -> new ApiInfo<>());
            }
        }
        apiInfo.data = data;
    }

    private String format(String path) {
        if (path.length() == 1) return path;
        if (path.charAt(0) == '/') path = path.substring(1);
        if (path.length() == 1) return path;
        if (path.charAt(path.length() - 1) == '/') path = path.substring(0, path.length() - 1);
        return path;
    }

    public T get(String method, String path) {
        ApiInfo apiInfo = childes.get(method);
        if (apiInfo == null) return null;
        String[] array = format(path).split("/");
        int i = 0;
        for (; i < array.length; ++i) {
            if (apiInfo.child != null) apiInfo = apiInfo.child;
            else if (apiInfo.childes != null) {
                apiInfo = apiInfo.childes.get(array[i]);
                if (apiInfo == null) return null;
            } else return null;
        }
        return apiInfo.data;
    }

    public static void consumerDto(ApiDocDtoHandler handler, Consumer consumer, BiFunction pathFun) {
        if (handler == null) return;
        Set beanNames = new HashSet<>();
        BeanContainer.consumerBeanAnnotatedWith(RestController.class, (name, bean) -> consumerDto0(handler, consumer, pathFun, beanNames, name, bean));
        BeanContainer.consumerBeanAnnotatedWith(Controller.class, (name, bean) -> consumerDto0(handler, consumer, pathFun, beanNames, name, bean));
        DocHelp.clearInfo();
    }

    private static void consumerDto0(ApiDocDtoHandler handler, Consumer consumer, BiFunction pathFun, Set beanNames, String beanName, Object bean) {
        if (beanNames.contains(beanName)) return;
        beanNames.add(beanName);

        Object targetBean = BeanContainer.getTargetBean(bean);
        Class cls = targetBean.getClass();
        String typeName = cls.getTypeName();

        Permission cPermission = cls.getAnnotation(Permission.class);
        int cPer;
        if (cPermission == null) cPer = 0;
        else {
            cPer = cPermission.value();
            ServerFailureMsg.requireNon(cPer < 0, "类:%s,权限值不能小于0", typeName);
        }

        DocDto doc = DocHelp.execute(cls);
        if (doc.classDoc == null || NullHelp.isEmpty(doc.methodDocs)) return;
        String groupName = NullHelp.isBlank(doc.classDoc.commentText()) ? cls.getName() : doc.classDoc.commentText();

        ApiInfoDto cDto = apiInfoDto(cls.getAnnotation(RequestMapping.class), a -> Tuple3.apply(a.path(), a.value(), null));
        String basePath = cDto == null ? "" : cDto.path;

        AccessibleHelp.methods(cls, m -> {
            String[] parameterNames = BeanContainer.getParameterNames(m);
            MethodDoc methodDoc = doc.methodDocs.get(DocHelp.key(m, parameterNames));
            if (methodDoc == null) return false;
            String name = NullHelp.isBlank(methodDoc.commentText()) ? m.getName() : methodDoc.commentText();

            ApiInfoDto dto = apiInfoDto(m.getAnnotation(RequestMapping.class), a -> Tuple3.apply(a.path(), a.value(), a.method().length == 0 ? "GET" : a.method()[0].name()));
            if (dto == null)
                dto = apiInfoDto(m.getAnnotation(GetMapping.class), a -> Tuple3.apply(a.path(), a.value(), "GET"));
            if (dto == null)
                dto = apiInfoDto(m.getAnnotation(PostMapping.class), a -> Tuple3.apply(a.path(), a.value(), "POST"));
            if (dto == null)
                dto = apiInfoDto(m.getAnnotation(PutMapping.class), a -> Tuple3.apply(a.path(), a.value(), "PUT"));
            if (dto == null)
                dto = apiInfoDto(m.getAnnotation(PatchMapping.class), a -> Tuple3.apply(a.path(), a.value(), "PATCH"));
            if (dto == null)
                dto = apiInfoDto(m.getAnnotation(DeleteMapping.class), a -> Tuple3.apply(a.path(), a.value(), "DELETE"));

            if (dto == null) return false;

            dto.groupName = groupName;
            dto.name = name;

            dto.notePath = FileHelp.jointPath(basePath, dto.path);
            dto.path = FileHelp.jointPath(formatPath(basePath), formatPath(dto.path));
            if (NullHelp.isBlank(dto.path)) dto.path = "/";
            else if (dto.path.charAt(0) != '/') dto.path = '/' + dto.path;

            String methodName = m.getName();
            Permission permission = m.getAnnotation(Permission.class);
            int per;
            boolean login;
            if (permission == null) {
                Integer per0;
                if (pathFun != null && (per0 = pathFun.apply(dto.notePath, dto.path)) != null && per0 > 0) {
                    login = true;
                    per = per0;
                } else {
                    login = false;
                    per = 0;
                }
            } else {
                ServerFailureMsg.requireNon(permission.value() < 0, "类:%s,方法:%s,权限值不能小于0", typeName, methodName);
                login = true;
                if (permission.value() == 0) per = 0;
                else per = cPer + permission.value();
            }

            dto.permission = per;
            dto.login = login;

            dto.reqVo = reqVo(handler, methodDoc, parameterNames, m, dto);
            dto.resVo = ApiDocDto.of(handler, m.getReturnType(), m.getGenericReturnType(), Boolean.TRUE, DocHelp.getReturn(methodDoc), null, null);

            consumer.accept(dto);
            return false;
        });
    }

    private static  ApiInfoDto apiInfoDto(A a, Function> func) {
        if (a == null) return null;
        Tuple3 tuple3 = func.apply(a);
        ApiInfoDto dto = new ApiInfoDto();
        dto.path = tuple3._1.length != 0 ? tuple3._1[0] : tuple3._2.length != 0 ? tuple3._2[0] : "";
        dto.method = tuple3._3;
        return dto;
    }

    private static String formatPath(String path) {
        if (NullHelp.isBlank(path)) return "";
        return RegexHelp.replace(BeanContainer.getExpressionProperty(path), "\\{[^}]+?\\}", matcher -> "{}");
    }

    private static ReqApiInfoVo reqVo(ApiDocDtoHandler handler, MethodDoc methodDoc, String[] parameterNames, Method m, ApiInfoDto dto) {
        ReqApiInfoVo reqVo = new ReqApiInfoVo();
        reqVo.notePath = dto.notePath;
        reqVo.method = dto.method;
        reqVo.contentType = ApiContentType.JSON.getCode();
        reqVo.pathVariables = new ArrayList<>();
        reqVo.params = new ArrayList<>();
        reqVo.parts = new ArrayList<>();

        Map paramComment = DocHelp.getParams(methodDoc);
        if (NullHelp.nonEmpty(parameterNames)) {
            Annotation[][] parameterAnnotations = m.getParameterAnnotations();
            Class[] parameterTypes = m.getParameterTypes();
            Type[] genericParameterTypes = m.getGenericParameterTypes();

            Set existParamName = new HashSet<>();
            ApiDocType type;
            if (NullHelp.isEmpty(parameterAnnotations)) {
                for (int i = 0; i < parameterNames.length; ++i) {
                    if (existParamName.contains(parameterNames[i])) continue;
                    existParamName.add(parameterNames[i]);

                    type = handler.type(parameterTypes[i]);
                    if (Map.class.isAssignableFrom(parameterTypes[i]) || simpleParam(type))
                        reqVo.params.add(ApiDocDto.of(handler, parameterTypes[i], genericParameterTypes[i], Boolean.FALSE, paramComment.get(parameterNames[i]), parameterNames[i], null));
                    else if (objectParam(type)) {
                        ApiDocDto apiDocDto = ApiDocDto.of(handler, parameterTypes[i], genericParameterTypes[i], Boolean.FALSE, paramComment.get(parameterNames[i]), parameterNames[i], null);
                        List properties = apiDocDto.getProperties();
                        if (NullHelp.nonEmpty(properties)) {
                            for (ApiDocDto p : properties) {
                                if (existParamName.contains(p.getField())) continue;
                                existParamName.add(p.getField());

                                reqVo.params.add(p);
                            }
                        }
                    }
                }
            } else {
                Map requires = new HashMap<>();
                Map values = new HashMap<>();

                Map pathVariables = new HashMap<>();
                int bodyIdx = -1;
                Map params = new HashMap<>();
                Map parts = new HashMap<>();

                boolean mark;
                for (int i = 0; i < parameterNames.length; ++i) {
                    mark = false;
                    if (NullHelp.nonEmpty(parameterAnnotations[i])) {
                        for (Annotation a : parameterAnnotations[i]) {
                            if (a instanceof PathVariable) {
                                PathVariable pathVariable = (PathVariable) a;
                                requires.put(i, pathVariable.required());
                                if (NullHelp.nonBlank(pathVariable.value())) pathVariables.put(i, pathVariable.value());
                                else if (NullHelp.nonBlank(pathVariable.name()))
                                    pathVariables.put(i, pathVariable.name());
                                else pathVariables.put(i, parameterNames[i]);
                                mark = true;
                                break;
                            } else if (a instanceof RequestBody) {
                                RequestBody requestBody = (RequestBody) a;
                                requires.put(i, requestBody.required());
                                bodyIdx = i;
                                mark = true;
                                break;
                            } else if (a instanceof RequestParam) {
                                RequestParam requestParam = (RequestParam) a;
                                requires.put(i, requestParam.required());
                                if (!"\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n".equals(requestParam.defaultValue()))
                                    values.put(i, requestParam.defaultValue());
                                if (NullHelp.nonBlank(requestParam.value())) params.put(i, requestParam.value());
                                else if (NullHelp.nonBlank(requestParam.name())) params.put(i, requestParam.name());
                                else params.put(i, parameterNames[i]);
                                mark = true;
                                break;
                            } else if (a instanceof RequestAttribute) {
                                RequestAttribute requestAttribute = (RequestAttribute) a;
                                requires.put(i, requestAttribute.required());
                                if (NullHelp.nonBlank(requestAttribute.value()))
                                    params.put(i, requestAttribute.value());
                                else if (NullHelp.nonBlank(requestAttribute.name()))
                                    params.put(i, requestAttribute.name());
                                else params.put(i, parameterNames[i]);
                                mark = true;
                                break;
                            } else if (a instanceof RequestPart) {
                                RequestPart requestPart = (RequestPart) a;
                                requires.put(i, requestPart.required());
                                if (NullHelp.nonBlank(requestPart.value())) parts.put(i, requestPart.value());
                                else if (NullHelp.nonBlank(requestPart.name())) parts.put(i, requestPart.name());
                                else parts.put(i, parameterNames[i]);
                                mark = true;
                                break;
                            }
                        }
                    }

                    if (!mark) {
                        requires.put(i, Boolean.FALSE);
                        params.put(i, parameterNames[i]);
                    }
                }

                if (bodyIdx != -1) {
                    for (int i = 0; i < parameterNames.length; ++i) {
                        if (pathVariables.containsKey(i)) {
                            if (pathVariable(handler.type(parameterTypes[i])))
                                reqVo.pathVariables.add(ApiDocDto.of(handler, parameterTypes[i], genericParameterTypes[i], Boolean.TRUE, paramComment.get(parameterNames[i]), pathVariables.get(i), null));
                        } else if (i == bodyIdx)
                            reqVo.body = ApiDocDto.of(handler, parameterTypes[i], genericParameterTypes[i], requires.get(i), paramComment.get(parameterNames[i]), null, null);
                    }
                } else {
                    for (int i = 0; i < parameterNames.length; ++i) {
                        if (pathVariables.containsKey(i)) {
                            if (pathVariable(handler.type(parameterTypes[i])))
                                reqVo.pathVariables.add(ApiDocDto.of(handler, parameterTypes[i], genericParameterTypes[i], Boolean.TRUE, paramComment.get(parameterNames[i]), pathVariables.get(i), null));
                        } else if (parts.containsKey(i)) {
                            if (part(handler.type(parameterTypes[i])))
                                reqVo.parts.add(ApiDocDto.of(handler, parameterTypes[i], genericParameterTypes[i], requires.get(i), paramComment.get(parameterNames[i]), parts.get(i), null));
                        } else if (params.containsKey(i)) {
                            if (existParamName.contains(params.get(i))) continue;
                            existParamName.add(params.get(i));

                            type = handler.type(parameterTypes[i]);
                            if (Map.class.isAssignableFrom(parameterTypes[i]) || simpleParam(type))
                                reqVo.params.add(ApiDocDto.of(handler, parameterTypes[i], genericParameterTypes[i], requires.get(i), paramComment.get(parameterNames[i]), params.get(i), values.get(i)));
                            else if (objectParam(type)) {
                                ApiDocDto apiDocDto = ApiDocDto.of(handler, parameterTypes[i], genericParameterTypes[i], requires.get(i), paramComment.get(parameterNames[i]), params.get(i), values.get(i));
                                List properties = apiDocDto.getProperties();
                                if (NullHelp.nonEmpty(properties)) {
                                    for (ApiDocDto p : properties) {
                                        if (existParamName.contains(p.getField())) continue;
                                        existParamName.add(p.getField());

                                        reqVo.params.add(p);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return reqVo;
    }

    private static boolean pathVariable(ApiDocType type) {
        return type == ApiDocType.string || type == ApiDocType.bool || type == ApiDocType.integer || type == ApiDocType.number || type == ApiDocType.date || type == ApiDocType.array;
    }

    private static boolean part(ApiDocType type) {
        return type == ApiDocType.file || type == ApiDocType.array;
    }

    private static boolean simpleParam(ApiDocType type) {
        return type == ApiDocType.string || type == ApiDocType.bool || type == ApiDocType.integer || type == ApiDocType.number || type == ApiDocType.date || type == ApiDocType.file || type == ApiDocType.array;
    }

    private static boolean objectParam(ApiDocType type) {
        return type == ApiDocType.object;
    }

    public static void consumerDto(Consumer consumer, BiFunction pathFun) {
        consumerDto(BeanContainer.get(ApiDocDtoHandler.class), consumer, pathFun);
    }

    public static  List generate(Function mapper) {
        List result = new LinkedList<>();
        Uniquer uniquer = Uniquer.normal(ApiInfoDto::getGroupName, ApiInfoDto::getPath, ApiInfoDto::getMethod);
        consumerDto(dto -> {
            if (uniquer.putIfAbsent(dto)) {
                T t = mapper.apply(dto);
                if (t != null) result.add(t);
            }
        }, null);
        return result.isEmpty() ? Collections.emptyList() : new ArrayList<>(result);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy