mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-09-27 08:57:14 +08:00
feat(extension/tenant): 新增 continew-starter-extension-tenant 多租户模块(暂时仅支持行级隔离)
This commit is contained in:
@@ -119,6 +119,11 @@ public class PropertiesConstants {
|
|||||||
*/
|
*/
|
||||||
public static final String MESSAGING_WEBSOCKET = MESSAGING + StringConstants.DOT + "websocket";
|
public static final String MESSAGING_WEBSOCKET = MESSAGING + StringConstants.DOT + "websocket";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多租户配置
|
||||||
|
*/
|
||||||
|
public static final String TENANT = CONTINEW_STARTER + StringConstants.DOT + "tenant";
|
||||||
|
|
||||||
private PropertiesConstants() {
|
private PropertiesConstants() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -346,14 +346,12 @@
|
|||||||
<artifactId>continew-starter-extension-crud-core</artifactId>
|
<artifactId>continew-starter-extension-crud-core</artifactId>
|
||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 扩展模块 - CRUD - MyBatis Plus ORM 模块 -->
|
<!-- 扩展模块 - CRUD - MyBatis Plus ORM 模块 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>top.continew</groupId>
|
<groupId>top.continew</groupId>
|
||||||
<artifactId>continew-starter-extension-crud-mp</artifactId>
|
<artifactId>continew-starter-extension-crud-mp</artifactId>
|
||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 扩展模块 - CRUD - MyBatis Flex ORM 模块 -->
|
<!-- 扩展模块 - CRUD - MyBatis Flex ORM 模块 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>top.continew</groupId>
|
<groupId>top.continew</groupId>
|
||||||
@@ -361,6 +359,19 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 扩展模块 - 多租户 - 核心模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter-extension-tenant-core</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- 扩展模块 - 多租户 - MyBatis Plus ORM 模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter-extension-tenant-mp</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- 认证模块 - JustAuth -->
|
<!-- 认证模块 - JustAuth -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>top.continew</groupId>
|
<groupId>top.continew</groupId>
|
||||||
|
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter-extension-tenant</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>continew-starter-extension-tenant-core</artifactId>
|
||||||
|
<description>ContiNew Starter 扩展模块 - 多租户 - 核心模块</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- TTL(线程间传递 ThreadLocal,异步执行时上下文传递的解决方案) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>transmittable-thread-local</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* 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<String> 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<String> getIgnoreTables() {
|
||||||
|
return ignoreTables;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIgnoreTables(List<String> ignoreTables) {
|
||||||
|
this.ignoreTables = ignoreTables;
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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<TenantContext> 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);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,20 @@
|
|||||||
|
package top.continew.starter.extension.tenant.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 租户隔离级别
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 2.7.0
|
||||||
|
*/
|
||||||
|
public enum TenantIsolationLevel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 行级
|
||||||
|
*/
|
||||||
|
LINE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据源级
|
||||||
|
*/
|
||||||
|
DATASOURCE
|
||||||
|
}
|
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter-extension-tenant</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>continew-starter-extension-tenant-mp</artifactId>
|
||||||
|
<description>ContiNew Starter 扩展模块 - 多租户 - MyBatis Plus ORM 模块</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- 核心模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter-extension-tenant-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MyBatis Plus(MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,简化开发、提高效率) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Dynamic Datasource(基于 Spring Boot 的快速集成多数据源的启动器) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1 @@
|
|||||||
|
top.continew.starter.extension.tenant.autoconfigure.TenantAutoConfiguration
|
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter-extension</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>continew-starter-extension-tenant</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<description>ContiNew Starter 扩展模块 - 多租户</description>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>continew-starter-extension-tenant-core</module>
|
||||||
|
<module>continew-starter-extension-tenant-mp</module>
|
||||||
|
</modules>
|
||||||
|
</project>
|
Reference in New Issue
Block a user