浏览代码

feat:更新接口文档依赖

yangliu 3 月之前
父节点
当前提交
fa9efc84d4

+ 246 - 77
hfln-framework-design-starter/knife4j-doc-spring-boot-starter/README.md

@@ -9,6 +9,7 @@
 - 支持 JWT 等安全认证配置
 - 支持接口分组管理
 - 支持中文界面
+- 支持配置文件管理服务列表
 
 ## 快速开始
 
@@ -57,109 +58,277 @@ lnxx:
 在网关的 `application.yml` 中添加如下配置:
 
 ```yaml
+lnxx:
+  knife4j:
+    doc:
+      # 基础配置
+      enable: false  # 禁用普通模式
+      title: "微服务 API 文档聚合"
+      description: "统一管理所有微服务的 API 文档"
+      version: "1.0.0"
+      
+      # 网关配置
+      gateway:
+        enable: true  # 启用网关模式
+        services:     # 配置需要聚合的服务列表
+          # 用户服务
+          - name: "用户服务"
+            url: "/v3/api-docs"  # 注意:这里不需要服务名称前缀
+            description: "用户注册、登录、权限管理等相关接口"
+          
+          # 订单服务
+          - name: "订单服务"
+            url: "/v3/api-docs"  # 注意:这里不需要服务名称前缀
+            description: "订单创建、查询、状态管理等相关接口"
+          
+          # 商品服务
+          - name: "商品服务"
+            url: "/v3/api-docs"  # 注意:这里不需要服务名称前缀
+            description: "商品信息、库存、分类管理等相关接口"
+          
+          # 支付服务
+          - name: "支付服务"
+            url: "/v3/api-docs"  # 注意:这里不需要服务名称前缀
+            description: "支付处理、退款、对账等相关接口"
+          
+          # 通知服务
+          - name: "通知服务"
+            url: "/v3/api-docs"  # 注意:这里不需要服务名称前缀
+            description: "短信、邮件、推送通知等相关接口"
+```
+
+#### 2.3 网关路由配置
+
+确保网关能正确路由到各个微服务,**关键是要配置 StripPrefix 过滤器**:
+
+```yaml
 spring:
   cloud:
     gateway:
       routes:
-        # 文档聚合路由
-        - id: knife4j
-          uri: http://localhost:${server.port}
+        # 用户服务路由
+        - id: user-service
+          uri: lb://user-service
+          predicates:
+            - Path=/user-service/**
+          filters:
+            - StripPrefix=1  # 移除路径前缀,将 /user-service/v3/api-docs 转换为 /v3/api-docs
+        
+        # 订单服务路由
+        - id: order-service
+          uri: lb://order-service
+          predicates:
+            - Path=/order-service/**
+          filters:
+            - StripPrefix=1  # 移除路径前缀
+        
+        # 商品服务路由
+        - id: product-service
+          uri: lb://product-service
+          predicates:
+            - Path=/product-service/**
+          filters:
+            - StripPrefix=1  # 移除路径前缀
+        
+        # 支付服务路由
+        - id: payment-service
+          uri: lb://payment-service
+          predicates:
+            - Path=/payment-service/**
+          filters:
+            - StripPrefix=1  # 移除路径前缀
+        
+        # 通知服务路由
+        - id: notification-service
+          uri: lb://notification-service
           predicates:
-            - Path=/v3/api-docs/**
+            - Path=/notification-service/**
           filters:
-            - RewritePath=/v3/api-docs/(?<path>.*), /$\{path}/v3/api-docs
+            - StripPrefix=1  # 移除路径前缀
+```
+
+### 3. 访问文档
+
+- 单个服务文档地址:`http://服务地址:端口/doc.html`
+- 网关聚合文档地址:`http://网关地址:端口/swagger-ui/index.html`
+- 网关 API 文档:`http://网关地址:端口/v3/api-docs/swagger-config`
+
+## 配置说明
+
+### 微服务配置
+
+| 配置项 | 说明 | 默认值 |
+|-------|------|--------|
+| lnxx.knife4j.doc.enable | 是否启用文档 | true |
+| lnxx.knife4j.doc.base-package | 接口扫描包路径 | - |
+| lnxx.knife4j.doc.title | 文档标题 | API 文档 |
+| lnxx.knife4j.doc.description | 文档描述 | - |
+| lnxx.knife4j.doc.version | 文档版本 | 1.0.0 |
+| lnxx.knife4j.doc.enable-security | 是否启用安全认证 | false |
+| lnxx.knife4j.doc.name | 联系方式姓名 | - |
+| lnxx.knife4j.doc.url | 联系方式网站 | - |
+| lnxx.knife4j.doc.email | 联系方式邮箱 | - |
+
+### 网关配置
+
+| 配置项 | 说明 | 默认值 |
+|-------|------|--------|
+| lnxx.knife4j.doc.gateway.enable | 是否启用网关模式 | false |
+| lnxx.knife4j.doc.gateway.services | 需要聚合的服务列表 | - |
+
+#### 服务配置参数
+
+| 配置项 | 说明 | 必填 |
+|-------|------|------|
+| name | 服务名称 | 是 |
+| url | 服务的 API 文档路径(不需要服务名称前缀) | 是 |
+| description | 服务描述 | 否 |
+
+## 路径配置说明
+
+### 网关路由工作原理
+
+1. **客户端请求**:`http://gateway:8080/user-service/v3/api-docs`
+2. **网关路由**:匹配到 `user-service` 路由
+3. **StripPrefix 过滤**:移除 `/user-service` 前缀
+4. **转发请求**:`http://user-service:8081/v3/api-docs`
+
+### 配置示例对比
 
