mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-10-21 20:57:15 +08:00
refactor(api-doc): 将默认API文档UI从Knife4j替换为NextDoc4j
- 移除对Knife4j相关依赖和资源配置 - 新增NextDoc4j依赖及版本管理 - 重构BaseEnumParameterHandler以继承ModelResolver - 添加ObjectMapper注入支持枚举参数解析 - 调整资源处理器配置移除旧版静态资源配置 - 删除自定义OpenApiHandler及相关构建器配置 - 更新YAML配置文件启用NextDoc4j并移除Knife4j设置
This commit is contained in:
@@ -22,10 +22,11 @@
|
||||
<artifactId>continew-starter-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Knife4j(前身是 swagger-bootstrap-ui,集 Swagger2 和 OpenAPI3 为一体的增强解决方案) -->
|
||||
<!--NextDoc4j (现代化 API 文档 UI 工具 全面替代 Swagger UI) -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
|
||||
<groupId>top.nextdoc4j</groupId>
|
||||
<artifactId>nextdoc4j-springboot3-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
@@ -17,6 +17,7 @@
|
||||
package top.continew.starter.apidoc.autoconfigure;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
@@ -29,32 +30,21 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springdoc.core.configuration.SpringDocConfiguration;
|
||||
import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
|
||||
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
|
||||
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
|
||||
import org.springdoc.core.properties.SpringDocConfigProperties;
|
||||
import org.springdoc.core.providers.JavadocProvider;
|
||||
import org.springdoc.core.service.OpenAPIService;
|
||||
import org.springdoc.core.service.SecurityService;
|
||||
import org.springdoc.core.utils.PropertyResolverUtils;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
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.BaseEnumParameterHandler;
|
||||
import top.continew.starter.apidoc.handler.OpenApiHandler;
|
||||
import top.continew.starter.core.autoconfigure.application.ApplicationProperties;
|
||||
import top.continew.starter.core.util.CollUtils;
|
||||
import top.continew.starter.core.util.GeneralPropertySourceFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* API 文档自动配置
|
||||
@@ -73,10 +63,6 @@ public class SpringDocAutoConfiguration implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler("/favicon.ico").addResourceLocations("classpath:/");
|
||||
registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
|
||||
registry.addResourceHandler("/webjars/**")
|
||||
.addResourceLocations("classpath:/META-INF/resources/webjars/")
|
||||
.setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,20 +125,6 @@ public class SpringDocAutoConfiguration implements WebMvcConfigurer {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义 OpenApi 处理器
|
||||
*/
|
||||
@Bean
|
||||
public OpenAPIService openApiBuilder(Optional<OpenAPI> openAPI,
|
||||
SecurityService securityParser,
|
||||
SpringDocConfigProperties springDocConfigProperties,
|
||||
PropertyResolverUtils propertyResolverUtils,
|
||||
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers,
|
||||
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers,
|
||||
Optional<JavadocProvider> javadocProvider) {
|
||||
return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义 BaseEnum 枚举参数配置(针对实现了 BaseEnum 的枚举,优化其枚举值和描述展示)
|
||||
*
|
||||
@@ -160,8 +132,8 @@ public class SpringDocAutoConfiguration implements WebMvcConfigurer {
|
||||
* @since 2.4.0
|
||||
*/
|
||||
@Bean
|
||||
public BaseEnumParameterHandler customParameterCustomizer() {
|
||||
return new BaseEnumParameterHandler();
|
||||
public BaseEnumParameterHandler customParameterCustomizer(ObjectMapper mapper) {
|
||||
return new BaseEnumParameterHandler(mapper);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
|
@@ -18,19 +18,23 @@ package top.continew.starter.apidoc.handler;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
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.core.converter.ModelConverter;
|
||||
import io.swagger.v3.core.converter.ModelConverterContext;
|
||||
import io.swagger.v3.core.jackson.ModelResolver;
|
||||
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 top.continew.starter.apidoc.util.ApiDocUtils;
|
||||
import top.continew.starter.core.enums.BaseEnum;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -42,7 +46,11 @@ import java.util.List;
|
||||
* @author echo
|
||||
* @since 2.5.2
|
||||
*/
|
||||
public class BaseEnumParameterHandler implements ParameterCustomizer, PropertyCustomizer {
|
||||
public class BaseEnumParameterHandler extends ModelResolver implements ParameterCustomizer {
|
||||
|
||||
public BaseEnumParameterHandler(ObjectMapper mapper) {
|
||||
super(mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parameter customize(Parameter parameterModel, MethodParameter methodParameter) {
|
||||
@@ -62,16 +70,18 @@ public class BaseEnumParameterHandler implements ParameterCustomizer, PropertyCu
|
||||
}
|
||||
|
||||
@Override
|
||||
public Schema customize(Schema schema, AnnotatedType type) {
|
||||
public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator<ModelConverter> chain) {
|
||||
Schema resolve = super.resolve(type, context, chain);
|
||||
Class<?> rawClass = resolveRawClass(type.getType());
|
||||
// 判断是否为 BaseEnum 的子类型
|
||||
if (!ClassUtil.isAssignable(BaseEnum.class, rawClass)) {
|
||||
return schema;
|
||||
return resolve;
|
||||
}
|
||||
|
||||
// 自定义参数描述并封装参数配置
|
||||
configureSchema(schema, rawClass);
|
||||
schema.setDescription(appendEnumDescription(schema.getDescription(), rawClass));
|
||||
return schema;
|
||||
configureSchema(resolve, rawClass);
|
||||
resolve.setDescription(appendEnumDescription(resolve.getDescription(), rawClass));
|
||||
return resolve;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,275 +0,0 @@
|
||||
/*
|
||||
* 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.collection.CollUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import io.swagger.v3.core.jackson.TypeNameResolver;
|
||||
import io.swagger.v3.core.util.AnnotationsUtils;
|
||||
import io.swagger.v3.oas.annotations.tags.Tags;
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.Operation;
|
||||
import io.swagger.v3.oas.models.Paths;
|
||||
import io.swagger.v3.oas.models.tags.Tag;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
|
||||
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
|
||||
import org.springdoc.core.properties.SpringDocConfigProperties;
|
||||
import org.springdoc.core.providers.JavadocProvider;
|
||||
import org.springdoc.core.service.OpenAPIService;
|
||||
import org.springdoc.core.service.SecurityService;
|
||||
import org.springdoc.core.utils.PropertyResolverUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import top.continew.starter.core.util.CollUtils;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 自定义 OpenApi 处理器(对源码功能进行修改,增强使用)
|
||||
*
|
||||
* @author echo
|
||||
* @since 2.4.0
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
public class OpenApiHandler extends OpenAPIService {
|
||||
|
||||
/**
|
||||
* The Basic error controller.
|
||||
*/
|
||||
private static Class<?> basicErrorController;
|
||||
|
||||
/**
|
||||
* The Security parser.
|
||||
*/
|
||||
private final SecurityService securityParser;
|
||||
|
||||
/**
|
||||
* The Mappings map.
|
||||
*/
|
||||
private final Map<String, Object> mappingsMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The Springdoc tags.
|
||||
*/
|
||||
private final Map<HandlerMethod, Tag> springdocTags = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The Open api builder customisers.
|
||||
*/
|
||||
private final Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers;
|
||||
|
||||
/**
|
||||
* The server base URL customisers.
|
||||
*/
|
||||
private final Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers;
|
||||
|
||||
/**
|
||||
* The Spring doc config properties.
|
||||
*/
|
||||
private final SpringDocConfigProperties springDocConfigProperties;
|
||||
|
||||
/**
|
||||
* The Cached open api map.
|
||||
*/
|
||||
private final Map<String, OpenAPI> cachedOpenAPI = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The Property resolver utils.
|
||||
*/
|
||||
private final PropertyResolverUtils propertyResolverUtils;
|
||||
|
||||
/**
|
||||
* The javadoc provider.
|
||||
*/
|
||||
private final Optional<JavadocProvider> javadocProvider;
|
||||
|
||||
/**
|
||||
* The Context.
|
||||
*/
|
||||
private ApplicationContext context;
|
||||
|
||||
/**
|
||||
* The Open api.
|
||||
*/
|
||||
private OpenAPI openAPI;
|
||||
|
||||
/**
|
||||
* The Is servers present.
|
||||
*/
|
||||
private boolean isServersPresent;
|
||||
|
||||
/**
|
||||
* The Server base url.
|
||||
*/
|
||||
private String serverBaseUrl;
|
||||
|
||||
/**
|
||||
* Instantiates a new Open api builder.
|
||||
*
|
||||
* @param openAPI the open api
|
||||
* @param securityParser the security parser
|
||||
* @param springDocConfigProperties the spring doc config properties
|
||||
* @param propertyResolverUtils the property resolver utils
|
||||
* @param openApiBuilderCustomizers the open api builder customisers
|
||||
* @param serverBaseUrlCustomizers the server base url customizers
|
||||
* @param javadocProvider the javadoc provider
|
||||
*/
|
||||
public OpenApiHandler(Optional<OpenAPI> openAPI,
|
||||
SecurityService securityParser,
|
||||
SpringDocConfigProperties springDocConfigProperties,
|
||||
PropertyResolverUtils propertyResolverUtils,
|
||||
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
|
||||
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
|
||||
Optional<JavadocProvider> javadocProvider) {
|
||||
super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
|
||||
if (openAPI.isPresent()) {
|
||||
this.openAPI = openAPI.get();
|
||||
if (this.openAPI.getComponents() == null) {
|
||||
this.openAPI.setComponents(new Components());
|
||||
}
|
||||
if (this.openAPI.getPaths() == null) {
|
||||
this.openAPI.setPaths(new Paths());
|
||||
}
|
||||
if (CollUtil.isNotEmpty(this.openAPI.getServers())) {
|
||||
this.isServersPresent = true;
|
||||
}
|
||||
}
|
||||
this.propertyResolverUtils = propertyResolverUtils;
|
||||
this.securityParser = securityParser;
|
||||
this.springDocConfigProperties = springDocConfigProperties;
|
||||
this.openApiBuilderCustomisers = openApiBuilderCustomizers;
|
||||
this.serverBaseUrlCustomizers = serverBaseUrlCustomizers;
|
||||
this.javadocProvider = javadocProvider;
|
||||
if (springDocConfigProperties.isUseFqn()) {
|
||||
TypeNameResolver.std.setUseFqn(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI, Locale locale) {
|
||||
|
||||
Set<Tag> tags = new HashSet<>();
|
||||
Set<String> tagsStr = new HashSet<>();
|
||||
|
||||
buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr, locale);
|
||||
buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr, locale);
|
||||
|
||||
if (CollUtil.isNotEmpty(tagsStr)) {
|
||||
tagsStr = CollUtils.mapToSet(tagsStr, str -> propertyResolverUtils.resolve(str, locale));
|
||||
}
|
||||
if (springdocTags.containsKey(handlerMethod)) {
|
||||
Tag tag = springdocTags.get(handlerMethod);
|
||||
tagsStr.add(tag.getName());
|
||||
if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) {
|
||||
openAPI.addTagsItem(tag);
|
||||
}
|
||||
}
|
||||
|
||||
if (CollUtil.isNotEmpty(tagsStr)) {
|
||||
if (CollUtil.isEmpty(operation.getTags())) {
|
||||
operation.setTags(new ArrayList<>(tagsStr));
|
||||
} else {
|
||||
Set<String> operationTagsSet = new HashSet<>(operation.getTags());
|
||||
operationTagsSet.addAll(tagsStr);
|
||||
operation.getTags().clear();
|
||||
operation.getTags().addAll(operationTagsSet);
|
||||
}
|
||||
}
|
||||
|
||||
if (isAutoTagClasses(operation)) {
|
||||
|
||||
if (javadocProvider.isPresent()) {
|
||||
String description = javadocProvider.get().getClassJavadoc(handlerMethod.getBeanType());
|
||||
if (StringUtils.isNotBlank(description)) {
|
||||
Tag tag = new Tag();
|
||||
|
||||
// 自定义部分 修改使用java注释当tag名
|
||||
List<String> list = IoUtil.readLines(new StringReader(description), new ArrayList<>());
|
||||
// tag.setName(tagAutoName);
|
||||
tag.setName(list.get(0));
|
||||
operation.addTagsItem(list.get(0));
|
||||
|
||||
tag.setDescription(description);
|
||||
if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) {
|
||||
openAPI.addTagsItem(tag);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String tagAutoName = splitCamelCase(handlerMethod.getBeanType().getSimpleName());
|
||||
operation.addTagsItem(tagAutoName);
|
||||
}
|
||||
}
|
||||
|
||||
if (CollUtil.isNotEmpty(tags)) {
|
||||
// Existing tags
|
||||
List<Tag> openApiTags = openAPI.getTags();
|
||||
if (CollUtil.isNotEmpty(openApiTags)) {
|
||||
tags.addAll(openApiTags);
|
||||
}
|
||||
openAPI.setTags(new ArrayList<>(tags));
|
||||
}
|
||||
|
||||
// Handle SecurityRequirement at operation level
|
||||
io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirements = securityParser
|
||||
.getSecurityRequirements(handlerMethod);
|
||||
if (securityRequirements != null) {
|
||||
if (securityRequirements.length == 0) {
|
||||
operation.setSecurity(Collections.emptyList());
|
||||
} else {
|
||||
securityParser.buildSecurityRequirement(securityRequirements, operation);
|
||||
}
|
||||
}
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
private void buildTagsFromMethod(Method method, Set<Tag> tags, Set<String> tagsStr, Locale locale) {
|
||||
// method tags
|
||||
Set<Tags> tagsSet = AnnotatedElementUtils.findAllMergedAnnotations(method, Tags.class);
|
||||
Set<io.swagger.v3.oas.annotations.tags.Tag> methodTags = tagsSet.stream()
|
||||
.flatMap(x -> Stream.of(x.value()))
|
||||
.collect(Collectors.toSet());
|
||||
methodTags.addAll(AnnotatedElementUtils
|
||||
.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class));
|
||||
if (CollUtil.isNotEmpty(methodTags)) {
|
||||
tagsStr.addAll(CollUtils.mapToSet(methodTags, tag -> propertyResolverUtils.resolve(tag.name(), locale)));
|
||||
List<io.swagger.v3.oas.annotations.tags.Tag> allTags = new ArrayList<>(methodTags);
|
||||
addTags(allTags, tags, locale);
|
||||
}
|
||||
}
|
||||
|
||||
private void addTags(List<io.swagger.v3.oas.annotations.tags.Tag> sourceTags, Set<Tag> tags, Locale locale) {
|
||||
Optional<Set<Tag>> optionalTagSet = AnnotationsUtils.getTags(sourceTags
|
||||
.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true);
|
||||
optionalTagSet.ifPresent(tagsSet -> {
|
||||
tagsSet.forEach(tag -> {
|
||||
tag.name(propertyResolverUtils.resolve(tag.getName(), locale));
|
||||
tag.description(propertyResolverUtils.resolve(tag.getDescription(), locale));
|
||||
if (tags.stream().noneMatch(t -> t.getName().equals(tag.getName()))) {
|
||||
tags.add(tag);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@@ -10,8 +10,5 @@ springdoc:
|
||||
enabled: ${springdoc.swagger-ui.enabled}
|
||||
path: /v3/api-docs
|
||||
## 接口文档增强配置
|
||||
knife4j:
|
||||
enable: true
|
||||
setting:
|
||||
language: zh_cn
|
||||
swagger-model-name: 实体类列表
|
||||
nextdoc4j:
|
||||
enabled: true
|
||||
|
@@ -62,7 +62,8 @@
|
||||
<crane4j.version>2.9.0</crane4j.version>
|
||||
|
||||
<!-- API Documentation Versions -->
|
||||
<knife4j.version>4.5.0</knife4j.version>
|
||||
<nextdoc4j.version>1.0.1</nextdoc4j.version>
|
||||
<swagger-annotations.version>2.2.36</swagger-annotations.version>
|
||||
|
||||
<!-- Tracing and Logging Versions -->
|
||||
<tlog.version>1.5.2</tlog.version>
|
||||
@@ -325,11 +326,11 @@
|
||||
<version>${crane4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Knife4j(前身是 swagger-bootstrap-ui,集 Swagger2 和 OpenAPI3 为一体的增强解决方案) -->
|
||||
<!--NextDoc4j (现代化 API 文档 UI 工具 全面替代 Swagger UI) -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-dependencies</artifactId>
|
||||
<version>${knife4j.version}</version>
|
||||
<groupId>top.nextdoc4j</groupId>
|
||||
<artifactId>nextdoc4j-bom</artifactId>
|
||||
<version>${nextdoc4j.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -422,6 +423,13 @@
|
||||
<version>${commons-compress.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Swagger 注解 -->
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations-jakarta</artifactId>
|
||||
<version>${swagger-annotations.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- ContiNew Starter 依赖 -->
|
||||
<dependency>
|
||||
<groupId>top.continew.starter</groupId>
|
||||
|
Reference in New Issue
Block a user