Commit 49b4eedfc0ca13bfb0c4fdfab952b0574c754153

Authored by YunaiV
1 parent 97db4586

增加 UserRole 的缓存,完善权限相关的单元测试

yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java
... ... @@ -93,7 +93,7 @@ public class AuthController {
93 93 return null;
94 94 }
95 95 // 获得角色列表
96   - Set<Long> roleIds = permissionService.getUserRoleIds(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));
  96 + Set<Long> roleIds = permissionService.getUserRoleIdsFromCache(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));
97 97 List<RoleDO> roleList = roleService.getRolesFromCache(roleIds);
98 98 // 获得菜单列表
99 99 List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(roleIds,
... ... @@ -107,7 +107,7 @@ public class AuthController {
107 107 @ApiOperation("获得登录用户的菜单列表")
108 108 public CommonResult<List<AuthMenuRespVO>> getMenus() {
109 109 // 获得角色列表
110   - Set<Long> roleIds = permissionService.getUserRoleIds(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));
  110 + Set<Long> roleIds = permissionService.getUserRoleIdsFromCache(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));
111 111 // 获得用户拥有的菜单列表
112 112 List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(roleIds,
113 113 SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/UserRoleMapper.java
... ... @@ -4,8 +4,10 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 4 import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
5 5 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
6 6 import org.apache.ibatis.annotations.Mapper;
  7 +import org.apache.ibatis.annotations.Select;
7 8  
8 9 import java.util.Collection;
  10 +import java.util.Date;
9 11 import java.util.List;
10 12  
11 13 @Mapper
... ... @@ -36,4 +38,8 @@ public interface UserRoleMapper extends BaseMapperX&lt;UserRoleDO&gt; {
36 38 default List<UserRoleDO> selectListByRoleIds(Collection<Long> roleIds) {
37 39 return selectList(UserRoleDO::getRoleId, roleIds);
38 40 }
  41 +
  42 + @Select("SELECT COUNT(*) FROM system_user_role WHERE update_time > #{maxUpdateTime}")
  43 + Long selectCountByUpdateTimeGt(Date maxUpdateTime);
  44 +
39 45 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/UserRoleRefreshConsumer.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.mq.consumer.permission;
  2 +
  3 +import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
  4 +import cn.iocoder.yudao.module.system.mq.message.permission.UserRoleRefreshMessage;
  5 +import cn.iocoder.yudao.module.system.service.permission.PermissionService;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import javax.annotation.Resource;
  10 +
  11 +/**
  12 + * 针对 {@link UserRoleRefreshMessage} 的消费者
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@Component
  17 +@Slf4j
  18 +public class UserRoleRefreshConsumer extends AbstractChannelMessageListener<UserRoleRefreshMessage> {
  19 +
  20 + @Resource
  21 + private PermissionService permissionService;
  22 +
  23 + @Override
  24 + public void onMessage(UserRoleRefreshMessage message) {
  25 + log.info("[onMessage][收到 User 与 Role 的关联刷新消息]");
  26 + permissionService.initLocalCache();
  27 + }
  28 +
  29 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/UserRoleRefreshMessage.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.mq.message.permission;
  2 +
  3 +import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;
  4 +import lombok.Data;
  5 +import lombok.EqualsAndHashCode;
  6 +
  7 +/**
  8 + * 用户与角色的数据刷新 Message
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +public class UserRoleRefreshMessage extends AbstractChannelMessage {
  15 +
  16 + @Override
  17 + public String getChannel() {
  18 + return "system.user-role.refresh";
  19 + }
  20 +
  21 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/PermissionProducer.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.mq.producer.permission;
2 2  
3 3 import cn.iocoder.yudao.module.system.mq.message.permission.RoleMenuRefreshMessage;
4 4 import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
  5 +import cn.iocoder.yudao.module.system.mq.message.permission.UserRoleRefreshMessage;
5 6 import org.springframework.stereotype.Component;
6 7  
7 8 import javax.annotation.Resource;
... ... @@ -23,4 +24,12 @@ public class PermissionProducer {
23 24 redisMQTemplate.send(message);
24 25 }
25 26  
  27 + /**
  28 + * 发送 {@link UserRoleRefreshMessage} 消息
  29 + */
  30 + public void sendUserRoleRefreshMessage() {
  31 + UserRoleRefreshMessage message = new UserRoleRefreshMessage();
  32 + redisMQTemplate.send(message);
  33 + }
  34 +
26 35 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionService.java
... ... @@ -37,13 +37,13 @@ public interface PermissionService extends SecurityPermissionFrameworkService, D
37 37 Collection<Integer> menusStatuses);
38 38  
39 39 /**
40   - * 获得用户拥有的角色编号集合
  40 + * 获得用户拥有的角色编号集合,从缓存中获取
41 41 *
42 42 * @param userId 用户编号
43 43 * @param roleStatuses 角色状态集合. 允许为空,为空时不过滤
44 44 * @return 角色编号集合
45 45 */
46   - Set<Long> getUserRoleIds(Long userId, @Nullable Collection<Integer> roleStatuses);
  46 + Set<Long> getUserRoleIdsFromCache(Long userId, @Nullable Collection<Integer> roleStatuses);
47 47  
48 48 /**
49 49 * 获得角色拥有的菜单编号集合
... ... @@ -54,6 +54,14 @@ public interface PermissionService extends SecurityPermissionFrameworkService, D
54 54 Set<Long> getRoleMenuIds(Long roleId);
55 55  
56 56 /**
  57 + * 获得拥有多个角色的用户编号集合
  58 + *
  59 + * @param roleIds 角色编号集合
  60 + * @return 用户编号集合
  61 + */
  62 + Set<Long> getUserRoleIdListByRoleIds(Collection<Long> roleIds);
  63 +
  64 + /**
57 65 * 设置角色菜单
58 66 *
59 67 * @param roleId 角色编号
... ... @@ -70,14 +78,6 @@ public interface PermissionService extends SecurityPermissionFrameworkService, D
70 78 Set<Long> getUserRoleIdListByUserId(Long userId);
71 79  
72 80 /**
73   - * 获得拥有某个角色的用户编号集合
74   - *
75   - * @param roleId 角色编号
76   - * @return 用户编号集合
77   - */
78   - Set<Long> getUserRoleIdListByRoleId(Long roleId);
79   -
80   - /**
81 81 * 设置用户角色
82 82 *
83 83 * @param userId 角色编号
... ... @@ -115,12 +115,4 @@ public interface PermissionService extends SecurityPermissionFrameworkService, D
115 115 */
116 116 void processUserDeleted(Long userId);
117 117  
118   - /**
119   - * 获得拥有多个角色的用户编号集合
120   - *
121   - * @param roleIds 角色编号集合
122   - * @return 用户编号集合
123   - */
124   - Set<Long> getUserRoleIdListByRoleIds(Collection<Long> roleIds);
125   -
126 118 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java
... ... @@ -23,10 +23,13 @@ import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
23 23 import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;
24 24 import cn.iocoder.yudao.module.system.service.dept.DeptService;
25 25 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
  26 +import com.google.common.annotations.VisibleForTesting;
26 27 import com.google.common.base.Suppliers;
27 28 import com.google.common.collect.ImmutableMultimap;
28 29 import com.google.common.collect.Multimap;
29 30 import com.google.common.collect.Sets;
  31 +import lombok.Getter;
  32 +import lombok.Setter;
30 33 import lombok.extern.slf4j.Slf4j;
31 34 import org.springframework.context.annotation.Lazy;
32 35 import org.springframework.scheduling.annotation.Scheduled;
... ... @@ -40,6 +43,8 @@ import javax.annotation.Resource;
40 43 import java.util.*;
41 44 import java.util.function.Supplier;
42 45  
  46 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  47 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
43 48 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
44 49 import static java.util.Collections.singleton;
45 50  
... ... @@ -65,6 +70,8 @@ public class PermissionServiceImpl implements PermissionService {
65 70 *
66 71 * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
67 72 */
  73 + @Getter
  74 + @Setter // 单元测试
68 75 private volatile Multimap<Long, Long> roleMenuCache;
69 76 /**
70 77 * 菜单编号与角色编号的缓存映射
... ... @@ -73,11 +80,29 @@ public class PermissionServiceImpl implements PermissionService {
73 80 *
74 81 * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
75 82 */
  83 + @Getter
76 84 private volatile Multimap<Long, Long> menuRoleCache;
77 85 /**
78   - * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
  86 + * 缓存 RoleMenu 的最大更新时间,用于后续的增量轮询,判断是否有更新
79 87 */
80   - private volatile Date maxUpdateTime;
  88 + @Getter
  89 + private volatile Date roleMenuMaxUpdateTime;
  90 +
  91 + /**
  92 + * 用户编号与角色编号的缓存映射
  93 + * key:用户编号
  94 + * value:角色编号的数组
  95 + *
  96 + * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
  97 + */
  98 + @Getter
  99 + @Setter // 单元测试需要
  100 + private volatile Map<Long, Set<Long>> userRoleCache;
  101 + /**
  102 + * 缓存 UserRole 的最大更新时间,用于后续的增量轮询,判断是否有更新
  103 + */
  104 + @Getter
  105 + private volatile Date userRoleMaxUpdateTime;
81 106  
82 107 @Resource
83 108 private RoleMenuMapper roleMenuMapper;
... ... @@ -104,15 +129,21 @@ public class PermissionServiceImpl implements PermissionService {
104 129 @Lazy // 注入自己,所以延迟加载
105 130 private PermissionService self;
106 131  
107   - /**
108   - * 初始化 {@link #roleMenuCache} 和 {@link #menuRoleCache} 缓存
109   - */
110 132 @Override
111 133 @PostConstruct
112 134 @TenantIgnore // 初始化缓存,无需租户过滤
113 135 public void initLocalCache() {
  136 + initUserRoleLocalCache();
  137 + initRoleMenuLocalCache();
  138 + }
  139 +
  140 + /**
  141 + * 初始化 {@link #roleMenuCache} 和 {@link #menuRoleCache} 缓存
  142 + */
  143 + @VisibleForTesting
  144 + void initRoleMenuLocalCache() {
114 145 // 获取角色与菜单的关联列表,如果有更新
115   - List<RoleMenuDO> roleMenuList = loadRoleMenuIfUpdate(maxUpdateTime);
  146 + List<RoleMenuDO> roleMenuList = loadRoleMenuIfUpdate(roleMenuMaxUpdateTime);
116 147 if (CollUtil.isEmpty(roleMenuList)) {
117 148 return;
118 149 }
... ... @@ -126,8 +157,27 @@ public class PermissionServiceImpl implements PermissionService {
126 157 });
127 158 roleMenuCache = roleMenuCacheBuilder.build();
128 159 menuRoleCache = menuRoleCacheBuilder.build();
129   - maxUpdateTime = CollectionUtils.getMaxValue(roleMenuList, RoleMenuDO::getUpdateTime);
130   - log.info("[initLocalCache][初始化角色与菜单的关联数量为 {}]", roleMenuList.size());
  160 + roleMenuMaxUpdateTime = getMaxValue(roleMenuList, RoleMenuDO::getUpdateTime);
  161 + log.info("[initRoleMenuLocalCache][初始化角色与菜单的关联数量为 {}]", roleMenuList.size());
  162 + }
  163 +
  164 + /**
  165 + * 初始化 {@link #userRoleCache} 缓存
  166 + */
  167 + @VisibleForTesting
  168 + void initUserRoleLocalCache() {
  169 + // 获取用户与角色的关联列表,如果有更新
  170 + List<UserRoleDO> userRoleList = loadUserRoleIfUpdate(userRoleMaxUpdateTime);
  171 + if (CollUtil.isEmpty(userRoleList)) {
  172 + return;
  173 + }
  174 +
  175 + // 初始化 userRoleCache 缓存
  176 + ImmutableMultimap.Builder<Long, Long> userRoleCacheBuilder = ImmutableMultimap.builder();
  177 + userRoleList.forEach(userRoleDO -> userRoleCacheBuilder.put(userRoleDO.getUserId(), userRoleDO.getRoleId()));
  178 + userRoleCache = CollectionUtils.convertMultiMap2(userRoleList, UserRoleDO::getUserId, UserRoleDO::getRoleId);
  179 + userRoleMaxUpdateTime = getMaxValue(userRoleList, UserRoleDO::getUpdateTime);
  180 + log.info("[initUserRoleLocalCache][初始化用户与角色的关联数量为 {}]", userRoleList.size());
131 181 }
132 182  
133 183 @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
... ... @@ -156,6 +206,27 @@ public class PermissionServiceImpl implements PermissionService {
156 206 return roleMenuMapper.selectList();
157 207 }
158 208  
  209 + /**
  210 + * 如果用户与角色的关联发生变化,从数据库中获取最新的全量用户与角色的关联。
  211 + * 如果未发生变化,则返回空
  212 + *
  213 + * @param maxUpdateTime 当前角色与菜单的关联的最大更新时间
  214 + * @return 角色与菜单的关联列表
  215 + */
  216 + protected List<UserRoleDO> loadUserRoleIfUpdate(Date maxUpdateTime) {
  217 + // 第一步,判断是否要更新。
  218 + if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
  219 + log.info("[loadUserRoleIfUpdate][首次加载全量用户与角色的关联]");
  220 + } else { // 判断数据库中是否有更新的用户与角色的关联
  221 + if (userRoleMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
  222 + return null;
  223 + }
  224 + log.info("[loadUserRoleIfUpdate][增量加载全量用户与角色的关联]");
  225 + }
  226 + // 第二步,如果有更新,则从数据库加载所有用户与角色的关联
  227 + return userRoleMapper.selectList();
  228 + }
  229 +
159 230 @Override
160 231 public List<MenuDO> getRoleMenuListFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
161 232 Collection<Integer> menusStatuses) {
... ... @@ -176,35 +247,33 @@ public class PermissionServiceImpl implements PermissionService {
176 247 }
177 248  
178 249 @Override
179   - public Set<Long> getUserRoleIds(Long userId, Collection<Integer> roleStatuses) {
180   - List<UserRoleDO> userRoleList = userRoleMapper.selectListByUserId(userId);
  250 + public Set<Long> getUserRoleIdsFromCache(Long userId, Collection<Integer> roleStatuses) {
  251 + Set<Long> roleIds = new HashSet<>(userRoleCache.get(userId));
181 252 // 过滤角色状态
182 253 if (CollectionUtil.isNotEmpty(roleStatuses)) {
183   - userRoleList.removeIf(userRoleDO -> {
184   - RoleDO role = roleService.getRoleFromCache(userRoleDO.getRoleId());
  254 + roleIds.removeIf(roleId -> {
  255 + RoleDO role = roleService.getRoleFromCache(roleId);
185 256 return role == null || !roleStatuses.contains(role.getStatus());
186 257 });
187 258 }
188   - return CollectionUtils.convertSet(userRoleList, UserRoleDO::getRoleId);
  259 + return roleIds;
189 260 }
190 261  
191 262 @Override
192 263 public Set<Long> getRoleMenuIds(Long roleId) {
193 264 // 如果是管理员的情况下,获取全部菜单编号
194   - RoleDO role = roleService.getRole(roleId);
195   - if (roleService.hasAnySuperAdmin(Collections.singletonList(role))) {
196   - return CollectionUtils.convertSet(menuService.getMenus(), MenuDO::getId);
  265 + if (roleService.hasAnySuperAdmin(Collections.singleton(roleId))) {
  266 + return convertSet(menuService.getMenus(), MenuDO::getId);
197 267 }
198 268 // 如果是非管理员的情况下,获得拥有的菜单编号
199   - return CollectionUtils.convertSet(roleMenuMapper.selectListByRoleId(roleId),
200   - RoleMenuDO::getMenuId);
  269 + return convertSet(roleMenuMapper.selectListByRoleId(roleId), RoleMenuDO::getMenuId);
201 270 }
202 271  
203 272 @Override
204 273 @Transactional(rollbackFor = Exception.class)
205 274 public void assignRoleMenu(Long roleId, Set<Long> menuIds) {
206 275 // 获得角色拥有菜单编号
207   - Set<Long> dbMenuIds = CollectionUtils.convertSet(roleMenuMapper.selectListByRoleId(roleId),
  276 + Set<Long> dbMenuIds = convertSet(roleMenuMapper.selectListByRoleId(roleId),
208 277 RoleMenuDO::getMenuId);
209 278 // 计算新增和删除的菜单编号
210 279 Collection<Long> createMenuIds = CollUtil.subtract(menuIds, dbMenuIds);
... ... @@ -234,20 +303,21 @@ public class PermissionServiceImpl implements PermissionService {
234 303  
235 304 @Override
236 305 public Set<Long> getUserRoleIdListByUserId(Long userId) {
237   - return CollectionUtils.convertSet(userRoleMapper.selectListByUserId(userId),
  306 + return convertSet(userRoleMapper.selectListByUserId(userId),
238 307 UserRoleDO::getRoleId);
239 308 }
240 309  
241 310 @Override
242   - public Set<Long> getUserRoleIdListByRoleId(Long roleId) {
243   - return CollectionUtils.convertSet(userRoleMapper.selectListByRoleId(roleId),
244   - UserRoleDO::getRoleId);
  311 + public Set<Long> getUserRoleIdListByRoleIds(Collection<Long> roleIds) {
  312 + return convertSet(userRoleMapper.selectListByRoleIds(roleIds),
  313 + UserRoleDO::getUserId);
245 314 }
246 315  
247 316 @Override
  317 + @Transactional(rollbackFor = Exception.class)
248 318 public void assignUserRole(Long userId, Set<Long> roleIds) {
249 319 // 获得角色拥有角色编号
250   - Set<Long> dbRoleIds = CollectionUtils.convertSet(userRoleMapper.selectListByUserId(userId),
  320 + Set<Long> dbRoleIds = convertSet(userRoleMapper.selectListByUserId(userId),
251 321 UserRoleDO::getRoleId);
252 322 // 计算新增和删除的角色编号
253 323 Collection<Long> createRoleIds = CollUtil.subtract(roleIds, dbRoleIds);
... ... @@ -264,6 +334,15 @@ public class PermissionServiceImpl implements PermissionService {
264 334 if (!CollectionUtil.isEmpty(deleteMenuIds)) {
265 335 userRoleMapper.deleteListByUserIdAndRoleIdIds(userId, deleteMenuIds);
266 336 }
  337 + // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了
  338 + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
  339 +
  340 + @Override
  341 + public void afterCommit() {
  342 + permissionProducer.sendUserRoleRefreshMessage();
  343 + }
  344 +
  345 + });
267 346 }
268 347  
269 348 @Override
... ... @@ -284,6 +363,7 @@ public class PermissionServiceImpl implements PermissionService {
284 363 @Override
285 364 public void afterCommit() {
286 365 permissionProducer.sendRoleMenuRefreshMessage();
  366 + permissionProducer.sendUserRoleRefreshMessage();
287 367 }
288 368  
289 369 });
... ... @@ -305,8 +385,17 @@ public class PermissionServiceImpl implements PermissionService {
305 385 }
306 386  
307 387 @Override
  388 + @Transactional(rollbackFor = Exception.class)
308 389 public void processUserDeleted(Long userId) {
309 390 userRoleMapper.deleteListByUserId(userId);
  391 + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
  392 +
  393 + @Override
  394 + public void afterCommit() {
  395 + permissionProducer.sendUserRoleRefreshMessage();
  396 + }
  397 +
  398 + });
310 399 }
311 400  
312 401 @Override
... ... @@ -322,7 +411,7 @@ public class PermissionServiceImpl implements PermissionService {
322 411 }
323 412  
324 413 // 获得当前登录的角色。如果为空,说明没有权限
325   - Set<Long> roleIds = getUserRoleIds(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));
  414 + Set<Long> roleIds = getUserRoleIdsFromCache(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));
326 415 if (CollUtil.isEmpty(roleIds)) {
327 416 return false;
328 417 }
... ... @@ -357,7 +446,7 @@ public class PermissionServiceImpl implements PermissionService {
357 446 }
358 447  
359 448 // 获得当前登录的角色。如果为空,说明没有权限
360   - Set<Long> roleIds = getUserRoleIds(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));
  449 + Set<Long> roleIds = getUserRoleIdsFromCache(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));
361 450 if (CollUtil.isEmpty(roleIds)) {
362 451 return false;
363 452 }
... ... @@ -365,7 +454,7 @@ public class PermissionServiceImpl implements PermissionService {
365 454 if (roleService.hasAnySuperAdmin(roleIds)) {
366 455 return true;
367 456 }
368   - Set<String> userRoles = CollectionUtils.convertSet(roleService.getRolesFromCache(roleIds),
  457 + Set<String> userRoles = convertSet(roleService.getRolesFromCache(roleIds),
369 458 RoleDO::getCode);
370 459 return CollUtil.containsAny(userRoles, Sets.newHashSet(roles));
371 460 }
... ... @@ -375,7 +464,7 @@ public class PermissionServiceImpl implements PermissionService {
375 464 public DeptDataPermissionRespDTO getDeptDataPermission(Long userId) {
376 465 DeptDataPermissionRespDTO result = new DeptDataPermissionRespDTO();
377 466 // 获得用户的角色
378   - Set<Long> roleIds = getUserRoleIds(userId, singleton(CommonStatusEnum.ENABLE.getStatus()));
  467 + Set<Long> roleIds = getUserRoleIdsFromCache(userId, singleton(CommonStatusEnum.ENABLE.getStatus()));
379 468 if (CollUtil.isEmpty(roleIds)) {
380 469 return result;
381 470 }
... ... @@ -425,10 +514,4 @@ public class PermissionServiceImpl implements PermissionService {
425 514 return result;
426 515 }
427 516  
428   - @Override
429   - public Set<Long> getUserRoleIdListByRoleIds(Collection<Long> roleIds) {
430   - return CollectionUtils.convertSet(userRoleMapper.selectListByRoleIds(roleIds),
431   - UserRoleDO::getUserId);
432   - }
433   -
434 517 }
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java
1 1 package cn.iocoder.yudao.module.system.service.permission;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.map.MapUtil;
4 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  6 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
5 7 import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO;
6 8 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
7 9 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
  10 +import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
8 11 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
9 12 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
10 13 import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
... ... @@ -17,20 +20,24 @@ import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
17 20 import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;
18 21 import cn.iocoder.yudao.module.system.service.dept.DeptService;
19 22 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
  23 +import com.google.common.collect.ImmutableMultimap;
  24 +import com.google.common.collect.Multimap;
20 25 import org.junit.jupiter.api.Test;
21 26 import org.springframework.boot.test.mock.mockito.MockBean;
22 27 import org.springframework.context.annotation.Import;
23 28  
24 29 import javax.annotation.Resource;
25   -import java.util.List;
  30 +import java.util.*;
26 31  
  32 +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
27 33 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
28   -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
29   -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  34 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  35 +import static java.util.Arrays.asList;
30 36 import static java.util.Collections.singleton;
31 37 import static java.util.Collections.singletonList;
32 38 import static org.junit.jupiter.api.Assertions.*;
33 39 import static org.mockito.ArgumentMatchers.eq;
  40 +import static org.mockito.ArgumentMatchers.same;
34 41 import static org.mockito.Mockito.verify;
35 42 import static org.mockito.Mockito.when;
36 43  
... ... @@ -63,6 +70,226 @@ public class PermissionServiceTest extends BaseDbUnitTest {
63 70 private PermissionProducer permissionProducer;
64 71  
65 72 @Test
  73 + public void testInitRoleMenuLocalCache() {
  74 + // mock 数据
  75 + RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(1L).setMenuId(10L));
  76 + roleMenuMapper.insert(roleMenuDO01);
  77 + RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(1L).setMenuId(20L));
  78 + roleMenuMapper.insert(roleMenuDO02);
  79 +
  80 + // 调用
  81 + permissionService.initRoleMenuLocalCache();
  82 + // 断言 roleMenuCache 缓存
  83 + assertEquals(1, permissionService.getRoleMenuCache().keySet().size());
  84 + assertEquals(asList(10L, 20L), permissionService.getRoleMenuCache().get(1L));
  85 + // 断言 menuRoleCache 缓存
  86 + assertEquals(2, permissionService.getMenuRoleCache().size());
  87 + assertEquals(singletonList(1L), permissionService.getMenuRoleCache().get(10L));
  88 + assertEquals(singletonList(1L), permissionService.getMenuRoleCache().get(20L));
  89 + // 断言 maxUpdateTime 缓存
  90 + Date maxUpdateTime = permissionService.getRoleMenuMaxUpdateTime();
  91 + assertEquals(ObjectUtils.max(roleMenuDO01.getUpdateTime(), roleMenuDO02.getUpdateTime()), maxUpdateTime);
  92 + }
  93 +
  94 + @Test
  95 + public void testInitUserRoleLocalCache() {
  96 + // mock 数据
  97 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L));
  98 + userRoleMapper.insert(userRoleDO01);
  99 + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L));
  100 + userRoleMapper.insert(roleMenuDO02);
  101 +
  102 + // 调用
  103 + permissionService.initUserRoleLocalCache();
  104 + // 断言 roleMenuCache 缓存
  105 + assertEquals(1, permissionService.getUserRoleCache().size());
  106 + assertEquals(asSet(10L, 20L), permissionService.getUserRoleCache().get(1L));
  107 + // 断言 maxUpdateTime 缓存
  108 + Date maxUpdateTime = permissionService.getUserRoleMaxUpdateTime();
  109 + assertEquals(ObjectUtils.max(userRoleDO01.getUpdateTime(), roleMenuDO02.getUpdateTime()), maxUpdateTime);
  110 + }
  111 +
  112 + @Test
  113 + public void testGetRoleMenuListFromCache_superAdmin() {
  114 + // 准备参数
  115 + Collection<Long> roleIds = singletonList(100L);
  116 + Collection<Integer> menuTypes = asList(2, 3);
  117 + Collection<Integer> menusStatuses = asList(0, 1);
  118 + // mock 方法
  119 + List<RoleDO> roleList = singletonList(randomPojo(RoleDO.class, o -> o.setId(100L)));
  120 + when(roleService.getRolesFromCache(eq(roleIds))).thenReturn(roleList);
  121 + when(roleService.hasAnySuperAdmin(same(roleList))).thenReturn(true);
  122 + List<MenuDO> menuList = randomPojoList(MenuDO.class);
  123 + when(menuService.getMenuListFromCache(eq(menuTypes), eq(menusStatuses))).thenReturn(menuList);
  124 +
  125 + // 调用
  126 + List<MenuDO> result = permissionService.getRoleMenuListFromCache(roleIds, menuTypes, menusStatuses);
  127 + // 断言
  128 + assertSame(menuList, result);
  129 + }
  130 +
  131 + @Test
  132 + public void testGetRoleMenuListFromCache_normal() {
  133 + // 准备参数
  134 + Collection<Long> roleIds = asSet(100L, 200L);
  135 + Collection<Integer> menuTypes = asList(2, 3);
  136 + Collection<Integer> menusStatuses = asList(0, 1);
  137 + // mock 方法
  138 + Multimap<Long, Long> roleMenuCache = ImmutableMultimap.<Long, Long>builder().put(100L, 1000L)
  139 + .put(200L, 2000L).put(200L, 2001L).build();
  140 + permissionService.setRoleMenuCache(roleMenuCache);
  141 + List<MenuDO> menuList = randomPojoList(MenuDO.class);
  142 + when(menuService.getMenuListFromCache(eq(asList(1000L, 2000L, 2001L)), eq(menuTypes), eq(menusStatuses))).thenReturn(menuList);
  143 +
  144 + // 调用
  145 + List<MenuDO> result = permissionService.getRoleMenuListFromCache(roleIds, menuTypes, menusStatuses);
  146 + // 断言
  147 + assertSame(menuList, result);
  148 + }
  149 +
  150 + @Test
  151 + public void testGetUserRoleIdsFromCache() {
  152 + // 准备参数
  153 + Long userId = 1L;
  154 + Collection<Integer> roleStatuses = singleton(CommonStatusEnum.ENABLE.getStatus());
  155 + // mock 方法
  156 + Map<Long, Set<Long>> userRoleCache = MapUtil.<Long, Set<Long>>builder()
  157 + .put(1L, asSet(10L, 20L)).build();
  158 + permissionService.setUserRoleCache(userRoleCache);
  159 + RoleDO roleDO01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
  160 + when(roleService.getRoleFromCache(eq(10L))).thenReturn(roleDO01);
  161 + RoleDO roleDO02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
  162 + when(roleService.getRoleFromCache(eq(20L))).thenReturn(roleDO02);
  163 +
  164 + // 调用
  165 + Set<Long> roleIds = permissionService.getUserRoleIdsFromCache(userId, roleStatuses);
  166 + // 断言
  167 + assertEquals(asSet(10L), roleIds);
  168 + }
  169 +
  170 + @Test
  171 + public void testGetRoleMenuIds_superAdmin() {
  172 + // 准备参数
  173 + Long roleId = 100L;
  174 + // mock 方法
  175 + when(roleService.hasAnySuperAdmin(eq(singleton(100L)))).thenReturn(true);
  176 + List<MenuDO> menuList = singletonList(randomPojo(MenuDO.class).setId(1L));
  177 + when(menuService.getMenus()).thenReturn(menuList);
  178 +
  179 + // 调用
  180 + Set<Long> menuIds = permissionService.getRoleMenuIds(roleId);
  181 + // 断言
  182 + assertEquals(singleton(1L), menuIds);
  183 + }
  184 +
  185 + @Test
  186 + public void testGetRoleMenuIds_normal() {
  187 + // 准备参数
  188 + Long roleId = 100L;
  189 + // mock 数据
  190 + RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1L);
  191 + roleMenuMapper.insert(roleMenu01);
  192 + RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(2L);
  193 + roleMenuMapper.insert(roleMenu02);
  194 +
  195 + // 调用
  196 + Set<Long> menuIds = permissionService.getRoleMenuIds(roleId);
  197 + // 断言
  198 + assertEquals(asSet(1L, 2L), menuIds);
  199 + }
  200 +
  201 + @Test
  202 + public void testAssignRoleMenu() {
  203 + // 准备参数
  204 + Long roleId = 1L;
  205 + Set<Long> menuIds = asSet(200L, 300L);
  206 + // mock 数据
  207 + RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(1L).setMenuId(100L);
  208 + roleMenuMapper.insert(roleMenu01);
  209 + RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(1L).setMenuId(200L);
  210 + roleMenuMapper.insert(roleMenu02);
  211 +
  212 + // 调用
  213 + permissionService.assignRoleMenu(roleId, menuIds);
  214 + // 断言
  215 + List<RoleMenuDO> roleMenuList = roleMenuMapper.selectList();
  216 + assertEquals(2, roleMenuList.size());
  217 + assertEquals(1L, roleMenuList.get(0).getRoleId());
  218 + assertEquals(200L, roleMenuList.get(0).getMenuId());
  219 + assertEquals(1L, roleMenuList.get(1).getRoleId());
  220 + assertEquals(300L, roleMenuList.get(1).getMenuId());
  221 + verify(permissionProducer).sendRoleMenuRefreshMessage();
  222 + }
  223 +
  224 + @Test
  225 + public void testAssignUserRole() {
  226 + // 准备参数
  227 + Long userId = 1L;
  228 + Set<Long> roleIds = asSet(200L, 300L);
  229 + // mock 数据
  230 + UserRoleDO userRole01 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(100L);
  231 + userRoleMapper.insert(userRole01);
  232 + UserRoleDO userRole02 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(200L);
  233 + userRoleMapper.insert(userRole02);
  234 +
  235 + // 调用
  236 + permissionService.assignUserRole(userId, roleIds);
  237 + // 断言
  238 + List<UserRoleDO> userRoleDOList = userRoleMapper.selectList();
  239 + assertEquals(2, userRoleDOList.size());
  240 + assertEquals(1L, userRoleDOList.get(0).getUserId());
  241 + assertEquals(200L, userRoleDOList.get(0).getRoleId());
  242 + assertEquals(1L, userRoleDOList.get(1).getUserId());
  243 + assertEquals(300L, userRoleDOList.get(1).getRoleId());
  244 + verify(permissionProducer).sendUserRoleRefreshMessage();
  245 + }
  246 +
  247 + @Test
  248 + public void testGetUserRoleIdListByUserId() {
  249 + // 准备参数
  250 + Long userId = 1L;
  251 + // mock 数据
  252 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L));
  253 + userRoleMapper.insert(userRoleDO01);
  254 + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L));
  255 + userRoleMapper.insert(roleMenuDO02);
  256 +
  257 + // 调用
  258 + Set<Long> result = permissionService.getUserRoleIdListByUserId(userId);
  259 + // 断言
  260 + assertEquals(asSet(10L, 20L), result);
  261 + }
  262 +
  263 + @Test
  264 + public void testGetUserRoleIdListByRoleIds() {
  265 + // 准备参数
  266 + Collection<Long> roleIds = asSet(10L, 20L);
  267 + // mock 数据
  268 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L));
  269 + userRoleMapper.insert(userRoleDO01);
  270 + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(2L).setRoleId(20L));
  271 + userRoleMapper.insert(roleMenuDO02);
  272 +
  273 + // 调用
  274 + Set<Long> result = permissionService.getUserRoleIdListByRoleIds(roleIds);
  275 + // 断言
  276 + assertEquals(asSet(1L, 2L), result);
  277 + }
  278 +
  279 + @Test
  280 + public void testAssignRoleDataScope() {
  281 + // 准备参数
  282 + Long roleId = 1L;
  283 + Integer dataScope = 2;
  284 + Set<Long> dataScopeDeptIds = asSet(10L, 20L);
  285 +
  286 + // 调用
  287 + permissionService.assignRoleDataScope(roleId, dataScope, dataScopeDeptIds);
  288 + // 断言
  289 + verify(roleService).updateRoleDataScope(eq(roleId), eq(dataScope), eq(dataScopeDeptIds));
  290 + }
  291 +
  292 + @Test
