diff --git a/continew-starter-core/src/main/java/top/continew/starter/core/constant/PropertiesConstants.java b/continew-starter-core/src/main/java/top/continew/starter/core/constant/PropertiesConstants.java index e297b018..6511f5ca 100644 --- a/continew-starter-core/src/main/java/top/continew/starter/core/constant/PropertiesConstants.java +++ b/continew-starter-core/src/main/java/top/continew/starter/core/constant/PropertiesConstants.java @@ -119,6 +119,11 @@ public class PropertiesConstants { */ public static final String MESSAGING_WEBSOCKET = MESSAGING + StringConstants.DOT + "websocket"; + /** + * 多租户配置 + */ + public static final String TENANT = CONTINEW_STARTER + StringConstants.DOT + "tenant"; + private PropertiesConstants() { } } diff --git a/continew-starter-dependencies/pom.xml b/continew-starter-dependencies/pom.xml index 04236572..ce1d332b 100644 --- a/continew-starter-dependencies/pom.xml +++ b/continew-starter-dependencies/pom.xml @@ -346,14 +346,12 @@ continew-starter-extension-crud-core ${revision} - top.continew continew-starter-extension-crud-mp ${revision} - top.continew @@ -361,6 +359,19 @@ ${revision} + + + top.continew + continew-starter-extension-tenant-core + ${revision} + + + + top.continew + continew-starter-extension-tenant-mp + ${revision} + + top.continew diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/pom.xml b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/pom.xml new file mode 100644 index 00000000..c67e6ae9 --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + top.continew + continew-starter-extension-tenant + ${revision} + + + continew-starter-extension-tenant-core + ContiNew Starter 扩展模块 - 多租户 - 核心模块 + + + + + com.alibaba + transmittable-thread-local + + + \ No newline at end of file diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantProperties.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantProperties.java new file mode 100644 index 00000000..0fdd9b41 --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantProperties.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.extension.tenant.autoconfigure; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import top.continew.starter.core.constant.PropertiesConstants; +import top.continew.starter.extension.tenant.enums.TenantIsolationLevel; + +import java.util.List; + +/** + * 租户配置属性 + * + * @author Charles7c + * @since 2.7.0 + */ +@ConfigurationProperties(PropertiesConstants.TENANT) +public class TenantProperties { + + /** + * 是否启用多租户 + */ + private boolean enabled = true; + + /** + * 租户隔离级别 + */ + private TenantIsolationLevel isolationLevel = TenantIsolationLevel.LINE; + + /** + * 租户 ID 列名 + */ + private String tenantIdColumn = "tenant_id"; + + /** + * 超级租户 ID + */ + private Long superTenantId = 1L; + + /** + * 忽略表(忽略拼接多租户条件) + */ + private List ignoreTables; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public TenantIsolationLevel getIsolationLevel() { + return isolationLevel; + } + + public void setIsolationLevel(TenantIsolationLevel isolationLevel) { + this.isolationLevel = isolationLevel; + } + + public String getTenantIdColumn() { + return tenantIdColumn; + } + + public void setTenantIdColumn(String tenantIdColumn) { + this.tenantIdColumn = tenantIdColumn; + } + + public Long getSuperTenantId() { + return superTenantId; + } + + public void setSuperTenantId(Long superTenantId) { + this.superTenantId = superTenantId; + } + + public List getIgnoreTables() { + return ignoreTables; + } + + public void setIgnoreTables(List ignoreTables) { + this.ignoreTables = ignoreTables; + } +} diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/context/TenantContext.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/context/TenantContext.java new file mode 100644 index 00000000..88704586 --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/context/TenantContext.java @@ -0,0 +1,23 @@ +package top.continew.starter.extension.tenant.context; + +/** + * 租户上下文 + * + * @author Charles7c + * @since 2.7.0 + */ +public class TenantContext { + + /** + * 租户 ID + */ + private Long tenantId; + + public Long getTenantId() { + return tenantId; + } + + public void setTenantId(Long tenantId) { + this.tenantId = tenantId; + } +} diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/context/TenantContextHolder.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/context/TenantContextHolder.java new file mode 100644 index 00000000..febbeb01 --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/context/TenantContextHolder.java @@ -0,0 +1,53 @@ +package top.continew.starter.extension.tenant.context; + +import com.alibaba.ttl.TransmittableThreadLocal; + +import java.util.Optional; + +/** + * 租户上下文 Holder + * + * @author Charles7c + * @since 2.7.0 + */ +public class TenantContextHolder { + + private static final TransmittableThreadLocal CONTEXT_HOLDER = new TransmittableThreadLocal<>(); + + private TenantContextHolder() { + } + + /** + * 设置上下文 + * + * @param context 上下文 + */ + public static void setContext(TenantContext context) { + CONTEXT_HOLDER.set(context); + } + + /** + * 获取上下文 + * + * @return 上下文 + */ + public static TenantContext getContext() { + return CONTEXT_HOLDER.get(); + } + + /** + * 清除上下文 + */ + public static void clearContext() { + CONTEXT_HOLDER.remove(); + } + + /** + * 获取租户 ID + * + * @return 租户 ID + */ + public static Long getTenantId() { + return Optional.ofNullable(getContext()).map(TenantContext::getTenantId).orElse(null); + } +} diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/enums/TenantIsolationLevel.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/enums/TenantIsolationLevel.java new file mode 100644 index 00000000..d4208fb4 --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/enums/TenantIsolationLevel.java @@ -0,0 +1,20 @@ +package top.continew.starter.extension.tenant.enums; + +/** + * 租户隔离级别 + * + * @author Charles7c + * @since 2.7.0 + */ +public enum TenantIsolationLevel { + + /** + * 行级 + */ + LINE, + + /** + * 数据源级 + */ + DATASOURCE +} diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/pom.xml b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/pom.xml new file mode 100644 index 00000000..1eee9b68 --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + top.continew + continew-starter-extension-tenant + ${revision} + + + continew-starter-extension-tenant-mp + ContiNew Starter 扩展模块 - 多租户 - MyBatis Plus ORM 模块 + + + + + top.continew + continew-starter-extension-tenant-core + + + + + com.baomidou + mybatis-plus-spring-boot3-starter + + + + + com.baomidou + dynamic-datasource-spring-boot3-starter + true + + + \ No newline at end of file diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantAutoConfiguration.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantAutoConfiguration.java new file mode 100644 index 00000000..c92ae968 --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantAutoConfiguration.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.extension.tenant.autoconfigure; + +import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; +import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import top.continew.starter.core.constant.PropertiesConstants; +import top.continew.starter.extension.tenant.handler.TenantLineHandlerImpl; + +/** + * 多租户自动配置 + * + * @author Charles7c + * @since 2.7.0 + */ +@AutoConfiguration +@EnableConfigurationProperties(TenantProperties.class) +@ConditionalOnProperty(prefix = PropertiesConstants.TENANT, name = PropertiesConstants.ENABLED, havingValue = "true", matchIfMissing = true) +public class TenantAutoConfiguration { + + private static final Logger log = LoggerFactory.getLogger(TenantAutoConfiguration.class); + + private TenantAutoConfiguration() { + } + + /** + * 租户隔离级别:行级 + */ + @AutoConfiguration + @ConditionalOnProperty(name = PropertiesConstants.TENANT + ".isolation-level", havingValue = "line", matchIfMissing = true) + public static class Line { + static { + log.debug("[ContiNew Starter] - Auto Configuration 'Tenant-Line' completed initialization."); + } + + /** + * 租户行级隔离拦截器 + */ + @Bean + @ConditionalOnMissingBean + public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantLineHandler tenantLineHandler) { + return new TenantLineInnerInterceptor(tenantLineHandler); + } + + /** + * 租户行级隔离处理器 + */ + @Bean + @ConditionalOnMissingBean + public TenantLineHandler tenantLineHandler(TenantProperties properties) { + return new TenantLineHandlerImpl(properties); + } + } +} diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/handler/TenantLineHandlerImpl.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/handler/TenantLineHandlerImpl.java new file mode 100644 index 00000000..55318b9a --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/handler/TenantLineHandlerImpl.java @@ -0,0 +1,46 @@ +package top.continew.starter.extension.tenant.handler; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.LongValue; +import top.continew.starter.extension.tenant.autoconfigure.TenantProperties; +import top.continew.starter.extension.tenant.context.TenantContextHolder; + +/** + * 租户行级隔离处理器 + * + * @author Charles7c + * @since 2.7.0 + */ +public class TenantLineHandlerImpl implements TenantLineHandler { + + private final TenantProperties tenantProperties; + + public TenantLineHandlerImpl(TenantProperties tenantProperties) { + this.tenantProperties = tenantProperties; + } + + @Override + public Expression getTenantId() { + Long tenantId = TenantContextHolder.getTenantId(); + if (null != tenantId) { + return new LongValue(tenantId); + } + return null; + } + + @Override + public String getTenantIdColumn() { + return tenantProperties.getTenantIdColumn(); + } + + @Override + public boolean ignoreTable(String tableName) { + Long tenantId = TenantContextHolder.getTenantId(); + if (null != tenantId && tenantId.equals(tenantProperties.getSuperTenantId())) { + return true; + } + return CollUtil.contains(tenantProperties.getIgnoreTables(), tableName); + } +} \ No newline at end of file diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..6c92a703 --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +top.continew.starter.extension.tenant.autoconfigure.TenantAutoConfiguration \ No newline at end of file diff --git a/continew-starter-extension/continew-starter-extension-tenant/pom.xml b/continew-starter-extension/continew-starter-extension-tenant/pom.xml new file mode 100644 index 00000000..ea63983f --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + top.continew + continew-starter-extension + ${revision} + + + continew-starter-extension-tenant + pom + ContiNew Starter 扩展模块 - 多租户 + + + continew-starter-extension-tenant-core + continew-starter-extension-tenant-mp + + \ No newline at end of file