Commit e91fc70f87b8f02f6013ee5170635a2e7f1eb3d8

Authored by YunaiV
1 parent 236d9536

feat:【framework 框架】兼容 knife4j 4.5.0 版本,升级 spring boot 到 3.5.4

@@ -42,7 +42,7 @@ @@ -42,7 +42,7 @@
42 <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version> 42 <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
43 <!-- maven-surefire-plugin 暂时无法通过 bom 的依赖读取(兼容老版本 IDEA 2024 及以前版本) --> 43 <!-- maven-surefire-plugin 暂时无法通过 bom 的依赖读取(兼容老版本 IDEA 2024 及以前版本) -->
44 <lombok.version>1.18.38</lombok.version> 44 <lombok.version>1.18.38</lombok.version>
45 - <spring.boot.version>3.4.5</spring.boot.version> 45 + <spring.boot.version>3.5.4</spring.boot.version>
46 <mapstruct.version>1.6.3</mapstruct.version> 46 <mapstruct.version>1.6.3</mapstruct.version>
47 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 47 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
48 </properties> 48 </properties>
yudao-dependencies/pom.xml
@@ -17,10 +17,10 @@ @@ -17,10 +17,10 @@
17 <revision>2025.08-SNAPSHOT</revision> 17 <revision>2025.08-SNAPSHOT</revision>
18 <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version> 18 <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
19 <!-- 统一依赖管理 --> 19 <!-- 统一依赖管理 -->
20 - <spring.boot.version>3.4.5</spring.boot.version> 20 + <spring.boot.version>3.5.4</spring.boot.version>
21 <!-- Web 相关 --> 21 <!-- Web 相关 -->
22 - <springdoc.version>2.8.3</springdoc.version>  
23 - <knife4j.version>4.6.0</knife4j.version> 22 + <springdoc.version>2.8.9</springdoc.version>
  23 + <knife4j.version>4.5.0</knife4j.version>
24 <!-- DB 相关 --> 24 <!-- DB 相关 -->
25 <druid.version>1.2.24</druid.version> 25 <druid.version>1.2.24</druid.version>
26 <mybatis.version>3.5.19</mybatis.version> 26 <mybatis.version>3.5.19</mybatis.version>
@@ -39,7 +39,7 @@ @@ -39,7 +39,7 @@
39 <lock4j.version>2.2.7</lock4j.version> 39 <lock4j.version>2.2.7</lock4j.version>
40 <!-- 监控相关 --> 40 <!-- 监控相关 -->
41 <skywalking.version>9.0.0</skywalking.version> 41 <skywalking.version>9.0.0</skywalking.version>
42 - <spring-boot-admin.version>3.4.5</spring-boot-admin.version> 42 + <spring-boot-admin.version>3.5.2</spring-boot-admin.version>
43 <opentracing.version>0.33.0</opentracing.version> 43 <opentracing.version>0.33.0</opentracing.version>
44 <!-- Test 测试相关 --> 44 <!-- Test 测试相关 -->
45 <podam.version>8.0.2.RELEASE</podam.version> 45 <podam.version>8.0.2.RELEASE</podam.version>
@@ -151,13 +151,19 @@ @@ -151,13 +151,19 @@
151 </dependency> 151 </dependency>
152 152
153 <dependency> 153 <dependency>
154 - <groupId>com.github.xingfudeshi</groupId> <!-- TODO 芋艿:https://github.com/xiaoymin/knife4j/issues/874 --> 154 + <groupId>com.github.xiaoymin</groupId>
155 <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> 155 <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
156 <version>${knife4j.version}</version> 156 <version>${knife4j.version}</version>
  157 + <exclusions>
  158 + <exclusion>
  159 + <groupId>org.springdoc</groupId>
  160 + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
  161 + </exclusion>
  162 + </exclusions>
