Commit 79a0b4cb56b323cbf202621e10c34c73963eb1de

Authored by YunaiV
1 parent 7bd81d03

增加菜单的定时刷新

ruoyi-quartz/pom.xml
... ... @@ -28,13 +28,6 @@
28 28 </exclusion>
29 29 </exclusions>
30 30 </dependency>
31   -
32   - <!-- 通用工具-->
33   - <dependency>
34   - <groupId>com.ruoyi</groupId>
35   - <artifactId>ruoyi-common</artifactId>
36   - </dependency>
37   -
38 31 </dependencies>
39 32  
40   -</project>
41 33 \ No newline at end of file
  34 +</project>
... ...
src/main/java/cn/iocoder/dashboard/framework/apollo/internals/ConfigFrameworkDAO.java
... ... @@ -23,6 +23,6 @@ public interface ConfigFrameworkDAO {
23 23 *
24 24 * @return 配置列表
25 25 */
26   - List<InfConfigDO> getSysConfigList();
  26 + List<InfConfigDO> selectList();
27 27  
28 28 }
... ...
src/main/java/cn/iocoder/dashboard/framework/apollo/internals/DBConfigRepository.java
... ... @@ -73,7 +73,6 @@ public class DBConfigRepository extends AbstractConfigRepository {
73 73 if (CollUtil.isEmpty(configs)) { // 如果没有更新,则返回
74 74 return;
75 75 }
76   - log.info("[sync][同步到新配置,配置数量为:{}]", configs.size());
77 76  
78 77 // 第二步,构建新的 Properties
79 78 Properties newProperties = this.buildProperties(configs);
... ... @@ -83,6 +82,7 @@ public class DBConfigRepository extends AbstractConfigRepository {
83 82 this.maxUpdateTime = configs.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
84 83 // 第四部,触发配置刷新!重要!!!!
85 84 super.fireRepositoryChange(m_namespace, newProperties);
  85 + log.info("[sync][缓存配置,数量为:{}]", configs.size());
86 86 }
87 87  
88 88 @Override
... ... @@ -150,7 +150,7 @@ public class DBConfigRepository extends AbstractConfigRepository {
150 150 log.info("[loadConfigIfUpdate][增量加载全量配置]");
151 151 }
152 152 // 第二步,如果有更新,则从数据库加载所有配置
153   - return configFrameworkDAO.getSysConfigList();
  153 + return configFrameworkDAO.selectList();
154 154 }
155 155  
156 156 }
... ...
src/main/java/cn/iocoder/dashboard/framework/quartz/config/QuartzConfig.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.quartz.config;
  2 +
  3 +import org.springframework.context.annotation.Configuration;
  4 +import org.springframework.scheduling.annotation.EnableScheduling;
  5 +
  6 +@Configuration
  7 +@EnableScheduling // 开启 Spring 自带的定时任务
  8 +public class QuartzConfig {
  9 +}
... ...
src/main/java/cn/iocoder/dashboard/framework/quartz/package-info.java 0 → 100644
  1 +/**
  2 + * 定时任务,采用 Quartz 实现进程内的任务执行。
  3 + * 考虑到高可用,使用 Quartz 自带的 MySQL 集群方案。
  4 + */
  5 +package cn.iocoder.dashboard.framework.quartz;
... ...
src/main/java/cn/iocoder/dashboard/modules/infra/dal/mysql/dao/config/InfConfigDAOImpl.java
... ... @@ -32,7 +32,7 @@ public class InfConfigDAOImpl implements ConfigFrameworkDAO {
32 32 }
33 33  
34 34 @Override
35   - public List<InfConfigDO> getSysConfigList() {
  35 + public List<InfConfigDO> selectList() {
36 36 return jdbcTemplate.query("SELECT `key`, `value`, update_time, deleted FROM inf_config", new BeanPropertyRowMapper<>(InfConfigDO.class));
37 37 }
38 38  
... ...
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysMenuMapper.java
... ... @@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
7 7 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
8 8 import org.apache.ibatis.annotations.Mapper;
9 9  
  10 +import java.util.Date;
10 11 import java.util.List;
11 12  
12 13 @Mapper
... ... @@ -30,4 +31,9 @@ public interface SysMenuMapper extends BaseMapper&lt;SysMenuDO&gt; {
30 31 return selectList(new QueryWrapper<>());
31 32 }
32 33  
  34 + default boolean selectExistsByUpdateTimeAfter(Date maxUpdateTime) {
  35 + return selectOne(new QueryWrapper<SysMenuDO>().select("id")
  36 + .gt("update_time", maxUpdateTime).last("LIMIT 1")) != null;
  37 + }
  38 +
33 39 }
... ...
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysMenuServiceImpl.java
1 1 package cn.iocoder.dashboard.modules.system.service.permission.impl;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
  5 +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
