|
@@ -0,0 +1,218 @@
|
|
|
|
|
+package com.fs.framework.datasource;
|
|
|
|
|
+
|
|
|
|
|
+import com.fs.common.config.RedisTenantContext;
|
|
|
|
|
+import com.fs.tenant.domain.TenantInfo;
|
|
|
|
|
+import com.fs.tenant.service.TenantInfoService;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
|
+
|
|
|
|
|
+import javax.annotation.Resource;
|
|
|
|
|
+import java.util.function.Consumer;
|
|
|
|
|
+import java.util.function.Function;
|
|
|
|
|
+import java.util.function.Supplier;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 租户数据源切换工具类
|
|
|
|
|
+ * 用于在指定租户数据源下执行增删改查操作
|
|
|
|
|
+ */
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+@Component
|
|
|
|
|
+public class TenantDataSourceUtil {
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private TenantDataSourceManager tenantDataSourceManager;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private TenantInfoService tenantInfoService;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 在指定租户数据源下执行操作(无返回值)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param tenantId 租户ID
|
|
|
|
|
+ * @param action 要执行的操作
|
|
|
|
|
+ */
|
|
|
|
|
+ public void execute(Long tenantId, Runnable action) {
|
|
|
|
|
+ TenantInfo tenantInfo = getTenantInfo(tenantId);
|
|
|
|
|
+ if (tenantInfo == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("租户不存在或已禁用,tenantId=" + tenantId);
|
|
|
|
|
+ }
|
|
|
|
|
+ execute(tenantInfo, action);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 在指定租户数据源下执行操作(无返回值)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param tenantInfo 租户信息
|
|
|
|
|
+ * @param action 要执行的操作
|
|
|
|
|
+ */
|
|
|
|
|
+ public void execute(TenantInfo tenantInfo, Runnable action) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 切换到租户数据源
|
|
|
|
|
+ tenantDataSourceManager.switchTenant(tenantInfo);
|
|
|
|
|
+ // 切换Redis租户上下文
|
|
|
|
|
+ RedisTenantContext.setTenantId(tenantInfo.getId());
|
|
|
|
|
+ log.debug("[TenantDS] 已切换到租户数据源和Redis: tenantId={}, tenantCode={}",
|
|
|
|
|
+ tenantInfo.getId(), tenantInfo.getTenantCode());
|
|
|
|
|
+
|
|
|
|
|
+ // 执行操作
|
|
|
|
|
+ action.run();
|
|
|
|
|
+
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ // 清理数据源上下文
|
|
|
|
|
+ tenantDataSourceManager.clear();
|
|
|
|
|
+ // 清理Redis租户上下文
|
|
|
|
|
+ RedisTenantContext.clear();
|
|
|
|
|
+ log.debug("[TenantDS] 已清理租户数据源和Redis上下文");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 在指定租户数据源下执行操作(带返回值)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param tenantId 租户ID
|
|
|
|
|
+ * @param action 要执行的操作
|
|
|
|
|
+ * @param <T> 返回值类型
|
|
|
|
|
+ * @return 操作结果
|
|
|
|
|
+ */
|
|
|
|
|
+ public <T> T executeWithResult(Long tenantId, Supplier<T> action) {
|
|
|
|
|
+ TenantInfo tenantInfo = getTenantInfo(tenantId);
|
|
|
|
|
+ if (tenantInfo == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("租户不存在或已禁用,tenantId=" + tenantId);
|
|
|
|
|
+ }
|
|
|
|
|
+ return executeWithResult(tenantInfo, action);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 在指定租户数据源下执行操作(带返回值)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param tenantInfo 租户信息
|
|
|
|
|
+ * @param action 要执行的操作
|
|
|
|
|
+ * @param <T> 返回值类型
|
|
|
|
|
+ * @return 操作结果
|
|
|
|
|
+ */
|
|
|
|
|
+ public <T> T executeWithResult(TenantInfo tenantInfo, Supplier<T> action) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 切换到租户数据源
|
|
|
|
|
+ tenantDataSourceManager.switchTenant(tenantInfo);
|
|
|
|
|
+ // 切换Redis租户上下文
|
|
|
|
|
+ RedisTenantContext.setTenantId(tenantInfo.getId());
|
|
|
|
|
+ log.debug("[TenantDS] 已切换到租户数据源和Redis: tenantId={}, tenantCode={}",
|
|
|
|
|
+ tenantInfo.getId(), tenantInfo.getTenantCode());
|
|
|
|
|
+
|
|
|
|
|
+ // 执行操作并返回结果
|
|
|
|
|
+ return action.get();
|
|
|
|
|
+
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ // 清理数据源上下文
|
|
|
|
|
+ tenantDataSourceManager.clear();
|
|
|
|
|
+ // 清理Redis租户上下文
|
|
|
|
|
+ RedisTenantContext.clear();
|
|
|
|
|
+ log.debug("[TenantDS] 已清理租户数据源和Redis上下文");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 在指定租户数据源下执行操作(消费租户信息,无返回值)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param tenantId 租户ID
|
|
|
|
|
+ * @param action 要执行的操作,接收租户信息作为参数
|
|
|
|
|
+ */
|
|
|
|
|
+ public void executeWithTenant(Long tenantId, Consumer<TenantInfo> action) {
|
|
|
|
|
+ TenantInfo tenantInfo = getTenantInfo(tenantId);
|
|
|
|
|
+ if (tenantInfo == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("租户不存在或已禁用,tenantId=" + tenantId);
|
|
|
|
|
+ }
|
|
|
|
|
+ executeWithTenant(tenantInfo, action);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 在指定租户数据源下执行操作(消费租户信息,无返回值)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param tenantInfo 租户信息
|
|
|
|
|
+ * @param action 要执行的操作,接收租户信息作为参数
|
|
|
|
|
+ */
|
|
|
|
|
+ public void executeWithTenant(TenantInfo tenantInfo, Consumer<TenantInfo> action) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 切换到租户数据源
|
|
|
|
|
+ tenantDataSourceManager.switchTenant(tenantInfo);
|
|
|
|
|
+ // 切换Redis租户上下文
|
|
|
|
|
+ RedisTenantContext.setTenantId(tenantInfo.getId());
|
|
|
|
|
+ log.debug("[TenantDS] 已切换到租户数据源和Redis: tenantId={}, tenantCode={}",
|
|
|
|
|
+ tenantInfo.getId(), tenantInfo.getTenantCode());
|
|
|
|
|
+
|
|
|
|
|
+ // 执行操作
|
|
|
|
|
+ action.accept(tenantInfo);
|
|
|
|
|
+
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ // 清理数据源上下文
|
|
|
|
|
+ tenantDataSourceManager.clear();
|
|
|
|
|
+ // 清理Redis租户上下文
|
|
|
|
|
+ RedisTenantContext.clear();
|
|
|
|
|
+ log.debug("[TenantDS] 已清理租户数据源和Redis上下文");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 在指定租户数据源下执行操作(消费租户信息,带返回值)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param tenantId 租户ID
|
|
|
|
|
+ * @param action 要执行的操作,接收租户信息并返回结果
|
|
|
|
|
+ * @param <R> 返回值类型
|
|
|
|
|
+ * @return 操作结果
|
|
|
|
|
+ */
|
|
|
|
|
+ public <R> R executeWithTenantAndResult(Long tenantId, Function<TenantInfo, R> action) {
|
|
|
|
|
+ TenantInfo tenantInfo = getTenantInfo(tenantId);
|
|
|
|
|
+ if (tenantInfo == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("租户不存在或已禁用,tenantId=" + tenantId);
|
|
|
|
|
+ }
|
|
|
|
|
+ return executeWithTenantAndResult(tenantInfo, action);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 在指定租户数据源下执行操作(消费租户信息,带返回值)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param tenantInfo 租户信息
|
|
|
|
|
+ * @param action 要执行的操作,接收租户信息并返回结果
|
|
|
|
|
+ * @param <R> 返回值类型
|
|
|
|
|
+ * @return 操作结果
|
|
|
|
|
+ */
|
|
|
|
|
+ public <R> R executeWithTenantAndResult(TenantInfo tenantInfo, Function<TenantInfo, R> action) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 切换到租户数据源
|
|
|
|
|
+ tenantDataSourceManager.switchTenant(tenantInfo);
|
|
|
|
|
+ // 切换Redis租户上下文
|
|
|
|
|
+ RedisTenantContext.setTenantId(tenantInfo.getId());
|
|
|
|
|
+ log.debug("[TenantDS] 已切换到租户数据源和Redis: tenantId={}, tenantCode={}",
|
|
|
|
|
+ tenantInfo.getId(), tenantInfo.getTenantCode());
|
|
|
|
|
+
|
|
|
|
|
+ // 执行操作并返回结果
|
|
|
|
|
+ return action.apply(tenantInfo);
|
|
|
|
|
+
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ // 清理数据源上下文
|
|
|
|
|
+ tenantDataSourceManager.clear();
|
|
|
|
|
+ // 清理Redis租户上下文
|
|
|
|
|
+ RedisTenantContext.clear();
|
|
|
|
|
+ log.debug("[TenantDS] 已清理租户数据源和Redis上下文");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取租户信息
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param tenantId 租户ID
|
|
|
|
|
+ * @return 租户信息,不存在或已禁用则返回null
|
|
|
|
|
+ */
|
|
|
|
|
+ private TenantInfo getTenantInfo(Long tenantId) {
|
|
|
|
|
+ TenantInfo tenantInfo = tenantInfoService.getById(tenantId);
|
|
|
|
|
+ if (tenantInfo == null) {
|
|
|
|
|
+ log.warn("[TenantDS] 租户不存在,tenantId={}", tenantId);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!Integer.valueOf(1).equals(tenantInfo.getStatus())) {
|
|
|
|
|
+ log.warn("[TenantDS] 租户已禁用,tenantId={}", tenantId);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ return tenantInfo;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|