mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-11-04 04:08:43 +08:00 
			
		
		
		
	简化本地缓存的实现,萌新更容易看懂!
This commit is contained in:
		@@ -6,9 +6,7 @@ import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqV
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
 | 
			
		||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
			
		||||
import org.apache.ibatis.annotations.Mapper;
 | 
			
		||||
import org.apache.ibatis.annotations.Select;
 | 
			
		||||
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@Mapper
 | 
			
		||||
@@ -30,7 +28,4 @@ public interface DeptMapper extends BaseMapperX<DeptDO> {
 | 
			
		||||
        return selectCount(DeptDO::getParentId, parentId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Select("SELECT COUNT(*) FROM system_dept WHERE update_time > #{maxUpdateTime}")
 | 
			
		||||
    Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,6 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 | 
			
		||||
import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO;
 | 
			
		||||
import org.apache.ibatis.annotations.Mapper;
 | 
			
		||||
import org.apache.ibatis.annotations.Select;
 | 
			
		||||
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -30,7 +27,4 @@ public interface OAuth2ClientMapper extends BaseMapperX<OAuth2ClientDO> {
 | 
			
		||||
        return selectOne(OAuth2ClientDO::getClientId, clientId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Select("SELECT COUNT(*) FROM system_oauth2_client WHERE update_time > #{maxUpdateTime}")
 | 
			
		||||
    int selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,7 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuLi
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
 | 
			
		||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
			
		||||
import org.apache.ibatis.annotations.Mapper;
 | 
			
		||||
import org.apache.ibatis.annotations.Select;
 | 
			
		||||
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@Mapper
 | 
			
		||||
@@ -28,7 +26,4 @@ public interface MenuMapper extends BaseMapperX<MenuDO> {
 | 
			
		||||
                .eqIfPresent(MenuDO::getStatus, reqVO.getStatus()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Select("SELECT COUNT(*) FROM system_menu WHERE update_time > #{maxUpdateTime}")
 | 
			
		||||
    Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,10 +8,8 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleEx
 | 
			
		||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
 | 
			
		||||
import org.apache.ibatis.annotations.Mapper;
 | 
			
		||||
import org.apache.ibatis.annotations.Select;
 | 
			
		||||
import org.springframework.lang.Nullable;
 | 
			
		||||
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@@ -47,7 +45,4 @@ public interface RoleMapper extends BaseMapperX<RoleDO> {
 | 
			
		||||
        return selectList(RoleDO::getStatus, statuses);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Select("SELECT COUNT(*) FROM system_role WHERE update_time > #{maxUpdateTime}")
 | 
			
		||||
    Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.dal.mysql.permission;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
 | 
			
		||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 | 
			
		||||
import org.springframework.stereotype.Repository;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 实体 {@link RoleMenuDO} 的批量插入 Mapper
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@Repository
 | 
			
		||||
public class RoleMenuBatchInsertMapper extends ServiceImpl<RoleMenuMapper, RoleMenuDO> {
 | 
			
		||||
}
 | 
			
		||||
@@ -2,13 +2,11 @@ package cn.iocoder.yudao.module.system.dal.mysql.permission;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
 | 
			
		||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 | 
			
		||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
			
		||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 | 
			
		||||
import org.apache.ibatis.annotations.Mapper;
 | 
			
		||||
import org.apache.ibatis.annotations.Select;
 | 
			
		||||
import org.springframework.stereotype.Repository;
 | 
			
		||||
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@@ -20,23 +18,21 @@ public interface RoleMenuMapper extends BaseMapperX<RoleMenuDO> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default List<RoleMenuDO> selectListByRoleId(Long roleId) {
 | 
			
		||||
        return selectList(new QueryWrapper<RoleMenuDO>().eq("role_id", roleId));
 | 
			
		||||
        return selectList(RoleMenuDO::getRoleId, roleId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default void deleteListByRoleIdAndMenuIds(Long roleId, Collection<Long> menuIds) {
 | 
			
		||||
        delete(new QueryWrapper<RoleMenuDO>().eq("role_id", roleId)
 | 
			
		||||
                .in("menu_id", menuIds));
 | 
			
		||||
        delete(new LambdaQueryWrapper<RoleMenuDO>()
 | 
			
		||||
                .eq(RoleMenuDO::getRoleId, roleId)
 | 
			
		||||
                .in(RoleMenuDO::getMenuId, menuIds));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default void deleteListByMenuId(Long menuId) {
 | 
			
		||||
        delete(new QueryWrapper<RoleMenuDO>().eq("menu_id", menuId));
 | 
			
		||||
        delete(new LambdaQueryWrapper<RoleMenuDO>().eq(RoleMenuDO::getMenuId, menuId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default void deleteListByRoleId(Long roleId) {
 | 
			
		||||
        delete(new QueryWrapper<RoleMenuDO>().eq("role_id", roleId));
 | 
			
		||||
        delete(new LambdaQueryWrapper<RoleMenuDO>().eq(RoleMenuDO::getRoleId, roleId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Select("SELECT COUNT(*) FROM system_role_menu WHERE update_time > #{maxUpdateTime}")
 | 
			
		||||
    Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.dal.mysql.permission;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
 | 
			
		||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 | 
			
		||||
import org.springframework.stereotype.Repository;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 实体 {@link UserRoleDO} 的批量插入 Mapper
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@Repository
 | 
			
		||||
public class UserRoleBatchInsertMapper extends ServiceImpl<UserRoleMapper, UserRoleDO> {
 | 
			
		||||
}
 | 
			
		||||
@@ -2,11 +2,9 @@ package cn.iocoder.yudao.module.system.dal.mysql.permission;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
 | 
			
		||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 | 
			
		||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
			
		||||
import org.apache.ibatis.annotations.Mapper;
 | 
			
		||||
import org.apache.ibatis.annotations.Select;
 | 
			
		||||
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@@ -14,32 +12,25 @@ import java.util.List;
 | 
			
		||||
public interface UserRoleMapper extends BaseMapperX<UserRoleDO> {
 | 
			
		||||
 | 
			
		||||
    default List<UserRoleDO> selectListByUserId(Long userId) {
 | 
			
		||||
        return selectList(new QueryWrapper<UserRoleDO>().eq("user_id", userId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default List<UserRoleDO> selectListByRoleId(Long roleId) {
 | 
			
		||||
        return selectList(new QueryWrapper<UserRoleDO>().eq("role_id", roleId));
 | 
			
		||||
        return selectList(UserRoleDO::getUserId, userId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default void deleteListByUserIdAndRoleIdIds(Long userId, Collection<Long> roleIds) {
 | 
			
		||||
        delete(new QueryWrapper<UserRoleDO>().eq("user_id", userId)
 | 
			
		||||
                .in("role_id", roleIds));
 | 
			
		||||
        delete(new LambdaQueryWrapper<UserRoleDO>()
 | 
			
		||||
                .eq(UserRoleDO::getUserId, userId)
 | 
			
		||||
                .in(UserRoleDO::getRoleId, roleIds));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default void deleteListByUserId(Long userId) {
 | 
			
		||||
        delete(new QueryWrapper<UserRoleDO>().eq("user_id", userId));
 | 
			
		||||
        delete(new LambdaQueryWrapper<UserRoleDO>().eq(UserRoleDO::getUserId, userId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default void deleteListByRoleId(Long roleId) {
 | 
			
		||||
        delete(new QueryWrapper<UserRoleDO>().eq("role_id", roleId));
 | 
			
		||||
        delete(new LambdaQueryWrapper<UserRoleDO>().eq(UserRoleDO::getRoleId, roleId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    default List<UserRoleDO> selectListByRoleIds(Collection<Long> roleIds) {
 | 
			
		||||
        return selectList(UserRoleDO::getRoleId, roleIds);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Select("SELECT COUNT(*) FROM system_user_role WHERE update_time > #{maxUpdateTime}")
 | 
			
		||||
    Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,9 +7,7 @@ import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.Sensitiv
 | 
			
		||||
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
 | 
			
		||||
import org.apache.ibatis.annotations.Mapper;
 | 
			
		||||
import org.apache.ibatis.annotations.Select;
 | 
			
		||||
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -42,7 +40,4 @@ public interface SensitiveWordMapper extends BaseMapperX<SensitiveWordDO> {
 | 
			
		||||
        return selectOne(SensitiveWordDO::getName, name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Select("SELECT COUNT(*) FROM system_sensitive_word WHERE update_time > #{maxUpdateTime}")
 | 
			
		||||
    Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,6 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 | 
			
		||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
 | 
			
		||||
import org.apache.ibatis.annotations.Mapper;
 | 
			
		||||
import org.apache.ibatis.annotations.Select;
 | 
			
		||||
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
 | 
			
		||||
@Mapper
 | 
			
		||||
public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> {
 | 
			
		||||
@@ -21,7 +18,4 @@ public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> {
 | 
			
		||||
                .orderByDesc(SmsChannelDO::getId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Select("SELECT COUNT(*) FROM system_sms_channel WHERE update_time > #{maxUpdateTime}")
 | 
			
		||||
    Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,17 +7,12 @@ import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTempla
 | 
			
		||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
 | 
			
		||||
import org.apache.ibatis.annotations.Mapper;
 | 
			
		||||
import org.apache.ibatis.annotations.Select;
 | 
			
		||||
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@Mapper
 | 
			
		||||
public interface SmsTemplateMapper extends BaseMapperX<SmsTemplateDO> {
 | 
			
		||||
 | 
			
		||||
    @Select("SELECT COUNT(*) FROM system_sms_template WHERE update_time > #{maxUpdateTime}")
 | 
			
		||||
    Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
 | 
			
		||||
 | 
			
		||||
    default SmsTemplateDO selectByCode(String code) {
 | 
			
		||||
        return selectOne(SmsTemplateDO::getCode, code);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,13 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.dal.mysql.tenant;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantExportReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
 | 
			
		||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 | 
			
		||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 | 
			
		||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantExportReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
 | 
			
		||||
import org.apache.ibatis.annotations.Mapper;
 | 
			
		||||
import org.apache.ibatis.annotations.Select;
 | 
			
		||||
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -52,7 +50,4 @@ public interface TenantMapper extends BaseMapperX<TenantDO> {
 | 
			
		||||
        return selectList(TenantDO::getPackageId, packageId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Select("SELECT COUNT(*) FROM system_tenant WHERE update_time > #{maxUpdateTime}")
 | 
			
		||||
    Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ import cn.iocoder.yudao.module.system.mq.producer.dept.DeptProducer;
 | 
			
		||||
import com.google.common.collect.ImmutableMap;
 | 
			
		||||
import com.google.common.collect.ImmutableMultimap;
 | 
			
		||||
import com.google.common.collect.Multimap;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.scheduling.annotation.Scheduled;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
@@ -40,19 +41,13 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class DeptServiceImpl implements DeptService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
 | 
			
		||||
     * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
 | 
			
		||||
     */
 | 
			
		||||
    private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 部门缓存
 | 
			
		||||
     * key:部门编号 {@link DeptDO#getId()}
 | 
			
		||||
     *
 | 
			
		||||
     * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
 | 
			
		||||
     */
 | 
			
		||||
    @SuppressWarnings("FieldCanBeLocal")
 | 
			
		||||
    @Getter
 | 
			
		||||
    private volatile Map<Long, DeptDO> deptCache;
 | 
			
		||||
    /**
 | 
			
		||||
     * 父部门缓存
 | 
			
		||||
@@ -61,11 +56,8 @@ public class DeptServiceImpl implements DeptService {
 | 
			
		||||
     *
 | 
			
		||||
     * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
 | 
			
		||||
     */
 | 
			
		||||
    @Getter
 | 
			
		||||
    private volatile Multimap<Long, DeptDO> parentDeptCache;
 | 
			
		||||
    /**
 | 
			
		||||
     * 缓存部门的最大更新时间,用于后续的增量轮询,判断是否有更新
 | 
			
		||||
     */
 | 
			
		||||
    private volatile LocalDateTime maxUpdateTime;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private DeptMapper deptMapper;
 | 
			
		||||
@@ -79,48 +71,21 @@ public class DeptServiceImpl implements DeptService {
 | 
			
		||||
    @Override
 | 
			
		||||
    @PostConstruct
 | 
			
		||||
    public synchronized void initLocalCache() {
 | 
			
		||||
        initLocalCacheIfUpdate(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
 | 
			
		||||
    public void schedulePeriodicRefresh() {
 | 
			
		||||
        initLocalCacheIfUpdate(this.maxUpdateTime);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 刷新本地缓存
 | 
			
		||||
     *
 | 
			
		||||
     * @param maxUpdateTime 最大更新时间
 | 
			
		||||
     *                      1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
 | 
			
		||||
     *                      2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
 | 
			
		||||
     */
 | 
			
		||||
    private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) {
 | 
			
		||||
        // 注意:忽略自动多租户,因为要全局初始化缓存
 | 
			
		||||
        TenantUtils.executeIgnore(() -> {
 | 
			
		||||
            // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
 | 
			
		||||
            // 如果没有增量的数据变化,则不进行本地缓存的刷新
 | 
			
		||||
            if (maxUpdateTime != null
 | 
			
		||||
                    && deptMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
 | 
			
		||||
                log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            // 第一步:查询数据
 | 
			
		||||
            List<DeptDO> depts = deptMapper.selectList();
 | 
			
		||||
            log.info("[initLocalCacheIfUpdate][缓存部门,数量为:{}]", depts.size());
 | 
			
		||||
            log.info("[initLocalCache][缓存部门,数量为:{}]", depts.size());
 | 
			
		||||
 | 
			
		||||
            // 第二步:构建缓存。创建或更新支付 Client
 | 
			
		||||
            // 构建缓存
 | 
			
		||||
            // 第二步:构建缓存
 | 
			
		||||
            ImmutableMap.Builder<Long, DeptDO> builder = ImmutableMap.builder();
 | 
			
		||||
            ImmutableMultimap.Builder<Long, DeptDO> parentBuilder = ImmutableMultimap.builder();
 | 
			
		||||
            depts.forEach(sysRoleDO -> {
 | 
			
		||||
                builder.put(sysRoleDO.getId(), sysRoleDO);
 | 
			
		||||
                parentBuilder.put(sysRoleDO.getParentId(), sysRoleDO);
 | 
			
		||||
            });
 | 
			
		||||
            // 设置缓存
 | 
			
		||||
            deptCache = builder.build();
 | 
			
		||||
            parentDeptCache = parentBuilder.build();
 | 
			
		||||
 | 
			
		||||
            // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
 | 
			
		||||
            this.maxUpdateTime = CollectionUtils.getMaxValue(depts, DeptDO::getUpdateTime);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,20 +17,17 @@ import com.google.common.annotations.VisibleForTesting;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.scheduling.annotation.Scheduled;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.PostConstruct;
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
 | 
			
		||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -43,12 +40,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class OAuth2ClientServiceImpl implements OAuth2ClientService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
 | 
			
		||||
     * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
 | 
			
		||||
     */
 | 
			
		||||
    private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 客户端缓存
 | 
			
		||||
     * key:客户端编号 {@link OAuth2ClientDO#getClientId()} ()}
 | 
			
		||||
@@ -58,11 +49,6 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService {
 | 
			
		||||
    @Getter // 解决单测
 | 
			
		||||
    @Setter // 解决单测
 | 
			
		||||
    private volatile Map<String, OAuth2ClientDO> clientCache;
 | 
			
		||||
    /**
 | 
			
		||||
     * 缓存角色的最大更新时间,用于后续的增量轮询,判断是否有更新
 | 
			
		||||
     */
 | 
			
		||||
    @Getter
 | 
			
		||||
    private volatile LocalDateTime maxUpdateTime;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private OAuth2ClientMapper oauth2ClientMapper;
 | 
			
		||||
@@ -76,37 +62,12 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService {
 | 
			
		||||
    @Override
 | 
			
		||||
    @PostConstruct
 | 
			
		||||
    public void initLocalCache() {
 | 
			
		||||
        initLocalCacheIfUpdate(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
 | 
			
		||||
    public void schedulePeriodicRefresh() {
 | 
			
		||||
        initLocalCacheIfUpdate(this.maxUpdateTime);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 刷新本地缓存
 | 
			
		||||
     *
 | 
			
		||||
     * @param maxUpdateTime 最大更新时间
 | 
			
		||||
     *                      1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
 | 
			
		||||
     *                      2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
 | 
			
		||||
     */
 | 
			
		||||
    private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) {
 | 
			
		||||
        // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
 | 
			
		||||
        // 如果没有增量的数据变化,则不进行本地缓存的刷新
 | 
			
		||||
        if (maxUpdateTime != null
 | 
			
		||||
                && oauth2ClientMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
 | 
			
		||||
            log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        // 第一步:查询数据
 | 
			
		||||
        List<OAuth2ClientDO> clients = oauth2ClientMapper.selectList();
 | 
			
		||||
        log.info("[initLocalCacheIfUpdate][缓存 OAuth2 客户端,数量为:{}]", clients.size());
 | 
			
		||||
        log.info("[initLocalCache][缓存 OAuth2 客户端,数量为:{}]", clients.size());
 | 
			
		||||
 | 
			
		||||
        // 第二步:构建缓存。
 | 
			
		||||
        clientCache = convertMap(clients, OAuth2ClientDO::getClientId);
 | 
			
		||||
 | 
			
		||||
        // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
 | 
			
		||||
        this.maxUpdateTime = getMaxValue(clients, OAuth2ClientDO::getUpdateTime);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -18,9 +18,9 @@ import com.google.common.annotations.VisibleForTesting;
 | 
			
		||||
import com.google.common.collect.ImmutableMap;
 | 
			
		||||
import com.google.common.collect.ImmutableMultimap;
 | 
			
		||||
import com.google.common.collect.Multimap;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
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;
 | 
			
		||||
@@ -28,7 +28,6 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
 | 
			
		||||
 | 
			
		||||
import javax.annotation.PostConstruct;
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
@@ -43,18 +42,13 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class MenuServiceImpl implements MenuService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
 | 
			
		||||
     * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
 | 
			
		||||
     */
 | 
			
		||||
    private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 菜单缓存
 | 
			
		||||
     * key:菜单编号
 | 
			
		||||
     *
 | 
			
		||||
     * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
 | 
			
		||||
     */
 | 
			
		||||
    @Getter
 | 
			
		||||
    private volatile Map<Long, MenuDO> menuCache;
 | 
			
		||||
    /**
 | 
			
		||||
     * 权限与菜单缓存
 | 
			
		||||
@@ -63,11 +57,8 @@ public class MenuServiceImpl implements MenuService {
 | 
			
		||||
     *
 | 
			
		||||
     * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
 | 
			
		||||
     */
 | 
			
		||||
    @Getter
 | 
			
		||||
    private volatile Multimap<String, MenuDO> permissionMenuCache;
 | 
			
		||||
    /**
 | 
			
		||||
     * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
 | 
			
		||||
     */
 | 
			
		||||
    private volatile LocalDateTime maxUpdateTime;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private MenuMapper menuMapper;
 | 
			
		||||
@@ -86,33 +77,11 @@ public class MenuServiceImpl implements MenuService {
 | 
			
		||||
    @Override
 | 
			
		||||
    @PostConstruct
 | 
			
		||||
    public synchronized void initLocalCache() {
 | 
			
		||||
        initLocalCacheIfUpdate(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
 | 
			
		||||
    public void schedulePeriodicRefresh() {
 | 
			
		||||
        initLocalCacheIfUpdate(this.maxUpdateTime);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 刷新本地缓存
 | 
			
		||||
     *
 | 
			
		||||
     * @param maxUpdateTime 最大更新时间
 | 
			
		||||
     *                      1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
 | 
			
		||||
     *                      2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
 | 
			
		||||
     */
 | 
			
		||||
    private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) {
 | 
			
		||||
        // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
 | 
			
		||||
        // 如果没有增量的数据变化,则不进行本地缓存的刷新
 | 
			
		||||
        if (maxUpdateTime != null
 | 
			
		||||
            && menuMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
 | 
			
		||||
            log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        // 第一步:查询数据
 | 
			
		||||
        List<MenuDO> menuList = menuMapper.selectList();
 | 
			
		||||
        log.info("[initLocalCacheIfUpdate][缓存菜单,数量为:{}]", menuList.size());
 | 
			
		||||
        log.info("[initLocalCache][缓存菜单,数量为:{}]", menuList.size());
 | 
			
		||||
 | 
			
		||||
        // 第二步:构建缓存。
 | 
			
		||||
        // 第二步:构建缓存
 | 
			
		||||
        ImmutableMap.Builder<Long, MenuDO> menuCacheBuilder = ImmutableMap.builder();
 | 
			
		||||
        ImmutableMultimap.Builder<String, MenuDO> permMenuCacheBuilder = ImmutableMultimap.builder();
 | 
			
		||||
        menuList.forEach(menuDO -> {
 | 
			
		||||
@@ -123,9 +92,6 @@ public class MenuServiceImpl implements MenuService {
 | 
			
		||||
        });
 | 
			
		||||
        menuCache = menuCacheBuilder.build();
 | 
			
		||||
        permissionMenuCache = permMenuCacheBuilder.build();
 | 
			
		||||
 | 
			
		||||
        // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
 | 
			
		||||
        this.maxUpdateTime = CollectionUtils.getMaxValue(menuList, MenuDO::getUpdateTime);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -16,9 +16,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuBatchInsertMapper;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleBatchInsertMapper;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
 | 
			
		||||
import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
 | 
			
		||||
import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;
 | 
			
		||||
@@ -32,7 +30,6 @@ import com.google.common.collect.Sets;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.scheduling.annotation.Scheduled;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.transaction.annotation.Transactional;
 | 
			
		||||
import org.springframework.transaction.support.TransactionSynchronization;
 | 
			
		||||
@@ -40,12 +37,10 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
 | 
			
		||||
 | 
			
		||||
import javax.annotation.PostConstruct;
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.function.Supplier;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
 | 
			
		||||
import static java.util.Collections.singleton;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -57,12 +52,6 @@ import static java.util.Collections.singleton;
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class PermissionServiceImpl implements PermissionService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
 | 
			
		||||
     * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
 | 
			
		||||
     */
 | 
			
		||||
    private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 角色编号与菜单编号的缓存映射
 | 
			
		||||
     * key:角色编号
 | 
			
		||||
@@ -83,11 +72,6 @@ public class PermissionServiceImpl implements PermissionService {
 | 
			
		||||
    @Getter
 | 
			
		||||
    @Setter // 单元测试需要
 | 
			
		||||
    private volatile Multimap<Long, Long> menuRoleCache;
 | 
			
		||||
    /**
 | 
			
		||||
     * 缓存 RoleMenu 的最大更新时间,用于后续的增量轮询,判断是否有更新
 | 
			
		||||
     */
 | 
			
		||||
    @Getter
 | 
			
		||||
    private volatile LocalDateTime roleMenuMaxUpdateTime;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户编号与角色编号的缓存映射
 | 
			
		||||
@@ -99,20 +83,11 @@ public class PermissionServiceImpl implements PermissionService {
 | 
			
		||||
    @Getter
 | 
			
		||||
    @Setter // 单元测试需要
 | 
			
		||||
    private volatile Map<Long, Set<Long>> userRoleCache;
 | 
			
		||||
    /**
 | 
			
		||||
     * 缓存 UserRole 的最大更新时间,用于后续的增量轮询,判断是否有更新
 | 
			
		||||
     */
 | 
			
		||||
    @Getter
 | 
			
		||||
    private volatile LocalDateTime userRoleMaxUpdateTime;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private RoleMenuMapper roleMenuMapper;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private RoleMenuBatchInsertMapper roleMenuBatchInsertMapper;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private UserRoleMapper userRoleMapper;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private UserRoleBatchInsertMapper userRoleBatchInsertMapper;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private RoleService roleService;
 | 
			
		||||
@@ -129,38 +104,22 @@ public class PermissionServiceImpl implements PermissionService {
 | 
			
		||||
    @Override
 | 
			
		||||
    @PostConstruct
 | 
			
		||||
    public void initLocalCache() {
 | 
			
		||||
        initLocalCacheIfUpdateForRoleMenu(null);
 | 
			
		||||
        initLocalCacheIfUpdateForUserRole(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
 | 
			
		||||
    public void schedulePeriodicRefresh() {
 | 
			
		||||
        initLocalCacheIfUpdateForRoleMenu(this.roleMenuMaxUpdateTime);
 | 
			
		||||
        initLocalCacheIfUpdateForUserRole(this.userRoleMaxUpdateTime);
 | 
			
		||||
        initLocalCacheForRoleMenu();
 | 
			
		||||
        initLocalCacheForUserRole();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 刷新 RoleMenu 本地缓存
 | 
			
		||||
     *
 | 
			
		||||
     * @param maxUpdateTime 最大更新时间
 | 
			
		||||
     *                      1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
 | 
			
		||||
     *                      2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
 | 
			
		||||
     */
 | 
			
		||||
    @VisibleForTesting
 | 
			
		||||
    void initLocalCacheIfUpdateForRoleMenu(LocalDateTime maxUpdateTime) {
 | 
			
		||||
    void initLocalCacheForRoleMenu() {
 | 
			
		||||
        // 注意:忽略自动多租户,因为要全局初始化缓存
 | 
			
		||||
        TenantUtils.executeIgnore(() -> {
 | 
			
		||||
            // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
 | 
			
		||||
            // 如果没有增量的数据变化,则不进行本地缓存的刷新
 | 
			
		||||
            if (maxUpdateTime != null
 | 
			
		||||
                    && roleMenuMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
 | 
			
		||||
                log.info("[initLocalCacheIfUpdateForRoleMenu][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            // 第一步:查询数据
 | 
			
		||||
            List<RoleMenuDO> roleMenus = roleMenuMapper.selectList();
 | 
			
		||||
            log.info("[initLocalCacheIfUpdateForRoleMenu][缓存角色与菜单,数量为:{}]", roleMenus.size());
 | 
			
		||||
            log.info("[initLocalCacheForRoleMenu][缓存角色与菜单,数量为:{}]", roleMenus.size());
 | 
			
		||||
 | 
			
		||||
            // 第二步:构建缓存。
 | 
			
		||||
            // 第二步:构建缓存
 | 
			
		||||
            ImmutableMultimap.Builder<Long, Long> roleMenuCacheBuilder = ImmutableMultimap.builder();
 | 
			
		||||
            ImmutableMultimap.Builder<Long, Long> menuRoleCacheBuilder = ImmutableMultimap.builder();
 | 
			
		||||
            roleMenus.forEach(roleMenuDO -> {
 | 
			
		||||
@@ -169,40 +128,24 @@ public class PermissionServiceImpl implements PermissionService {
 | 
			
		||||
            });
 | 
			
		||||
            roleMenuCache = roleMenuCacheBuilder.build();
 | 
			
		||||
            menuRoleCache = menuRoleCacheBuilder.build();
 | 
			
		||||
 | 
			
		||||
            // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
 | 
			
		||||
            this.roleMenuMaxUpdateTime = getMaxValue(roleMenus, RoleMenuDO::getUpdateTime);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 刷新 UserRole 本地缓存
 | 
			
		||||
     *
 | 
			
		||||
     * @param maxUpdateTime 最大更新时间
 | 
			
		||||
     *                      1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
 | 
			
		||||
     *                      2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
 | 
			
		||||
     */
 | 
			
		||||
    @VisibleForTesting
 | 
			
		||||
    void initLocalCacheIfUpdateForUserRole(LocalDateTime maxUpdateTime) {
 | 
			
		||||
    void initLocalCacheForUserRole() {
 | 
			
		||||
        // 注意:忽略自动多租户,因为要全局初始化缓存
 | 
			
		||||
        TenantUtils.executeIgnore(() -> {
 | 
			
		||||
            // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
 | 
			
		||||
            // 如果没有增量的数据变化,则不进行本地缓存的刷新
 | 
			
		||||
            if (maxUpdateTime != null
 | 
			
		||||
                    && userRoleMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
 | 
			
		||||
                log.info("[initLocalCacheIfUpdateForUserRole][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            // 第一步:加载数据
 | 
			
		||||
            List<UserRoleDO> userRoles = userRoleMapper.selectList();
 | 
			
		||||
            log.info("[initLocalCacheIfUpdateForUserRole][缓存用户与角色,数量为:{}]", userRoles.size());
 | 
			
		||||
            log.info("[initLocalCacheForUserRole][缓存用户与角色,数量为:{}]", userRoles.size());
 | 
			
		||||
 | 
			
		||||
            // 第二步:构建缓存。
 | 
			
		||||
            ImmutableMultimap.Builder<Long, Long> userRoleCacheBuilder = ImmutableMultimap.builder();
 | 
			
		||||
            userRoles.forEach(userRoleDO -> userRoleCacheBuilder.put(userRoleDO.getUserId(), userRoleDO.getRoleId()));
 | 
			
		||||
            userRoleCache = CollectionUtils.convertMultiMap2(userRoles, UserRoleDO::getUserId, UserRoleDO::getRoleId);
 | 
			
		||||
 | 
			
		||||
            // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
 | 
			
		||||
            this.userRoleMaxUpdateTime = getMaxValue(userRoles, UserRoleDO::getUpdateTime);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -264,7 +207,7 @@ public class PermissionServiceImpl implements PermissionService {
 | 
			
		||||
        Collection<Long> deleteMenuIds = CollUtil.subtract(dbMenuIds, menuIds);
 | 
			
		||||
        // 执行新增和删除。对于已经授权的菜单,不用做任何处理
 | 
			
		||||
        if (!CollectionUtil.isEmpty(createMenuIds)) {
 | 
			
		||||
            roleMenuBatchInsertMapper.saveBatch(CollectionUtils.convertList(createMenuIds, menuId -> {
 | 
			
		||||
            roleMenuMapper.insertBatch(CollectionUtils.convertList(createMenuIds, menuId -> {
 | 
			
		||||
                RoleMenuDO entity = new RoleMenuDO();
 | 
			
		||||
                entity.setRoleId(roleId);
 | 
			
		||||
                entity.setMenuId(menuId);
 | 
			
		||||
@@ -308,7 +251,7 @@ public class PermissionServiceImpl implements PermissionService {
 | 
			
		||||
        Collection<Long> deleteMenuIds = CollUtil.subtract(dbRoleIds, roleIds);
 | 
			
		||||
        // 执行新增和删除。对于已经授权的角色,不用做任何处理
 | 
			
		||||
        if (!CollectionUtil.isEmpty(createRoleIds)) {
 | 
			
		||||
            userRoleBatchInsertMapper.saveBatch(CollectionUtils.convertList(createRoleIds, roleId -> {
 | 
			
		||||
            userRoleMapper.insertBatch(CollectionUtils.convertList(createRoleIds, roleId -> {
 | 
			
		||||
                UserRoleDO entity = new UserRoleDO();
 | 
			
		||||
                entity.setUserId(userId);
 | 
			
		||||
                entity.setRoleId(roleId);
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@ import com.google.common.annotations.VisibleForTesting;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.lang.Nullable;
 | 
			
		||||
import org.springframework.scheduling.annotation.Scheduled;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.transaction.annotation.Transactional;
 | 
			
		||||
import org.springframework.transaction.support.TransactionSynchronization;
 | 
			
		||||
@@ -31,7 +30,6 @@ import org.springframework.util.StringUtils;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.PostConstruct;
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
@@ -47,12 +45,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class RoleServiceImpl implements RoleService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
 | 
			
		||||
     * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
 | 
			
		||||
     */
 | 
			
		||||
    private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 角色缓存
 | 
			
		||||
     * key:角色编号 {@link RoleDO#getId()}
 | 
			
		||||
@@ -61,11 +53,6 @@ public class RoleServiceImpl implements RoleService {
 | 
			
		||||
     */
 | 
			
		||||
    @Getter
 | 
			
		||||
    private volatile Map<Long, RoleDO> roleCache;
 | 
			
		||||
    /**
 | 
			
		||||
     * 缓存角色的最大更新时间,用于后续的增量轮询,判断是否有更新
 | 
			
		||||
     */
 | 
			
		||||
    @Getter
 | 
			
		||||
    private volatile LocalDateTime maxUpdateTime;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private PermissionService permissionService;
 | 
			
		||||
@@ -82,39 +69,14 @@ public class RoleServiceImpl implements RoleService {
 | 
			
		||||
    @Override
 | 
			
		||||
    @PostConstruct
 | 
			
		||||
    public void initLocalCache() {
 | 
			
		||||
        initLocalCacheIfUpdate(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
 | 
			
		||||
    public void schedulePeriodicRefresh() {
 | 
			
		||||
        initLocalCacheIfUpdate(this.maxUpdateTime);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 刷新本地缓存
 | 
			
		||||
     *
 | 
			
		||||
     * @param maxUpdateTime 最大更新时间
 | 
			
		||||
     *                      1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
 | 
			
		||||
     *                      2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
 | 
			
		||||
     */
 | 
			
		||||
    private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) {
 | 
			
		||||
        // 注意:忽略自动多租户,因为要全局初始化缓存
 | 
			
		||||
        TenantUtils.executeIgnore(() -> {
 | 
			
		||||
            // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
 | 
			
		||||
            // 如果没有增量的数据变化,则不进行本地缓存的刷新
 | 
			
		||||
            if (maxUpdateTime != null
 | 
			
		||||
                    && roleMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
 | 
			
		||||
                log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            // 第一步:查询数据
 | 
			
		||||
            List<RoleDO> roleList = roleMapper.selectList();
 | 
			
		||||
            log.info("[initLocalCacheIfUpdate][缓存角色,数量为:{}]", roleList.size());
 | 
			
		||||
            log.info("[initLocalCache][缓存角色,数量为:{}]", roleList.size());
 | 
			
		||||
 | 
			
		||||
            // 第二步:构建缓存。
 | 
			
		||||
            // 第二步:构建缓存
 | 
			
		||||
            roleCache = CollectionUtils.convertMap(roleList, RoleDO::getId);
 | 
			
		||||
 | 
			
		||||
            // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
 | 
			
		||||
            this.maxUpdateTime = CollectionUtils.getMaxValue(roleList, RoleDO::getUpdateTime);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,13 +17,11 @@ import com.google.common.collect.HashMultimap;
 | 
			
		||||
import com.google.common.collect.Multimap;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.scheduling.annotation.Scheduled;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.PostConstruct;
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 | 
			
		||||
@@ -40,12 +38,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_
 | 
			
		||||
@Validated
 | 
			
		||||
public class SensitiveWordServiceImpl implements SensitiveWordService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
 | 
			
		||||
     * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
 | 
			
		||||
     */
 | 
			
		||||
    private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 敏感词标签缓存
 | 
			
		||||
     * key:敏感词编号 {@link SensitiveWordDO#getId()}
 | 
			
		||||
@@ -55,12 +47,6 @@ public class SensitiveWordServiceImpl implements SensitiveWordService {
 | 
			
		||||
    @Getter
 | 
			
		||||
    private volatile Set<String> sensitiveWordTagsCache = Collections.emptySet();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 缓存敏感词的最大更新时间,用于后续的增量轮询,判断是否有更新
 | 
			
		||||
     */
 | 
			
		||||
    @Getter
 | 
			
		||||
    private volatile LocalDateTime maxUpdateTime;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SensitiveWordMapper sensitiveWordMapper;
 | 
			
		||||
 | 
			
		||||
@@ -84,42 +70,17 @@ public class SensitiveWordServiceImpl implements SensitiveWordService {
 | 
			
		||||
    @Override
 | 
			
		||||
    @PostConstruct
 | 
			
		||||
    public void initLocalCache() {
 | 
			
		||||
        initLocalCacheIfUpdate(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
 | 
			
		||||
    public void schedulePeriodicRefresh() {
 | 
			
		||||
        initLocalCacheIfUpdate(this.maxUpdateTime);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 刷新本地缓存
 | 
			
		||||
     *
 | 
			
		||||
     * @param maxUpdateTime 最大更新时间
 | 
			
		||||
     *                      1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
 | 
			
		||||
     *                      2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
 | 
			
		||||
     */
 | 
			
		||||
    private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) {
 | 
			
		||||
        // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
 | 
			
		||||
        // 如果没有增量的数据变化,则不进行本地缓存的刷新
 | 
			
		||||
        if (maxUpdateTime != null
 | 
			
		||||
                && sensitiveWordMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
 | 
			
		||||
            log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        // 第一步:查询数据
 | 
			
		||||
        List<SensitiveWordDO> sensitiveWords = sensitiveWordMapper.selectList();
 | 
			
		||||
        log.info("[initLocalCacheIfUpdate][缓存敏感词,数量为:{}]", sensitiveWords.size());
 | 
			
		||||
        log.info("[initLocalCache][缓存敏感词,数量为:{}]", sensitiveWords.size());
 | 
			
		||||
 | 
			
		||||
        // 第二步:构建缓存。
 | 
			
		||||
        // 第二步:构建缓存
 | 
			
		||||
        // 写入 sensitiveWordTagsCache 缓存
 | 
			
		||||
        Set<String> tags = new HashSet<>();
 | 
			
		||||
        sensitiveWords.forEach(word -> tags.addAll(word.getTags()));
 | 
			
		||||
        sensitiveWordTagsCache = tags;
 | 
			
		||||
        // 写入 defaultSensitiveWordTrie、tagSensitiveWordTries 缓存
 | 
			
		||||
        initSensitiveWordTrie(sensitiveWords);
 | 
			
		||||
 | 
			
		||||
        // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
 | 
			
		||||
        this.maxUpdateTime = CollectionUtils.getMaxValue(sensitiveWords, SensitiveWordDO::getUpdateTime);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void initSensitiveWordTrie(List<SensitiveWordDO> wordDOs) {
 | 
			
		||||
 
 | 
			
		||||
@@ -11,17 +11,14 @@ import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
 | 
			
		||||
import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.scheduling.annotation.Scheduled;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.PostConstruct;
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
 | 
			
		||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
 | 
			
		||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
 | 
			
		||||
 | 
			
		||||
@@ -34,17 +31,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNE
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class SmsChannelServiceImpl implements SmsChannelService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
 | 
			
		||||
     * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
 | 
			
		||||
     */
 | 
			
		||||
    private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
 | 
			
		||||
     */
 | 
			
		||||
    private volatile LocalDateTime maxUpdateTime;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private SmsClientFactory smsClientFactory;
 | 
			
		||||
 | 
			
		||||
@@ -60,38 +46,13 @@ public class SmsChannelServiceImpl implements SmsChannelService {
 | 
			
		||||
    @Override
 | 
			
		||||
    @PostConstruct
 | 
			
		||||
    public void initLocalCache() {
 | 
			
		||||
        initLocalCacheIfUpdate(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
 | 
			
		||||
    public void schedulePeriodicRefresh() {
 | 
			
		||||
        initLocalCacheIfUpdate(this.maxUpdateTime);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 刷新本地缓存
 | 
			
		||||
     *
 | 
			
		||||
     * @param maxUpdateTime 最大更新时间
 | 
			
		||||
     *                      1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
 | 
			
		||||
     *                      2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
 | 
			
		||||
     */
 | 
			
		||||
    private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) {
 | 
			
		||||
        // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
 | 
			
		||||
        // 如果没有增量的数据变化,则不进行本地缓存的刷新
 | 
			
		||||
        if (maxUpdateTime != null
 | 
			
		||||
                && smsChannelMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
 | 
			
		||||
            log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        // 第一步:查询数据
 | 
			
		||||
        List<SmsChannelDO> channels = smsChannelMapper.selectList();
 | 
			
		||||
        log.info("[initLocalCacheIfUpdate][缓存短信渠道,数量为:{}]", channels.size());
 | 
			
		||||
        log.info("[initLocalCache][缓存短信渠道,数量为:{}]", channels.size());
 | 
			
		||||
 | 
			
		||||
        // 第二步:构建缓存。创建或更新短信 Client
 | 
			
		||||
        // 第二步:构建缓存:创建或更新短信 Client
 | 
			
		||||
        List<SmsChannelProperties> propertiesList = SmsChannelConvert.INSTANCE.convertList02(channels);
 | 
			
		||||
        propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties));
 | 
			
		||||
 | 
			
		||||
        // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
 | 
			
		||||
        this.maxUpdateTime = getMaxValue(channels, SmsChannelDO::getUpdateTime);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.service.sms;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.hutool.core.util.ReUtil;
 | 
			
		||||
import cn.hutool.core.util.StrUtil;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 | 
			
		||||
@@ -20,15 +19,17 @@ import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper;
 | 
			
		||||
import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;
 | 
			
		||||
import com.google.common.annotations.VisibleForTesting;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.scheduling.annotation.Scheduled;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.PostConstruct;
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 | 
			
		||||
@@ -44,12 +45,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class SmsTemplateServiceImpl implements SmsTemplateService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
 | 
			
		||||
     * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
 | 
			
		||||
     */
 | 
			
		||||
    private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 正则表达式,匹配 {} 中的变量
 | 
			
		||||
     */
 | 
			
		||||
@@ -73,51 +68,18 @@ public class SmsTemplateServiceImpl implements SmsTemplateService {
 | 
			
		||||
     *
 | 
			
		||||
     * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
 | 
			
		||||
     */
 | 
			
		||||
    @Getter // 为了方便测试,这里提供 getter 方法
 | 
			
		||||
    private volatile Map<String, SmsTemplateDO> smsTemplateCache;
 | 
			
		||||
    /**
 | 
			
		||||
     * 缓存短信模板的最大更新时间,用于后续的增量轮询,判断是否有更新
 | 
			
		||||
     */
 | 
			
		||||
    private volatile LocalDateTime maxUpdateTime;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @PostConstruct
 | 
			
		||||
    public void initLocalCache() {
 | 
			
		||||
        // 获取短信模板列表,如果有更新
 | 
			
		||||
        List<SmsTemplateDO> smsTemplateList = this.loadSmsTemplateIfUpdate(maxUpdateTime);
 | 
			
		||||
        if (CollUtil.isEmpty(smsTemplateList)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        // 第一步:查询数据
 | 
			
		||||
        List<SmsTemplateDO> smsTemplateList = smsTemplateMapper.selectList();
 | 
			
		||||
        log.info("[initLocalCache][缓存短信模版,数量为:{}]", smsTemplateList.size());
 | 
			
		||||
 | 
			
		||||
        // 写入缓存
 | 
			
		||||
        // 第二步:构建缓存
 | 
			
		||||
        smsTemplateCache = CollectionUtils.convertMap(smsTemplateList, SmsTemplateDO::getCode);
 | 
			
		||||
        maxUpdateTime = CollectionUtils.getMaxValue(smsTemplateList, SmsTemplateDO::getUpdateTime);
 | 
			
		||||
        log.info("[initLocalCache][初始化 SmsTemplate 数量为 {}]", smsTemplateList.size());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 如果短信模板发生变化,从数据库中获取最新的全量短信模板。
 | 
			
		||||
     * 如果未发生变化,则返回空
 | 
			
		||||
     *
 | 
			
		||||
     * @param maxUpdateTime 当前短信模板的最大更新时间
 | 
			
		||||
     * @return 短信模板列表
 | 
			
		||||
     */
 | 
			
		||||
    private List<SmsTemplateDO> loadSmsTemplateIfUpdate(LocalDateTime maxUpdateTime) {
 | 
			
		||||
        // 第一步,判断是否要更新。
 | 
			
		||||
        if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
 | 
			
		||||
            log.info("[loadSmsTemplateIfUpdate][首次加载全量短信模板]");
 | 
			
		||||
        } else { // 判断数据库中是否有更新的短信模板
 | 
			
		||||
            if (smsTemplateMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            log.info("[loadSmsTemplateIfUpdate][增量加载全量短信模板]");
 | 
			
		||||
        }
 | 
			
		||||
        // 第二步,如果有更新,则从数据库加载所有短信模板
 | 
			
		||||
        return smsTemplateMapper.selectList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
 | 
			
		||||
    public void schedulePeriodicRefresh() {
 | 
			
		||||
        initLocalCache();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user