Pārlūkot izejas kodu

Merge remote-tracking branch 'origin/master'

YPZ 3 nedēļas atpakaļ
vecāks
revīzija
ae25c4c93e

+ 97 - 0
java/storlead-api/src/main/java/com/springfox/documentation/schema/Example.java

@@ -0,0 +1,97 @@
+package com.springfox.documentation.schema;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import springfox.documentation.service.VendorExtension;
+
+@JsonInclude(Include.NON_EMPTY)
+public class Example {
+    private final String id;
+    private final String summary;
+    private final String description;
+    private final Object value;
+    private final String externalValue;
+    private final String mediaType;
+    private final List<VendorExtension> extensions = new ArrayList();
+
+    /** @deprecated */
+    @Deprecated
+    public Example(Object value) {
+        this.value = value;
+        this.mediaType = null;
+        this.externalValue = null;
+        this.id = null;
+        this.summary = null;
+        this.description = null;
+    }
+
+    /** @deprecated */
+    @Deprecated
+    public Example(String mediaType, Object value) {
+        this.mediaType = mediaType;
+        this.value = value;
+        this.externalValue = null;
+        this.id = null;
+        this.summary = null;
+        this.description = null;
+    }
+
+    public Example(String id, String summary, String description, Object value, String externalValue, String mediaType) {
+        this.id = id;
+        this.summary = summary;
+        this.description = description;
+        this.value = value;
+        this.externalValue = externalValue;
+        this.mediaType = mediaType;
+    }
+
+    public String getId() {
+        return this.id;
+    }
+
+    public String getSummary() {
+        return this.summary;
+    }
+
+    public String getDescription() {
+        return this.description;
+    }
+
+    public String getExternalValue() {
+        return this.externalValue;
+    }
+
+    public List<VendorExtension> getExtensions() {
+        return this.extensions;
+    }
+
+    public Object getValue() {
+        return this.value;
+    }
+
+    public Optional<String> getMediaType() {
+        return Optional.ofNullable(this.mediaType);
+    }
+
+    public String toString() {
+        return String.valueOf(this.value);
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        } else if (o != null && !o.toString().equals("")) {
+            Example example = (Example)o;
+            return this.id.equals(example.id) && Objects.equals(this.summary, example.summary) && Objects.equals(this.description, example.description) && this.value.equals(example.value) && this.externalValue.equals(example.externalValue) && this.mediaType.equals(example.mediaType) && this.extensions.equals(example.extensions);
+        } else {
+            return false;
+        }
+    }
+
+    public int hashCode() {
+        return Objects.hash(new Object[]{this.id, this.summary, this.description, this.value, this.externalValue, this.mediaType, this.extensions});
+    }
+}

+ 1 - 1
java/storlead-api/src/main/java/com/storlead/login/AuthCaptchaApiController.java

@@ -19,7 +19,7 @@ import javax.annotation.Resource;
  */
 @RestController
 @RequestMapping("/sys/auth/captcha")