+#### ❌ 错误配置(会导致 404)
+```yaml
+# 网关路由配置
+spring:
+  cloud:
+    gateway:
+      routes:
+        - id: user-service
+          uri: lb://user-service
+          predicates:
+            - Path=/user-service/**
+          filters:
+            - StripPrefix=1
+
+# Swagger 聚合配置
 lnxx:
   knife4j:
     doc:
-      enable: true
-      title: API 文档中心
-      description: 微服务接口文档
-      version: ${project.version}
-      enable-security: true
-
-# 配置需要聚合的微服务文档
-springdoc:
-  swagger-ui:
-    path: /doc.html
-  api-docs:
-    enabled: true
-  group-configs:
-    - group: 'default'
-      paths-to-match: '/**'
-      packages-to-scan: cn.hfln
+      gateway:
+        services:
+          - name: "用户服务"
+            url: "/user-service/v3/api-docs"  # ❌ 错误:包含服务名称
 ```
 
-#### 2.3 创建网关聚合配置类
+#### ✅ 正确配置
+```yaml
+# 网关路由配置
+spring:
+  cloud:
+    gateway:
+      routes:
+        - id: user-service
+          uri: lb://user-service
+          predicates:
+            - Path=/user-service/**
+          filters:
+            - StripPrefix=1
 
-```java
-@Configuration
-public class Knife4jGatewayConfig {
-    
-    @Bean
-    public RouterFunction<ServerResponse> apiRouter() {
-        return RouterFunctions.route()
-            .GET("/v3/api-docs/swagger-config", request ->
-                ServerResponse.ok().bodyValue(swaggerConfig()))
-            .build();
-    }
-
-    private Map<String, Object> swaggerConfig() {
-        Map<String, Object> config = new HashMap<>();
-        // 配置要聚合的微服务,key 为服务名,value 为文档地址
-        Map<String, String> services = new HashMap<>();
-        services.put("用户服务", "/user-service/v3/api-docs");
-        services.put("订单服务", "/order-service/v3/api-docs");
-        // ... 添加更多服务
-        
-        config.put("urls", services);
-        return config;
-    }
-}
+# Swagger 聚合配置
+lnxx:
+  knife4j:
+    doc:
+      gateway:
+        services:
+          - name: "用户服务"
+            url: "/v3/api-docs"  # ✅ 正确:不包含服务名称
 ```
 
-### 3. 访问文档
+## 动态配置
 
-- 单个服务文档地址:`http://服务地址:端口/doc.html`
-- 网关聚合文档地址:`http://网关地址:端口/doc.html`
+如果需要动态配置服务列表,可以:
 