157 </dependency> 163 </dependency>
158 <dependency> 164 <dependency>
159 <groupId>org.springdoc</groupId> 165 <groupId>org.springdoc</groupId>
160 - <artifactId>springdoc-openapi-starter-webmvc-api</artifactId> 166 + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
161 <version>${springdoc.version}</version> 167 <version>${springdoc.version}</version>
162 </dependency> 168 </dependency>
163 169
yudao-framework/yudao-common/pom.xml
@@ -60,7 +60,7 @@ @@ -60,7 +60,7 @@
60 60
61 <dependency> 61 <dependency>
62 <groupId>org.springdoc</groupId> 62 <groupId>org.springdoc</groupId>
63 - <artifactId>springdoc-openapi-starter-webmvc-api</artifactId> 63 + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
64 <scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 --> 64 <scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 -->
65 </dependency> 65 </dependency>
66 66
yudao-framework/yudao-spring-boot-starter-web/pom.xml
@@ -39,12 +39,12 @@ @@ -39,12 +39,12 @@
39 </dependency> 39 </dependency>
40 40
41 <dependency> 41 <dependency>
42 - <groupId>com.github.xingfudeshi</groupId> 42 + <groupId>com.github.xiaoymin</groupId>
43 <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> 43 <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
44 </dependency> 44 </dependency>
45 <dependency> 45 <dependency>
46 <groupId>org.springdoc</groupId> 46 <groupId>org.springdoc</groupId>
47 - <artifactId>springdoc-openapi-starter-webmvc-api</artifactId> 47 + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
48 </dependency> 48 </dependency>
49 49
50 <dependency> 50 <dependency>
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/Knife4jOpenApiCustomizer.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.swagger.config;
  2 +
  3 +import com.github.xiaoymin.knife4j.annotations.ApiSupport;
  4 +import com.github.xiaoymin.knife4j.core.conf.ExtensionsConstants;
  5 +import com.github.xiaoymin.knife4j.core.conf.GlobalConstants;
  6 +import com.github.xiaoymin.knife4j.spring.configuration.Knife4jProperties;
  7 +import com.github.xiaoymin.knife4j.spring.configuration.Knife4jSetting;
  8 +import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
  9 +import io.swagger.v3.oas.annotations.tags.Tag;
  10 +import io.swagger.v3.oas.models.OpenAPI;
  11 +import lombok.extern.slf4j.Slf4j;
  12 +import org.apache.commons.lang3.ArrayUtils;
  13 +import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
  14 +import org.springdoc.core.properties.SpringDocConfigProperties;
  15 +import org.springframework.beans.factory.config.BeanDefinition;
  16 +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
  17 +import org.springframework.context.annotation.Configuration;
  18 +import org.springframework.context.annotation.Primary;
  19 +import org.springframework.core.type.filter.AnnotationTypeFilter;
  20 +import org.springframework.util.CollectionUtils;
  21 +import org.springframework.web.bind.annotation.RestController;
  22 +
  23 +import java.lang.annotation.Annotation;
  24 +import java.util.*;
  25 +import java.util.stream.Collectors;
  26 +
  27 +/**
  28 + * 增强扩展属性支持
  29 + *
  30 + * 参考 <a href="https://github.com/xiaoymin/knife4j/issues/913">Spring Boot 3.4 以上版本 /v3/api-docs 解决接口报错,依赖修复</a>
  31 + *
  32 + * @since 4.1.0
  33 + * @author <a href="xiaoymin@foxmail.com">xiaoymin@foxmail.com</a>
  34 + * 2022/12/11 22:40
  35 + */
  36 +@Primary
  37 +@Configuration
  38 +@Slf4j
  39 +public class Knife4jOpenApiCustomizer extends com.github.xiaoymin.knife4j.spring.extension.Knife4jOpenApiCustomizer
  40 + implements GlobalOpenApiCustomizer {
  41 +
  42 + final Knife4jProperties knife4jProperties;
  43 + final SpringDocConfigProperties properties;
  44 +
  45 + public Knife4jOpenApiCustomizer(Knife4jProperties knife4jProperties, SpringDocConfigProperties properties) {
  46 + super(knife4jProperties,properties);
  47 + this.knife4jProperties = knife4jProperties;
  48 + this.properties = properties;
  49 + }
  50 +
  51 + @Override
  52 + public void customise(OpenAPI openApi) {
  53 + if (knife4jProperties.isEnable()) {
  54 + Knife4jSetting setting = knife4jProperties.getSetting();
  55 + OpenApiExtensionResolver openApiExtensionResolver = new OpenApiExtensionResolver(setting, knife4jProperties.getDocuments());
  56 + // 解析初始化
  57 + openApiExtensionResolver.start();
  58 + Map<String, Object> objectMap = new HashMap<>();
  59 + objectMap.put(GlobalConstants.EXTENSION_OPEN_SETTING_NAME, setting);
  60 + objectMap.put(GlobalConstants.EXTENSION_OPEN_MARKDOWN_NAME, openApiExtensionResolver.getMarkdownFiles());
  61 + openApi.addExtension(GlobalConstants.EXTENSION_OPEN_API_NAME, objectMap);
  62 + addOrderExtension(openApi);
  63 + }
  64 + }
  65 +
  66 + /**
  67 + * 往 OpenAPI 内 tags 字段添加 x-order 属性
  68 + *
  69 + * @param openApi openApi
  70 + */
  71 + private void addOrderExtension(OpenAPI openApi) {
  72 + if (CollectionUtils.isEmpty(properties.getGroupConfigs())) {
  73 + return;
  74 + }
  75 + // 获取包扫描路径
  76 + Set<String> packagesToScan =
  77 + properties.getGroupConfigs().stream()
  78 + .map(SpringDocConfigProperties.GroupConfig::getPackagesToScan)
  79 + .filter(toScan -> !CollectionUtils.isEmpty(toScan))
  80 + .flatMap(List::stream)
  81 + .collect(Collectors.toSet());
  82 + if (CollectionUtils.isEmpty(packagesToScan)) {
  83 + return;
  84 + }
  85 + // 扫描包下被 ApiSupport 注解的 RestController Class
  86 + Set<Class<?>> classes = packagesToScan.stream()
  87 + .map(packageToScan -> scanPackageByAnnotation(packageToScan, RestController.class))
  88 + .flatMap(Set::stream)
  89 + .filter(clazz -> clazz.isAnnotationPresent(ApiSupport.class))
  90 + .collect(Collectors.toSet());
  91 + if (!CollectionUtils.isEmpty(classes)) {
  92 + // ApiSupport oder 值存入 tagSortMap<Tag.name,ApiSupport.order>
  93 + Map<String, Integer> tagOrderMap = new HashMap<>();
  94 + classes.forEach(clazz -> {
  95 + Tag tag = getTag(clazz);
  96 + if (Objects.nonNull(tag)) {
  97 + ApiSupport apiSupport = clazz.getAnnotation(ApiSupport.class);
  98 + tagOrderMap.putIfAbsent(tag.name(), apiSupport.order());
  99 + }
  100 + });
  101 + // 往 openApi tags 字段添加 x-order 增强属性
  102 + if (openApi.getTags() != null) {
  103 + openApi.getTags().forEach(tag -> {
  104 + if (tagOrderMap.containsKey(tag.getName())) {
  105 + tag.addExtension(ExtensionsConstants.EXTENSION_ORDER, tagOrderMap.get(tag.getName()));
  106 + }
  107 + });
  108 + }
  109 + }
  110 + }
  111 +
  112 + private Tag getTag(Class<?> clazz) {
  113 + // 从类上获取
  114 + Tag tag = clazz.getAnnotation(Tag.class);
  115 + if (Objects.isNull(tag)) {
  116 + // 从接口上获取
  117 + Class<?>[] interfaces = clazz.getInterfaces();
  118 + if (ArrayUtils.isNotEmpty(interfaces)) {
  119 + for (Class<?> interfaceClazz : interfaces) {
  120 + Tag anno = interfaceClazz.getAnnotation(Tag.class);
  121 + if (Objects.nonNull(anno)) {
  122 + tag = anno;
  123 + break;
  124 + }
  125 + }
  126 + }
  127 + }
  128 + return tag;
  129 + }
  130 +
  131 + private Set<Class<?>> scanPackageByAnnotation(String packageName, final Class<? extends Annotation> annotationClass) {
  132 + ClassPathScanningCandidateComponentProvider scanner =
  133 + new ClassPathScanningCandidateComponentProvider(false);
  134 + scanner.addIncludeFilter(new AnnotationTypeFilter(annotationClass));
  135 + Set<Class<?>> classes = new HashSet<>();
  136 + for (BeanDefinition beanDefinition : scanner.findCandidateComponents(packageName)) {
  137 + try {
  138 + Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
  139 + classes.add(clazz);
  140 + } catch (ClassNotFoundException ignore) {
  141 + }
  142 + }
  143 + return classes;
  144 + }
  145 +
  146 +}