-@Api(tags = "System -> 验证码(解耦)")
+@Api(tags = "系统模块 -> 验证码(解耦)")
 public class AuthCaptchaApiController {
 
     @Resource

+ 1 - 1
java/storlead-api/src/main/java/com/storlead/login/TenantLoginApiController.java

@@ -51,7 +51,7 @@ import java.util.concurrent.TimeUnit;
  */
 @RestController
 @RequestMapping("/sys/auth")
-@Api(tags = "System -> 租户登录")
+@Api(tags = "系统模块 -> 租户登录")
 @Slf4j
 public class TenantLoginApiController {
 

+ 15 - 0
java/storlead-api/src/main/resources/application.yml

@@ -55,6 +55,7 @@ domainname: http://localhost:10010/router/rest
 # 多租户:无 tenant_id 列的表须列入忽略,否则 SQL 会拼 tenant_id 导致 Unknown column
 storlead:
   es:
+    # false:使用 Noop 实现,不连接 ES(本地无 ES 时请保持 false)
     enabled: false
     host: 127.0.0.1
     port: 9200
@@ -76,3 +77,17 @@ storlead:
           - com.storlead.mail.service
           - com.storlead.mail.mapper
         datasource: sales
+
+management:
+  health:
+    elasticsearch:
+      enabled: false
+
+knife4j:
+  enable: true
+  production: false
+  setting:
+    enable-swagger-models: true
+    enable-document-manage: true
+    enable-search: true
+    enable-footer: false

+ 7 - 2
java/storlead-dependencies/pom.xml

@@ -19,7 +19,7 @@
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <spring.boot.starter.parent>2.7.0</spring.boot.starter.parent>
         <spring.boot.version>2.7.0</spring.boot.version>
-        <knife4j.version>4.5.0</knife4j.version>
+        <knife4j.version>3.0.3</knife4j.version>
         <spring-web.version>5.3.32</spring-web.version>
         <jsoup.version>1.18.3</jsoup.version>
         <mockito-inline.version>4.11.0</mockito-inline.version>
@@ -70,6 +70,11 @@
 
 
 <!--            &lt;!&ndash;swagger&ndash;&gt;-->
+            <dependency>
+                <groupId>io.springfox</groupId>
+                <artifactId>springfox-boot-starter</artifactId>
+                <version>${swagger.version}</version>
+            </dependency>
             <dependency>
                 <groupId>io.springfox</groupId>
                 <artifactId>springfox-swagger2</artifactId>
@@ -181,7 +186,7 @@
 
             <dependency>
                 <groupId>com.github.xiaoymin</groupId>
-                <artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
+                <artifactId>knife4j-spring-boot-starter</artifactId>
                 <version>${knife4j.version}</version>
             </dependency>
 <!--            <dependency>-->

+ 6 - 0
java/storlead-es/storlead-es-biz/src/main/java/com/storlead/es/config/ElasticSearchConfig.java

@@ -10,6 +10,7 @@ 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.data.elasticsearch.core.ElasticsearchRestTemplate;
 
 /**
  * Elasticsearch 客户端,仅在 {@code storlead.es.enabled=true} 时创建。
@@ -32,4 +33,9 @@ public class ElasticSearchConfig {
                         .setHttpClientConfigCallback(httpClientBuilder ->
                                 httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)));
     }
+
+    @Bean
+    public ElasticsearchRestTemplate elasticsearchRestTemplate(RestHighLevelClient restHighLevelClient) {
+        return new ElasticsearchRestTemplate(restHighLevelClient);
+    }
 }

+ 3 - 1
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/properties/UrlChainDefinitionPorperties.java

@@ -12,7 +12,7 @@ import org.springframework.context.annotation.Configuration;
 @Data
 @Configuration
 public class UrlChainDefinitionPorperties {
-    private String contextPath = "/api" ;
+    private String contextPath = "/router/rest" ;
 	private String [] authWhitelist = {
 			"/",
 			"/lingcun/common/view/**",
@@ -40,6 +40,8 @@ public class UrlChainDefinitionPorperties {
 			"/**/swagger-ui",
 			"/swagger**/**",
 			"/webjars/**",
+			"/swagger-resources",
+			"/swagger-resources/**",
 			"/v2/**",
 			"/xwork/**",
 			"/lingcun/getPhoneCode",

+ 21 - 1
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/UrlChainBlackAndWhiteUtil.java

@@ -2,6 +2,7 @@ package com.storlead.framework.common.util;
 
 import com.storlead.framework.common.properties.UrlChainDefinitionPorperties;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
 import org.springframework.util.AntPathMatcher;
 
@@ -16,7 +17,13 @@ public class UrlChainBlackAndWhiteUtil {
     @Autowired
     private UrlChainDefinitionPorperties properties;
 
+    @Value("${server.servlet.context-path:}")
+    private String servletContextPath;
+
     public String getContextPath() {
+        if (servletContextPath != null && !servletContextPath.isEmpty()) {
+            return servletContextPath;
+        }
         return properties.getContextPath();
     }
 
@@ -28,7 +35,7 @@ public class UrlChainBlackAndWhiteUtil {
     public boolean IsWhiteUri(String uri)
     {
         String [] AUTH_WHITELIST = properties.getAuthWhitelist();
-        uri = uri.replace(properties.getContextPath()+"/","/");
+        uri = normalizeRequestUri(uri);
         for (int i = 0; i < AUTH_WHITELIST.length; i++)
         {
             if (AUTH_WHITELIST[i].length() > 0)
@@ -44,4 +51,17 @@ public class UrlChainBlackAndWhiteUtil {
         }
         return false;
     }
+
+    private String normalizeRequestUri(String uri) {
+        String ctx = getContextPath();
+        if (ctx != null && !ctx.isEmpty() && !"/".equals(ctx) && uri.startsWith(ctx)) {
+            uri = uri.substring(ctx.length());
+        } else {
+            uri = uri.replace(properties.getContextPath() + "/", "/");
+        }
+        if (!uri.startsWith("/")) {
+            uri = "/" + uri;
+        }
+        return uri;
+    }
 }

+ 7 - 1
java/storlead-framework/storlead-web/pom.xml

@@ -76,7 +76,8 @@
 
         <dependency>
             <groupId>com.github.xiaoymin</groupId>
-            <artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
+            <artifactId>knife4j-spring-boot-starter</artifactId>
+            <version>${knife4j.version}</version>
         </dependency>
 
         <dependency>
@@ -84,6 +85,11 @@
             <artifactId>lombok</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-boot-starter</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>io.springfox</groupId>
             <artifactId>springfox-swagger2</artifactId>

+ 89 - 68
java/storlead-framework/storlead-web/src/main/java/com/storlead/framework/web/config/Swagger2Config.java

@@ -1,7 +1,9 @@
 package com.storlead.framework.web.config;
 
+import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
 import com.storlead.framework.common.constant.DefContants;
-import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
 import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties;
 import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
 import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
@@ -13,53 +15,90 @@ import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandl
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.core.env.Environment;
+import org.springframework.util.ReflectionUtils;
 import org.springframework.util.StringUtils;
 import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
 import springfox.documentation.builders.ApiInfoBuilder;
-import springfox.documentation.builders.ParameterBuilder;
 import springfox.documentation.builders.PathSelectors;
 import springfox.documentation.builders.RequestHandlerSelectors;
-import springfox.documentation.schema.ModelRef;
 import springfox.documentation.service.*;
 import springfox.documentation.spi.DocumentationType;
 import springfox.documentation.spi.service.contexts.SecurityContext;
 import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
+import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
 
+import java.lang.reflect.Field;
 import java.util.*;
+import java.util.stream.Collectors;
 
 /**
- * @program: sp-sales-platform
- * @description:
- * @author: chenkq
- * @create: 2024-03-29 17:35
+ * Springfox 3 + Knife4j 3:统一扫描 com.storlead 下全部 Controller。
  */
-
 @Configuration
+@EnableSwagger2
+@EnableKnife4j
 public class Swagger2Config implements WebMvcConfigurer {
 
-    // 显示swagger-ui.html文档展示页,还必须注入swagger资源:
-
     @Override
     public void addResourceHandlers(ResourceHandlerRegistry registry) {
-        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
-        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
-        registry.addResourceHandler("/webjars/").addResourceLocations("classpath:/META-INF/resources/webjars/");
+        registry.addResourceHandler("/swagger-ui.html")
+                .addResourceLocations("classpath:/META-INF/resources/");
+        registry.addResourceHandler("/doc.html")
+                .addResourceLocations("classpath:/META-INF/resources/");
+        registry.addResourceHandler("/webjars/**")
+                .addResourceLocations("classpath:/META-INF/resources/webjars/");
     }
 
-    //
-    // swagger2的配置文件,这里可以配置swagger2的一些基本的内容,比如扫描的包等等
-
-    // @return Docket
+    /**
+     * Spring Boot 2.6+ 与 Springfox 兼容:去掉使用 PathPatternParser 的 HandlerMapping。
+     */
+    @Bean
+    public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
+        return new BeanPostProcessor() {
+            @Override
+            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+                if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
+                    customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
+                }
+                return bean;
+            }
+
+            private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
+                List<T> copy = mappings.stream()
+                        .filter(mapping -> mapping.getPatternParser() == null)
+                        .collect(Collectors.toList());
+                mappings.clear();
+                mappings.addAll(copy);
+            }
+
+            @SuppressWarnings("unchecked")
+            private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
+                Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
+                if (field == null) {
+                    return Collections.emptyList();
+                }
+                ReflectionUtils.makeAccessible(field);
+                try {
+                    return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
+                } catch (IllegalAccessException e) {
+                    throw new IllegalStateException(e);
+                }
+            }
+        };
+    }
 
     @Bean
-    public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier
-            , ServletEndpointsSupplier servletEndpointsSupplier
-            , ControllerEndpointsSupplier controllerEndpointsSupplier
-            , EndpointMediaTypes endpointMediaTypes
-            , CorsEndpointProperties corsProperties
-            , WebEndpointProperties webEndpointProperties
-            , Environment environment) {
+    public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier,
+            ServletEndpointsSupplier servletEndpointsSupplier,
+            ControllerEndpointsSupplier controllerEndpointsSupplier,
+            EndpointMediaTypes endpointMediaTypes,
+            CorsEndpointProperties corsProperties,
+            WebEndpointProperties webEndpointProperties,
+            Environment environment) {
         List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
         Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
         allEndpoints.addAll(webEndpoints);
@@ -67,72 +106,54 @@ public class Swagger2Config implements WebMvcConfigurer {
         allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
         String basePath = webEndpointProperties.getBasePath();
         EndpointMapping endpointMapping = new EndpointMapping(basePath);
-        boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
-        return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null);
-    }
-    private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {
-        return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
+        boolean shouldRegisterLinksMapping = shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
+        return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes,
+                corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath),
+                shouldRegisterLinksMapping, null);
     }
 
-    @Bean
-    public Docket platformAdminApi() {
-        return buildGroupedDocket("platform-admin", "/platform/admin/.*");
+    private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment,
+            String basePath) {
+        return webEndpointProperties.getDiscovery().isEnabled()
+                && (StringUtils.hasText(basePath)
+                || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
     }
 
+    /**
+     * 单一分组,扫描 com.storlead 包下全部接口(不限制 @ApiOperation,避免漏接口)。
+     */
     @Bean
-    public Docket tenantAccountApi() {
-        return buildGroupedDocket("tenant-account", "/account/.*");
-    }
-
-    private Docket buildGroupedDocket(String groupName, String pathRegex) {
+    public Docket allApiDocket() {
         return new Docket(DocumentationType.SWAGGER_2)
-                .groupName(groupName)
+                .groupName("all")
                 .apiInfo(apiInfo())
                 .select()
                 .apis(RequestHandlerSelectors.basePackage("com.storlead"))
-                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
-                .paths(PathSelectors.regex(pathRegex))
+                .paths(PathSelectors.any())
                 .build()
-                .securityContexts(Arrays.asList(securityContext()))
-                .securitySchemes(Arrays.asList(new ApiKey("token", "token", io.swagger.v3.oas.models.security.SecurityScheme.In.HEADER.name())));
+                .securityContexts(Collections.singletonList(securityContext()))
+                .securitySchemes(Collections.singletonList(new ApiKey("token", DefContants.ACCESS_TOKEN, "header")));
     }
 
     private SecurityContext securityContext() {
         return SecurityContext.builder()
                 .securityReferences(defaultAuth())
-                //.forPaths(PathSelectors.regex("/*.*"))
                 .build();
     }
 
     private List<SecurityReference> defaultAuth() {
-        AuthorizationScope authorizationScope
-                = new AuthorizationScope("global", "accessEverything");
-        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
-        authorizationScopes[0] = authorizationScope;
-        return Collections.singletonList(
-                new SecurityReference("token", authorizationScopes));
-    }
-
-    @Bean
-    SecurityScheme securityScheme() {
-        return new ApiKey(DefContants.ACCESS_TOKEN, DefContants.ACCESS_TOKEN, "header");
-    }
-
-    private List<Parameter> setHeaderToken() {
-        ParameterBuilder tokenPar = new ParameterBuilder();
-        List<Parameter> pars = new ArrayList<>();
-        tokenPar.name("token").description("token").modelRef(new ModelRef("string")).parameterType("header")
-                .required(false).build();
-        pars.add(tokenPar.build());
-        return pars;
+        AuthorizationScope scope = new AuthorizationScope("global", "accessEverything");
+        return Collections.singletonList(new SecurityReference("token", new AuthorizationScope[]{scope}));
     }
 
     private ApiInfo apiInfo() {
-        return new ApiInfoBuilder() // //大标题
-                .title("领存CRM后台服务API接口文档") // 版本号 .version("1.0") //
-                .termsOfServiceUrl("NO terms of service") // 描述 .description("后台API接口")
+        return new ApiInfoBuilder()
+                .title("领存CRM后台服务API接口文档")
+                .description("com.storlead 包下全部 REST 接口")
+                .version("1.0")
+                .termsOfServiceUrl("NO terms of service")
                 .license("The Apache License, Version 2.0")
-                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html").build();
+                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
+                .build();
     }
-
 }

+ 1 - 1
java/storlead-system/storlead-system-api/src/main/java/com/storlead/system/controller/SysAppApiController.java

@@ -28,7 +28,7 @@ public class SysAppApiController {
 
     @ApiOperation("应用分页")
     @PostMapping("/pagelist")
-    public Result<IPage<SysAppEntity>> pagelist(@RequestBody SysAppPageDTO dto) {
+    public Result<?> pagelist(@RequestBody SysAppPageDTO dto) {
         return Result.ok(sysAppService.pageQuery(dto));
     }
 

+ 4 - 0
java/storlead-system/storlead-system-biz/pom.xml

@@ -45,6 +45,10 @@
             <groupId>com.storlead.boot</groupId>
             <artifactId>storlead-auth</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.storlead.boot</groupId>
+            <artifactId>storlead-redis</artifactId>
+        </dependency>
         <dependency>
             <groupId>com.storlead.boot</groupId>
             <artifactId>storlead-web</artifactId>

+ 1 - 0
java/storlead-system/storlead-system-biz/src/main/java/com/storlead/system/util/SystemConfigItemCacheUtil.java

@@ -26,6 +26,7 @@ import java.util.regex.Pattern;
 public class SystemConfigItemCacheUtil {
 
     public final static String itemCacheKey = "reids_config_item_cache_";
+
     @Resource
     private RedisService redisService;
 

+ 2 - 2
pom.xml

@@ -52,7 +52,7 @@
         <mapstruct.version>1.6.3</mapstruct.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <spring.boot.starter.parent>2.7.0</spring.boot.starter.parent>
-        <knife4j.version>4.5.0</knife4j.version>
+        <knife4j.version>3.0.3</knife4j.version>
         <spring-web.version>5.3.32</spring-web.version>
         <jsoup.version>1.18.3</jsoup.version>
         <mockito-inline.version>4.11.0</mockito-inline.version>
@@ -447,7 +447,7 @@
 
         <dependency>
             <groupId>com.github.xiaoymin</groupId>
-            <artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
+            <artifactId>knife4j-spring-boot-starter</artifactId>
             <version>${knife4j.version}</version>
         </dependency>
         <!--            <dependency>-->