chenkq 2 viikkoa sitten
vanhempi
sitoutus
a6321dec72
22 muutettua tiedostoa jossa 437 lisäystä ja 191 poistoa
  1. 1 1
      pom.xml
  2. 117 50
      storlead-dependencies/pom.xml
  3. 3 0
      storlead-framework/storlead-auth/src/main/java/com/storlead/framework/auth/vo/LoginUser.java
  4. 10 0
      storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/DSConstants.java
  5. 1 1
      storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/dto/query/QueryBaseDTO.java
  6. 3 1
      storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/properties/UrlChainDefinitionPorperties.java
  7. 1 1
      storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/SystemUtils.java
  8. 21 1
      storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/UrlChainBlackAndWhiteUtil.java
  9. 0 1
      storlead-framework/storlead-core/pom.xml
  10. 138 113
      storlead-framework/storlead-mybatis/src/main/java/com/storlead/framework/mybatis/datasource/ModuleDefaultDataSourceAspect.java
  11. 1 1
      storlead-framework/storlead-mybatis/src/main/java/com/storlead/framework/mybatis/query/QueryBaseEntity.java
  12. 5 1
      storlead-framework/storlead-redis/src/main/java/com/storlead/framework/redis/config/RedissonConfig.java
  13. 7 2
      storlead-framework/storlead-web/pom.xml
  14. 0 3
      storlead-framework/storlead-web/src/main/java/com/storlead/framework/web/filter/AuthRequestFilter.java
  15. 1 1
      storlead-framework/storlead-web/src/main/java/com/storlead/framework/web/interceptor/WebConfig.java
  16. 1 1
      storlead-framework/storlead-web/src/main/java/com/storlead/framework/web/message/WebResp.java
  17. 1 1
      storlead-framework/storlead-web/src/main/java/com/storlead/framework/web/process/ApiResultHandler.java
  18. 5 0
      storlead-mail-api/src/main/resources/application-dev.yml
  19. 5 0
      storlead-mail-api/src/main/resources/application-test.yml
  20. 32 11
      storlead-mail/storlead-mail-common/src/main/resources/sql/mkt_mail_message_receive.sql
  21. 82 0
      storlead-mail/storlead-mail-common/src/main/resources/sql/mkt_mail_recreate.sql
  22. 2 1
      storlead-mail/storlead-mail-dispatch/src/main/java/com/storlead/sales/mail/dispatch/MktEmailPullTaskJob.java

+ 1 - 1
pom.xml

@@ -44,7 +44,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>

+ 117 - 50
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>
@@ -60,31 +60,43 @@
 
     <dependencyManagement>
         <dependencies>
-
             <dependency>
                 <groupId>org.springframework</groupId>
                 <artifactId>spring-web</artifactId>
                 <version>${spring-web.version}</version>
             </dependency>
 
+
+
+
+            <!--            &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>
                 <version>${swagger.version}</version>
             </dependency>
-
             <dependency>
                 <groupId>io.springfox</groupId>
                 <artifactId>springfox-swagger-ui</artifactId>
                 <version>${swagger.ui.version}</version>
             </dependency>
-
             <dependency>
                 <groupId>io.swagger</groupId>
                 <artifactId>swagger-annotations</artifactId>
                 <version>${swagger.annotations.version}</version>
             </dependency>
 
+            <!--            <dependency>-->
+            <!--                <groupId>com.google.code.gson</groupId>-->
+            <!--                <artifactId>gson</artifactId>-->
+            <!--                <version>${google.code.gson.version}</version> &lt;!&ndash; 建议使用最新版本 &ndash;&gt;-->
+            <!--            </dependency>-->
+
             <dependency>
                 <groupId>com.alibaba</groupId>
                 <artifactId>fastjson</artifactId>
@@ -97,6 +109,19 @@
                 <version>4.5.3</version>
             </dependency>
 
+            <!--            <dependency>-->
+            <!--                <groupId>org.apache.httpcomponents.client5</groupId>-->
+            <!--                <artifactId>httpclient5</artifactId>-->
+            <!--                <version>${apache.httpcore5.client5.version}</version>-->
+            <!--            </dependency>-->
+
+            <!--            &lt;!&ndash; https://mvnrepository.com/artifact/org.apache.httpcomponents.core5/httpcore5 &ndash;&gt;-->
+            <!--            <dependency>-->
+            <!--                <groupId>org.apache.httpcomponents.core5</groupId>-->
+            <!--                <artifactId>httpcore5</artifactId>-->
+            <!--                <version>${apache.httpcore5.version}</version>-->
+            <!--            </dependency>-->
+
             <!--JWT-->
             <dependency>
                 <groupId>com.auth0</groupId>
@@ -110,33 +135,36 @@
                 <artifactId>jjwt</artifactId>
                 <version>${io.jsonwebtoken.version}</version>
             </dependency>
