Commit 79a0b4cb56b323cbf202621e10c34c73963eb1de

Authored by YunaiV
1 parent 7bd81d03

增加菜单的定时刷新

ruoyi-quartz/pom.xml
@@ -28,13 +28,6 @@ @@ -28,13 +28,6 @@
28 </exclusion> 28 </exclusion>
29 </exclusions> 29 </exclusions>
30 </dependency> 30 </dependency>
31 -  
32 - <!-- 通用工具-->  
33 - <dependency>  
34 - <groupId>com.ruoyi</groupId>  
35 - <artifactId>ruoyi-common</artifactId>  
36 - </dependency>  
37 -  
38 </dependencies> 31 </dependencies>
39 32
40 -</project>  
41 \ No newline at end of file 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,6 +23,6 @@ public interface ConfigFrameworkDAO {
23 * 23 *
24 * @return 配置列表 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,7 +73,6 @@ public class DBConfigRepository extends AbstractConfigRepository {
73 if (CollUtil.isEmpty(configs)) { // 如果没有更新,则返回 73 if (CollUtil.isEmpty(configs)) { // 如果没有更新,则返回
74 return; 74 return;
75 } 75 }
76 - log.info("[sync][同步到新配置,配置数量为:{}]", configs.size());  
77 76
78 // 第二步,构建新的 Properties 77 // 第二步,构建新的 Properties
79 Properties newProperties = this.buildProperties(configs); 78 Properties newProperties = this.buildProperties(configs);
@@ -83,6 +82,7 @@ public class DBConfigRepository extends AbstractConfigRepository { @@ -83,6 +82,7 @@ public class DBConfigRepository extends AbstractConfigRepository {
83 this.maxUpdateTime = configs.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime(); 82 this.maxUpdateTime = configs.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
84 // 第四部,触发配置刷新!重要!!!! 83 // 第四部,触发配置刷新!重要!!!!
85 super.fireRepositoryChange(m_namespace, newProperties); 84 super.fireRepositoryChange(m_namespace, newProperties);
  85 + log.info("[sync][缓存配置,数量为:{}]", configs.size());
86 } 86 }
87 87
88 @Override 88 @Override
@@ -150,7 +150,7 @@ public class DBConfigRepository extends AbstractConfigRepository { @@ -150,7 +150,7 @@ public class DBConfigRepository extends AbstractConfigRepository {
150 log.info("[loadConfigIfUpdate][增量加载全量配置]"); 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,7 +32,7 @@ public class InfConfigDAOImpl implements ConfigFrameworkDAO {
32 } 32 }
33 33
34 @Override 34 @Override
35 - public List<InfConfigDO> getSysConfigList() { 35 + public List<InfConfigDO> selectList() {
36 return jdbcTemplate.query("SELECT `key`, `value`, update_time, deleted FROM inf_config", new BeanPropertyRowMapper<>(InfConfigDO.class)); 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,6 +7,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
7 import com.baomidou.mybatisplus.core.mapper.BaseMapper; 7 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
8 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
9 9
  10 +import java.util.Date;
10 import java.util.List; 11 import java.util.List;
11 12
12 @Mapper 13 @Mapper
@@ -30,4 +31,9 @@ public interface SysMenuMapper extends BaseMapper&lt;SysMenuDO&gt; { @@ -30,4 +31,9 @@ public interface SysMenuMapper extends BaseMapper&lt;SysMenuDO&gt; {
30 return selectList(new QueryWrapper<>()); 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 package cn.iocoder.dashboard.modules.system.service.permission.impl; 1 package cn.iocoder.dashboard.modules.system.service.permission.impl;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil; 4 import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
  5 +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
4 import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuCreateReqVO; 6 import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuCreateReqVO;
5 import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuListReqVO; 7 import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuListReqVO;
6 import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuUpdateReqVO; 8 import cn.iocoder.dashboard.modules.system.controller.permission.vo.menu.SysMenuUpdateReqVO;
@@ -16,14 +18,12 @@ import com.google.common.collect.ImmutableMap; @@ -16,14 +18,12 @@ import com.google.common.collect.ImmutableMap;
16 import com.google.common.collect.ImmutableMultimap; 18 import com.google.common.collect.ImmutableMultimap;
17 import com.google.common.collect.Multimap; 19 import com.google.common.collect.Multimap;
18 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
  21 +import org.springframework.scheduling.annotation.Scheduled;
19 import org.springframework.stereotype.Service; 22 import org.springframework.stereotype.Service;
20 23
21 import javax.annotation.PostConstruct; 24 import javax.annotation.PostConstruct;
22 import javax.annotation.Resource; 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 import java.util.stream.Collectors; 27 import java.util.stream.Collectors;
28 28
29 import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; 29 import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
@@ -38,6 +38,12 @@ import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; @@ -38,6 +38,12 @@ import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
38 public class SysMenuServiceImpl implements SysMenuService { 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 * key:菜单编号 48 * key:菜单编号
43 * 49 *
@@ -52,6 +58,10 @@ public class SysMenuServiceImpl implements SysMenuService { @@ -52,6 +58,10 @@ public class SysMenuServiceImpl implements SysMenuService {
52 * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 58 * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
53 */ 59 */
54 private volatile Multimap<String, SysMenuDO> permMenuCache; 60 private volatile Multimap<String, SysMenuDO> permMenuCache;
  61 + /**
  62 + * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
  63 + */
  64 + private volatile Date maxUpdateTime;
55 65
56 @Resource 66 @Resource
57 private SysMenuMapper menuMapper; 67 private SysMenuMapper menuMapper;
@@ -63,8 +73,14 @@ public class SysMenuServiceImpl implements SysMenuService { @@ -63,8 +73,14 @@ public class SysMenuServiceImpl implements SysMenuService {
63 */ 73 */
64 @Override 74 @Override
65 @PostConstruct 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 ImmutableMap.Builder<Long, SysMenuDO> menuCacheBuilder = ImmutableMap.builder(); 84 ImmutableMap.Builder<Long, SysMenuDO> menuCacheBuilder = ImmutableMap.builder();
69 ImmutableMultimap.Builder<String, SysMenuDO> permMenuCacheBuilder = ImmutableMultimap.builder(); 85 ImmutableMultimap.Builder<String, SysMenuDO> permMenuCacheBuilder = ImmutableMultimap.builder();
70 menuList.forEach(menuDO -> { 86 menuList.forEach(menuDO -> {
@@ -73,7 +89,35 @@ public class SysMenuServiceImpl implements SysMenuService { @@ -73,7 +89,35 @@ public class SysMenuServiceImpl implements SysMenuService {
73 }); 89 });
74 menuCache = menuCacheBuilder.build(); 90 menuCache = menuCacheBuilder.build();
75 permMenuCache = permMenuCacheBuilder.build(); 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 @Override 123 @Override