mirror of
				https://github.com/YunaiV/ruoyi-vue-pro.git
				synced 2025-10-31 10:37:41 +08:00 
			
		
		
		
	增加菜单的定时刷新
This commit is contained in:
		| @ -28,13 +28,6 @@ | |||||||
|                 </exclusion> |                 </exclusion> | ||||||
|             </exclusions> |             </exclusions> | ||||||
|         </dependency> |         </dependency> | ||||||
|  |  | ||||||
|         <!-- 通用工具--> |  | ||||||
|         <dependency> |  | ||||||
|             <groupId>com.ruoyi</groupId> |  | ||||||
|             <artifactId>ruoyi-common</artifactId> |  | ||||||
|         </dependency> |  | ||||||
|  |  | ||||||
|     </dependencies> |     </dependencies> | ||||||
|  |  | ||||||
| </project> | </project> | ||||||
| @ -23,6 +23,6 @@ public interface ConfigFrameworkDAO { | |||||||
|      * |      * | ||||||
|      * @return 配置列表 |      * @return 配置列表 | ||||||
|      */ |      */ | ||||||
|     List<InfConfigDO> getSysConfigList(); |     List<InfConfigDO> selectList(); | ||||||
|  |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -73,7 +73,6 @@ public class DBConfigRepository extends AbstractConfigRepository { | |||||||
|         if (CollUtil.isEmpty(configs)) { // 如果没有更新,则返回 |         if (CollUtil.isEmpty(configs)) { // 如果没有更新,则返回 | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         log.info("[sync][同步到新配置,配置数量为:{}]", configs.size()); |  | ||||||
|  |  | ||||||
|         // 第二步,构建新的 Properties |         // 第二步,构建新的 Properties | ||||||
|         Properties newProperties = this.buildProperties(configs); |         Properties newProperties = this.buildProperties(configs); | ||||||
| @ -83,6 +82,7 @@ public class DBConfigRepository extends AbstractConfigRepository { | |||||||
|         this.maxUpdateTime = configs.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime(); |         this.maxUpdateTime = configs.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime(); | ||||||
|         // 第四部,触发配置刷新!重要!!!! |         // 第四部,触发配置刷新!重要!!!! | ||||||
|         super.fireRepositoryChange(m_namespace, newProperties); |         super.fireRepositoryChange(m_namespace, newProperties); | ||||||
|  |         log.info("[sync][缓存配置,数量为:{}]", configs.size()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @ -150,7 +150,7 @@ public class DBConfigRepository extends AbstractConfigRepository { | |||||||
|             log.info("[loadConfigIfUpdate][增量加载全量配置]"); |             log.info("[loadConfigIfUpdate][增量加载全量配置]"); | ||||||
|         } |         } | ||||||
|         // 第二步,如果有更新,则从数据库加载所有配置 |         // 第二步,如果有更新,则从数据库加载所有配置 | ||||||
|         return configFrameworkDAO.getSysConfigList(); |         return configFrameworkDAO.selectList(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -0,0 +1,9 @@ | |||||||
|  | package cn.iocoder.dashboard.framework.quartz.config; | ||||||
|  |  | ||||||
|  | import org.springframework.context.annotation.Configuration; | ||||||
|  | import org.springframework.scheduling.annotation.EnableScheduling; | ||||||
|  |  | ||||||
|  | @Configuration | ||||||
|  | @EnableScheduling // 开启 Spring 自带的定时任务 | ||||||
|  | public class QuartzConfig { | ||||||
|  | } | ||||||
| @ -0,0 +1,5 @@ | |||||||
|  | /** | ||||||
|  |  * 定时任务,采用 Quartz 实现进程内的任务执行。 | ||||||
|  |  * 考虑到高可用,使用 Quartz 自带的 MySQL 集群方案。 | ||||||
|  |  */ | ||||||
|  | package cn.iocoder.dashboard.framework.quartz; | ||||||
| @ -32,7 +32,7 @@ public class InfConfigDAOImpl implements ConfigFrameworkDAO { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public List<InfConfigDO> getSysConfigList() { |     public List<InfConfigDO> selectList() { | ||||||
|         return jdbcTemplate.query("SELECT `key`, `value`, update_time, deleted FROM inf_config", new BeanPropertyRowMapper<>(InfConfigDO.class)); |         return jdbcTemplate.query("SELECT `key`, `value`, update_time, deleted FROM inf_config", new BeanPropertyRowMapper<>(InfConfigDO.class)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | |||||||
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; | import com.baomidou.mybatisplus.core.mapper.BaseMapper; | ||||||
| import org.apache.ibatis.annotations.Mapper; | import org.apache.ibatis.annotations.Mapper; | ||||||
|  |  | ||||||
|  | import java.util.Date; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| @Mapper | @Mapper | ||||||
| @ -30,4 +31,9 @@ public interface SysMenuMapper extends BaseMapper<SysMenuDO> { | |||||||
|         return selectList(new QueryWrapper<>()); |         return selectList(new QueryWrapper<>()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     default boolean selectExistsByUpdateTimeAfter(Date maxUpdateTime) { | ||||||
|  |         return selectOne(new QueryWrapper<SysMenuDO>().select("id") | ||||||
|  |                 .gt("update_time", maxUpdateTime).last("LIMIT 1")) != null; | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,6 +1,8 @@ | |||||||
| package cn.iocoder.dashboard.modules.system.service.permission.impl; | package cn.iocoder.dashboard.modules.system.service.permission.impl; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.collection.CollUtil; | ||||||
| import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil; | import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil; | ||||||
|  | import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; | ||||||
| import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuCreateReqVO; | import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuCreateReqVO; | ||||||
| import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuListReqVO; | import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuListReqVO; | ||||||
| import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuUpdateReqVO; | import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuUpdateReqVO; | ||||||
| @ -16,14 +18,12 @@ import com.google.common.collect.ImmutableMap; | |||||||
| import com.google.common.collect.ImmutableMultimap; | import com.google.common.collect.ImmutableMultimap; | ||||||
| import com.google.common.collect.Multimap; | import com.google.common.collect.Multimap; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import org.springframework.scheduling.annotation.Scheduled; | ||||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||||
|  |  | ||||||
| import javax.annotation.PostConstruct; | import javax.annotation.PostConstruct; | ||||||
| import javax.annotation.Resource; | import javax.annotation.Resource; | ||||||
| import java.util.Collection; | import java.util.*; | ||||||
| import java.util.Collections; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.Map; |  | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
| import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; | import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; | ||||||
| @ -37,6 +37,12 @@ import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; | |||||||
| @Slf4j | @Slf4j | ||||||
| public class SysMenuServiceImpl implements SysMenuService { | public class SysMenuServiceImpl implements SysMenuService { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 定时执行 {@link #schedulePeriodicRefresh()} 的周期 | ||||||
|  |      * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高 | ||||||
|  |      */ | ||||||
|  |     private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 菜单缓存 |      * 菜单缓存 | ||||||
|      * key:菜单编号 |      * key:菜单编号 | ||||||
| @ -52,6 +58,10 @@ public class SysMenuServiceImpl implements SysMenuService { | |||||||
|      * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 |      * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 | ||||||
|      */ |      */ | ||||||
|     private volatile Multimap<String, SysMenuDO> permMenuCache; |     private volatile Multimap<String, SysMenuDO> permMenuCache; | ||||||
|  |     /** | ||||||
|  |      * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新 | ||||||
|  |      */ | ||||||
|  |     private volatile Date maxUpdateTime; | ||||||
|  |  | ||||||
|     @Resource |     @Resource | ||||||
|     private SysMenuMapper menuMapper; |     private SysMenuMapper menuMapper; | ||||||
| @ -63,8 +73,14 @@ public class SysMenuServiceImpl implements SysMenuService { | |||||||
|      */ |      */ | ||||||
|     @Override |     @Override | ||||||
|     @PostConstruct |     @PostConstruct | ||||||
|     public void init() { |     public synchronized void init() { | ||||||
|         List<SysMenuDO> menuList = menuMapper.selectList(); |         // 获取 | ||||||
|  |         List<SysMenuDO> menuList = this.loadMenuIfUpdate(maxUpdateTime); | ||||||
|  |         if (CollUtil.isEmpty(menuList)) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 构建缓存 | ||||||
|         ImmutableMap.Builder<Long, SysMenuDO> menuCacheBuilder = ImmutableMap.builder(); |         ImmutableMap.Builder<Long, SysMenuDO> menuCacheBuilder = ImmutableMap.builder(); | ||||||
|         ImmutableMultimap.Builder<String, SysMenuDO> permMenuCacheBuilder = ImmutableMultimap.builder(); |         ImmutableMultimap.Builder<String, SysMenuDO> permMenuCacheBuilder = ImmutableMultimap.builder(); | ||||||
|         menuList.forEach(menuDO -> { |         menuList.forEach(menuDO -> { | ||||||
| @ -73,7 +89,35 @@ public class SysMenuServiceImpl implements SysMenuService { | |||||||
|         }); |         }); | ||||||
|         menuCache = menuCacheBuilder.build(); |         menuCache = menuCacheBuilder.build(); | ||||||
|         permMenuCache = permMenuCacheBuilder.build(); |         permMenuCache = permMenuCacheBuilder.build(); | ||||||
|         log.info("[init][初始化菜单数量为 {}]", menuList.size()); |         assert menuList.size() > 0; // 断言,避免告警 | ||||||
|  |         maxUpdateTime = menuList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime(); | ||||||
|  |         log.info("[init][缓存菜单,数量为:{}]", menuList.size()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) | ||||||
|  |     public void schedulePeriodicRefresh() { | ||||||
|  |         init(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 如果菜单发生变化,从数据库中获取最新的全量菜单。 | ||||||
|  |      * 如果未发生变化,则返回空 | ||||||
|  |      * | ||||||
|  |      * @param maxUpdateTime 当前菜单的最大更新时间 | ||||||
|  |      * @return 菜单列表 | ||||||
|  |      */ | ||||||
|  |     private List<SysMenuDO> loadMenuIfUpdate(Date maxUpdateTime) { | ||||||
|  |         // 第一步,判断是否要更新。 | ||||||
|  |         if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 | ||||||
|  |             log.info("[loadMenuIfUpdate][首次加载全量菜单]"); | ||||||
|  |         } else { // 判断数据库中是否有更新的菜单 | ||||||
|  |             if (!menuMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) { | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  |             log.info("[loadMenuIfUpdate][增量加载全量菜单]"); | ||||||
|  |         } | ||||||
|  |         // 第二步,如果有更新,则从数据库加载所有菜单 | ||||||
|  |         return menuMapper.selectList(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV