feat(plugin/tenant): 新增多租户插件模块 (#175)

This commit is contained in:
小熊
2025-07-10 20:38:59 +08:00
committed by GitHub
parent 72493f8161
commit ed6dd65a51
70 changed files with 3539 additions and 65 deletions

View File

@@ -23,6 +23,7 @@ import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
@@ -41,6 +42,8 @@ import top.continew.admin.system.service.UserService;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.util.ExceptionUtils;
import top.continew.starter.core.util.StrUtils;
import top.continew.starter.extension.tenant.TenantHandler;
import top.continew.starter.extension.tenant.context.TenantContextHolder;
import top.continew.starter.log.dao.LogDao;
import top.continew.starter.log.model.LogRecord;
import top.continew.starter.log.model.LogRequest;
@@ -85,7 +88,10 @@ public class LogDaoLocalImpl implements LogDao {
logDO.setCreateTime(LocalDateTime.ofInstant(logRecord.getTimestamp(), ZoneId.systemDefault()));
// 设置操作人
this.setCreateUser(logDO, logRequest, logResponse);
logMapper.insert(logDO);
Long tenantId = TenantContextHolder.getTenantId();
SpringUtil.getBean(TenantHandler.class).execute(tenantId, () -> {
logMapper.insert(logDO);
});
}
/**

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.admin.config.tenant;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import top.continew.admin.common.constant.SysConstants;
import top.continew.starter.core.constant.PropertiesConstants;
import top.continew.starter.extension.tenant.context.TenantContextHolder;
import top.continew.starter.extension.tenant.enums.TenantIsolationLevel;
/**
* @description: 主数据源切面
* @author: 小熊
* @create: 2025-01-15 16:02
*/
@Aspect
@Component
@ConditionalOnProperty(prefix = PropertiesConstants.TENANT, name = PropertiesConstants.ENABLED, havingValue = "true")
public class DataSourceSwitchAspect {
@Pointcut("execution(* top.continew.admin.tenant.mapper..*(..)) || " + "execution(* top.continew.admin.tenant.service..*(..)) || " + "execution(* top.continew.admin.system.mapper.ClientMapper.*(..)) || " + "execution(* top.continew.admin.system.service.ClientService.*(..)) || " + "execution(* top.continew.admin.system.mapper.DictMapper.*(..)) || " + "execution(* top.continew.admin.system.service.DictService.*(..)) || " + "execution(* top.continew.admin.system.mapper.DictItemMapper.*(..)) || " + "execution(* top.continew.admin.system.service.DictItemService.*(..)) || " + "execution(* top.continew.admin.system.mapper.OptionMapper.*(..)) || " + "execution(* top.continew.admin.system.service.OptionService.*(..)) || " + "execution(* top.continew.admin.system.mapper.StorageMapper.*(..)) || " + "execution(* top.continew.admin.system.service.StorageService.*(..))")
public void MasterDataSourceMethods() {
}
@Before("MasterDataSourceMethods()")
public void switchToMasterDataSource() {
if (TenantContextHolder.getIsolationLevel() == TenantIsolationLevel.DATASOURCE) {
DynamicDataSourceContextHolder.push(SysConstants.DEFAULT_DATASOURCE);
}
}
@After("MasterDataSourceMethods()")
public void clearDataSourceContext() {
if (TenantContextHolder.getIsolationLevel() == TenantIsolationLevel.DATASOURCE) {
DynamicDataSourceContextHolder.poll();
}
}
}