From 2976675331362f98c4969256c9e9e0e17454e259 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 25 Feb 2023 11:50:05 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BC=93=E5=AD=98=E6=94=B9=E9=80=A0=EF=BC=9ARo?= =?UTF-8?q?le=20=E4=BD=BF=E7=94=A8=20Redis=20=E4=BD=9C=E4=B8=BA=E7=BC=93?= =?UTF-8?q?=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/util/spring/SpringAopUtils.java | 46 ------- .../tenant/config/TenantProperties.java | 7 + .../config/YudaoTenantAutoConfiguration.java | 7 +- .../core/redis/TenantRedisCacheManager.java | 16 ++- .../yudao-spring-boot-starter-redis/pom.xml | 5 + .../config/YudaoCacheAutoConfiguration.java | 5 +- .../config/YudaoRedisAutoConfiguration.java | 15 ++- .../dto/TenantDataSourceConfigRespDTO.java | 31 +++++ .../controller/admin/auth/AuthController.java | 5 +- .../admin/user/UserProfileController.java | 2 +- .../dal/mysql/permission/RoleMapper.java | 7 +- .../system/dal/redis/RedisKeyConstants.java | 8 ++ .../permission/RoleRefreshConsumer.java | 29 ---- .../permission/RoleRefreshMessage.java | 21 --- .../mq/producer/permission/RoleProducer.java | 28 ---- .../permission/PermissionServiceImpl.java | 3 +- .../service/permission/RoleService.java | 45 +++---- .../service/permission/RoleServiceImpl.java | 126 +++++++----------- .../service/tenant/TenantServiceImpl.java | 2 +- .../src/main/resources/application-local.yaml | 12 +- .../src/main/resources/application.yaml | 2 + 21 files changed, 178 insertions(+), 244 deletions(-) delete mode 100644 yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringAopUtils.java create mode 100644 yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/tenant/dto/TenantDataSourceConfigRespDTO.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/RoleRefreshConsumer.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/RoleRefreshMessage.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/RoleProducer.java diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringAopUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringAopUtils.java deleted file mode 100644 index b71342cb34..0000000000 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringAopUtils.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.framework.common.util.spring; - -import cn.hutool.core.bean.BeanUtil; -import org.springframework.aop.framework.AdvisedSupport; -import org.springframework.aop.framework.AopProxy; -import org.springframework.aop.support.AopUtils; - -/** - * Spring AOP 工具类 - * - * 参考波克尔 http://www.bubuko.com/infodetail-3471885.html 实现 - */ -public class SpringAopUtils { - - /** - * 获取代理的目标对象 - * - * @param proxy 代理对象 - * @return 目标对象 - */ - public static Object getTarget(Object proxy) throws Exception { - // 不是代理对象 - if (!AopUtils.isAopProxy(proxy)) { - return proxy; - } - // Jdk 代理 - if (AopUtils.isJdkDynamicProxy(proxy)) { - return getJdkDynamicProxyTargetObject(proxy); - } - // Cglib 代理 - return getCglibProxyTargetObject(proxy); - } - - private static Object getCglibProxyTargetObject(Object proxy) throws Exception { - Object dynamicAdvisedInterceptor = BeanUtil.getFieldValue(proxy, "CGLIB$CALLBACK_0"); - AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(dynamicAdvisedInterceptor, "advised"); - return advisedSupport.getTargetSource().getTarget(); - } - - private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception { - AopProxy aopProxy = (AopProxy) BeanUtil.getFieldValue(proxy, "h"); - AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(aopProxy, "advised"); - return advisedSupport.getTargetSource().getTarget(); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/TenantProperties.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/TenantProperties.java index 7cb813ca94..4a172d12b0 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/TenantProperties.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/TenantProperties.java @@ -39,4 +39,11 @@ public class TenantProperties { */ private Set ignoreTables = Collections.emptySet(); + /** + * 需要忽略多租户的 {@link org.springframework.cache.Cache} + * + * 即默认所有 Cache 都开启多租户的功能 + */ + private Set ignoreCaches = Collections.emptySet(); + } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java index 000015a7b9..ad6ae1a842 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java @@ -18,9 +18,7 @@ import cn.iocoder.yudao.framework.tenant.core.web.TenantContextWebFilter; import cn.iocoder.yudao.framework.web.config.WebProperties; import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; import cn.iocoder.yudao.module.system.api.tenant.TenantApi; -import com.baomidou.dynamic.datasource.processor.DsHeaderProcessor; import com.baomidou.dynamic.datasource.processor.DsProcessor; -import com.baomidou.dynamic.datasource.processor.DsSessionProcessor; import com.baomidou.dynamic.datasource.processor.DsSpelExpressionProcessor; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; @@ -136,12 +134,13 @@ public class YudaoTenantAutoConfiguration { @Bean @Primary // 引入租户时,tenantRedisCacheManager 为主 Bean public RedisCacheManager tenantRedisCacheManager(RedisTemplate redisTemplate, - RedisCacheConfiguration redisCacheConfiguration) { + RedisCacheConfiguration redisCacheConfiguration, + TenantProperties tenantProperties) { // 创建 RedisCacheWriter 对象 RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory()); RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory); // 创建 TenantRedisCacheManager 对象 - return new TenantRedisCacheManager(cacheWriter, redisCacheConfiguration); + return new TenantRedisCacheManager(cacheWriter, redisCacheConfiguration, tenantProperties); } } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisCacheManager.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisCacheManager.java index 8058b2f54c..3e4a5cee3e 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisCacheManager.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisCacheManager.java @@ -1,6 +1,9 @@ package cn.iocoder.yudao.framework.tenant.core.redis; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.tenant.config.TenantProperties; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import jodd.io.StreamUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.cache.Cache; import org.springframework.data.redis.cache.RedisCacheConfiguration; @@ -17,17 +20,20 @@ import org.springframework.data.redis.cache.RedisCacheWriter; @Slf4j public class TenantRedisCacheManager extends RedisCacheManager { + private final TenantProperties tenantProperties; + public TenantRedisCacheManager(RedisCacheWriter cacheWriter, - RedisCacheConfiguration defaultCacheConfiguration) { + RedisCacheConfiguration defaultCacheConfiguration, + TenantProperties tenantProperties) { super(cacheWriter, defaultCacheConfiguration); + this.tenantProperties = tenantProperties; } @Override public Cache getCache(String name) { - // 如果开启多租户,则 name 拼接租户后缀 - if (!TenantContextHolder.isIgnore() - && TenantContextHolder.getTenantId() != null) { - name = name + ":" + TenantContextHolder.getTenantId(); + // 如果不忽略多租户的 Cache,则自动拼接租户后缀 + if (!tenantProperties.getIgnoreCaches().contains(name)) { + name = name + StrUtil.COLON + TenantContextHolder.getRequiredTenantId(); } // 继续基于父方法 diff --git a/yudao-framework/yudao-spring-boot-starter-redis/pom.xml b/yudao-framework/yudao-spring-boot-starter-redis/pom.xml index 430ede2550..70fb78429e 100644 --- a/yudao-framework/yudao-spring-boot-starter-redis/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-redis/pom.xml @@ -32,11 +32,16 @@ spring-boot-starter-cache + io.netty netty-all + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + diff --git a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java index 0b837569e4..d40ba4f777 100644 --- a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java @@ -10,6 +10,8 @@ import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; +import static cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration.buildRedisSerializer; + /** * Cache 配置类,基于 Redis 实现 */ @@ -28,7 +30,8 @@ public class YudaoCacheAutoConfiguration { public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) { // 设置使用 JSON 序列化方式 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); - config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json())); + config = config.serializeValuesWith( + RedisSerializationContext.SerializationPair.fromSerializer(buildRedisSerializer())); // 设置 CacheProperties.Redis 的属性 CacheProperties.Redis redisProperties = cacheProperties.getRedis(); diff --git a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java index 901830c69d..5904a3a2b0 100644 --- a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java @@ -1,5 +1,8 @@ package cn.iocoder.yudao.framework.redis.config; +import cn.hutool.core.util.ReflectUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.connection.RedisConnectionFactory; @@ -25,9 +28,17 @@ public class YudaoRedisAutoConfiguration { template.setKeySerializer(RedisSerializer.string()); template.setHashKeySerializer(RedisSerializer.string()); // 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。 - template.setValueSerializer(RedisSerializer.json()); - template.setHashValueSerializer(RedisSerializer.json()); + template.setValueSerializer(buildRedisSerializer()); + template.setHashValueSerializer(buildRedisSerializer()); return template; } + public static RedisSerializer buildRedisSerializer() { + RedisSerializer json = RedisSerializer.json(); + // 解决 LocalDateTime 的序列化 + ObjectMapper objectMapper = (ObjectMapper) ReflectUtil.getFieldValue(json, "mapper"); + objectMapper.registerModules(new JavaTimeModule()); + return json; + } + } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/tenant/dto/TenantDataSourceConfigRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/tenant/dto/TenantDataSourceConfigRespDTO.java new file mode 100644 index 0000000000..eb6bfbe3e8 --- /dev/null +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/tenant/dto/TenantDataSourceConfigRespDTO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.system.api.tenant.dto; + +import lombok.Data; + +/** + * 多租户的数据源配置 Response DTO + * + * @author 芋道源码 + */ +@Data +public class TenantDataSourceConfigRespDTO { + + /** + * 连接名 + */ + private String name; + + /** + * 数据源连接 + */ + private String url; + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java index 7ef7012ccc..af3584b69f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java @@ -96,8 +96,9 @@ public class AuthController { return null; } // 获得角色列表 - Set roleIds = permissionService.getUserRoleIdsFromCache(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus())); - List roleList = roleService.getRoleListFromCache(roleIds); + Set roleIds = permissionService.getUserRoleIdsFromCache(getLoginUserId(), + singleton(CommonStatusEnum.ENABLE.getStatus())); + List roleList = roleService.getRoleList(roleIds); // 获得菜单列表 List menuList = permissionService.getRoleMenuListFromCache(roleIds, SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType(), MenuTypeEnum.BUTTON.getType()), diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.java index d9fc4a6356..2684484502 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.java @@ -63,7 +63,7 @@ public class UserProfileController { AdminUserDO user = userService.getUser(getLoginUserId()); UserProfileRespVO resp = UserConvert.INSTANCE.convert03(user); // 获得用户角色 - List userRoles = roleService.getRoleListFromCache(permissionService.getUserRoleIdListByUserId(user.getId())); + List userRoles = roleService.getRoleList(permissionService.getUserRoleIdListByUserId(user.getId())); resp.setRoles(UserConvert.INSTANCE.convertList(userRoles)); // 获得部门信息 if (user.getDeptId() != null) { diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMapper.java index dba7592232..391fb66bc4 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMapper.java @@ -4,17 +4,20 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleExportReqVO; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; +import com.baomidou.dynamic.datasource.annotation.Master; import org.apache.ibatis.annotations.Mapper; +import org.springframework.cache.annotation.Cacheable; import org.springframework.lang.Nullable; import java.util.Collection; import java.util.List; @Mapper -// TODO 芋艿:@TenantDS +@TenantDS public interface RoleMapper extends BaseMapperX { default PageResult selectPage(RolePageReqVO reqVO) { @@ -42,7 +45,7 @@ public interface RoleMapper extends BaseMapperX { return selectOne(RoleDO::getCode, code); } - default List selectListByStatus(@Nullable Collection statuses) { + default List selectListByStatus(Collection statuses) { return selectList(RoleDO::getStatus, statuses); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java index 5760e3c0f2..55d0011c4b 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java @@ -26,4 +26,12 @@ public interface RedisKeyConstants { "social_auth_state:%s", // 参数为 state STRING, String.class, Duration.ofHours(24)); // 值为 state + /** + * 角色的缓存 + * + * KEY 格式:role::{id} + * 数据格式:String + */ + String ROLE = "role"; + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/RoleRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/RoleRefreshConsumer.java deleted file mode 100644 index bb53b7499a..0000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/RoleRefreshConsumer.java +++ /dev/null @@ -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.RoleRefreshMessage; -import cn.iocoder.yudao.module.system.service.permission.RoleService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link RoleRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class RoleRefreshConsumer extends AbstractChannelMessageListener { - - @Resource - private RoleService roleService; - - @Override - public void onMessage(RoleRefreshMessage message) { - log.info("[onMessage][收到 Role 刷新消息]"); - roleService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/RoleRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/RoleRefreshMessage.java deleted file mode 100644 index e80d8f30c3..0000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/RoleRefreshMessage.java +++ /dev/null @@ -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 RoleRefreshMessage extends AbstractChannelMessage { - - @Override - public String getChannel() { - return "system.role.refresh"; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/RoleProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/RoleProducer.java deleted file mode 100644 index c249d964e8..0000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/RoleProducer.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.permission; - -import cn.iocoder.yudao.module.system.mq.message.permission.RoleRefreshMessage; -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * Role 角色相关消息的 Producer - * - * @author 芋道源码 - */ -@Component -public class RoleProducer { - - @Resource - private RedisMQTemplate redisMQTemplate; - - /** - * 发送 {@link RoleRefreshMessage} 消息 - */ - public void sendRoleRefreshMessage() { - RoleRefreshMessage message = new RoleRefreshMessage(); - redisMQTemplate.send(message); - } - -} 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 43365cf466..164c6b72fb 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 @@ -158,8 +158,7 @@ public class PermissionServiceImpl implements PermissionService { } // 判断角色是否包含超级管理员。如果是超级管理员,获取到全部 - List roleList = roleService.getRoleListFromCache(roleIds); - if (roleService.hasAnySuperAdmin(roleList)) { + if (roleService.hasAnySuperAdmin(roleIds)) { return menuService.getMenuListFromCache(menuTypes, menusStatuses); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java index 70acbdcb7f..2d68c71d94 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java @@ -6,7 +6,6 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleEx import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; -import org.springframework.lang.Nullable; import javax.validation.Valid; import java.util.Collection; @@ -20,11 +19,6 @@ import java.util.Set; */ public interface RoleService { - /** - * 初始化角色的本地缓存 - */ - void initLocalCache(); - /** * 创建角色 * @@ -65,6 +59,14 @@ public interface RoleService { */ void updateRoleDataScope(Long id, Integer dataScope, Set dataScopeDeptIds); + /** + * 获得角色 + * + * @param id 角色编号 + * @return 角色 + */ + RoleDO getRole(Long id); + /** * 获得角色,从缓存中 * @@ -76,10 +78,10 @@ public interface RoleService { /** * 获得角色列表 * - * @param statuses 筛选的状态。允许空,空时不筛选 + * @param ids 角色编号数组 * @return 角色列表 */ - List getRoleListByStatus(@Nullable Collection statuses); + List getRoleList(Collection ids); /** * 获得角色数组,从缓存中 @@ -90,12 +92,19 @@ public interface RoleService { List getRoleListFromCache(Collection ids); /** - * 判断角色数组中,是否有超级管理员 + * 获得角色列表 * - * @param roleList 角色数组 - * @return 是否有管理员 + * @param statuses 筛选的状态 + * @return 角色列表 */ - boolean hasAnySuperAdmin(Collection roleList); + List getRoleListByStatus(Collection statuses); + + /** + * 获得所有角色列表 + * + * @return 角色列表 + */ + List getRoleList(); /** * 判断角色编号数组中,是否有管理员 @@ -103,17 +112,7 @@ public interface RoleService { * @param ids 角色编号数组 * @return 是否有管理员 */ - default boolean hasAnySuperAdmin(Set ids) { - return hasAnySuperAdmin(getRoleListFromCache(ids)); - } - - /** - * 获得角色 - * - * @param id 角色编号 - * @return 角色 - */ - RoleDO getRole(Long id); + boolean hasAnySuperAdmin(Collection ids); /** * 获得角色分页 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java index 30ac2daa05..e49b449991 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java @@ -3,9 +3,9 @@ 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.ObjectUtil; +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.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleExportReqVO; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; @@ -13,26 +13,24 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleUp import cn.iocoder.yudao.module.system.convert.permission.RoleConvert; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; -import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer; import com.google.common.annotations.VisibleForTesting; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionSynchronization; -import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.StringUtils; -import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.*; -import java.util.stream.Collectors; 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.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @@ -45,41 +43,12 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @Slf4j public class RoleServiceImpl implements RoleService { - /** - * 角色缓存 - * key:角色编号 {@link RoleDO#getId()} - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter - private volatile Map roleCache; - @Resource private PermissionService permissionService; @Resource private RoleMapper roleMapper; - @Resource - private RoleProducer roleProducer; - - /** - * 初始化 {@link #roleCache} 缓存 - */ - @Override - @PostConstruct - public void initLocalCache() { - // 注意:忽略自动多租户,因为要全局初始化缓存 - TenantUtils.executeIgnore(() -> { - // 第一步:查询数据 - List roleList = roleMapper.selectList(); - log.info("[initLocalCache][缓存角色,数量为:{}]", roleList.size()); - - // 第二步:构建缓存 - roleCache = convertMap(roleList, RoleDO::getId); - }); - } - @Override @Transactional public Long createRole(RoleCreateReqVO reqVO, Integer type) { @@ -91,18 +60,12 @@ public class RoleServiceImpl implements RoleService { role.setStatus(CommonStatusEnum.ENABLE.getStatus()); role.setDataScope(DataScopeEnum.ALL.getScope()); // 默认可查看所有数据。原因是,可能一些项目不需要项目权限 roleMapper.insert(role); - // 发送刷新消息 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - @Override - public void afterCommit() { - roleProducer.sendRoleRefreshMessage(); - } - }); // 返回 return role.getId(); } @Override + @CacheEvict(value = RedisKeyConstants.ROLE, key = "#reqVO.id") public void updateRole(RoleUpdateReqVO reqVO) { // 校验是否可以更新 validateRoleForUpdate(reqVO.getId()); @@ -112,11 +75,10 @@ public class RoleServiceImpl implements RoleService { // 更新到数据库 RoleDO updateObj = RoleConvert.INSTANCE.convert(reqVO); roleMapper.updateById(updateObj); - // 发送刷新消息 - roleProducer.sendRoleRefreshMessage(); } @Override + @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id") public void updateRoleStatus(Long id, Integer status) { // 校验是否可以更新 validateRoleForUpdate(id); @@ -124,11 +86,10 @@ public class RoleServiceImpl implements RoleService { // 更新状态 RoleDO updateObj = new RoleDO().setId(id).setStatus(status); roleMapper.updateById(updateObj); - // 发送刷新消息 - roleProducer.sendRoleRefreshMessage(); } @Override + @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id") public void updateRoleDataScope(Long id, Integer dataScope, Set dataScopeDeptIds) { // 校验是否可以更新 validateRoleForUpdate(id); @@ -139,12 +100,11 @@ public class RoleServiceImpl implements RoleService { updateObject.setDataScope(dataScope); updateObject.setDataScopeDeptIds(dataScopeDeptIds); roleMapper.updateById(updateObject); - // 发送刷新消息 - roleProducer.sendRoleRefreshMessage(); } @Override @Transactional(rollbackFor = Exception.class) + @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id") public void deleteRole(Long id) { // 校验是否可以更新 validateRoleForUpdate(id); @@ -152,50 +112,54 @@ public class RoleServiceImpl implements RoleService { roleMapper.deleteById(id); // 删除相关数据 permissionService.processRoleDeleted(id); - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - @Override - public void afterCommit() { - roleProducer.sendRoleRefreshMessage(); - } - - }); - } - - @Override - public RoleDO getRoleFromCache(Long id) { - return roleCache.get(id); } @Override public List getRoleListByStatus(@Nullable Collection statuses) { - if (CollUtil.isEmpty(statuses)) { - return roleMapper.selectList(); - } return roleMapper.selectListByStatus(statuses); } + @Override + public List getRoleList() { + return roleMapper.selectList(); + } + + @Override + public RoleDO getRole(Long id) { + return roleMapper.selectById(id); + } + + @Override + @Cacheable(value = RedisKeyConstants.ROLE, key = "#id", unless = "#result == null") + public RoleDO getRoleFromCache(Long id) { + return roleMapper.selectById(id); + } + + @Override + public List getRoleList(Collection ids) { + return roleMapper.selectBatchIds(ids); + } + @Override public List getRoleListFromCache(Collection ids) { if (CollectionUtil.isEmpty(ids)) { return Collections.emptyList(); } - return roleCache.values().stream().filter(roleDO -> ids.contains(roleDO.getId())) - .collect(Collectors.toList()); + // 这里采用 for 循环从缓存中获取,主要考虑 Spring CacheManager 无法批量操作的问题 + RoleServiceImpl self = getSelf(); + return convertList(ids, self::getRoleFromCache); } + @Override - public boolean hasAnySuperAdmin(Collection roleList) { - if (CollectionUtil.isEmpty(roleList)) { + public boolean hasAnySuperAdmin(Collection ids) { + if (CollectionUtil.isEmpty(ids)) { return false; } - return roleList.stream().anyMatch(role -> RoleCodeEnum.isSuperAdmin(role.getCode())); - } - - @Override - public RoleDO getRole(Long id) { - return roleMapper.selectById(id); + return ids.stream().anyMatch(id -> { + RoleDO role = getRoleFromCache(id); + return role != null && RoleCodeEnum.isSuperAdmin(role.getCode()); + }); } @Override @@ -276,4 +240,14 @@ public class RoleServiceImpl implements RoleService { } }); } + + /** + * 获得自身的代理对象,解决 AOP 生效问题 + * + * @return 自己 + */ + private RoleServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java index 3a70d31b90..92e8df961b 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java @@ -156,7 +156,7 @@ public class TenantServiceImpl implements TenantService { public void updateTenantRoleMenu(Long tenantId, Set menuIds) { TenantUtils.execute(tenantId, () -> { // 获得所有角色 - List roles = roleService.getRoleListByStatus(null); + List roles = roleService.getRoleList(); roles.forEach(role -> Assert.isTrue(tenantId.equals(role.getTenantId()), "角色({}/{}) 租户不匹配", role.getId(), role.getTenantId(), tenantId)); // 兜底校验 // 重新分配每个角色的权限 diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index baa26647ec..a4aad05520 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -44,7 +44,7 @@ spring: primary: master datasource: master: - name: ruoyi-vue-pro + name: ruoyi-vue-pro-master url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true # MySQL Connector/J 8.X 连接的示例 # url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例 @@ -65,6 +65,16 @@ spring: password: 123456 # username: sa # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W + tenant_1_ds: + name: tenant_1 + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro-tenant-a?useSSL=false&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true + username: root + password: 123456 + tenant_2_ds: + name: tenant_2 + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro-tenant-b?useSSL=false&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true + username: root + password: 123456 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 redis: diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 29ab3b1d1f..21eda5c800 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -188,6 +188,8 @@ yudao: - rep_demo_jianpiao - tmp_report_data_1 - tmp_report_data_income + ignore-caches: + - demo sms-code: # 短信验证码相关的配置项 expire-times: 10m send-frequency: 1m