-<!--            <dependency>-->
-<!--                <groupId>org.apache.httpcomponents.core5</groupId>-->
-<!--                <artifactId>httpcore5-h2</artifactId>-->
-<!--                <version>${apache.httpcore5-h2.version}</version>-->
-<!--            </dependency>-->
+            <!--            &lt;!&ndash; https://mvnrepository.com/artifact/org.apache.httpcomponents.core5/httpcore5-h2 &ndash;&gt;-->
+            <!--            <dependency>-->
+            <!--                <groupId>org.apache.httpcomponents.core5</groupId>-->
+            <!--                <artifactId>httpcore5-h2</artifactId>-->
+            <!--                <version>${apache.httpcore5-h2.version}</version>-->
+            <!--            </dependency>-->
 
             <dependency>
                 <groupId>org.apache.httpcomponents</groupId>
                 <artifactId>httpasyncclient</artifactId>
                 <version>${apache.httpasyncclient.version}</version>
             </dependency>
-<!--            <dependency>-->
-<!--                <groupId>com.github.xiaoymin</groupId>-->
-<!--                <artifactId>swagger-bootstrap-ui</artifactId>-->
-<!--                <version>1.8.7</version>-->
-<!--            </dependency>-->
-
-<!--            <dependency>-->
-<!--                <groupId>com.squareup.okhttp3</groupId>-->
-<!--                <artifactId>okhttp</artifactId>-->
-<!--            </dependency>-->
-
-<!--            <dependency>-->
-<!--                <groupId>com.squareup.okhttp3</groupId>-->
-<!--                <artifactId>okhttp-sse</artifactId>-->
-<!--                <version>${okhttp.sse.version}</version>-->
-<!--            </dependency>-->
+            <!--            <dependency>-->
+            <!--                <groupId>com.github.xiaoymin</groupId>-->
+            <!--                <artifactId>swagger-bootstrap-ui</artifactId>-->
+            <!--                <version>1.8.7</version>-->
+            <!--            </dependency>-->
+
+
+            <!--            <dependency>-->
+            <!--                <groupId>com.squareup.okhttp3</groupId>-->
+            <!--                <artifactId>okhttp</artifactId>-->
+            <!--                <version>${squareup.okhttp.version}</version> &lt;!&ndash; 2023年最新稳定版 &ndash;&gt;-->
+            <!--            </dependency>-->
+
+            <!--            <dependency>-->
+            <!--                <groupId>com.squareup.okhttp3</groupId>-->
+            <!--                <artifactId>okhttp-sse</artifactId>-->
+            <!--                <version>${okhttp.sse.version}</version>-->
+            <!--            </dependency>-->
 
             <dependency>
                 <groupId>org.projectlombok</groupId>
@@ -158,14 +186,14 @@
 
             <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>-->
-<!--                <groupId>cn.hutool</groupId>-->
-<!--                <artifactId>hutool-all</artifactId>-->
-<!--                <version>4.5.11</version>-->
-<!--            </dependency>-->
+            <!--            <dependency>-->
+            <!--                <groupId>cn.hutool</groupId>-->
+            <!--                <artifactId>hutool-all</artifactId>-->
+            <!--                <version>4.5.11</version>-->
+            <!--            </dependency>-->
 
             <dependency>
                 <groupId>com.googlecode.aviator</groupId>
@@ -193,6 +221,14 @@
                 <version>${mysql.connector.version}</version>
             </dependency>
 
+            <!-- 动态数据源 -->
+            <!-- mybatis-plus -->
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-boot-starter</artifactId>
+                <version>${mybatis-plus.version}</version>
+            </dependency>
+
             <!-- 动态数据源 -->
             <dependency>
                 <groupId>com.baomidou</groupId>
@@ -215,24 +251,25 @@
             </dependency>
 
 
-<!--            <dependency>-->
-<!--                <groupId>com.aliyun.kms</groupId>-->
-<!--                <artifactId>kms-transfer-client</artifactId>-->
-<!--                <version>0.1.0</version>-->
-<!--            </dependency>-->
+            <!--            <dependency>-->
+            <!--                <groupId>com.aliyun.kms</groupId>-->
+            <!--                <artifactId>kms-transfer-client</artifactId>-->
+            <!--                <version>0.1.0</version>-->
+            <!--            </dependency>-->
 
 
 
-<!--            <dependency>-->
-<!--                <groupId>com.github.yulichang</groupId>-->
-<!--                <version>${mybatis-plus-join.version}</version>-->
-<!--            </dependency>-->
+            <!--            <dependency>-->
+            <!--                <groupId>com.github.yulichang</groupId>-->
+            <!--                <artifactId>mybatis-plus-join-boot-starter</artifactId> &lt;!&ndash; MyBatis 联表查询 &ndash;&gt;-->
+            <!--                <version>${mybatis-plus-join.version}</version>-->
+            <!--            </dependency>-->
 
-<!--            <dependency>-->
-<!--                <groupId>org.java-websocket</groupId>-->
-<!--                <artifactId>Java-WebSocket</artifactId>-->
-<!--                <version>${spring.web.socket.version}</version>-->
-<!--            </dependency>-->
+            <!--            <dependency>-->
+            <!--                <groupId>org.java-websocket</groupId>-->
+            <!--                <artifactId>Java-WebSocket</artifactId>-->
+            <!--                <version>${spring.web.socket.version}</version>-->
+            <!--            </dependency>-->
 
             <!-- Web 相关 -->
             <dependency>
@@ -247,11 +284,11 @@
                 </exclusions>
             </dependency>
 
-<!--            <dependency>-->
-<!--                <groupId>org.jsoup</groupId>-->
-<!--                <artifactId>jsoup</artifactId>-->
-<!--                <version>${jsoup.version}</version>-->
-<!--            </dependency>-->
+            <!--            <dependency>-->
+            <!--                <groupId>org.jsoup</groupId>-->
+            <!--                <artifactId>jsoup</artifactId>-->
+            <!--                <version>${jsoup.version}</version>-->
+            <!--            </dependency>-->
 
             <dependency>
                 <groupId>com.google.guava</groupId>
@@ -285,4 +322,34 @@
 
     </dependencyManagement>
 
+    <!--    <build>-->
+    <!--        <plugins>-->
+    <!--            &lt;!&ndash; 统一 revision 版本 &ndash;&gt;-->
+    <!--            <plugin>-->
+    <!--                <groupId>org.codehaus.mojo</groupId>-->
+    <!--                <artifactId>flatten-maven-plugin</artifactId>-->
+    <!--                <version>${flatten-maven-plugin.version}</version>-->
+    <!--                <configuration>-->
+    <!--                    <flattenMode>bom</flattenMode>-->
+    <!--                    <updatePomFile>true</updatePomFile>-->
+    <!--                </configuration>-->
+    <!--                <executions>-->
+    <!--                    <execution>-->
+    <!--                        <goals>-->
+    <!--                            <goal>flatten</goal>-->
+    <!--                        </goals>-->
+    <!--                        <id>flatten</id>-->
+    <!--                        <phase>process-resources</phase>-->
+    <!--                    </execution>-->
+    <!--                    <execution>-->
+    <!--                        <goals>-->
+    <!--                            <goal>clean</goal>-->
+    <!--                        </goals>-->
+    <!--                        <id>flatten.clean</id>-->
+    <!--                        <phase>clean</phase>-->
+    <!--                    </execution>-->
+    <!--                </executions>-->
+    <!--            </plugin>-->
+    <!--        </plugins>-->
+    <!--    </build>-->
 </project>

+ 3 - 0
storlead-framework/storlead-auth/src/main/java/com/storlead/framework/auth/vo/LoginUser.java