4 6 import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuCreateReqVO;
5 7 import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuListReqVO;
6 8 import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuUpdateReqVO;
... ... @@ -16,14 +18,12 @@ import com.google.common.collect.ImmutableMap;
16 18 import com.google.common.collect.ImmutableMultimap;
17 19 import com.google.common.collect.Multimap;
18 20 import lombok.extern.slf4j.Slf4j;
  21 +import org.springframework.scheduling.annotation.Scheduled;
19 22 import org.springframework.stereotype.Service;
20 23  
21 24 import javax.annotation.PostConstruct;
22 25 import javax.annotation.Resource;
23   -import java.util.Collection;
24   -import java.util.Collections;
25   -import java.util.List;
26   -import java.util.Map;
  26 +import java.util.*;
27 27 import java.util.stream.Collectors;
28 28  
29 29 import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
... ... @@ -38,6 +38,12 @@ import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
38 38 public class SysMenuServiceImpl implements SysMenuService {
39 39  
40 40 /**
  41 + * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
  42 + * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
  43 + */
  44 + private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
  45 +
  46 + /**
41 47 * 菜单缓存
42 48 * key:菜单编号
43 49 *
... ... @@ -52,6 +58,10 @@ public class SysMenuServiceImpl implements SysMenuService {
52 58 * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
53 59 */
54 60 private volatile Multimap<String, SysMenuDO> permMenuCache;
  61 + /**
  62 + * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
  63 + */
  64 + private volatile Date maxUpdateTime;
55 65  
56 66 @Resource
57 67 private SysMenuMapper menuMapper;
... ... @@ -63,8 +73,14 @@ public class SysMenuServiceImpl implements SysMenuService {
63 73 */
64 74 @Override
65 75 @PostConstruct
66   - public void init() {
67   - List<SysMenuDO> menuList = menuMapper.selectList();
  76 + public synchronized void init() {
  77 + // 获取
  78 + List<SysMenuDO> menuList = this.loadMenuIfUpdate(maxUpdateTime);
  79 + if (CollUtil.isEmpty(menuList)) {
  80 + return;
  81 + }
  82 +
  83 + // 构建缓存
68 84 ImmutableMap.Builder<Long, SysMenuDO> menuCacheBuilder = ImmutableMap.builder();
69 85 ImmutableMultimap.Builder<String, SysMenuDO> permMenuCacheBuilder = ImmutableMultimap.builder();
70 86 menuList.forEach(menuDO -> {
... ... @@ -73,7 +89,35 @@ public class SysMenuServiceImpl implements SysMenuService {
73 89 });
74 90 menuCache = menuCacheBuilder.build();
75 91 permMenuCache = permMenuCacheBuilder.build();
76   - log.info("[init][初始化菜单数量为 {}]", menuList.size());
  92 + assert menuList.size() > 0; // 断言,避免告警
  93 + maxUpdateTime = menuList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
  94 + log.info("[init][缓存菜单,数量为:{}]", menuList.size());
  95 + }
  96 +
  97 + @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
  98 + public void schedulePeriodicRefresh() {
  99 + init();
  100 + }
  101 +
  102 + /**
  103 + * 如果菜单发生变化,从数据库中获取最新的全量菜单。
  104 + * 如果未发生变化,则返回空
  105 + *
  106 + * @param maxUpdateTime 当前菜单的最大更新时间
  107 + * @return 菜单列表
  108 + */
  109 + private List<SysMenuDO> loadMenuIfUpdate(Date maxUpdateTime) {
  110 + // 第一步,判断是否要更新。
  111 + if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
  112 + log.info("[loadMenuIfUpdate][首次加载全量菜单]");
  113 + } else { // 判断数据库中是否有更新的菜单
  114 + if (!menuMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) {
  115 + return null;
  116 + }
  117 + log.info("[loadMenuIfUpdate][增量加载全量菜单]");
  118 + }
  119 + // 第二步,如果有更新,则从数据库加载所有菜单
  120 + return menuMapper.selectList();
77 121 }
78 122  
79 123 @Override
... ...