mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-11-04 12:18:42 +08:00 
			
		
		
		
	基于 Guava 实现 tenant 租户数据的本地缓存
This commit is contained in:
		@@ -1,29 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.mq.consumer.tenant;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
 | 
			
		||||
import cn.iocoder.yudao.module.system.mq.message.tenant.TenantRefreshMessage;
 | 
			
		||||
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 针对 {@link cn.iocoder.yudao.module.system.mq.message.tenant.TenantRefreshMessage} 的消费者
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@Component
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class TenantRefreshConsumer extends AbstractChannelMessageListener<TenantRefreshMessage> {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private TenantService tenantService;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onMessage(TenantRefreshMessage message) {
 | 
			
		||||
        log.info("[onMessage][收到 Tenant 刷新消息]");
 | 
			
		||||
        tenantService.initLocalCache();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.mq.message.tenant;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 租户数据刷新 Message
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
public class TenantRefreshMessage extends AbstractChannelMessage {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getChannel() {
 | 
			
		||||
        return "system.tenant.refresh";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.mq.producer.tenant;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
 | 
			
		||||
import cn.iocoder.yudao.module.system.mq.message.permission.RoleRefreshMessage;
 | 
			
		||||
import cn.iocoder.yudao.module.system.mq.message.tenant.TenantRefreshMessage;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tenant 租户相关消息的 Producer
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@Component
 | 
			
		||||
public class TenantProducer {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private RedisMQTemplate redisMQTemplate;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 发送 {@link RoleRefreshMessage} 消息
 | 
			
		||||
     */
 | 
			
		||||
    public void sendTenantRefreshMessage() {
 | 
			
		||||
        TenantRefreshMessage message = new TenantRefreshMessage();
 | 
			
		||||
        redisMQTemplate.send(message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.system.service.tenant;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
 | 
			
		||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
 | 
			
		||||
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
 | 
			
		||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantCreateReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantExportReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
 | 
			
		||||
@@ -20,12 +19,7 @@ import java.util.Set;
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
public interface TenantService extends TenantFrameworkService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 初始化租户的本地缓存
 | 
			
		||||
     */
 | 
			
		||||
    void initLocalCache();
 | 
			
		||||
public interface TenantService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 创建租户
 | 
			
		||||
 
 | 
			
		||||
@@ -23,31 +23,25 @@ import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper;
 | 
			
		||||
import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
 | 
			
		||||
import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
 | 
			
		||||
import cn.iocoder.yudao.module.system.mq.producer.tenant.TenantProducer;
 | 
			
		||||
import cn.iocoder.yudao.module.system.service.permission.MenuService;
 | 
			
		||||
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
 | 
			
		||||
import cn.iocoder.yudao.module.system.service.permission.RoleService;
 | 
			
		||||
import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler;
 | 
			
		||||
import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler;
 | 
			
		||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.context.annotation.Lazy;
 | 
			
		||||
import org.springframework.scheduling.annotation.Scheduled;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.transaction.annotation.Transactional;
 | 
			
		||||
import org.springframework.transaction.support.TransactionSynchronization;
 | 
			
		||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.PostConstruct;
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertImmutableMap;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
 | 
			
		||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
 | 
			
		||||
import static java.util.Collections.singleton;
 | 
			
		||||
 | 
			
		||||
@@ -61,26 +55,6 @@ import static java.util.Collections.singleton;
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class TenantServiceImpl implements TenantService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
 | 
			
		||||
     * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
 | 
			
		||||
     */
 | 
			
		||||
    private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 角色缓存
 | 
			
		||||
     * key:角色编号 {@link RoleDO#getId()}
 | 
			
		||||
     *
 | 
			
		||||
     * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
 | 
			
		||||
     */
 | 
			
		||||
    @Getter
 | 
			
		||||
    private volatile Map<Long, TenantDO> tenantCache;
 | 
			
		||||
    /**
 | 
			
		||||
     * 缓存角色的最大更新时间,用于后续的增量轮询,判断是否有更新
 | 
			
		||||
     */
 | 
			
		||||
    @Getter
 | 
			
		||||
    private volatile Date maxUpdateTime;
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
 | 
			
		||||
    @Autowired(required = false) // 由于 yudao.tenant.enable 配置项,可以关闭多租户的功能,所以这里只能不强制注入
 | 
			
		||||
    private TenantProperties tenantProperties;
 | 
			
		||||
@@ -100,61 +74,15 @@ public class TenantServiceImpl implements TenantService {
 | 
			
		||||
    @Resource
 | 
			
		||||
    private PermissionService permissionService;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private TenantProducer tenantProducer;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 初始化 {@link #tenantCache} 缓存
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    @PostConstruct
 | 
			
		||||
    public void initLocalCache() {
 | 
			
		||||
        // 获取租户列表,如果有更新
 | 
			
		||||
        List<TenantDO> tenantList = loadTenantIfUpdate(maxUpdateTime);
 | 
			
		||||
        if (CollUtil.isEmpty(tenantList)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 写入缓存
 | 
			
		||||
        tenantCache = convertImmutableMap(tenantList, TenantDO::getId);
 | 
			
		||||
        maxUpdateTime = getMaxValue(tenantList, TenantDO::getUpdateTime);
 | 
			
		||||
        log.info("[initLocalCache][初始化 Tenant 数量为 {}]", tenantList.size());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
 | 
			
		||||
    public void schedulePeriodicRefresh() {
 | 
			
		||||
        initLocalCache();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 如果租户发生变化,从数据库中获取最新的全量租户。
 | 
			
		||||
     * 如果未发生变化,则返回空
 | 
			
		||||
     *
 | 
			
		||||
     * @param maxUpdateTime 当前租户的最大更新时间
 | 
			
		||||
     * @return 租户列表
 | 
			
		||||
     */
 | 
			
		||||
    private List<TenantDO> loadTenantIfUpdate(Date maxUpdateTime) {
 | 
			
		||||
        // 第一步,判断是否要更新。
 | 
			
		||||
        if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
 | 
			
		||||
            log.info("[loadTenantIfUpdate][首次加载全量租户]");
 | 
			
		||||
        } else { // 判断数据库中是否有更新的租户
 | 
			
		||||
            if (tenantMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            log.info("[loadTenantIfUpdate][增量加载全量租户]");
 | 
			
		||||
        }
 | 
			
		||||
        // 第二步,如果有更新,则从数据库加载所有租户
 | 
			
		||||
        return tenantMapper.selectList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<Long> getTenantIds() {
 | 
			
		||||
        return new ArrayList<>(tenantCache.keySet());
 | 
			
		||||
        List<TenantDO> tenants = tenantMapper.selectList();
 | 
			
		||||
        return CollectionUtils.convertList(tenants, TenantDO::getId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void validTenant(Long id) {
 | 
			
		||||
        TenantDO tenant = tenantCache.get(id);
 | 
			
		||||
        TenantDO tenant = getTenant(id);
 | 
			
		||||
        if (tenant == null) {
 | 
			
		||||
            throw exception(TENANT_NOT_EXISTS);
 | 
			
		||||
        }
 | 
			
		||||
@@ -184,13 +112,6 @@ public class TenantServiceImpl implements TenantService {
 | 
			
		||||
            // 修改租户的管理员
 | 
			
		||||
            tenantMapper.updateById(new TenantDO().setId(tenant.getId()).setContactUserId(userId));
 | 
			
		||||
        });
 | 
			
		||||
        // 发送刷新消息
 | 
			
		||||
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void afterCommit() {
 | 
			
		||||
                tenantProducer.sendTenantRefreshMessage();
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        return tenant.getId();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -228,13 +149,6 @@ public class TenantServiceImpl implements TenantService {
 | 
			
		||||
        if (ObjectUtil.notEqual(tenant.getPackageId(), updateReqVO.getPackageId())) {
 | 
			
		||||
            updateTenantRoleMenu(tenant.getId(), tenantPackage.getMenuIds());
 | 
			
		||||
        }
 | 
			
		||||
        // 发送刷新消息
 | 
			
		||||
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void afterCommit() {
 | 
			
		||||
                tenantProducer.sendTenantRefreshMessage();
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user