66 293 public void testProcessRoleDeleted() {
67 294 // 准备参数
68 295 Long roleId = randomLongId();
... ... @@ -89,6 +316,7 @@ public class PermissionServiceTest extends BaseDbUnitTest {
89 316 assertPojoEquals(dbUserRoles.get(0), userRoleDO02);
90 317 // 断言调用
91 318 verify(permissionProducer).sendRoleMenuRefreshMessage();
  319 + verify(permissionProducer).sendUserRoleRefreshMessage();
92 320 }
93 321  
94 322 @Test
... ... @@ -127,14 +355,33 @@ public class PermissionServiceTest extends BaseDbUnitTest {
127 355 List<UserRoleDO> dbUserRoles = userRoleMapper.selectList();
128 356 assertEquals(1, dbUserRoles.size());
129 357 assertPojoEquals(dbUserRoles.get(0), userRoleDO02);
  358 + // 断言调用
  359 + verify(permissionProducer).sendUserRoleRefreshMessage();
130 360 }
131 361  
  362 +// @Test
  363 +// public void testHasAnyRoles_superAdmin() {
  364 +// // 准备参数
  365 +// String[] roles = new String[]{"yunai", "tudou"};
  366 +// // mock 方法
  367 +// List<RoleDO> roleList = singletonList(randomPojo(RoleDO.class, o -> o.setId(100L)));
  368 +// when(roleService.getRolesFromCache(eq(roleIds))).thenReturn(roleList);
  369 +// when(roleService.hasAnySuperAdmin(same(roleList))).thenReturn(true);
  370 +// List<MenuDO> menuList = randomPojoList(MenuDO.class);
  371 +// when(menuService.getMenuListFromCache(eq(menuTypes), eq(menusStatuses))).thenReturn(menuList);
  372 +//
  373 +// // 调用
  374 +// List<MenuDO> result = permissionService.getRoleMenuListFromCache(roleIds, menuTypes, menusStatuses);
  375 +// // 断言
  376 +// assertSame(menuList, result);
  377 +// }
  378 +
132 379 @Test
133 380 public void testGetDeptDataPermission_All() {
134 381 // 准备参数
135 382 Long userId = 1L;
136 383 // mock 用户的角色编号
137   - userRoleMapper.insert(new UserRoleDO().setUserId(userId).setRoleId(2L));
  384 + permissionService.setUserRoleCache(MapUtil.<Long, Set<Long>>builder().put(1L, asSet(2L)).build());
138 385 // mock 获得用户的角色
139 386 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope())
140 387 .setStatus(CommonStatusEnum.ENABLE.getStatus()));
... ... @@ -154,7 +401,7 @@ public class PermissionServiceTest extends BaseDbUnitTest {
154 401 // 准备参数
155 402 Long userId = 1L;
156 403 // mock 用户的角色编号
157   - userRoleMapper.insert(new UserRoleDO().setUserId(userId).setRoleId(2L));
  404 + permissionService.setUserRoleCache(MapUtil.<Long, Set<Long>>builder().put(1L, asSet(2L)).build());
158 405 // mock 获得用户的角色
159 406 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope())
160 407 .setStatus(CommonStatusEnum.ENABLE.getStatus()));
... ... @@ -164,7 +411,7 @@ public class PermissionServiceTest extends BaseDbUnitTest {
164 411 when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的,看看会不会重复调用
165 412  
166 413 // 调用
167   - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(1L);
  414 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);
