缓存改造:Menu 使用 Redis 作为缓存
This commit is contained in:
parent
2db6a4510c
commit
dfb1bdb9fb
|
@ -25,4 +25,7 @@ public interface MenuMapper extends BaseMapperX<MenuDO> {
|
|||
.eqIfPresent(MenuDO::getStatus, reqVO.getStatus()));
|
||||
}
|
||||
|
||||
default List<MenuDO> selectListByPermission(String permission) {
|
||||
return selectList(MenuDO::getPermission, permission);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,4 +62,12 @@ public interface RedisKeyConstants {
|
|||
*/
|
||||
String DEPT_CHILDREN_ID_LIST_EXPIRE = "30s";
|
||||
|
||||
/**
|
||||
* 拥有权限对应的菜单编号数组的缓存
|
||||
*
|
||||
* KEY 格式:permission_menu_ids::{permission}
|
||||
* 数据类型:String 菜单编号数组
|
||||
*/
|
||||
String PERMISSION_MENU_ID_LIST = "permission_menu_ids";
|
||||
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
package cn.iocoder.yudao.module.system.mq.consumer.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.module.system.mq.message.permission.MenuRefreshMessage;
|
||||
import cn.iocoder.yudao.module.system.service.permission.MenuService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 针对 {@link MenuRefreshMessage} 的消费者
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class MenuRefreshConsumer extends AbstractChannelMessageListener<MenuRefreshMessage> {
|
||||
|
||||
@Resource
|
||||
private MenuService menuService;
|
||||
|
||||
@Override
|
||||
public void onMessage(MenuRefreshMessage message) {
|
||||
log.info("[onMessage][收到 Menu 刷新消息]");
|
||||
menuService.initLocalCache();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package cn.iocoder.yudao.module.system.mq.message.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 菜单数据刷新 Message
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MenuRefreshMessage extends AbstractChannelMessage {
|
||||
|
||||
@Override
|
||||
public String getChannel() {
|
||||
return "system.menu.refresh";
|
||||
}
|
||||
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package cn.iocoder.yudao.module.system.mq.producer.permission;
|
||||
|
||||
import cn.iocoder.yudao.module.system.mq.message.permission.MenuRefreshMessage;
|
||||
import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* Menu 菜单相关消息的 Producer
|
||||
*/
|
||||
@Component
|
||||
public class MenuProducer {
|
||||
|
||||
@Resource
|
||||
private RedisMQTemplate redisMQTemplate;
|
||||
|
||||
/**
|
||||
* 发送 {@link MenuRefreshMessage} 消息
|
||||
*/
|
||||
public void sendMenuRefreshMessage() {
|
||||
MenuRefreshMessage message = new MenuRefreshMessage();
|
||||
redisMQTemplate.send(message);
|
||||
}
|
||||
|
||||
}
|
|
@ -15,11 +15,6 @@ import java.util.List;
|
|||
*/
|
||||
public interface MenuService {
|
||||
|
||||
/**
|
||||
* 初始化菜单的本地缓存
|
||||
*/
|
||||
void initLocalCache();
|
||||
|
||||
/**
|
||||
* 创建菜单
|
||||
*
|
||||
|
@ -66,30 +61,6 @@ public interface MenuService {
|
|||
*/
|
||||
List<MenuDO> getMenuList(MenuListReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 获得所有菜单,从缓存中
|
||||
*
|
||||
* 任一参数为空时,则返回为空
|
||||
*
|
||||
* @param menuTypes 菜单类型数组
|
||||
* @param menusStatuses 菜单状态数组
|
||||
* @return 菜单列表
|
||||
*/
|
||||
List<MenuDO> getMenuListFromCache(Collection<Integer> menuTypes, Collection<Integer> menusStatuses);
|
||||
|
||||
/**
|
||||
* 获得指定编号的菜单数组,从缓存中
|
||||
*
|
||||
* 任一参数为空时,则返回为空
|
||||
*
|
||||
* @param menuIds 菜单编号数组
|
||||
* @param menuTypes 菜单类型数组
|
||||
* @param menusStatuses 菜单状态数组
|
||||
* @return 菜单数组
|
||||
*/
|
||||
List<MenuDO> getMenuListFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,
|
||||
Collection<Integer> menusStatuses);
|
||||
|
||||
/**
|
||||
* 获得权限对应的菜单数组
|
||||
*
|
||||
|
|
|
@ -1,34 +1,25 @@
|
|||
package cn.iocoder.yudao.module.system.service.permission;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuCreateReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.system.convert.permission.MenuConvert;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.permission.MenuMapper;
|
||||
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
|
||||
import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.mq.producer.permission.MenuProducer;
|
||||
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT;
|
||||
|
@ -43,26 +34,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
|||
@Slf4j
|
||||
public class MenuServiceImpl implements MenuService {
|
||||
|
||||
/**
|
||||
* 菜单缓存
|
||||
* key:菜单编号
|
||||
*
|
||||
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
private volatile Map<Long, MenuDO> menuCache;
|
||||
/**
|
||||
* 权限与菜单缓存
|
||||
* key:权限 {@link MenuDO#getPermission()}
|
||||
* value:MenuDO 数组,因为一个权限可能对应多个 MenuDO 对象
|
||||
*
|
||||
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
private volatile Multimap<String, MenuDO> permissionMenuCache;
|
||||
|
||||
@Resource
|
||||
private MenuMapper menuMapper;
|
||||
@Resource
|
||||
|
@ -71,32 +42,6 @@ public class MenuServiceImpl implements MenuService {
|
|||
@Lazy // 延迟,避免循环依赖报错
|
||||
private TenantService tenantService;
|
||||
|
||||
@Resource
|
||||
private MenuProducer menuProducer;
|
||||
|
||||
/**
|
||||
* 初始化 {@link #menuCache} 和 {@link #permissionMenuCache} 缓存
|
||||
*/
|
||||
@Override
|
||||
@PostConstruct
|
||||
public synchronized void initLocalCache() {
|
||||
// 第一步:查询数据
|
||||
List<MenuDO> menuList = menuMapper.selectList();
|
||||
log.info("[initLocalCache][缓存菜单,数量为:{}]", menuList.size());
|
||||
|
||||
// 第二步:构建缓存
|
||||
ImmutableMap.Builder<Long, MenuDO> menuCacheBuilder = ImmutableMap.builder();
|
||||
ImmutableMultimap.Builder<String, MenuDO> permMenuCacheBuilder = ImmutableMultimap.builder();
|
||||
menuList.forEach(menuDO -> {
|
||||
menuCacheBuilder.put(menuDO.getId(), menuDO);
|
||||
if (StrUtil.isNotEmpty(menuDO.getPermission())) { // 会存在 permission 为 null 的情况,导致 put 报 NPE 异常
|
||||
permMenuCacheBuilder.put(menuDO.getPermission(), menuDO);
|
||||
}
|
||||
});
|
||||
menuCache = menuCacheBuilder.build();
|
||||
permissionMenuCache = permMenuCacheBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long createMenu(MenuCreateReqVO reqVO) {
|
||||
// 校验父菜单存在
|
||||
|
@ -108,8 +53,6 @@ public class MenuServiceImpl implements MenuService {
|
|||
MenuDO menu = MenuConvert.INSTANCE.convert(reqVO);
|
||||
initMenuProperty(menu);
|
||||
menuMapper.insert(menu);
|
||||
// 发送刷新消息
|
||||
menuProducer.sendMenuRefreshMessage();
|
||||
// 返回
|
||||
return menu.getId();
|
||||
}
|
||||
|
@ -129,8 +72,6 @@ public class MenuServiceImpl implements MenuService {
|
|||
MenuDO updateObject = MenuConvert.INSTANCE.convert(reqVO);
|
||||
initMenuProperty(updateObject);
|
||||
menuMapper.updateById(updateObject);
|
||||
// 发送刷新消息
|
||||
menuProducer.sendMenuRefreshMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -148,15 +89,6 @@ public class MenuServiceImpl implements MenuService {
|
|||
menuMapper.deleteById(menuId);
|
||||
// 删除授予给角色的权限
|
||||
permissionService.processMenuDeleted(menuId);
|
||||
// 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
menuProducer.sendMenuRefreshMessage();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -178,33 +110,9 @@ public class MenuServiceImpl implements MenuService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<MenuDO> getMenuListFromCache(Collection<Integer> menuTypes, Collection<Integer> menusStatuses) {
|
||||
// 任一一个参数为空,则返回空
|
||||
if (CollectionUtils.isAnyEmpty(menuTypes, menusStatuses)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 创建新数组,避免缓存被修改
|
||||
return menuCache.values().stream().filter(menu -> menuTypes.contains(menu.getType())
|
||||
&& menusStatuses.contains(menu.getStatus()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MenuDO> getMenuListFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,
|
||||
Collection<Integer> menusStatuses) {
|
||||
// 任一一个参数为空,则返回空
|
||||
if (CollectionUtils.isAnyEmpty(menuIds, menuTypes, menusStatuses)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return menuCache.values().stream().filter(menu -> menuIds.contains(menu.getId())
|
||||
&& menuTypes.contains(menu.getType())
|
||||
&& menusStatuses.contains(menu.getStatus()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, key = "#permission")
|
||||
public List<MenuDO> getMenuListByPermissionFromCache(String permission) {
|
||||
return new ArrayList<>(permissionMenuCache.get(permission));
|
||||
return menuMapper.selectListByPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -190,7 +190,7 @@ yudao:
|
|||
- tmp_report_data_1
|
||||
- tmp_report_data_income
|
||||
ignore-caches:
|
||||
- demo
|
||||
- permission_menu_ids
|
||||
sms-code: # 短信验证码相关的配置项
|
||||
expire-times: 10m
|
||||
send-frequency: 1m
|
||||
|
|
Loading…
Reference in New Issue