@@ -169,6 +169,9 @@ public class LoginUser implements Serializable {
     @ApiModelProperty(value = "岗位职责")
     private String jobDes;
 
+    @ApiModelProperty(value = "备注")
+    private String remark;
+
     private Set<Long> roleIds;
 
     private String apiUrl;

+ 10 - 0
storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/DSConstants.java

@@ -21,6 +21,16 @@ public class DSConstants {
      */
     public static final String DATASOURCE_SALES = "sales";
 
+    /**
+     * 营销/交易库(与 spring.datasource.dynamic.datasource.trade 的 key 一致)
+     */
+    public static final String DATASOURCE_TRADE = "trade";
+
+    /**
+     * 邮件库(与 spring.datasource.dynamic.datasource.mail 的 key 一致)
+     */
+    public static final String DATASOURCE_MAIL = "mail";
+
     /**
      * 数据源分组 oa库
      */

+ 1 - 1
storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/dto/query/QueryBaseDTO.java

@@ -17,7 +17,7 @@ public class QueryBaseDTO extends PageDTO {
     private String blurry;
 
     @ApiModelProperty(value = "菜单Id")
-    private Long scopeMenuId;
+    private String scopeMenuId;
 
     @ApiModelProperty(value = "数据权限范围sql")
     private String dataScopeSql;

+ 3 - 1
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",

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

@@ -16,7 +16,7 @@ public class SystemUtils {
      * */
     public static void printJson(HttpServletResponse response, String json){
         try {
-            response.setContentType("application/json");
+            response.setContentType("application/json;charset=UTF-8");
             response.setCharacterEncoding("UTF-8");
             response.getWriter().print(json);
             response.getWriter().flush();

+ 21 - 1
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;
+    }
 }

+ 0 - 1
storlead-framework/storlead-core/pom.xml

@@ -6,7 +6,6 @@
         <groupId>com.storlead.boot</groupId>
         <artifactId>storlead-framework</artifactId>
         <version>1.0</version>
-        <relativePath>../pom.xml</relativePath>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 138 - 113
storlead-framework/storlead-mybatis/src/main/java/com/storlead/framework/mybatis/datasource/ModuleDefaultDataSourceAspect.java

@@ -1,113 +1,138 @@
-package com.storlead.framework.mybatis.datasource;//package com.storlead.framework.mybatis.datasource;
-//
-//import com.baomidou.dynamic.datasource.annotation.DS;
-//import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
-//import lombok.RequiredArgsConstructor;
-//import lombok.extern.slf4j.Slf4j;
-//import org.aspectj.lang.ProceedingJoinPoint;
-//import org.aspectj.lang.annotation.Around;
-//import org.aspectj.lang.annotation.Aspect;
-//import org.aspectj.lang.annotation.Pointcut;
-//import org.aspectj.lang.reflect.MethodSignature;
-//import org.springframework.core.annotation.AnnotatedElementUtils;
-//import org.springframework.core.annotation.Order;
-//import org.springframework.stereotype.Component;
-//import org.springframework.util.StringUtils;
-//
-//import java.lang.reflect.Method;
-//
-///**
-// * 子模块默认数据源:按配置包前缀 push 数据源;若目标已标注 {@link DS} 则跳过,由官方 {@code @DS} 切面处理。
-// */
-//@Slf4j
-//@Aspect
-//@Component
-//@Order(0)
-//@RequiredArgsConstructor
-//public class ModuleDefaultDataSourceAspect {
-//
-//    private final ModuleDefaultDataSourceProperties properties;
-//
-//    @Pointcut("execution(* *(..)) && (within(com.storlead..service..*) || within(com.storlead..mapper..*))")
-//    public void moduleServiceOrMapperLayer() {
-//    }
-//
-//    @Around("moduleServiceOrMapperLayer()")
-//    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
-//        if (!properties.isModuleDefaultEnabled()) {
-//            return joinPoint.proceed();
-//        }
-//        if (hasDsAnnotation(joinPoint)) {
-//            return joinPoint.proceed();
-//        }
-//        String datasource = resolveDatasource(joinPoint);
-//        if (!StringUtils.hasText(datasource)) {
-//            return joinPoint.proceed();
-//        }
-//        DynamicDataSourceContextHolder.push(datasource);
-//        try {
-//            if (log.isDebugEnabled()) {
-//                log.debug("module default datasource [{}] -> {}", datasource, joinPoint.getSignature().toShortString());
-//            }
-//            return joinPoint.proceed();
-//        } finally {
-//            DynamicDataSourceContextHolder.poll();
-//        }
-//    }
-//
-//    private boolean hasDsAnnotation(ProceedingJoinPoint joinPoint) {
-//        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
-//        Method method = signature.getMethod();
-//        Class<?> declaringType = signature.getDeclaringType();
-//        if (AnnotatedElementUtils.hasAnnotation(method, DS.class)) {
-//            return true;
-//        }
-//        if (declaringType != null && AnnotatedElementUtils.hasAnnotation(declaringType, DS.class)) {
-//            return true;
-//        }
-//        return AnnotatedElementUtils.hasAnnotation(joinPoint.getTarget().getClass(), DS.class);
-//    }
-//
-//    /**
-//     * 最长包前缀匹配
-//     */
-//    private String resolveDatasource(ProceedingJoinPoint joinPoint) {
-//        String className = resolveBusinessClassName(joinPoint);
-//        String matchedDs = null;
-//        int matchedLen = -1;
-//        for (ModuleDefaultDataSourceProperties.ModuleDefaultRule rule : properties.getModuleDefaults()) {
-//            if (rule.getPackages() == null || !StringUtils.hasText(rule.getDatasource())) {
-//                continue;
-//            }
-//            for (String pkg : rule.getPackages()) {
-//                if (!StringUtils.hasText(pkg)) {
-//                    continue;
-//                }
-//                if (matchesPackage(className, pkg.trim()) && pkg.length() > matchedLen) {
-//                    matchedLen = pkg.length();
-//                    matchedDs = rule.getDatasource();
-//                }
-//            }
-//        }
-//        return matchedDs;
-//    }
-//
-//    private boolean matchesPackage(String className, String packagePrefix) {
-//        return className.equals(packagePrefix)
-//                || className.startsWith(packagePrefix + ".");
-//    }
-//
-//    /**
-//     * Mapper 为 JDK 代理时,用接口全限定名匹配包前缀。
-//     */
-//    private String resolveBusinessClassName(ProceedingJoinPoint joinPoint) {
-//        if (joinPoint.getSignature() instanceof MethodSignature) {
-//            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
-//            Class<?> declaringType = signature.getDeclaringType();
-//            if (declaringType != null && declaringType.getName().startsWith("com.storlead.")) {
-//                return declaringType.getName();
-//            }
-//        }
-//        return joinPoint.getTarget().getClass().getName();
-//    }
-//}
+package com.storlead.framework.mybatis.datasource;
+
+import com.baomidou.dynamic.datasource.annotation.DS;
+import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import java.lang.reflect.Method;
+
+/**
+ * 子模块默认数据源:按配置包前缀 push 数据源。
+ * <p>
+ * 若目标已标注 {@link DS},由本切面直接 push 其 value(不再仅“跳过”依赖官方切面,
+ * 避免官方 @DS 未织入时落到 primary/master)。
+ */
+@Slf4j
+@Aspect
+@Component
+@Order(0)
+@RequiredArgsConstructor
+public class ModuleDefaultDataSourceAspect {
+
+    private final ModuleDefaultDataSourceProperties properties;
+
+    @Pointcut("execution(* *(..)) && (within(com.storlead..service..*) || within(com.storlead..mapper..*))")
+    public void moduleServiceOrMapperLayer() {
+    }
+
+    @Around("moduleServiceOrMapperLayer()")
+    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
+        if (!properties.isModuleDefaultEnabled()) {
+            return joinPoint.proceed();
+        }
+        String datasource = resolveExplicitDs(joinPoint);
+        if (!StringUtils.hasText(datasource)) {
+            datasource = resolveDatasource(joinPoint);
+        }
+        if (!StringUtils.hasText(datasource)) {
+            return joinPoint.proceed();
+        }
+        DynamicDataSourceContextHolder.push(datasource);
+        try {
+            if (log.isDebugEnabled()) {
+                log.debug("module datasource [{}] -> {}", datasource, joinPoint.getSignature().toShortString());
+            }
+            return joinPoint.proceed();
+        } finally {
+            DynamicDataSourceContextHolder.poll();
+        }
+    }
+
+    /**
+     * 方法 / 声明类 / 实际目标类上的 @DS 优先
+     */
+    private String resolveExplicitDs(ProceedingJoinPoint joinPoint) {
+        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+        Method method = signature.getMethod();
+        DS ds = AnnotatedElementUtils.findMergedAnnotation(method, DS.class);
+        if (ds != null && StringUtils.hasText(ds.value())) {
+            return ds.value();
+        }
+        Class<?> declaringType = signature.getDeclaringType();
+        if (declaringType != null) {
+            ds = AnnotatedElementUtils.findMergedAnnotation(declaringType, DS.class);
+            if (ds != null && StringUtils.hasText(ds.value())) {
+                return ds.value();
+            }
+        }
+        Class<?> targetClass = joinPoint.getTarget() != null ? joinPoint.getTarget().getClass() : null;
+        if (targetClass != null) {
+            ds = AnnotatedElementUtils.findMergedAnnotation(targetClass, DS.class);
+            if (ds != null && StringUtils.hasText(ds.value())) {
+                return ds.value();
+            }
+            // CGLIB 代理取父类
+            Class<?> superclass = targetClass.getSuperclass();
+            if (superclass != null && superclass != Object.class) {
+                ds = AnnotatedElementUtils.findMergedAnnotation(superclass, DS.class);
+                if (ds != null && StringUtils.hasText(ds.value())) {
+                    return ds.value();
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 最长包前缀匹配
+     */
+    private String resolveDatasource(ProceedingJoinPoint joinPoint) {
+        String className = resolveBusinessClassName(joinPoint);
+        String matchedDs = null;
+        int matchedLen = -1;
+        for (ModuleDefaultDataSourceProperties.ModuleDefaultRule rule : properties.getModuleDefaults()) {
+            if (rule.getPackages() == null || !StringUtils.hasText(rule.getDatasource())) {
+                continue;
+            }
+            for (String pkg : rule.getPackages()) {
+                if (!StringUtils.hasText(pkg)) {
+                    continue;
+                }
+                if (matchesPackage(className, pkg.trim()) && pkg.length() > matchedLen) {
+                    matchedLen = pkg.length();
+                    matchedDs = rule.getDatasource();
+                }
+            }
+        }
+        return matchedDs;
+    }
+
+    private boolean matchesPackage(String className, String packagePrefix) {
+        return className.equals(packagePrefix)
+                || className.startsWith(packagePrefix + ".");
+    }
+
+    /**
+     * Mapper 为 JDK 代理时,用接口全限定名匹配包前缀。
+     */
+    private String resolveBusinessClassName(ProceedingJoinPoint joinPoint) {
+        if (joinPoint.getSignature() instanceof MethodSignature) {
+            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+            Class<?> declaringType = signature.getDeclaringType();
+            if (declaringType != null && declaringType.getName().startsWith("com.storlead.")) {
+                return declaringType.getName();
+            }
+        }
+        return joinPoint.getTarget().getClass().getName();
+    }
+}

+ 1 - 1
storlead-framework/storlead-mybatis/src/main/java/com/storlead/framework/mybatis/query/QueryBaseEntity.java

@@ -27,7 +27,7 @@ public class QueryBaseEntity extends Page {
 
     @TableField(exist = false)
     @ApiModelProperty(value = "菜单Id")
-    private Long scopeMenuId;
+    private String scopeMenuId;
 
     @TableField(exist = false)
     @ApiModelProperty(value = "是否需要走权限:默认需要:1: 不需要,默认: 0 ,需要: 0")

+ 5 - 1
storlead-framework/storlead-redis/src/main/java/com/storlead/framework/redis/config/RedissonConfig.java

@@ -28,7 +28,11 @@ public class RedissonConfig {
     @Bean
     public RedissonClient getRedisson() {
         Config config = new Config();
-        config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password);
+        config.useSingleServer()
+                .setAddress("redis://" + host + ":" + port)
+                .setPassword(password)
+                .setConnectTimeout(10000)
+                .setTimeout(10000);
         RedissonClient redissonClient = Redisson.create(config);
         return redissonClient;
     }

+ 7 - 2
storlead-framework/storlead-web/pom.xml

@@ -6,7 +6,6 @@
         <groupId>com.storlead.boot</groupId>
         <artifactId>storlead-framework</artifactId>
         <version>1.0</version>
-        <relativePath>../pom.xml</relativePath>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
@@ -77,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>
@@ -85,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>

+ 0 - 3
storlead-framework/storlead-web/src/main/java/com/storlead/framework/web/filter/AuthRequestFilter.java

@@ -124,17 +124,14 @@ public class AuthRequestFilter implements Filter, Ordered {
     private Boolean loginHandle(HttpServletRequest req) {
         // 获取URL
         String url = req.getRequestURI();
-        log.error("login--WebSocket ---- url---"+url);
         Boolean isNeedLogin = chainBlackAndWhiteUtil.IsWhiteUri(url);
         if (isNeedLogin) {
             return isNeedLogin;
         }
-        log.error("login--WebSocket ---- isNeedLogin---"+url);
         String token = req.getHeader(DefContants.ACCESS_TOKEN);
         if (StrUtil.isBlank(token)) {
             token = req.getHeader("Sec-WebSocket-Protocol");
             if (StrUtil.isNotBlank(token)) {
-                log.error("login--WebSocket ---- token---"+token);
                 req.setAttribute("token",token);
             }
         }

+ 1 - 1
storlead-framework/storlead-web/src/main/java/com/storlead/framework/web/interceptor/WebConfig.java

@@ -1,4 +1,4 @@
-package com.storlead.framework.web.interceptor;//package com.storlead.framework.web.interceptor;
+//package com.storlead.framework.web.interceptor;
 //
 //import com.storlead.utils.common.FileProperties;
 //import org.springframework.beans.factory.annotation.Autowired;

+ 1 - 1
storlead-framework/storlead-web/src/main/java/com/storlead/framework/web/message/WebResp.java

@@ -1,4 +1,4 @@
-package com.storlead.framework.web.message;//package com.storlead.framework.web.message;
+//package com.storlead.framework.web.message;
 //
 //import lombok.Data;
 //import lombok.NoArgsConstructor;

+ 1 - 1
storlead-framework/storlead-web/src/main/java/com/storlead/framework/web/process/ApiResultHandler.java

@@ -1,4 +1,4 @@
-package com.storlead.framework.web.process;//package com.storlead.framework.web.process;
+//package com.storlead.framework.web.process;
 //
 //import com.fasterxml.jackson.core.JsonParser;
 //import com.fasterxml.jackson.databind.DeserializationFeature;

+ 5 - 0
storlead-mail-api/src/main/resources/application-dev.yml

@@ -91,6 +91,11 @@ spring:
           username: root
           password: rCgRgLjH99Xvg5BN
           driver-class-name: com.mysql.jdbc.Driver
+        trade:
+          url: jdbc:mysql://mysql.test.storlead.com:39091/sp_trade_platform_test?useSSL=false&useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true
+          username: root
+          password: rCgRgLjH99Xvg5BN
+          driver-class-name: com.mysql.jdbc.Driver
 
 #mybatis plus 设置
 mybatis-plus:

+ 5 - 0
storlead-mail-api/src/main/resources/application-test.yml

@@ -91,6 +91,11 @@ spring:
           username: root
           password: rCgRgLjH99Xvg5BN
           driver-class-name: com.mysql.jdbc.Driver
+        trade:
+          url: jdbc:mysql://mysql.test.storlead.com:39091/sp_trade_platform_test?useSSL=false&useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true
+          username: root
+          password: rCgRgLjH99Xvg5BN
+          driver-class-name: com.mysql.jdbc.Driver
 
 #mybatis plus 设置
 mybatis-plus:

+ 32 - 11
storlead-mail/storlead-mail-common/src/main/resources/sql/mkt_mail_message_receive.sql

@@ -1,19 +1,40 @@
--- mkt_mail_message 收信扩展:用 folder 区分收/发,并支持回复原信绑定
-ALTER TABLE mkt_mail_message
-    ADD COLUMN folder varchar(32) NULL COMMENT '文件夹:INBOX/SENT/SENTING/DRAFT' AFTER content,
-    ADD COLUMN msg_uid varchar(128) NULL COMMENT '服务器邮件UID(IMAP/POP)' AFTER folder,
-    ADD COLUMN recipient_date datetime NULL COMMENT '收信时间(服务器 ReceivedDate)' AFTER msg_uid,
-    ADD COLUMN sent_date datetime NULL COMMENT '邮件头 SentDate' AFTER recipient_date,
-    ADD COLUMN is_only_head tinyint NOT NULL DEFAULT 0 COMMENT '是否仅拉头:1是 0已有正文' AFTER sent_date,
-    ADD COLUMN is_read tinyint NOT NULL DEFAULT 0 COMMENT '是否已读:0未读 1已读' AFTER is_only_head,
-    ADD COLUMN in_out_mark tinyint NULL COMMENT '进出标记:1收件 2发件' AFTER is_read,
-    ADD COLUMN email_size bigint NULL COMMENT '邮件大小(字节)' AFTER in_out_mark,
-    ADD COLUMN in_reply_to varchar(512) NULL COMMENT 'In-Reply-To 头,用于匹配回复原信' AFTER email_size,
+-- mkt_mail_message 收信扩展(存量表若已建但缺列,按需执行;某列已存在则跳过该条)
+-- 报错 Unknown column 'msg_uid' 时优先执行本脚本
+
+ALTER TABLE mkt_mail_message
+    ADD COLUMN folder varchar(32) NULL COMMENT '文件夹:INBOX/SENT/SENTING/DRAFT' AFTER content;
+
+ALTER TABLE mkt_mail_message
+    ADD COLUMN msg_uid varchar(128) NULL COMMENT '服务器邮件UID(IMAP/POP)' AFTER folder;
+
+ALTER TABLE mkt_mail_message
+    ADD COLUMN recipient_date datetime NULL COMMENT '收信时间(服务器 ReceivedDate)' AFTER msg_uid;
+
+ALTER TABLE mkt_mail_message
+    ADD COLUMN sent_date datetime NULL COMMENT '邮件头 SentDate' AFTER recipient_date;
+
+ALTER TABLE mkt_mail_message
+    ADD COLUMN is_only_head tinyint NOT NULL DEFAULT 0 COMMENT '是否仅拉头:1是 0已有正文' AFTER sent_date;
+
+ALTER TABLE mkt_mail_message
+    ADD COLUMN is_read tinyint NOT NULL DEFAULT 0 COMMENT '是否已读:0未读 1已读' AFTER is_only_head;
+
+ALTER TABLE mkt_mail_message
+    ADD COLUMN in_out_mark tinyint NULL COMMENT '进出标记:1收件 2发件' AFTER is_read;
+
+ALTER TABLE mkt_mail_message
+    ADD COLUMN email_size bigint NULL COMMENT '邮件大小(字节)' AFTER in_out_mark;
+
+ALTER TABLE mkt_mail_message
+    ADD COLUMN in_reply_to varchar(512) NULL COMMENT 'In-Reply-To 头,用于匹配回复原信' AFTER email_size;
+
+ALTER TABLE mkt_mail_message
     ADD COLUMN reply_msg_id bigint NULL COMMENT '回复的原邮件ID,对应 mkt_mail_message.id' AFTER in_reply_to;
 
 -- 存量发信记录默认视为发件箱
 UPDATE mkt_mail_message SET folder = 'SENTING', in_out_mark = 2 WHERE folder IS NULL AND status IN (-2, -1, 0, 1, 2);
 
+-- 索引已存在会报 Duplicate key name,可忽略
 CREATE INDEX idx_mkt_account_folder_recv ON mkt_mail_message (account_id, folder, recipient_date);
 CREATE INDEX idx_mkt_account_message_id ON mkt_mail_message (account_id, message_id);
 CREATE INDEX idx_mkt_reply_msg_id ON mkt_mail_message (reply_msg_id);

+ 82 - 0
storlead-mail/storlead-mail-common/src/main/resources/sql/mkt_mail_recreate.sql

@@ -0,0 +1,82 @@
+-- ============================================================
+-- 营销邮件表:删表重建(会清空数据,仅用于开发/测试环境)
+-- 执行前请确认库名正确
+-- ============================================================
+
+DROP TABLE IF EXISTS mkt_mail_attachment;
+DROP TABLE IF EXISTS mkt_mail_message;
+
+-- 营销邮件投递/收信表(与 CRM emails 分表)
+CREATE TABLE mkt_mail_message (
+    id              bigint       NOT NULL AUTO_INCREMENT COMMENT '主键,自增;一封营销投递记录一行(建议一收件人一行)',
+    account_id      bigint       NOT NULL COMMENT '发信账户ID,对应 smtp_pop_settings.id,且该账户 scene=MKT',
+    org_id          bigint       NULL COMMENT '组织ID;组织级营销账号时冗余,便于按组织隔离查询',
+    biz_ref         varchar(64)  NULL COMMENT '调用方业务单号(如营销系统活动/任务ID),邮件服务不解析业务含义,仅回查',
+    biz_batch_no    varchar(64)  NULL COMMENT '同一次批量发送的批次号,用于按批汇总状态',
+    message_id      varchar(255) NULL COMMENT 'SMTP发出后的Message-ID,发送成功后回写,用于对账/排重',
+    track_token     varchar(64)  NULL COMMENT '打开追踪唯一token;is_track=1时生成,对应像素URL',
+    subject         varchar(512) NULL COMMENT '邮件主题',
+    from_addr       varchar(255) NULL COMMENT '发件人邮箱地址(一般取账户email_address)',
+    from_name       varchar(255) NULL COMMENT '发件人显示名(一般取账户昵称)',
+    recipient       varchar(512) NOT NULL COMMENT '收件人邮箱;单收件人一行,便于打开追踪归属',
+    recipient_cc    text         NULL COMMENT '抄送人,多个用逗号分隔',
+    recipient_bcc   text         NULL COMMENT '密送人,多个用逗号分隔',
+    content         mediumtext   NULL COMMENT '邮件正文(HTML/文本);体积大时可后续改为外置只存引用',
+    folder          varchar(32)  NULL COMMENT '文件夹:INBOX/SENT/SENTING/DRAFT',
+    msg_uid         varchar(128) NULL COMMENT '服务器邮件UID(IMAP/POP)',
+    recipient_date  datetime     NULL COMMENT '收信时间(服务器 ReceivedDate)',
+    sent_date       datetime     NULL COMMENT '邮件头 SentDate',
+    is_only_head    tinyint      NOT NULL DEFAULT 0 COMMENT '是否仅拉头:1是 0已有正文',
+    is_read         tinyint      NOT NULL DEFAULT 0 COMMENT '是否已读:0未读 1已读',
+    in_out_mark     tinyint      NULL COMMENT '进出标记:1收件 2发件',
+    email_size      bigint       NULL COMMENT '邮件大小(字节)',
+    in_reply_to     varchar(512) NULL COMMENT 'In-Reply-To 头,用于匹配回复原信',
+    reply_msg_id    bigint       NULL COMMENT '回复的原邮件ID,对应 mkt_mail_message.id',
+    status          tinyint      NOT NULL DEFAULT 0 COMMENT '投递状态:-2草稿;-1待定时发送;0发送中;1发送成功/已收妥;2发送失败',
+    fail_reason     varchar(512) NULL COMMENT '发送失败原因(异常信息摘要)',
+    is_track        tinyint      NOT NULL DEFAULT 0 COMMENT '是否开启打开追踪:0否;1是',
+    opened          tinyint      NOT NULL DEFAULT 0 COMMENT '是否已打开(像素回调):0未打开;1已打开;存在网关预取假阳性,仅为弱信号',
+    first_open_time datetime     NULL COMMENT '首次打开时间(首次有效像素回调时间)',
+    open_count      int          NOT NULL DEFAULT 0 COMMENT '打开次数累计(每次追踪回调+1,含重复打开/预取)',
+    sent_time       datetime     NULL COMMENT '实际SMTP发送成功时间',
+    delay_send_time datetime     NULL COMMENT '定时发送时间;未到点时status=-1',
+    owner_by        bigint       NULL COMMENT '数据归属人(操作/归属用户ID)',
+    create_by       bigint       NULL COMMENT '创建人',
+    update_by       bigint       NULL COMMENT '最后更新人',
+    create_time     datetime     NULL COMMENT '创建时间',
+    update_time     datetime     NULL COMMENT '最后更新时间',
+    is_delete       tinyint      NOT NULL DEFAULT 0 COMMENT '逻辑删除:0未删除;1已删除',
+    enabled         tinyint      NOT NULL DEFAULT 1 COMMENT '是否启用:1启用;0禁用',
+    sort            int          NULL COMMENT '排序号(预留,一般投递记录不用)',
+    PRIMARY KEY (id),
+    UNIQUE KEY uk_mkt_track_token (track_token),
+    KEY idx_mkt_biz_ref (biz_ref, status),
+    KEY idx_mkt_account_sent (account_id, sent_time),
+    KEY idx_mkt_org_sent (org_id, sent_time),
+    KEY idx_mkt_account_folder_recv (account_id, folder, recipient_date),
+    KEY idx_mkt_account_message_id (account_id, message_id),
+    KEY idx_mkt_reply_msg_id (reply_msg_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='营销邮件投递记录';
+
+-- 营销正式附件表(mail_id 指向 mkt_mail_message.id)
+CREATE TABLE mkt_mail_attachment (
+    id           bigint       NOT NULL AUTO_INCREMENT COMMENT '主键',
+    mail_id      bigint       NOT NULL COMMENT '营销邮件ID,对应 mkt_mail_message.id',
+    account_id   bigint       NULL COMMENT '发信账户ID,对应 smtp_pop_settings.id',
+    file_code    varchar(64)  NULL COMMENT '文件编码/随机码',
+    file_name    varchar(255) NULL COMMENT '文件名(不含扩展名)',
+    file_path    varchar(512) NULL COMMENT '相对存储路径',
+    file_size    bigint       NULL COMMENT '文件大小(字节)',
+    file_ext     varchar(32)  NULL COMMENT '扩展名',
+    owner_by     bigint       NULL COMMENT '数据归属人',
+    create_by    bigint       NULL COMMENT '创建人',
+    update_by    bigint       NULL COMMENT '最后更新人',
+    create_time  datetime     NULL COMMENT '创建时间',
+    update_time  datetime     NULL COMMENT '最后更新时间',
+    is_delete    tinyint      NOT NULL DEFAULT 0 COMMENT '逻辑删除:0未删除;1已删除',
+    enabled      tinyint      NOT NULL DEFAULT 1 COMMENT '是否启用:1启用;0禁用',
+    sort         int          NULL COMMENT '排序号',
+    PRIMARY KEY (id),
+    KEY idx_mkt_att_mail (mail_id),
+    KEY idx_mkt_att_account (account_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='营销邮件正式附件';

+ 2 - 1
storlead-mail/storlead-mail-dispatch/src/main/java/com/storlead/sales/mail/dispatch/MktEmailPullTaskJob.java

@@ -7,6 +7,7 @@ import com.storlead.sales.mail.common.service.SmtpPopSettingsService;
 import com.storlead.sales.mail.mkt.service.MktMailReceiveService;
 import lombok.NoArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 import org.springframework.util.CollectionUtils;
 
@@ -27,7 +28,7 @@ public class MktEmailPullTaskJob {
     @Resource
     private MktMailReceiveService mktMailReceiveService;
 
-    //@Scheduled(cron = "${storlead.mail.scheduler.mkt-pull-email-cron:0 */10 * * * ?}")
+    @Scheduled(cron = "0 */5 * * * ?")
     public void pullMktMailTask() {
         LambdaQueryWrapper<SmtpPopSettingsEntity> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(SmtpPopSettingsEntity::getIsDelete, 0);