|
|
|
|
@ -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
|
|
|
|
|
|