168 415 // 断言
169 416 assertFalse(result.getAll());
170 417 assertFalse(result.getSelf());
... ... @@ -178,7 +425,7 @@ public class PermissionServiceTest extends BaseDbUnitTest {
178 425 // 准备参数
179 426 Long userId = 1L;
180 427 // mock 用户的角色编号
181   - userRoleMapper.insert(new UserRoleDO().setUserId(userId).setRoleId(2L));
  428 + permissionService.setUserRoleCache(MapUtil.<Long, Set<Long>>builder().put(1L, asSet(2L)).build());
182 429 // mock 获得用户的角色
183 430 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope())
184 431 .setStatus(CommonStatusEnum.ENABLE.getStatus()));
... ... @@ -188,7 +435,7 @@ public class PermissionServiceTest extends BaseDbUnitTest {
188 435 when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的,看看会不会重复调用
189 436  
190 437 // 调用
191   - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(1L);
  438 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);
192 439 // 断言
193 440 assertFalse(result.getAll());
194 441 assertFalse(result.getSelf());
... ... @@ -201,7 +448,7 @@ public class PermissionServiceTest extends BaseDbUnitTest {
201 448 // 准备参数
202 449 Long userId = 1L;
203 450 // mock 用户的角色编号
204   - userRoleMapper.insert(new UserRoleDO().setUserId(userId).setRoleId(2L));
  451 + permissionService.setUserRoleCache(MapUtil.<Long, Set<Long>>builder().put(1L, asSet(2L)).build());
205 452 // mock 获得用户的角色
206 453 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope())
207 454 .setStatus(CommonStatusEnum.ENABLE.getStatus()));
... ... @@ -229,7 +476,7 @@ public class PermissionServiceTest extends BaseDbUnitTest {
229 476 // 准备参数
230 477 Long userId = 1L;
231 478 // mock 用户的角色编号
232   - userRoleMapper.insert(new UserRoleDO().setUserId(userId).setRoleId(2L));
  479 + permissionService.setUserRoleCache(MapUtil.<Long, Set<Long>>builder().put(1L, asSet(2L)).build());
233 480 // mock 获得用户的角色
234 481 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope())
235 482 .setStatus(CommonStatusEnum.ENABLE.getStatus()));
... ...