
shz.spring.api.ApiInfo Maven / Gradle / Ivy
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