From 6f8ca56e1649094c0936366a65aab87ff85864bc Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 29 Dec 2022 00:11:11 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=9C=AC=E5=9C=B0=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E7=9A=84=E5=88=B7=E6=96=B0=E5=AE=9E=E7=8E=B0=EF=BC=8C?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=8F=98=E6=9B=B4=E6=97=B6=EF=BC=8C=E5=BC=BA?= =?UTF-8?q?=E5=88=B6=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tenant/core/aop/TenantIgnoreAspect.java | 3 + .../tenant/core/util/TenantUtils.java | 16 ++ .../service/file/FileConfigServiceImpl.java | 65 ++++---- .../file/FileConfigServiceImplTest.java | 2 +- .../infra/service/file/FileServiceTest.java | 2 +- .../sms/SmsChannelRefreshConsumer.java | 2 +- .../system/service/dept/DeptServiceImpl.java | 78 +++++----- .../oauth2/OAuth2ClientServiceImpl.java | 48 +++--- .../service/permission/MenuServiceImpl.java | 57 ++++--- .../permission/PermissionServiceImpl.java | 142 ++++++++---------- .../service/permission/RoleServiceImpl.java | 52 +++---- .../SensitiveWordServiceImpl.java | 64 ++++---- .../system/service/sms/SmsChannelService.java | 2 +- .../service/sms/SmsChannelServiceImpl.java | 55 +++---- .../permission/PermissionServiceTest.java | 8 +- .../service/sms/SmsChannelServiceTest.java | 2 +- 16 files changed, 276 insertions(+), 322 deletions(-) diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/aop/TenantIgnoreAspect.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/aop/TenantIgnoreAspect.java index 89dfeff4b..b7d0fa362 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/aop/TenantIgnoreAspect.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/aop/TenantIgnoreAspect.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.framework.tenant.core.aop; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; @@ -11,6 +12,8 @@ import org.aspectj.lang.annotation.Aspect; * 例如说,一个定时任务,读取所有数据,进行处理。 * 又例如说,读取所有数据,进行缓存。 * + * 整体逻辑的实现,和 {@link TenantUtils#executeIgnore(Runnable)} 需要保持一致 + * * @author 芋道源码 */ @Aspect diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java index e47288ab6..9198b73f1 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java @@ -34,6 +34,22 @@ public class TenantUtils { } } + /** + * 忽略租户,执行对应的逻辑 + * + * @param runnable 逻辑 + */ + public static void executeIgnore(Runnable runnable) { + Boolean oldIgnore = TenantContextHolder.isIgnore(); + try { + TenantContextHolder.setIgnore(true); + // 执行逻辑 + runnable.run(); + } finally { + TenantContextHolder.setIgnore(oldIgnore); + } + } + /** * 使用指定租户,执行对应的逻辑 * diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java index e39f4028a..d0afab53d 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.infra.service.file; -import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.util.IdUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -20,7 +19,6 @@ import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper; import cn.iocoder.yudao.module.infra.mq.producer.file.FileConfigProducer; 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; @@ -79,20 +77,36 @@ public class FileConfigServiceImpl implements FileConfigService { @Resource private Validator validator; - @Resource - @Lazy // 注入自己,所以延迟加载 - private FileConfigService self; - @Override @PostConstruct public void initFileClients() { - // 获取文件配置,如果有更新 - List configs = loadFileConfigIfUpdate(maxUpdateTime); - if (CollUtil.isEmpty(configs)) { + 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 + && fileConfigMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { + log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime); return; } + List configs = fileConfigMapper.selectList(); + log.info("[initLocalCacheIfUpdate][缓存文件配置,数量为:{}]", configs.size()); - // 创建或更新支付 Client + // 第二步:构建缓存。创建或更新文件 Client configs.forEach(config -> { fileClientFactory.createOrUpdateFileClient(config.getId(), config.getStorage(), config.getConfig()); // 如果是 master,进行设置 @@ -101,35 +115,8 @@ public class FileConfigServiceImpl implements FileConfigService { } }); - // 写入缓存 - maxUpdateTime = CollectionUtils.getMaxValue(configs, FileConfigDO::getUpdateTime); - log.info("[initFileClients][初始化 FileConfig 数量为 {}]", configs.size()); - } - - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) - public void schedulePeriodicRefresh() { - self.initFileClients(); - } - - /** - * 如果文件配置发生变化,从数据库中获取最新的全量文件配置。 - * 如果未发生变化,则返回空 - * - * @param maxUpdateTime 当前文件配置的最大更新时间 - * @return 文件配置列表 - */ - private List loadFileConfigIfUpdate(LocalDateTime maxUpdateTime) { - // 第一步,判断是否要更新。 - if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 - log.info("[loadFileConfigIfUpdate][首次加载全量文件配置]"); - } else { // 判断数据库中是否有更新的文件配置 - if (fileConfigMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { - return null; - } - log.info("[loadFileConfigIfUpdate][增量加载全量文件配置]"); - } - // 第二步,如果有更新,则从数据库加载所有文件配置 - return fileConfigMapper.selectList(); + // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。 + this.maxUpdateTime = CollectionUtils.getMaxValue(configs, FileConfigDO::getUpdateTime); } @Override diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java index 1b28b0e12..96581d744 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java @@ -240,7 +240,7 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { // mock 获得 Client FileClient fileClient = mock(FileClient.class); when(fileClientFactory.getFileClient(eq(id))).thenReturn(fileClient); - when(fileClient.upload(any(), any())).thenReturn("https://www.iocoder.cn"); + when(fileClient.upload(any(), any(), any())).thenReturn("https://www.iocoder.cn"); // 调用,并断言 assertEquals("https://www.iocoder.cn", fileConfigService.testFileConfig(id)); diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceTest.java index 33473360a..511172ce9 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceTest.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceTest.java @@ -78,7 +78,7 @@ public class FileServiceTest extends BaseDbUnitTest { FileClient client = mock(FileClient.class); when(fileConfigService.getMasterFileClient()).thenReturn(client); String url = randomString(); - when(client.upload(same(content), same(path))).thenReturn(url); + when(client.upload(same(content), same(path), same("image/jpeg"))).thenReturn(url); when(client.getId()).thenReturn(10L); String name = "单测文件名"; // 调用 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsChannelRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsChannelRefreshConsumer.java index 35c5eb4e0..1bc38f19e 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsChannelRefreshConsumer.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsChannelRefreshConsumer.java @@ -23,7 +23,7 @@ public class SmsChannelRefreshConsumer { @EventListener public void execute(SmsChannelRefreshMessage message) { log.info("[execute][收到 SmsChannel 刷新消息]"); - smsChannelService.initSmsClients(); + smsChannelService.initLocalCache(); } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java index 64a195c85..dbdedd2f8 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java @@ -4,7 +4,7 @@ import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateReqVO; @@ -17,7 +17,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; 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.validation.annotation.Validated; @@ -73,58 +72,55 @@ public class DeptServiceImpl implements DeptService { @Resource private DeptProducer deptProducer; - @Resource - @Lazy // 注入自己,所以延迟加载 - private DeptService self; - + /** + * 初始化 {@link #parentDeptCache} 和 {@link #deptCache} 缓存 + */ @Override @PostConstruct - @TenantIgnore // 初始化缓存,无需租户过滤 public synchronized void initLocalCache() { - // 获取部门列表,如果有更新 - List deptList = loadDeptIfUpdate(maxUpdateTime); - if (CollUtil.isEmpty(deptList)) { - return; - } - - // 构建缓存 - ImmutableMap.Builder builder = ImmutableMap.builder(); - ImmutableMultimap.Builder parentBuilder = ImmutableMultimap.builder(); - deptList.forEach(sysRoleDO -> { - builder.put(sysRoleDO.getId(), sysRoleDO); - parentBuilder.put(sysRoleDO.getParentId(), sysRoleDO); - }); - // 设置缓存 - deptCache = builder.build(); - parentDeptCache = parentBuilder.build(); - maxUpdateTime = CollectionUtils.getMaxValue(deptList, DeptDO::getUpdateTime); - log.info("[initLocalCache][初始化 Dept 数量为 {}]", deptList.size()); + initLocalCacheIfUpdate(null); } @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) public void schedulePeriodicRefresh() { - self.initLocalCache(); + initLocalCacheIfUpdate(this.maxUpdateTime); } /** - * 如果部门发生变化,从数据库中获取最新的全量部门。 - * 如果未发生变化,则返回空 + * 刷新本地缓存 * - * @param maxUpdateTime 当前部门的最大更新时间 - * @return 部门列表 + * @param maxUpdateTime 最大更新时间 + * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存 + * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存 */ - protected List loadDeptIfUpdate(LocalDateTime maxUpdateTime) { - // 第一步,判断是否要更新。 - if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 - log.info("[loadMenuIfUpdate][首次加载全量部门]"); - } else { // 判断数据库中是否有更新的部门 - if (deptMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { - return null; + private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) { + // 注意:忽略自动多租户,因为要全局初始化缓存 + TenantUtils.executeIgnore(() -> { + // 第一步:基于 maxUpdateTime 判断缓存是否刷新。 + // 如果没有增量的数据变化,则不进行本地缓存的刷新 + if (maxUpdateTime != null + && deptMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { + log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime); + return; } - log.info("[loadMenuIfUpdate][增量加载全量部门]"); - } - // 第二步,如果有更新,则从数据库加载所有部门 - return deptMapper.selectList(); + List depts = deptMapper.selectList(); + log.info("[initLocalCacheIfUpdate][缓存部门,数量为:{}]", depts.size()); + + // 第二步:构建缓存。创建或更新支付 Client + // 构建缓存 + ImmutableMap.Builder builder = ImmutableMap.builder(); + ImmutableMultimap.Builder 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); + }); } @Override diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java index ccad3b9a7..d9cadd1aa 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java @@ -25,7 +25,6 @@ import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.time.LocalDateTime; import java.util.Collection; -import java.time.LocalDateTime; import java.util.List; import java.util.Map; @@ -77,42 +76,37 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService { @Override @PostConstruct public void initLocalCache() { - // 获取客户端列表,如果有更新 - List tenantList = loadOAuth2ClientIfUpdate(maxUpdateTime); - if (CollUtil.isEmpty(tenantList)) { - return; - } - - // 写入缓存 - clientCache = convertMap(tenantList, OAuth2ClientDO::getClientId); - maxUpdateTime = getMaxValue(tenantList, OAuth2ClientDO::getUpdateTime); - log.info("[initLocalCache][初始化 OAuth2Client 数量为 {}]", tenantList.size()); + initLocalCacheIfUpdate(null); } @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) public void schedulePeriodicRefresh() { - initLocalCache(); + initLocalCacheIfUpdate(this.maxUpdateTime); } /** - * 如果客户端发生变化,从数据库中获取最新的全量客户端。 - * 如果未发生变化,则返回空 + * 刷新本地缓存 * - * @param maxUpdateTime 当前客户端的最大更新时间 - * @return 客户端列表 + * @param maxUpdateTime 最大更新时间 + * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存 + * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存 */ - private List loadOAuth2ClientIfUpdate(LocalDateTime maxUpdateTime) { - // 第一步,判断是否要更新。 - if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 - log.info("[loadOAuth2ClientIfUpdate][首次加载全量客户端]"); - } else { // 判断数据库中是否有更新的客户端 - if (oauth2ClientMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { - return null; - } - log.info("[loadOAuth2ClientIfUpdate][增量加载全量客户端]"); + private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) { + // 第一步:基于 maxUpdateTime 判断缓存是否刷新。 + // 如果没有增量的数据变化,则不进行本地缓存的刷新 + if (maxUpdateTime != null + && oauth2ClientMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { + log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime); + return; } - // 第二步,如果有更新,则从数据库加载所有客户端 - return oauth2ClientMapper.selectList(); + List clients = oauth2ClientMapper.selectList(); + log.info("[initLocalCacheIfUpdate][缓存 OAuth2 客户端,数量为:{}]", clients.size()); + + // 第二步:构建缓存。 + clientCache = convertMap(clients, OAuth2ClientDO::getClientId); + + // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。 + this.maxUpdateTime = getMaxValue(clients, OAuth2ClientDO::getUpdateTime); } @Override diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java index 3d135177c..180fcf808 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java @@ -86,13 +86,33 @@ public class MenuServiceImpl implements MenuService { @Override @PostConstruct public synchronized void initLocalCache() { - // 获取菜单列表,如果有更新 - List menuList = this.loadMenuIfUpdate(maxUpdateTime); - if (CollUtil.isEmpty(menuList)) { + 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 menuList = menuMapper.selectList(); + log.info("[initLocalCacheIfUpdate][缓存菜单,数量为:{}]", menuList.size()); - // 构建缓存 + // 第二步:构建缓存。 ImmutableMap.Builder menuCacheBuilder = ImmutableMap.builder(); ImmutableMultimap.Builder permMenuCacheBuilder = ImmutableMultimap.builder(); menuList.forEach(menuDO -> { @@ -103,34 +123,9 @@ public class MenuServiceImpl implements MenuService { }); menuCache = menuCacheBuilder.build(); permissionMenuCache = permMenuCacheBuilder.build(); - maxUpdateTime = CollectionUtils.getMaxValue(menuList, MenuDO::getUpdateTime); - log.info("[initLocalCache][缓存菜单,数量为:{}]", menuList.size()); - } - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) - public void schedulePeriodicRefresh() { - initLocalCache(); - } - - /** - * 如果菜单发生变化,从数据库中获取最新的全量菜单。 - * 如果未发生变化,则返回空 - * - * @param maxUpdateTime 当前菜单的最大更新时间 - * @return 菜单列表 - */ - private List loadMenuIfUpdate(LocalDateTime maxUpdateTime) { - // 第一步,判断是否要更新。 - if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 - log.info("[loadMenuIfUpdate][首次加载全量菜单]"); - } else { // 判断数据库中是否有更新的菜单 - if (menuMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { - return null; - } - log.info("[loadMenuIfUpdate][增量加载全量菜单]"); - } - // 第二步,如果有更新,则从数据库加载所有菜单 - return menuMapper.selectList(); + // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。 + this.maxUpdateTime = CollectionUtils.getMaxValue(menuList, MenuDO::getUpdateTime); } @Override diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java index 0270187fc..bc4d51b8e 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java @@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; @@ -31,7 +32,6 @@ import com.google.common.collect.Sets; import lombok.Getter; import lombok.Setter; 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; @@ -126,106 +126,84 @@ public class PermissionServiceImpl implements PermissionService { @Resource private PermissionProducer permissionProducer; - @Resource - @Lazy // 注入自己,所以延迟加载 - private PermissionService self; - @Override @PostConstruct - @TenantIgnore // 初始化缓存,无需租户过滤 public void initLocalCache() { - initUserRoleLocalCache(); - initRoleMenuLocalCache(); - } - - /** - * 初始化 {@link #roleMenuCache} 和 {@link #menuRoleCache} 缓存 - */ - @VisibleForTesting - void initRoleMenuLocalCache() { - // 获取角色与菜单的关联列表,如果有更新 - List roleMenuList = loadRoleMenuIfUpdate(roleMenuMaxUpdateTime); - if (CollUtil.isEmpty(roleMenuList)) { - return; - } - - // 初始化 roleMenuCache 和 menuRoleCache 缓存 - ImmutableMultimap.Builder roleMenuCacheBuilder = ImmutableMultimap.builder(); - ImmutableMultimap.Builder menuRoleCacheBuilder = ImmutableMultimap.builder(); - roleMenuList.forEach(roleMenuDO -> { - roleMenuCacheBuilder.put(roleMenuDO.getRoleId(), roleMenuDO.getMenuId()); - menuRoleCacheBuilder.put(roleMenuDO.getMenuId(), roleMenuDO.getRoleId()); - }); - roleMenuCache = roleMenuCacheBuilder.build(); - menuRoleCache = menuRoleCacheBuilder.build(); - roleMenuMaxUpdateTime = getMaxValue(roleMenuList, RoleMenuDO::getUpdateTime); - log.info("[initRoleMenuLocalCache][初始化角色与菜单的关联数量为 {}]", roleMenuList.size()); - } - - /** - * 初始化 {@link #userRoleCache} 缓存 - */ - @VisibleForTesting - void initUserRoleLocalCache() { - // 获取用户与角色的关联列表,如果有更新 - List userRoleList = loadUserRoleIfUpdate(userRoleMaxUpdateTime); - if (CollUtil.isEmpty(userRoleList)) { - return; - } - - // 初始化 userRoleCache 缓存 - ImmutableMultimap.Builder userRoleCacheBuilder = ImmutableMultimap.builder(); - userRoleList.forEach(userRoleDO -> userRoleCacheBuilder.put(userRoleDO.getUserId(), userRoleDO.getRoleId())); - userRoleCache = CollectionUtils.convertMultiMap2(userRoleList, UserRoleDO::getUserId, UserRoleDO::getRoleId); - userRoleMaxUpdateTime = getMaxValue(userRoleList, UserRoleDO::getUpdateTime); - log.info("[initUserRoleLocalCache][初始化用户与角色的关联数量为 {}]", userRoleList.size()); + initLocalCacheIfUpdateForRoleMenu(null); + initLocalCacheIfUpdateForUserRole(null); } @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) public void schedulePeriodicRefresh() { - self.initLocalCache(); + initLocalCacheIfUpdateForRoleMenu(this.roleMenuMaxUpdateTime); + initLocalCacheIfUpdateForUserRole(this.userRoleMaxUpdateTime); } /** - * 如果角色与菜单的关联发生变化,从数据库中获取最新的全量角色与菜单的关联。 - * 如果未发生变化,则返回空 + * 刷新 RoleMenu 本地缓存 * - * @param maxUpdateTime 当前角色与菜单的关联的最大更新时间 - * @return 角色与菜单的关联列表 + * @param maxUpdateTime 最大更新时间 + * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存 + * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存 */ - protected List loadRoleMenuIfUpdate(LocalDateTime maxUpdateTime) { - // 第一步,判断是否要更新。 - if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 - log.info("[loadRoleMenuIfUpdate][首次加载全量角色与菜单的关联]"); - } else { // 判断数据库中是否有更新的角色与菜单的关联 - if (roleMenuMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { - return null; + @VisibleForTesting + void initLocalCacheIfUpdateForRoleMenu(LocalDateTime maxUpdateTime) { + // 注意:忽略自动多租户,因为要全局初始化缓存 + TenantUtils.executeIgnore(() -> { + // 第一步:基于 maxUpdateTime 判断缓存是否刷新。 + // 如果没有增量的数据变化,则不进行本地缓存的刷新 + if (maxUpdateTime != null + && roleMenuMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { + log.info("[initLocalCacheIfUpdateForRoleMenu][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime); + return; } - log.info("[loadRoleMenuIfUpdate][增量加载全量角色与菜单的关联]"); - } - // 第二步,如果有更新,则从数据库加载所有角色与菜单的关联 - return roleMenuMapper.selectList(); + List roleMenus = roleMenuMapper.selectList(); + log.info("[initLocalCacheIfUpdateForRoleMenu][缓存角色与菜单,数量为:{}]", roleMenus.size()); + + // 第二步:构建缓存。 + ImmutableMultimap.Builder roleMenuCacheBuilder = ImmutableMultimap.builder(); + ImmutableMultimap.Builder menuRoleCacheBuilder = ImmutableMultimap.builder(); + roleMenus.forEach(roleMenuDO -> { + roleMenuCacheBuilder.put(roleMenuDO.getRoleId(), roleMenuDO.getMenuId()); + menuRoleCacheBuilder.put(roleMenuDO.getMenuId(), roleMenuDO.getRoleId()); + }); + roleMenuCache = roleMenuCacheBuilder.build(); + menuRoleCache = menuRoleCacheBuilder.build(); + + // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。 + this.roleMenuMaxUpdateTime = getMaxValue(roleMenus, RoleMenuDO::getUpdateTime); + }); } /** - * 如果用户与角色的关联发生变化,从数据库中获取最新的全量用户与角色的关联。 - * 如果未发生变化,则返回空 + * 刷新 UserRole 本地缓存 * - * @param maxUpdateTime 当前角色与菜单的关联的最大更新时间 - * @return 角色与菜单的关联列表 + * @param maxUpdateTime 最大更新时间 + * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存 + * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存 */ - protected List loadUserRoleIfUpdate(LocalDateTime maxUpdateTime) { - // 第一步,判断是否要更新。 - if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 - log.info("[loadUserRoleIfUpdate][首次加载全量用户与角色的关联]"); - } else { // 判断数据库中是否有更新的用户与角色的关联 - if (userRoleMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { - return null; + @VisibleForTesting + void initLocalCacheIfUpdateForUserRole(LocalDateTime maxUpdateTime) { + // 注意:忽略自动多租户,因为要全局初始化缓存 + TenantUtils.executeIgnore(() -> { + // 第一步:基于 maxUpdateTime 判断缓存是否刷新。 + // 如果没有增量的数据变化,则不进行本地缓存的刷新 + if (maxUpdateTime != null + && userRoleMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { + log.info("[initLocalCacheIfUpdateForUserRole][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime); + return; } - log.info("[loadUserRoleIfUpdate][增量加载全量用户与角色的关联]"); - } - // 第二步,如果有更新,则从数据库加载所有用户与角色的关联 - return userRoleMapper.selectList(); + List userRoles = userRoleMapper.selectList(); + log.info("[initLocalCacheIfUpdateForUserRole][缓存用户与角色,数量为:{}]", userRoles.size()); + + // 第二步:构建缓存。 + ImmutableMultimap.Builder 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); + }); } @Override diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java index e3c8e36c2..3121640bb 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleExportReqVO; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; @@ -86,44 +87,41 @@ public class RoleServiceImpl implements RoleService { */ @Override @PostConstruct - @TenantIgnore // 忽略自动多租户,全局初始化缓存 public void initLocalCache() { - // 获取角色列表,如果有更新 - List roleList = loadRoleIfUpdate(maxUpdateTime); - if (CollUtil.isEmpty(roleList)) { - return; - } - - // 写入缓存 - roleCache = CollectionUtils.convertMap(roleList, RoleDO::getId); - maxUpdateTime = CollectionUtils.getMaxValue(roleList, RoleDO::getUpdateTime); - log.info("[initLocalCache][初始化 Role 数量为 {}]", roleList.size()); + initLocalCacheIfUpdate(null); } @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) public void schedulePeriodicRefresh() { - self.initLocalCache(); + initLocalCacheIfUpdate(this.maxUpdateTime); } /** - * 如果角色发生变化,从数据库中获取最新的全量角色。 - * 如果未发生变化,则返回空 + * 刷新本地缓存 * - * @param maxUpdateTime 当前角色的最大更新时间 - * @return 角色列表 + * @param maxUpdateTime 最大更新时间 + * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存 + * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存 */ - private List loadRoleIfUpdate(LocalDateTime maxUpdateTime) { - // 第一步,判断是否要更新。 - if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 - log.info("[loadRoleIfUpdate][首次加载全量角色]"); - } else { // 判断数据库中是否有更新的角色 - if (roleMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { - return null; + private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) { + // 注意:忽略自动多租户,因为要全局初始化缓存 + TenantUtils.executeIgnore(() -> { + // 第一步:基于 maxUpdateTime 判断缓存是否刷新。 + // 如果没有增量的数据变化,则不进行本地缓存的刷新 + if (maxUpdateTime != null + && roleMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { + log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime); + return; } - log.info("[loadRoleIfUpdate][增量加载全量角色]"); - } - // 第二步,如果有更新,则从数据库加载所有角色 - return roleMapper.selectList(); + List roleList = roleMapper.selectList(); + log.info("[initLocalCacheIfUpdate][缓存角色,数量为:{}]", roleList.size()); + + // 第二步:构建缓存。 + roleCache = CollectionUtils.convertMap(roleList, RoleDO::getId); + + // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。 + this.maxUpdateTime = CollectionUtils.getMaxValue(roleList, RoleDO::getUpdateTime); + }); } @Override diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImpl.java index 3ec581a67..cc7975bfa 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImpl.java @@ -84,21 +84,42 @@ public class SensitiveWordServiceImpl implements SensitiveWordService { @Override @PostConstruct public void initLocalCache() { - // 获取敏感词列表,如果有更新 - List sensitiveWordList = loadSensitiveWordIfUpdate(maxUpdateTime); - if (CollUtil.isEmpty(sensitiveWordList)) { + 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 sensitiveWords = sensitiveWordMapper.selectList(); + log.info("[initLocalCacheIfUpdate][缓存敏感词,数量为:{}]", sensitiveWords.size()); + // 第二步:构建缓存。 // 写入 sensitiveWordTagsCache 缓存 Set tags = new HashSet<>(); - sensitiveWordList.forEach(word -> tags.addAll(word.getTags())); + sensitiveWords.forEach(word -> tags.addAll(word.getTags())); sensitiveWordTagsCache = tags; // 写入 defaultSensitiveWordTrie、tagSensitiveWordTries 缓存 - initSensitiveWordTrie(sensitiveWordList); - // 写入 maxUpdateTime 最大更新时间 - maxUpdateTime = CollectionUtils.getMaxValue(sensitiveWordList, SensitiveWordDO::getUpdateTime); - log.info("[initLocalCache][初始化 敏感词 数量为 {}]", sensitiveWordList.size()); + initSensitiveWordTrie(sensitiveWords); + + // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。 + this.maxUpdateTime = CollectionUtils.getMaxValue(sensitiveWords, SensitiveWordDO::getUpdateTime); } private void initSensitiveWordTrie(List wordDOs) { @@ -122,33 +143,6 @@ public class SensitiveWordServiceImpl implements SensitiveWordService { this.tagSensitiveWordTries = tagSensitiveWordTries; } - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) - public void schedulePeriodicRefresh() { - initLocalCache(); - } - - /** - * 如果敏感词发生变化,从数据库中获取最新的全量敏感词。 - * 如果未发生变化,则返回空 - * - * @param maxUpdateTime 当前敏感词的最大更新时间 - * @return 敏感词列表 - */ - private List loadSensitiveWordIfUpdate(LocalDateTime maxUpdateTime) { - // 第一步,判断是否要更新。 - // 如果更新时间为空,说明 DB 一定有新数据 - if (maxUpdateTime == null) { - log.info("[loadSensitiveWordIfUpdate][首次加载全量敏感词]"); - } else { // 判断数据库中是否有更新的敏感词 - if (sensitiveWordMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { - return null; - } - log.info("[loadSensitiveWordIfUpdate][增量加载全量敏感词]"); - } - // 第二步,如果有更新,则从数据库加载所有敏感词 - return sensitiveWordMapper.selectList(); - } - @Override public Long createSensitiveWord(SensitiveWordCreateReqVO createReqVO) { // 校验唯一性 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelService.java index 50e13a602..fb0b707d7 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelService.java @@ -21,7 +21,7 @@ public interface SmsChannelService { /** * 初始化短信客户端 */ - void initSmsClients(); + void initLocalCache(); /** * 创建短信渠道 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java index 74852587c..7aaccce93 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java @@ -23,6 +23,7 @@ import java.time.LocalDateTime; 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; @@ -30,7 +31,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNE * 短信渠道Service实现类 * * @author zzf - * @date 2021/1/25 9:25 */ @Service @Slf4j @@ -61,46 +61,39 @@ public class SmsChannelServiceImpl implements SmsChannelService { @Override @PostConstruct - public void initSmsClients() { - // 获取短信渠道,如果有更新 - List smsChannels = this.loadSmsChannelIfUpdate(maxUpdateTime); - if (CollUtil.isEmpty(smsChannels)) { - return; - } - - // 创建或更新短信 Client - List propertiesList = SmsChannelConvert.INSTANCE.convertList02(smsChannels); - propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties)); - - // 写入缓存 - maxUpdateTime = CollectionUtils.getMaxValue(smsChannels, SmsChannelDO::getUpdateTime); - log.info("[initSmsClients][初始化 SmsChannel 数量为 {}]", smsChannels.size()); + public void initLocalCache() { + initLocalCacheIfUpdate(null); } @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) public void schedulePeriodicRefresh() { - initSmsClients(); + initLocalCacheIfUpdate(this.maxUpdateTime); } /** - * 如果短信渠道发生变化,从数据库中获取最新的全量短信渠道。 - * 如果未发生变化,则返回空 + * 刷新本地缓存 * - * @param maxUpdateTime 当前短信渠道的最大更新时间 - * @return 短信渠道列表 + * @param maxUpdateTime 最大更新时间 + * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存 + * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存 */ - private List loadSmsChannelIfUpdate(LocalDateTime maxUpdateTime) { - // 第一步,判断是否要更新。 - if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 - log.info("[loadSmsChannelIfUpdate][首次加载全量短信渠道]"); - } else { // 判断数据库中是否有更新的短信渠道 - if (smsChannelMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { - return null; - } - log.info("[loadSmsChannelIfUpdate][增量加载全量短信渠道]"); + private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) { + // 第一步:基于 maxUpdateTime 判断缓存是否刷新。 + // 如果没有增量的数据变化,则不进行本地缓存的刷新 + if (maxUpdateTime != null + && smsChannelMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { + log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime); + return; } - // 第二步,如果有更新,则从数据库加载所有短信渠道 - return smsChannelMapper.selectList(); + List channels = smsChannelMapper.selectList(); + log.info("[initLocalCacheIfUpdate][缓存短信渠道,数量为:{}]", channels.size()); + + // 第二步:构建缓存。创建或更新短信 Client + List propertiesList = SmsChannelConvert.INSTANCE.convertList02(channels); + propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties)); + + // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。 + this.maxUpdateTime = getMaxValue(channels, SmsChannelDO::getUpdateTime); } @Override diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java index 610828b6f..6352c4593 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java @@ -71,7 +71,7 @@ public class PermissionServiceTest extends BaseDbUnitTest { private PermissionProducer permissionProducer; @Test - public void testInitRoleMenuLocalCache() { + public void testInitLocalCacheIfUpdateForRoleMenu() { // mock 数据 RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(1L).setMenuId(10L)); roleMenuMapper.insert(roleMenuDO01); @@ -79,7 +79,7 @@ public class PermissionServiceTest extends BaseDbUnitTest { roleMenuMapper.insert(roleMenuDO02); // 调用 - permissionService.initRoleMenuLocalCache(); + permissionService.initLocalCacheIfUpdateForRoleMenu(null); // 断言 roleMenuCache 缓存 assertEquals(1, permissionService.getRoleMenuCache().keySet().size()); assertEquals(asList(10L, 20L), permissionService.getRoleMenuCache().get(1L)); @@ -93,7 +93,7 @@ public class PermissionServiceTest extends BaseDbUnitTest { } @Test - public void testInitUserRoleLocalCache() { + public void testInitLocalCacheIfUpdateForUserRole() { // mock 数据 UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); userRoleMapper.insert(userRoleDO01); @@ -101,7 +101,7 @@ public class PermissionServiceTest extends BaseDbUnitTest { userRoleMapper.insert(roleMenuDO02); // 调用 - permissionService.initUserRoleLocalCache(); + permissionService.initLocalCacheIfUpdateForUserRole(null); // 断言 roleMenuCache 缓存 assertEquals(1, permissionService.getUserRoleCache().size()); assertEquals(asSet(10L, 20L), permissionService.getUserRoleCache().get(1L)); diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java index 1820bb22a..bac34ca56 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java @@ -57,7 +57,7 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { smsChannelMapper.insert(smsChannelDO02); // 调用 - smsChannelService.initSmsClients(); + smsChannelService.initLocalCache(); // 校验 maxUpdateTime 属性 LocalDateTime maxUpdateTime = (LocalDateTime) BeanUtil.getFieldValue(smsChannelService, "maxUpdateTime"); assertEquals(max(smsChannelDO01.getUpdateTime(), smsChannelDO02.getUpdateTime()), maxUpdateTime);