params) {
return StrUtil.format(content, params);
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java
index 260159b79..1d7c32f7d 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java
@@ -16,18 +16,6 @@ import java.util.Map;
*/
public interface NotifyTemplateService {
- /**
- * 初始化站内信模板的本地缓存
- */
- void initLocalCache();
-
- /**
- * 获得站内信模板,从缓存中
- *
- * @param code 模板编码
- * @return 站内信模板
- */
- NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code);
/**
* 创建站内信模版
@@ -58,7 +46,13 @@ public interface NotifyTemplateService {
* @return 站内信模版
*/
NotifyTemplateDO getNotifyTemplate(Long id);
-
+ /**
+ * 获得站内信模板,从缓存中
+ *
+ * @param code 模板编码
+ * @return 站内信模板
+ */
+ NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code);
/**
* 获得站内信模版分页
*
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java
index 5b63ac720..d317a2b33 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java
@@ -3,27 +3,28 @@ package cn.iocoder.yudao.module.system.service.notify;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO;
import cn.iocoder.yudao.module.system.convert.notify.NotifyTemplateConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper;
-import cn.iocoder.yudao.module.system.mq.producer.notify.NotifyProducer;
+import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
-import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_CODE_DUPLICATE;
+import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS;
/**
* 站内信模版 Service 实现类
@@ -43,36 +44,6 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
@Resource
private NotifyTemplateMapper notifyTemplateMapper;
- @Resource
- private NotifyProducer notifyProducer;
-
- /**
- * 站内信模板缓存
- * key:站内信模板编码 {@link NotifyTemplateDO#getCode()}
- *
- * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
- */
- private volatile Map notifyTemplateCache;
-
- /**
- * 初始化站内信模板的本地缓存
- */
- @Override
- @PostConstruct
- public void initLocalCache() {
- // 第一步:查询数据
- List templates = notifyTemplateMapper.selectList();
- log.info("[initLocalCache][缓存站内信模版,数量为:{}]", templates.size());
-
- // 第二步:构建缓存
- notifyTemplateCache = CollectionUtils.convertMap(templates, NotifyTemplateDO::getCode);
- }
-
- @Override
- public NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code) {
- return notifyTemplateCache.get(code);
- }
-
@Override
public Long createNotifyTemplate(NotifyTemplateCreateReqVO createReqVO) {
// 校验站内信编码是否重复
@@ -82,13 +53,12 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
NotifyTemplateDO notifyTemplate = NotifyTemplateConvert.INSTANCE.convert(createReqVO);
notifyTemplate.setParams(parseTemplateContentParams(notifyTemplate.getContent()));
notifyTemplateMapper.insert(notifyTemplate);
-
- // 发送刷新消息
- notifyProducer.sendNotifyTemplateRefreshMessage();
return notifyTemplate.getId();
}
@Override
+ @CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE,
+ allEntries = true) // allEntries 清空所有缓存,因为可能修改到 code 字段,不好清理
public void updateNotifyTemplate(NotifyTemplateUpdateReqVO updateReqVO) {
// 校验存在
validateNotifyTemplateExists(updateReqVO.getId());
@@ -99,9 +69,6 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
NotifyTemplateDO updateObj = NotifyTemplateConvert.INSTANCE.convert(updateReqVO);
updateObj.setParams(parseTemplateContentParams(updateObj.getContent()));
notifyTemplateMapper.updateById(updateObj);
-
- // 发送刷新消息
- notifyProducer.sendNotifyTemplateRefreshMessage();
}
@VisibleForTesting
@@ -110,13 +77,13 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
}
@Override
+ @CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE,
+ allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 code,不好清理
public void deleteNotifyTemplate(Long id) {
// 校验存在
validateNotifyTemplateExists(id);
// 删除
notifyTemplateMapper.deleteById(id);
- // 发送刷新消息
- notifyProducer.sendNotifyTemplateRefreshMessage();
}
private void validateNotifyTemplateExists(Long id) {
@@ -124,6 +91,12 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
throw exception(NOTIFY_TEMPLATE_NOT_EXISTS);
}
}
+ @Override
+ @Cacheable(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, key = "#code",
+ unless = "#result == null")
+ public NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code) {
+ return notifyTemplateMapper.selectByCode(code);
+ }
@Override
public NotifyTemplateDO getNotifyTemplate(Long id) {
@@ -136,7 +109,7 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
}
@VisibleForTesting
- public void validateNotifyTemplateCodeDuplicate(Long id, String code) {
+ void validateNotifyTemplateCodeDuplicate(Long id, String code) {
NotifyTemplateDO template = notifyTemplateMapper.selectByCode(code);
if (template == null) {
return;
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java
index 60e9f2298..de826c261 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java
@@ -18,11 +18,6 @@ import java.util.Collection;
*/
public interface OAuth2ClientService {
- /**
- * 初始化 OAuth2Client 的本地缓存
- */
- void initLocalCache();
-
/**
* 创建 OAuth2 客户端
*
@@ -53,6 +48,14 @@ public interface OAuth2ClientService {
*/
OAuth2ClientDO getOAuth2Client(Long id);
+ /**
+ * 获得 OAuth2 客户端,从缓存中
+ *
+ * @param clientId 客户端编号
+ * @return OAuth2 客户端
+ */
+ OAuth2ClientDO getOAuth2ClientFromCache(String clientId);
+
/**
* 获得 OAuth2 客户端分页
*
@@ -82,7 +85,7 @@ public interface OAuth2ClientService {
* @param redirectUri 重定向地址
* @return 客户端
*/
- OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret,
- String authorizedGrantType, Collection scopes, String redirectUri);
+ OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, String authorizedGrantType,
+ Collection scopes, String redirectUri);
}
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 970387bbb..833b4cde1 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
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.service.oauth2;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
@@ -12,22 +13,18 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2Cl
import cn.iocoder.yudao.module.system.convert.auth.OAuth2ClientConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO;
import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper;
-import cn.iocoder.yudao.module.system.mq.producer.auth.OAuth2ClientProducer;
+import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import com.google.common.annotations.VisibleForTesting;
-import lombok.Getter;
-import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
-import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Collection;
-import java.util.List;
-import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
/**
@@ -40,48 +37,21 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
@Slf4j
public class OAuth2ClientServiceImpl implements OAuth2ClientService {
- /**
- * 客户端缓存
- * key:客户端编号 {@link OAuth2ClientDO#getClientId()} ()}
- *
- * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
- */
- @Getter // 解决单测
- @Setter // 解决单测
- private volatile Map clientCache;
-
@Resource
private OAuth2ClientMapper oauth2ClientMapper;
- @Resource
- private OAuth2ClientProducer oauth2ClientProducer;
-
- /**
- * 初始化 {@link #clientCache} 缓存
- */
- @Override
- @PostConstruct
- public void initLocalCache() {
- // 第一步:查询数据
- List clients = oauth2ClientMapper.selectList();
- log.info("[initLocalCache][缓存 OAuth2 客户端,数量为:{}]", clients.size());
-
- // 第二步:构建缓存。
- clientCache = convertMap(clients, OAuth2ClientDO::getClientId);
- }
-
@Override
public Long createOAuth2Client(OAuth2ClientCreateReqVO createReqVO) {
validateClientIdExists(null, createReqVO.getClientId());
// 插入
OAuth2ClientDO oauth2Client = OAuth2ClientConvert.INSTANCE.convert(createReqVO);
oauth2ClientMapper.insert(oauth2Client);
- // 发送刷新消息
- oauth2ClientProducer.sendOAuth2ClientRefreshMessage();
return oauth2Client.getId();
}
@Override
+ @CacheEvict(cacheNames = RedisKeyConstants.OAUTH_CLIENT,
+ allEntries = true) // allEntries 清空所有缓存,因为可能修改到 clientId 字段,不好清理
public void updateOAuth2Client(OAuth2ClientUpdateReqVO updateReqVO) {
// 校验存在
validateOAuth2ClientExists(updateReqVO.getId());
@@ -91,18 +61,16 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService {
// 更新
OAuth2ClientDO updateObj = OAuth2ClientConvert.INSTANCE.convert(updateReqVO);
oauth2ClientMapper.updateById(updateObj);
- // 发送刷新消息
- oauth2ClientProducer.sendOAuth2ClientRefreshMessage();
}
@Override
+ @CacheEvict(cacheNames = RedisKeyConstants.OAUTH_CLIENT,
+ allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 key,不好清理
public void deleteOAuth2Client(Long id) {
// 校验存在
validateOAuth2ClientExists(id);
// 删除
oauth2ClientMapper.deleteById(id);
- // 发送刷新消息
- oauth2ClientProducer.sendOAuth2ClientRefreshMessage();
}
private void validateOAuth2ClientExists(Long id) {
@@ -110,6 +78,12 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService {
throw exception(OAUTH2_CLIENT_NOT_EXISTS);
}
}
+ @Override
+ @Cacheable(cacheNames = RedisKeyConstants.OAUTH_CLIENT, key = "#clientId",
+ unless = "#result == null")
+ public OAuth2ClientDO getOAuth2ClientFromCache(String clientId) {
+ return oauth2ClientMapper.selectByClientId(clientId);
+ }
@VisibleForTesting
void validateClientIdExists(Long id, String clientId) {
@@ -137,10 +111,10 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService {
}
@Override
- public OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret,
- String authorizedGrantType, Collection scopes, String redirectUri) {
+ public OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, String authorizedGrantType,
+ Collection scopes, String redirectUri) {
// 校验客户端存在、且开启
- OAuth2ClientDO client = clientCache.get(clientId);
+ OAuth2ClientDO client = getSelf().getOAuth2ClientFromCache(clientId);
if (client == null) {
throw exception(OAUTH2_CLIENT_NOT_EXISTS);
}
@@ -166,5 +140,13 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService {
}
return client;
}
+ /**
+ * 获得自身的代理对象,解决 AOP 生效问题
+ *
+ * @return 自己
+ */
+ private OAuth2ClientServiceImpl getSelf() {
+ return SpringUtil.getBean(getClass());
+ }
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuService.java
index 02418c077..59c33734a 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuService.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuService.java
@@ -15,11 +15,6 @@ import java.util.List;
*/
public interface MenuService {
- /**
- * 初始化菜单的本地缓存
- */
- void initLocalCache();
-
/**
* 创建菜单
*
@@ -58,6 +53,7 @@ public interface MenuService {
*/
List getMenuListByTenant(MenuListReqVO reqVO);
+
/**
* 筛选菜单列表
*
@@ -65,38 +61,13 @@ public interface MenuService {
* @return 菜单列表
*/
List getMenuList(MenuListReqVO reqVO);
-
/**
- * 获得所有菜单,从缓存中
- *
- * 任一参数为空时,则返回为空
- *
- * @param menuTypes 菜单类型数组
- * @param menusStatuses 菜单状态数组
- * @return 菜单列表
- */
- List getMenuListFromCache(Collection menuTypes, Collection menusStatuses);
-
- /**
- * 获得指定编号的菜单数组,从缓存中
- *
- * 任一参数为空时,则返回为空
- *
- * @param menuIds 菜单编号数组
- * @param menuTypes 菜单类型数组
- * @param menusStatuses 菜单状态数组
- * @return 菜单数组
- */
- List getMenuListFromCache(Collection menuIds, Collection menuTypes,
- Collection menusStatuses);
-
- /**
- * 获得权限对应的菜单数组
+ * 获得权限对应的菜单编号数组
*
* @param permission 权限标识
* @return 数组
*/
- List getMenuListByPermissionFromCache(String permission);
+ List getMenuIdListByPermissionFromCache(String permission);
/**
* 获得菜单
@@ -105,5 +76,12 @@ public interface MenuService {
* @return 菜单
*/
MenuDO getMenu(Long id);
+ /**
+ * 获得菜单数组
+ *
+ * @param ids 菜单编号数组
+ * @return 菜单数组
+ */
+ List getMenuList(Collection ids);
}
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 ba56dc41e..53d864e08 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
@@ -1,7 +1,6 @@
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.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuCreateReqVO;
@@ -10,28 +9,22 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuUp
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.CacheEvict;
+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.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
@@ -44,26 +37,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
@Slf4j
public class MenuServiceImpl implements MenuService {
- /**
- * 菜单缓存
- * key:菜单编号
- *
- * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
- */
- @Getter
- @Setter
- private volatile Map menuCache;
- /**
- * 权限与菜单缓存
- * key:权限 {@link MenuDO#getPermission()}
- * value:MenuDO 数组,因为一个权限可能对应多个 MenuDO 对象
- *
- * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
- */
- @Getter
- @Setter
- private volatile Multimap permissionMenuCache;
-
@Resource
private MenuMapper menuMapper;
@Resource
@@ -72,33 +45,8 @@ public class MenuServiceImpl implements MenuService {
@Lazy // 延迟,避免循环依赖报错
private TenantService tenantService;
- @Resource
- private MenuProducer menuProducer;
-
- /**
- * 初始化 {@link #menuCache} 和 {@link #permissionMenuCache} 缓存
- */
- @Override
- @PostConstruct
- public synchronized void initLocalCache() {
- // 第一步:查询数据
- List menuList = menuMapper.selectList();
- log.info("[initLocalCache][缓存菜单,数量为:{}]", menuList.size());
-
- // 第二步:构建缓存
- ImmutableMap.Builder menuCacheBuilder = ImmutableMap.builder();
- ImmutableMultimap.Builder 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
+ @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, key = "#reqVO.permission")
public Long createMenu(MenuCreateReqVO reqVO) {
// 校验父菜单存在
validateParentMenu(reqVO.getParentId(), null);
@@ -109,13 +57,13 @@ public class MenuServiceImpl implements MenuService {
MenuDO menu = MenuConvert.INSTANCE.convert(reqVO);
initMenuProperty(menu);
menuMapper.insert(menu);
- // 发送刷新消息
- menuProducer.sendMenuRefreshMessage();
// 返回
return menu.getId();
}
@Override
+ @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST,
+ allEntries = true) // allEntries 清空所有缓存,因为 permission 如果变更,涉及到新老两个 permission。直接清理,简单有效
public void updateMenu(MenuUpdateReqVO reqVO) {
// 校验更新的菜单是否存在
if (menuMapper.selectById(reqVO.getId()) == null) {
@@ -130,34 +78,25 @@ public class MenuServiceImpl implements MenuService {
MenuDO updateObject = MenuConvert.INSTANCE.convert(reqVO);
initMenuProperty(updateObject);
menuMapper.updateById(updateObject);
- // 发送刷新消息
- menuProducer.sendMenuRefreshMessage();
}
@Override
@Transactional(rollbackFor = Exception.class)
- public void deleteMenu(Long menuId) {
+ @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST,
+ allEntries = true) // allEntries 清空所有缓存,因为此时不知道 id 对应的 permission 是多少。直接清理,简单有效
+ public void deleteMenu(Long id) {
// 校验是否还有子菜单
- if (menuMapper.selectCountByParentId(menuId) > 0) {
+ if (menuMapper.selectCountByParentId(id) > 0) {
throw exception(MENU_EXISTS_CHILDREN);
}
// 校验删除的菜单是否存在
- if (menuMapper.selectById(menuId) == null) {
+ if (menuMapper.selectById(id) == null) {
throw exception(MENU_NOT_EXISTS);
}
// 标记删除
- menuMapper.deleteById(menuId);
+ menuMapper.deleteById(id);
// 删除授予给角色的权限
- permissionService.processMenuDeleted(menuId);
- // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了
- TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
-
- @Override
- public void afterCommit() {
- menuProducer.sendMenuRefreshMessage();
- }
-
- });
+ permissionService.processMenuDeleted(id);
}
@Override
@@ -179,40 +118,20 @@ public class MenuServiceImpl implements MenuService {
}
@Override
- public List getMenuListFromCache(Collection menuTypes, Collection 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());
+ @Cacheable(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, key = "#permission")
+ public List getMenuIdListByPermissionFromCache(String permission) {
+ List menus = menuMapper.selectListByPermission(permission);
+ return convertList(menus, MenuDO::getId);
}
- @Override
- public List getMenuListFromCache(Collection menuIds, Collection menuTypes,
- Collection 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
- public List getMenuListByPermissionFromCache(String permission) {
- return new ArrayList<>(permissionMenuCache.get(permission));
- }
-
- @Override
public MenuDO getMenu(Long id) {
return menuMapper.selectById(id);
}
+ public List getMenuList(Collection ids) {
+ return menuMapper.selectBatchIds(ids);
+ }
+
/**
* 校验父菜单是否合法
*
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionService.java
index 97174d629..70a6af4df 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionService.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionService.java
@@ -1,13 +1,12 @@
package cn.iocoder.yudao.module.system.service.permission;
import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
-import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
-import org.springframework.lang.Nullable;
import java.util.Collection;
-import java.util.List;
import java.util.Set;
+import static java.util.Collections.singleton;
+
/**
* 权限 Service 接口
*
@@ -17,103 +16,6 @@ import java.util.Set;
*/
public interface PermissionService {
- /**
- * 初始化权限的本地缓存
- */
- void initLocalCache();
-
- /**
- * 获得角色们拥有的菜单列表,从缓存中获取
- *
- * 任一参数为空时,则返回为空
- *
- * @param roleIds 角色编号数组
- * @param menuTypes 菜单类型数组
- * @param menusStatuses 菜单状态数组
- * @return 菜单列表
- */
- List getRoleMenuListFromCache(Collection roleIds, Collection menuTypes,
- Collection menusStatuses);
-
- /**
- * 获得用户拥有的角色编号集合,从缓存中获取
- *
- * @param userId 用户编号
- * @param roleStatuses 角色状态集合. 允许为空,为空时不过滤
- * @return 角色编号集合
- */
- Set getUserRoleIdsFromCache(Long userId, @Nullable Collection roleStatuses);
-
- /**
- * 获得角色拥有的菜单编号集合
- *
- * @param roleId 角色编号
- * @return 菜单编号集合
- */
- Set getRoleMenuIds(Long roleId);
-
- /**
- * 获得拥有多个角色的用户编号集合
- *
- * @param roleIds 角色编号集合
- * @return 用户编号集合
- */
- Set getUserRoleIdListByRoleIds(Collection roleIds);
-
- /**
- * 设置角色菜单
- *
- * @param roleId 角色编号
- * @param menuIds 菜单编号集合
- */
- void assignRoleMenu(Long roleId, Set menuIds);
-
- /**
- * 获得用户拥有的角色编号集合
- *
- * @param userId 用户编号
- * @return 角色编号集合
- */
- Set getUserRoleIdListByUserId(Long userId);
-
- /**
- * 设置用户角色
- *
- * @param userId 角色编号
- * @param roleIds 角色编号集合
- */
- void assignUserRole(Long userId, Set roleIds);
-
- /**
- * 设置角色的数据权限
- *
- * @param roleId 角色编号
- * @param dataScope 数据范围
- * @param dataScopeDeptIds 部门编号数组
- */
- void assignRoleDataScope(Long roleId, Integer dataScope, Set dataScopeDeptIds);
-
- /**
- * 处理角色删除时,删除关联授权数据
- *
- * @param roleId 角色编号
- */
- void processRoleDeleted(Long roleId);
-
- /**
- * 处理菜单删除时,删除关联授权数据
- *
- * @param menuId 菜单编号
- */
- void processMenuDeleted(Long menuId);
-
- /**
- * 处理用户删除是,删除关联授权数据
- *
- * @param userId 用户编号
- */
- void processUserDeleted(Long userId);
-
/**
* 判断是否有权限,任一一个即可
*
@@ -131,6 +33,108 @@ public interface PermissionService {
*/
boolean hasAnyRoles(Long userId, String... roles);
+ // ========== 角色-菜单的相关方法 ==========
+
+ /**
+ * 设置角色菜单
+ *
+ * @param roleId 角色编号
+ * @param menuIds 菜单编号集合
+ */
+ void assignRoleMenu(Long roleId, Set menuIds);
+
+ /**
+ * 处理角色删除时,删除关联授权数据
+ *
+ * @param roleId 角色编号
+ */
+ void processRoleDeleted(Long roleId);
+
+ /**
+ * 处理菜单删除时,删除关联授权数据
+ *
+ * @param menuId 菜单编号
+ */
+ void processMenuDeleted(Long menuId);
+
+ /**
+ * 获得角色拥有的菜单编号集合
+ *
+ * @param roleId 角色编号
+ * @return 菜单编号集合
+ */
+ default Set getRoleMenuListByRoleId(Long roleId) {
+ return getRoleMenuListByRoleId(singleton(roleId));
+ }
+
+ /**
+ * 获得角色们拥有的菜单编号集合
+ *
+ * @param roleIds 角色编号数组
+ * @return 菜单编号集合
+ */
+ Set getRoleMenuListByRoleId(Collection roleIds);
+
+ /**
+ * 获得拥有指定菜单的角色编号数组,从缓存中获取
+ *
+ * @param menuId 菜单编号
+ * @return 角色编号数组
+ */
+ Set getMenuRoleIdListByMenuIdFromCache(Long menuId);
+
+ // ========== 用户-角色的相关方法 ==========
+
+ /**
+ * 设置用户角色
+ *
+ * @param userId 角色编号
+ * @param roleIds 角色编号集合
+ */
+ void assignUserRole(Long userId, Set roleIds);
+
+ /**
+ * 处理用户删除时,删除关联授权数据
+ *
+ * @param userId 用户编号
+ */
+ void processUserDeleted(Long userId);
+
+ /**
+ * 获得拥有多个角色的用户编号集合
+ *
+ * @param roleIds 角色编号集合
+ * @return 用户编号集合
+ */
+ Set getUserRoleIdListByRoleId(Collection roleIds);
+
+ /**
+ * 获得用户拥有的角色编号集合
+ *
+ * @param userId 用户编号
+ * @return 角色编号集合
+ */
+ Set getUserRoleIdListByUserId(Long userId);
+
+ /**
+ * 获得用户拥有的角色编号集合,从缓存中获取
+ *
+ * @param userId 用户编号
+ * @return 角色编号集合
+ */
+ Set getUserRoleIdListByUserIdFromCache(Long userId);
+
+ // ========== 用户-部门的相关方法 ==========
+
+ /**
+ * 设置角色的数据权限
+ *
+ * @param roleId 角色编号
+ * @param dataScope 数据范围
+ * @param dataScopeDeptIds 部门编号数组
+ */
+ void assignRoleDataScope(Long roleId, Integer dataScope, Set dataScopeDeptIds);
+
/**
* 获得登陆用户的部门数据权限
*
@@ -139,4 +143,4 @@ public interface PermissionService {
*/
DeptDataPermissionRespDTO getDeptDataPermission(Long userId);
-}
+}
\ No newline at end of file
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 43365cf46..e9ec95c8f 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
@@ -3,45 +3,38 @@ package cn.iocoder.yudao.module.system.service.permission;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
-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;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper;
import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
+import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
-import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
+import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Suppliers;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
-import lombok.Getter;
-import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.Caching;
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.function.Supplier;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
-import static java.util.Collections.singleton;
+import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
/**
* 权限 Service 实现类
@@ -52,38 +45,6 @@ import static java.util.Collections.singleton;
@Slf4j
public class PermissionServiceImpl implements PermissionService {
- /**
- * 角色编号与菜单编号的缓存映射
- * key:角色编号
- * value:菜单编号的数组
- *
- * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
- */
- @Getter
- @Setter // 单元测试需要
- private volatile Multimap roleMenuCache;
- /**
- * 菜单编号与角色编号的缓存映射
- * key:菜单编号
- * value:角色编号的数组
- *
- * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
- */
- @Getter
- @Setter // 单元测试需要
- private volatile Multimap menuRoleCache;
-
- /**
- * 用户编号与角色编号的缓存映射
- * key:用户编号
- * value:角色编号的数组
- *
- * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
- */
- @Getter
- @Setter // 单元测试需要
- private volatile Map> userRoleCache;
-
@Resource
private RoleMenuMapper roleMenuMapper;
@Resource
@@ -98,115 +59,89 @@ public class PermissionServiceImpl implements PermissionService {
@Resource
private AdminUserService userService;
- @Resource
- private PermissionProducer permissionProducer;
-
@Override
- @PostConstruct
- public void initLocalCache() {
- initLocalCacheForRoleMenu();
- initLocalCacheForUserRole();
+ public boolean hasAnyPermissions(Long userId, String... permissions) {
+ // 如果为空,说明已经有权限
+ if (ArrayUtil.isEmpty(permissions)) {
+ return true;
+ }
+
+ // 获得当前登录的角色。如果为空,说明没有权限
+ List roles = getEnableUserRoleListByUserIdFromCache(userId);
+ if (CollUtil.isEmpty(roles)) {
+ return false;
+ }
+
+ // 情况一:遍历判断每个权限,如果有一满足,说明有权限
+ for (String permission : permissions) {
+ if (hasAnyPermission(roles, permission)) {
+ return true;
+ }
+ }
+
+ // 情况二:如果是超管,也说明有权限
+ return roleService.hasAnySuperAdmin(convertSet(roles, RoleDO::getId));
}
/**
- * 刷新 RoleMenu 本地缓存
+ * 判断指定角色,是否拥有该 permission 权限
+ *
+ * @param roles 指定角色数组
+ * @param permission 权限标识
+ * @return 是否拥有
*/
- @VisibleForTesting
- void initLocalCacheForRoleMenu() {
- // 注意:忽略自动多租户,因为要全局初始化缓存
- TenantUtils.executeIgnore(() -> {
- // 第一步:查询数据
- List roleMenus = roleMenuMapper.selectList();
- log.info("[initLocalCacheForRoleMenu][缓存角色与菜单,数量为:{}]", roleMenus.size());
+ private boolean hasAnyPermission(List roles, String permission) {
+ List menuIds = menuService.getMenuIdListByPermissionFromCache(permission);
+ // 采用严格模式,如果权限找不到对应的 Menu 的话,也认为没有权限
+ if (CollUtil.isEmpty(menuIds)) {
+ return false;
+ }
- // 第二步:构建缓存
- 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();
- });
- }
-
- /**
- * 刷新 UserRole 本地缓存
- */
- @VisibleForTesting
- void initLocalCacheForUserRole() {
- // 注意:忽略自动多租户,因为要全局初始化缓存
- TenantUtils.executeIgnore(() -> {
- // 第一步:加载数据
- List userRoles = userRoleMapper.selectList();
- log.info("[initLocalCacheForUserRole][缓存用户与角色,数量为:{}]", userRoles.size());
-
- // 第二步:构建缓存。
- ImmutableMultimap.Builder userRoleCacheBuilder = ImmutableMultimap.builder();
- userRoles.forEach(userRoleDO -> userRoleCacheBuilder.put(userRoleDO.getUserId(), userRoleDO.getRoleId()));
- userRoleCache = CollectionUtils.convertMultiMap2(userRoles, UserRoleDO::getUserId, UserRoleDO::getRoleId);
- });
+ // 判断是否有权限
+ Set roleIds = convertSet(roles, RoleDO::getId);
+ for (Long menuId : menuIds) {
+ // 获得拥有该菜单的角色编号集合
+ Set menuRoleIds = getSelf().getMenuRoleIdListByMenuIdFromCache(menuId);
+ // 如果有交集,说明有权限
+ if (CollUtil.containsAny(menuRoleIds, roleIds)) {
+ return true;
+ }
+ }
+ return false;
}
@Override
- public List getRoleMenuListFromCache(Collection roleIds, Collection menuTypes,
- Collection menusStatuses) {
- // 任一一个参数为空时,不返回任何菜单
- if (CollectionUtils.isAnyEmpty(roleIds, menuTypes, menusStatuses)) {
- return Collections.emptyList();
+ public boolean hasAnyRoles(Long userId, String... roles) {
+ // 如果为空,说明已经有权限
+ if (ArrayUtil.isEmpty(roles)) {
+ return true;
}
- // 判断角色是否包含超级管理员。如果是超级管理员,获取到全部
- List roleList = roleService.getRoleListFromCache(roleIds);
- if (roleService.hasAnySuperAdmin(roleList)) {
- return menuService.getMenuListFromCache(menuTypes, menusStatuses);
+ // 获得当前登录的角色。如果为空,说明没有权限
+ List roleList = getEnableUserRoleListByUserIdFromCache(userId);
+ if (CollUtil.isEmpty(roleList)) {
+ return false;
}
- // 获得角色拥有的菜单关联
- List menuIds = MapUtils.getList(roleMenuCache, roleIds);
- return menuService.getMenuListFromCache(menuIds, menuTypes, menusStatuses);
+ // 判断是否有角色
+ Set userRoles = convertSet(roleList, RoleDO::getCode);
+ return CollUtil.containsAny(userRoles, Sets.newHashSet(roles));
}
- @Override
- public Set getUserRoleIdsFromCache(Long userId, Collection roleStatuses) {
- Set cacheRoleIds = userRoleCache.get(userId);
- // 创建用户的时候没有分配角色,会存在空指针异常
- if (CollUtil.isEmpty(cacheRoleIds)) {
- return Collections.emptySet();
- }
- Set roleIds = new HashSet<>(cacheRoleIds);
- // 过滤角色状态
- if (CollectionUtil.isNotEmpty(roleStatuses)) {
- roleIds.removeIf(roleId -> {
- RoleDO role = roleService.getRoleFromCache(roleId);
- return role == null || !roleStatuses.contains(role.getStatus());
- });
- }
- return roleIds;
- }
+ // ========== 角色-菜单的相关方法 ==========
@Override
- public Set getRoleMenuIds(Long roleId) {
- // 如果是管理员的情况下,获取全部菜单编号
- if (roleService.hasAnySuperAdmin(Collections.singleton(roleId))) {
- return convertSet(menuService.getMenuList(), MenuDO::getId);
- }
- // 如果是非管理员的情况下,获得拥有的菜单编号
- return convertSet(roleMenuMapper.selectListByRoleId(roleId), RoleMenuDO::getMenuId);
- }
-
- @Override
- @Transactional(rollbackFor = Exception.class)
+ @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
+ @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST,
+ allEntries = true) // allEntries 清空所有缓存,主要一次更新涉及到的 menuIds 较多,反倒批量会更快
public void assignRoleMenu(Long roleId, Set menuIds) {
// 获得角色拥有菜单编号
- Set dbMenuIds = convertSet(roleMenuMapper.selectListByRoleId(roleId),
- RoleMenuDO::getMenuId);
+ Set