Просмотр исходного кода

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

YunaiV 1 год назад
Родитель
Сommit
e91fc70f87

+ 1 - 1
pom.xml

@@ -42,7 +42,7 @@
         <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
         <!-- maven-surefire-plugin 暂时无法通过 bom 的依赖读取(兼容老版本 IDEA 2024 及以前版本) -->
         <lombok.version>1.18.38</lombok.version>
-        <spring.boot.version>3.4.5</spring.boot.version>
+        <spring.boot.version>3.5.4</spring.boot.version>
         <mapstruct.version>1.6.3</mapstruct.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>

+ 12 - 6
yudao-dependencies/pom.xml

@@ -17,10 +17,10 @@
         <revision>2025.08-SNAPSHOT</revision>
         <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
         <!-- 统一依赖管理 -->
-        <spring.boot.version>3.4.5</spring.boot.version>
+        <spring.boot.version>3.5.4</spring.boot.version>
         <!-- Web 相关 -->
-        <springdoc.version>2.8.3</springdoc.version>
-        <knife4j.version>4.6.0</knife4j.version>
+        <springdoc.version>2.8.9</springdoc.version>
+        <knife4j.version>4.5.0</knife4j.version>
         <!-- DB 相关 -->
         <druid.version>1.2.24</druid.version>
         <mybatis.version>3.5.19</mybatis.version>
@@ -39,7 +39,7 @@
         <lock4j.version>2.2.7</lock4j.version>
         <!-- 监控相关 -->
         <skywalking.version>9.0.0</skywalking.version>
-        <spring-boot-admin.version>3.4.5</spring-boot-admin.version>
+        <spring-boot-admin.version>3.5.2</spring-boot-admin.version>
         <opentracing.version>0.33.0</opentracing.version>
         <!-- Test 测试相关 -->
         <podam.version>8.0.2.RELEASE</podam.version>
@@ -151,13 +151,19 @@
             </dependency>
 
             <dependency>
