refactor: 租户重构

This commit is contained in:
Ray.Hao
2025-12-11 08:18:01 +08:00
parent 36d2db6dc5
commit 47cabcbcfc
14 changed files with 789 additions and 44 deletions

View File

@@ -0,0 +1,76 @@
package com.youlai.boot.config;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.youlai.boot.config.property.TenantProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.List;
/**
* 多租户动态字段配置
* <p>
* 在多租户模式启用时,动态修改 BaseEntity 中 tenant_id 字段的 exist 属性为 true
* 这样可以实现:
* - 单租户模式tenant_id exist=false不映射该字段兼容没有该字段的表
* - 多租户模式tenant_id exist=true自动填充租户ID到INSERT/UPDATE语句
* </p>
*
* @author Ray.Hao
* @since 3.0.0
*/
@Slf4j
@Component
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "youlai.tenant", name = "enabled", havingValue = "true")
public class TenantDynamicFieldConfig implements InitializingBean {
private final TenantProperties tenantProperties;
@Override
public void afterPropertiesSet() {
log.info("多租户模式已启用,开始动态配置 tenant_id 字段映射...");
int modifiedCount = 0;
List<TableInfo> tableInfos = TableInfoHelper.getTableInfos();
for (TableInfo tableInfo : tableInfos) {
// 检查是否是忽略的表
String tableName = tableInfo.getTableName();
if (tenantProperties.getIgnoreTables().contains(tableName)) {
log.debug("表 {} 在忽略列表中,跳过 tenant_id 字段配置", tableName);
continue;
}
// 查找 tenant_id 字段
TableFieldInfo tenantField = tableInfo.getFieldList().stream()
.filter(field -> tenantProperties.getColumn().equals(field.getColumn()))
.findFirst()
.orElse(null);
if (tenantField != null) {
try {
// 通过反射修改 exist 属性为 true
Field existField = TableFieldInfo.class.getDeclaredField("exist");
existField.setAccessible(true);
existField.set(tenantField, true);
modifiedCount++;
log.debug("已为表 {} 启用 tenant_id 字段映射", tableName);
} catch (NoSuchFieldException | IllegalAccessException e) {
log.warn("修改表 {} 的 tenant_id 字段配置失败: {}", tableName, e.getMessage());
}
} else {
log.warn("表 {} 未找到 tenant_id 字段,请检查实体类是否继承 BaseEntity", tableName);
}
}
log.info("多租户字段配置完成,共修改 {} 张表", modifiedCount);
}
}

View File

@@ -7,17 +7,21 @@ import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.youlai.boot.core.interceptor.TenantValidationInterceptor;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.HibernateValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.validation.beanvalidation.SpringConstraintValidatorFactory;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.math.BigInteger;
@@ -39,6 +43,9 @@ public class WebMvcConfig implements WebMvcConfigurer {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Autowired(required = false)
private TenantValidationInterceptor tenantValidationInterceptor;
/**
* 配置消息转换器
*
@@ -78,6 +85,22 @@ public class WebMvcConfig implements WebMvcConfigurer {
* @param autowireCapableBeanFactory 用于注入 SpringConstraintValidatorFactory
* @return Validator 实例
*/
/**
* 配置拦截器
*
* @param registry 拦截器注册器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册租户校验拦截器(仅在多租户模式启用时生效)
if (tenantValidationInterceptor != null) {
registry.addInterceptor(tenantValidationInterceptor)
.addPathPatterns("/api/**")
.order(2); // 在认证拦截器之后执行
log.info("租户校验拦截器已注册");
}
}
@Bean
public Validator validator(final AutowireCapableBeanFactory autowireCapableBeanFactory) {
try (ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)