chore(api-doc): 优化部分代码

This commit is contained in:
2024-08-14 22:14:02 +08:00
parent bf51837991
commit 4c4f98a86e
8 changed files with 287 additions and 282 deletions

View File

@@ -17,16 +17,11 @@
package top.continew.starter.apidoc.autoconfigure;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.SimpleType;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import jakarta.annotation.PostConstruct;
@@ -48,7 +43,7 @@ import org.springframework.http.CacheControl;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import top.continew.starter.apidoc.handler.GenericEnumHandler;
import top.continew.starter.apidoc.handler.BaseEnumParameterHandler;
import top.continew.starter.apidoc.handler.OpenApiHandler;
import top.continew.starter.core.autoconfigure.project.ProjectProperties;
import top.continew.starter.core.util.GeneralPropertySourceFactory;
@@ -154,17 +149,16 @@ public class SpringDocAutoConfiguration implements WebMvcConfigurer {
}
/**
* 自定义参数配置(针对 BaseEnum 展示枚举值和描述)
* 自定义 BaseEnum 枚举参数配置(针对实现了 BaseEnum 的枚举,优化其枚举值和描述展示
*
* @return {@link GenericEnumHandler }
* @return {@link BaseEnumParameterHandler }
* @since 2.4.0
*/
@Bean
public GenericEnumHandler customParameterCustomizer() {
return new GenericEnumHandler();
public BaseEnumParameterHandler customParameterCustomizer() {
return new BaseEnumParameterHandler();
}
@PostConstruct
public void postConstruct() {
log.debug("[ContiNew Starter] - Auto Configuration 'ApiDoc' completed initialization.");

View File

@@ -0,0 +1,118 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.apidoc.handler;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.SimpleType;
import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import org.springdoc.core.customizers.ParameterCustomizer;
import org.springdoc.core.customizers.PropertyCustomizer;
import org.springframework.core.MethodParameter;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import top.continew.starter.apidoc.util.DocUtils;
import top.continew.starter.core.enums.BaseEnum;
/**
* 自定义 BaseEnum 枚举参数处理器
* <p>
* 针对实现了 BaseEnum 的枚举,优化其枚举值和描述展示
* </p>
*
* @author echo
* @since 2.5.2
*/
public class BaseEnumParameterHandler implements ParameterCustomizer, PropertyCustomizer {
@Override
public Parameter customize(Parameter parameterModel, MethodParameter methodParameter) {
Class<?> parameterType = methodParameter.getParameterType();
// 判断是否为 BaseEnum 的子类型
if (!ClassUtil.isAssignable(BaseEnum.class, parameterType)) {
return parameterModel;
}
String description = parameterModel.getDescription();
if (StrUtil.contains(description, "color:red")) {
return parameterModel;
}
// 自定义枚举描述并封装参数配置
configureSchema(parameterModel.getSchema(), parameterType);
parameterModel.setDescription(appendEnumDescription(description, parameterType));
return parameterModel;
}
@Override
public Schema customize(Schema schema, AnnotatedType type) {
Class<?> rawClass = resolveRawClass(type.getType());
// 判断是否为 BaseEnum 的子类型
if (!ClassUtil.isAssignable(BaseEnum.class, rawClass)) {
return schema;
}
// 自定义参数描述并封装参数配置
configureSchema(schema, rawClass);
schema.setDescription(appendEnumDescription(schema.getDescription(), rawClass));
return schema;
}
/**
* 封装 Schema 配置
*
* @param schema Schema
* @param enumClass 枚举类型
*/
private void configureSchema(Schema schema, Class<?> enumClass) {
BaseEnum[] enums = (BaseEnum[])enumClass.getEnumConstants();
List<String> valueList = Arrays.stream(enums).map(e -> e.getValue().toString()).toList();
schema.setEnum(valueList);
String enumValueType = DocUtils.getEnumValueTypeAsString(enumClass);
schema.setType(enumValueType);
schema.setFormat(DocUtils.resolveFormat(enumValueType));
}
/**
* 追加枚举描述
*
* @param originalDescription 原始描述
* @param enumClass 枚举类型
* @return 追加后的描述字符串
*/
private String appendEnumDescription(String originalDescription, Class<?> enumClass) {
return originalDescription + "<span style='color:red'>" + DocUtils.getDescMap(enumClass) + "</span>";
}
/**
* 解析原始类
*
* @param type 类型
* @return 原始类的 Class 对象
*/
private Class<?> resolveRawClass(Type type) {
if (type instanceof SimpleType simpleType) {
return simpleType.getRawClass();
} else if (type instanceof CollectionType collectionType) {
return collectionType.getContentType().getRawClass();
} else {
return Object.class;
}
}
}

View File

@@ -1,102 +0,0 @@
package top.continew.starter.apidoc.handler;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.SimpleType;
import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import org.springdoc.core.customizers.ParameterCustomizer;
import org.springdoc.core.customizers.PropertyCustomizer;
import org.springframework.core.MethodParameter;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import top.continew.starter.apidoc.util.DocUtils;
import top.continew.starter.core.enums.BaseEnum;
/**
* 枚举处理程序
* 主要实现对 继承了 BaseEnum 类型的枚举参数和属性进行处理
*
* @Author echo
* @date 2024/08/12
*/
public class GenericEnumHandler implements ParameterCustomizer, PropertyCustomizer {
@Override
public Parameter customize(Parameter parameterModel, MethodParameter methodParameter) {
Class<?> parameterType = methodParameter.getParameterType();
// 判断是否为 BaseEnum 的子类型
if (!ClassUtil.isAssignable(BaseEnum.class, parameterType)) {
return parameterModel;
}
String description = parameterModel.getDescription();
if (StrUtil.contains(description, "color:red")) {
return parameterModel;
}
// 自定义枚举描述并封装参数配置
configureSchema(parameterModel.getSchema(), parameterType);
parameterModel.setDescription(appendEnumDescription(description, parameterType));
return parameterModel;
}
@Override
public Schema customize(Schema schema, AnnotatedType type) {
Class<?> rawClass = resolveRawClass(type.getType());
// 判断是否为 BaseEnum 的子类型
if (!ClassUtil.isAssignable(BaseEnum.class, rawClass)) {
return schema;
}
// 自定义参数描述并封装参数配置
configureSchema(schema, rawClass);
schema.setDescription(appendEnumDescription(schema.getDescription(), rawClass));
return schema;
}
/**
* 封装 Schema 配置
*
* @param schema Schema
* @param enumClass 枚举类型
*/
private void configureSchema(Schema schema, Class<?> enumClass) {
BaseEnum[] enums = (BaseEnum[]) enumClass.getEnumConstants();
List<String> valueList = Arrays.stream(enums)
.map(e -> e.getValue().toString()).toList();
schema.setEnum(valueList);
String enumValueType = DocUtils.getEnumValueTypeAsString(enumClass);
schema.setType(enumValueType);
schema.setFormat(DocUtils.resolveFormat(enumValueType));
}
/**
* 追加枚举描述
*
* @param originalDescription 原始描述
* @param enumClass 枚举类型
* @return 追加后的描述字符串
*/
private String appendEnumDescription(String originalDescription, Class<?> enumClass) {
return originalDescription + "<span style='color:red'>" + DocUtils.getDescMap(enumClass) + "</span>";
}
/**
* 解析原始类
*
* @param type 类型
* @return 原始类的 Class 对象
*/
private Class<?> resolveRawClass(Type type) {
if (type instanceof SimpleType) {
return ((SimpleType) type).getRawClass();
} else if (type instanceof CollectionType) {
return ((CollectionType) type).getContentType().getRawClass();
} else {
return Object.class;
}
}
}

View File

@@ -1,15 +1,26 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.apidoc.util;
import cn.hutool.core.collection.CollUtil;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.web.bind.annotation.RestController;
import top.continew.starter.core.enums.BaseEnum;
@@ -17,116 +28,91 @@ import top.continew.starter.core.enums.BaseEnum;
/**
* 接口文档工具类
*
* @Author echo
* @date 2024/07/31
* @author echo
* @since 2.5.2
*/
public class DocUtils {
private DocUtils() {
}
private DocUtils() {
}
/**
* 获取枚举值类型
*
* @param enumClass 枚举类型
* @return 枚举值类型
*/
public static String getEnumValueTypeAsString(Class<?> enumClass) {
// 获取枚举类实现的所有接口
Type[] interfaces = enumClass.getGenericInterfaces();
// 定义枚举值类型的映射
Map<Class<?>, String> typeMap = Map.of(
Integer.class, "integer",
Long.class, "long",
Double.class, "number",
String.class, "string"
);
// 遍历所有接口
for (Type type : interfaces) {
// 检查接口是否为参数化类型并且原始类型为 BaseEnum
if (type instanceof ParameterizedType parameterizedType && parameterizedType.getRawType() == BaseEnum.class) {
Type actualType = parameterizedType.getActualTypeArguments()[0];
// 检查实际类型参数是否为类类型,并返回对应的字符串类型
if (actualType instanceof Class<?> actualClass) {
return typeMap.getOrDefault(actualClass, "string");
/**
* 获取枚举值类型
*
* @param enumClass 枚举类型
* @return 枚举值类型
*/
public static String getEnumValueTypeAsString(Class<?> enumClass) {
// 获取枚举类实现的所有接口
Type[] interfaces = enumClass.getGenericInterfaces();
// 定义枚举值类型的映射
Map<Class<?>, String> typeMap = Map
.of(Integer.class, "integer", Long.class, "long", Double.class, "number", String.class, "string");
// 遍历所有接口
for (Type type : interfaces) {
// 检查接口是否为参数化类型并且原始类型为 BaseEnum
if (type instanceof ParameterizedType parameterizedType && parameterizedType
.getRawType() == BaseEnum.class) {
Type actualType = parameterizedType.getActualTypeArguments()[0];
// 检查实际类型参数是否为类类型,并返回对应的字符串类型
if (actualType instanceof Class<?> actualClass) {
return typeMap.getOrDefault(actualClass, "string");
}
}
}
}
// 默认返回 "string" 类型
return "string";
}
// 默认返回 "string" 类型
return "string";
}
/**
* 解析枚举值的格式
*
* @param enumValueType 枚举值类型
* @return String 格式化类型
*/
public static String resolveFormat(String enumValueType) {
return switch (enumValueType) {
case "integer" -> "int32";
case "long" -> "int64";
case "number" -> "double";
default -> enumValueType;
};
}
/**
* 具有RestController 注释 既检查是否继承了BaseController
*
* @param clazz clazz
* @return boolean
*/
public static boolean hasRestControllerAnnotation(Class<?> clazz) {
// 如果注释包含 RestController 注解,则返回 true
if (clazz.isAnnotationPresent(RestController.class)) {
return true;
/**
* 解析枚举值的格式
*
* @param enumValueType 枚举值类型
* @return String 格式化类型
*/
public static String resolveFormat(String enumValueType) {
return switch (enumValueType) {
case "integer" -> "int32";
case "long" -> "int64";
case "number" -> "double";
default -> enumValueType;
};
}
// 递归检查父类
Class<?> superClass = clazz.getSuperclass();
// 循环检查父类
while (superClass != null && !superClass.equals(Object.class)) {
// 如果父类包含 RestController 注解,则返回 true
if (hasRestControllerAnnotation(superClass)) {
return true;
}
// 递归检查接口
superClass = superClass.getSuperclass();
}
return false;
}
/**
* 获取枚举描述 Map
*
* @param enumClass 枚举类型
* @return 枚举描述 Map
*/
public static Map<Object, String> getDescMap(Class<?> enumClass) {
BaseEnum[] enums = (BaseEnum[]) enumClass.getEnumConstants();
return Arrays.stream(enums)
.collect(Collectors.toMap(BaseEnum::getValue, BaseEnum::getDescription, (a, b) -> a, LinkedHashMap::new));
}
/**
* 将collection转化为Set集合但是两者的泛型不同<br>
* <B>{@code Collection<E> ------> Set<T> } </B>
*
* @param collection 需要转化的集合
* @param function collection中的泛型转化为set泛型的lambda表达式
* @param <E> collection中的泛型
* @param <T> Set中的泛型
* @return 转化后的Set
*/
public static <E, T> Set<T> toSet(Collection<E> collection, Function<E, T> function) {
if (CollUtil.isEmpty(collection) || function == null) {
return CollUtil.newHashSet();
/**
* 具有 RestController 注释既检查是否继承了BaseController
*
* @param clazz clazz
* @return boolean
*/
public static boolean hasRestControllerAnnotation(Class<?> clazz) {
// 如果注释包含 RestController 注解,则返回 true
if (clazz.isAnnotationPresent(RestController.class)) {
return true;
}
// 递归检查父类
Class<?> superClass = clazz.getSuperclass();
// 循环检查父类
while (superClass != null && !superClass.equals(Object.class)) {
// 如果父类包含 RestController 注解,则返回 true
if (hasRestControllerAnnotation(superClass)) {
return true;
}
// 递归检查接口
superClass = superClass.getSuperclass();
}
return false;
}
/**
* 获取枚举描述 Map
*
* @param enumClass 枚举类型
* @return 枚举描述 Map
*/
public static Map<Object, String> getDescMap(Class<?> enumClass) {
BaseEnum[] enums = (BaseEnum[])enumClass.getEnumConstants();
return Arrays.stream(enums)
.collect(Collectors.toMap(BaseEnum::getValue, BaseEnum::getDescription, (a, b) -> a, LinkedHashMap::new));
}
return collection
.stream()
.map(function)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}
}