-                <groupId>com.github.xingfudeshi</groupId> <!-- TODO 芋艿:https://github.com/xiaoymin/knife4j/issues/874 -->
+                <groupId>com.github.xiaoymin</groupId>
                 <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
                 <version>${knife4j.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.springdoc</groupId>
+                        <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
+                    </exclusion>
+                </exclusions>
             </dependency>
             <dependency>
                 <groupId>org.springdoc</groupId>
-                <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
+                <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
                 <version>${springdoc.version}</version>
             </dependency>
 

+ 1 - 1
yudao-framework/yudao-common/pom.xml

@@ -60,7 +60,7 @@
 
         <dependency>
             <groupId>org.springdoc</groupId>
-            <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
+            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
             <scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 -->
         </dependency>
 

+ 2 - 2
yudao-framework/yudao-spring-boot-starter-web/pom.xml

@@ -39,12 +39,12 @@
         </dependency>
 
         <dependency>
-            <groupId>com.github.xingfudeshi</groupId>
+            <groupId>com.github.xiaoymin</groupId>
             <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springdoc</groupId>
-            <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
+            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
         </dependency>
 
         <dependency>

+ 146 - 0
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/Knife4jOpenApiCustomizer.java

@@ -0,0 +1,146 @@
+package cn.iocoder.yudao.framework.swagger.config;
+
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import com.github.xiaoymin.knife4j.core.conf.ExtensionsConstants;
+import com.github.xiaoymin.knife4j.core.conf.GlobalConstants;
+import com.github.xiaoymin.knife4j.spring.configuration.Knife4jProperties;
+import com.github.xiaoymin.knife4j.spring.configuration.Knife4jSetting;
+import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.models.OpenAPI;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ArrayUtils;
+import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
+import org.springdoc.core.properties.SpringDocConfigProperties;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.type.filter.AnnotationTypeFilter;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.lang.annotation.Annotation;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 增强扩展属性支持
+ *
+ * 参考 <a href="https://github.com/xiaoymin/knife4j/issues/913">Spring Boot 3.4 以上版本 /v3/api-docs 解决接口报错,依赖修复</a>
+ *
+ * @since 4.1.0
+ * @author <a href="xiaoymin@foxmail.com">xiaoymin@foxmail.com</a>
+ * 2022/12/11 22:40
+ */
+@Primary
+@Configuration
+@Slf4j
+public class Knife4jOpenApiCustomizer extends com.github.xiaoymin.knife4j.spring.extension.Knife4jOpenApiCustomizer
+        implements GlobalOpenApiCustomizer {
+
+    final Knife4jProperties knife4jProperties;
+    final SpringDocConfigProperties properties;
+
+    public Knife4jOpenApiCustomizer(Knife4jProperties knife4jProperties, SpringDocConfigProperties properties) {
+        super(knife4jProperties,properties);
+        this.knife4jProperties = knife4jProperties;
+        this.properties = properties;
+    }
+
+    @Override
+    public void customise(OpenAPI openApi) {
+        if (knife4jProperties.isEnable()) {
+            Knife4jSetting setting = knife4jProperties.getSetting();
+            OpenApiExtensionResolver openApiExtensionResolver = new OpenApiExtensionResolver(setting, knife4jProperties.getDocuments());
+            // 解析初始化
+            openApiExtensionResolver.start();
+            Map<String, Object> objectMap = new HashMap<>();
+            objectMap.put(GlobalConstants.EXTENSION_OPEN_SETTING_NAME, setting);
+            objectMap.put(GlobalConstants.EXTENSION_OPEN_MARKDOWN_NAME, openApiExtensionResolver.getMarkdownFiles());
+            openApi.addExtension(GlobalConstants.EXTENSION_OPEN_API_NAME, objectMap);
+            addOrderExtension(openApi);
+        }
+    }
+
+    /**
+     * 往 OpenAPI 内 tags 字段添加 x-order 属性
+     *
+     * @param openApi openApi
+     */
+    private void addOrderExtension(OpenAPI openApi) {
+        if (CollectionUtils.isEmpty(properties.getGroupConfigs())) {
+            return;
+        }
+        // 获取包扫描路径
+        Set<String> packagesToScan =
+                properties.getGroupConfigs().stream()
+                        .map(SpringDocConfigProperties.GroupConfig::getPackagesToScan)
+                        .filter(toScan -> !CollectionUtils.isEmpty(toScan))
+                        .flatMap(List::stream)
+                        .collect(Collectors.toSet());
+        if (CollectionUtils.isEmpty(packagesToScan)) {
+            return;
+        }
+        // 扫描包下被 ApiSupport 注解的 RestController Class
+        Set<Class<?>> classes = packagesToScan.stream()
+                .map(packageToScan -> scanPackageByAnnotation(packageToScan, RestController.class))
+                .flatMap(Set::stream)
+                .filter(clazz -> clazz.isAnnotationPresent(ApiSupport.class))
+                .collect(Collectors.toSet());
+        if (!CollectionUtils.isEmpty(classes)) {
+            // ApiSupport oder 值存入 tagSortMap<Tag.name,ApiSupport.order>
+            Map<String, Integer> tagOrderMap = new HashMap<>();
+            classes.forEach(clazz -> {
+                Tag tag = getTag(clazz);
+                if (Objects.nonNull(tag)) {
+                    ApiSupport apiSupport = clazz.getAnnotation(ApiSupport.class);
+                    tagOrderMap.putIfAbsent(tag.name(), apiSupport.order());
+                }
+            });
+            // 往 openApi tags 字段添加 x-order 增强属性
+            if (openApi.getTags() != null) {
+                openApi.getTags().forEach(tag -> {
+                    if (tagOrderMap.containsKey(tag.getName())) {
+                        tag.addExtension(ExtensionsConstants.EXTENSION_ORDER, tagOrderMap.get(tag.getName()));
+                    }
+                });
+            }
+        }
+    }
+
+    private Tag getTag(Class<?> clazz) {
+        // 从类上获取
+        Tag tag = clazz.getAnnotation(Tag.class);
+        if (Objects.isNull(tag)) {
+            // 从接口上获取
+            Class<?>[] interfaces = clazz.getInterfaces();
+            if (ArrayUtils.isNotEmpty(interfaces)) {
+                for (Class<?> interfaceClazz : interfaces) {
+                    Tag anno = interfaceClazz.getAnnotation(Tag.class);
+                    if (Objects.nonNull(anno)) {
+                        tag = anno;
+                        break;
+                    }
+                }
+            }
+        }
+        return tag;
+    }
+
+    private Set<Class<?>> scanPackageByAnnotation(String packageName, final Class<? extends Annotation> annotationClass) {
+        ClassPathScanningCandidateComponentProvider scanner =
+                new ClassPathScanningCandidateComponentProvider(false);
+        scanner.addIncludeFilter(new AnnotationTypeFilter(annotationClass));
+        Set<Class<?>> classes = new HashSet<>();
+        for (BeanDefinition beanDefinition : scanner.findCandidateComponents(packageName)) {
+            try {
+                Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
+                classes.add(clazz);
+            } catch (ClassNotFoundException ignore) {
+            }
+        }
+        return classes;
+    }
+
+}

+ 2 - 0
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;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
 import org.springframework.context.annotation.Primary;
 import org.springframework.http.HttpHeaders;
 
@@ -46,6 +47,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_
 @ConditionalOnClass({OpenAPI.class})
 @EnableConfigurationProperties(SwaggerProperties.class)
 @ConditionalOnProperty(prefix = "springdoc.api-docs", name = "enabled", havingValue = "true", matchIfMissing = true) // 设置为 false 时,禁用
+@Import(Knife4jOpenApiCustomizer.class)
 public class YudaoSwaggerAutoConfiguration {
 
     // ========== 全局 OpenAPI 配置 ==========

+ 1 - 1
yudao-server/src/main/resources/application.yaml

@@ -48,7 +48,7 @@ springdoc:
   default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档
 
 knife4j:
-  enable: false # TODO 芋艿:需要关闭增强,具体原因见:https://github.com/xiaoymin/knife4j/issues/874
+  enable: true
   setting:
     language: zh_cn