优化本地缓存的刷新实现,数据变更时,强制刷新
parent
81340a636b
commit
6f8ca56e16
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定租户,执行对应的逻辑
|
||||
*
|
||||
|
|
|
@ -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<FileConfigDO> 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<FileConfigDO> 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<FileConfigDO> 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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 = "单测文件名";
|
||||
// 调用
|
||||
|
|
|
@ -23,7 +23,7 @@ public class SmsChannelRefreshConsumer {
|
|||
@EventListener
|
||||
public void execute(SmsChannelRefreshMessage message) {
|
||||
log.info("[execute][收到 SmsChannel 刷新消息]");
|
||||
smsChannelService.initSmsClients();
|
||||
smsChannelService.initLocalCache();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<DeptDO> deptList = loadDeptIfUpdate(maxUpdateTime);
|
||||
if (CollUtil.isEmpty(deptList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建缓存
|
||||
ImmutableMap.Builder<Long, DeptDO> builder = ImmutableMap.builder();
|
||||
ImmutableMultimap.Builder<Long, DeptDO> 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<DeptDO> 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<DeptDO> depts = deptMapper.selectList();
|
||||
log.info("[initLocalCacheIfUpdate][缓存部门,数量为:{}]", 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);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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<OAuth2ClientDO> 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<OAuth2ClientDO> 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<OAuth2ClientDO> clients = oauth2ClientMapper.selectList();
|
||||
log.info("[initLocalCacheIfUpdate][缓存 OAuth2 客户端,数量为:{}]", clients.size());
|
||||
|
||||
// 第二步:构建缓存。
|
||||
clientCache = convertMap(clients, OAuth2ClientDO::getClientId);
|
||||
|
||||
// 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
|
||||
this.maxUpdateTime = getMaxValue(clients, OAuth2ClientDO::getUpdateTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -86,13 +86,33 @@ public class MenuServiceImpl implements MenuService {
|
|||
@Override
|
||||
@PostConstruct
|
||||
public synchronized void initLocalCache() {
|
||||
// 获取菜单列表,如果有更新
|
||||
List<MenuDO> 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<MenuDO> menuList = menuMapper.selectList();
|
||||
log.info("[initLocalCacheIfUpdate][缓存菜单,数量为:{}]", menuList.size());
|
||||
|
||||
// 构建缓存
|
||||
// 第二步:构建缓存。
|
||||
ImmutableMap.Builder<Long, MenuDO> menuCacheBuilder = ImmutableMap.builder();
|
||||
ImmutableMultimap.Builder<String, MenuDO> 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<MenuDO> 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
|
||||
|
|
|
@ -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<RoleMenuDO> roleMenuList = loadRoleMenuIfUpdate(roleMenuMaxUpdateTime);
|
||||
if (CollUtil.isEmpty(roleMenuList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化 roleMenuCache 和 menuRoleCache 缓存
|
||||
ImmutableMultimap.Builder<Long, Long> roleMenuCacheBuilder = ImmutableMultimap.builder();
|
||||
ImmutableMultimap.Builder<Long, Long> 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<UserRoleDO> userRoleList = loadUserRoleIfUpdate(userRoleMaxUpdateTime);
|
||||
if (CollUtil.isEmpty(userRoleList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化 userRoleCache 缓存
|
||||
ImmutableMultimap.Builder<Long, Long> 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<RoleMenuDO> 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<RoleMenuDO> roleMenus = roleMenuMapper.selectList();
|
||||
log.info("[initLocalCacheIfUpdateForRoleMenu][缓存角色与菜单,数量为:{}]", roleMenus.size());
|
||||
|
||||
// 第二步:构建缓存。
|
||||
ImmutableMultimap.Builder<Long, Long> roleMenuCacheBuilder = ImmutableMultimap.builder();
|
||||
ImmutableMultimap.Builder<Long, Long> 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<UserRoleDO> 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<UserRoleDO> userRoles = userRoleMapper.selectList();
|
||||
log.info("[initLocalCacheIfUpdateForUserRole][缓存用户与角色,数量为:{}]", 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);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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<RoleDO> 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<RoleDO> 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<RoleDO> roleList = roleMapper.selectList();
|
||||
log.info("[initLocalCacheIfUpdate][缓存角色,数量为:{}]", roleList.size());
|
||||
|
||||
// 第二步:构建缓存。
|
||||
roleCache = CollectionUtils.convertMap(roleList, RoleDO::getId);
|
||||
|
||||
// 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
|
||||
this.maxUpdateTime = CollectionUtils.getMaxValue(roleList, RoleDO::getUpdateTime);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -84,21 +84,42 @@ public class SensitiveWordServiceImpl implements SensitiveWordService {
|
|||
@Override
|
||||
@PostConstruct
|
||||
public void initLocalCache() {
|
||||
// 获取敏感词列表,如果有更新
|
||||
List<SensitiveWordDO> 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<SensitiveWordDO> sensitiveWords = sensitiveWordMapper.selectList();
|
||||
log.info("[initLocalCacheIfUpdate][缓存敏感词,数量为:{}]", sensitiveWords.size());
|
||||
|
||||
// 第二步:构建缓存。
|
||||
// 写入 sensitiveWordTagsCache 缓存
|
||||
Set<String> 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<SensitiveWordDO> 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<SensitiveWordDO> 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) {
|
||||
// 校验唯一性
|
||||
|
|
|
@ -21,7 +21,7 @@ public interface SmsChannelService {
|
|||
/**
|
||||
* 初始化短信客户端
|
||||
*/
|
||||
void initSmsClients();
|
||||
void initLocalCache();
|
||||
|
||||
/**
|
||||
* 创建短信渠道
|
||||
|
|
|
@ -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<SmsChannelDO> smsChannels = this.loadSmsChannelIfUpdate(maxUpdateTime);
|
||||
if (CollUtil.isEmpty(smsChannels)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建或更新短信 Client
|
||||
List<SmsChannelProperties> 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<SmsChannelDO> 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<SmsChannelDO> channels = smsChannelMapper.selectList();
|
||||
log.info("[initLocalCacheIfUpdate][缓存短信渠道,数量为:{}]", channels.size());
|
||||
|
||||
// 第二步:构建缓存。创建或更新短信 Client
|
||||
List<SmsChannelProperties> propertiesList = SmsChannelConvert.INSTANCE.convertList02(channels);
|
||||
propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties));
|
||||
|
||||
// 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
|
||||
this.maxUpdateTime = getMaxValue(channels, SmsChannelDO::getUpdateTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue