From 8b704ff4831f3fd348991e50f03169b2d423e9e6 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 29 Jul 2023 06:37:27 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20Redis=20=E7=BC=93=E5=AD=98?= =?UTF-8?q?=EF=BC=8C=E6=9B=BF=E4=BB=A3=E6=9C=AC=E5=9C=B0=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../banner/core/BannerApplicationRunner.java | 2 + .../mybatis/core/mapper/BaseMapperX.java | 8 + .../mybatis/core/type/EncryptTypeHandler.java | 2 +- .../yudao-spring-boot-starter-redis/pom.xml | 5 + .../config/YudaoCacheAutoConfiguration.java | 31 +- .../config/YudaoRedisAutoConfiguration.java | 15 +- .../redis/core/TimeoutRedisCacheManager.java | 51 ++ .../admin/redis/RedisController.java | 79 +-- .../api/permission/PermissionApiImpl.java | 2 +- .../controller/admin/auth/AuthController.java | 42 +- .../auth/vo/AuthPermissionInfoRespVO.java | 48 ++ .../permission/PermissionController.java | 6 +- .../system/convert/auth/AuthConvert.java | 20 +- .../system/dal/mysql/dept/DeptMapper.java | 5 + .../dal/mysql/permission/MenuMapper.java | 3 + .../dal/mysql/permission/RoleMenuMapper.java | 14 +- .../system/dal/redis/RedisKeyConstants.java | 100 ++- .../oauth2/OAuth2AccessTokenRedisDAO.java | 2 +- .../auth/OAuth2ClientRefreshConsumer.java | 29 - .../mq/consumer/dept/DeptRefreshConsumer.java | 29 - .../mail/MailAccountRefreshConsumer.java | 29 - .../mail/MailTemplateRefreshConsumer.java | 29 - .../notify/NotifyTemplateRefreshConsumer.java | 29 - .../permission/MenuRefreshConsumer.java | 28 - .../permission/RoleMenuRefreshConsumer.java | 28 - .../permission/RoleRefreshConsumer.java | 29 - .../permission/UserRoleRefreshConsumer.java | 28 - .../sms/SmsTemplateRefreshConsumer.java | 29 - .../auth/OAuth2ClientRefreshMessage.java | 23 - .../mq/message/dept/DeptRefreshMessage.java | 23 - .../mail/MailAccountRefreshMessage.java | 23 - .../mail/MailTemplateRefreshMessage.java | 23 - .../notify/NotifyTemplateRefreshMessage.java | 23 - .../permission/MenuRefreshMessage.java | 19 - .../permission/RoleMenuRefreshMessage.java | 21 - .../permission/RoleRefreshMessage.java | 21 - .../permission/UserRoleRefreshMessage.java | 21 - .../producer/auth/OAuth2ClientProducer.java | 20 - .../system/mq/producer/dept/DeptProducer.java | 20 - .../system/mq/producer/mail/MailProducer.java | 16 - .../mq/producer/notify/NotifyProducer.java | 26 - .../mq/producer/permission/MenuProducer.java | 20 - .../permission/PermissionProducer.java | 28 - .../mq/producer/permission/RoleProducer.java | 22 - .../system/mq/producer/sms/SmsProducer.java | 8 - .../system/service/dept/DeptService.java | 49 +- .../system/service/dept/DeptServiceImpl.java | 200 ++---- .../service/mail/MailAccountService.java | 21 +- .../service/mail/MailAccountServiceImpl.java | 53 +- .../service/mail/MailTemplateService.java | 5 - .../service/mail/MailTemplateServiceImpl.java | 56 +- .../service/notify/NotifyTemplateService.java | 21 +- .../notify/NotifyTemplateServiceImpl.java | 60 +- .../service/oauth2/OAuth2ClientService.java | 17 +- .../oauth2/OAuth2ClientServiceImpl.java | 70 +- .../service/permission/MenuService.java | 41 +- .../service/permission/MenuServiceImpl.java | 143 +--- .../service/permission/PermissionService.java | 154 ++-- .../permission/PermissionServiceImpl.java | 443 +++++------- .../service/permission/RoleService.java | 51 +- .../service/permission/RoleServiceImpl.java | 188 +++-- .../service/sms/SmsTemplateService.java | 51 +- .../service/sms/SmsTemplateServiceImpl.java | 79 +-- .../tenant/TenantPackageServiceImpl.java | 4 +- .../service/tenant/TenantServiceImpl.java | 9 +- .../service/user/AdminUserServiceImpl.java | 3 +- .../service/dept/DeptServiceImplTest.java | 424 +++++------ .../mail/MailAccountServiceImplTest.java | 76 +- .../mail/MailTemplateServiceImplTest.java | 97 +-- .../notify/NotifyTemplateServiceImplTest.java | 80 +-- .../oauth2/OAuth2ClientServiceImplTest.java | 156 ++--- .../permission/MenuServiceImplTest.java | 140 +--- .../permission/PermissionServiceTest.java | 656 +++++++++--------- .../permission/RoleServiceImplTest.java | 245 ++++--- .../sms/SmsTemplateServiceImplTest.java | 121 ++-- .../service/tenant/TenantServiceImplTest.java | 4 +- .../user/AdminUserServiceImplTest.java | 4 +- 77 files changed, 1788 insertions(+), 3012 deletions(-) create mode 100644 yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/TimeoutRedisCacheManager.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/auth/OAuth2ClientRefreshConsumer.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/dept/DeptRefreshConsumer.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailAccountRefreshConsumer.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailTemplateRefreshConsumer.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/notify/NotifyTemplateRefreshConsumer.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/MenuRefreshConsumer.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/RoleMenuRefreshConsumer.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/consumer/permission/UserRoleRefreshConsumer.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsTemplateRefreshConsumer.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/auth/OAuth2ClientRefreshMessage.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/dept/DeptRefreshMessage.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailAccountRefreshMessage.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailTemplateRefreshMessage.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/notify/NotifyTemplateRefreshMessage.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/MenuRefreshMessage.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/RoleMenuRefreshMessage.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/message/permission/UserRoleRefreshMessage.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/auth/OAuth2ClientProducer.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/dept/DeptProducer.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/notify/NotifyProducer.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/MenuProducer.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/PermissionProducer.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-spring-boot-starter-banner/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java b/yudao-framework/yudao-spring-boot-starter-banner/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java index bda196c5c..c300c525e 100644 --- a/yudao-framework/yudao-spring-boot-starter-banner/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java +++ b/yudao-framework/yudao-spring-boot-starter-banner/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java @@ -39,6 +39,8 @@ public class BannerApplicationRunner implements ApplicationRunner { System.out.println("[微信公众号 yudao-module-mp 教程][参考 https://cloud.iocoder.cn/mp/build/ 开启]"); // 商城 System.out.println("[商城系统 yudao-module-mall 教程][参考 https://cloud.iocoder.cn/mall/build/ 开启]"); + // 支付 + System.out.println("[支付系统 yudao-module-pay - 已禁用][参考 https://doc.iocoder.cn/pay/build/ 开启]"); }); } diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java index a831337e8..9eed6d0e0 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java @@ -103,8 +103,16 @@ public interface BaseMapperX extends BaseMapper { update(update, new QueryWrapper<>()); } + default void updateBatch(Collection entities) { + Db.updateBatchById(entities); + } + default void updateBatch(Collection entities, int size) { Db.updateBatchById(entities, size); } + default void saveOrUpdateBatch(Collection collection) { + Db.saveOrUpdateBatch(collection); + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/EncryptTypeHandler.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/EncryptTypeHandler.java index 9327ebbfe..7ef0f4ece 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/EncryptTypeHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/EncryptTypeHandler.java @@ -13,7 +13,7 @@ import java.sql.ResultSet; import java.sql.SQLException; /** - * 字段字段的 TypeHandler 实现类,基于 {@link AES} 实现 + * 字段字段的 TypeHandler 实现类,基于 {@link cn.hutool.crypto.symmetric.AES} 实现 * 可通过 jasypt.encryptor.password 配置项,设置密钥 * * @author 芋道源码 diff --git a/yudao-framework/yudao-spring-boot-starter-redis/pom.xml b/yudao-framework/yudao-spring-boot-starter-redis/pom.xml index 4b11789b4..13b447b71 100644 --- a/yudao-framework/yudao-spring-boot-starter-redis/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-redis/pom.xml @@ -36,6 +36,11 @@ 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 0b837569e..1442e8a83 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 @@ -1,5 +1,7 @@ package cn.iocoder.yudao.framework.redis.config; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.redis.core.TimeoutRedisCacheManager; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.cache.CacheProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -7,8 +9,15 @@ import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.cache.RedisCacheWriter; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.RedisSerializationContext; -import org.springframework.data.redis.serializer.RedisSerializer; + +import java.util.Objects; + +import static cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration.buildRedisSerializer; /** * Cache 配置类,基于 Redis 实现 @@ -20,15 +29,19 @@ public class YudaoCacheAutoConfiguration { /** * RedisCacheConfiguration Bean - * + *

* 参考 org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration 的 createConfiguration 方法 */ @Bean @Primary public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) { - // 设置使用 JSON 序列化方式 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); - config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json())); + // 设置使用 : 单冒号,而不是双 :: 冒号,避免 Redis Desktop Manager 多余空格 + // 详细可见 https://blog.csdn.net/chuixue24/article/details/103928965 博客 + config = config.computePrefixWith(cacheName -> cacheName + StrUtil.COLON); + // 设置使用 JSON 序列化方式 + config = config.serializeValuesWith( + RedisSerializationContext.SerializationPair.fromSerializer(buildRedisSerializer())); // 设置 CacheProperties.Redis 的属性 CacheProperties.Redis redisProperties = cacheProperties.getRedis(); @@ -47,4 +60,14 @@ public class YudaoCacheAutoConfiguration { return config; } + @Bean + public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate, + RedisCacheConfiguration redisCacheConfiguration) { + // 创建 RedisCacheWriter 对象 + RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory()); + RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory); + // 创建 TenantRedisCacheManager 对象 + return new TimeoutRedisCacheManager(cacheWriter, redisCacheConfiguration); + } + } 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 901830c69..5904a3a2b 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-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/TimeoutRedisCacheManager.java b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/TimeoutRedisCacheManager.java new file mode 100644 index 000000000..cfdee653d --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/TimeoutRedisCacheManager.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.framework.redis.core; + +import cn.hutool.core.util.StrUtil; +import org.springframework.boot.convert.DurationStyle; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.redis.cache.RedisCache; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.cache.RedisCacheWriter; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +/** + * 支持自定义过期时间的 {@link RedisCacheManager} 实现类 + * + * 在 {@link Cacheable#cacheNames()} 格式为 "key#ttl" 时,# 后面的 ttl 为过期时间,单位为秒 + * + * @author 芋道源码 + */ +public class TimeoutRedisCacheManager extends RedisCacheManager { + + private static final String SPLIT = "#"; + + public TimeoutRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) { + super(cacheWriter, defaultCacheConfiguration); + } + + @Override + protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) { + if (StrUtil.isEmpty(name)) { + return super.createRedisCache(name, cacheConfig); + } + // 如果使用 # 分隔,大小不为 2,则说明不使用自定义过期时间 + String[] names = StrUtil.splitToArray(name, SPLIT); + if (names.length != 2) { + return super.createRedisCache(name, cacheConfig); + } + + // 核心:通过修改 cacheConfig 的过期时间,实现自定义过期时间 + if (cacheConfig != null) { + // 移除 # 后面的 : 以及后面的内容,避免影响解析 + names[1] = StrUtil.subBefore(names[1], StrUtil.COLON, false); + // 解析时间 + Duration duration = DurationStyle.detectAndParse(names[1], ChronoUnit.SECONDS); + cacheConfig = cacheConfig.entryTtl(duration); + } + return super.createRedisCache(names[0], cacheConfig); + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.java index c82b21cda..57a3d6b89 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.java @@ -1,27 +1,20 @@ package cn.iocoder.yudao.module.infra.controller.admin.redis; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; -import cn.iocoder.yudao.framework.redis.core.RedisKeyRegistry; -import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisKeyDefineRespVO; -import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisKeyValueRespVO; import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisMonitorRespVO; import cn.iocoder.yudao.module.infra.convert.redis.RedisConvert; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.data.redis.connection.RedisServerCommands; -import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.RedisCallback; -import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; -import java.util.*; +import java.util.Properties; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -47,66 +40,4 @@ public class RedisController { return success(RedisConvert.INSTANCE.build(info, dbSize, commandStats)); } - @GetMapping("/get-key-define-list") - @Operation(summary = "获得 Redis Key 模板列表") - @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')") - public CommonResult> getKeyDefineList() { - List keyDefines = RedisKeyRegistry.list(); - return success(RedisConvert.INSTANCE.convertList(keyDefines)); - } - - @GetMapping("/get-key-list") - @Operation(summary = "获得 Redis keys 键名列表") - @Parameter(name = "keyTemplate", description = "Redis Key 定义", example = "true") - @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')") - public CommonResult> getKeyDefineList(@RequestParam("keyTemplate") String keyTemplate) { - return success(getKeyDefineList0(keyTemplate)); - } - - private Set getKeyDefineList0(String keyTemplate) { - // key 格式化 - String key = StrUtil.replace(keyTemplate, "%[s|c|b|d|x|o|f|a|e|g]", parameter -> "*"); - // scan 扫描 key - Set keys = new LinkedHashSet<>(); - stringRedisTemplate.execute((RedisCallback>) connection -> { - try (Cursor cursor = connection.scan(ScanOptions.scanOptions().match(key).count(100).build())) { - cursor.forEachRemaining(value -> keys.add(StrUtil.utf8Str(value))); - } catch (Exception e) { - throw new RuntimeException(e); - } - return keys; - }); - return keys; - } - - @GetMapping("/get-key-value") - @Operation(summary = "获得 Redis key 内容") - @Parameter(name = "key", description = "Redis Key", example = "oauth2_access_token:233") - @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')") - public CommonResult getKeyValue(@RequestParam("key") String key) { - String value = stringRedisTemplate.opsForValue().get(key); - return success(new RedisKeyValueRespVO(key, value)); - } - - @DeleteMapping("/delete-key") - @Operation(summary = "删除 Redis Key") - @Parameter(name = "key", description = "Redis Key", example = "oauth2_access_token:233") - @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')") - public CommonResult deleteKey(@RequestParam("key") String key) { - stringRedisTemplate.delete(key); - return success(Boolean.TRUE); - } - - @DeleteMapping("/delete-keys") - @Operation(summary = "删除 Redis Key 根据模板") - @Parameter(name = "keyTemplate", description = "Redis Key 定义", example = "true") - @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')") - public CommonResult deleteKeys(@RequestParam("keyTemplate") String keyTemplate) { - Set keys = getKeyDefineList0(keyTemplate); - if (CollUtil.isNotEmpty(keys)) { - stringRedisTemplate.delete(keys); - } - return success(Boolean.TRUE); - } - } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApiImpl.java index 6141e1536..bec7d5ce9 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApiImpl.java @@ -21,7 +21,7 @@ public class PermissionApiImpl implements PermissionApi { @Override public CommonResult> getUserRoleIdListByRoleIds(Collection roleIds) { - return success(permissionService.getUserRoleIdListByRoleIds(roleIds)); + return success(permissionService.getUserRoleIdListByRoleId(roleIds)); } @Override 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 ab001cfc0..57a70b80f 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 @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.system.controller.admin.auth; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.collection.SetUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.security.config.SecurityProperties; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; @@ -12,8 +11,8 @@ 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.user.AdminUserDO; import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; -import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; +import cn.iocoder.yudao.module.system.service.permission.MenuService; import cn.iocoder.yudao.module.system.service.permission.PermissionService; import cn.iocoder.yudao.module.system.service.permission.RoleService; import cn.iocoder.yudao.module.system.service.social.SocialUserService; @@ -34,9 +33,9 @@ import java.util.List; import java.util.Set; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.obtainAuthorization; -import static java.util.Collections.singleton; @Tag(name = "管理后台 - 认证") @RestController @@ -52,6 +51,8 @@ public class AuthController { @Resource private RoleService roleService; @Resource + private MenuService menuService; + @Resource private PermissionService permissionService; @Resource private SocialUserService socialUserService; @@ -91,33 +92,24 @@ public class AuthController { @GetMapping("/get-permission-info") @Operation(summary = "获取登录用户的权限信息") public CommonResult getPermissionInfo() { - // 获得用户信息 + // 1.1 获得用户信息 AdminUserDO user = userService.getUser(getLoginUserId()); if (user == null) { return null; } - // 获得角色列表 - Set roleIds = permissionService.getUserRoleIdsFromCache(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus())); - List roleList = roleService.getRoleListFromCache(roleIds); - // 获得菜单列表 - List menuList = permissionService.getRoleMenuListFromCache(roleIds, - SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType(), MenuTypeEnum.BUTTON.getType()), - singleton(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的 - // 拼接结果返回 - return success(AuthConvert.INSTANCE.convert(user, roleList, menuList)); - } - @GetMapping("/list-menus") - @Operation(summary = "获得登录用户的菜单列表") - public CommonResult> getMenuList() { - // 获得角色列表 - Set roleIds = permissionService.getUserRoleIdsFromCache(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus())); - // 获得用户拥有的菜单列表 - List menuList = permissionService.getRoleMenuListFromCache(roleIds, - SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型 - singleton(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的 - // 转换成 Tree 结构返回 - return success(AuthConvert.INSTANCE.buildMenuTree(menuList)); + // 1.2 获得角色列表 + Set roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId()); + List roles = roleService.getRoleList(roleIds); + roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色 + + // 1.3 获得菜单列表 + Set menuIds = permissionService.getRoleMenuListByRoleId(convertSet(roles, RoleDO::getId)); + List menuList = menuService.getMenuList(menuIds); + menuList.removeIf(menu -> !CommonStatusEnum.ENABLE.getStatus().equals(menu.getStatus())); // 移除禁用的菜单 + + // 2. 拼接结果返回 + return success(AuthConvert.INSTANCE.convert(user, roles, menuList)); } // ========== 短信登录相关 ========== diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java index d22765cbe..7b3a4938d 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java @@ -6,6 +6,7 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.util.List; import java.util.Set; @Schema(description = "管理后台 - 登录用户的权限信息 Response VO,额外包括用户信息和角色列表") @@ -24,6 +25,9 @@ public class AuthPermissionInfoRespVO { @Schema(description = "操作权限数组", requiredMode = Schema.RequiredMode.REQUIRED) private Set permissions; + @Schema(description = "菜单树", required = true) + private List menus; + @Schema(description = "用户信息 VO") @Data @NoArgsConstructor @@ -42,4 +46,48 @@ public class AuthPermissionInfoRespVO { } + @Schema(description = "管理后台 - 登录用户的菜单信息 Response VO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class MenuVO { + + @Schema(description = "菜单名称", required = true, example = "芋道") + private Long id; + + @Schema(description = "父菜单 ID", required = true, example = "1024") + private Long parentId; + + @Schema(description = "菜单名称", required = true, example = "芋道") + private String name; + + @Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post") + private String path; + + @Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index") + private String component; + + @Schema(description = "组件名", example = "SystemUser") + private String componentName; + + @Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list") + private String icon; + + @Schema(description = "是否可见", required = true, example = "false") + private Boolean visible; + + @Schema(description = "是否缓存", required = true, example = "false") + private Boolean keepAlive; + + @Schema(description = "是否总是显示", example = "false") + private Boolean alwaysShow; + + /** + * 子路由 + */ + private List children; + + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/PermissionController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/PermissionController.java index ca768d14f..91f5d4266 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/PermissionController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/PermissionController.java @@ -37,10 +37,10 @@ public class PermissionController { @Operation(summary = "获得角色拥有的菜单编号") @Parameter(name = "roleId", description = "角色编号", required = true) - @GetMapping("/list-role-resources") + @GetMapping("/list-role-menus") @PreAuthorize("@ss.hasPermission('system:permission:assign-role-menu')") - public CommonResult> listRoleMenus(Long roleId) { - return success(permissionService.getRoleMenuIds(roleId)); + public CommonResult> getRoleMenuList(Long roleId) { + return success(permissionService.getRoleMenuListByRoleId(roleId)); } @PostMapping("/assign-role-menu") diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java index de5f401c7..1b79036fa 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; 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.user.AdminUserDO; +import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; import org.slf4j.LoggerFactory; @@ -27,14 +28,16 @@ public interface AuthConvert { default AuthPermissionInfoRespVO convert(AdminUserDO user, List roleList, List menuList) { return AuthPermissionInfoRespVO.builder() - .user(AuthPermissionInfoRespVO.UserVO.builder().id(user.getId()).nickname(user.getNickname()) - .avatar(user.getAvatar()).build()) + .user(AuthPermissionInfoRespVO.UserVO.builder().id(user.getId()).nickname(user.getNickname()).avatar(user.getAvatar()).build()) .roles(convertSet(roleList, RoleDO::getCode)) + // 权限标识信息 .permissions(convertSet(menuList, MenuDO::getPermission)) + // 菜单树 + .menus(buildMenuTree(menuList)) .build(); } - AuthMenuRespVO convertTreeNode(MenuDO menu); + AuthPermissionInfoRespVO.MenuVO convertTreeNode(MenuDO menu); /** * 将菜单列表,构建成菜单树 @@ -42,20 +45,23 @@ public interface AuthConvert { * @param menuList 菜单列表 * @return 菜单树 */ - default List buildMenuTree(List menuList) { + default List buildMenuTree(List menuList) { + // 移除按钮 + menuList.removeIf(menu -> menu.getType().equals(MenuTypeEnum.BUTTON.getType())); // 排序,保证菜单的有序性 menuList.sort(Comparator.comparing(MenuDO::getSort)); + // 构建菜单树 // 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。 - Map treeNodeMap = new LinkedHashMap<>(); + Map treeNodeMap = new LinkedHashMap<>(); menuList.forEach(menu -> treeNodeMap.put(menu.getId(), AuthConvert.INSTANCE.convertTreeNode(menu))); // 处理父子关系 treeNodeMap.values().stream().filter(node -> !node.getParentId().equals(ID_ROOT)).forEach(childNode -> { // 获得父节点 - AuthMenuRespVO parentNode = treeNodeMap.get(childNode.getParentId()); + AuthPermissionInfoRespVO.MenuVO parentNode = treeNodeMap.get(childNode.getParentId()); if (parentNode == null) { LoggerFactory.getLogger(getClass()).error("[buildRouterTree][resource({}) 找不到父资源({})]", - childNode.getId(), childNode.getParentId()); + childNode.getId(), childNode.getParentId()); return; } // 将自己添加到父节点中 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java index def79df69..cc4f334e6 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqV import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import org.apache.ibatis.annotations.Mapper; +import java.util.Collection; import java.util.List; @Mapper @@ -25,4 +26,8 @@ public interface DeptMapper extends BaseMapperX { return selectCount(DeptDO::getParentId, parentId); } + default List selectListByParentId(Collection parentIds) { + return selectList(DeptDO::getParentId, parentIds); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java index 3d645bdf2..8458faa67 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java @@ -25,4 +25,7 @@ public interface MenuMapper extends BaseMapperX { .eqIfPresent(MenuDO::getStatus, reqVO.getStatus())); } + default List selectListByPermission(String permission) { + return selectList(MenuDO::getPermission, permission); + } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMenuMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMenuMapper.java index 20f418676..02cb6b8a4 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMenuMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMenuMapper.java @@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.system.dal.mysql.permission; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.apache.ibatis.annotations.Mapper; -import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.List; @@ -13,14 +11,18 @@ import java.util.List; @Mapper public interface RoleMenuMapper extends BaseMapperX { - @Repository - class BatchInsertMapper extends ServiceImpl { - } - default List selectListByRoleId(Long roleId) { return selectList(RoleMenuDO::getRoleId, roleId); } + default List selectListByRoleId(Collection roleIds) { + return selectList(RoleMenuDO::getRoleId, roleIds); + } + + default List selectListByMenuId(Long menuId) { + return selectList(RoleMenuDO::getMenuId, menuId); + } + default void deleteListByRoleIdAndMenuIds(Long roleId, Collection menuIds) { delete(new LambdaQueryWrapper() .eq(RoleMenuDO::getRoleId, roleId) 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 5760e3c0f..cda6dab4b 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 @@ -1,12 +1,7 @@ package cn.iocoder.yudao.module.system.dal.redis; -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import java.time.Duration; - -import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING; - /** * System Redis Key 枚举类 * @@ -14,16 +9,93 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.S */ public interface RedisKeyConstants { - RedisKeyDefine CAPTCHA_CODE = new RedisKeyDefine("验证码的缓存", - "captcha_code:%s", // 参数为 uuid - STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); + /** + * 指定部门的所有子部门编号数组的缓存 + *

+ * KEY 格式:dept_children_ids:{id} + * VALUE 数据类型:String 子部门编号集合 + */ + String DEPT_CHILDREN_ID_LIST = "dept_children_ids"; - RedisKeyDefine OAUTH2_ACCESS_TOKEN = new RedisKeyDefine("访问令牌的缓存", - "oauth2_access_token:%s", // 参数为访问令牌 token - STRING, OAuth2AccessTokenDO.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); + /** + * 角色的缓存 + *

+ * KEY 格式:role:{id} + * VALUE 数据类型:String 角色信息 + */ + String ROLE = "role"; - RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交登陆的 state", // 注意,它是被 JustAuth 的 justauth.type.prefix 使用到 - "social_auth_state:%s", // 参数为 state - STRING, String.class, Duration.ofHours(24)); // 值为 state + /** + * 用户拥有的角色编号的缓存 + *

+ * KEY 格式:user_role_ids:{userId} + * VALUE 数据类型:String 角色编号集合 + */ + String USER_ROLE_ID_LIST = "user_role_ids"; + /** + * 拥有指定菜单的角色编号的缓存 + *

+ * KEY 格式:user_role_ids:{menuId} + * VALUE 数据类型:String 角色编号集合 + */ + String MENU_ROLE_ID_LIST = "menu_role_ids"; + + /** + * 拥有权限对应的菜单编号数组的缓存 + *

+ * KEY 格式:permission_menu_ids:{permission} + * VALUE 数据类型:String 菜单编号数组 + */ + String PERMISSION_MENU_ID_LIST = "permission_menu_ids"; + + /** + * OAuth2 客户端的缓存 + *

+ * KEY 格式:user:{id} + * VALUE 数据类型:String 客户端信息 + */ + String OAUTH_CLIENT = "oauth_client"; + + /** + * 访问令牌的缓存 + *

+ * KEY 格式:oauth2_access_token:{token} + * VALUE 数据类型:String 访问令牌信息 {@link OAuth2AccessTokenDO} + *

+ * 由于动态过期时间,使用 RedisTemplate 操作 + */ + String OAUTH2_ACCESS_TOKEN = "oauth2_access_token:%s"; + + /** + * 站内信模版的缓存 + *

+ * KEY 格式:notify_template:{code} + * VALUE 数据格式:String 模版信息 + */ + String NOTIFY_TEMPLATE = "notify_template"; + + /** + * 邮件账号的缓存 + *

+ * KEY 格式:sms_template:{id} + * VALUE 数据格式:String 账号信息 + */ + String MAIL_ACCOUNT = "mail_account"; + + /** + * 邮件模版的缓存 + *

+ * KEY 格式:mail_template:{code} + * VALUE 数据格式:String 模版信息 + */ + String MAIL_TEMPLATE = "mail_template"; + + /** + * 短信模版的缓存 + *

+ * KEY 格式:sms_template:{id} + * VALUE 数据格式:String 模版信息 + */ + String SMS_TEMPLATE = "sms_template"; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java index d57beb881..7827dfa10 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java @@ -53,7 +53,7 @@ public class OAuth2AccessTokenRedisDAO { } private static String formatKey(String accessToken) { - return String.format(OAUTH2_ACCESS_TOKEN.getKeyTemplate(), accessToken); + return String.format(OAUTH2_ACCESS_TOKEN, accessToken); } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/auth/OAuth2ClientRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/auth/OAuth2ClientRefreshConsumer.java deleted file mode 100644 index 52763e96e..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/auth/OAuth2ClientRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.auth; - -import cn.iocoder.yudao.module.system.mq.message.auth.OAuth2ClientRefreshMessage; -import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link OAuth2ClientRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class OAuth2ClientRefreshConsumer { - - @Resource - private OAuth2ClientService oauth2ClientService; - - @EventListener - public void execute(OAuth2ClientRefreshMessage message) { - log.info("[execute][收到 OAuth2Client 刷新消息]"); - oauth2ClientService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/dept/DeptRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/dept/DeptRefreshConsumer.java deleted file mode 100644 index 981a0b9f9..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/dept/DeptRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.dept; - -import cn.iocoder.yudao.module.system.mq.message.dept.DeptRefreshMessage; -import cn.iocoder.yudao.module.system.service.dept.DeptService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link DeptRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class DeptRefreshConsumer { - - @Resource - private DeptService deptService; - - @EventListener - public void execute(DeptRefreshMessage message) { - log.info("[execute][收到 Dept 刷新消息]"); - deptService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailAccountRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailAccountRefreshConsumer.java deleted file mode 100644 index 63a2e625e..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailAccountRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.mail; - -import cn.iocoder.yudao.module.system.mq.message.mail.MailAccountRefreshMessage; -import cn.iocoder.yudao.module.system.service.mail.MailAccountService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link MailAccountRefreshMessage} 的消费者 - * - * @author wangjingyi - */ -@Component -@Slf4j -public class MailAccountRefreshConsumer { - - @Resource - private MailAccountService mailAccountService; - - @EventListener - public void onMessage(MailAccountRefreshMessage message) { - log.info("[onMessage][收到 Mail Account 刷新信息]"); - mailAccountService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailTemplateRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailTemplateRefreshConsumer.java deleted file mode 100644 index c6f823dd6..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailTemplateRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.mail; - -import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage; -import cn.iocoder.yudao.module.system.service.mail.MailTemplateService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link MailTemplateRefreshMessage} 的消费者 - * - * @author wangjingyi - */ -@Component -@Slf4j -public class MailTemplateRefreshConsumer { - - @Resource - private MailTemplateService mailTemplateService; - - @EventListener - public void onMessage(MailTemplateRefreshMessage message) { - log.info("[onMessage][收到 Mail Template 刷新信息]"); - mailTemplateService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/notify/NotifyTemplateRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/notify/NotifyTemplateRefreshConsumer.java deleted file mode 100644 index 757a6f251..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/notify/NotifyTemplateRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.notify; - -import cn.iocoder.yudao.module.system.mq.message.notify.NotifyTemplateRefreshMessage; -import cn.iocoder.yudao.module.system.service.notify.NotifyTemplateService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link NotifyTemplateRefreshMessage} 的消费者 - * - * @author xrcoder - */ -@Component -@Slf4j -public class NotifyTemplateRefreshConsumer { - - @Resource - private NotifyTemplateService notifyTemplateService; - - @EventListener - public void onMessage(NotifyTemplateRefreshMessage message) { - log.info("[onMessage][收到 NotifyTemplate 刷新消息]"); - notifyTemplateService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/MenuRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/MenuRefreshConsumer.java deleted file mode 100644 index 6103022b4..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/MenuRefreshConsumer.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.permission; - -import cn.iocoder.yudao.module.system.mq.message.permission.MenuRefreshMessage; -import cn.iocoder.yudao.module.system.service.permission.MenuService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link MenuRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class MenuRefreshConsumer { - - @Resource - private MenuService menuService; - - @EventListener - public void execute(MenuRefreshMessage menuRefreshMessage) { - log.info("[execute][收到 Menu 刷新消息]"); - menuService.initLocalCache(); - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/RoleMenuRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/RoleMenuRefreshConsumer.java deleted file mode 100644 index 52b53606c..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/RoleMenuRefreshConsumer.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.permission; - -import cn.iocoder.yudao.module.system.mq.message.permission.RoleMenuRefreshMessage; -import cn.iocoder.yudao.module.system.service.permission.PermissionService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link RoleMenuRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class RoleMenuRefreshConsumer { - - @Resource - private PermissionService permissionService; - - @EventListener - public void execute(RoleMenuRefreshMessage roleMenuRefreshMessage) { - log.info("[execute][收到 Role 与 Menu 的关联刷新消息]"); - permissionService.initLocalCache(); - } -} 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 fa8b48f5e..000000000 --- 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.module.system.mq.message.permission.RoleRefreshMessage; -import cn.iocoder.yudao.module.system.service.permission.RoleService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link RoleRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class RoleRefreshConsumer { - - @Resource - private RoleService roleService; - - @EventListener - public void execute(RoleRefreshMessage message) { - log.info("[execute][收到 Role 刷新消息]"); - roleService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/UserRoleRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/UserRoleRefreshConsumer.java deleted file mode 100644 index e1426797a..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/UserRoleRefreshConsumer.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.permission; - -import cn.iocoder.yudao.module.system.mq.message.permission.UserRoleRefreshMessage; -import cn.iocoder.yudao.module.system.service.permission.PermissionService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link UserRoleRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class UserRoleRefreshConsumer { - - @Resource - private PermissionService permissionService; - - @EventListener - public void execute(UserRoleRefreshMessage userRoleRefreshMessage) { - log.info("[execute][收到 User 与 Role 的关联刷新消息]"); - permissionService.initLocalCache(); - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsTemplateRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsTemplateRefreshConsumer.java deleted file mode 100644 index 737654d31..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsTemplateRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.sms; - -import cn.iocoder.yudao.module.system.mq.message.sms.SmsTemplateRefreshMessage; -import cn.iocoder.yudao.module.system.service.sms.SmsTemplateService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link SmsTemplateRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class SmsTemplateRefreshConsumer { - - @Resource - private SmsTemplateService smsTemplateService; - - @EventListener - public void execute(SmsTemplateRefreshMessage message) { - log.info("[execute][收到 SmsTemplate 刷新消息]"); - smsTemplateService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/auth/OAuth2ClientRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/auth/OAuth2ClientRefreshMessage.java deleted file mode 100644 index c0f858d9b..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/auth/OAuth2ClientRefreshMessage.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.auth; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.springframework.cloud.bus.event.RemoteApplicationEvent; - -/** - * OAuth 2.0 客户端的数据刷新 Message - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class OAuth2ClientRefreshMessage extends RemoteApplicationEvent { - - public OAuth2ClientRefreshMessage() { - } - - public OAuth2ClientRefreshMessage(Object source, String originService, String destinationService) { - super(source, originService, DEFAULT_DESTINATION_FACTORY.getDestination(destinationService)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/dept/DeptRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/dept/DeptRefreshMessage.java deleted file mode 100644 index 5c6ab596f..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/dept/DeptRefreshMessage.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.dept; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.springframework.cloud.bus.event.RemoteApplicationEvent; - -/** - * 部门数据刷新 Message - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class DeptRefreshMessage extends RemoteApplicationEvent { - - public DeptRefreshMessage() { - } - - public DeptRefreshMessage(Object source, String originService, String destinationService) { - super(source, originService, DEFAULT_DESTINATION_FACTORY.getDestination(destinationService)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailAccountRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailAccountRefreshMessage.java deleted file mode 100644 index 9a0db2fcc..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailAccountRefreshMessage.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.mail; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.springframework.cloud.bus.event.RemoteApplicationEvent; - -/** - * 邮箱账号的数据刷新 Message - * - * @author wangjingyi - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class MailAccountRefreshMessage extends RemoteApplicationEvent { - - public MailAccountRefreshMessage() { - } - - public MailAccountRefreshMessage(Object source, String originService, String destinationService) { - super(source, originService, DEFAULT_DESTINATION_FACTORY.getDestination(destinationService)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailTemplateRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailTemplateRefreshMessage.java deleted file mode 100644 index 0dd0fa5bd..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailTemplateRefreshMessage.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.mail; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.springframework.cloud.bus.event.RemoteApplicationEvent; - -/** - * 邮箱模板的数据刷新 Message - * - * @author wangjingyi - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class MailTemplateRefreshMessage extends RemoteApplicationEvent { - - public MailTemplateRefreshMessage() { - } - - public MailTemplateRefreshMessage(Object source, String originService, String destinationService) { - super(source, originService, DEFAULT_DESTINATION_FACTORY.getDestination(destinationService)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/notify/NotifyTemplateRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/notify/NotifyTemplateRefreshMessage.java deleted file mode 100644 index e220708cb..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/notify/NotifyTemplateRefreshMessage.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.notify; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.springframework.cloud.bus.event.RemoteApplicationEvent; - -/** - * 站内信模板的数据刷新 Message - * - * @author xrcoder - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class NotifyTemplateRefreshMessage extends RemoteApplicationEvent { - - public NotifyTemplateRefreshMessage() { - } - - public NotifyTemplateRefreshMessage(Object source, String originService, String destinationService) { - super(source, originService, DEFAULT_DESTINATION_FACTORY.getDestination(destinationService)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/MenuRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/MenuRefreshMessage.java deleted file mode 100644 index c569107d9..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/MenuRefreshMessage.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.permission; - -import org.springframework.cloud.bus.event.RemoteApplicationEvent; - -/** - * 菜单数据刷新 Message - * - * @author 芋道源码 - */ -public class MenuRefreshMessage extends RemoteApplicationEvent { - - public MenuRefreshMessage() { - } - - public MenuRefreshMessage(Object source, String originService, String destinationService) { - super(source, originService, DEFAULT_DESTINATION_FACTORY.getDestination(destinationService)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/RoleMenuRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/RoleMenuRefreshMessage.java deleted file mode 100644 index 7b073b407..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/RoleMenuRefreshMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.permission; - -import lombok.Data; -import org.springframework.cloud.bus.event.RemoteApplicationEvent; - -/** - * 角色与菜单数据刷新 Message - * - * @author 芋道源码 - */ -@Data -public class RoleMenuRefreshMessage extends RemoteApplicationEvent { - - public RoleMenuRefreshMessage() { - } - - public RoleMenuRefreshMessage(Object source, String originService, String destinationService) { - super(source, originService, DEFAULT_DESTINATION_FACTORY.getDestination(destinationService)); - } - -} 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 fee07b6c0..000000000 --- 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 lombok.Data; -import org.springframework.cloud.bus.event.RemoteApplicationEvent; - -/** - * 角色数据刷新 Message - * - * @author 芋道源码 - */ -@Data -public class RoleRefreshMessage extends RemoteApplicationEvent { - - public RoleRefreshMessage() { - } - - public RoleRefreshMessage(Object source, String originService, String destinationService) { - super(source, originService, DEFAULT_DESTINATION_FACTORY.getDestination(destinationService)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/UserRoleRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/UserRoleRefreshMessage.java deleted file mode 100644 index bb94c8369..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/UserRoleRefreshMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.permission; - -import lombok.Data; -import org.springframework.cloud.bus.event.RemoteApplicationEvent; - -/** - * 用户与角色的数据刷新 Message - * - * @author 芋道源码 - */ -@Data -public class UserRoleRefreshMessage extends RemoteApplicationEvent { - - public UserRoleRefreshMessage() { - } - - public UserRoleRefreshMessage(Object source, String originService, String destinationService) { - super(source, originService, DEFAULT_DESTINATION_FACTORY.getDestination(destinationService)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/auth/OAuth2ClientProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/auth/OAuth2ClientProducer.java deleted file mode 100644 index 6d45d43e2..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/auth/OAuth2ClientProducer.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.auth; - -import cn.iocoder.yudao.framework.mq.core.bus.AbstractBusProducer; -import cn.iocoder.yudao.module.system.mq.message.auth.OAuth2ClientRefreshMessage; -import org.springframework.stereotype.Component; - -/** - * OAuth 2.0 客户端相关消息的 Producer - */ -@Component -public class OAuth2ClientProducer extends AbstractBusProducer { - - /** - * 发送 {@link OAuth2ClientRefreshMessage} 消息 - */ - public void sendOAuth2ClientRefreshMessage() { - publishEvent(new OAuth2ClientRefreshMessage(this, getBusId(), selfDestinationService())); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/dept/DeptProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/dept/DeptProducer.java deleted file mode 100644 index 4b7c1493e..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/dept/DeptProducer.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.dept; - -import cn.iocoder.yudao.framework.mq.core.bus.AbstractBusProducer; -import cn.iocoder.yudao.module.system.mq.message.dept.DeptRefreshMessage; -import org.springframework.stereotype.Component; - -/** - * Dept 部门相关消息的 Producer - */ -@Component -public class DeptProducer extends AbstractBusProducer { - - /** - * 发送 {@link DeptRefreshMessage} 消息 - */ - public void sendDeptRefreshMessage() { - publishEvent(new DeptRefreshMessage(this, getBusId(), selfDestinationService())); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java index 3916bcb27..78a661cbb 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java @@ -1,9 +1,7 @@ package cn.iocoder.yudao.module.system.mq.producer.mail; import cn.iocoder.yudao.framework.mq.core.bus.AbstractBusProducer; -import cn.iocoder.yudao.module.system.mq.message.mail.MailAccountRefreshMessage; import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; -import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.stream.function.StreamBridge; import org.springframework.stereotype.Component; @@ -23,20 +21,6 @@ public class MailProducer extends AbstractBusProducer { @Resource private StreamBridge streamBridge; - /** - * 发送 {@link MailTemplateRefreshMessage} 消息 - */ - public void sendMailTemplateRefreshMessage() { - publishEvent(new MailTemplateRefreshMessage(this, getBusId(), selfDestinationService())); - } - - /** - * 发送 {@link MailAccountRefreshMessage} 消息 - */ - public void sendMailAccountRefreshMessage() { - publishEvent(new MailAccountRefreshMessage(this, getBusId(), selfDestinationService())); - } - /** * 发送 {@link MailSendMessage} 消息 * diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/notify/NotifyProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/notify/NotifyProducer.java deleted file mode 100644 index c8c2f6f86..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/notify/NotifyProducer.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.notify; - -import cn.iocoder.yudao.framework.mq.core.bus.AbstractBusProducer; -import cn.iocoder.yudao.module.system.mq.message.notify.NotifyTemplateRefreshMessage; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * Notify 站内信相关消息的 Producer - * - * @author xrcoder - * @since 2022-08-06 - */ -@Slf4j -@Component -public class NotifyProducer extends AbstractBusProducer { - - /** - * 发送 {@link NotifyTemplateRefreshMessage} 消息 - */ - public void sendNotifyTemplateRefreshMessage() { - publishEvent(new NotifyTemplateRefreshMessage(this, getBusId(), selfDestinationService())); - } - - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/MenuProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/MenuProducer.java deleted file mode 100644 index 598a656b1..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/MenuProducer.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.permission; - -import cn.iocoder.yudao.framework.mq.core.bus.AbstractBusProducer; -import cn.iocoder.yudao.module.system.mq.message.permission.MenuRefreshMessage; -import org.springframework.stereotype.Component; - -/** - * Menu 菜单相关消息的 Producer - */ -@Component -public class MenuProducer extends AbstractBusProducer { - - /** - * 发送 {@link MenuRefreshMessage} 消息 - */ - public void sendMenuRefreshMessage() { - publishEvent(new MenuRefreshMessage(this, getBusId(), selfDestinationService())); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/PermissionProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/PermissionProducer.java deleted file mode 100644 index 5b726a968..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/PermissionProducer.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.permission; - -import cn.iocoder.yudao.framework.mq.core.bus.AbstractBusProducer; -import cn.iocoder.yudao.module.system.mq.message.permission.RoleMenuRefreshMessage; -import cn.iocoder.yudao.module.system.mq.message.permission.UserRoleRefreshMessage; -import org.springframework.stereotype.Component; - -/** - * Permission 权限相关消息的 Producer - */ -@Component -public class PermissionProducer extends AbstractBusProducer { - - /** - * 发送 {@link RoleMenuRefreshMessage} 消息 - */ - public void sendRoleMenuRefreshMessage() { - publishEvent(new RoleMenuRefreshMessage(this, getBusId(), selfDestinationService())); - } - - /** - * 发送 {@link UserRoleRefreshMessage} 消息 - */ - public void sendUserRoleRefreshMessage() { - publishEvent(new UserRoleRefreshMessage(this, getBusId(), selfDestinationService())); - } - -} 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 e157ef625..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/RoleProducer.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.permission; - -import cn.iocoder.yudao.framework.mq.core.bus.AbstractBusProducer; -import cn.iocoder.yudao.module.system.mq.message.permission.RoleRefreshMessage; -import org.springframework.stereotype.Component; - -/** - * Role 角色相关消息的 Producer - * - * @author 芋道源码 - */ -@Component -public class RoleProducer extends AbstractBusProducer { - - /** - * 发送 {@link RoleRefreshMessage} 消息 - */ - public void sendRoleRefreshMessage() { - publishEvent(new RoleRefreshMessage(this, getBusId(), selfDestinationService())); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sms/SmsProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sms/SmsProducer.java index 1242e2dcb..b7991870f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sms/SmsProducer.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sms/SmsProducer.java @@ -4,7 +4,6 @@ import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.mq.core.bus.AbstractBusProducer; import cn.iocoder.yudao.module.system.mq.message.sms.SmsChannelRefreshMessage; import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; -import cn.iocoder.yudao.module.system.mq.message.sms.SmsTemplateRefreshMessage; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.stream.function.StreamBridge; import org.springframework.stereotype.Component; @@ -32,13 +31,6 @@ public class SmsProducer extends AbstractBusProducer { publishEvent(new SmsChannelRefreshMessage(this, getBusId(), selfDestinationService())); } - /** - * 发送 {@link SmsTemplateRefreshMessage} 消息 - */ - public void sendSmsTemplateRefreshMessage() { - publishEvent(new SmsTemplateRefreshMessage(this, getBusId(), selfDestinationService())); - } - /** * 发送 {@link SmsSendMessage} 消息 * diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java index 87033d4b3..1029b424c 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java @@ -7,10 +7,7 @@ import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqV import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; /** * 部门 Service 接口 @@ -19,11 +16,6 @@ import java.util.Map; */ public interface DeptService { - /** - * 初始化部门的本地缓存 - */ - void initLocalCache(); - /** * 创建部门 * @@ -47,21 +39,12 @@ public interface DeptService { void deleteDept(Long id); /** - * 筛选部门列表 + * 获得部门信息 * - * @param reqVO 筛选条件请求 VO - * @return 部门列表 + * @param id 部门编号 + * @return 部门信息 */ - List getDeptList(DeptListReqVO reqVO); - - /** - * 获得所有子部门,从缓存中 - * - * @param parentId 部门编号 - * @param recursive 是否递归获取所有 - * @return 子部门列表 - */ - List getDeptListByParentIdFromCache(Long parentId, boolean recursive); + DeptDO getDept(Long id); /** * 获得部门信息数组 @@ -71,6 +54,14 @@ public interface DeptService { */ List getDeptList(Collection ids); + /** + * 筛选部门列表 + * + * @param reqVO 筛选条件请求 VO + * @return 部门列表 + */ + List getDeptList(DeptListReqVO reqVO); + /** * 获得指定编号的部门 Map * @@ -86,12 +77,20 @@ public interface DeptService { } /** - * 获得部门信息 + * 获得指定部门的所有子部门 * * @param id 部门编号 - * @return 部门信息 + * @return 子部门列表 */ - DeptDO getDept(Long id); + List getChildDeptList(Long id); + + /** + * 获得所有子部门,从缓存中 + * + * @param id 父部门编号 + * @return 子部门列表 + */ + Set getChildDeptIdListFromCache(Long id); /** * 校验部门们是否有效。如下情况,视为无效: diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java index 8dc52fdf8..d088242fd 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java @@ -1,30 +1,29 @@ package cn.iocoder.yudao.module.system.service.dept; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateReqVO; import cn.iocoder.yudao.module.system.convert.dept.DeptConvert; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; import cn.iocoder.yudao.module.system.enums.dept.DeptIdEnum; -import cn.iocoder.yudao.module.system.mq.producer.dept.DeptProducer; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Multimap; -import lombok.Getter; +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.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; /** @@ -37,55 +36,12 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @Slf4j public class DeptServiceImpl implements DeptService { - /** - * 部门缓存 - * key:部门编号 {@link DeptDO#getId()} - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter - private volatile Map deptCache; - /** - * 父部门缓存 - * key:部门编号 {@link DeptDO#getParentId()} - * value: 直接子部门列表 - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter - private volatile Multimap parentDeptCache; - @Resource private DeptMapper deptMapper; - @Resource - private DeptProducer deptProducer; - - /** - * 初始化 {@link #parentDeptCache} 和 {@link #deptCache} 缓存 - */ - @Override - @PostConstruct - public synchronized void initLocalCache() { - // 注意:忽略自动多租户,因为要全局初始化缓存 - TenantUtils.executeIgnore(() -> { - // 第一步:查询数据 - List depts = deptMapper.selectList(); - log.info("[initLocalCache][缓存部门,数量为:{}]", depts.size()); - - // 第二步:构建缓存 - ImmutableMap.Builder builder = ImmutableMap.builder(); - ImmutableMultimap.Builder parentBuilder = ImmutableMultimap.builder(); - depts.forEach(deptDO -> { - builder.put(deptDO.getId(), deptDO); - parentBuilder.put(deptDO.getParentId(), deptDO); - }); - deptCache = builder.build(); - parentDeptCache = parentBuilder.build(); - }); - } - @Override + @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 public Long createDept(DeptCreateReqVO reqVO) { // 校验正确性 if (reqVO.getParentId() == null) { @@ -95,12 +51,12 @@ public class DeptServiceImpl implements DeptService { // 插入部门 DeptDO dept = DeptConvert.INSTANCE.convert(reqVO); deptMapper.insert(dept); - // 发送刷新消息 - deptProducer.sendDeptRefreshMessage(); return dept.getId(); } @Override + @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 public void updateDept(DeptUpdateReqVO reqVO) { // 校验正确性 if (reqVO.getParentId() == null) { @@ -110,11 +66,11 @@ public class DeptServiceImpl implements DeptService { // 更新部门 DeptDO updateObj = DeptConvert.INSTANCE.convert(reqVO); deptMapper.updateById(updateObj); - // 发送刷新消息 - deptProducer.sendDeptRefreshMessage(); } @Override + @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 public void deleteDept(Long id) { // 校验是否存在 validateDeptExists(id); @@ -124,70 +80,30 @@ public class DeptServiceImpl implements DeptService { } // 删除部门 deptMapper.deleteById(id); - // 发送刷新消息 - deptProducer.sendDeptRefreshMessage(); - } - - @Override - public List getDeptList(DeptListReqVO reqVO) { - return deptMapper.selectList(reqVO); - } - - @Override - public List getDeptListByParentIdFromCache(Long parentId, boolean recursive) { - if (parentId == null) { - return Collections.emptyList(); - } - List result = new ArrayList<>(); - // 递归,简单粗暴 - getDeptsByParentIdFromCache(result, parentId, - recursive ? Integer.MAX_VALUE : 1, // 如果递归获取,则无限;否则,只递归 1 次 - parentDeptCache); - return result; - } - - /** - * 递归获取所有的子部门,添加到 result 结果 - * - * @param result 结果 - * @param parentId 父编号 - * @param recursiveCount 递归次数 - * @param parentDeptMap 父部门 Map,使用缓存,避免变化 - */ - private void getDeptsByParentIdFromCache(List result, Long parentId, int recursiveCount, - Multimap parentDeptMap) { - // 递归次数为 0,结束! - if (recursiveCount == 0) { - return; - } - - // 获得子部门 - Collection depts = parentDeptMap.get(parentId); - if (CollUtil.isEmpty(depts)) { - return; - } - // 针对多租户,过滤掉非当前租户的部门 - Long tenantId = TenantContextHolder.getTenantId(); - if (tenantId != null) { - depts = CollUtil.filterNew(depts, dept -> tenantId.equals(dept.getTenantId())); - } - result.addAll(depts); - - // 继续递归 - depts.forEach(dept -> getDeptsByParentIdFromCache(result, dept.getId(), - recursiveCount - 1, parentDeptMap)); } private void validateForCreateOrUpdate(Long id, Long parentId, String name) { // 校验自己存在 validateDeptExists(id); // 校验父部门的有效性 - validateParentDeptEnable(id, parentId); + validateParentDept(id, parentId); // 校验部门名的唯一性 validateDeptNameUnique(id, parentId, name); } - private void validateParentDeptEnable(Long id, Long parentId) { + @VisibleForTesting + void validateDeptExists(Long id) { + if (id == null) { + return; + } + DeptDO dept = deptMapper.selectById(id); + if (dept == null) { + throw exception(DEPT_NOT_FOUND); + } + } + + @VisibleForTesting + void validateParentDept(Long id, Long parentId) { if (parentId == null || DeptIdEnum.ROOT.getId().equals(parentId)) { return; } @@ -200,49 +116,71 @@ public class DeptServiceImpl implements DeptService { if (dept == null) { throw exception(DEPT_PARENT_NOT_EXITS); } - // 父部门被禁用 - if (!CommonStatusEnum.ENABLE.getStatus().equals(dept.getStatus())) { - throw exception(DEPT_NOT_ENABLE); - } // 父部门不能是原来的子部门 - List children = getDeptListByParentIdFromCache(id, true); + List children = getChildDeptList(id); if (children.stream().anyMatch(dept1 -> dept1.getId().equals(parentId))) { throw exception(DEPT_PARENT_IS_CHILD); } } - private void validateDeptExists(Long id) { - if (id == null) { - return; - } - DeptDO dept = deptMapper.selectById(id); + @VisibleForTesting + void validateDeptNameUnique(Long id, Long parentId, String name) { + DeptDO dept = deptMapper.selectByParentIdAndName(parentId, name); if (dept == null) { - throw exception(DEPT_NOT_FOUND); - } - } - - private void validateDeptNameUnique(Long id, Long parentId, String name) { - DeptDO menu = deptMapper.selectByParentIdAndName(parentId, name); - if (menu == null) { return; } // 如果 id 为空,说明不用比较是否为相同 id 的岗位 if (id == null) { throw exception(DEPT_NAME_DUPLICATE); } - if (!menu.getId().equals(id)) { + if (ObjectUtil.notEqual(dept.getId(), id)) { throw exception(DEPT_NAME_DUPLICATE); } } + @Override + public DeptDO getDept(Long id) { + return deptMapper.selectById(id); + } + @Override public List getDeptList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } return deptMapper.selectBatchIds(ids); } @Override - public DeptDO getDept(Long id) { - return deptMapper.selectById(id); + public List getDeptList(DeptListReqVO reqVO) { + return deptMapper.selectList(reqVO); + } + + @Override + public List getChildDeptList(Long id) { + List children = new LinkedList<>(); + // 遍历每一层 + Collection parentIds = Collections.singleton(id); + for (int i = 0; i < Short.MAX_VALUE; i++) { // 使用 Short.MAX_VALUE 避免 bug 场景下,存在死循环 + // 查询当前层,所有的子部门 + List depts = deptMapper.selectListByParentId(parentIds); + // 1. 如果没有子部门,则结束遍历 + if (CollUtil.isEmpty(depts)) { + break; + } + // 2. 如果有子部门,继续遍历 + children.addAll(depts); + parentIds = convertSet(depts, DeptDO::getId); + } + return children; + } + + @Override + @DataPermission(enable = false) // 禁用数据权限,避免简历不正确的缓存 + @Cacheable(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, key = "#id") + public Set getChildDeptIdListFromCache(Long id) { + List children = getChildDeptList(id); + return convertSet(children, DeptDO::getId); } @Override diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java index b0e02cb4b..62e97a829 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java @@ -17,19 +17,6 @@ import java.util.List; */ public interface MailAccountService { - /** - * 初始化邮箱账号的本地缓存 - */ - void initLocalCache(); - - /** - * 从缓存中获取邮箱账号 - * - * @param id 编号 - * @return 邮箱账号 - */ - MailAccountDO getMailAccountFromCache(Long id); - /** * 创建邮箱账号 * @@ -60,6 +47,14 @@ public interface MailAccountService { */ MailAccountDO getMailAccount(Long id); + /** + * 从缓存中获取邮箱账号 + * + * @param id 编号 + * @return 邮箱账号 + */ + MailAccountDO getMailAccountFromCache(Long id); + /** * 获取邮箱账号分页信息 * diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImpl.java index 4da029e47..457056f40 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImpl.java @@ -7,20 +7,18 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccou import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert; import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper; -import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer; -import lombok.Getter; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; import lombok.extern.slf4j.Slf4j; +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 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.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_NOT_EXISTS; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS; /** * 邮箱账号 Service 实现类 @@ -39,46 +37,16 @@ public class MailAccountServiceImpl implements MailAccountService { @Resource private MailTemplateService mailTemplateService; - @Resource - private MailProducer mailProducer; - - /** - * 邮箱账号缓存 - * key:邮箱账号编码 {@link MailAccountDO#getId()} - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter - private volatile Map mailAccountCache; - - @Override - @PostConstruct - public void initLocalCache() { - // 第一步:查询数据 - List accounts = mailAccountMapper.selectList(); - log.info("[initLocalCache][缓存邮箱账号,数量:{}]", accounts.size()); - - // 第二步:构建缓存 - mailAccountCache = convertMap(accounts, MailAccountDO::getId); - } - - @Override - public MailAccountDO getMailAccountFromCache(Long id) { - return mailAccountCache.get(id); - } - @Override public Long createMailAccount(MailAccountCreateReqVO createReqVO) { // 插入 MailAccountDO account = MailAccountConvert.INSTANCE.convert(createReqVO); mailAccountMapper.insert(account); - - // 发送刷新消息 - mailProducer.sendMailAccountRefreshMessage(); return account.getId(); } @Override + @Cacheable(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#updateReqVO.id") public void updateMailAccount(MailAccountUpdateReqVO updateReqVO) { // 校验是否存在 validateMailAccountExists(updateReqVO.getId()); @@ -86,11 +54,10 @@ public class MailAccountServiceImpl implements MailAccountService { // 更新 MailAccountDO updateObj = MailAccountConvert.INSTANCE.convert(updateReqVO); mailAccountMapper.updateById(updateObj); - // 发送刷新消息 - mailProducer.sendMailAccountRefreshMessage(); } @Override + @Cacheable(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#id") public void deleteMailAccount(Long id) { // 校验是否存在账号 validateMailAccountExists(id); @@ -101,8 +68,6 @@ public class MailAccountServiceImpl implements MailAccountService { // 删除 mailAccountMapper.deleteById(id); - // 发送刷新消息 - mailProducer.sendMailAccountRefreshMessage(); } private void validateMailAccountExists(Long id) { @@ -116,6 +81,12 @@ public class MailAccountServiceImpl implements MailAccountService { return mailAccountMapper.selectById(id); } + @Override + @Cacheable(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#id", unless = "#result == null") + public MailAccountDO getMailAccountFromCache(Long id) { + return getMailAccount(id); + } + @Override public PageResult getMailAccountPage(MailAccountPageReqVO pageReqVO) { return mailAccountMapper.selectPage(pageReqVO); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java index cb9dc61ef..69e02c810 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java @@ -18,11 +18,6 @@ import java.util.Map; */ public interface MailTemplateService { - /** - * 初始化邮件模版的本地缓存 - */ - void initLocalCache(); - /** * 邮件模版创建 * diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java index 62baa7e83..6d17c2b59 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java @@ -10,14 +10,14 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemp import cn.iocoder.yudao.module.system.convert.mail.MailTemplateConvert; import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper; -import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; 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.stereotype.Service; import org.springframework.validation.annotation.Validated; -import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.validation.Valid; import java.util.List; @@ -25,8 +25,8 @@ 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.framework.common.util.collection.CollectionUtils.convertMap; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_CODE_EXISTS; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS; /** * 邮箱模版 Service 实现类 @@ -47,29 +47,6 @@ public class MailTemplateServiceImpl implements MailTemplateService { @Resource private MailTemplateMapper mailTemplateMapper; - @Resource - private MailProducer mailProducer; - - /** - * 邮件模板缓存 - * key:邮件模版标识 {@link MailTemplateDO#getCode()} - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter - private volatile Map mailTemplateCache; - - @Override - @PostConstruct - public void initLocalCache() { - // 第一步:查询数据 - List templates = mailTemplateMapper.selectList(); - log.info("[initLocalCache][缓存邮件模版,数量:{}]", templates.size()); - - // 第二步:构建缓存 - mailTemplateCache = convertMap(templates, MailTemplateDO::getCode); - } - @Override public Long createMailTemplate(MailTemplateCreateReqVO createReqVO) { // 校验 code 是否唯一 @@ -79,12 +56,12 @@ public class MailTemplateServiceImpl implements MailTemplateService { MailTemplateDO template = MailTemplateConvert.INSTANCE.convert(createReqVO) .setParams(parseTemplateContentParams(createReqVO.getContent())); mailTemplateMapper.insert(template); - // 发送刷新消息 - mailProducer.sendMailTemplateRefreshMessage(); return template.getId(); } @Override + @CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, + allEntries = true) // allEntries 清空所有缓存,因为可能修改到 code 字段,不好清理 public void updateMailTemplate(@Valid MailTemplateUpdateReqVO updateReqVO) { // 校验是否存在 validateMailTemplateExists(updateReqVO.getId()); @@ -95,12 +72,10 @@ public class MailTemplateServiceImpl implements MailTemplateService { MailTemplateDO updateObj = MailTemplateConvert.INSTANCE.convert(updateReqVO) .setParams(parseTemplateContentParams(updateReqVO.getContent())); mailTemplateMapper.updateById(updateObj); - // 发送刷新消息 - mailProducer.sendMailTemplateRefreshMessage(); } @VisibleForTesting - public void validateCodeUnique(Long id, String code) { + void validateCodeUnique(Long id, String code) { MailTemplateDO template = mailTemplateMapper.selectByCode(code); if (template == null) { return; @@ -113,14 +88,14 @@ public class MailTemplateServiceImpl implements MailTemplateService { } @Override + @CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, + allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 code,不好清理 public void deleteMailTemplate(Long id) { // 校验是否存在 validateMailTemplateExists(id); // 删除 mailTemplateMapper.deleteById(id); - // 发送刷新消息 - mailProducer.sendMailTemplateRefreshMessage(); } private void validateMailTemplateExists(Long id) { @@ -132,6 +107,12 @@ public class MailTemplateServiceImpl implements MailTemplateService { @Override public MailTemplateDO getMailTemplate(Long id) {return mailTemplateMapper.selectById(id);} + @Override + @Cacheable(value = RedisKeyConstants.MAIL_TEMPLATE, key = "#code", unless = "#result == null") + public MailTemplateDO getMailTemplateByCodeFromCache(String code) { + return mailTemplateMapper.selectByCode(code); + } + @Override public PageResult getMailTemplatePage(MailTemplatePageReqVO pageReqVO) { return mailTemplateMapper.selectPage(pageReqVO); @@ -140,11 +121,6 @@ public class MailTemplateServiceImpl implements MailTemplateService { @Override public List getMailTemplateList() {return mailTemplateMapper.selectList();} - @Override - public MailTemplateDO getMailTemplateByCodeFromCache(String code) { - return mailTemplateCache.get(code); - } - @Override public String formatMailTemplateContent(String content, Map 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..2657e0dd9 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,19 +16,6 @@ import java.util.Map; */ public interface NotifyTemplateService { - /** - * 初始化站内信模板的本地缓存 - */ - void initLocalCache(); - - /** - * 获得站内信模板,从缓存中 - * - * @param code 模板编码 - * @return 站内信模板 - */ - NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code); - /** * 创建站内信模版 * @@ -59,6 +46,14 @@ public interface NotifyTemplateService { */ 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..c28e42d7c 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) { @@ -130,13 +97,20 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService { return notifyTemplateMapper.selectById(id); } + @Override + @Cacheable(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, key = "#code", + unless = "#result == null") + public NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code) { + return notifyTemplateMapper.selectByCode(code); + } + @Override public PageResult getNotifyTemplatePage(NotifyTemplatePageReqVO pageReqVO) { return notifyTemplateMapper.selectPage(pageReqVO); } @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..0f795495a 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) { @@ -131,16 +99,23 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService { return oauth2ClientMapper.selectById(id); } + @Override + @Cacheable(cacheNames = RedisKeyConstants.OAUTH_CLIENT, key = "#clientId", + unless = "#result == null") + public OAuth2ClientDO getOAuth2ClientFromCache(String clientId) { + return oauth2ClientMapper.selectByClientId(clientId); + } + @Override public PageResult getOAuth2ClientPage(OAuth2ClientPageReqVO pageReqVO) { return oauth2ClientMapper.selectPage(pageReqVO); } @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); } @@ -167,4 +142,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..e627ba1f4 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(); - /** * 创建菜单 * @@ -67,36 +62,12 @@ public interface MenuService { 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); /** * 获得菜单 @@ -106,4 +77,12 @@ public interface MenuService { */ 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 06e1355b0..a108e8f77 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,36 +1,29 @@ package cn.iocoder.yudao.module.system.service.permission; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuUpdateReqVO; import cn.iocoder.yudao.module.system.convert.permission.MenuConvert; import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; import cn.iocoder.yudao.module.system.dal.mysql.permission.MenuMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; -import cn.iocoder.yudao.module.system.mq.producer.permission.MenuProducer; import cn.iocoder.yudao.module.system.service.tenant.TenantService; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Multimap; -import lombok.Getter; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.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.*; @@ -43,26 +36,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 @@ -71,33 +44,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); @@ -108,13 +56,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) { @@ -129,34 +77,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 @@ -178,33 +117,10 @@ 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()); - } - - @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)); + @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 @@ -212,15 +128,20 @@ public class MenuServiceImpl implements MenuService { return menuMapper.selectById(id); } + @Override + public List getMenuList(Collection ids) { + return menuMapper.selectBatchIds(ids); + } + /** * 校验父菜单是否合法 - * + *

* 1. 不能设置自己为父菜单 * 2. 父菜单不存在 * 3. 父菜单必须是 {@link MenuTypeEnum#MENU} 菜单类型 * * @param parentId 父菜单编号 - * @param childId 当前菜单编号 + * @param childId 当前菜单编号 */ @VisibleForTesting void validateParentMenu(Long parentId, Long childId) { @@ -238,19 +159,19 @@ public class MenuServiceImpl implements MenuService { } // 父菜单必须是目录或者菜单类型 if (!MenuTypeEnum.DIR.getType().equals(menu.getType()) - && !MenuTypeEnum.MENU.getType().equals(menu.getType())) { + && !MenuTypeEnum.MENU.getType().equals(menu.getType())) { throw exception(MENU_PARENT_NOT_DIR_OR_MENU); } } /** * 校验菜单是否合法 - * + *

* 1. 校验相同父菜单编号下,是否存在相同的菜单名 * - * @param name 菜单名字 + * @param name 菜单名字 * @param parentId 父菜单编号 - * @param id 菜单编号 + * @param id 菜单编号 */ @VisibleForTesting void validateMenu(Long parentId, String name, Long id) { @@ -269,7 +190,7 @@ public class MenuServiceImpl implements MenuService { /** * 初始化菜单的通用属性。 - * + *

* 例如说,只有目录或者菜单类型的菜单,才设置 icon * * @param menu 菜单 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..efc471431 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,16 +1,15 @@ 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 接口 - * + *

* 提供用户-角色、角色-菜单、角色-部门的关联权限处理 * * @author 芋道源码 @@ -18,81 +17,32 @@ import java.util.Set; public interface PermissionService { /** - * 初始化权限的本地缓存 + * 判断是否有权限,任一一个即可 + * + * @param userId 用户编号 + * @param permissions 权限 + * @return 是否 */ - void initLocalCache(); + boolean hasAnyPermissions(Long userId, String... permissions); /** - * 获得角色们拥有的菜单列表,从缓存中获取 + * 判断是否有角色,任一一个即可 * - * 任一参数为空时,则返回为空 - * - * @param roleIds 角色编号数组 - * @param menuTypes 菜单类型数组 - * @param menusStatuses 菜单状态数组 - * @return 菜单列表 + * @param roles 角色数组 + * @return 是否 */ - List getRoleMenuListFromCache(Collection roleIds, Collection menuTypes, - Collection menusStatuses); + boolean hasAnyRoles(Long userId, String... roles); - /** - * 获得用户拥有的角色编号集合,从缓存中获取 - * - * @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 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); - /** * 处理角色删除时,删除关联授权数据 * @@ -108,28 +58,82 @@ public interface PermissionService { 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 userId 用户编号 - * @param permissions 权限 - * @return 是否 + * @param roleIds 角色编号集合 + * @return 用户编号集合 */ - boolean hasAnyPermissions(Long userId, String... permissions); + Set getUserRoleIdListByRoleId(Collection roleIds); /** - * 判断是否有角色,任一一个即可 + * 获得用户拥有的角色编号集合 * - * @param roles 角色数组 - * @return 是否 + * @param userId 用户编号 + * @return 角色编号集合 */ - boolean hasAnyRoles(Long userId, String... roles); + 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); /** * 获得登陆用户的部门数据权限 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..81bfcfc1f 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 dbMenuIds = convertSet(roleMenuMapper.selectListByRoleId(roleId), RoleMenuDO::getMenuId); // 计算新增和删除的菜单编号 Collection createMenuIds = CollUtil.subtract(menuIds, dbMenuIds); Collection deleteMenuIds = CollUtil.subtract(dbMenuIds, menuIds); // 执行新增和删除。对于已经授权的菜单,不用做任何处理 - if (!CollectionUtil.isEmpty(createMenuIds)) { + if (CollUtil.isNotEmpty(createMenuIds)) { roleMenuMapper.insertBatch(CollectionUtils.convertList(createMenuIds, menuId -> { RoleMenuDO entity = new RoleMenuDO(); entity.setRoleId(roleId); @@ -214,34 +149,57 @@ public class PermissionServiceImpl implements PermissionService { return entity; })); } - if (!CollectionUtil.isEmpty(deleteMenuIds)) { + if (CollUtil.isNotEmpty(deleteMenuIds)) { roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds); } - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - @Override - public void afterCommit() { - permissionProducer.sendRoleMenuRefreshMessage(); - } - - }); - } - - @Override - public Set getUserRoleIdListByUserId(Long userId) { - return convertSet(userRoleMapper.selectListByUserId(userId), - UserRoleDO::getRoleId); - } - - @Override - public Set getUserRoleIdListByRoleIds(Collection roleIds) { - return convertSet(userRoleMapper.selectListByRoleIds(roleIds), - UserRoleDO::getUserId); } @Override @Transactional(rollbackFor = Exception.class) + @Caching(evict = { + @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST, + allEntries = true), // allEntries 清空所有缓存,此处无法方便获得 roleId 对应的 menu 缓存们 + @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,此处无法方便获得 roleId 对应的 user 缓存们 + }) + public void processRoleDeleted(Long roleId) { + // 标记删除 UserRole + userRoleMapper.deleteListByRoleId(roleId); + // 标记删除 RoleMenu + roleMenuMapper.deleteListByRoleId(roleId); + } + + @Override + @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST, key = "#menuId") + public void processMenuDeleted(Long menuId) { + roleMenuMapper.deleteListByMenuId(menuId); + } + + @Override + public Set getRoleMenuListByRoleId(Collection roleIds) { + if (CollUtil.isEmpty(roleIds)) { + return Collections.emptySet(); + } + + // 如果是管理员的情况下,获取全部菜单编号 + if (roleService.hasAnySuperAdmin(roleIds)) { + return convertSet(menuService.getMenuList(), MenuDO::getId); + } + // 如果是非管理员的情况下,获得拥有的菜单编号 + return convertSet(roleMenuMapper.selectListByRoleId(roleIds), RoleMenuDO::getMenuId); + } + + @Override + @Cacheable(value = RedisKeyConstants.MENU_ROLE_ID_LIST, key = "#menuId") + public Set getMenuRoleIdListByMenuIdFromCache(Long menuId) { + return convertSet(roleMenuMapper.selectListByMenuId(menuId), RoleMenuDO::getRoleId); + } + + // ========== 用户-角色的相关方法 ========== + + @Override + @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 + @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId") public void assignUserRole(Long userId, Set roleIds) { // 获得角色拥有角色编号 Set dbRoleIds = convertSet(userRoleMapper.selectListByUserId(userId), @@ -261,137 +219,68 @@ public class PermissionServiceImpl implements PermissionService { if (!CollectionUtil.isEmpty(deleteMenuIds)) { userRoleMapper.deleteListByUserIdAndRoleIdIds(userId, deleteMenuIds); } - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - @Override - public void afterCommit() { - permissionProducer.sendUserRoleRefreshMessage(); - } - - }); } + @Override + @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId") + public void processUserDeleted(Long userId) { + userRoleMapper.deleteListByUserId(userId); + } + + @Override + public Set getUserRoleIdListByUserId(Long userId) { + return convertSet(userRoleMapper.selectListByUserId(userId), UserRoleDO::getRoleId); + } + + @Override + @Cacheable(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId") + public Set getUserRoleIdListByUserIdFromCache(Long userId) { + return getUserRoleIdListByUserId(userId); + } + + @Override + public Set getUserRoleIdListByRoleId(Collection roleIds) { + return convertSet(userRoleMapper.selectListByRoleIds(roleIds), UserRoleDO::getUserId); + } + + /** + * 获得用户拥有的角色,并且这些角色是开启状态的 + * + * @param userId 用户编号 + * @return 用户拥有的角色 + */ + @VisibleForTesting + List getEnableUserRoleListByUserIdFromCache(Long userId) { + // 获得用户拥有的角色编号 + Set roleIds = getSelf().getUserRoleIdListByUserIdFromCache(userId); + // 获得角色数组,并移除被禁用的 + List roles = roleService.getRoleListFromCache(roleIds); + roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); + return roles; + } + + // ========== 用户-部门的相关方法 ========== + @Override public void assignRoleDataScope(Long roleId, Integer dataScope, Set dataScopeDeptIds) { roleService.updateRoleDataScope(roleId, dataScope, dataScopeDeptIds); } - @Override - @Transactional(rollbackFor = Exception.class) - public void processRoleDeleted(Long roleId) { - // 标记删除 UserRole - userRoleMapper.deleteListByRoleId(roleId); - // 标记删除 RoleMenu - roleMenuMapper.deleteListByRoleId(roleId); - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - @Override - public void afterCommit() { - permissionProducer.sendRoleMenuRefreshMessage(); - permissionProducer.sendUserRoleRefreshMessage(); - } - - }); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void processMenuDeleted(Long menuId) { - roleMenuMapper.deleteListByMenuId(menuId); - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - @Override - public void afterCommit() { - permissionProducer.sendRoleMenuRefreshMessage(); - } - - }); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void processUserDeleted(Long userId) { - userRoleMapper.deleteListByUserId(userId); - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - @Override - public void afterCommit() { - permissionProducer.sendUserRoleRefreshMessage(); - } - - }); - } - - @Override - public boolean hasAnyPermissions(Long userId, String... permissions) { - // 如果为空,说明已经有权限 - if (ArrayUtil.isEmpty(permissions)) { - return true; - } - - // 获得当前登录的角色。如果为空,说明没有权限 - Set roleIds = getUserRoleIdsFromCache(userId, singleton(CommonStatusEnum.ENABLE.getStatus())); - if (CollUtil.isEmpty(roleIds)) { - return false; - } - // 判断是否是超管。如果是,当然符合条件 - if (roleService.hasAnySuperAdmin(roleIds)) { - return true; - } - - // 遍历权限,判断是否有一个满足 - return Arrays.stream(permissions).anyMatch(permission -> { - List menuList = menuService.getMenuListByPermissionFromCache(permission); - // 采用严格模式,如果权限找不到对应的 Menu 的话,认为 - if (CollUtil.isEmpty(menuList)) { - return false; - } - // 获得是否拥有该权限,任一一个 - return menuList.stream().anyMatch(menu -> CollUtil.containsAny(roleIds, - menuRoleCache.get(menu.getId()))); - }); - } - - @Override - public boolean hasAnyRoles(Long userId, String... roles) { - // 如果为空,说明已经有权限 - if (ArrayUtil.isEmpty(roles)) { - return true; - } - - // 获得当前登录的角色。如果为空,说明没有权限 - Set roleIds = getUserRoleIdsFromCache(userId, singleton(CommonStatusEnum.ENABLE.getStatus())); - if (CollUtil.isEmpty(roleIds)) { - return false; - } - // 判断是否是超管。如果是,当然符合条件 - if (roleService.hasAnySuperAdmin(roleIds)) { - return true; - } - Set userRoles = convertSet(roleService.getRoleListFromCache(roleIds), - RoleDO::getCode); - return CollUtil.containsAny(userRoles, Sets.newHashSet(roles)); - } - @Override @DataPermission(enable = false) // 关闭数据权限,不然就会出现递归获取数据权限的问题 - @TenantIgnore // 忽略多租户的自动过滤。如果不忽略,会导致添加租户时,因为切换租户,导致获取不到 User。即使忽略,本身该方法不存在跨租户的操作,不会存在问题。 public DeptDataPermissionRespDTO getDeptDataPermission(Long userId) { // 获得用户的角色 - Set roleIds = getUserRoleIdsFromCache(userId, singleton(CommonStatusEnum.ENABLE.getStatus())); + List roles = getEnableUserRoleListByUserIdFromCache(userId); + // 如果角色为空,则只能查看自己 DeptDataPermissionRespDTO result = new DeptDataPermissionRespDTO(); - if (CollUtil.isEmpty(roleIds)) { + if (CollUtil.isEmpty(roles)) { result.setSelf(true); return result; } - List roles = roleService.getRoleListFromCache(roleIds); // 获得用户的部门编号的缓存,通过 Guava 的 Suppliers 惰性求值,即有且仅有第一次发起 DB 的查询 - Supplier userDeptIdCache = Suppliers.memoize(() -> userService.getUser(userId).getDeptId()); + Supplier userDeptId = Suppliers.memoize(() -> userService.getUser(userId).getDeptId()); // 遍历每个角色,计算 for (RoleDO role : roles) { // 为空时,跳过 @@ -408,20 +297,19 @@ public class PermissionServiceImpl implements PermissionService { CollUtil.addAll(result.getDeptIds(), role.getDataScopeDeptIds()); // 自定义可见部门时,保证可以看到自己所在的部门。否则,一些场景下可能会有问题。 // 例如说,登录时,基于 t_user 的 username 查询会可能被 dept_id 过滤掉 - CollUtil.addAll(result.getDeptIds(), userDeptIdCache.get()); + CollUtil.addAll(result.getDeptIds(), userDeptId.get()); continue; } // 情况三,DEPT_ONLY if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_ONLY.getScope())) { - CollectionUtils.addIfNotNull(result.getDeptIds(), userDeptIdCache.get()); + CollectionUtils.addIfNotNull(result.getDeptIds(), userDeptId.get()); continue; } // 情况四,DEPT_DEPT_AND_CHILD if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_AND_CHILD.getScope())) { - List depts = deptService.getDeptListByParentIdFromCache(userDeptIdCache.get(), true); - CollUtil.addAll(result.getDeptIds(), CollectionUtils.convertList(depts, DeptDO::getId)); + CollUtil.addAll(result.getDeptIds(), deptService.getChildDeptIdListFromCache(userDeptId.get())); // 添加本身部门编号 - CollUtil.addAll(result.getDeptIds(), userDeptIdCache.get()); + CollUtil.addAll(result.getDeptIds(), userDeptId.get()); continue; } // 情况五,SELF @@ -430,9 +318,18 @@ public class PermissionServiceImpl implements PermissionService { continue; } // 未知情况,error log 即可 - log.error("[getDeptDataPermission][LoginUser({}) role({}) 无法处理]", userId, JsonUtils.toJsonString(result)); + log.error("[getDeptDataPermission][LoginUser({}) role({}) 无法处理]", userId, toJsonString(result)); } return result; } + /** + * 获得自身的代理对象,解决 AOP 生效问题 + * + * @return 自己 + */ + private PermissionServiceImpl 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/RoleService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java index 70acbdcb7..d837d1e8b 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,30 +92,19 @@ public interface RoleService { List getRoleListFromCache(Collection ids); /** - * 判断角色数组中,是否有超级管理员 + * 获得角色列表 * - * @param roleList 角色数组 - * @return 是否有管理员 + * @param statuses 筛选的状态 + * @return 角色列表 */ - boolean hasAnySuperAdmin(Collection roleList); + List getRoleListByStatus(Collection statuses); /** - * 判断角色编号数组中,是否有管理员 + * 获得所有角色列表 * - * @param ids 角色编号数组 - * @return 是否有管理员 + * @return 角色列表 */ - default boolean hasAnySuperAdmin(Set ids) { - return hasAnySuperAdmin(getRoleListFromCache(ids)); - } - - /** - * 获得角色 - * - * @param id 角色编号 - * @return 角色 - */ - RoleDO getRole(Long id); + List getRoleList(); /** * 获得角色分页 @@ -131,6 +122,14 @@ public interface RoleService { */ List getRoleList(RoleExportReqVO reqVO); + /** + * 判断角色编号数组中,是否有管理员 + * + * @param ids 角色编号数组 + * @return 是否有管理员 + */ + boolean hasAnySuperAdmin(Collection ids); + /** * 校验角色们是否有效。如下情况,视为无效: * 1. 角色编号不存在 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 30ac2daa0..c8ae9f343 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,23 @@ 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.lang.Nullable; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; 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,43 +42,14 @@ 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 + @Transactional(rollbackFor = Exception.class) public Long createRole(RoleCreateReqVO reqVO, Integer type) { // 校验角色 validateRoleDuplicate(reqVO.getName(), reqVO.getCode(), null); @@ -91,18 +59,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 +74,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 +85,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 +99,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,60 +111,6 @@ 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 getRoleListFromCache(Collection ids) { - if (CollectionUtil.isEmpty(ids)) { - return Collections.emptyList(); - } - return roleCache.values().stream().filter(roleDO -> ids.contains(roleDO.getId())) - .collect(Collectors.toList()); - } - - @Override - public boolean hasAnySuperAdmin(Collection roleList) { - if (CollectionUtil.isEmpty(roleList)) { - return false; - } - return roleList.stream().anyMatch(role -> RoleCodeEnum.isSuperAdmin(role.getCode())); - } - - @Override - public RoleDO getRole(Long id) { - return roleMapper.selectById(id); - } - - @Override - public PageResult getRolePage(RolePageReqVO reqVO) { - return roleMapper.selectPage(reqVO); - } - - @Override - public List getRoleList(RoleExportReqVO reqVO) { - return roleMapper.selectList(reqVO); } /** @@ -257,6 +162,69 @@ public class RoleServiceImpl implements RoleService { } } + @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 getRoleListByStatus(Collection statuses) { + return roleMapper.selectListByStatus(statuses); + } + + @Override + public List getRoleList() { + return roleMapper.selectList(); + } + + @Override + public List getRoleList(Collection ids) { + if (CollectionUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return roleMapper.selectBatchIds(ids); + } + + @Override + public List getRoleListFromCache(Collection ids) { + if (CollectionUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + // 这里采用 for 循环从缓存中获取,主要考虑 Spring CacheManager 无法批量操作的问题 + RoleServiceImpl self = getSelf(); + return convertList(ids, self::getRoleFromCache); + } + + @Override + public PageResult getRolePage(RolePageReqVO reqVO) { + return roleMapper.selectPage(reqVO); + } + + @Override + public List getRoleList(RoleExportReqVO reqVO) { + return roleMapper.selectList(reqVO); + } + + @Override + public boolean hasAnySuperAdmin(Collection ids) { + if (CollectionUtil.isEmpty(ids)) { + return false; + } + RoleServiceImpl self = getSelf(); + return ids.stream().anyMatch(id -> { + RoleDO role = self.getRoleFromCache(id); + return role != null && RoleCodeEnum.isSuperAdmin(role.getCode()); + }); + } + @Override public void validateRoleList(Collection ids) { if (CollUtil.isEmpty(ids)) { @@ -276,4 +244,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/sms/SmsTemplateService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java index 997586c73..f3f886f55 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java @@ -8,7 +8,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; import cn.iocoder.yudao.framework.common.pojo.PageResult; import javax.validation.Valid; -import java.util.Collection; import java.util.List; import java.util.Map; @@ -16,40 +15,10 @@ import java.util.Map; * 短信模板 Service 接口 * * @author zzf - * @date 2021/1/25 9:24 + * @since 2021/1/25 9:24 */ public interface SmsTemplateService { - /** - * 初始化短信模板的本地缓存 - */ - void initLocalCache(); - - /** - * 获得短信模板,从缓存中 - * - * @param code 模板编码 - * @return 短信模板 - */ - SmsTemplateDO getSmsTemplateByCodeFromCache(String code); - - /** - * 格式化短信内容 - * - * @param content 短信模板的内容 - * @param params 内容的参数 - * @return 格式化后的内容 - */ - String formatSmsTemplateContent(String content, Map params); - - /** - * 获得短信模板 - * - * @param code 模板编码 - * @return 短信模板 - */ - SmsTemplateDO getSmsTemplateByCode(String code); - /** * 创建短信模板 * @@ -81,12 +50,12 @@ public interface SmsTemplateService { SmsTemplateDO getSmsTemplate(Long id); /** - * 获得短信模板列表 + * 获得短信模板,从缓存中 * - * @param ids 编号 - * @return 短信模板列表 + * @param code 模板编码 + * @return 短信模板 */ - List getSmsTemplateList(Collection ids); + SmsTemplateDO getSmsTemplateByCodeFromCache(String code); /** * 获得短信模板分页 @@ -112,4 +81,14 @@ public interface SmsTemplateService { */ Long countByChannelId(Long channelId); + + /** + * 格式化短信内容 + * + * @param content 短信模板的内容 + * @param params 内容的参数 + * @return 格式化后的内容 + */ + String formatSmsTemplateContent(String content, Map params); + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java index 20c4ec3f3..0f276a915 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java @@ -4,7 +4,6 @@ import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.sms.core.client.SmsClient; import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory; import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; @@ -17,16 +16,15 @@ import cn.iocoder.yudao.module.system.convert.sms.SmsTemplateConvert; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper; -import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; 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.stereotype.Service; import org.springframework.util.Assert; -import javax.annotation.PostConstruct; import javax.annotation.Resource; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; @@ -59,49 +57,6 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { @Resource private SmsClientFactory smsClientFactory; - @Resource - private SmsProducer smsProducer; - - /** - * 短信模板缓存 - * key:短信模板编码 {@link SmsTemplateDO#getCode()} - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter // 为了方便测试,这里提供 getter 方法 - private volatile Map smsTemplateCache; - - @Override - @PostConstruct - public void initLocalCache() { - // 第一步:查询数据 - List smsTemplateList = smsTemplateMapper.selectList(); - log.info("[initLocalCache][缓存短信模版,数量为:{}]", smsTemplateList.size()); - - // 第二步:构建缓存 - smsTemplateCache = CollectionUtils.convertMap(smsTemplateList, SmsTemplateDO::getCode); - } - - @Override - public SmsTemplateDO getSmsTemplateByCodeFromCache(String code) { - return smsTemplateCache.get(code); - } - - @Override - public String formatSmsTemplateContent(String content, Map params) { - return StrUtil.format(content, params); - } - - @Override - public SmsTemplateDO getSmsTemplateByCode(String code) { - return smsTemplateMapper.selectByCode(code); - } - - @VisibleForTesting - public List parseTemplateContentParams(String content) { - return ReUtil.findAllGroup1(PATTERN_PARAMS, content); - } - @Override public Long createSmsTemplate(SmsTemplateCreateReqVO createReqVO) { // 校验短信渠道 @@ -116,13 +71,13 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { template.setParams(parseTemplateContentParams(template.getContent())); template.setChannelCode(channelDO.getCode()); smsTemplateMapper.insert(template); - // 发送刷新消息 - smsProducer.sendSmsTemplateRefreshMessage(); // 返回 return template.getId(); } @Override + @CacheEvict(cacheNames = RedisKeyConstants.SMS_TEMPLATE, + allEntries = true) // allEntries 清空所有缓存,因为可能修改到 code 字段,不好清理 public void updateSmsTemplate(SmsTemplateUpdateReqVO updateReqVO) { // 校验存在 validateSmsTemplateExists(updateReqVO.getId()); @@ -138,18 +93,16 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { updateObj.setParams(parseTemplateContentParams(updateObj.getContent())); updateObj.setChannelCode(channelDO.getCode()); smsTemplateMapper.updateById(updateObj); - // 发送刷新消息 - smsProducer.sendSmsTemplateRefreshMessage(); } @Override + @CacheEvict(cacheNames = RedisKeyConstants.SMS_TEMPLATE, + allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 code,不好清理 public void deleteSmsTemplate(Long id) { // 校验存在 validateSmsTemplateExists(id); // 更新 smsTemplateMapper.deleteById(id); - // 发送刷新消息 - smsProducer.sendSmsTemplateRefreshMessage(); } private void validateSmsTemplateExists(Long id) { @@ -164,8 +117,10 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { } @Override - public List getSmsTemplateList(Collection ids) { - return smsTemplateMapper.selectBatchIds(ids); + @Cacheable(cacheNames = RedisKeyConstants.SMS_TEMPLATE, key = "#code", + unless = "#result == null") + public SmsTemplateDO getSmsTemplateByCodeFromCache(String code) { + return smsTemplateMapper.selectByCode(code); } @Override @@ -217,7 +172,7 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { * @param apiTemplateId API 模板编号 */ @VisibleForTesting - public void validateApiTemplate(Long channelId, String apiTemplateId) { + void validateApiTemplate(Long channelId, String apiTemplateId) { // 获得短信模板 SmsClient smsClient = smsClientFactory.getSmsClient(channelId); Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", channelId)); @@ -226,4 +181,14 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { templateResult.checkError(); } + @Override + public String formatSmsTemplateContent(String content, Map params) { + return StrUtil.format(content, params); + } + + @VisibleForTesting + List parseTemplateContentParams(String content) { + return ReUtil.findAllGroup1(PATTERN_PARAMS, content); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java index 4e6e60198..8a65ab1f0 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java @@ -10,9 +10,9 @@ import cn.iocoder.yudao.module.system.convert.tenant.TenantPackageConvert; import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantPackageMapper; +import com.baomidou.dynamic.datasource.annotation.DSTransactional; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; @@ -47,7 +47,7 @@ public class TenantPackageServiceImpl implements TenantPackageService { } @Override - @Transactional(rollbackFor = Exception.class) + @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 public void updateTenantPackage(TenantPackageUpdateReqVO updateReqVO) { // 校验存在 TenantPackageDO tenantPackage = validateTenantPackageExists(updateReqVO.getId()); 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 1664ff6e3..226c939e4 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 @@ -29,6 +29,7 @@ import cn.iocoder.yudao.module.system.service.permission.RoleService; import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler; import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler; import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import com.baomidou.dynamic.datasource.annotation.DSTransactional; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; @@ -95,7 +96,7 @@ public class TenantServiceImpl implements TenantService { } @Override - @Transactional(rollbackFor = Exception.class) + @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 public Long createTenant(TenantCreateReqVO createReqVO) { // 校验租户名称是否重复 validTenantNameDuplicate(createReqVO.getName(), null); @@ -137,7 +138,7 @@ public class TenantServiceImpl implements TenantService { } @Override - @Transactional(rollbackFor = Exception.class) + @DSTransactional public void updateTenant(TenantUpdateReqVO updateReqVO) { // 校验存在 TenantDO tenant = validateUpdateTenant(updateReqVO.getId()); @@ -170,7 +171,7 @@ public class TenantServiceImpl implements TenantService { } @Override - @Transactional(rollbackFor = Exception.class) + @DSTransactional public void updateTenantRoleMenu(Long tenantId, Set menuIds) { TenantUtils.execute(tenantId, () -> { // 获得所有角色 @@ -186,7 +187,7 @@ public class TenantServiceImpl implements TenantService { return; } // 如果是其他角色,则去掉超过套餐的权限 - Set roleMenuIds = permissionService.getRoleMenuIds(role.getId()); + Set roleMenuIds = permissionService.getRoleMenuListByRoleId(role.getId()); roleMenuIds = CollUtil.intersectionDistinct(roleMenuIds, menuIds); permissionService.assignRoleMenu(role.getId(), roleMenuIds); log.info("[updateTenantRoleMenu][角色({}/{}) 的权限修改为({})]", role.getId(), role.getTenantId(), roleMenuIds); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java index 74948bbf0..dbfc02ed3 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java @@ -290,8 +290,7 @@ public class AdminUserServiceImpl implements AdminUserService { if (deptId == null) { return Collections.emptySet(); } - Set deptIds = convertSet(deptService.getDeptListByParentIdFromCache( - deptId, true), DeptDO::getId); + Set deptIds = convertSet(deptService.getChildDeptList(deptId), DeptDO::getId); deptIds.add(deptId); // 包括自身 return deptIds; } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImplTest.java index 3771fc33e..cf9dc5a35 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImplTest.java @@ -1,9 +1,7 @@ package cn.iocoder.yudao.module.system.service.dept; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; @@ -11,27 +9,20 @@ import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateRe import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper; import cn.iocoder.yudao.module.system.enums.dept.DeptIdEnum; -import cn.iocoder.yudao.module.system.mq.producer.dept.DeptProducer; -import com.google.common.collect.Multimap; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; import java.util.Arrays; import java.util.List; -import java.util.Map; -import java.util.function.Consumer; +import java.util.Set; -import static cn.hutool.core.util.RandomUtil.randomEle; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.verify; /** * {@link DeptServiceImpl} 的单元测试类 @@ -45,39 +36,163 @@ public class DeptServiceImplTest extends BaseDbUnitTest { private DeptServiceImpl deptService; @Resource private DeptMapper deptMapper; - @MockBean - private DeptProducer deptProducer; - - @BeforeEach - public void setUp() { - // 清理租户上下文 - TenantContextHolder.clear(); - } @Test - public void testInitLocalCache() { - // mock 数据 - DeptDO deptDO1 = randomDeptDO(); - deptMapper.insert(deptDO1); - DeptDO deptDO2 = randomDeptDO(); - deptMapper.insert(deptDO2); + public void testCreateDept() { + // 准备参数 + DeptCreateReqVO reqVO = randomPojo(DeptCreateReqVO.class, o -> { + o.setParentId(DeptIdEnum.ROOT.getId()); + o.setStatus(randomCommonStatus()); + }); // 调用 - deptService.initLocalCache(); - // 断言 deptCache 缓存 - Map deptCache = deptService.getDeptCache(); - assertEquals(2, deptCache.size()); - assertPojoEquals(deptDO1, deptCache.get(deptDO1.getId())); - assertPojoEquals(deptDO2, deptCache.get(deptDO2.getId())); - // 断言 parentDeptCache 缓存 - Multimap parentDeptCache = deptService.getParentDeptCache(); - assertEquals(2, parentDeptCache.size()); - assertPojoEquals(deptDO1, parentDeptCache.get(deptDO1.getParentId())); - assertPojoEquals(deptDO2, parentDeptCache.get(deptDO2.getParentId())); + Long deptId = deptService.createDept(reqVO); + // 断言 + assertNotNull(deptId); + // 校验记录的属性是否正确 + DeptDO deptDO = deptMapper.selectById(deptId); + assertPojoEquals(reqVO, deptDO); } @Test - public void testListDepts() { + public void testUpdateDept() { + // mock 数据 + DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus())); + deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 + // 准备参数 + DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class, o -> { + // 设置更新的 ID + o.setParentId(DeptIdEnum.ROOT.getId()); + o.setId(dbDeptDO.getId()); + o.setStatus(randomCommonStatus()); + }); + + // 调用 + deptService.updateDept(reqVO); + // 校验是否更新正确 + DeptDO deptDO = deptMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, deptDO); + } + + @Test + public void testDeleteDept_success() { + // mock 数据 + DeptDO dbDeptDO = randomPojo(DeptDO.class); + deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbDeptDO.getId(); + + // 调用 + deptService.deleteDept(id); + // 校验数据不存在了 + assertNull(deptMapper.selectById(id)); + } + + @Test + public void testDeleteDept_exitsChildren() { + // mock 数据 + DeptDO parentDept = randomPojo(DeptDO.class); + deptMapper.insert(parentDept);// @Sql: 先插入出一条存在的数据 + // 准备参数 + DeptDO childrenDeptDO = randomPojo(DeptDO.class, o -> { + o.setParentId(parentDept.getId()); + o.setStatus(randomCommonStatus()); + }); + // 插入子部门 + deptMapper.insert(childrenDeptDO); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.deleteDept(parentDept.getId()), DEPT_EXITS_CHILDREN); + } + + @Test + public void testValidateDeptExists_notFound() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.validateDeptExists(id), DEPT_NOT_FOUND); + } + + @Test + public void testValidateParentDept_parentError() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.validateParentDept(id, id), + DEPT_PARENT_ERROR); + } + + @Test + public void testValidateParentDept_parentIsChild() { + // mock 数据(父节点) + DeptDO parentDept = randomPojo(DeptDO.class); + deptMapper.insert(parentDept); + // mock 数据(子节点) + DeptDO childDept = randomPojo(DeptDO.class, o -> { + o.setParentId(parentDept.getId()); + }); + deptMapper.insert(childDept); + + // 准备参数 + Long id = parentDept.getId(); + Long parentId = childDept.getId(); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.validateParentDept(id, parentId), DEPT_PARENT_IS_CHILD); + } + + @Test + public void testValidateNameUnique_duplicate() { + // mock 数据 + DeptDO deptDO = randomPojo(DeptDO.class); + deptMapper.insert(deptDO); + + // 准备参数 + Long id = randomLongId(); + Long parentId = deptDO.getParentId(); + String name = deptDO.getName(); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.validateDeptNameUnique(id, parentId, name), + DEPT_NAME_DUPLICATE); + } + + @Test + public void testGetDept() { + // mock 数据 + DeptDO deptDO = randomPojo(DeptDO.class); + deptMapper.insert(deptDO); + // 准备参数 + Long id = deptDO.getId(); + + // 调用 + DeptDO dbDept = deptService.getDept(id); + // 断言 + assertEquals(deptDO, dbDept); + } + + @Test + public void testGetDeptList_ids() { + // mock 数据 + DeptDO deptDO01 = randomPojo(DeptDO.class); + deptMapper.insert(deptDO01); + DeptDO deptDO02 = randomPojo(DeptDO.class); + deptMapper.insert(deptDO02); + // 准备参数 + List ids = Arrays.asList(deptDO01.getId(), deptDO02.getId()); + + // 调用 + List deptDOList = deptService.getDeptList(ids); + // 断言 + assertEquals(2, deptDOList.size()); + assertEquals(deptDO01, deptDOList.get(0)); + assertEquals(deptDO02, deptDOList.get(1)); + } + + @Test + public void testGetDeptList_reqVO() { // mock 数据 DeptDO dept = randomPojo(DeptDO.class, o -> { // 等会查询到 o.setName("开发部"); @@ -101,216 +216,55 @@ public class DeptServiceImplTest extends BaseDbUnitTest { } @Test - public void testCreateDept_success() { + public void testGetChildDeptList() { + // mock 数据(1 级别子节点) + DeptDO dept1 = randomPojo(DeptDO.class, o -> o.setName("1")); + deptMapper.insert(dept1); + DeptDO dept2 = randomPojo(DeptDO.class, o -> o.setName("2")); + deptMapper.insert(dept2); + // mock 数据(2 级子节点) + DeptDO dept1a = randomPojo(DeptDO.class, o -> o.setName("1-a").setParentId(dept1.getId())); + deptMapper.insert(dept1a); + DeptDO dept2a = randomPojo(DeptDO.class, o -> o.setName("2-a").setParentId(dept2.getId())); + deptMapper.insert(dept2a); // 准备参数 - DeptCreateReqVO reqVO = randomPojo(DeptCreateReqVO.class, o -> { - o.setParentId(DeptIdEnum.ROOT.getId()); - o.setStatus(randomCommonStatus()); - }); + Long id = dept1.getParentId(); // 调用 - Long deptId = deptService.createDept(reqVO); + List result = deptService.getChildDeptList(id); // 断言 - assertNotNull(deptId); - // 校验记录的属性是否正确 - DeptDO deptDO = deptMapper.selectById(deptId); - assertPojoEquals(reqVO, deptDO); - // 校验调用 - verify(deptProducer).sendDeptRefreshMessage(); + assertEquals(result.size(), 2); + assertPojoEquals(dept1, result.get(0)); + assertPojoEquals(dept1a, result.get(1)); } @Test - public void testUpdateDept_success() { - // mock 数据 - DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus())); - deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 + public void testGetChildDeptListFromCache() { + // mock 数据(1 级别子节点) + DeptDO dept1 = randomPojo(DeptDO.class, o -> o.setName("1")); + deptMapper.insert(dept1); + DeptDO dept2 = randomPojo(DeptDO.class, o -> o.setName("2")); + deptMapper.insert(dept2); + // mock 数据(2 级子节点) + DeptDO dept1a = randomPojo(DeptDO.class, o -> o.setName("1-a").setParentId(dept1.getId())); + deptMapper.insert(dept1a); + DeptDO dept2a = randomPojo(DeptDO.class, o -> o.setName("2-a").setParentId(dept2.getId())); + deptMapper.insert(dept2a); // 准备参数 - DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class, o -> { - // 设置更新的 ID - o.setParentId(DeptIdEnum.ROOT.getId()); - o.setId(dbDeptDO.getId()); - o.setStatus(randomCommonStatus()); - }); + Long id = dept1.getParentId(); // 调用 - deptService.updateDept(reqVO); - // 校验是否更新正确 - DeptDO deptDO = deptMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, deptDO); - // 校验调用 - verify(deptProducer).sendDeptRefreshMessage(); - } - - @Test - public void testDeleteDept_success() { - // mock 数据 - DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus())); - deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbDeptDO.getId(); - - // 调用 - deptService.deleteDept(id); - // 校验数据不存在了 - assertNull(deptMapper.selectById(id)); - // 校验调用 - verify(deptProducer).sendDeptRefreshMessage(); - } - - @Test - public void testValidateDept_nameDuplicateForUpdate() { - // mock 数据 - DeptDO deptDO = randomDeptDO(); - // 设置根节点部门 - deptDO.setParentId(DeptIdEnum.ROOT.getId()); - deptMapper.insert(deptDO); - // mock 数据 稍后模拟重复它的 name - DeptDO nameDeptDO = randomDeptDO(); - // 设置根节点部门 - nameDeptDO.setParentId(DeptIdEnum.ROOT.getId()); - deptMapper.insert(nameDeptDO); - // 准备参数 - DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class, o -> { - // 设置根节点部门 - o.setParentId(DeptIdEnum.ROOT.getId()); - // 设置更新的 ID - o.setId(deptDO.getId()); - // 模拟 name 重复 - o.setName(nameDeptDO.getName()); - }); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.updateDept(reqVO), DEPT_NAME_DUPLICATE); - } - - @Test - public void testValidateDept_parentNotExitsForCreate() { - // 准备参数 - DeptCreateReqVO reqVO = randomPojo(DeptCreateReqVO.class, - o -> o.setStatus(randomCommonStatus())); - - // 调用,并断言异常 - assertServiceException(() -> deptService.createDept(reqVO), DEPT_PARENT_NOT_EXITS); - } - - @Test - public void testValidateDept_notFoundForDelete() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.deleteDept(id), DEPT_NOT_FOUND); - } - - @Test - public void testValidateDept_exitsChildrenForDelete() { - // mock 数据 - DeptDO parentDept = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus())); - deptMapper.insert(parentDept);// @Sql: 先插入出一条存在的数据 - // 准备参数 - DeptDO childrenDeptDO = randomPojo(DeptDO.class, o -> { - o.setParentId(parentDept.getId()); - o.setStatus(randomCommonStatus()); - }); - // 插入子部门 - deptMapper.insert(childrenDeptDO); - // 调用, 并断言异常 - assertServiceException(() -> deptService.deleteDept(parentDept.getId()), DEPT_EXITS_CHILDREN); - } - - @Test - public void testValidateDept_parentErrorForUpdate() { - // mock 数据 - DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus())); - deptMapper.insert(dbDeptDO); - // 准备参数 - DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class, o -> { - // 设置自己为父部门 - o.setParentId(dbDeptDO.getId()); - // 设置更新的 ID - o.setId(dbDeptDO.getId()); - }); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.updateDept(reqVO), DEPT_PARENT_ERROR); - } - - @Test - public void testValidateDept_notEnableForCreate() { - // mock 数据 - DeptDO deptDO = randomPojo(DeptDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); - deptMapper.insert(deptDO); - // 准备参数 - DeptCreateReqVO reqVO = randomPojo(DeptCreateReqVO.class, o -> { - // 设置未启用的部门为父部门 - o.setParentId(deptDO.getId()); - }); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.createDept(reqVO), DEPT_NOT_ENABLE); - } - - @Test - public void testCheckDept_parentIsChildForUpdate() { - // mock 数据 - DeptDO parentDept = randomPojo(DeptDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - deptMapper.insert(parentDept); - DeptDO childDept = randomPojo(DeptDO.class, o -> { - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setParentId(parentDept.getId()); - }); - deptMapper.insert(childDept); - // 初始化本地缓存 - deptService.initLocalCache(); - - // 准备参数 - DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class, o -> { - // 设置自己的子部门为父部门 - o.setParentId(childDept.getId()); - // 设置更新的 ID - o.setId(parentDept.getId()); - }); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.updateDept(reqVO), DEPT_PARENT_IS_CHILD); - } - - @Test - public void testGetDeptList() { - // mock 数据 - DeptDO deptDO01 = randomDeptDO(); - deptMapper.insert(deptDO01); - DeptDO deptDO02 = randomDeptDO(); - deptMapper.insert(deptDO02); - // 准备参数 - List ids = Arrays.asList(deptDO01.getId(), deptDO02.getId()); - - // 调用 - List deptDOList = deptService.getDeptList(ids); + Set result = deptService.getChildDeptIdListFromCache(id); // 断言 - assertEquals(2, deptDOList.size()); - assertEquals(deptDO01, deptDOList.get(0)); - assertEquals(deptDO02, deptDOList.get(1)); - } - - @Test - public void testGetDept() { - // mock 数据 - DeptDO deptDO = randomDeptDO(); - deptMapper.insert(deptDO); - // 准备参数 - Long id = deptDO.getId(); - - // 调用 - DeptDO dbDept = deptService.getDept(id); - // 断言 - assertEquals(deptDO, dbDept); + assertEquals(result.size(), 2); + assertTrue(result.contains(dept1.getId())); + assertTrue(result.contains(dept1a.getId())); } @Test public void testValidateDeptList_success() { // mock 数据 - DeptDO deptDO = randomDeptDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); + DeptDO deptDO = randomPojo(DeptDO.class).setStatus(CommonStatusEnum.ENABLE.getStatus()); deptMapper.insert(deptDO); // 准备参数 List ids = singletonList(deptDO.getId()); @@ -331,7 +285,7 @@ public class DeptServiceImplTest extends BaseDbUnitTest { @Test public void testValidateDeptList_notEnable() { // mock 数据 - DeptDO deptDO = randomDeptDO().setStatus(CommonStatusEnum.DISABLE.getStatus()); + DeptDO deptDO = randomPojo(DeptDO.class).setStatus(CommonStatusEnum.DISABLE.getStatus()); deptMapper.insert(deptDO); // 准备参数 List ids = singletonList(deptDO.getId()); @@ -340,12 +294,4 @@ public class DeptServiceImplTest extends BaseDbUnitTest { assertServiceException(() -> deptService.validateDeptList(ids), DEPT_NOT_ENABLE, deptDO.getName()); } - @SafeVarargs - private static DeptDO randomDeptDO(Consumer... consumers) { - Consumer consumer = (o) -> { - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 - }; - return randomPojo(DeptDO.class, ArrayUtils.append(consumer, consumers)); - } - } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java index a0e41f75c..ea2725e25 100755 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java @@ -7,14 +7,12 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccou import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper; -import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; import java.util.List; -import java.util.Map; import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; @@ -23,14 +21,13 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_NOT_EXISTS; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** -* {@link MailAccountServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ + * {@link MailAccountServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ @Import(MailAccountServiceImpl.class) public class MailAccountServiceImplTest extends BaseDbUnitTest { @@ -42,23 +39,6 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { @MockBean private MailTemplateService mailTemplateService; - @MockBean - private MailProducer mailProducer; - - @Test - public void testInitLocalCache() { - MailAccountDO accountDO1 = randomPojo(MailAccountDO.class); - mailAccountMapper.insert(accountDO1); - MailAccountDO accountDO02 = randomPojo(MailAccountDO.class); - mailAccountMapper.insert(accountDO02); - - // 调用 - mailAccountService.initLocalCache(); - // 断言 mailAccountCache 缓存 - Map mailAccountCache = mailAccountService.getMailAccountCache(); - assertPojoEquals(accountDO1, mailAccountCache.get(accountDO1.getId())); - assertPojoEquals(accountDO02, mailAccountCache.get(accountDO02.getId())); - } @Test public void testCreateMailAccount_success() { @@ -72,7 +52,6 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { // 校验记录的属性是否正确 MailAccountDO mailAccount = mailAccountMapper.selectById(mailAccountId); assertPojoEquals(reqVO, mailAccount); - verify(mailProducer).sendMailAccountRefreshMessage(); } @Test @@ -91,7 +70,6 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { // 校验是否更新正确 MailAccountDO mailAccount = mailAccountMapper.selectById(reqVO.getId()); // 获取最新的 assertPojoEquals(reqVO, mailAccount); - verify(mailProducer).sendMailAccountRefreshMessage(); } @Test @@ -115,9 +93,8 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { // 调用 mailAccountService.deleteMailAccount(id); - // 校验数据不存在了 - assertNull(mailAccountMapper.selectById(id)); - verify(mailProducer).sendMailAccountRefreshMessage(); + // 校验数据不存在了 + assertNull(mailAccountMapper.selectById(id)); } @Test @@ -125,7 +102,6 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { // mock 数据 MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class); mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 - mailAccountService.initLocalCache(); // 准备参数 Long id = dbMailAccount.getId(); @@ -146,27 +122,27 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { @Test public void testGetMailAccountPage() { - // mock 数据 - MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class, o -> { // 等会查询到 - o.setMail("768@qq.com"); - o.setUsername("yunai"); - }); - mailAccountMapper.insert(dbMailAccount); - // 测试 mail 不匹配 - mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setMail("788@qq.com"))); - // 测试 username 不匹配 - mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setUsername("tudou"))); - // 准备参数 - MailAccountPageReqVO reqVO = new MailAccountPageReqVO(); - reqVO.setMail("768"); - reqVO.setUsername("yu"); + // mock 数据 + MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class, o -> { // 等会查询到 + o.setMail("768@qq.com"); + o.setUsername("yunai"); + }); + mailAccountMapper.insert(dbMailAccount); + // 测试 mail 不匹配 + mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setMail("788@qq.com"))); + // 测试 username 不匹配 + mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setUsername("tudou"))); + // 准备参数 + MailAccountPageReqVO reqVO = new MailAccountPageReqVO(); + reqVO.setMail("768"); + reqVO.setUsername("yu"); - // 调用 - PageResult pageResult = mailAccountService.getMailAccountPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbMailAccount, pageResult.getList().get(0)); + // 调用 + PageResult pageResult = mailAccountService.getMailAccountPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbMailAccount, pageResult.getList().get(0)); } @Test diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java index 83f23e92e..8776595ea 100755 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java @@ -8,9 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemp import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper; -import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer; import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; @@ -27,13 +25,12 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.verify; /** -* {@link MailTemplateServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ + * {@link MailTemplateServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ @Import(MailTemplateServiceImpl.class) public class MailTemplateServiceImplTest extends BaseDbUnitTest { @@ -43,24 +40,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @Resource private MailTemplateMapper mailTemplateMapper; - @MockBean - private MailProducer mailProducer; - - @Test - public void testInitLocalCache() { - MailTemplateDO templateDO01 = randomPojo(MailTemplateDO.class); - mailTemplateMapper.insert(templateDO01); - MailTemplateDO templateDO02 = randomPojo(MailTemplateDO.class); - mailTemplateMapper.insert(templateDO02); - - // 调用 - mailTemplateService.initLocalCache(); - // 断言 mailTemplateCache 缓存 - Map mailTemplateCache = mailTemplateService.getMailTemplateCache(); - assertPojoEquals(templateDO01, mailTemplateCache.get(templateDO01.getCode())); - assertPojoEquals(templateDO02, mailTemplateCache.get(templateDO02.getCode())); - } - @Test public void testCreateMailTemplate_success() { // 准备参数 @@ -73,7 +52,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { // 校验记录的属性是否正确 MailTemplateDO mailTemplate = mailTemplateMapper.selectById(mailTemplateId); assertPojoEquals(reqVO, mailTemplate); - verify(mailProducer).sendMailTemplateRefreshMessage(); } @Test @@ -91,7 +69,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { // 校验是否更新正确 MailTemplateDO mailTemplate = mailTemplateMapper.selectById(reqVO.getId()); // 获取最新的 assertPojoEquals(reqVO, mailTemplate); - verify(mailProducer).sendMailTemplateRefreshMessage(); } @Test @@ -115,7 +92,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { mailTemplateService.deleteMailTemplate(id); // 校验数据不存在了 assertNull(mailTemplateMapper.selectById(id)); - verify(mailProducer).sendMailTemplateRefreshMessage(); } @Test @@ -129,39 +105,39 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @Test public void testGetMailTemplatePage() { - // mock 数据 - MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class, o -> { // 等会查询到 - o.setName("源码"); - o.setCode("test_01"); - o.setAccountId(1L); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCreateTime(buildTime(2023, 2, 3)); - }); - mailTemplateMapper.insert(dbMailTemplate); - // 测试 name 不匹配 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setName("芋道"))); - // 测试 code 不匹配 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCode("test_02"))); - // 测试 accountId 不匹配 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setAccountId(2L))); - // 测试 status 不匹配 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 createTime 不匹配 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCreateTime(buildTime(2023, 1, 5)))); - // 准备参数 - MailTemplatePageReqVO reqVO = new MailTemplatePageReqVO(); - reqVO.setName("源"); - reqVO.setCode("est_01"); - reqVO.setAccountId(1L); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 5)); + // mock 数据 + MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class, o -> { // 等会查询到 + o.setName("源码"); + o.setCode("test_01"); + o.setAccountId(1L); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2023, 2, 3)); + }); + mailTemplateMapper.insert(dbMailTemplate); + // 测试 name 不匹配 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setName("芋道"))); + // 测试 code 不匹配 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCode("test_02"))); + // 测试 accountId 不匹配 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setAccountId(2L))); + // 测试 status 不匹配 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime 不匹配 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCreateTime(buildTime(2023, 1, 5)))); + // 准备参数 + MailTemplatePageReqVO reqVO = new MailTemplatePageReqVO(); + reqVO.setName("源"); + reqVO.setCode("est_01"); + reqVO.setAccountId(1L); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 5)); - // 调用 - PageResult pageResult = mailTemplateService.getMailTemplatePage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbMailTemplate, pageResult.getList().get(0)); + // 调用 + PageResult pageResult = mailTemplateService.getMailTemplatePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbMailTemplate, pageResult.getList().get(0)); } @Test @@ -199,7 +175,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { // mock 数据 MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); mailTemplateMapper.insert(dbMailTemplate); - mailTemplateService.initLocalCache(); // 准备参数 String code = dbMailTemplate.getCode(); diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java index 28f6f9a38..04c410fb7 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java @@ -8,9 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.Notify import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO; 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 org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; @@ -25,13 +23,12 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.verify; /** -* {@link NotifyTemplateServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ + * {@link NotifyTemplateServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ @Import(NotifyTemplateServiceImpl.class) public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { @@ -41,9 +38,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { @Resource private NotifyTemplateMapper notifyTemplateMapper; - @MockBean - private NotifyProducer notifyProducer; - @Test public void testCreateNotifyTemplate_success() { // 准备参数 @@ -57,7 +51,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { // 校验记录的属性是否正确 NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(notifyTemplateId); assertPojoEquals(reqVO, notifyTemplate); - verify(notifyProducer).sendNotifyTemplateRefreshMessage(); } @Test @@ -76,7 +69,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { // 校验是否更新正确 NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(reqVO.getId()); // 获取最新的 assertPojoEquals(reqVO, notifyTemplate); - verify(notifyProducer).sendNotifyTemplateRefreshMessage(); } @Test @@ -98,9 +90,8 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { // 调用 notifyTemplateService.deleteNotifyTemplate(id); - // 校验数据不存在了 - assertNull(notifyTemplateMapper.selectById(id)); - verify(notifyProducer).sendNotifyTemplateRefreshMessage(); + // 校验数据不存在了 + assertNull(notifyTemplateMapper.selectById(id)); } @Test @@ -114,35 +105,35 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { @Test public void testGetNotifyTemplatePage() { - // mock 数据 - NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class, o -> { // 等会查询到 - o.setName("芋头"); - o.setCode("test_01"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCreateTime(buildTime(2022, 2, 3)); - }); - notifyTemplateMapper.insert(dbNotifyTemplate); - // 测试 name 不匹配 - notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setName("投"))); - // 测试 code 不匹配 - notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCode("test_02"))); - // 测试 status 不匹配 - notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 createTime 不匹配 - notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCreateTime(buildTime(2022, 1, 5)))); - // 准备参数 - NotifyTemplatePageReqVO reqVO = new NotifyTemplatePageReqVO(); - reqVO.setName("芋"); - reqVO.setCode("est_01"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 5)); + // mock 数据 + NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class, o -> { // 等会查询到 + o.setName("芋头"); + o.setCode("test_01"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2022, 2, 3)); + }); + notifyTemplateMapper.insert(dbNotifyTemplate); + // 测试 name 不匹配 + notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setName("投"))); + // 测试 code 不匹配 + notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCode("test_02"))); + // 测试 status 不匹配 + notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime 不匹配 + notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCreateTime(buildTime(2022, 1, 5)))); + // 准备参数 + NotifyTemplatePageReqVO reqVO = new NotifyTemplatePageReqVO(); + reqVO.setName("芋"); + reqVO.setCode("est_01"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 5)); - // 调用 - PageResult pageResult = notifyTemplateService.getNotifyTemplatePage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbNotifyTemplate, pageResult.getList().get(0)); + // 调用 + PageResult pageResult = notifyTemplateService.getNotifyTemplatePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbNotifyTemplate, pageResult.getList().get(0)); } @Test @@ -164,7 +155,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { // mock 数据 NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class); notifyTemplateMapper.insert(dbNotifyTemplate); - notifyTemplateService.initLocalCache(); // 准备参数 String code = dbNotifyTemplate.getCode(); @@ -173,7 +163,7 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { // 断言 assertPojoEquals(dbNotifyTemplate, notifyTemplate); } - + @Test public void testFormatNotifyTemplateContent() { // 准备参数 diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImplTest.java index 3a671b0b9..25b31220c 100755 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImplTest.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.system.service.oauth2; -import cn.hutool.core.map.MapUtil; +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.test.core.ut.BaseDbUnitTest; @@ -9,14 +9,12 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2Cl import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; 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 org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.mockito.MockedStatic; import org.springframework.context.annotation.Import; import javax.annotation.Resource; import java.util.Collections; -import java.util.Map; import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; @@ -24,13 +22,14 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.verify; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mockStatic; /** -* {@link OAuth2ClientServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ + * {@link OAuth2ClientServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ @Import(OAuth2ClientServiceImpl.class) public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { @@ -40,26 +39,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { @Resource private OAuth2ClientMapper oauth2ClientMapper; - @MockBean - private OAuth2ClientProducer oauth2ClientProducer; - - @Test - public void testInitLocalCache() { - // mock 数据 - OAuth2ClientDO clientDO1 = randomPojo(OAuth2ClientDO.class); - oauth2ClientMapper.insert(clientDO1); - OAuth2ClientDO clientDO2 = randomPojo(OAuth2ClientDO.class); - oauth2ClientMapper.insert(clientDO2); - - // 调用 - oauth2ClientService.initLocalCache(); - // 断言 clientCache 缓存 - Map clientCache = oauth2ClientService.getClientCache(); - assertEquals(2, clientCache.size()); - assertPojoEquals(clientDO1, clientCache.get(clientDO1.getClientId())); - assertPojoEquals(clientDO2, clientCache.get(clientDO2.getClientId())); - } - @Test public void testCreateOAuth2Client_success() { // 准备参数 @@ -73,7 +52,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { // 校验记录的属性是否正确 OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(oauth2ClientId); assertPojoEquals(reqVO, oAuth2Client); - verify(oauth2ClientProducer).sendOAuth2ClientRefreshMessage(); } @Test @@ -92,7 +70,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { // 校验是否更新正确 OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(reqVO.getId()); // 获取最新的 assertPojoEquals(reqVO, oAuth2Client); - verify(oauth2ClientProducer).sendOAuth2ClientRefreshMessage(); } @Test @@ -116,7 +93,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { oauth2ClientService.deleteOAuth2Client(id); // 校验数据不存在了 assertNull(oauth2ClientMapper.selectById(id)); - verify(oauth2ClientProducer).sendOAuth2ClientRefreshMessage(); } @Test @@ -167,62 +143,78 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { } @Test - public void testGetOAuth2ClientPage() { - // mock 数据 - OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class, o -> { // 等会查询到 - o.setName("潜龙"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - }); - oauth2ClientMapper.insert(dbOAuth2Client); - // 测试 name 不匹配 - oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setName("凤凰"))); - // 测试 status 不匹配 - oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 准备参数 - OAuth2ClientPageReqVO reqVO = new OAuth2ClientPageReqVO(); - reqVO.setName("龙"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + public void testGetOAuth2ClientFromCache() { + // mock 数据 + OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class); + oauth2ClientMapper.insert(clientDO); + // 准备参数 + String clientId = clientDO.getClientId(); - // 调用 - PageResult pageResult = oauth2ClientService.getOAuth2ClientPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbOAuth2Client, pageResult.getList().get(0)); + // 调用,并断言 + OAuth2ClientDO dbClientDO = oauth2ClientService.getOAuth2ClientFromCache(clientId); + assertPojoEquals(clientDO, dbClientDO); + } + + @Test + public void testGetOAuth2ClientPage() { + // mock 数据 + OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class, o -> { // 等会查询到 + o.setName("潜龙"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + oauth2ClientMapper.insert(dbOAuth2Client); + // 测试 name 不匹配 + oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setName("凤凰"))); + // 测试 status 不匹配 + oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 准备参数 + OAuth2ClientPageReqVO reqVO = new OAuth2ClientPageReqVO(); + reqVO.setName("龙"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + + // 调用 + PageResult pageResult = oauth2ClientService.getOAuth2ClientPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbOAuth2Client, pageResult.getList().get(0)); } @Test public void testValidOAuthClientFromCache() { - // mock 方法 - OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("default") - .setStatus(CommonStatusEnum.ENABLE.getStatus()); - OAuth2ClientDO client02 = randomPojo(OAuth2ClientDO.class).setClientId("disable") - .setStatus(CommonStatusEnum.DISABLE.getStatus()); - Map clientCache = MapUtil.builder() - .put(client.getClientId(), client) - .put(client02.getClientId(), client02).build(); - oauth2ClientService.setClientCache(clientCache); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(OAuth2ClientServiceImpl.class))) + .thenReturn(oauth2ClientService); - // 调用,并断言 - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache(randomString(), - null, null, null, null), OAUTH2_CLIENT_NOT_EXISTS); - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("disable", - null, null, null, null), OAUTH2_CLIENT_DISABLE); - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", - randomString(), null, null, null), OAUTH2_CLIENT_CLIENT_SECRET_ERROR); - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", - null, randomString(), null, null), OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS); - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", - null, null, Collections.singleton(randomString()), null), OAUTH2_CLIENT_SCOPE_OVER); - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", - null, null, null, "test"), OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH, "test"); - // 成功调用(1:参数完整) - OAuth2ClientDO result = oauth2ClientService.validOAuthClientFromCache(client.getClientId(), client.getSecret(), - client.getAuthorizedGrantTypes().get(0), client.getScopes(), client.getRedirectUris().get(0)); - assertPojoEquals(client, result); - // 成功调用(2:只有 clientId 参数) - result = oauth2ClientService.validOAuthClientFromCache(client.getClientId()); - assertPojoEquals(client, result); + // mock 方法 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("default") + .setStatus(CommonStatusEnum.ENABLE.getStatus()); + oauth2ClientMapper.insert(client); + OAuth2ClientDO client02 = randomPojo(OAuth2ClientDO.class).setClientId("disable") + .setStatus(CommonStatusEnum.DISABLE.getStatus()); + oauth2ClientMapper.insert(client02); + + // 调用,并断言 + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache(randomString(), + null, null, null, null), OAUTH2_CLIENT_NOT_EXISTS); + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("disable", + null, null, null, null), OAUTH2_CLIENT_DISABLE); + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", + randomString(), null, null, null), OAUTH2_CLIENT_CLIENT_SECRET_ERROR); + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", + null, randomString(), null, null), OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS); + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", + null, null, Collections.singleton(randomString()), null), OAUTH2_CLIENT_SCOPE_OVER); + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", + null, null, null, "test"), OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH, "test"); + // 成功调用(1:参数完整) + OAuth2ClientDO result = oauth2ClientService.validOAuthClientFromCache(client.getClientId(), client.getSecret(), + client.getAuthorizedGrantTypes().get(0), client.getScopes(), client.getRedirectUris().get(0)); + assertPojoEquals(client, result); + // 成功调用(2:只有 clientId 参数) + result = oauth2ClientService.validOAuthClientFromCache(client.getClientId()); + assertPojoEquals(client, result); + } } } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java index d643c5501..a0c17b3fb 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java @@ -8,10 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuUp 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.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.collect.LinkedListMultimap; -import com.google.common.collect.Multimap; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; @@ -26,8 +23,6 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.argThat; @@ -46,35 +41,12 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @MockBean private PermissionService permissionService; @MockBean - private MenuProducer menuProducer; - @MockBean private TenantService tenantService; - @Test - public void testInitLocalCache_success() { - MenuDO menuDO1 = randomPojo(MenuDO.class); - menuMapper.insert(menuDO1); - MenuDO menuDO2 = randomPojo(MenuDO.class); - menuMapper.insert(menuDO2); - - // 调用 - menuService.initLocalCache(); - // 校验 menuCache 缓存 - Map menuCache = menuService.getMenuCache(); - assertEquals(2, menuCache.size()); - assertPojoEquals(menuDO1, menuCache.get(menuDO1.getId())); - assertPojoEquals(menuDO2, menuCache.get(menuDO2.getId())); - // 校验 permissionMenuCache 缓存 - Multimap permissionMenuCache = menuService.getPermissionMenuCache(); - assertEquals(2, permissionMenuCache.size()); - assertPojoEquals(menuDO1, permissionMenuCache.get(menuDO1.getPermission())); - assertPojoEquals(menuDO2, permissionMenuCache.get(menuDO2.getPermission())); - } - @Test public void testCreateMenu_success() { // mock 数据(构造父菜单) - MenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, + MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", 0L); menuMapper.insert(menuDO); Long parentId = menuDO.getId(); @@ -89,14 +61,12 @@ public class MenuServiceImplTest extends BaseDbUnitTest { // 校验记录的属性是否正确 MenuDO dbMenu = menuMapper.selectById(menuId); assertPojoEquals(reqVO, dbMenu); - // 校验调用 - verify(menuProducer).sendMenuRefreshMessage(); } @Test public void testUpdateMenu_success() { // mock 数据(构造父子菜单) - MenuDO sonMenuDO = initParentAndSonMenu(); + MenuDO sonMenuDO = createParentAndSonMenu(); Long sonId = sonMenuDO.getId(); // 准备参数 MenuUpdateReqVO reqVO = randomPojo(MenuUpdateReqVO.class, o -> { @@ -111,8 +81,6 @@ public class MenuServiceImplTest extends BaseDbUnitTest { // 校验记录的属性是否正确 MenuDO dbMenu = menuMapper.selectById(sonId); assertPojoEquals(reqVO, dbMenu); - // 校验调用 - verify(menuProducer).sendMenuRefreshMessage(); } @Test @@ -137,7 +105,6 @@ public class MenuServiceImplTest extends BaseDbUnitTest { MenuDO dbMenuDO = menuMapper.selectById(id); assertNull(dbMenuDO); verify(permissionService).processMenuDeleted(id); - verify(menuProducer).sendMenuRefreshMessage(); } @Test @@ -149,7 +116,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @Test public void testDeleteMenu_existChildren() { // mock 数据(构造父子菜单) - MenuDO sonMenu = initParentAndSonMenu(); + MenuDO sonMenu = createParentAndSonMenu(); // 准备参数 Long parentId = sonMenu.getParentId(); @@ -218,85 +185,6 @@ public class MenuServiceImplTest extends BaseDbUnitTest { assertPojoEquals(menu100, result.get(0)); } - @Test - public void testListMenusFromCache_withoutId() { - // mock 缓存 - Map menuCache = new HashMap<>(); - // 可被匹配 - MenuDO menuDO = randomPojo(MenuDO.class, o -> o.setId(1L) - .setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus())); - menuCache.put(menuDO.getId(), menuDO); - // 测试 type 不匹配 - menuCache.put(3L, randomPojo(MenuDO.class, o -> o.setId(3L) - .setType(MenuTypeEnum.BUTTON.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus()))); - // 测试 status 不匹配 - menuCache.put(4L, randomPojo(MenuDO.class, o -> o.setId(4L) - .setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.DISABLE.getStatus()))); - menuService.setMenuCache(menuCache); - // 准备参数 - Collection menuTypes = singletonList(MenuTypeEnum.MENU.getType()); - Collection menusStatuses = singletonList(CommonStatusEnum.ENABLE.getStatus()); - - // 调用 - List list = menuService.getMenuListFromCache(menuTypes, menusStatuses); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(menuDO, list.get(0)); - } - - @Test - public void testListMenusFromCache_withId() { - // mock 缓存 - Map menuCache = new HashMap<>(); - // 可被匹配 - MenuDO menuDO = randomPojo(MenuDO.class, o -> o.setId(1L) - .setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus())); - menuCache.put(menuDO.getId(), menuDO); - // 测试 id 不匹配 - menuCache.put(2L, randomPojo(MenuDO.class, o -> o.setId(2L) - .setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus()))); - // 测试 type 不匹配 - menuCache.put(3L, randomPojo(MenuDO.class, o -> o.setId(3L) - .setType(MenuTypeEnum.BUTTON.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus()))); - // 测试 status 不匹配 - menuCache.put(4L, randomPojo(MenuDO.class, o -> o.setId(4L) - .setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.DISABLE.getStatus()))); - menuService.setMenuCache(menuCache); - // 准备参数 - Collection menuIds = asList(1L, 3L, 4L); - Collection menuTypes = singletonList(MenuTypeEnum.MENU.getType()); - Collection menusStatuses = singletonList(CommonStatusEnum.ENABLE.getStatus()); - - // 调用 - List list = menuService.getMenuListFromCache(menuIds, menuTypes, menusStatuses); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(menuDO, list.get(0)); - } - - @Test - public void testGetMenuListByPermissionFromCache() { - // mock 缓存 - Multimap permissionMenuCache = LinkedListMultimap.create(); - // 可被匹配 - MenuDO menuDO01 = randomPojo(MenuDO.class, o -> o.setId(1L).setPermission("123")); - permissionMenuCache.put(menuDO01.getPermission(), menuDO01); - MenuDO menuDO02 = randomPojo(MenuDO.class, o -> o.setId(2L).setPermission("123")); - permissionMenuCache.put(menuDO02.getPermission(), menuDO02); - // 不可匹配 - permissionMenuCache.put("456", randomPojo(MenuDO.class, o -> o.setId(3L).setPermission("456"))); - menuService.setPermissionMenuCache(permissionMenuCache); - // 准备参数 - String permission = "123"; - - // 调用 - List list = menuService.getMenuListByPermissionFromCache(permission); - // 断言 - assertEquals(2, list.size()); - assertPojoEquals(menuDO01, list.get(0)); - assertPojoEquals(menuDO02, list.get(1)); - } - @Test public void testGetMenu() { // mock 数据 @@ -314,7 +202,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @Test public void testValidateParentMenu_success() { // mock 数据 - MenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, "parent", 0L); + MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", 0L); menuMapper.insert(menuDO); // 准备参数 Long parentId = menuDO.getId(); @@ -340,7 +228,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @Test public void testValidateParentMenu_parentTypeError() { // mock 数据 - MenuDO menuDO = createMenuDO(MenuTypeEnum.BUTTON, "parent", 0L); + MenuDO menuDO = buildMenuDO(MenuTypeEnum.BUTTON, "parent", 0L); menuMapper.insert(menuDO); // 准备参数 Long parentId = menuDO.getId(); @@ -353,7 +241,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @Test public void testValidateMenu_success() { // mock 父子菜单 - MenuDO sonMenu = initParentAndSonMenu(); + MenuDO sonMenu = createParentAndSonMenu(); // 准备参数 Long parentId = sonMenu.getParentId(); Long otherSonMenuId = randomLongId(); @@ -366,7 +254,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @Test public void testValidateMenu_sonMenuNameDuplicate() { // mock 父子菜单 - MenuDO sonMenu = initParentAndSonMenu(); + MenuDO sonMenu = createParentAndSonMenu(); // 准备参数 Long parentId = sonMenu.getParentId(); Long otherSonMenuId = randomLongId(); @@ -380,26 +268,26 @@ public class MenuServiceImplTest extends BaseDbUnitTest { // ====================== 初始化方法 ====================== /** - * 构造父子菜单,返回子菜单 + * 插入父子菜单,返回子菜单 * * @return 子菜单 */ - private MenuDO initParentAndSonMenu() { + private MenuDO createParentAndSonMenu() { // 构造父子菜单 - MenuDO parentMenuDO = createMenuDO(MenuTypeEnum.MENU, "parent", ID_ROOT); + MenuDO parentMenuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", ID_ROOT); menuMapper.insert(parentMenuDO); // 构建子菜单 - MenuDO sonMenuDO = createMenuDO(MenuTypeEnum.MENU, "testSonName", + MenuDO sonMenuDO = buildMenuDO(MenuTypeEnum.MENU, "testSonName", parentMenuDO.getParentId()); menuMapper.insert(sonMenuDO); return sonMenuDO; } - private MenuDO createMenuDO(MenuTypeEnum type, String name, Long parentId) { - return createMenuDO(type, name, parentId, randomCommonStatus()); + private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId) { + return buildMenuDO(type, name, parentId, randomCommonStatus()); } - private MenuDO createMenuDO(MenuTypeEnum type, String name, Long parentId, Integer status) { + private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId, Integer status) { return randomPojo(MenuDO.class, o -> o.setId(null).setName(name).setParentId(parentId) .setType(type.getType()).setStatus(status)); } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java index 35b250208..442abd741 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.system.service.permission; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.map.MapUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO; @@ -14,32 +14,28 @@ import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; 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.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.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Multimap; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.Set; +import static cn.hutool.core.collection.ListUtil.toList; import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static java.util.Arrays.asList; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @Import({PermissionServiceImpl.class}) public class PermissionServiceTest extends BaseDbUnitTest { @@ -61,131 +57,74 @@ public class PermissionServiceTest extends BaseDbUnitTest { @MockBean private AdminUserService userService; - @MockBean - private PermissionProducer permissionProducer; - @Test - public void testInitLocalCacheForRoleMenu() { - // mock 数据 - RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(1L).setMenuId(10L)); - roleMenuMapper.insert(roleMenuDO01); - RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(1L).setMenuId(20L)); - roleMenuMapper.insert(roleMenuDO02); + public void testHasAnyPermissions_superAdmin() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); - // 调用 - permissionService.initLocalCacheForRoleMenu(); - // 断言 roleMenuCache 缓存 - assertEquals(1, permissionService.getRoleMenuCache().keySet().size()); - assertEquals(asList(10L, 20L), permissionService.getRoleMenuCache().get(1L)); - // 断言 menuRoleCache 缓存 - assertEquals(2, permissionService.getMenuRoleCache().size()); - assertEquals(singletonList(1L), permissionService.getMenuRoleCache().get(10L)); - assertEquals(singletonList(1L), permissionService.getMenuRoleCache().get(20L)); + // 准备参数 + Long userId = 1L; + String[] roles = new String[]{"system:user:query", "system:user:create"}; + // mock 用户登录的角色 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); + RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); + // mock 其它方法 + when(roleService.hasAnySuperAdmin(eq(asSet(100L)))).thenReturn(true); + + // 调用,并断言 + assertTrue(permissionService.hasAnyPermissions(userId, roles)); + } } @Test - public void testInitLocalCacheForUserRole() { - // mock 数据 - UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); - userRoleMapper.insert(userRoleDO01); - UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); - userRoleMapper.insert(roleMenuDO02); + public void testHasAnyPermissions_normal() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); - // 调用 - permissionService.initLocalCacheForUserRole(); - // 断言 roleMenuCache 缓存 - assertEquals(1, permissionService.getUserRoleCache().size()); - assertEquals(asSet(10L, 20L), permissionService.getUserRoleCache().get(1L)); + // 准备参数 + Long userId = 1L; + String[] roles = new String[]{"system:user:query", "system:user:create"}; + // mock 用户登录的角色 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); + RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); + // mock 菜单 + Long menuId = 1000L; + when(menuService.getMenuIdListByPermissionFromCache( + eq("system:user:create"))).thenReturn(singletonList(menuId)); + roleMenuMapper.insert(randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1000L)); + + // 调用,并断言 + assertTrue(permissionService.hasAnyPermissions(userId, roles)); + } } @Test - public void testGetRoleMenuListFromCache_superAdmin() { - // 准备参数 - Collection roleIds = singletonList(100L); - Collection menuTypes = asList(2, 3); - Collection menusStatuses = asList(0, 1); - // mock 方法 - List roleList = singletonList(randomPojo(RoleDO.class, o -> o.setId(100L))); - when(roleService.getRoleListFromCache(eq(roleIds))).thenReturn(roleList); - when(roleService.hasAnySuperAdmin(same(roleList))).thenReturn(true); - List menuList = randomPojoList(MenuDO.class); - when(menuService.getMenuListFromCache(eq(menuTypes), eq(menusStatuses))).thenReturn(menuList); + public void testHasAnyRoles() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); - // 调用 - List result = permissionService.getRoleMenuListFromCache(roleIds, menuTypes, menusStatuses); - // 断言 - assertSame(menuList, result); + // 准备参数 + Long userId = 1L; + String[] roles = new String[]{"yunai", "tudou"}; + // mock 用户与角色的缓存 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); + RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L).setCode("tudou") + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); + + // 调用,并断言 + assertTrue(permissionService.hasAnyRoles(userId, roles)); + } } - @Test - public void testGetRoleMenuListFromCache_normal() { - // 准备参数 - Collection roleIds = asSet(100L, 200L); - Collection menuTypes = asList(2, 3); - Collection menusStatuses = asList(0, 1); - // mock 方法 - Multimap roleMenuCache = ImmutableMultimap.builder().put(100L, 1000L) - .put(200L, 2000L).put(200L, 2001L).build(); - permissionService.setRoleMenuCache(roleMenuCache); - List menuList = randomPojoList(MenuDO.class); - when(menuService.getMenuListFromCache(eq(asList(1000L, 2000L, 2001L)), eq(menuTypes), eq(menusStatuses))).thenReturn(menuList); - - // 调用 - List result = permissionService.getRoleMenuListFromCache(roleIds, menuTypes, menusStatuses); - // 断言 - assertSame(menuList, result); - } - - @Test - public void testGetUserRoleIdsFromCache() { - // 准备参数 - Long userId = 1L; - Collection roleStatuses = singleton(CommonStatusEnum.ENABLE.getStatus()); - // mock 方法 - Map> userRoleCache = MapUtil.>builder() - .put(1L, asSet(10L, 20L)).build(); - permissionService.setUserRoleCache(userRoleCache); - RoleDO roleDO01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleFromCache(eq(10L))).thenReturn(roleDO01); - RoleDO roleDO02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); - when(roleService.getRoleFromCache(eq(20L))).thenReturn(roleDO02); - - // 调用 - Set roleIds = permissionService.getUserRoleIdsFromCache(userId, roleStatuses); - // 断言 - assertEquals(asSet(10L), roleIds); - } - - @Test - public void testGetRoleMenuIds_superAdmin() { - // 准备参数 - Long roleId = 100L; - // mock 方法 - when(roleService.hasAnySuperAdmin(eq(singleton(100L)))).thenReturn(true); - List menuList = singletonList(randomPojo(MenuDO.class).setId(1L)); - when(menuService.getMenuList()).thenReturn(menuList); - - // 调用 - Set menuIds = permissionService.getRoleMenuIds(roleId); - // 断言 - assertEquals(singleton(1L), menuIds); - } - - @Test - public void testGetRoleMenuIds_normal() { - // 准备参数 - Long roleId = 100L; - // mock 数据 - RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1L); - roleMenuMapper.insert(roleMenu01); - RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(2L); - roleMenuMapper.insert(roleMenu02); - - // 调用 - Set menuIds = permissionService.getRoleMenuIds(roleId); - // 断言 - assertEquals(asSet(1L, 2L), menuIds); - } + // ========== 角色-菜单的相关方法 ========== @Test public void testAssignRoleMenu() { @@ -207,75 +146,6 @@ public class PermissionServiceTest extends BaseDbUnitTest { assertEquals(200L, roleMenuList.get(0).getMenuId()); assertEquals(1L, roleMenuList.get(1).getRoleId()); assertEquals(300L, roleMenuList.get(1).getMenuId()); - verify(permissionProducer).sendRoleMenuRefreshMessage(); - } - - @Test - public void testAssignUserRole() { - // 准备参数 - Long userId = 1L; - Set roleIds = asSet(200L, 300L); - // mock 数据 - UserRoleDO userRole01 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(100L); - userRoleMapper.insert(userRole01); - UserRoleDO userRole02 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(200L); - userRoleMapper.insert(userRole02); - - // 调用 - permissionService.assignUserRole(userId, roleIds); - // 断言 - List userRoleDOList = userRoleMapper.selectList(); - assertEquals(2, userRoleDOList.size()); - assertEquals(1L, userRoleDOList.get(0).getUserId()); - assertEquals(200L, userRoleDOList.get(0).getRoleId()); - assertEquals(1L, userRoleDOList.get(1).getUserId()); - assertEquals(300L, userRoleDOList.get(1).getRoleId()); - verify(permissionProducer).sendUserRoleRefreshMessage(); - } - - @Test - public void testGetUserRoleIdListByUserId() { - // 准备参数 - Long userId = 1L; - // mock 数据 - UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); - userRoleMapper.insert(userRoleDO01); - UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); - userRoleMapper.insert(roleMenuDO02); - - // 调用 - Set result = permissionService.getUserRoleIdListByUserId(userId); - // 断言 - assertEquals(asSet(10L, 20L), result); - } - - @Test - public void testGetUserRoleIdListByRoleIds() { - // 准备参数 - Collection roleIds = asSet(10L, 20L); - // mock 数据 - UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); - userRoleMapper.insert(userRoleDO01); - UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(2L).setRoleId(20L)); - userRoleMapper.insert(roleMenuDO02); - - // 调用 - Set result = permissionService.getUserRoleIdListByRoleIds(roleIds); - // 断言 - assertEquals(asSet(1L, 2L), result); - } - - @Test - public void testAssignRoleDataScope() { - // 准备参数 - Long roleId = 1L; - Integer dataScope = 2; - Set dataScopeDeptIds = asSet(10L, 20L); - - // 调用 - permissionService.assignRoleDataScope(roleId, dataScope, dataScopeDeptIds); - // 断言 - verify(roleService).updateRoleDataScope(eq(roleId), eq(dataScope), eq(dataScopeDeptIds)); } @Test @@ -303,9 +173,6 @@ public class PermissionServiceTest extends BaseDbUnitTest { List dbUserRoles = userRoleMapper.selectList(); assertEquals(1, dbUserRoles.size()); assertPojoEquals(dbUserRoles.get(0), userRoleDO02); - // 断言调用 - verify(permissionProducer).sendRoleMenuRefreshMessage(); - verify(permissionProducer).sendUserRoleRefreshMessage(); } @Test @@ -324,8 +191,77 @@ public class PermissionServiceTest extends BaseDbUnitTest { List dbRoleMenus = roleMenuMapper.selectList(); assertEquals(1, dbRoleMenus.size()); assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02); - // 断言调用 - verify(permissionProducer).sendRoleMenuRefreshMessage(); + } + + @Test + public void testGetRoleMenuIds_superAdmin() { + // 准备参数 + Long roleId = 100L; + // mock 方法 + when(roleService.hasAnySuperAdmin(eq(singleton(100L)))).thenReturn(true); + List menuList = singletonList(randomPojo(MenuDO.class).setId(1L)); + when(menuService.getMenuList()).thenReturn(menuList); + + // 调用 + Set menuIds = permissionService.getRoleMenuListByRoleId(roleId); + // 断言 + assertEquals(singleton(1L), menuIds); + } + + @Test + public void testGetRoleMenuIds_normal() { + // 准备参数 + Long roleId = 100L; + // mock 数据 + RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1L); + roleMenuMapper.insert(roleMenu01); + RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(2L); + roleMenuMapper.insert(roleMenu02); + + // 调用 + Set menuIds = permissionService.getRoleMenuListByRoleId(roleId); + // 断言 + assertEquals(asSet(1L, 2L), menuIds); + } + + @Test + public void testGetMenuRoleIdListByMenuIdFromCache() { + // 准备参数 + Long menuId = 1L; + // mock 数据 + RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1L); + roleMenuMapper.insert(roleMenu01); + RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(200L).setMenuId(1L); + roleMenuMapper.insert(roleMenu02); + + // 调用 + Set roleIds = permissionService.getMenuRoleIdListByMenuIdFromCache(menuId); + // 断言 + assertEquals(asSet(100L, 200L), roleIds); + } + + // ========== 用户-角色的相关方法 ========== + + @Test + public void testAssignUserRole() { + // 准备参数 + Long userId = 1L; + Set roleIds = asSet(200L, 300L); + // mock 数据 + UserRoleDO userRole01 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(100L); + userRoleMapper.insert(userRole01); + UserRoleDO userRole02 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(200L); + userRoleMapper.insert(userRole02); + + // 调用 + permissionService.assignUserRole(userId, roleIds); + // 断言 + List userRoleDOList = userRoleMapper.selectList(); + assertEquals(2, userRoleDOList.size()); + assertEquals(1L, userRoleDOList.get(0).getUserId()); + assertEquals(200L, userRoleDOList.get(0).getRoleId()); + assertEquals(1L, userRoleDOList.get(1).getUserId()); + assertEquals(300L, userRoleDOList.get(1).getRoleId()); } @Test @@ -344,202 +280,248 @@ public class PermissionServiceTest extends BaseDbUnitTest { List dbUserRoles = userRoleMapper.selectList(); assertEquals(1, dbUserRoles.size()); assertPojoEquals(dbUserRoles.get(0), userRoleDO02); - // 断言调用 - verify(permissionProducer).sendUserRoleRefreshMessage(); } @Test - public void testHasAnyPermissions_superAdmin() { + public void testGetUserRoleIdListByUserId() { // 准备参数 Long userId = 1L; - String[] roles = new String[]{"system:user:query", "system:user:create"}; - // mock 用户与角色的缓存 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(100L)).build()); - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleFromCache(eq(100L))).thenReturn(role); - // mock 其它方法 - when(roleService.hasAnySuperAdmin(eq(asSet(100L)))).thenReturn(true); + // mock 数据 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); + userRoleMapper.insert(userRoleDO01); + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); + userRoleMapper.insert(roleMenuDO02); // 调用 - boolean has = permissionService.hasAnyPermissions(userId, roles); + Set result = permissionService.getUserRoleIdListByUserId(userId); // 断言 - assertTrue(has); + assertEquals(asSet(10L, 20L), result); } @Test - public void testHasAnyPermissions_normal() { + public void testGetUserRoleIdListByUserIdFromCache() { // 准备参数 Long userId = 1L; - String[] roles = new String[]{"system:user:query", "system:user:create"}; - // mock 用户与角色的缓存 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(100L)).build()); - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleFromCache(eq(100L))).thenReturn(role); - // mock 其它方法 - MenuDO menu = randomPojo(MenuDO.class, o -> o.setId(1000L)); - when(menuService.getMenuListByPermissionFromCache(eq("system:user:create"))).thenReturn(singletonList(menu)); - permissionService.setMenuRoleCache(ImmutableMultimap.builder().put(1000L, 100L).build()); - + // mock 数据 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); + userRoleMapper.insert(userRoleDO01); + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); + userRoleMapper.insert(roleMenuDO02); // 调用 - boolean has = permissionService.hasAnyPermissions(userId, roles); + Set result = permissionService.getUserRoleIdListByUserIdFromCache(userId); // 断言 - assertTrue(has); + assertEquals(asSet(10L, 20L), result); } @Test - public void testHasAnyRoles_superAdmin() { + public void testGetUserRoleIdsFromCache() { // 准备参数 Long userId = 1L; - String[] roles = new String[]{"yunai", "tudou"}; - // mock 用户与角色的缓存 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(100L)).build()); - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleFromCache(eq(100L))).thenReturn(role); - // mock 其它方法 - when(roleService.hasAnySuperAdmin(eq(asSet(100L)))).thenReturn(true); + // mock 数据 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); + userRoleMapper.insert(userRoleDO01); + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); + userRoleMapper.insert(roleMenuDO02); // 调用 - boolean has = permissionService.hasAnyRoles(userId, roles); + Set result = permissionService.getUserRoleIdListByUserIdFromCache(userId); // 断言 - assertTrue(has); + assertEquals(asSet(10L, 20L), result); } @Test - public void testHasAnyRoles_normal() { + public void testGetUserRoleIdListByRoleId() { // 准备参数 - Long userId = 1L; - String[] roles = new String[]{"yunai", "tudou"}; - // mock 用户与角色的缓存 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(100L)).build()); - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L).setCode("yunai") - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleFromCache(eq(100L))).thenReturn(role); - // mock 其它方法 - when(roleService.getRoleListFromCache(eq(asSet(100L)))).thenReturn(singletonList(role)); + Collection roleIds = asSet(10L, 20L); + // mock 数据 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); + userRoleMapper.insert(userRoleDO01); + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(2L).setRoleId(20L)); + userRoleMapper.insert(roleMenuDO02); // 调用 - boolean has = permissionService.hasAnyRoles(userId, roles); + Set result = permissionService.getUserRoleIdListByRoleId(roleIds); // 断言 - assertTrue(has); + assertEquals(asSet(1L, 2L), result); + } + + @Test + public void testGetEnableUserRoleListByUserIdFromCache() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); + + // 准备参数 + Long userId = 1L; + // mock 用户登录的角色 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(200L)); + RoleDO role01 = randomPojo(RoleDO.class, o -> o.setId(100L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + RoleDO role02 = randomPojo(RoleDO.class, o -> o.setId(200L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(asSet(100L, 200L)))) + .thenReturn(toList(role01, role02)); + + // 调用 + List result = permissionService.getEnableUserRoleListByUserIdFromCache(userId); + // 断言 + assertEquals(1, result.size()); + assertPojoEquals(role01, result.get(0)); + } + } + + // ========== 用户-部门的相关方法 ========== + + @Test + public void testAssignRoleDataScope() { + // 准备参数 + Long roleId = 1L; + Integer dataScope = 2; + Set dataScopeDeptIds = asSet(10L, 20L); + + // 调用 + permissionService.assignRoleDataScope(roleId, dataScope, dataScopeDeptIds); + // 断言 + verify(roleService).updateRoleDataScope(eq(roleId), eq(dataScope), eq(dataScopeDeptIds)); } @Test public void testGetDeptDataPermission_All() { - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(2L)).build()); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO)); - when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertTrue(result.getAll()); - assertFalse(result.getSelf()); - assertTrue(CollUtil.isEmpty(result.getDeptIds())); + // 准备参数 + Long userId = 1L; + // mock 用户的角色编号 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); + // mock 获得用户的角色 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope()) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); + + // 调用 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); + // 断言 + assertTrue(result.getAll()); + assertFalse(result.getSelf()); + assertTrue(CollUtil.isEmpty(result.getDeptIds())); + } } @Test public void testGetDeptDataPermission_DeptCustom() { - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(2L)).build()); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO)); - when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO); - // mock 部门的返回 - when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的,看看会不会重复调用 + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertFalse(result.getAll()); - assertFalse(result.getSelf()); - assertEquals(roleDO.getDataScopeDeptIds().size() + 1, result.getDeptIds().size()); - assertTrue(CollUtil.containsAll(result.getDeptIds(), roleDO.getDataScopeDeptIds())); - assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); + // 准备参数 + Long userId = 1L; + // mock 用户的角色编号 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); + // mock 获得用户的角色 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope()) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); + // mock 部门的返回 + when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), + null, null); // 最后返回 null 的目的,看看会不会重复调用 + + // 调用 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); + // 断言 + assertFalse(result.getAll()); + assertFalse(result.getSelf()); + assertEquals(roleDO.getDataScopeDeptIds().size() + 1, result.getDeptIds().size()); + assertTrue(CollUtil.containsAll(result.getDeptIds(), roleDO.getDataScopeDeptIds())); + assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); + } } @Test public void testGetDeptDataPermission_DeptOnly() { - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(2L)).build()); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO)); - when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO); - // mock 部门的返回 - when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的,看看会不会重复调用 + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertFalse(result.getAll()); - assertFalse(result.getSelf()); - assertEquals(1, result.getDeptIds().size()); - assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); + // 准备参数 + Long userId = 1L; + // mock 用户的角色编号 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); + // mock 获得用户的角色 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope()) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); + // mock 部门的返回 + when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), + null, null); // 最后返回 null 的目的,看看会不会重复调用 + + // 调用 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); + // 断言 + assertFalse(result.getAll()); + assertFalse(result.getSelf()); + assertEquals(1, result.getDeptIds().size()); + assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); + } } @Test public void testGetDeptDataPermission_DeptAndChild() { - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(2L)).build()); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO)); - when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO); - // mock 部门的返回 - when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的,看看会不会重复调用 - // mock 方法(部门) - DeptDO deptDO = randomPojo(DeptDO.class); - when(deptService.getDeptListByParentIdFromCache(eq(3L), eq(true))) - .thenReturn(singletonList(deptDO)); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertFalse(result.getAll()); - assertFalse(result.getSelf()); - assertEquals(2, result.getDeptIds().size()); - assertTrue(CollUtil.contains(result.getDeptIds(), deptDO.getId())); - assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); + // 准备参数 + Long userId = 1L; + // mock 用户的角色编号 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); + // mock 获得用户的角色 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope()) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); + // mock 部门的返回 + when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), + null, null); // 最后返回 null 的目的,看看会不会重复调用 + // mock 方法(部门) + DeptDO deptDO = randomPojo(DeptDO.class); + when(deptService.getChildDeptIdListFromCache(eq(3L))).thenReturn(singleton(deptDO.getId())); + + // 调用 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); + // 断言 + assertFalse(result.getAll()); + assertFalse(result.getSelf()); + assertEquals(2, result.getDeptIds().size()); + assertTrue(CollUtil.contains(result.getDeptIds(), deptDO.getId())); + assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); + } } @Test public void testGetDeptDataPermission_Self() { - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(2L)).build()); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO)); - when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertFalse(result.getAll()); - assertTrue(result.getSelf()); - assertTrue(CollUtil.isEmpty(result.getDeptIds())); + // 准备参数 + Long userId = 1L; + // mock 用户的角色编号 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); + // mock 获得用户的角色 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope()) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); + + // 调用 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); + // 断言 + assertFalse(result.getAll()); + assertTrue(result.getSelf()); + assertTrue(CollUtil.isEmpty(result.getDeptIds())); + } } } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java index a114556c4..5c7e96398 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.system.service.permission; +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.test.core.ut.BaseDbUnitTest; @@ -11,15 +12,14 @@ 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.enums.permission.DataScopeEnum; import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; -import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.Set; import static cn.hutool.core.util.RandomUtil.randomEle; @@ -33,6 +33,8 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.verify; @Import(RoleServiceImpl.class) @@ -46,26 +48,9 @@ public class RoleServiceImplTest extends BaseDbUnitTest { @MockBean private PermissionService permissionService; - @MockBean - private RoleProducer roleProducer; @Test - public void testInitLocalCache() { - RoleDO roleDO1 = randomPojo(RoleDO.class); - roleMapper.insert(roleDO1); - RoleDO roleDO2 = randomPojo(RoleDO.class); - roleMapper.insert(roleDO2); - - // 调用 - roleService.initLocalCache(); - // 断言 roleCache 缓存 - Map roleCache = roleService.getRoleCache(); - assertPojoEquals(roleDO1, roleCache.get(roleDO1.getId())); - assertPojoEquals(roleDO2, roleCache.get(roleDO2.getId())); - } - - @Test - public void testCreateRole_success() { + public void testCreateRole() { // 准备参数 RoleCreateReqVO reqVO = randomPojo(RoleCreateReqVO.class); @@ -77,12 +62,10 @@ public class RoleServiceImplTest extends BaseDbUnitTest { assertEquals(RoleTypeEnum.CUSTOM.getType(), roleDO.getType()); assertEquals(CommonStatusEnum.ENABLE.getStatus(), roleDO.getStatus()); assertEquals(DataScopeEnum.ALL.getScope(), roleDO.getDataScope()); - // verify 发送刷新消息 - verify(roleProducer).sendRoleRefreshMessage(); } @Test - public void testUpdateRole_success() { + public void testUpdateRole() { // mock 数据 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); roleMapper.insert(roleDO); @@ -95,12 +78,10 @@ public class RoleServiceImplTest extends BaseDbUnitTest { // 断言 RoleDO newRoleDO = roleMapper.selectById(id); assertPojoEquals(reqVO, newRoleDO); - // verify 发送刷新消息 - verify(roleProducer).sendRoleRefreshMessage(); } @Test - public void testUpdateRoleStatus_success() { + public void testUpdateRoleStatus() { // mock 数据 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()) .setType(RoleTypeEnum.CUSTOM.getType())); @@ -114,12 +95,10 @@ public class RoleServiceImplTest extends BaseDbUnitTest { // 断言 RoleDO dbRoleDO = roleMapper.selectById(roleId); assertEquals(CommonStatusEnum.DISABLE.getStatus(), dbRoleDO.getStatus()); - // verify 发送刷新消息 - verify(roleProducer).sendRoleRefreshMessage(); } @Test - public void testUpdateRoleDataScope_success() { + public void testUpdateRoleDataScope() { // mock 数据 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); roleMapper.insert(roleDO); @@ -134,12 +113,10 @@ public class RoleServiceImplTest extends BaseDbUnitTest { RoleDO dbRoleDO = roleMapper.selectById(id); assertEquals(dataScope, dbRoleDO.getDataScope()); assertEquals(dataScopeRoleIds, dbRoleDO.getDataScopeDeptIds()); - // verify 发送刷新消息 - verify(roleProducer).sendRoleRefreshMessage(); } @Test - public void testDeleteRole_success() { + public void testDeleteRole() { // mock 数据 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); roleMapper.insert(roleDO); @@ -152,23 +129,65 @@ public class RoleServiceImplTest extends BaseDbUnitTest { assertNull(roleMapper.selectById(id)); // verify 删除相关数据 verify(permissionService).processRoleDeleted(id); - // verify 发送刷新消息 - verify(roleProducer).sendRoleRefreshMessage(); } @Test - public void testGetRoleFromCache() { - // mock 数据(缓存) + public void testValidateRoleDuplicate_success() { + // 调用,不会抛异常 + roleService.validateRoleDuplicate(randomString(), randomString(), null); + } + + @Test + public void testValidateRoleDuplicate_nameDuplicate() { + // mock 数据 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setName("role_name")); + roleMapper.insert(roleDO); + // 准备参数 + String name = "role_name"; + + // 调用,并断言异常 + assertServiceException(() -> roleService.validateRoleDuplicate(name, randomString(), null), + ROLE_NAME_DUPLICATE, name); + } + + @Test + public void testValidateRoleDuplicate_codeDuplicate() { + // mock 数据 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setCode("code")); + roleMapper.insert(roleDO); + // 准备参数 + String code = "code"; + + // 调用,并断言异常 + assertServiceException(() -> roleService.validateRoleDuplicate(randomString(), code, null), + ROLE_CODE_DUPLICATE, code); + } + + @Test + public void testValidateUpdateRole_success() { RoleDO roleDO = randomPojo(RoleDO.class); roleMapper.insert(roleDO); - roleService.initLocalCache(); - // 参数准备 + // 准备参数 Long id = roleDO.getId(); - // 调用 - RoleDO dbRoleDO = roleService.getRoleFromCache(id); - // 断言 - assertPojoEquals(roleDO, dbRoleDO); + // 调用,无异常 + roleService.validateRoleForUpdate(id); + } + + @Test + public void testValidateUpdateRole_roleIdNotExist() { + assertServiceException(() -> roleService.validateRoleForUpdate(randomLongId()), ROLE_NOT_EXISTS); + } + + @Test + public void testValidateUpdateRole_systemRoleCanNotBeUpdate() { + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.SYSTEM.getType())); + roleMapper.insert(roleDO); + // 准备参数 + Long id = roleDO.getId(); + + assertServiceException(() -> roleService.validateRoleForUpdate(id), + ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE); } @Test @@ -186,22 +205,21 @@ public class RoleServiceImplTest extends BaseDbUnitTest { } @Test - public void testGetRoleListByStatus_statusNotEmpty() { - // mock 数据 - RoleDO dbRole = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - roleMapper.insert(dbRole); - // 测试 status 不匹配 - roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + public void testGetRoleFromCache() { + // mock 数据(缓存) + RoleDO roleDO = randomPojo(RoleDO.class); + roleMapper.insert(roleDO); + // 参数准备 + Long id = roleDO.getId(); // 调用 - List list = roleService.getRoleListByStatus(singleton(CommonStatusEnum.ENABLE.getStatus())); + RoleDO dbRoleDO = roleService.getRoleFromCache(id); // 断言 - assertEquals(1, list.size()); - assertPojoEquals(dbRole, list.get(0)); + assertPojoEquals(roleDO, dbRoleDO); } @Test - public void testGetRoleListByStatus_statusEmpty() { + public void testGetRoleListByStatus() { // mock 数据 RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); roleMapper.insert(dbRole01); @@ -209,29 +227,33 @@ public class RoleServiceImplTest extends BaseDbUnitTest { roleMapper.insert(dbRole02); // 调用 - List list = roleService.getRoleListByStatus(null); + List list = roleService.getRoleListByStatus( + singleton(CommonStatusEnum.ENABLE.getStatus())); // 断言 - assertEquals(2, list.size()); + assertEquals(1, list.size()); assertPojoEquals(dbRole01, list.get(0)); - assertPojoEquals(dbRole02, list.get(1)); } @Test public void testGetRoleListFromCache() { - // mock 数据 - RoleDO dbRole = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - roleMapper.insert(dbRole); - // 测试 id 不匹配 - roleMapper.insert(cloneIgnoreId(dbRole, o -> {})); - roleService.initLocalCache(); - // 准备参数 - Collection ids = singleton(dbRole.getId()); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class))) + .thenReturn(roleService); - // 调用 - List list = roleService.getRoleListFromCache(ids); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(dbRole, list.get(0)); + // mock 数据 + RoleDO dbRole = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); + roleMapper.insert(dbRole); + // 测试 id 不匹配 + roleMapper.insert(cloneIgnoreId(dbRole, o -> {})); + // 准备参数 + Collection ids = singleton(dbRole.getId()); + + // 调用 + List list = roleService.getRoleListFromCache(ids); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbRole, list.get(0)); + } } @Test @@ -296,72 +318,37 @@ public class RoleServiceImplTest extends BaseDbUnitTest { } @Test - public void testHasAnySuperAdmin() { - // 是超级 - assertTrue(roleService.hasAnySuperAdmin(singletonList(randomPojo(RoleDO.class, - o -> o.setCode("super_admin"))))); - // 非超级 - assertFalse(roleService.hasAnySuperAdmin(singletonList(randomPojo(RoleDO.class, - o -> o.setCode("tenant_admin"))))); + public void testHasAnySuperAdmin_true() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class))) + .thenReturn(roleService); + + // mock 数据 + RoleDO dbRole = randomPojo(RoleDO.class).setCode("super_admin"); + roleMapper.insert(dbRole); + // 准备参数 + Long id = dbRole.getId(); + + // 调用,并调用 + assertTrue(roleService.hasAnySuperAdmin(singletonList(id))); + } } @Test - public void testValidateRoleDuplicate_success() { - // 调用,不会抛异常 - roleService.validateRoleDuplicate(randomString(), randomString(), null); - } + public void testHasAnySuperAdmin_false() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class))) + .thenReturn(roleService); - @Test - public void testValidateRoleDuplicate_nameDuplicate() { - // mock 数据 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setName("role_name")); - roleMapper.insert(roleDO); - // 准备参数 - String name = "role_name"; + // mock 数据 + RoleDO dbRole = randomPojo(RoleDO.class).setCode("tenant_admin"); + roleMapper.insert(dbRole); + // 准备参数 + Long id = dbRole.getId(); - // 调用,并断言异常 - assertServiceException(() -> roleService.validateRoleDuplicate(name, randomString(), null), - ROLE_NAME_DUPLICATE, name); - } - - @Test - public void testValidateRoleDuplicate_codeDuplicate() { - // mock 数据 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setCode("code")); - roleMapper.insert(roleDO); - // 准备参数 - String code = "code"; - - // 调用,并断言异常 - assertServiceException(() -> roleService.validateRoleDuplicate(randomString(), code, null), - ROLE_CODE_DUPLICATE, code); - } - - @Test - public void testValidateUpdateRole_success() { - RoleDO roleDO = randomPojo(RoleDO.class); - roleMapper.insert(roleDO); - // 准备参数 - Long id = roleDO.getId(); - - // 调用,无异常 - roleService.validateRoleForUpdate(id); - } - - @Test - public void testValidateUpdateRole_roleIdNotExist() { - assertServiceException(() -> roleService.validateRoleForUpdate(randomLongId()), ROLE_NOT_EXISTS); - } - - @Test - public void testValidateUpdateRole_systemRoleCanNotBeUpdate() { - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.SYSTEM.getType())); - roleMapper.insert(roleDO); - // 准备参数 - Long id = roleDO.getId(); - - assertServiceException(() -> roleService.validateRoleForUpdate(id), - ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE); + // 调用,并调用 + assertFalse(roleService.hasAnySuperAdmin(singletonList(id))); + } } @Test diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java index 3b9b577a2..5aaf43b30 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java @@ -18,7 +18,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper; import cn.iocoder.yudao.module.system.enums.sms.SmsTemplateTypeEnum; -import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer; import com.google.common.collect.Lists; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; @@ -26,7 +25,6 @@ import org.springframework.context.annotation.Import; import javax.annotation.Resource; import java.util.List; -import java.util.Map; import java.util.function.Consumer; import static cn.hutool.core.util.RandomUtil.randomEle; @@ -55,25 +53,6 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { private SmsClientFactory smsClientFactory; @MockBean private SmsClient smsClient; - @MockBean - private SmsProducer smsProducer; - - @Test - void testInitLocalCache() { - // mock 数据 - SmsTemplateDO smsTemplate01 = randomSmsTemplateDO(); - smsTemplateMapper.insert(smsTemplate01); - SmsTemplateDO smsTemplate02 = randomSmsTemplateDO(); - smsTemplateMapper.insert(smsTemplate02); - - // 调用 - smsTemplateService.initLocalCache(); - // 断言 deptCache 缓存 - Map smsTemplateCache = smsTemplateService.getSmsTemplateCache(); - assertEquals(2, smsTemplateCache.size()); - assertPojoEquals(smsTemplate01, smsTemplateCache.get(smsTemplate01.getCode())); - assertPojoEquals(smsTemplate02, smsTemplateCache.get(smsTemplate02.getCode())); - } @Test public void testParseTemplateContentParams() { @@ -116,8 +95,6 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { assertPojoEquals(reqVO, smsTemplate); assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams()); assertEquals(channelDO.getCode(), smsTemplate.getChannelCode()); - // 校验调用 - verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage(); } @Test @@ -151,8 +128,6 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { assertPojoEquals(reqVO, smsTemplate); assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams()); assertEquals(channelDO.getCode(), smsTemplate.getChannelCode()); - // 校验调用 - verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage(); } @Test @@ -174,10 +149,8 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { // 调用 smsTemplateService.deleteSmsTemplate(id); - // 校验数据不存在了 - assertNull(smsTemplateMapper.selectById(id)); - // 校验调用 - verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage(); + // 校验数据不存在了 + assertNull(smsTemplateMapper.selectById(id)); } @Test @@ -191,47 +164,47 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { @Test public void testGetSmsTemplatePage() { - // mock 数据 - SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> { // 等会查询到 - o.setType(SmsTemplateTypeEnum.PROMOTION.getType()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCode("tudou"); - o.setContent("芋道源码"); - o.setApiTemplateId("yunai"); - o.setChannelId(1L); - o.setCreateTime(buildTime(2021, 11, 11)); - }); - smsTemplateMapper.insert(dbSmsTemplate); - // 测试 type 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setType(SmsTemplateTypeEnum.VERIFICATION_CODE.getType()))); - // 测试 status 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 code 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCode("yuanma"))); - // 测试 content 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setContent("源码"))); - // 测试 apiTemplateId 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setApiTemplateId("nai"))); - // 测试 channelId 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setChannelId(2L))); - // 测试 createTime 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCreateTime(buildTime(2021, 12, 12)))); - // 准备参数 - SmsTemplatePageReqVO reqVO = new SmsTemplatePageReqVO(); - reqVO.setType(SmsTemplateTypeEnum.PROMOTION.getType()); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCode("tu"); - reqVO.setContent("芋道"); - reqVO.setApiTemplateId("yu"); - reqVO.setChannelId(1L); - reqVO.setCreateTime(buildBetweenTime(2021, 11, 1, 2021, 12, 1)); + // mock 数据 + SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> { // 等会查询到 + o.setType(SmsTemplateTypeEnum.PROMOTION.getType()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCode("tudou"); + o.setContent("芋道源码"); + o.setApiTemplateId("yunai"); + o.setChannelId(1L); + o.setCreateTime(buildTime(2021, 11, 11)); + }); + smsTemplateMapper.insert(dbSmsTemplate); + // 测试 type 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setType(SmsTemplateTypeEnum.VERIFICATION_CODE.getType()))); + // 测试 status 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 code 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCode("yuanma"))); + // 测试 content 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setContent("源码"))); + // 测试 apiTemplateId 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setApiTemplateId("nai"))); + // 测试 channelId 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setChannelId(2L))); + // 测试 createTime 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCreateTime(buildTime(2021, 12, 12)))); + // 准备参数 + SmsTemplatePageReqVO reqVO = new SmsTemplatePageReqVO(); + reqVO.setType(SmsTemplateTypeEnum.PROMOTION.getType()); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCode("tu"); + reqVO.setContent("芋道"); + reqVO.setApiTemplateId("yu"); + reqVO.setChannelId(1L); + reqVO.setCreateTime(buildBetweenTime(2021, 11, 1, 2021, 12, 1)); - // 调用 - PageResult pageResult = smsTemplateService.getSmsTemplatePage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbSmsTemplate, pageResult.getList().get(0)); + // 调用 + PageResult pageResult = smsTemplateService.getSmsTemplatePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbSmsTemplate, pageResult.getList().get(0)); } @Test @@ -271,11 +244,11 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { reqVO.setChannelId(1L); reqVO.setCreateTime(buildBetweenTime(2021, 11, 1, 2021, 12, 1)); - // 调用 - List list = smsTemplateService.getSmsTemplateList(reqVO); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(dbSmsTemplate, list.get(0)); + // 调用 + List list = smsTemplateService.getSmsTemplateList(reqVO); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbSmsTemplate, list.get(0)); } @Test diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java index 38357b280..1fb9c4fca 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java @@ -199,7 +199,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest { role101.setTenantId(dbTenant.getId()); when(roleService.getRoleListByStatus(isNull())).thenReturn(asList(role100, role101)); // mock 每个角色的权限 - when(permissionService.getRoleMenuIds(eq(101L))).thenReturn(asSet(201L, 202L)); + when(permissionService.getRoleMenuListByRoleId(eq(101L))).thenReturn(asSet(201L, 202L)); // 调用 tenantService.updateTenant(reqVO); @@ -454,7 +454,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest { TenantContextHolder.setTenantId(dbTenant.getId()); // mock 菜单 when(menuService.getMenuList()).thenReturn(Arrays.asList(randomPojo(MenuDO.class, o -> o.setId(100L)), - randomPojo(MenuDO.class, o -> o.setId(101L)))); + randomPojo(MenuDO.class, o -> o.setId(101L)))); // 调用 tenantService.handleTenantMenu(handler); diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java index a5e0183a4..acc4357cc 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java @@ -345,7 +345,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { reqVO.setDeptId(1L); // 其中,1L 是 2L 的父部门 // mock 方法 List deptList = newArrayList(randomPojo(DeptDO.class, o -> o.setId(2L))); - when(deptService.getDeptListByParentIdFromCache(eq(reqVO.getDeptId()), eq(true))).thenReturn(deptList); + when(deptService.getChildDeptList(eq(reqVO.getDeptId()))).thenReturn(deptList); // 调用 PageResult pageResult = userService.getUserPage(reqVO); @@ -368,7 +368,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { reqVO.setDeptId(1L); // 其中,1L 是 2L 的父部门 // mock 方法 List deptList = newArrayList(randomPojo(DeptDO.class, o -> o.setId(2L))); - when(deptService.getDeptListByParentIdFromCache(eq(reqVO.getDeptId()), eq(true))).thenReturn(deptList); + when(deptService.getChildDeptList(eq(reqVO.getDeptId()))).thenReturn(deptList); // 调用 List list = userService.getUserList(reqVO);