-## 注意事项
+### 1. 从注册中心动态获取
 
-1. 网关聚合时,确保所有微服务的 OpenAPI 文档都已启用
-2. 如果启用了安全认证,需要在 Swagger UI 界面配置对应的认证信息
-3. 建议在生产环境关闭或限制文档访问
-4. 网关层建议配置文档访问的路由限制
+```java
+@Autowired
+private DiscoveryClient discoveryClient;
+
+private List<ServiceUrl> buildServiceUrls() {
+    List<ServiceUrl> serviceUrls = new ArrayList<>();
+    discoveryClient.getServices().forEach(serviceId -> {
+        serviceUrls.add(new ServiceUrl(
+            serviceId + "服务",
+            "/v3/api-docs",  // 注意:这里不需要服务名称前缀
+            serviceId + "相关接口"
+        ));
+    });
+    return serviceUrls;
+}
+```
 
-## 常见问题
+### 2. 从数据库配置
 
-1. 文档无法访问
-   - 检查服务是否正常启动
-   - 检查网关路由配置是否正确
-   - 检查安全认证配置
+```java
+@Autowired
+private ServiceConfigRepository repository;
+
+private List<ServiceUrl> buildServiceUrls() {
+    return repository.findAll().stream()
+        .map(config -> new ServiceUrl(
+            config.getName(),
+            "/v3/api-docs",  // 注意:这里不需要服务名称前缀
+            config.getDescription()
+        ))
+        .collect(Collectors.toList());
+}
+```
 
-2. 网关聚合失败
-   - 检查微服务的文档地址是否可访问
-   - 检查网关路由配置是否正确
-   - 检查网络连通性
+## 注意事项
 
-3. 认证失败
-   - 检查认证配置是否正确
-   - 确认 Token 格式是否正确
-   - 检查认证服务是否正常
+1. **网关模式与普通模式不能同时启用**
+2. **确保网关能正确路由到各个微服务**
+3. **各个微服务需要单独配置 Knife4j 文档功能**
+4. **网关中的配置主要用于聚合,不生成自己的 API 文档**
+5. **服务 URL 路径不需要包含服务名称前缀**
+6. **网关路由必须配置 StripPrefix 过滤器**
+7. **建议在生产环境关闭或限制文档访问**
 
-## 配置说明
+## 常见问题
 
-| 配置项 | 说明 | 默认值 |
-|-------|------|--------|
-| lnxx.knife4j.doc.enable | 是否启用文档 | true |
-| lnxx.knife4j.doc.base-package | 接口扫描包路径 | - |
-| lnxx.knife4j.doc.title | 文档标题 | API 文档 |
-| lnxx.knife4j.doc.description | 文档描述 | - |
-| lnxx.knife4j.doc.version | 文档版本 | 1.0.0 |
-| lnxx.knife4j.doc.enable-security | 是否启用安全认证 | false |
+### 1. 文档无法访问
+- 检查服务是否正常启动
+- 检查网关路由配置是否正确
+- 检查安全认证配置
+
+### 2. 网关聚合失败(404 错误)
+- 检查微服务的文档地址是否可访问
+- 检查网关路由配置是否正确
+- 检查网络连通性
+- 检查服务列表配置是否正确
+- **检查是否配置了 StripPrefix 过滤器**
+- **检查服务 URL 是否包含服务名称前缀**
+
+### 3. 认证失败
+- 检查认证配置是否正确
+- 确认 Token 格式是否正确
+- 检查认证服务是否正常
+
+### 4. 服务列表为空
+- 检查配置文件中的服务配置是否正确
+- 检查网关模式是否已启用
+- 检查服务 URL 路径是否正确
+
+### 5. 静态资源无法加载
+- 检查 webjars 依赖是否正确引入
+- 检查资源路径配置是否正确
 
 ## 版本说明
 
 - 基于 Knife4j 4.x
 - 支持 Spring Boot 2.6.x 及以上版本
