From 152781b05131e48bf6e94d71cc72dd54af52a3fb Mon Sep 17 00:00:00 2001
From: houzhongjian <houzhongyi@126.com>
Date: 星期四, 10 四月 2025 14:13:29 +0800
Subject: [PATCH] 恢复iailab-framework

---
 iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/dynamic/TenantDsProcessor.java |  106 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 106 insertions(+), 0 deletions(-)

diff --git a/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/dynamic/TenantDsProcessor.java b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/dynamic/TenantDsProcessor.java
new file mode 100644
index 0000000..24b2eff
--- /dev/null
+++ b/iailab-framework/iailab-common-biz-tenant/src/main/java/com/iailab/framework/tenant/core/db/dynamic/TenantDsProcessor.java
@@ -0,0 +1,106 @@
+package com.iailab.framework.tenant.core.db.dynamic;
+
+import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
+import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
+import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
+import com.baomidou.dynamic.datasource.processor.DsProcessor;
+import com.iailab.framework.tenant.core.context.DataContextHolder;
+import com.iailab.framework.tenant.core.context.TenantContextHolder;
+import com.iailab.framework.tenant.core.service.TenantFrameworkService;
+import com.iailab.module.infra.api.db.DataSourceConfigServiceApi;
+import com.iailab.module.infra.api.db.dto.DataSourceConfigRespDTO;
+import lombok.RequiredArgsConstructor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.context.annotation.Lazy;
+
+import javax.annotation.Resource;
+import javax.sql.DataSource;
+import java.util.Objects;
+
+/**
+ * 基于 {@link TenantDS} 的数据源处理器
+ *
+ * 1. 如果有 @TenantDS 注解,返回该租户的数据源
+ * 2. 如果该租户的数据源未创建,则进行创建
+ *
+ * @author 芋道源码
+ */
+@RequiredArgsConstructor
+public class TenantDsProcessor extends DsProcessor {
+
+    /**
+     * 用于获取租户数据源配置的 Service
+     */
+    @Resource
+    @Lazy
+    private TenantFrameworkService tenantFrameworkService;
+
+    /**
+     * 动态数据源
+     */
+    @Resource
+    @Lazy  // 为什么添加 @Lazy 注解?因为它和 DynamicRoutingDataSource 相互依赖,导致无法初始化
+    private DynamicRoutingDataSource dynamicRoutingDataSource;
+
+    /**
+     * 用于创建租户数据源的 Creator
+     */
+    @Resource
+    @Lazy
+    private DefaultDataSourceCreator dataSourceCreator;
+
+    @Resource
+    @Lazy
+    private DataSourceConfigServiceApi dataSourceConfigServiceApi;
+
+    @Override
+    public boolean matches(String key) {
+        return Objects.equals(key, TenantDS.KEY) || Objects.equals(key, DataDS.KEY);
+    }
+
+    @Override
+    public String doDetermineDatasource(MethodInvocation invocation, String key) {
+        if (DataDS.KEY.equals(key)){
+            // 获得数据源配置
+            Long dataSourceId = DataContextHolder.getRequiredDataSourceId();
+            DataSourceConfigRespDTO dataSourceConfigRespDTO = dataSourceConfigServiceApi.getDataSourceConfig(dataSourceId);
+            DataSourceProperty dataSourceProperty = new DataSourceProperty();
+            dataSourceProperty.setPoolName(dataSourceConfigRespDTO.getName());
+            dataSourceProperty.setUrl(dataSourceConfigRespDTO.getUrl());
+            dataSourceProperty.setUsername(dataSourceConfigRespDTO.getUsername());
+            dataSourceProperty.setPassword(dataSourceConfigRespDTO.getPassword());
+            // 创建 or 创建数据源,并返回数据源名字
+            return createDatasourceIfAbsent(dataSourceProperty);
+        }else if(TenantDS.KEY.equals(key)){
+            // 获得数据源配置
+            Long tenantId = TenantContextHolder.getRequiredTenantId();
+            DataSourceProperty dataSourceProperty = tenantFrameworkService.getDataSourceProperty(tenantId);
+            // 创建 or 创建数据源,并返回数据源名字
+            return createDatasourceIfAbsent(dataSourceProperty);
+        }
+        return key;
+    }
+
+    private String createDatasourceIfAbsent(DataSourceProperty dataSourceProperty) {
+        // 1. 重点:如果数据源不存在,则进行创建
+        if (isDataSourceNotExist(dataSourceProperty)) {
+            // 问题一:为什么要加锁?因为,如果多个线程同时执行到这里,会导致多次创建数据源
+            // 问题二:为什么要使用 poolName 加锁?保证多个不同的 poolName 可以并发创建数据源
+            // 问题三:为什么要使用 intern 方法?因为,intern 方法,会返回一个字符串的常量池中的引用
+            // intern 的说明,可见 https://www.cnblogs.com/xrq730/p/6662232.html 文章
+            synchronized (dataSourceProperty.getPoolName().intern()) {
+                if (isDataSourceNotExist(dataSourceProperty)) {
+                    DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
+                    dynamicRoutingDataSource.addDataSource(dataSourceProperty.getPoolName(), dataSource);
+                }
+            }
+        }
+        // 2. 返回数据源的名字
+        return dataSourceProperty.getPoolName();
+    }
+
+    private boolean isDataSourceNotExist(DataSourceProperty dataSourceProperty) {
+        return !dynamicRoutingDataSource.getDataSources().containsKey(dataSourceProperty.getPoolName());
+    }
+
+}

--
Gitblit v1.9.3