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

1、调整重启后返回主库的信息了

yys 2 месяцев назад
Родитель
Сommit
e3d7156b61

+ 46 - 0
fs-company/src/main/java/com/fs/framework/datasource/TenantDataSourceManager.java

@@ -1,7 +1,12 @@
 package com.fs.framework.datasource;
 
 import com.alibaba.druid.pool.DruidDataSource;
+import com.fs.common.enums.DataSourceType;
+import com.fs.framework.datasource.DynamicDataSourceContextHolder;
 import com.fs.tenant.domain.TenantInfo;
+import com.fs.tenant.service.TenantInfoService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
 
 import javax.annotation.Resource;
@@ -13,9 +18,14 @@ import java.util.concurrent.ConcurrentHashMap;
 @Component
 public class TenantDataSourceManager {
 
+    private static final Logger log = LoggerFactory.getLogger(TenantDataSourceManager.class);
+
     @Resource
     private DynamicDataSource dynamicDataSource;
 
+    @Resource
+    private TenantInfoService tenantInfoService;
+
     /**
      * 租户数据源缓存
      */
@@ -60,6 +70,42 @@ public class TenantDataSourceManager {
         DynamicDataSourceContextHolder.clearDataSourceType();
     }
 
+    /**
+     * 根据租户ID确保数据源已注册并切换(用于 Filter/拦截器等非登录场景)
+     * 解决 JVM 重启后 TENANT_DS_CACHE 被清空,导致 resolvedDataSources 中找不到租户数据源的问题
+     *
+     * @param tenantId 租户ID
+     */
+    public void ensureSwitchByTenantId(Long tenantId) {
+        String tenantKey = buildTenantKey(tenantId);
+
+        if (TENANT_DS_CACHE.containsKey(tenantKey)) {
+            DynamicDataSourceContextHolder.setDataSourceType(tenantKey);
+            log.debug("[TenantDS] 数据源已缓存,直接切换: {}", tenantKey);
+            return;
+        }
+
+        DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
+        try {
+            TenantInfo tenantInfo = tenantInfoService.getById(tenantId);
+            if (tenantInfo == null) {
+                log.warn("[TenantDS] 租户ID={} 在主库中不存在,回退到主库", tenantId);
+                DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
+                return;
+            }
+            if (!tenantInfo.getStatus().equals(1)) {
+                log.warn("[TenantDS] 租户ID={} 已禁用,回退到主库", tenantId);
+                DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
+                return;
+            }
+            switchTenant(tenantInfo);
+            log.info("[TenantDS] 动态注册并切换数据源: key={}, url={}", tenantKey, tenantInfo.getDbUrl());
+        } catch (Exception e) {
+            log.error("[TenantDS] 动态注册租户数据源失败, tenantId={}, 回退到主库", tenantId, e);
+            DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
+        }
+    }
+
     /**
      * 创建租户数据源(MySQL + Druid)
      */

+ 5 - 3
fs-company/src/main/java/com/fs/framework/security/filter/JwtAuthenticationTokenFilter.java

@@ -9,6 +9,7 @@ import com.fs.config.saas.ProjectConfig;
 import com.fs.core.config.TenantConfigContext;
 import com.fs.framework.datasource.DynamicDataSource;
 import com.fs.framework.datasource.DynamicDataSourceContextHolder;
+import com.fs.framework.datasource.TenantDataSourceManager;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.security.SecurityUtils;
 import com.fs.framework.service.TokenService;
@@ -44,6 +45,8 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
     private DynamicDataSource dynamicDataSource;
     @Autowired
     private SysConfigMapper sysConfigMapper;
+    @Autowired
+    private TenantDataSourceManager tenantDataSourceManager;
 
     @Override
     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
@@ -53,11 +56,10 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
             LoginUser loginUser = tokenService.getLoginUser(request);
             if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication()))
             {
-                // 根据 tenantId 切换数据源
+                // 根据 tenantId 切换数据源(先确保数据源已注册到 DynamicDataSource)
                 Long tenantId = loginUser.getTenantId();
                 if (tenantId != null) {
-                    String tenantKey = "tenant:" + tenantId;
-                    DynamicDataSourceContextHolder.setDataSourceType(tenantKey);
+                    tenantDataSourceManager.ensureSwitchByTenantId(tenantId);
                 } else {
                     // 没有租户,回主库
                     DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());

+ 50 - 0
fs-framework/src/main/java/com/fs/framework/datasource/TenantDataSourceManager.java

@@ -1,7 +1,12 @@
 package com.fs.framework.datasource;
 
 import com.alibaba.druid.pool.DruidDataSource;
+import com.fs.common.enums.DataSourceType;
+import com.fs.framework.datasource.DynamicDataSourceContextHolder;
 import com.fs.tenant.domain.TenantInfo;
+import com.fs.tenant.service.TenantInfoService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
 
 import javax.annotation.Resource;
@@ -13,9 +18,14 @@ import java.util.concurrent.ConcurrentHashMap;
 @Component
 public class TenantDataSourceManager {
 
+    private static final Logger log = LoggerFactory.getLogger(TenantDataSourceManager.class);
+
     @Resource
     private DynamicDataSource dynamicDataSource;
 
+    @Resource
+    private TenantInfoService tenantInfoService;
+
     /**
      * 租户数据源缓存
      */
@@ -60,6 +70,46 @@ public class TenantDataSourceManager {
         DynamicDataSourceContextHolder.clearDataSourceType();
     }
 
+    /**
+     * 根据租户ID确保数据源已注册并切换(用于 Filter/拦截器等非登录场景)
+     * 解决 JVM 重启后 TENANT_DS_CACHE 被清空,导致 resolvedDataSources 中找不到租户数据源的问题
+     *
+     * @param tenantId 租户ID
+     */
+    public void ensureSwitchByTenantId(Long tenantId) {
+        String tenantKey = buildTenantKey(tenantId);
+
+        // 如果缓存中已有,直接切库
+        if (TENANT_DS_CACHE.containsKey(tenantKey)) {
+            DynamicDataSourceContextHolder.setDataSourceType(tenantKey);
+            log.debug("[TenantDS] 数据源已缓存,直接切换: {}", tenantKey);
+            return;
+        }
+
+        // 缓存中没有,需要从主库查租户信息并注册数据源
+        // 先临时切到主库
+        DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
+        try {
+            TenantInfo tenantInfo = tenantInfoService.getById(tenantId);
+            if (tenantInfo == null) {
+                log.warn("[TenantDS] 租户ID={} 在主库中不存在,回退到主库", tenantId);
+                DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
+                return;
+            }
+            if (!tenantInfo.getStatus().equals(1)) {
+                log.warn("[TenantDS] 租户ID={} 已禁用,回退到主库", tenantId);
+                DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
+                return;
+            }
+            // 注册并切换
+            switchTenant(tenantInfo);
+            log.info("[TenantDS] 动态注册并切换数据源: key={}, url={}", tenantKey, tenantInfo.getDbUrl());
+        } catch (Exception e) {
+            log.error("[TenantDS] 动态注册租户数据源失败, tenantId={}, 回退到主库", tenantId, e);
+            DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
+        }
+    }
+
     /**
      * 创建租户数据源(MySQL + Druid)
      */

+ 5 - 3
fs-framework/src/main/java/com/fs/framework/security/filter/JwtAuthenticationTokenFilter.java

@@ -9,6 +9,7 @@ import com.fs.config.saas.ProjectConfig;
 import com.fs.core.config.TenantConfigContext;
 import com.fs.framework.datasource.DynamicDataSource;
 import com.fs.framework.datasource.DynamicDataSourceContextHolder;
+import com.fs.framework.datasource.TenantDataSourceManager;
 import com.fs.framework.web.service.TokenService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
@@ -40,6 +41,8 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
     private DynamicDataSource dynamicDataSource;
     @Autowired
     private SysConfigMapper sysConfigMapper;
+    @Autowired
+    private TenantDataSourceManager tenantDataSourceManager;
 
 
     @Override
@@ -51,11 +54,10 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
             if (StringUtils.isNotNull(loginUser)
                     && StringUtils.isNull(SecurityUtils.getAuthentication()))
             {
-                // 根据 tenantId 切换数据源
+                // 根据 tenantId 切换数据源(先确保数据源已注册到 DynamicDataSource)
                 Long tenantId = loginUser.getTenantId();
                 if (tenantId != null) {
-                    String tenantKey = "tenant:" + tenantId;
-                    DynamicDataSourceContextHolder.setDataSourceType(tenantKey);
+                    tenantDataSourceManager.ensureSwitchByTenantId(tenantId);
                 } else {
                     // 没有租户,回主库
                     DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());