-- 支持 Spring Cloud 2021.x 及以上版本
+- 支持 Spring Cloud 2021.x 及以上版本
+- 支持 OpenAPI 3 规范
+
+## 示例项目
+
+完整的配置示例可以参考 `application-gateway.yml` 文件。

+ 17 - 0
hfln-framework-design-starter/knife4j-doc-spring-boot-starter/pom.xml

@@ -23,7 +23,24 @@
             <version>${knife4j.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-openapi3-ui</artifactId>
+            <version>${knife4j.version}</version>
+        </dependency>
 
+        <!-- 测试依赖 -->
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-test</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>

+ 43 - 1
hfln-framework-design-starter/knife4j-doc-spring-boot-starter/src/main/java/cn/hfln/framework/knife4j/doc/base/BaseSwaggerConfig.java

@@ -7,7 +7,12 @@ import io.swagger.v3.oas.models.info.Info;
 import io.swagger.v3.oas.models.security.SecurityRequirement;
 import io.swagger.v3.oas.models.security.SecurityScheme;
 import io.swagger.v3.oas.models.servers.Server;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
 
 /**
  * @USER: YangLiu
@@ -15,6 +20,8 @@ import java.util.Collections;
  */
 public abstract class BaseSwaggerConfig {
 
+    private static final Logger log = LoggerFactory.getLogger(BaseSwaggerConfig.class);
+
     /**
      * 自定义Swagger配置
      */
@@ -24,6 +31,12 @@ public abstract class BaseSwaggerConfig {
      * 创建自定义的 OpenAPI 配置
      */
     protected OpenAPI createOpenAPI(SwaggerProperties swaggerProperties) {
+        log.info("=== BaseSwaggerConfig.createOpenAPI 开始 ===");
+        log.info("传入的 SwaggerProperties: title={}, servers={}, serverUrl={}", 
+                swaggerProperties.getTitle(), 
+                swaggerProperties.getServers() != null ? swaggerProperties.getServers().size() : "null",
+                swaggerProperties.getServerUrl());
+        
         OpenAPI openAPI = new OpenAPI()
                 .info(new Info()
                         .title(swaggerProperties.getTitle())
@@ -34,11 +47,27 @@ public abstract class BaseSwaggerConfig {
                                 .url(swaggerProperties.getContactUrl())
                                 .email(swaggerProperties.getContactEmail())));
 
-        if (swaggerProperties.getServerUrl() != null && !swaggerProperties.getServerUrl().isEmpty()) {
+        // 优先使用 servers 列表
+        if (swaggerProperties.getServers() != null && !swaggerProperties.getServers().isEmpty()) {
+            log.info("=== 使用 servers 列表设置 OpenAPI servers 字段 ===");
+            List<Server> serverList = new ArrayList<>();
+            for (SwaggerProperties.ServerConfig sc : swaggerProperties.getServers()) {
+                Server server = new Server().url(sc.getUrl()).description(sc.getDescription());
+                serverList.add(server);
+                log.info("  添加 server: url={}, description={}", sc.getUrl(), sc.getDescription());
+            }
+            openAPI.setServers(serverList);
+            log.info("servers 列表设置完成,共 {} 个", serverList.size());
+        } else if (swaggerProperties.getServerUrl() != null && !swaggerProperties.getServerUrl().isEmpty()) {
+            log.info("=== 使用单个 serverUrl 设置 OpenAPI servers 字段 ===");
+            log.info("  serverUrl: {}", swaggerProperties.getServerUrl());
             openAPI.setServers(Collections.singletonList(new Server().url(swaggerProperties.getServerUrl())));
+        } else {
+            log.warn("=== 未设置任何 servers 或 serverUrl,OpenAPI 将使用默认 servers ===");
         }
 
         if (swaggerProperties.isEnableSecurity()) {
+            log.info("启用安全认证配置");
             openAPI.addSecurityItem(new SecurityRequirement().addList("Authorization"))
                     .components(new Components()
                             .addSecuritySchemes("Authorization",
@@ -47,6 +76,19 @@ public abstract class BaseSwaggerConfig {
                                             .scheme("bearer")
                                             .bearerFormat("JWT")));
         }
+        
+        // 最终验证
+        if (openAPI.getServers() != null && !openAPI.getServers().isEmpty()) {
+            log.info("=== OpenAPI 最终 servers 字段验证 ===");
+            for (int i = 0; i < openAPI.getServers().size(); i++) {
+                Server server = openAPI.getServers().get(i);
+                log.info("  [{}] 最终 server: url={}, description={}", i, server.getUrl(), server.getDescription());
+            }
+        } else {
+            log.error("=== 最终 OpenAPI servers 字段为空!UI 将无法自动添加前缀 ===");
+        }
+        
+        log.info("=== BaseSwaggerConfig.createOpenAPI 完成 ===");
         return openAPI;
     }
 }

+ 11 - 0
hfln-framework-design-starter/knife4j-doc-spring-boot-starter/src/main/java/cn/hfln/framework/knife4j/doc/base/SwaggerProperties.java

@@ -3,6 +3,7 @@ package cn.hfln.framework.knife4j.doc.base;
 import lombok.Builder;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import java.util.List;
 
 /**
  * @USER: YangLiu
@@ -52,4 +53,14 @@ public class SwaggerProperties {
      * OpenAPI servers 字段,支持网关前缀
      */
     private String serverUrl;
+    /**
+     * OpenAPI servers 字段,支持多个
+     */
+    private List<ServerConfig> servers;
+
+    @Data
+    public static class ServerConfig {
+        private String url;
+        private String description;
+    }
 }

+ 65 - 45
hfln-framework-design-starter/knife4j-doc-spring-boot-starter/src/main/java/cn/hfln/framework/knife4j/doc/configure/DocAutoConfigure.java

@@ -4,22 +4,17 @@ import cn.hfln.framework.knife4j.doc.base.BaseSwaggerConfig;
 import cn.hfln.framework.knife4j.doc.base.SwaggerProperties;
 import cn.hfln.framework.knife4j.doc.properties.DocProperties;
 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.Components;
-import io.swagger.v3.oas.models.security.SecurityRequirement;
-import io.swagger.v3.oas.models.security.SecurityScheme;
-import io.swagger.v3.oas.models.servers.Server;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springdoc.core.SpringDocConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 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.Configuration;
 import org.springframework.context.annotation.Import;
 import org.springframework.context.annotation.Primary;
 
+import java.util.List;
 import java.util.stream.Collectors;
 
 /**
@@ -27,61 +22,61 @@ import java.util.stream.Collectors;
  * knife4j 接口文档配置
  */
 @Configuration
-@EnableConfigurationProperties(DocProperties.class)
 @ConditionalOnProperty(value = "lnxx.knife4j.doc.enable", havingValue = "true", matchIfMissing = true)
 @Import(SpringDocConfiguration.class)
 public class DocAutoConfigure extends BaseSwaggerConfig {
 
+    private static final Logger log = LoggerFactory.getLogger(DocAutoConfigure.class);
+
     private final DocProperties properties;
 
     public DocAutoConfigure(DocProperties properties) {
         this.properties = properties;
+        log.info("=== Knife4j DocAutoConfigure 初始化 ===");
+        log.info("DocProperties: {}", properties);
+        if (properties.getServers() != null && !properties.getServers().isEmpty()) {
+            log.info("配置的 servers 字段:");
+            for (int i = 0; i < properties.getServers().size(); i++) {
+                DocProperties.ServerConfig server = properties.getServers().get(i);
+                log.info("  [{}] url: {}, description: {}", i, server.getUrl(), server.getDescription());
+            }
+        } else {
+            log.warn("未配置 servers 字段,serverUrl: {}", properties.getServerUrl());
+        }
     }
 
     @Bean
     @Primary
     @ConditionalOnMissingBean
+    @ConditionalOnProperty(value = "lnxx.knife4j.doc.gateway.enable", havingValue = "false", matchIfMissing = true)
     public OpenAPI customOpenAPI() {
-        OpenAPI openAPI = new OpenAPI()
-                .info(new Info()
-                        .title(properties.getTitle())
-                        .description(String.format("<div style='font-size:%spx;color:%s;'>%s</div>",
-                                properties.getDescriptionFontSize(), properties.getDescriptionColor(), properties.getDescription()))
-                        .version(properties.getVersion())
-                        .contact(new Contact()
-                                .name(properties.getName())
-                                .url(properties.getUrl())
-                                .email(properties.getEmail()))
-                        .license(new License()
-                                .name(properties.getLicense())
-                                .url(properties.getLicenseUrl()))
-                        .termsOfService(properties.getTermsOfServiceUrl()));
-
-        // 只使用 yml 配置的 servers 字段,不设置默认 server
-        if (properties.getServers() != null && !properties.getServers().isEmpty()) {
-            openAPI.setServers(properties.getServers().stream()
-                    .map(cfg -> new Server().url(cfg.getUrl()).description(cfg.getDescription()))
-                    .collect(Collectors.toList()));
+        log.info("=== 开始创建 OpenAPI 实例 ===");
+        SwaggerProperties swaggerProps = swaggerProperties();
+        log.info("SwaggerProperties servers: {}", swaggerProps.getServers());
+        log.info("SwaggerProperties serverUrl: {}", swaggerProps.getServerUrl());
+        
+        OpenAPI openAPI = createOpenAPI(swaggerProps);
+        
+        if (openAPI.getServers() != null && !openAPI.getServers().isEmpty()) {
+            log.info("=== OpenAPI servers 字段设置成功 ===");
+            for (int i = 0; i < openAPI.getServers().size(); i++) {
+                log.info("  [{}] server url: {}, description: {}", 
+                    i, 
+                    openAPI.getServers().get(i).getUrl(), 
+                    openAPI.getServers().get(i).getDescription());
+            }
+        } else {
+            log.error("=== OpenAPI servers 字段为空!这将导致 UI 请求不带前缀 ===");
         }
-
-        if (properties.isEnableSecurity()) {
-            openAPI.addSecurityItem(new SecurityRequirement().addList("Bearer"))
-                    .components(new Components()
-                            .addSecuritySchemes("Bearer",
-                                    new SecurityScheme()
-                                            .type(SecurityScheme.Type.HTTP)
-                                            .scheme("bearer")
-                                            .bearerFormat("JWT")
-                                            .in(SecurityScheme.In.HEADER)
-                                            .name("Authorization")));
-        }
-
+        
         return openAPI;
     }
 
     @Override
     public SwaggerProperties swaggerProperties() {
-        return SwaggerProperties.builder()
+        log.info("=== 构建 SwaggerProperties ===");
+        
+        SwaggerProperties.SwaggerPropertiesBuilder builder = SwaggerProperties.builder()
                 .apiBasePackage(properties.getBasePackage())
                 .title(properties.getTitle())
                 .description(String.format("<div style='font-size:%spx;color:%s;'>%s</div>",
@@ -92,7 +87,32 @@ public class DocAutoConfigure extends BaseSwaggerConfig {
                 .contactEmail(properties.getEmail())
                 .enableSecurity(properties.isEnableSecurity())
                 .group("default")
-                .serverUrl(properties.getServerUrl())
-                .build();
+                .serverUrl(properties.getServerUrl());
+        
+        // 转换 servers 字段
+        if (properties.getServers() != null && !properties.getServers().isEmpty()) {
+            log.info("正在转换 DocProperties.ServerConfig -> SwaggerProperties.ServerConfig");
+            List<SwaggerProperties.ServerConfig> convertedServers = properties.getServers().stream()
+                    .map(s -> {
+                        SwaggerProperties.ServerConfig sc = new SwaggerProperties.ServerConfig();
+                        sc.setUrl(s.getUrl());
+                        sc.setDescription(s.getDescription());
+                        log.info("  转换: {} -> {}", s.getUrl(), sc.getUrl());
+                        return sc;
+                    })
+                    .collect(Collectors.toList());
+            builder.servers(convertedServers);
+            log.info("servers 字段转换完成,共 {} 个", convertedServers.size());
+        } else {
+            log.warn("properties.getServers() 为空或 null");
+            builder.servers(null);
+        }
+        
+        SwaggerProperties result = builder.build();
+        log.info("=== SwaggerProperties 构建完成 ===");
+        log.info("最终 SwaggerProperties.servers: {}", 
+            result.getServers() != null ? result.getServers().size() + " 个" : "null");
+        
+        return result;
     }
 }

+ 37 - 1
hfln-framework-design-starter/knife4j-doc-spring-boot-starter/src/main/java/cn/hfln/framework/knife4j/doc/properties/DocProperties.java

@@ -21,7 +21,6 @@ public class DocProperties {
      */
     private String basePackage;
 
-
     /**
      * 是否要启用登录认证
      */
@@ -82,12 +81,48 @@ public class DocProperties {
      */
     private List<ServerConfig> servers;
 
+    /**
+     * 网关配置
+     */
+    private Gateway gateway = new Gateway();
+
     @Data
     public static class ServerConfig {
         private String url;
         private String description;
     }
 
+    @Data
+    public static class Gateway {
+        /**
+         * 是否启用网关模式
+         */
+        private Boolean enable = false;
+        
+        /**
+         * 需要聚合的服务列表
+         */
+        private List<ServiceConfig> services;
+    }
+
+    @Data
+    public static class ServiceConfig {
+        /**
+         * 服务名称
+         */
+        private String name;
+        
+        /**
+         * 服务URL路径
+         */
+        private String url;
+        
+        /**
+         * 服务描述
+         */
+        private String description;
+    }
+
     @Override
     public String toString() {
         return "DocProperties{" +
@@ -105,6 +140,7 @@ public class DocProperties {
                 ", licenseUrl='" + licenseUrl + '\'' +
                 ", version='" + version + '\'' +
                 ", serverUrl='" + serverUrl + '\'' +
+                ", gateway=" + gateway +
                 '}';
     }
 }

+ 3 - 1
hfln-framework-design-starter/knife4j-doc-spring-boot-starter/src/main/resources/META-INF/spring.factories

@@ -1,3 +1,5 @@
 # Auto Configure
 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-cn.hfln.framework.knife4j.doc.configure.DocAutoConfigure
+cn.hfln.framework.knife4j.doc.configure.PropertiesAutoConfigure,\
+cn.hfln.framework.knife4j.doc.configure.DocAutoConfigure,\
+cn.hfln.framework.knife4j.doc.gateway.GatewayAutoConfigure

+ 1 - 11
pom.xml

@@ -40,7 +40,7 @@
         <sentinel.version>1.8.5</sentinel.version>
         <nacos.version>2.1.0</nacos.version>
         <rocketmq.version>4.9.4</rocketmq.version>
-        <knife4j.version>4.1.0</knife4j.version>
+        <knife4j.version>4.4.0</knife4j.version>
         <mybatis-plus.version>3.5.1</mybatis-plus.version>
         <fastjson.version>2.0.41</fastjson.version>
         <transmittable.version>2.14.2</transmittable.version>
@@ -205,16 +205,6 @@
                 <version>${alidysms.version}</version>
             </dependency>
 
-            <dependency>
-                <groupId>com.github.xiaoymin</groupId>
-                <artifactId>knife4j-spring-boot-starter</artifactId>
-                <version>${knife4j.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>com.github.xiaoymin</groupId>
-                <artifactId>knife4j-micro-spring-boot-starter</artifactId>
-                <version>${knife4j.version}</version>
-            </dependency>
 
 
             <dependency>