0 \ No newline at end of file 147 \ No newline at end of file
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java
@@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
23 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 23 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
24 import org.springframework.boot.context.properties.EnableConfigurationProperties; 24 import org.springframework.boot.context.properties.EnableConfigurationProperties;
25 import org.springframework.context.annotation.Bean; 25 import org.springframework.context.annotation.Bean;
  26 +import org.springframework.context.annotation.Import;
26 import org.springframework.context.annotation.Primary; 27 import org.springframework.context.annotation.Primary;
27 import org.springframework.http.HttpHeaders; 28 import org.springframework.http.HttpHeaders;
28 29
@@ -46,6 +47,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_ @@ -46,6 +47,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_
46 @ConditionalOnClass({OpenAPI.class}) 47 @ConditionalOnClass({OpenAPI.class})
47 @EnableConfigurationProperties(SwaggerProperties.class) 48 @EnableConfigurationProperties(SwaggerProperties.class)
48 @ConditionalOnProperty(prefix = "springdoc.api-docs", name = "enabled", havingValue = "true", matchIfMissing = true) // 设置为 false 时,禁用 49 @ConditionalOnProperty(prefix = "springdoc.api-docs", name = "enabled", havingValue = "true", matchIfMissing = true) // 设置为 false 时,禁用
  50 +@Import(Knife4jOpenApiCustomizer.class)
49 public class YudaoSwaggerAutoConfiguration { 51 public class YudaoSwaggerAutoConfiguration {
50 52
51 // ========== 全局 OpenAPI 配置 ========== 53 // ========== 全局 OpenAPI 配置 ==========
yudao-server/src/main/resources/application.yaml
@@ -48,7 +48,7 @@ springdoc: @@ -48,7 +48,7 @@ springdoc:
48 default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 48 default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档
49 49
50 knife4j: 50 knife4j:
51 - enable: false # TODO 芋艿:需要关闭增强,具体原因见:https://github.com/xiaoymin/knife4j/issues/874 51 + enable: true
52 setting: 52 setting:
53 language: zh_cn 53 language: zh_cn
54 54