Commit 7a49e45b4b97a805d6b8c57292e2305f4e980323

Authored by 芋道源码
Committed by Gitee
2 parents 418ae829 24a406e4

!548 Redis 缓存替代本地缓存,降低学习成本

Merge pull request !548 from 芋道源码/master-redis
Showing 121 changed files with 1904 additions and 3662 deletions

Too many changes.

To preserve performance only 100 of 121 files are displayed.

yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisKeyDefine.java deleted
1 -package cn.iocoder.yudao.framework.tenant.core.redis;  
2 -  
3 -import cn.hutool.core.util.ArrayUtil;  
4 -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;  
5 -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;  
6 -  
7 -import java.time.Duration;  
8 -  
9 -/**  
10 - * 多租户拓展的 RedisKeyDefine 实现类  
11 - *  
12 - * 由于 Redis 不同于 MySQL 有 column 字段,无法通过类似 WHERE tenant_id = ? 的方式过滤  
13 - * 所以需要通过在 Redis Key 上增加后缀的方式,进行租户之间的隔离。具体的步骤是:  
14 - * 1. 假设 Redis Key 是 user:%d,示例是 user:1;对应到多租户的 Redis Key 是 user:%d:%d,  
15 - * 2. 在 Redis DAO 中,需要使用 {@link #formatKey(Object...)} 方法,进行 Redis Key 的格式化  
16 - *  
17 - * 注意,大多数情况下,并不用使用 TenantRedisKeyDefine 实现。主要的使用场景,还是 Redis Key 可能存在冲突的情况。  
18 - * 例如说,租户 1 和 2 都有一个手机号作为 Key,则他们会存在冲突的问题  
19 - *  
20 - * @author 芋道源码  
21 - */  
22 -public class TenantRedisKeyDefine extends RedisKeyDefine {  
23 -  
24 - /**  
25 - * 多租户的 KEY 模板  
26 - */  
27 - private static final String KEY_TEMPLATE_SUFFIX = ":%d";  
28 -  
29 - public TenantRedisKeyDefine(String memo, String keyTemplate, KeyTypeEnum keyType, Class<?> valueType, Duration timeout) {  
30 - super(memo, buildKeyTemplate(keyTemplate), keyType, valueType, timeout);  
31 - }  
32 -  
33 - public TenantRedisKeyDefine(String memo, String keyTemplate, KeyTypeEnum keyType, Class<?> valueType, TimeoutTypeEnum timeoutType) {  
34 - super(memo, buildKeyTemplate(keyTemplate), keyType, valueType, timeoutType);  
35 - }  
36 -  
37 - private static String buildKeyTemplate(String keyTemplate) {  
38 - return keyTemplate + KEY_TEMPLATE_SUFFIX;  
39 - }  
40 -  
41 - @Override  
42 - public String formatKey(Object... args) {  
43 - args = ArrayUtil.append(args, TenantContextHolder.getRequiredTenantId());  
44 - return super.formatKey(args);  
45 - }  
46 -  
47 -}  
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/test/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisKeyDefineTest.java deleted
1 -package cn.iocoder.yudao.framework.tenant.core.redis;  
2 -  
3 -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;  
4 -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;  
5 -import org.junit.jupiter.api.Test;  
6 -  
7 -import static org.junit.jupiter.api.Assertions.assertEquals;  
8 -  
9 -class TenantRedisKeyDefineTest {  
10 -  
11 - @Test  
12 - public void testFormatKey() {  
13 - Long tenantId = 30L;  
14 - TenantContextHolder.setTenantId(tenantId);  
15 - // 准备参数  
16 - TenantRedisKeyDefine define = new TenantRedisKeyDefine("", "user:%d:%d", RedisKeyDefine.KeyTypeEnum.HASH,  
17 - Object.class, RedisKeyDefine.TimeoutTypeEnum.FIXED);  
18 - Long userId = 10L;  
19 - Integer userType = 1;  
20 -  
21 - // 调用  
22 - String key = define.formatKey(userId, userType);  
23 - // 断言  
24 - assertEquals("user:10:1:30", key);  
25 - }  
26 -  
27 -}  
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/config/YudaoCaptchaConfiguration.java
1 package cn.iocoder.yudao.framework.captcha.config; 1 package cn.iocoder.yudao.framework.captcha.config;
2 2
3 -import cn.hutool.core.util.ClassUtil;  
4 -import cn.iocoder.yudao.framework.captcha.core.enums.CaptchaRedisKeyConstants;  
5 import cn.iocoder.yudao.framework.captcha.core.service.RedisCaptchaServiceImpl; 3 import cn.iocoder.yudao.framework.captcha.core.service.RedisCaptchaServiceImpl;
6 import com.xingyuv.captcha.properties.AjCaptchaProperties; 4 import com.xingyuv.captcha.properties.AjCaptchaProperties;
7 import com.xingyuv.captcha.service.CaptchaCacheService; 5 import com.xingyuv.captcha.service.CaptchaCacheService;
@@ -15,12 +13,6 @@ import javax.annotation.Resource; @@ -15,12 +13,6 @@ import javax.annotation.Resource;
15 @AutoConfiguration 13 @AutoConfiguration
16 public class YudaoCaptchaConfiguration { 14 public class YudaoCaptchaConfiguration {
17 15
18 - static {  
19 - // 手动加载 Lock4jRedisKeyConstants 类,因为它不会被使用到  
20 - // 如果不加载,会导致 Redis 监控,看到它的 Redis Key 枚举  
21 - ClassUtil.loadClass(CaptchaRedisKeyConstants.class.getName());  
22 - }  
23 -  
24 @Resource 16 @Resource
25 private StringRedisTemplate stringRedisTemplate; 17 private StringRedisTemplate stringRedisTemplate;
26 18
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/core/enums/CaptchaRedisKeyConstants.java
1 package cn.iocoder.yudao.framework.captcha.core.enums; 1 package cn.iocoder.yudao.framework.captcha.core.enums;
2 2
3 -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;  
4 -import com.xingyuv.captcha.model.vo.PointVO;  
5 -  
6 -import java.time.Duration;  
7 -  
8 -import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING;  
9 -  
10 /** 3 /**
11 * 验证码 Redis Key 枚举类 4 * 验证码 Redis Key 枚举类
12 * 5 *
@@ -14,12 +7,22 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.S @@ -14,12 +7,22 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.S
14 */ 7 */
15 public interface CaptchaRedisKeyConstants { 8 public interface CaptchaRedisKeyConstants {
16 9
17 - RedisKeyDefine AJ_CAPTCHA_REQ_LIMIT = new RedisKeyDefine("验证码的请求限流",  
18 - "AJ.CAPTCHA.REQ.LIMIT-%s-%s",  
19 - STRING, Integer.class, Duration.ofSeconds(60)); // 例如说:验证失败 5 次,get 接口锁定 10 + /**
  11 + * 验证码的请求限流
  12 + *
  13 + * KEY 格式:AJ.CAPTCHA.REQ.LIMIT-%s-%s
  14 + * VALUE 数据类型:String // 例如说:验证失败 5 次,get 接口锁定
  15 + * 过期时间:60 秒
  16 + */
  17 + String AJ_CAPTCHA_REQ_LIMIT = "AJ.CAPTCHA.REQ.LIMIT-%s-%s";
20 18
21 - RedisKeyDefine AJ_CAPTCHA_RUNNING = new RedisKeyDefine("验证码的坐标",  
22 - "RUNNING:CAPTCHA:%s", // AbstractCaptchaService.REDIS_CAPTCHA_KEY  
23 - STRING, PointVO.class, Duration.ofSeconds(120)); // {"secretKey":"PP1w2Frr2KEejD2m","x":162,"y":5} 19 + /**
  20 + * 验证码的坐标
  21 + *
  22 + * KEY 格式:RUNNING:CAPTCHA:%s // AbstractCaptchaService.REDIS_CAPTCHA_KEY
  23 + * VALUE 数据类型:String // PointVO.class {"secretKey":"PP1w2Frr2KEejD2m","x":162,"y":5}
  24 + * 过期时间:120 秒
  25 + */
  26 + String AJ_CAPTCHA_RUNNING = "RUNNING:CAPTCHA:%s";
24 27
25 } 28 }
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
@@ -106,6 +106,7 @@ public interface BaseMapperX&lt;T&gt; extends BaseMapper&lt;T&gt; { @@ -106,6 +106,7 @@ public interface BaseMapperX&lt;T&gt; extends BaseMapper&lt;T&gt; {
106 default void updateBatch(Collection<T> entities) { 106 default void updateBatch(Collection<T> entities) {
107 Db.updateBatchById(entities); 107 Db.updateBatchById(entities);
108 } 108 }
  109 +
109 default void updateBatch(Collection<T> entities, int size) { 110 default void updateBatch(Collection<T> entities, int size) {
110 Db.updateBatchById(entities, size); 111 Db.updateBatchById(entities, size);
111 } 112 }
yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/redis/IdempotentRedisDAO.java
1 package cn.iocoder.yudao.framework.idempotent.core.redis; 1 package cn.iocoder.yudao.framework.idempotent.core.redis;
2 2
3 -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;  
4 import lombok.AllArgsConstructor; 3 import lombok.AllArgsConstructor;
5 import org.springframework.data.redis.core.StringRedisTemplate; 4 import org.springframework.data.redis.core.StringRedisTemplate;
6 5
7 import java.util.concurrent.TimeUnit; 6 import java.util.concurrent.TimeUnit;
8 7
9 -import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING;  
10 -  
11 /** 8 /**
12 * 幂等 Redis DAO 9 * 幂等 Redis DAO
13 * 10 *
@@ -16,9 +13,14 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.S @@ -16,9 +13,14 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.S
16 @AllArgsConstructor 13 @AllArgsConstructor
17 public class IdempotentRedisDAO { 14 public class IdempotentRedisDAO {
18 15
19 - private static final RedisKeyDefine IDEMPOTENT = new RedisKeyDefine("幂等操作",  
20 - "idempotent:%s", // 参数为 uuid  
21 - STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); 16 + /**
  17 + * 幂等操作
  18 + *
  19 + * KEY 格式:idempotent:%s // 参数为 uuid
  20 + * VALUE 格式:String
  21 + * 过期时间:不固定
  22 + */
  23 + private static final String IDEMPOTENT = "idempotent:%s";
22 24
23 private final StringRedisTemplate redisTemplate; 25 private final StringRedisTemplate redisTemplate;
24 26
@@ -28,7 +30,7 @@ public class IdempotentRedisDAO { @@ -28,7 +30,7 @@ public class IdempotentRedisDAO {
28 } 30 }
29 31
30 private static String formatKey(String key) { 32 private static String formatKey(String key) {
31 - return String.format(IDEMPOTENT.getKeyTemplate(), key); 33 + return String.format(IDEMPOTENT, key);
32 } 34 }
33 35
34 } 36 }
yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/config/YudaoLock4jConfiguration.java
1 package cn.iocoder.yudao.framework.lock4j.config; 1 package cn.iocoder.yudao.framework.lock4j.config;
2 2
3 -import cn.hutool.core.util.ClassUtil;  
4 -import com.baomidou.lock.spring.boot.autoconfigure.LockAutoConfiguration;  
5 import cn.iocoder.yudao.framework.lock4j.core.DefaultLockFailureStrategy; 3 import cn.iocoder.yudao.framework.lock4j.core.DefaultLockFailureStrategy;
6 -import cn.iocoder.yudao.framework.lock4j.core.Lock4jRedisKeyConstants; 4 +import com.baomidou.lock.spring.boot.autoconfigure.LockAutoConfiguration;
7 import org.springframework.boot.autoconfigure.AutoConfiguration; 5 import org.springframework.boot.autoconfigure.AutoConfiguration;
8 import org.springframework.context.annotation.Bean; 6 import org.springframework.context.annotation.Bean;
9 7
10 @AutoConfiguration(before = LockAutoConfiguration.class) 8 @AutoConfiguration(before = LockAutoConfiguration.class)
11 public class YudaoLock4jConfiguration { 9 public class YudaoLock4jConfiguration {
12 10
13 - static {  
14 - // 手动加载 Lock4jRedisKeyConstants 类,因为它不会被使用到  
15 - // 如果不加载,会导致 Redis 监控,看到它的 Redis Key 枚举  
16 - ClassUtil.loadClass(Lock4jRedisKeyConstants.class.getName());  
17 - }  
18 -  
19 @Bean 11 @Bean
20 public DefaultLockFailureStrategy lockFailureStrategy() { 12 public DefaultLockFailureStrategy lockFailureStrategy() {
21 return new DefaultLockFailureStrategy(); 13 return new DefaultLockFailureStrategy();
yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/core/Lock4jRedisKeyConstants.java
1 package cn.iocoder.yudao.framework.lock4j.core; 1 package cn.iocoder.yudao.framework.lock4j.core;
2 2
3 -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;  
4 -import org.redisson.api.RLock;  
5 -  
6 -import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.HASH;  
7 -  
8 /** 3 /**
9 * Lock4j Redis Key 枚举类 4 * Lock4j Redis Key 枚举类
10 * 5 *
@@ -12,8 +7,13 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.H @@ -12,8 +7,13 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.H
12 */ 7 */
13 public interface Lock4jRedisKeyConstants { 8 public interface Lock4jRedisKeyConstants {
14 9
15 - RedisKeyDefine LOCK4J = new RedisKeyDefine("分布式锁",  
16 - "lock4j:%s", // 参数来自 DefaultLockKeyBuilder 类  
17 - HASH, RLock.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); // Redisson 的 Lock 锁,使用 Hash 数据结构 10 + /**
  11 + * 分布式锁
  12 + *
  13 + * KEY 格式:lock4j:%s // 参数来自 DefaultLockKeyBuilder 类
  14 + * VALUE 数据格式:HASH // RLock.class:Redisson 的 Lock 锁,使用 Hash 数据结构
  15 + * 过期时间:不固定
  16 + */
  17 + String LOCK4J = "lock4j:%s";
18 18
19 } 19 }
yudao-framework/yudao-spring-boot-starter-redis/pom.xml
@@ -37,6 +37,11 @@ @@ -37,6 +37,11 @@
37 <artifactId>netty-all</artifactId> 37 <artifactId>netty-all</artifactId>
38 </dependency> 38 </dependency>
39 39
  40 +
  41 + <dependency>
  42 + <groupId>com.fasterxml.jackson.datatype</groupId>
  43 + <artifactId>jackson-datatype-jsr310</artifactId>
  44 + </dependency>
40 </dependencies> 45 </dependencies>
41 46
42 </project> 47 </project>
yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java
1 package cn.iocoder.yudao.framework.redis.config; 1 package cn.iocoder.yudao.framework.redis.config;
2 2
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.redis.core.TimeoutRedisCacheManager;
3 import org.springframework.boot.autoconfigure.AutoConfiguration; 5 import org.springframework.boot.autoconfigure.AutoConfiguration;
4 import org.springframework.boot.autoconfigure.cache.CacheProperties; 6 import org.springframework.boot.autoconfigure.cache.CacheProperties;
5 import org.springframework.boot.context.properties.EnableConfigurationProperties; 7 import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -7,8 +9,15 @@ import org.springframework.cache.annotation.EnableCaching; @@ -7,8 +9,15 @@ import org.springframework.cache.annotation.EnableCaching;
7 import org.springframework.context.annotation.Bean; 9 import org.springframework.context.annotation.Bean;
8 import org.springframework.context.annotation.Primary; 10 import org.springframework.context.annotation.Primary;
9 import org.springframework.data.redis.cache.RedisCacheConfiguration; 11 import org.springframework.data.redis.cache.RedisCacheConfiguration;
  12 +import org.springframework.data.redis.cache.RedisCacheManager;
  13 +import org.springframework.data.redis.cache.RedisCacheWriter;
  14 +import org.springframework.data.redis.connection.RedisConnectionFactory;
  15 +import org.springframework.data.redis.core.RedisTemplate;
10 import org.springframework.data.redis.serializer.RedisSerializationContext; 16 import org.springframework.data.redis.serializer.RedisSerializationContext;
11 -import org.springframework.data.redis.serializer.RedisSerializer; 17 +
  18 +import java.util.Objects;
  19 +
  20 +import static cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration.buildRedisSerializer;
12 21
13 /** 22 /**
14 * Cache 配置类,基于 Redis 实现 23 * Cache 配置类,基于 Redis 实现
@@ -20,15 +29,19 @@ public class YudaoCacheAutoConfiguration { @@ -20,15 +29,19 @@ public class YudaoCacheAutoConfiguration {
20 29
21 /** 30 /**
22 * RedisCacheConfiguration Bean 31 * RedisCacheConfiguration Bean
23 - * 32 + * <p>
24 * 参考 org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration 的 createConfiguration 方法 33 * 参考 org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration 的 createConfiguration 方法
25 */ 34 */
26 @Bean 35 @Bean
27 @Primary 36 @Primary
28 public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) { 37 public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
29 - // 设置使用 JSON 序列化方式  
30 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); 38 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
31 - config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json())); 39 + // 设置使用 : 单冒号,而不是双 :: 冒号,避免 Redis Desktop Manager 多余空格
  40 + // 详细可见 https://blog.csdn.net/chuixue24/article/details/103928965 博客
  41 + config = config.computePrefixWith(cacheName -> cacheName + StrUtil.COLON);
  42 + // 设置使用 JSON 序列化方式
  43 + config = config.serializeValuesWith(
  44 + RedisSerializationContext.SerializationPair.fromSerializer(buildRedisSerializer()));
32 45
33 // 设置 CacheProperties.Redis 的属性 46 // 设置 CacheProperties.Redis 的属性
34 CacheProperties.Redis redisProperties = cacheProperties.getRedis(); 47 CacheProperties.Redis redisProperties = cacheProperties.getRedis();
@@ -47,4 +60,14 @@ public class YudaoCacheAutoConfiguration { @@ -47,4 +60,14 @@ public class YudaoCacheAutoConfiguration {
47 return config; 60 return config;
48 } 61 }
49 62
  63 + @Bean
  64 + public RedisCacheManager redisCacheManager(RedisTemplate<String, Object> redisTemplate,
  65 + RedisCacheConfiguration redisCacheConfiguration) {
  66 + // 创建 RedisCacheWriter 对象
  67 + RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
  68 + RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
  69 + // 创建 TenantRedisCacheManager 对象
  70 + return new TimeoutRedisCacheManager(cacheWriter, redisCacheConfiguration);
  71 + }
  72 +
50 } 73 }
yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java
1 package cn.iocoder.yudao.framework.redis.config; 1 package cn.iocoder.yudao.framework.redis.config;
2 2
  3 +import cn.hutool.core.util.ReflectUtil;
  4 +import com.fasterxml.jackson.databind.ObjectMapper;
  5 +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
3 import org.springframework.boot.autoconfigure.AutoConfiguration; 6 import org.springframework.boot.autoconfigure.AutoConfiguration;
4 import org.springframework.context.annotation.Bean; 7 import org.springframework.context.annotation.Bean;
5 import org.springframework.data.redis.connection.RedisConnectionFactory; 8 import org.springframework.data.redis.connection.RedisConnectionFactory;
@@ -25,9 +28,17 @@ public class YudaoRedisAutoConfiguration { @@ -25,9 +28,17 @@ public class YudaoRedisAutoConfiguration {
25 template.setKeySerializer(RedisSerializer.string()); 28 template.setKeySerializer(RedisSerializer.string());
26 template.setHashKeySerializer(RedisSerializer.string()); 29 template.setHashKeySerializer(RedisSerializer.string());
27 // 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。 30 // 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。
28 - template.setValueSerializer(RedisSerializer.json());  
29 - template.setHashValueSerializer(RedisSerializer.json()); 31 + template.setValueSerializer(buildRedisSerializer());
  32 + template.setHashValueSerializer(buildRedisSerializer());
30 return template; 33 return template;
31 } 34 }
32 35
  36 + public static RedisSerializer<?> buildRedisSerializer() {
  37 + RedisSerializer<Object> json = RedisSerializer.json();
  38 + // 解决 LocalDateTime 的序列化
  39 + ObjectMapper objectMapper = (ObjectMapper) ReflectUtil.getFieldValue(json, "mapper");
  40 + objectMapper.registerModules(new JavaTimeModule());
  41 + return json;
  42 + }
  43 +
33 } 44 }
yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/RedisKeyDefine.java deleted
1 -package cn.iocoder.yudao.framework.redis.core;  
2 -  
3 -import com.fasterxml.jackson.annotation.JsonValue;  
4 -import lombok.AllArgsConstructor;  
5 -import lombok.Data;  
6 -import lombok.Getter;  
7 -  
8 -import java.time.Duration;  
9 -  
10 -/**  
11 - * Redis Key 定义类  
12 - *  
13 - * @author 芋道源码  
14 - */  
15 -@Data  
16 -public class RedisKeyDefine {  
17 -  
18 - @Getter  
19 - @AllArgsConstructor  
20 - public enum KeyTypeEnum {  
21 -  
22 - STRING("String"),  
23 - LIST("List"),  
24 - HASH("Hash"),  
25 - SET("Set"),  
26 - ZSET("Sorted Set"),  
27 - STREAM("Stream"),  
28 - PUBSUB("Pub/Sub");  
29 -  
30 - /**  
31 - * 类型  
32 - */  
33 - @JsonValue  
34 - private final String type;  
35 -  
36 - }  
37 -  
38 - @Getter  
39 - @AllArgsConstructor  
40 - public enum TimeoutTypeEnum {  
41 -  
42 - FOREVER(1), // 永不超时  
43 - DYNAMIC(2), // 动态超时  
44 - FIXED(3); // 固定超时  
45 -  
46 - /**  
47 - * 类型  
48 - */  
49 - @JsonValue  
50 - private final Integer type;  
51 -  
52 - }  
53 -  
54 - /**  
55 - * Key 模板  
56 - */  
57 - private final String keyTemplate;  
58 - /**  
59 - * Key 类型的枚举  
60 - */  
61 - private final KeyTypeEnum keyType;  
62 - /**  
63 - * Value 类型  
64 - *  
65 - * 如果是使用分布式锁,设置为 {@link java.util.concurrent.locks.Lock} 类型  
66 - */  
67 - private final Class<?> valueType;  
68 - /**  
69 - * 超时类型  
70 - */  
71 - private final TimeoutTypeEnum timeoutType;  
72 - /**  
73 - * 过期时间  
74 - */  
75 - private final Duration timeout;  
76 - /**  
77 - * 备注  
78 - */  
79 - private final String memo;  
80 -  
81 - private RedisKeyDefine(String memo, String keyTemplate, KeyTypeEnum keyType, Class<?> valueType,  
82 - TimeoutTypeEnum timeoutType, Duration timeout) {  
83 - this.memo = memo;  
84 - this.keyTemplate = keyTemplate;  
85 - this.keyType = keyType;  
86 - this.valueType = valueType;  
87 - this.timeout = timeout;  
88 - this.timeoutType = timeoutType;  
89 - // 添加注册表  
90 - RedisKeyRegistry.add(this);  
91 - }  
92 -  
93 - public RedisKeyDefine(String memo, String keyTemplate, KeyTypeEnum keyType, Class<?> valueType, Duration timeout) {  
94 - this(memo, keyTemplate, keyType, valueType, TimeoutTypeEnum.FIXED, timeout);  
95 - }  
96 -  
97 - public RedisKeyDefine(String memo, String keyTemplate, KeyTypeEnum keyType, Class<?> valueType, TimeoutTypeEnum timeoutType) {  
98 - this(memo, keyTemplate, keyType, valueType, timeoutType, Duration.ZERO);  
99 - }  
100 -  
101 - /**  
102 - * 格式化 Key  
103 - *  
104 - * 注意,内部采用 {@link String#format(String, Object...)} 实现  
105 - *  
106 - * @param args 格式化的参数  
107 - * @return Key  
108 - */  
109 - public String formatKey(Object... args) {  
110 - return String.format(keyTemplate, args);  
111 - }  
112 -  
113 -}  
yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/RedisKeyRegistry.java deleted
1 -package cn.iocoder.yudao.framework.redis.core;  
2 -  
3 -import java.util.ArrayList;  
4 -import java.util.List;  
5 -  
6 -/**  
7 - * {@link RedisKeyDefine} 注册表  
8 - */  
9 -public class RedisKeyRegistry {  
10 -  
11 - /**  
12 - * Redis RedisKeyDefine 数组  
13 - */  
14 - private static final List<RedisKeyDefine> DEFINES = new ArrayList<>();  
15 -  
16 - public static void add(RedisKeyDefine define) {  
17 - DEFINES.add(define);  
18 - }  
19 -  
20 - public static List<RedisKeyDefine> list() {  
21 - return DEFINES;  
22 - }  
23 -  
24 - public static int size() {  
25 - return DEFINES.size();  
26 - }  
27 -  
28 -}  
yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/TimeoutRedisCacheManager.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.redis.core;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import org.springframework.boot.convert.DurationStyle;
  5 +import org.springframework.cache.annotation.Cacheable;
  6 +import org.springframework.data.redis.cache.RedisCache;
  7 +import org.springframework.data.redis.cache.RedisCacheConfiguration;
  8 +import org.springframework.data.redis.cache.RedisCacheManager;
  9 +import org.springframework.data.redis.cache.RedisCacheWriter;
  10 +
  11 +import java.time.Duration;
  12 +import java.time.temporal.ChronoUnit;
  13 +
  14 +/**
  15 + * 支持自定义过期时间的 {@link RedisCacheManager} 实现类
  16 + *
  17 + * 在 {@link Cacheable#cacheNames()} 格式为 "key#ttl" 时,# 后面的 ttl 为过期时间,单位为秒
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +public class TimeoutRedisCacheManager extends RedisCacheManager {
  22 +
  23 + private static final String SPLIT = "#";
  24 +
  25 + public TimeoutRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
  26 + super(cacheWriter, defaultCacheConfiguration);
  27 + }
  28 +
  29 + @Override
  30 + protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
  31 + if (StrUtil.isEmpty(name)) {
  32 + return super.createRedisCache(name, cacheConfig);
  33 + }
  34 + // 如果使用 # 分隔,大小不为 2,则说明不使用自定义过期时间
  35 + String[] names = StrUtil.splitToArray(name, SPLIT);
  36 + if (names.length != 2) {
  37 + return super.createRedisCache(name, cacheConfig);
  38 + }
  39 +
  40 + // 核心:通过修改 cacheConfig 的过期时间,实现自定义过期时间
  41 + if (cacheConfig != null) {
  42 + // 移除 # 后面的 : 以及后面的内容,避免影响解析
  43 + names[1] = StrUtil.subBefore(names[1], StrUtil.COLON, false);
  44 + // 解析时间
  45 + Duration duration = DurationStyle.detectAndParse(names[1], ChronoUnit.SECONDS);
  46 + cacheConfig = cacheConfig.entryTtl(duration);
  47 + }
  48 + return super.createRedisCache(names[0], cacheConfig);
  49 + }
  50 +
  51 +}
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.http
@@ -2,8 +2,3 @@ @@ -2,8 +2,3 @@
2 GET {{baseUrl}}/infra/redis/get-monitor-info 2 GET {{baseUrl}}/infra/redis/get-monitor-info
3 Authorization: Bearer {{token}} 3 Authorization: Bearer {{token}}
4 tenant-id: {{adminTenentId}} 4 tenant-id: {{adminTenentId}}
5 -  
6 -### 请求 /infra/redis/get-key-list 接口 => 成功  
7 -GET {{baseUrl}}/infra/redis/get-key-list  
8 -Authorization: Bearer {{token}}  
9 -tenant-id: {{adminTenentId}}  
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.java
1 package cn.iocoder.yudao.module.infra.controller.admin.redis; 1 package cn.iocoder.yudao.module.infra.controller.admin.redis;
2 2
3 -import cn.hutool.core.collection.CollUtil;  
4 -import cn.hutool.core.util.StrUtil;  
5 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;  
7 -import cn.iocoder.yudao.framework.redis.core.RedisKeyRegistry;  
8 -import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisKeyDefineRespVO;  
9 -import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisKeyValueRespVO;  
10 import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisMonitorRespVO; 4 import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisMonitorRespVO;
11 import cn.iocoder.yudao.module.infra.convert.redis.RedisConvert; 5 import cn.iocoder.yudao.module.infra.convert.redis.RedisConvert;
12 -import io.swagger.v3.oas.annotations.tags.Tag;  
13 -import io.swagger.v3.oas.annotations.Parameter;  
14 import io.swagger.v3.oas.annotations.Operation; 6 import io.swagger.v3.oas.annotations.Operation;
  7 +import io.swagger.v3.oas.annotations.tags.Tag;
15 import org.springframework.data.redis.connection.RedisServerCommands; 8 import org.springframework.data.redis.connection.RedisServerCommands;
16 -import org.springframework.data.redis.core.Cursor;  
17 import org.springframework.data.redis.core.RedisCallback; 9 import org.springframework.data.redis.core.RedisCallback;
18 -import org.springframework.data.redis.core.ScanOptions;  
19 import org.springframework.data.redis.core.StringRedisTemplate; 10 import org.springframework.data.redis.core.StringRedisTemplate;
20 import org.springframework.security.access.prepost.PreAuthorize; 11 import org.springframework.security.access.prepost.PreAuthorize;
21 -import org.springframework.web.bind.annotation.*; 12 +import org.springframework.web.bind.annotation.GetMapping;
  13 +import org.springframework.web.bind.annotation.RequestMapping;
  14 +import org.springframework.web.bind.annotation.RestController;
22 15
23 import javax.annotation.Resource; 16 import javax.annotation.Resource;
24 -import java.util.*; 17 +import java.util.Properties;
25 18
26 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 19 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
27 20
@@ -47,66 +40,4 @@ public class RedisController { @@ -47,66 +40,4 @@ public class RedisController {
47 return success(RedisConvert.INSTANCE.build(info, dbSize, commandStats)); 40 return success(RedisConvert.INSTANCE.build(info, dbSize, commandStats));
48 } 41 }
49 42
50 - @GetMapping("/get-key-define-list")  
51 - @Operation(summary = "获得 Redis Key 模板列表")  
52 - @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")  
53 - public CommonResult<List<RedisKeyDefineRespVO>> getKeyDefineList() {  
54 - List<RedisKeyDefine> keyDefines = RedisKeyRegistry.list();  
55 - return success(RedisConvert.INSTANCE.convertList(keyDefines));  
56 - }  
57 -  
58 - @GetMapping("/get-key-list")  
59 - @Operation(summary = "获得 Redis keys 键名列表")  
60 - @Parameter(name = "keyTemplate", description = "Redis Key 定义", example = "true")  
61 - @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")  
62 - public CommonResult<Set<String>> getKeyDefineList(@RequestParam("keyTemplate") String keyTemplate) {  
63 - return success(getKeyDefineList0(keyTemplate));  
64 - }  
65 -  
66 - private Set<String> getKeyDefineList0(String keyTemplate) {  
67 - // key 格式化  
68 - String key = StrUtil.replace(keyTemplate, "%[s|c|b|d|x|o|f|a|e|g]", parameter -> "*");  
69 - // scan 扫描 key  
70 - Set<String> keys = new LinkedHashSet<>();  
71 - stringRedisTemplate.execute((RedisCallback<Set<String>>) connection -> {  
72 - try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(key).count(100).build())) {  
73 - cursor.forEachRemaining(value -> keys.add(StrUtil.utf8Str(value)));  
74 - } catch (Exception e) {  
75 - throw new RuntimeException(e);  
76 - }  
77 - return keys;  
78 - });  
79 - return keys;  
80 - }  
81 -  
82 - @GetMapping("/get-key-value")  
83 - @Operation(summary = "获得 Redis key 内容")  
84 - @Parameter(name = "key", description = "Redis Key", example = "oauth2_access_token:233")  
85 - @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")  
86 - public CommonResult<RedisKeyValueRespVO> getKeyValue(@RequestParam("key") String key) {  
87 - String value = stringRedisTemplate.opsForValue().get(key);  
88 - return success(new RedisKeyValueRespVO(key, value));  
89 - }  
90 -  
91 - @DeleteMapping("/delete-key")  
92 - @Operation(summary = "删除 Redis Key")  
93 - @Parameter(name = "key", description = "Redis Key", example = "oauth2_access_token:233")  
94 - @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")  
95 - public CommonResult<Boolean> deleteKey(@RequestParam("key") String key) {  
96 - stringRedisTemplate.delete(key);  
97 - return success(Boolean.TRUE);  
98 - }  
99 -  
100 - @DeleteMapping("/delete-keys")  
101 - @Operation(summary = "删除 Redis Key 根据模板")  
102 - @Parameter(name = "keyTemplate", description = "Redis Key 定义", example = "true")  
103 - @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")  
104 - public CommonResult<Boolean> deleteKeys(@RequestParam("keyTemplate") String keyTemplate) {  
105 - Set<String> keys = getKeyDefineList0(keyTemplate);  
106 - if (CollUtil.isNotEmpty(keys)) {  
107 - stringRedisTemplate.delete(keys);  
108 - }  
109 - return success(Boolean.TRUE);  
110 - }  
111 -  
112 } 43 }
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/vo/RedisKeyDefineRespVO.java deleted
1 -package cn.iocoder.yudao.module.infra.controller.admin.redis.vo;  
2 -  
3 -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;  
4 -import io.swagger.v3.oas.annotations.media.Schema;  
5 -import lombok.AllArgsConstructor;  
6 -import lombok.Builder;  
7 -import lombok.Data;  
8 -  
9 -import java.time.Duration;  
10 -  
11 -@Schema(description = "管理后台 - Redis Key 信息 Response VO")  
12 -@Data  
13 -@Builder  
14 -@AllArgsConstructor  
15 -public class RedisKeyDefineRespVO {  
16 -  
17 - @Schema(description = "Key 模板", requiredMode = Schema.RequiredMode.REQUIRED, example = "login_user:%s")  
18 - private String keyTemplate;  
19 -  
20 - @Schema(description = "Key 类型的枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "String")  
21 - private RedisKeyDefine.KeyTypeEnum keyType;  
22 -  
23 - @Schema(description = "Value 类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "java.lang.String")  
24 - private Class<?> valueType;  
25 -  
26 - @Schema(description = "超时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
27 - private RedisKeyDefine.TimeoutTypeEnum timeoutType;  
28 -  
29 - @Schema(description = "过期时间,单位:毫秒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")  
30 - private Duration timeout;  
31 -  
32 - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "啦啦啦啦~")  
33 - private String memo;  
34 -  
35 -}  
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/vo/RedisKeyValueRespVO.java deleted
1 -package cn.iocoder.yudao.module.infra.controller.admin.redis.vo;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import lombok.AllArgsConstructor;  
5 -import lombok.Data;  
6 -  
7 -@Schema(description = "管理后台 - 单个 Redis Key Value Response VO")  
8 -@Data  
9 -@AllArgsConstructor  
10 -public class RedisKeyValueRespVO {  
11 -  
12 - @Schema(description = "c5f6990767804a928f4bb96ca249febf", requiredMode = Schema.RequiredMode.REQUIRED, example = "String")  
13 - private String key;  
14 -  
15 - @Schema(requiredMode = Schema.RequiredMode.REQUIRED, example = "String")  
16 - private String value;  
17 -  
18 -}  
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/redis/RedisConvert.java
1 package cn.iocoder.yudao.module.infra.convert.redis; 1 package cn.iocoder.yudao.module.infra.convert.redis;
2 2
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
4 -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;  
5 -import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisKeyDefineRespVO;  
6 import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisMonitorRespVO; 4 import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisMonitorRespVO;
7 import org.mapstruct.Mapper; 5 import org.mapstruct.Mapper;
8 import org.mapstruct.factory.Mappers; 6 import org.mapstruct.factory.Mappers;
9 7
10 import java.util.ArrayList; 8 import java.util.ArrayList;
11 -import java.util.List;  
12 import java.util.Properties; 9 import java.util.Properties;
13 10
14 @Mapper 11 @Mapper
@@ -29,6 +26,4 @@ public interface RedisConvert { @@ -29,6 +26,4 @@ public interface RedisConvert {
29 return respVO; 26 return respVO;
30 } 27 }
31 28
32 - List<RedisKeyDefineRespVO> convertList(List<RedisKeyDefine> list);  
33 -  
34 } 29 }
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java
@@ -6,6 +6,9 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; @@ -6,6 +6,9 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO; 6 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO;
7 import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO; 7 import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
8 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
  9 +import org.apache.ibatis.annotations.Select;
  10 +
  11 +import java.time.LocalDateTime;
9 12
10 @Mapper 13 @Mapper
11 public interface FileConfigMapper extends BaseMapperX<FileConfigDO> { 14 public interface FileConfigMapper extends BaseMapperX<FileConfigDO> {
@@ -18,4 +21,7 @@ public interface FileConfigMapper extends BaseMapperX&lt;FileConfigDO&gt; { @@ -18,4 +21,7 @@ public interface FileConfigMapper extends BaseMapperX&lt;FileConfigDO&gt; {
18 .orderByDesc(FileConfigDO::getId)); 21 .orderByDesc(FileConfigDO::getId));
19 } 22 }
20 23
  24 + @Select("SELECT COUNT(*) FROM infra_file_config WHERE update_time > #{maxUpdateTime}")
  25 + Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
  26 +
21 } 27 }
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/file/FileConfigRefreshConsumer.java deleted
1 -package cn.iocoder.yudao.module.infra.mq.consumer.file;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;  
4 -import cn.iocoder.yudao.module.infra.mq.message.file.FileConfigRefreshMessage;  
5 -import cn.iocoder.yudao.module.infra.service.file.FileConfigService;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import org.springframework.stereotype.Component;  
8 -  
9 -import javax.annotation.Resource;  
10 -  
11 -/**  
12 - * 针对 {@link FileConfigRefreshMessage} 的消费者  
13 - *  
14 - * @author 芋道源码  
15 - */  
16 -@Component  
17 -@Slf4j  
18 -public class FileConfigRefreshConsumer extends AbstractChannelMessageListener<FileConfigRefreshMessage> {  
19 -  
20 - @Resource  
21 - private FileConfigService fileConfigService;  
22 -  
23 - @Override  
24 - public void onMessage(FileConfigRefreshMessage message) {  
25 - log.info("[onMessage][收到 FileConfig 刷新消息]");  
26 - fileConfigService.initLocalCache();  
27 - }  
28 -  
29 -}  
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/package-info.java
1 /** 1 /**
2 - * 占位符,避免缩进 2 + * 消息队列的消费者
3 */ 3 */
4 package cn.iocoder.yudao.module.infra.mq.consumer; 4 package cn.iocoder.yudao.module.infra.mq.consumer;
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/file/FileConfigRefreshMessage.java deleted
1 -package cn.iocoder.yudao.module.infra.mq.message.file;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;  
4 -import lombok.Data;  
5 -  
6 -/**  
7 - * 文件配置数据刷新 Message  
8 - */  
9 -@Data  
10 -public class FileConfigRefreshMessage extends AbstractChannelMessage {  
11 -  
12 - @Override  
13 - public String getChannel() {  
14 - return "infra.file-config.refresh";  
15 - }  
16 -  
17 -}  
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/package-info.java
1 /** 1 /**
2 - * 占位符,避免缩进 2 + * 消息队列的消息
3 */ 3 */
4 package cn.iocoder.yudao.module.infra.mq.message; 4 package cn.iocoder.yudao.module.infra.mq.message;
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/file/FileConfigProducer.java deleted
1 -package cn.iocoder.yudao.module.infra.mq.producer.file;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;  
4 -import cn.iocoder.yudao.module.infra.mq.message.file.FileConfigRefreshMessage;  
5 -import org.springframework.stereotype.Component;  
6 -  
7 -import javax.annotation.Resource;  
8 -  
9 -/**  
10 - * 文件配置相关消息的 Producer  
11 - */  
12 -@Component  
13 -public class FileConfigProducer {  
14 -  
15 - @Resource  
16 - private RedisMQTemplate redisMQTemplate;  
17 -  
18 - /**  
19 - * 发送 {@link FileConfigRefreshMessage} 消息  
20 - */  
21 - public void sendFileConfigRefreshMessage() {  
22 - FileConfigRefreshMessage message = new FileConfigRefreshMessage();  
23 - redisMQTemplate.send(message);  
24 - }  
25 -  
26 -}  
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/package-info.java
1 /** 1 /**
2 - * 占位符,避免缩进 2 + * 消息队列的生产者
3 */ 3 */
4 package cn.iocoder.yudao.module.infra.mq.producer; 4 package cn.iocoder.yudao.module.infra.mq.producer;
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java
@@ -17,11 +17,6 @@ import javax.validation.Valid; @@ -17,11 +17,6 @@ import javax.validation.Valid;
17 public interface FileConfigService { 17 public interface FileConfigService {
18 18
19 /** 19 /**
20 - * 初始化文件客户端  
21 - */  
22 - void initLocalCache();  
23 -  
24 - /**  
25 * 创建文件配置 20 * 创建文件配置
26 * 21 *
27 * @param createReqVO 创建信息 22 * @param createReqVO 创建信息
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java
1 package cn.iocoder.yudao.module.infra.service.file; 1 package cn.iocoder.yudao.module.infra.service.file;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.hutool.core.io.resource.ResourceUtil; 4 import cn.hutool.core.io.resource.ResourceUtil;
4 import cn.hutool.core.util.IdUtil; 5 import cn.hutool.core.util.IdUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
6 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 8 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
7 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; 9 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
8 import cn.iocoder.yudao.framework.file.core.client.FileClient; 10 import cn.iocoder.yudao.framework.file.core.client.FileClient;
@@ -15,20 +17,20 @@ import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigU @@ -15,20 +17,20 @@ import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigU
15 import cn.iocoder.yudao.module.infra.convert.file.FileConfigConvert; 17 import cn.iocoder.yudao.module.infra.convert.file.FileConfigConvert;
16 import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO; 18 import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
17 import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper; 19 import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper;
18 -import cn.iocoder.yudao.module.infra.mq.producer.file.FileConfigProducer;  
19 import lombok.Getter; 20 import lombok.Getter;
20 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
  22 +import org.springframework.scheduling.annotation.Scheduled;
21 import org.springframework.stereotype.Service; 23 import org.springframework.stereotype.Service;
22 import org.springframework.transaction.annotation.Transactional; 24 import org.springframework.transaction.annotation.Transactional;
23 -import org.springframework.transaction.support.TransactionSynchronization;  
24 -import org.springframework.transaction.support.TransactionSynchronizationManager;  
25 import org.springframework.validation.annotation.Validated; 25 import org.springframework.validation.annotation.Validated;
26 26
27 import javax.annotation.PostConstruct; 27 import javax.annotation.PostConstruct;
28 import javax.annotation.Resource; 28 import javax.annotation.Resource;
29 import javax.validation.Validator; 29 import javax.validation.Validator;
  30 +import java.time.LocalDateTime;
30 import java.util.List; 31 import java.util.List;
31 import java.util.Map; 32 import java.util.Map;
  33 +import java.util.concurrent.TimeUnit;
32 34
33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 35 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
34 import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG_DELETE_FAIL_MASTER; 36 import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG_DELETE_FAIL_MASTER;
@@ -46,6 +48,12 @@ public class FileConfigServiceImpl implements FileConfigService { @@ -46,6 +48,12 @@ public class FileConfigServiceImpl implements FileConfigService {
46 48
47 @Resource 49 @Resource
48 private FileClientFactory fileClientFactory; 50 private FileClientFactory fileClientFactory;
  51 +
  52 + /**
  53 + * 文件配置的缓存
  54 + */
  55 + @Getter
  56 + private List<FileConfigDO> fileConfigCache;
49 /** 57 /**
50 * Master FileClient 对象,有且仅有一个,即 {@link FileConfigDO#getMaster()} 对应的 58 * Master FileClient 对象,有且仅有一个,即 {@link FileConfigDO#getMaster()} 对应的
51 */ 59 */
@@ -56,12 +64,8 @@ public class FileConfigServiceImpl implements FileConfigService { @@ -56,12 +64,8 @@ public class FileConfigServiceImpl implements FileConfigService {
56 private FileConfigMapper fileConfigMapper; 64 private FileConfigMapper fileConfigMapper;
57 65
58 @Resource 66 @Resource
59 - private FileConfigProducer fileConfigProducer;  
60 -  
61 - @Resource  
62 private Validator validator; 67 private Validator validator;
63 68
64 - @Override  
65 @PostConstruct 69 @PostConstruct
66 public void initLocalCache() { 70 public void initLocalCache() {
67 // 第一步:查询数据 71 // 第一步:查询数据
@@ -76,6 +80,27 @@ public class FileConfigServiceImpl implements FileConfigService { @@ -76,6 +80,27 @@ public class FileConfigServiceImpl implements FileConfigService {
76 masterFileClient = fileClientFactory.getFileClient(config.getId()); 80 masterFileClient = fileClientFactory.getFileClient(config.getId());
77 } 81 }
78 }); 82 });
  83 + this.fileConfigCache = configs;
  84 + }
  85 +
  86 + /**
  87 + * 通过定时任务轮询,刷新缓存
  88 + *
  89 + * 目的:多节点部署时,通过轮询”通知“所有节点,进行刷新
  90 + */
  91 + @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS)
  92 + public void refreshLocalCache() {
  93 + // 情况一:如果缓存里没有数据,则直接刷新缓存
  94 + if (CollUtil.isEmpty(fileConfigCache)) {
  95 + initLocalCache();
  96 + return;
  97 + }
  98 +
  99 + // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存
  100 + LocalDateTime maxTime = CollectionUtils.getMaxValue(fileConfigCache, FileConfigDO::getUpdateTime);
  101 + if (fileConfigMapper.selectCountByUpdateTimeGt(maxTime) > 0) {
  102 + initLocalCache();
  103 + }
79 } 104 }
80 105
81 @Override 106 @Override
@@ -85,9 +110,9 @@ public class FileConfigServiceImpl implements FileConfigService { @@ -85,9 +110,9 @@ public class FileConfigServiceImpl implements FileConfigService {
85 .setConfig(parseClientConfig(createReqVO.getStorage(), createReqVO.getConfig())) 110 .setConfig(parseClientConfig(createReqVO.getStorage(), createReqVO.getConfig()))
86 .setMaster(false); // 默认非 master 111 .setMaster(false); // 默认非 master
87 fileConfigMapper.insert(fileConfig); 112 fileConfigMapper.insert(fileConfig);
88 - // 发送刷新配置的消息  
89 - fileConfigProducer.sendFileConfigRefreshMessage();  
90 - // 返回 113 +
  114 + // 刷新缓存
  115 + initLocalCache();
91 return fileConfig.getId(); 116 return fileConfig.getId();
92 } 117 }
93 118
@@ -99,8 +124,9 @@ public class FileConfigServiceImpl implements FileConfigService { @@ -99,8 +124,9 @@ public class FileConfigServiceImpl implements FileConfigService {
99 FileConfigDO updateObj = FileConfigConvert.INSTANCE.convert(updateReqVO) 124 FileConfigDO updateObj = FileConfigConvert.INSTANCE.convert(updateReqVO)
100 .setConfig(parseClientConfig(config.getStorage(), updateReqVO.getConfig())); 125 .setConfig(parseClientConfig(config.getStorage(), updateReqVO.getConfig()));
101 fileConfigMapper.updateById(updateObj); 126 fileConfigMapper.updateById(updateObj);
102 - // 发送刷新配置的消息  
103 - fileConfigProducer.sendFileConfigRefreshMessage(); 127 +
  128 + // 刷新缓存
  129 + initLocalCache();
104 } 130 }
105 131
106 @Override 132 @Override
@@ -112,15 +138,9 @@ public class FileConfigServiceImpl implements FileConfigService { @@ -112,15 +138,9 @@ public class FileConfigServiceImpl implements FileConfigService {
112 fileConfigMapper.updateBatch(new FileConfigDO().setMaster(false)); 138 fileConfigMapper.updateBatch(new FileConfigDO().setMaster(false));
113 // 更新 139 // 更新
114 fileConfigMapper.updateById(new FileConfigDO().setId(id).setMaster(true)); 140 fileConfigMapper.updateById(new FileConfigDO().setId(id).setMaster(true));
115 - // 发送刷新配置的消息  
116 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {  
117 -  
118 - @Override  
119 - public void afterCommit() {  
120 - fileConfigProducer.sendFileConfigRefreshMessage();  
121 - }  
122 141
123 - }); 142 + // 刷新缓存
  143 + initLocalCache();
124 } 144 }
125 145
126 private FileClientConfig parseClientConfig(Integer storage, Map<String, Object> config) { 146 private FileClientConfig parseClientConfig(Integer storage, Map<String, Object> config) {
@@ -143,8 +163,9 @@ public class FileConfigServiceImpl implements FileConfigService { @@ -143,8 +163,9 @@ public class FileConfigServiceImpl implements FileConfigService {
143 } 163 }
144 // 删除 164 // 删除
145 fileConfigMapper.deleteById(id); 165 fileConfigMapper.deleteById(id);
146 - // 发送刷新配置的消息  
147 - fileConfigProducer.sendFileConfigRefreshMessage(); 166 +
  167 + // 刷新缓存
  168 + initLocalCache();
148 } 169 }
149 170
150 private FileConfigDO validateFileConfigExists(Long id) { 171 private FileConfigDO validateFileConfigExists(Long id) {
yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java
@@ -16,7 +16,6 @@ import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigP @@ -16,7 +16,6 @@ import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigP
16 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigUpdateReqVO; 16 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigUpdateReqVO;
17 import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO; 17 import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
18 import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper; 18 import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper;
19 -import cn.iocoder.yudao.module.infra.mq.producer.file.FileConfigProducer;  
20 import lombok.Data; 19 import lombok.Data;
21 import org.junit.jupiter.api.Test; 20 import org.junit.jupiter.api.Test;
22 import org.springframework.boot.test.mock.mockito.MockBean; 21 import org.springframework.boot.test.mock.mockito.MockBean;
@@ -56,8 +55,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { @@ -56,8 +55,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
56 private FileConfigMapper fileConfigMapper; 55 private FileConfigMapper fileConfigMapper;
57 56
58 @MockBean 57 @MockBean
59 - private FileConfigProducer fileConfigProducer;  
60 - @MockBean  
61 private Validator validator; 58 private Validator validator;
62 @MockBean 59 @MockBean
63 private FileClientFactory fileClientFactory; 60 private FileClientFactory fileClientFactory;
@@ -81,6 +78,10 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { @@ -81,6 +78,10 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
81 verify(fileClientFactory).createOrUpdateFileClient(eq(2L), 78 verify(fileClientFactory).createOrUpdateFileClient(eq(2L),
82 eq(configDO2.getStorage()), eq(configDO2.getConfig())); 79 eq(configDO2.getStorage()), eq(configDO2.getConfig()));
83 assertSame(masterFileClient, fileConfigService.getMasterFileClient()); 80 assertSame(masterFileClient, fileConfigService.getMasterFileClient());
  81 + // 断言 fileConfigCache 缓存
  82 + assertEquals(2, fileConfigService.getFileConfigCache().size());
  83 + assertEquals(configDO1, fileConfigService.getFileConfigCache().get(0));
  84 + assertEquals(configDO2, fileConfigService.getFileConfigCache().get(1));
84 } 85 }
85 86
86 @Test 87 @Test
@@ -101,8 +102,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { @@ -101,8 +102,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
101 assertFalse(fileConfig.getMaster()); 102 assertFalse(fileConfig.getMaster());
102 assertEquals("/yunai", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath()); 103 assertEquals("/yunai", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath());
103 assertEquals("https://www.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain()); 104 assertEquals("https://www.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain());
104 - // verify 调用  
105 - verify(fileConfigProducer).sendFileConfigRefreshMessage();  
106 } 105 }
107 106
108 @Test 107 @Test
@@ -126,8 +125,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { @@ -126,8 +125,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
126 assertPojoEquals(reqVO, fileConfig, "config"); 125 assertPojoEquals(reqVO, fileConfig, "config");
127 assertEquals("/yunai2", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath()); 126 assertEquals("/yunai2", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath());
128 assertEquals("https://doc.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain()); 127 assertEquals("https://doc.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain());
129 - // verify 调用  
130 - verify(fileConfigProducer).sendFileConfigRefreshMessage();  
131 } 128 }
132 129
133 @Test 130 @Test
@@ -152,8 +149,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { @@ -152,8 +149,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
152 // 断言数据 149 // 断言数据
153 assertTrue(fileConfigMapper.selectById(dbFileConfig.getId()).getMaster()); 150 assertTrue(fileConfigMapper.selectById(dbFileConfig.getId()).getMaster());
154 assertFalse(fileConfigMapper.selectById(masterFileConfig.getId()).getMaster()); 151 assertFalse(fileConfigMapper.selectById(masterFileConfig.getId()).getMaster());
155 - // verify 调用  
156 - verify(fileConfigProducer).sendFileConfigRefreshMessage();  
157 } 152 }
158 153
159 @Test 154 @Test
@@ -174,8 +169,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { @@ -174,8 +169,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
174 fileConfigService.deleteFileConfig(id); 169 fileConfigService.deleteFileConfig(id);
175 // 校验数据不存在了 170 // 校验数据不存在了
176 assertNull(fileConfigMapper.selectById(id)); 171 assertNull(fileConfigMapper.selectById(id));
177 - // verify 调用  
178 - verify(fileConfigProducer).sendFileConfigRefreshMessage();  
179 } 172 }
180 173
181 @Test 174 @Test
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/RedisKeyConstants.java
1 package cn.iocoder.yudao.module.pay.dal.redis; 1 package cn.iocoder.yudao.module.pay.dal.redis;
2 2
3 -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;  
4 -import org.redisson.api.RLock;  
5 -  
6 /** 3 /**
7 * 支付 Redis Key 枚举类 4 * 支付 Redis Key 枚举类
8 * 5 *
@@ -10,9 +7,14 @@ import org.redisson.api.RLock; @@ -10,9 +7,14 @@ import org.redisson.api.RLock;
10 */ 7 */
11 public interface RedisKeyConstants { 8 public interface RedisKeyConstants {
12 9
13 - RedisKeyDefine PAY_NOTIFY_LOCK = new RedisKeyDefine("通知任务的分布式锁",  
14 - "pay_notify:lock:%d", // 参数来自 DefaultLockKeyBuilder 类  
15 - RedisKeyDefine.KeyTypeEnum.HASH, RLock.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); // Redisson 的 Lock 锁,使用 Hash 数据结构 10 + /**
  11 + * 通知任务的分布式锁
  12 + *
  13 + * KEY 格式:pay_notify:lock:%d // 参数来自 DefaultLockKeyBuilder 类
  14 + * VALUE 数据格式:HASH // RLock.class:Redisson 的 Lock 锁,使用 Hash 数据结构
  15 + * 过期时间:不固定
  16 + */
  17 + String PAY_NOTIFY_LOCK = "pay_notify:lock:%d";
16 18
17 /** 19 /**
18 * 支付序号的缓存 20 * 支付序号的缓存
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/notify/PayNotifyLockRedisDAO.java
@@ -33,7 +33,7 @@ public class PayNotifyLockRedisDAO { @@ -33,7 +33,7 @@ public class PayNotifyLockRedisDAO {
33 } 33 }
34 34
35 private static String formatKey(Long id) { 35 private static String formatKey(Long id) {
36 - return String.format(PAY_NOTIFY_LOCK.getKeyTemplate(), id); 36 + return String.format(PAY_NOTIFY_LOCK, id);
37 } 37 }
38 38
39 } 39 }
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 { @@ -21,7 +21,7 @@ public class PermissionApiImpl implements PermissionApi {
21 21
22 @Override 22 @Override
23 public Set<Long> getUserRoleIdListByRoleIds(Collection<Long> roleIds) { 23 public Set<Long> getUserRoleIdListByRoleIds(Collection<Long> roleIds) {
24 - return permissionService.getUserRoleIdListByRoleIds(roleIds); 24 + return permissionService.getUserRoleIdListByRoleId(roleIds);
25 } 25 }
26 26
27 @Override 27 @Override
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 POST {{baseUrl}}/system/auth/login 2 POST {{baseUrl}}/system/auth/login
3 Content-Type: application/json 3 Content-Type: application/json
4 tenant-id: {{adminTenentId}} 4 tenant-id: {{adminTenentId}}
  5 +tag: Yunai.local
5 6
6 { 7 {
7 "username": "admin", 8 "username": "admin",
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; @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.system.controller.admin.auth;
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 -import cn.iocoder.yudao.framework.common.util.collection.SetUtils;  
7 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 6 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
8 import cn.iocoder.yudao.framework.security.config.SecurityProperties; 7 import cn.iocoder.yudao.framework.security.config.SecurityProperties;
9 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; 8 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
@@ -12,8 +11,8 @@ import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; @@ -12,8 +11,8 @@ import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
12 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; 11 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
13 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; 12 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
14 import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; 13 import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
15 -import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;  
16 import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; 14 import cn.iocoder.yudao.module.system.service.auth.AdminAuthService;
  15 +import cn.iocoder.yudao.module.system.service.permission.MenuService;
17 import cn.iocoder.yudao.module.system.service.permission.PermissionService; 16 import cn.iocoder.yudao.module.system.service.permission.PermissionService;
18 import cn.iocoder.yudao.module.system.service.permission.RoleService; 17 import cn.iocoder.yudao.module.system.service.permission.RoleService;
19 import cn.iocoder.yudao.module.system.service.social.SocialUserService; 18 import cn.iocoder.yudao.module.system.service.social.SocialUserService;
@@ -34,9 +33,9 @@ import java.util.List; @@ -34,9 +33,9 @@ import java.util.List;
34 import java.util.Set; 33 import java.util.Set;
35 34
36 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 35 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  36 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
37 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 37 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
38 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.obtainAuthorization; 38 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.obtainAuthorization;
39 -import static java.util.Collections.singleton;  
40 39
41 @Tag(name = "管理后台 - 认证") 40 @Tag(name = "管理后台 - 认证")
42 @RestController 41 @RestController
@@ -52,6 +51,8 @@ public class AuthController { @@ -52,6 +51,8 @@ public class AuthController {
52 @Resource 51 @Resource
53 private RoleService roleService; 52 private RoleService roleService;
54 @Resource 53 @Resource
  54 + private MenuService menuService;
  55 + @Resource
55 private PermissionService permissionService; 56 private PermissionService permissionService;
56 @Resource 57 @Resource
57 private SocialUserService socialUserService; 58 private SocialUserService socialUserService;
@@ -91,33 +92,24 @@ public class AuthController { @@ -91,33 +92,24 @@ public class AuthController {
91 @GetMapping("/get-permission-info") 92 @GetMapping("/get-permission-info")
92 @Operation(summary = "获取登录用户的权限信息") 93 @Operation(summary = "获取登录用户的权限信息")
93 public CommonResult<AuthPermissionInfoRespVO> getPermissionInfo() { 94 public CommonResult<AuthPermissionInfoRespVO> getPermissionInfo() {
94 - // 获得用户信息 95 + // 1.1 获得用户信息
95 AdminUserDO user = userService.getUser(getLoginUserId()); 96 AdminUserDO user = userService.getUser(getLoginUserId());
96 if (user == null) { 97 if (user == null) {
97 return null; 98 return null;
98 } 99 }
99 - // 获得角色列表  
100 - Set<Long> roleIds = permissionService.getUserRoleIdsFromCache(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));  
101 - List<RoleDO> roleList = roleService.getRoleListFromCache(roleIds);  
102 - // 获得菜单列表  
103 - List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(roleIds,  
104 - SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType(), MenuTypeEnum.BUTTON.getType()),  
105 - singleton(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的  
106 - // 拼接结果返回  
107 - return success(AuthConvert.INSTANCE.convert(user, roleList, menuList));  
108 - }  
109 100
110 - @GetMapping("/list-menus")  
111 - @Operation(summary = "获得登录用户的菜单列表")  
112 - public CommonResult<List<AuthMenuRespVO>> getMenuList() {  
113 - // 获得角色列表  
114 - Set<Long> roleIds = permissionService.getUserRoleIdsFromCache(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));  
115 - // 获得用户拥有的菜单列表  
116 - List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(roleIds,  
117 - SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型  
118 - singleton(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的  
119 - // 转换成 Tree 结构返回  
120 - return success(AuthConvert.INSTANCE.buildMenuTree(menuList)); 101 + // 1.2 获得角色列表
  102 + Set<Long> roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId());
  103 + List<RoleDO> roles = roleService.getRoleList(roleIds);
  104 + roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色
  105 +
  106 + // 1.3 获得菜单列表
  107 + Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(convertSet(roles, RoleDO::getId));
  108 + List<MenuDO> menuList = menuService.getMenuList(menuIds);
  109 + menuList.removeIf(menu -> !CommonStatusEnum.ENABLE.getStatus().equals(menu.getStatus())); // 移除禁用的菜单
  110 +
  111 + // 2. 拼接结果返回
  112 + return success(AuthConvert.INSTANCE.convert(user, roles, menuList));
121 } 113 }
122 114
123 // ========== 短信登录相关 ========== 115 // ========== 短信登录相关 ==========
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; @@ -6,6 +6,7 @@ import lombok.Builder;
6 import lombok.Data; 6 import lombok.Data;
7 import lombok.NoArgsConstructor; 7 import lombok.NoArgsConstructor;
8 8
  9 +import java.util.List;
9 import java.util.Set; 10 import java.util.Set;
10 11
11 @Schema(description = "管理后台 - 登录用户的权限信息 Response VO,额外包括用户信息和角色列表") 12 @Schema(description = "管理后台 - 登录用户的权限信息 Response VO,额外包括用户信息和角色列表")
@@ -24,6 +25,9 @@ public class AuthPermissionInfoRespVO { @@ -24,6 +25,9 @@ public class AuthPermissionInfoRespVO {
24 @Schema(description = "操作权限数组", requiredMode = Schema.RequiredMode.REQUIRED) 25 @Schema(description = "操作权限数组", requiredMode = Schema.RequiredMode.REQUIRED)
25 private Set<String> permissions; 26 private Set<String> permissions;
26 27
  28 + @Schema(description = "菜单树", required = true)
  29 + private List<MenuVO> menus;
  30 +
27 @Schema(description = "用户信息 VO") 31 @Schema(description = "用户信息 VO")
28 @Data 32 @Data
29 @NoArgsConstructor 33 @NoArgsConstructor
@@ -42,4 +46,48 @@ public class AuthPermissionInfoRespVO { @@ -42,4 +46,48 @@ public class AuthPermissionInfoRespVO {
42 46
43 } 47 }
44 48
  49 + @Schema(description = "管理后台 - 登录用户的菜单信息 Response VO")
  50 + @Data
  51 + @NoArgsConstructor
  52 + @AllArgsConstructor
  53 + @Builder
  54 + public static class MenuVO {
  55 +
  56 + @Schema(description = "菜单名称", required = true, example = "芋道")
  57 + private Long id;
  58 +
  59 + @Schema(description = "父菜单 ID", required = true, example = "1024")
  60 + private Long parentId;
  61 +
  62 + @Schema(description = "菜单名称", required = true, example = "芋道")
  63 + private String name;
  64 +
  65 + @Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post")
  66 + private String path;
  67 +
  68 + @Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index")
  69 + private String component;
  70 +
  71 + @Schema(description = "组件名", example = "SystemUser")
  72 + private String componentName;
  73 +
  74 + @Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list")
  75 + private String icon;
  76 +
  77 + @Schema(description = "是否可见", required = true, example = "false")
  78 + private Boolean visible;
  79 +
  80 + @Schema(description = "是否缓存", required = true, example = "false")
  81 + private Boolean keepAlive;
  82 +
  83 + @Schema(description = "是否总是显示", example = "false")
  84 + private Boolean alwaysShow;
  85 +
  86 + /**
  87 + * 子路由
  88 + */
  89 + private List<MenuVO> children;
  90 +
  91 + }
  92 +
45 } 93 }
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 { @@ -37,10 +37,10 @@ public class PermissionController {
37 37
38 @Operation(summary = "获得角色拥有的菜单编号") 38 @Operation(summary = "获得角色拥有的菜单编号")
39 @Parameter(name = "roleId", description = "角色编号", required = true) 39 @Parameter(name = "roleId", description = "角色编号", required = true)
40 - @GetMapping("/list-role-resources") 40 + @GetMapping("/list-role-menus")
41 @PreAuthorize("@ss.hasPermission('system:permission:assign-role-menu')") 41 @PreAuthorize("@ss.hasPermission('system:permission:assign-role-menu')")
42 - public CommonResult<Set<Long>> listRoleMenus(Long roleId) {  
43 - return success(permissionService.getRoleMenuIds(roleId)); 42 + public CommonResult<Set<Long>> getRoleMenuList(Long roleId) {
  43 + return success(permissionService.getRoleMenuListByRoleId(roleId));
44 } 44 }
45 45
46 @PostMapping("/assign-role-menu") 46 @PostMapping("/assign-role-menu")
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; @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
8 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; 8 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
9 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; 9 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
10 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; 10 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
  11 +import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;
11 import org.mapstruct.Mapper; 12 import org.mapstruct.Mapper;
12 import org.mapstruct.factory.Mappers; 13 import org.mapstruct.factory.Mappers;
13 import org.slf4j.LoggerFactory; 14 import org.slf4j.LoggerFactory;
@@ -27,14 +28,16 @@ public interface AuthConvert { @@ -27,14 +28,16 @@ public interface AuthConvert {
27 28
28 default AuthPermissionInfoRespVO convert(AdminUserDO user, List<RoleDO> roleList, List<MenuDO> menuList) { 29 default AuthPermissionInfoRespVO convert(AdminUserDO user, List<RoleDO> roleList, List<MenuDO> menuList) {
29 return AuthPermissionInfoRespVO.builder() 30 return AuthPermissionInfoRespVO.builder()
30 - .user(AuthPermissionInfoRespVO.UserVO.builder().id(user.getId()).nickname(user.getNickname())  
31 - .avatar(user.getAvatar()).build()) 31 + .user(AuthPermissionInfoRespVO.UserVO.builder().id(user.getId()).nickname(user.getNickname()).avatar(user.getAvatar()).build())
32 .roles(convertSet(roleList, RoleDO::getCode)) 32 .roles(convertSet(roleList, RoleDO::getCode))
  33 + // 权限标识信息
33 .permissions(convertSet(menuList, MenuDO::getPermission)) 34 .permissions(convertSet(menuList, MenuDO::getPermission))
  35 + // 菜单树
  36 + .menus(buildMenuTree(menuList))
34 .build(); 37 .build();
35 } 38 }
36 39
37 - AuthMenuRespVO convertTreeNode(MenuDO menu); 40 + AuthPermissionInfoRespVO.MenuVO convertTreeNode(MenuDO menu);
38 41
39 /** 42 /**
40 * 将菜单列表,构建成菜单树 43 * 将菜单列表,构建成菜单树
@@ -42,20 +45,23 @@ public interface AuthConvert { @@ -42,20 +45,23 @@ public interface AuthConvert {
42 * @param menuList 菜单列表 45 * @param menuList 菜单列表
43 * @return 菜单树 46 * @return 菜单树
44 */ 47 */
45 - default List<AuthMenuRespVO> buildMenuTree(List<MenuDO> menuList) { 48 + default List<AuthPermissionInfoRespVO.MenuVO> buildMenuTree(List<MenuDO> menuList) {
  49 + // 移除按钮
  50 + menuList.removeIf(menu -> menu.getType().equals(MenuTypeEnum.BUTTON.getType()));
46 // 排序,保证菜单的有序性 51 // 排序,保证菜单的有序性
47 menuList.sort(Comparator.comparing(MenuDO::getSort)); 52 menuList.sort(Comparator.comparing(MenuDO::getSort));
  53 +
48 // 构建菜单树 54 // 构建菜单树
49 // 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。 55 // 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。
50 - Map<Long, AuthMenuRespVO> treeNodeMap = new LinkedHashMap<>(); 56 + Map<Long, AuthPermissionInfoRespVO.MenuVO> treeNodeMap = new LinkedHashMap<>();
51 menuList.forEach(menu -> treeNodeMap.put(menu.getId(), AuthConvert.INSTANCE.convertTreeNode(menu))); 57 menuList.forEach(menu -> treeNodeMap.put(menu.getId(), AuthConvert.INSTANCE.convertTreeNode(menu)));
52 // 处理父子关系 58 // 处理父子关系
53 treeNodeMap.values().stream().filter(node -> !node.getParentId().equals(ID_ROOT)).forEach(childNode -> { 59 treeNodeMap.values().stream().filter(node -> !node.getParentId().equals(ID_ROOT)).forEach(childNode -> {
54 // 获得父节点 60 // 获得父节点
55 - AuthMenuRespVO parentNode = treeNodeMap.get(childNode.getParentId()); 61 + AuthPermissionInfoRespVO.MenuVO parentNode = treeNodeMap.get(childNode.getParentId());
56 if (parentNode == null) { 62 if (parentNode == null) {
57 LoggerFactory.getLogger(getClass()).error("[buildRouterTree][resource({}) 找不到父资源({})]", 63 LoggerFactory.getLogger(getClass()).error("[buildRouterTree][resource({}) 找不到父资源({})]",
58 - childNode.getId(), childNode.getParentId()); 64 + childNode.getId(), childNode.getParentId());
59 return; 65 return;
60 } 66 }
61 // 将自己添加到父节点中 67 // 将自己添加到父节点中
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 @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqV
6 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; 6 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
7 import org.apache.ibatis.annotations.Mapper; 7 import org.apache.ibatis.annotations.Mapper;
8 8
  9 +import java.util.Collection;
9 import java.util.List; 10 import java.util.List;
10 11
11 @Mapper 12 @Mapper
@@ -25,4 +26,8 @@ public interface DeptMapper extends BaseMapperX&lt;DeptDO&gt; { @@ -25,4 +26,8 @@ public interface DeptMapper extends BaseMapperX&lt;DeptDO&gt; {
25 return selectCount(DeptDO::getParentId, parentId); 26 return selectCount(DeptDO::getParentId, parentId);
26 } 27 }
27 28
  29 + default List<DeptDO> selectListByParentId(Collection<Long> parentIds) {
  30 + return selectList(DeptDO::getParentId, parentIds);
  31 + }
  32 +
28 } 33 }
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&lt;MenuDO&gt; { @@ -25,4 +25,7 @@ public interface MenuMapper extends BaseMapperX&lt;MenuDO&gt; {
25 .eqIfPresent(MenuDO::getStatus, reqVO.getStatus())); 25 .eqIfPresent(MenuDO::getStatus, reqVO.getStatus()));
26 } 26 }
27 27
  28 + default List<MenuDO> selectListByPermission(String permission) {
  29 + return selectList(MenuDO::getPermission, permission);
  30 + }
28 } 31 }
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; @@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.system.dal.mysql.permission;
3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO; 4 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
5 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 5 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
6 -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;  
7 import org.apache.ibatis.annotations.Mapper; 6 import org.apache.ibatis.annotations.Mapper;
8 -import org.springframework.stereotype.Repository;  
9 7
10 import java.util.Collection; 8 import java.util.Collection;
11 import java.util.List; 9 import java.util.List;
@@ -13,14 +11,18 @@ import java.util.List; @@ -13,14 +11,18 @@ import java.util.List;
13 @Mapper 11 @Mapper
14 public interface RoleMenuMapper extends BaseMapperX<RoleMenuDO> { 12 public interface RoleMenuMapper extends BaseMapperX<RoleMenuDO> {
15 13
16 - @Repository  
17 - class BatchInsertMapper extends ServiceImpl<RoleMenuMapper, RoleMenuDO> {  
18 - }  
19 -  
20 default List<RoleMenuDO> selectListByRoleId(Long roleId) { 14 default List<RoleMenuDO> selectListByRoleId(Long roleId) {
21 return selectList(RoleMenuDO::getRoleId, roleId); 15 return selectList(RoleMenuDO::getRoleId, roleId);
22 } 16 }
23 17
  18 + default List<RoleMenuDO> selectListByRoleId(Collection<Long> roleIds) {
  19 + return selectList(RoleMenuDO::getRoleId, roleIds);
  20 + }
  21 +
  22 + default List<RoleMenuDO> selectListByMenuId(Long menuId) {
  23 + return selectList(RoleMenuDO::getMenuId, menuId);
  24 + }
  25 +
24 default void deleteListByRoleIdAndMenuIds(Long roleId, Collection<Long> menuIds) { 26 default void deleteListByRoleIdAndMenuIds(Long roleId, Collection<Long> menuIds) {
25 delete(new LambdaQueryWrapper<RoleMenuDO>() 27 delete(new LambdaQueryWrapper<RoleMenuDO>()
26 .eq(RoleMenuDO::getRoleId, roleId) 28 .eq(RoleMenuDO::getRoleId, roleId)
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sensitiveword/SensitiveWordMapper.java
@@ -7,7 +7,9 @@ import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.Sensitiv @@ -7,7 +7,9 @@ import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.Sensitiv
7 import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO; 7 import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO;
8 import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO; 8 import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
9 import org.apache.ibatis.annotations.Mapper; 9 import org.apache.ibatis.annotations.Mapper;
  10 +import org.apache.ibatis.annotations.Select;
10 11
  12 +import java.time.LocalDateTime;
11 import java.util.List; 13 import java.util.List;
12 14
13 /** 15 /**
@@ -40,4 +42,7 @@ public interface SensitiveWordMapper extends BaseMapperX&lt;SensitiveWordDO&gt; { @@ -40,4 +42,7 @@ public interface SensitiveWordMapper extends BaseMapperX&lt;SensitiveWordDO&gt; {
40 return selectOne(SensitiveWordDO::getName, name); 42 return selectOne(SensitiveWordDO::getName, name);
41 } 43 }
42 44
  45 + @Select("SELECT COUNT(*) FROM system_sensitive_word WHERE update_time > #{maxUpdateTime}")
  46 + Long selectCountByUpdateTimeGt(LocalDateTime maxTime);
  47 +
43 } 48 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsChannelMapper.java
@@ -6,6 +6,9 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; @@ -6,6 +6,9 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; 6 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
7 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; 7 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
8 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
  9 +import org.apache.ibatis.annotations.Select;
  10 +
  11 +import java.time.LocalDateTime;
9 12
10 @Mapper 13 @Mapper
11 public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> { 14 public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> {
@@ -18,4 +21,7 @@ public interface SmsChannelMapper extends BaseMapperX&lt;SmsChannelDO&gt; { @@ -18,4 +21,7 @@ public interface SmsChannelMapper extends BaseMapperX&lt;SmsChannelDO&gt; {
18 .orderByDesc(SmsChannelDO::getId)); 21 .orderByDesc(SmsChannelDO::getId));
19 } 22 }
20 23
  24 + @Select("SELECT COUNT(*) FROM system_sms_channel WHERE update_time > #{maxUpdateTime}")
  25 + Long selectCountByUpdateTimeGt(LocalDateTime maxTime);
  26 +
21 } 27 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java
1 package cn.iocoder.yudao.module.system.dal.redis; 1 package cn.iocoder.yudao.module.system.dal.redis;
2 2
3 -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;  
4 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; 3 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
5 4
6 -import java.time.Duration;  
7 -  
8 -import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING;  
9 -  
10 /** 5 /**
11 * System Redis Key 枚举类 6 * System Redis Key 枚举类
12 * 7 *
@@ -14,16 +9,93 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.S @@ -14,16 +9,93 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.S
14 */ 9 */
15 public interface RedisKeyConstants { 10 public interface RedisKeyConstants {
16 11
17 - RedisKeyDefine CAPTCHA_CODE = new RedisKeyDefine("验证码的缓存",  
18 - "captcha_code:%s", // 参数为 uuid  
19 - STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); 12 + /**
  13 + * 指定部门的所有子部门编号数组的缓存
  14 + * <p>
  15 + * KEY 格式:dept_children_ids:{id}
  16 + * VALUE 数据类型:String 子部门编号集合
  17 + */
  18 + String DEPT_CHILDREN_ID_LIST = "dept_children_ids";
  19 +
  20 + /**
  21 + * 角色的缓存
  22 + * <p>
  23 + * KEY 格式:role:{id}
  24 + * VALUE 数据类型:String 角色信息
  25 + */
  26 + String ROLE = "role";
  27 +
  28 + /**
  29 + * 用户拥有的角色编号的缓存
  30 + * <p>
  31 + * KEY 格式:user_role_ids:{userId}
  32 + * VALUE 数据类型:String 角色编号集合
  33 + */
  34 + String USER_ROLE_ID_LIST = "user_role_ids";
  35 +
  36 + /**
  37 + * 拥有指定菜单的角色编号的缓存
  38 + * <p>
  39 + * KEY 格式:user_role_ids:{menuId}
  40 + * VALUE 数据类型:String 角色编号集合
  41 + */
  42 + String MENU_ROLE_ID_LIST = "menu_role_ids";
  43 +
  44 + /**
  45 + * 拥有权限对应的菜单编号数组的缓存
  46 + * <p>
  47 + * KEY 格式:permission_menu_ids:{permission}
  48 + * VALUE 数据类型:String 菜单编号数组
  49 + */
  50 + String PERMISSION_MENU_ID_LIST = "permission_menu_ids";
  51 +
  52 + /**
  53 + * OAuth2 客户端的缓存
  54 + * <p>
  55 + * KEY 格式:user:{id}
  56 + * VALUE 数据类型:String 客户端信息
  57 + */
  58 + String OAUTH_CLIENT = "oauth_client";
  59 +
  60 + /**
  61 + * 访问令牌的缓存
  62 + * <p>
  63 + * KEY 格式:oauth2_access_token:{token}
  64 + * VALUE 数据类型:String 访问令牌信息 {@link OAuth2AccessTokenDO}
  65 + * <p>
  66 + * 由于动态过期时间,使用 RedisTemplate 操作
  67 + */
  68 + String OAUTH2_ACCESS_TOKEN = "oauth2_access_token:%s";
  69 +
  70 + /**
  71 + * 站内信模版的缓存
  72 + * <p>
  73 + * KEY 格式:notify_template:{code}
  74 + * VALUE 数据格式:String 模版信息
  75 + */
  76 + String NOTIFY_TEMPLATE = "notify_template";
20 77
21 - RedisKeyDefine OAUTH2_ACCESS_TOKEN = new RedisKeyDefine("访问令牌的缓存",  
22 - "oauth2_access_token:%s", // 参数为访问令牌 token  
23 - STRING, OAuth2AccessTokenDO.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); 78 + /**
  79 + * 邮件账号的缓存
  80 + * <p>
  81 + * KEY 格式:sms_template:{id}
  82 + * VALUE 数据格式:String 账号信息
  83 + */
  84 + String MAIL_ACCOUNT = "mail_account";
24 85
25 - RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交登陆的 state", // 注意,它是被 JustAuth 的 justauth.type.prefix 使用到  
26 - "social_auth_state:%s", // 参数为 state  
27 - STRING, String.class, Duration.ofHours(24)); // 值为 state 86 + /**
  87 + * 邮件模版的缓存
  88 + * <p>
  89 + * KEY 格式:mail_template:{code}
  90 + * VALUE 数据格式:String 模版信息
  91 + */
  92 + String MAIL_TEMPLATE = "mail_template";
28 93
  94 + /**
  95 + * 短信模版的缓存
  96 + * <p>
  97 + * KEY 格式:sms_template:{id}
  98 + * VALUE 数据格式:String 模版信息
  99 + */
  100 + String SMS_TEMPLATE = "sms_template";
29 } 101 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/common/CaptchaRedisDAO.java deleted
1 -package cn.iocoder.yudao.module.system.dal.redis.common;  
2 -  
3 -import org.springframework.data.redis.core.StringRedisTemplate;  
4 -import org.springframework.stereotype.Repository;  
5 -  
6 -import javax.annotation.Resource;  
7 -import java.time.Duration;  
8 -  
9 -import static cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants.CAPTCHA_CODE;  
10 -  
11 -/**  
12 - * 验证码的 Redis DAO  
13 - *  
14 - * @author 芋道源码  
15 - */  
16 -@Repository  
17 -public class CaptchaRedisDAO {  
18 -  
19 - @Resource  
20 - private StringRedisTemplate stringRedisTemplate;  
21 -  
22 - public String get(String uuid) {  
23 - String redisKey = formatKey(uuid);  
24 - return stringRedisTemplate.opsForValue().get(redisKey);  
25 - }  
26 -  
27 - public void set(String uuid, String code, Duration timeout) {  
28 - String redisKey = formatKey(uuid);  
29 - stringRedisTemplate.opsForValue().set(redisKey, code, timeout);  
30 - }  
31 -  
32 - public void delete(String uuid) {  
33 - String redisKey = formatKey(uuid);  
34 - stringRedisTemplate.delete(redisKey);  
35 - }  
36 -  
37 - private static String formatKey(String uuid) {  
38 - return String.format(CAPTCHA_CODE.getKeyTemplate(), uuid);  
39 - }  
40 -  
41 -}  
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 { @@ -53,7 +53,7 @@ public class OAuth2AccessTokenRedisDAO {
53 } 53 }
54 54
55 private static String formatKey(String accessToken) { 55 private static String formatKey(String accessToken) {
56 - return String.format(OAUTH2_ACCESS_TOKEN.getKeyTemplate(), accessToken); 56 + return String.format(OAUTH2_ACCESS_TOKEN, accessToken);
57 } 57 }
58 58
59 } 59 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/auth/OAuth2ClientRefreshConsumer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.consumer.auth;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;  
4 -import cn.iocoder.yudao.module.system.mq.message.auth.OAuth2ClientRefreshMessage;  
5 -import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import org.springframework.stereotype.Component;  
8 -  
9 -import javax.annotation.Resource;  
10 -  
11 -/**  
12 - * 针对 {@link OAuth2ClientRefreshMessage} 的消费者  
13 - *  
14 - * @author 芋道源码  
15 - */  
16 -@Component  
17 -@Slf4j  
18 -public class OAuth2ClientRefreshConsumer extends AbstractChannelMessageListener<OAuth2ClientRefreshMessage> {  
19 -  
20 - @Resource  
21 - private OAuth2ClientService oauth2ClientService;  
22 -  
23 - @Override  
24 - public void onMessage(OAuth2ClientRefreshMessage message) {  
25 - log.info("[onMessage][收到 OAuth2Client 刷新消息]");  
26 - oauth2ClientService.initLocalCache();  
27 - }  
28 -  
29 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/dept/DeptRefreshConsumer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.consumer.dept;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;  
4 -import cn.iocoder.yudao.module.system.mq.message.dept.DeptRefreshMessage;  
5 -import cn.iocoder.yudao.module.system.service.dept.DeptService;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import org.springframework.stereotype.Component;  
8 -  
9 -import javax.annotation.Resource;  
10 -  
11 -/**  
12 - * 针对 {@link DeptRefreshMessage} 的消费者  
13 - *  
14 - * @author 芋道源码  
15 - */  
16 -@Component  
17 -@Slf4j  
18 -public class DeptRefreshConsumer extends AbstractChannelMessageListener<DeptRefreshMessage> {  
19 -  
20 - @Resource  
21 - private DeptService deptService;  
22 -  
23 - @Override  
24 - public void onMessage(DeptRefreshMessage message) {  
25 - log.info("[onMessage][收到 Dept 刷新消息]");  
26 - deptService.initLocalCache();  
27 - }  
28 -  
29 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailAccountRefreshConsumer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.consumer.mail;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;  
4 -import cn.iocoder.yudao.module.system.mq.message.mail.MailAccountRefreshMessage;  
5 -import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage;  
6 -import cn.iocoder.yudao.module.system.service.mail.MailAccountService;  
7 -import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;  
8 -import lombok.extern.slf4j.Slf4j;  
9 -import org.springframework.stereotype.Component;  
10 -  
11 -import javax.annotation.Resource;  
12 -  
13 -/**  
14 - * 针对 {@link MailAccountRefreshMessage} 的消费者  
15 - *  
16 - * @author wangjingyi  
17 - */  
18 -@Component  
19 -@Slf4j  
20 -public class MailAccountRefreshConsumer extends AbstractChannelMessageListener<MailAccountRefreshMessage> {  
21 -  
22 - @Resource  
23 - private MailAccountService mailAccountService;  
24 -  
25 - @Override  
26 - public void onMessage(MailAccountRefreshMessage message) {  
27 - log.info("[onMessage][收到 Mail Account 刷新信息]");  
28 - mailAccountService.initLocalCache();  
29 - }  
30 -  
31 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailTemplateRefreshConsumer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.consumer.mail;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;  
4 -import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage;  
5 -import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import org.springframework.stereotype.Component;  
8 -  
9 -import javax.annotation.Resource;  
10 -  
11 -/**  
12 - * 针对 {@link MailTemplateRefreshMessage} 的消费者  
13 - *  
14 - * @author wangjingyi  
15 - */  
16 -@Component  
17 -@Slf4j  
18 -public class MailTemplateRefreshConsumer extends AbstractChannelMessageListener<MailTemplateRefreshMessage> {  
19 -  
20 - @Resource  
21 - private MailTemplateService mailTemplateService;  
22 -  
23 - @Override  
24 - public void onMessage(MailTemplateRefreshMessage message) {  
25 - log.info("[onMessage][收到 Mail Template 刷新信息]");  
26 - mailTemplateService.initLocalCache();  
27 - }  
28 -  
29 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/notify/NotifyTemplateRefreshConsumer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.consumer.notify;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;  
4 -import cn.iocoder.yudao.module.system.mq.message.notify.NotifyTemplateRefreshMessage;  
5 -import cn.iocoder.yudao.module.system.service.notify.NotifyTemplateService;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import org.springframework.stereotype.Component;  
8 -  
9 -import javax.annotation.Resource;  
10 -  
11 -/**  
12 - * 针对 {@link NotifyTemplateRefreshMessage} 的消费者  
13 - *  
14 - * @author xrcoder  
15 - */  
16 -@Component  
17 -@Slf4j  
18 -public class NotifyTemplateRefreshConsumer extends AbstractChannelMessageListener<NotifyTemplateRefreshMessage> {  
19 -  
20 - @Resource  
21 - private NotifyTemplateService notifyTemplateService;  
22 -  
23 - @Override  
24 - public void onMessage(NotifyTemplateRefreshMessage message) {  
25 - log.info("[onMessage][收到 NotifyTemplate 刷新消息]");  
26 - notifyTemplateService.initLocalCache();  
27 - }  
28 -  
29 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/MenuRefreshConsumer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.consumer.permission;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;  
4 -import cn.iocoder.yudao.module.system.mq.message.permission.MenuRefreshMessage;  
5 -import cn.iocoder.yudao.module.system.service.permission.MenuService;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import org.springframework.stereotype.Component;  
8 -  
9 -import javax.annotation.Resource;  
10 -  
11 -/**  
12 - * 针对 {@link MenuRefreshMessage} 的消费者  
13 - *  
14 - * @author 芋道源码  
15 - */  
16 -@Component  
17 -@Slf4j  
18 -public class MenuRefreshConsumer extends AbstractChannelMessageListener<MenuRefreshMessage> {  
19 -  
20 - @Resource  
21 - private MenuService menuService;  
22 -  
23 - @Override  
24 - public void onMessage(MenuRefreshMessage message) {  
25 - log.info("[onMessage][收到 Menu 刷新消息]");  
26 - menuService.initLocalCache();  
27 - }  
28 -  
29 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/RoleMenuRefreshConsumer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.consumer.permission;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;  
4 -import cn.iocoder.yudao.module.system.mq.message.permission.RoleMenuRefreshMessage;  
5 -import cn.iocoder.yudao.module.system.service.permission.PermissionService;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import org.springframework.stereotype.Component;  
8 -  
9 -import javax.annotation.Resource;  
10 -  
11 -/**  
12 - * 针对 {@link RoleMenuRefreshMessage} 的消费者  
13 - *  
14 - * @author 芋道源码  
15 - */  
16 -@Component  
17 -@Slf4j  
18 -public class RoleMenuRefreshConsumer extends AbstractChannelMessageListener<RoleMenuRefreshMessage> {  
19 -  
20 - @Resource  
21 - private PermissionService permissionService;  
22 -  
23 - @Override  
24 - public void onMessage(RoleMenuRefreshMessage message) {  
25 - log.info("[onMessage][收到 Role 与 Menu 的关联刷新消息]");  
26 - permissionService.initLocalCache();  
27 - }  
28 -  
29 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/RoleRefreshConsumer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.consumer.permission;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;  
4 -import cn.iocoder.yudao.module.system.mq.message.permission.RoleRefreshMessage;  
5 -import cn.iocoder.yudao.module.system.service.permission.RoleService;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import org.springframework.stereotype.Component;  
8 -  
9 -import javax.annotation.Resource;  
10 -  
11 -/**  
12 - * 针对 {@link RoleRefreshMessage} 的消费者  
13 - *  
14 - * @author 芋道源码  
15 - */  
16 -@Component  
17 -@Slf4j  
18 -public class RoleRefreshConsumer extends AbstractChannelMessageListener<RoleRefreshMessage> {  
19 -  
20 - @Resource  
21 - private RoleService roleService;  
22 -  
23 - @Override  
24 - public void onMessage(RoleRefreshMessage message) {  
25 - log.info("[onMessage][收到 Role 刷新消息]");  
26 - roleService.initLocalCache();  
27 - }  
28 -  
29 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/UserRoleRefreshConsumer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.consumer.permission;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;  
4 -import cn.iocoder.yudao.module.system.mq.message.permission.UserRoleRefreshMessage;  
5 -import cn.iocoder.yudao.module.system.service.permission.PermissionService;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import org.springframework.stereotype.Component;  
8 -  
9 -import javax.annotation.Resource;  
10 -  
11 -/**  
12 - * 针对 {@link UserRoleRefreshMessage} 的消费者  
13 - *  
14 - * @author 芋道源码  
15 - */  
16 -@Component  
17 -@Slf4j  
18 -public class UserRoleRefreshConsumer extends AbstractChannelMessageListener<UserRoleRefreshMessage> {  
19 -  
20 - @Resource  
21 - private PermissionService permissionService;  
22 -  
23 - @Override  
24 - public void onMessage(UserRoleRefreshMessage message) {  
25 - log.info("[onMessage][收到 User 与 Role 的关联刷新消息]");  
26 - permissionService.initLocalCache();  
27 - }  
28 -  
29 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sensitiveword/SensitiveWordRefreshConsumer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.consumer.sensitiveword;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;  
4 -import cn.iocoder.yudao.module.system.mq.message.sensitiveword.SensitiveWordRefreshMessage;  
5 -import cn.iocoder.yudao.module.system.service.sensitiveword.SensitiveWordService;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import org.springframework.stereotype.Component;  
8 -  
9 -import javax.annotation.Resource;  
10 -  
11 -/**  
12 - * 针对 {@link SensitiveWordRefreshMessage} 的消费者  
13 - *  
14 - * @author 芋道源码  
15 - */  
16 -@Component  
17 -@Slf4j  
18 -public class SensitiveWordRefreshConsumer extends AbstractChannelMessageListener<SensitiveWordRefreshMessage> {  
19 -  
20 - @Resource  
21 - private SensitiveWordService sensitiveWordService;  
22 -  
23 - @Override  
24 - public void onMessage(SensitiveWordRefreshMessage message) {  
25 - log.info("[onMessage][收到 SensitiveWord 刷新消息]");  
26 - sensitiveWordService.initLocalCache();  
27 - }  
28 -  
29 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsChannelRefreshConsumer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.consumer.sms;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;  
4 -import cn.iocoder.yudao.module.system.mq.message.sms.SmsChannelRefreshMessage;  
5 -import cn.iocoder.yudao.module.system.service.sms.SmsChannelService;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import org.springframework.stereotype.Component;  
8 -  
9 -import javax.annotation.Resource;  
10 -  
11 -/**  
12 - * 针对 {@link SmsChannelRefreshMessage} 的消费者  
13 - *  
14 - * @author 芋道源码  
15 - */  
16 -@Component  
17 -@Slf4j  
18 -public class SmsChannelRefreshConsumer extends AbstractChannelMessageListener<SmsChannelRefreshMessage> {  
19 -  
20 - @Resource  
21 - private SmsChannelService smsChannelService;  
22 -  
23 - @Override  
24 - public void onMessage(SmsChannelRefreshMessage message) {  
25 - log.info("[onMessage][收到 SmsChannel 刷新消息]");  
26 - smsChannelService.initLocalCache();  
27 - }  
28 -  
29 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsTemplateRefreshConsumer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.consumer.sms;  
2 -  
3 -import cn.iocoder.yudao.module.system.mq.message.sms.SmsTemplateRefreshMessage;  
4 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;  
5 -import cn.iocoder.yudao.module.system.service.sms.SmsTemplateService;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import org.springframework.stereotype.Component;  
8 -  
9 -import javax.annotation.Resource;  
10 -  
11 -/**  
12 - * 针对 {@link SmsTemplateRefreshMessage} 的消费者  
13 - *  
14 - * @author 芋道源码  
15 - */  
16 -@Component  
17 -@Slf4j  
18 -public class SmsTemplateRefreshConsumer extends AbstractChannelMessageListener<SmsTemplateRefreshMessage> {  
19 -  
20 - @Resource  
21 - private SmsTemplateService smsTemplateService;  
22 -  
23 - @Override  
24 - public void onMessage(SmsTemplateRefreshMessage message) {  
25 - log.info("[onMessage][收到 SmsTemplate 刷新消息]");  
26 - smsTemplateService.initLocalCache();  
27 - }  
28 -  
29 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/auth/OAuth2ClientRefreshMessage.java deleted
1 -package cn.iocoder.yudao.module.system.mq.message.auth;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;  
4 -import lombok.Data;  
5 -import lombok.EqualsAndHashCode;  
6 -  
7 -/**  
8 - * OAuth 2.0 客户端的数据刷新 Message  
9 - *  
10 - * @author 芋道源码  
11 - */  
12 -@Data  
13 -@EqualsAndHashCode(callSuper = true)  
14 -public class OAuth2ClientRefreshMessage extends AbstractChannelMessage {  
15 -  
16 - @Override  
17 - public String getChannel() {  
18 - return "system.oauth2-client.refresh";  
19 - }  
20 -  
21 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/dept/DeptRefreshMessage.java deleted
1 -package cn.iocoder.yudao.module.system.mq.message.dept;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;  
4 -import lombok.Data;  
5 -import lombok.EqualsAndHashCode;  
6 -  
7 -/**  
8 - * 部门数据刷新 Message  
9 - *  
10 - * @author 芋道源码  
11 - */  
12 -@Data  
13 -@EqualsAndHashCode(callSuper = true)  
14 -public class DeptRefreshMessage extends AbstractChannelMessage {  
15 -  
16 - @Override  
17 - public String getChannel() {  
18 - return "system.dept.refresh";  
19 - }  
20 -  
21 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailAccountRefreshMessage.java deleted
1 -package cn.iocoder.yudao.module.system.mq.message.mail;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;  
4 -import lombok.Data;  
5 -import lombok.EqualsAndHashCode;  
6 -  
7 -/**  
8 - * 邮箱账号的数据刷新 Message  
9 - *  
10 - * @author wangjingyi  
11 - */  
12 -@Data  
13 -@EqualsAndHashCode(callSuper = true)  
14 -public class MailAccountRefreshMessage extends AbstractChannelMessage {  
15 -  
16 - @Override  
17 - public String getChannel() {  
18 - return "system.mail-account.refresh";  
19 - }  
20 -  
21 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailTemplateRefreshMessage.java deleted
1 -package cn.iocoder.yudao.module.system.mq.message.mail;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;  
4 -import lombok.Data;  
5 -import lombok.EqualsAndHashCode;  
6 -  
7 -/**  
8 - * 邮箱模板的数据刷新 Message  
9 - *  
10 - * @author wangjingyi  
11 - */  
12 -@Data  
13 -@EqualsAndHashCode(callSuper = true)  
14 -public class MailTemplateRefreshMessage extends AbstractChannelMessage {  
15 -  
16 - @Override  
17 - public String getChannel() {  
18 - return "system.mail-template.refresh";  
19 - }  
20 -  
21 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/notify/NotifyTemplateRefreshMessage.java deleted
1 -package cn.iocoder.yudao.module.system.mq.message.notify;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;  
4 -import lombok.Data;  
5 -import lombok.EqualsAndHashCode;  
6 -  
7 -/**  
8 - * 站内信模板的数据刷新 Message  
9 - *  
10 - * @author xrcoder  
11 - */  
12 -@Data  
13 -@EqualsAndHashCode(callSuper = true)  
14 -public class NotifyTemplateRefreshMessage extends AbstractChannelMessage {  
15 -  
16 - @Override  
17 - public String getChannel() {  
18 - return "system.notify-template.refresh";  
19 - }  
20 -  
21 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/MenuRefreshMessage.java deleted
1 -package cn.iocoder.yudao.module.system.mq.message.permission;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;  
4 -import lombok.Data;  
5 -import lombok.EqualsAndHashCode;  
6 -  
7 -/**  
8 - * 菜单数据刷新 Message  
9 - *  
10 - * @author 芋道源码  
11 - */  
12 -@Data  
13 -@EqualsAndHashCode(callSuper = true)  
14 -public class MenuRefreshMessage extends AbstractChannelMessage {  
15 -  
16 - @Override  
17 - public String getChannel() {  
18 - return "system.menu.refresh";  
19 - }  
20 -  
21 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/RoleMenuRefreshMessage.java deleted
1 -package cn.iocoder.yudao.module.system.mq.message.permission;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;  
4 -import lombok.Data;  
5 -import lombok.EqualsAndHashCode;  
6 -  
7 -/**  
8 - * 角色与菜单数据刷新 Message  
9 - *  
10 - * @author 芋道源码  
11 - */  
12 -@Data  
13 -@EqualsAndHashCode(callSuper = true)  
14 -public class RoleMenuRefreshMessage extends AbstractChannelMessage {  
15 -  
16 - @Override  
17 - public String getChannel() {  
18 - return "system.role-menu.refresh";  
19 - }  
20 -  
21 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/RoleRefreshMessage.java deleted
1 -package cn.iocoder.yudao.module.system.mq.message.permission;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;  
4 -import lombok.Data;  
5 -import lombok.EqualsAndHashCode;  
6 -  
7 -/**  
8 - * 角色数据刷新 Message  
9 - *  
10 - * @author 芋道源码  
11 - */  
12 -@Data  
13 -@EqualsAndHashCode(callSuper = true)  
14 -public class RoleRefreshMessage extends AbstractChannelMessage {  
15 -  
16 - @Override  
17 - public String getChannel() {  
18 - return "system.role.refresh";  
19 - }  
20 -  
21 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/UserRoleRefreshMessage.java deleted
1 -package cn.iocoder.yudao.module.system.mq.message.permission;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;  
4 -import lombok.Data;  
5 -import lombok.EqualsAndHashCode;  
6 -  
7 -/**  
8 - * 用户与角色的数据刷新 Message  
9 - *  
10 - * @author 芋道源码  
11 - */  
12 -@Data  
13 -@EqualsAndHashCode(callSuper = true)  
14 -public class UserRoleRefreshMessage extends AbstractChannelMessage {  
15 -  
16 - @Override  
17 - public String getChannel() {  
18 - return "system.user-role.refresh";  
19 - }  
20 -  
21 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/sensitiveword/SensitiveWordRefreshMessage.java deleted
1 -package cn.iocoder.yudao.module.system.mq.message.sensitiveword;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;  
4 -import lombok.Data;  
5 -import lombok.EqualsAndHashCode;  
6 -  
7 -/**  
8 - * 敏感词的刷新 Message  
9 - */  
10 -@Data  
11 -@EqualsAndHashCode(callSuper = true)  
12 -public class SensitiveWordRefreshMessage extends AbstractChannelMessage {  
13 -  
14 - @Override  
15 - public String getChannel() {  
16 - return "system.sensitive-word.refresh";  
17 - }  
18 -  
19 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/sms/SmsChannelRefreshMessage.java deleted
1 -package cn.iocoder.yudao.module.system.mq.message.sms;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;  
4 -import lombok.Data;  
5 -import lombok.EqualsAndHashCode;  
6 -  
7 -/**  
8 - * 短信渠道的数据刷新 Message  
9 - *  
10 - * @author 芋道源码  
11 - */  
12 -@Data  
13 -@EqualsAndHashCode(callSuper = true)  
14 -public class SmsChannelRefreshMessage extends AbstractChannelMessage {  
15 -  
16 - @Override  
17 - public String getChannel() {  
18 - return "system.sms-channel.refresh";  
19 - }  
20 -  
21 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/sms/SmsTemplateRefreshMessage.java deleted
1 -package cn.iocoder.yudao.module.system.mq.message.sms;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;  
4 -import lombok.Data;  
5 -import lombok.EqualsAndHashCode;  
6 -  
7 -/**  
8 - * 短信模板的数据刷新 Message  
9 - *  
10 - * @author 芋道源码  
11 - */  
12 -@Data  
13 -@EqualsAndHashCode(callSuper = true)  
14 -public class SmsTemplateRefreshMessage extends AbstractChannelMessage {  
15 -  
16 - @Override  
17 - public String getChannel() {  
18 - return "system.sms-template.refresh";  
19 - }  
20 -  
21 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/auth/OAuth2ClientProducer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.producer.auth;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;  
4 -import cn.iocoder.yudao.module.system.mq.message.auth.OAuth2ClientRefreshMessage;  
5 -import org.springframework.stereotype.Component;  
6 -  
7 -import javax.annotation.Resource;  
8 -  
9 -/**  
10 - * OAuth 2.0 客户端相关消息的 Producer  
11 - */  
12 -@Component  
13 -public class OAuth2ClientProducer {  
14 -  
15 - @Resource  
16 - private RedisMQTemplate redisMQTemplate;  
17 -  
18 - /**  
19 - * 发送 {@link OAuth2ClientRefreshMessage} 消息  
20 - */  
21 - public void sendOAuth2ClientRefreshMessage() {  
22 - OAuth2ClientRefreshMessage message = new OAuth2ClientRefreshMessage();  
23 - redisMQTemplate.send(message);  
24 - }  
25 -  
26 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/dept/DeptProducer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.producer.dept;  
2 -  
3 -import cn.iocoder.yudao.module.system.mq.message.dept.DeptRefreshMessage;  
4 -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;  
5 -import org.springframework.stereotype.Component;  
6 -  
7 -import javax.annotation.Resource;  
8 -  
9 -/**  
10 - * Dept 部门相关消息的 Producer  
11 - */  
12 -@Component  
13 -public class DeptProducer {  
14 -  
15 - @Resource  
16 - private RedisMQTemplate redisMQTemplate;  
17 -  
18 - /**  
19 - * 发送 {@link DeptRefreshMessage} 消息  
20 - */  
21 - public void sendDeptRefreshMessage() {  
22 - DeptRefreshMessage message = new DeptRefreshMessage();  
23 - redisMQTemplate.send(message);  
24 - }  
25 -  
26 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java
1 package cn.iocoder.yudao.module.system.mq.producer.mail; 1 package cn.iocoder.yudao.module.system.mq.producer.mail;
2 2
3 import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; 3 import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
4 -import cn.iocoder.yudao.module.system.mq.message.mail.MailAccountRefreshMessage;  
5 import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; 4 import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
6 -import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage;  
7 import lombok.extern.slf4j.Slf4j; 5 import lombok.extern.slf4j.Slf4j;
8 import org.springframework.stereotype.Component; 6 import org.springframework.stereotype.Component;
9 7
@@ -23,22 +21,6 @@ public class MailProducer { @@ -23,22 +21,6 @@ public class MailProducer {
23 private RedisMQTemplate redisMQTemplate; 21 private RedisMQTemplate redisMQTemplate;
24 22
25 /** 23 /**
26 - * 发送 {@link MailTemplateRefreshMessage} 消息  
27 - */  
28 - public void sendMailTemplateRefreshMessage() {  
29 - MailTemplateRefreshMessage message = new MailTemplateRefreshMessage();  
30 - redisMQTemplate.send(message);  
31 - }  
32 -  
33 - /**  
34 - * 发送 {@link MailAccountRefreshMessage} 消息  
35 - */  
36 - public void sendMailAccountRefreshMessage() {  
37 - MailAccountRefreshMessage message = new MailAccountRefreshMessage();  
38 - redisMQTemplate.send(message);  
39 - }  
40 -  
41 - /**  
42 * 发送 {@link MailSendMessage} 消息 24 * 发送 {@link MailSendMessage} 消息
43 * 25 *
44 * @param sendLogId 发送日志编码 26 * @param sendLogId 发送日志编码
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/notify/NotifyProducer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.producer.notify;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;  
4 -import cn.iocoder.yudao.module.system.mq.message.notify.NotifyTemplateRefreshMessage;  
5 -import lombok.extern.slf4j.Slf4j;  
6 -import org.springframework.stereotype.Component;  
7 -  
8 -import javax.annotation.Resource;  
9 -  
10 -/**  
11 - * Notify 站内信相关消息的 Producer  
12 - *  
13 - * @author xrcoder  
14 - * @since 2022-08-06  
15 - */  
16 -@Slf4j  
17 -@Component  
18 -public class NotifyProducer {  
19 -  
20 - @Resource  
21 - private RedisMQTemplate redisMQTemplate;  
22 -  
23 -  
24 - /**  
25 - * 发送 {@link NotifyTemplateRefreshMessage} 消息  
26 - */  
27 - public void sendNotifyTemplateRefreshMessage() {  
28 - NotifyTemplateRefreshMessage message = new NotifyTemplateRefreshMessage();  
29 - redisMQTemplate.send(message);  
30 - }  
31 -  
32 -  
33 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/package-info.java deleted
1 -/**  
2 - * 占位  
3 - */  
4 -package cn.iocoder.yudao.module.system.mq.producer;  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/MenuProducer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.producer.permission;  
2 -  
3 -import cn.iocoder.yudao.module.system.mq.message.permission.MenuRefreshMessage;  
4 -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;  
5 -import org.springframework.stereotype.Component;  
6 -  
7 -import javax.annotation.Resource;  
8 -  
9 -/**  
10 - * Menu 菜单相关消息的 Producer  
11 - */  
12 -@Component  
13 -public class MenuProducer {  
14 -  
15 - @Resource  
16 - private RedisMQTemplate redisMQTemplate;  
17 -  
18 - /**  
19 - * 发送 {@link MenuRefreshMessage} 消息  
20 - */  
21 - public void sendMenuRefreshMessage() {  
22 - MenuRefreshMessage message = new MenuRefreshMessage();  
23 - redisMQTemplate.send(message);  
24 - }  
25 -  
26 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/PermissionProducer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.producer.permission;  
2 -  
3 -import cn.iocoder.yudao.module.system.mq.message.permission.RoleMenuRefreshMessage;  
4 -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;  
5 -import cn.iocoder.yudao.module.system.mq.message.permission.UserRoleRefreshMessage;  
6 -import org.springframework.stereotype.Component;  
7 -  
8 -import javax.annotation.Resource;  
9 -  
10 -/**  
11 - * Permission 权限相关消息的 Producer  
12 - */  
13 -@Component  
14 -public class PermissionProducer {  
15 -  
16 - @Resource  
17 - private RedisMQTemplate redisMQTemplate;  
18 -  
19 - /**  
20 - * 发送 {@link RoleMenuRefreshMessage} 消息  
21 - */  
22 - public void sendRoleMenuRefreshMessage() {  
23 - RoleMenuRefreshMessage message = new RoleMenuRefreshMessage();  
24 - redisMQTemplate.send(message);  
25 - }  
26 -  
27 - /**  
28 - * 发送 {@link UserRoleRefreshMessage} 消息  
29 - */  
30 - public void sendUserRoleRefreshMessage() {  
31 - UserRoleRefreshMessage message = new UserRoleRefreshMessage();  
32 - redisMQTemplate.send(message);  
33 - }  
34 -  
35 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/RoleProducer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.producer.permission;  
2 -  
3 -import cn.iocoder.yudao.module.system.mq.message.permission.RoleRefreshMessage;  
4 -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;  
5 -import org.springframework.stereotype.Component;  
6 -  
7 -import javax.annotation.Resource;  
8 -  
9 -/**  
10 - * Role 角色相关消息的 Producer  
11 - *  
12 - * @author 芋道源码  
13 - */  
14 -@Component  
15 -public class RoleProducer {  
16 -  
17 - @Resource  
18 - private RedisMQTemplate redisMQTemplate;  
19 -  
20 - /**  
21 - * 发送 {@link RoleRefreshMessage} 消息  
22 - */  
23 - public void sendRoleRefreshMessage() {  
24 - RoleRefreshMessage message = new RoleRefreshMessage();  
25 - redisMQTemplate.send(message);  
26 - }  
27 -  
28 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sensitiveword/SensitiveWordProducer.java deleted
1 -package cn.iocoder.yudao.module.system.mq.producer.sensitiveword;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;  
4 -import cn.iocoder.yudao.module.system.mq.message.sensitiveword.SensitiveWordRefreshMessage;  
5 -import org.springframework.stereotype.Component;  
6 -  
7 -import javax.annotation.Resource;  
8 -  
9 -/**  
10 - * 敏感词相关的 Producer  
11 - */  
12 -@Component  
13 -public class SensitiveWordProducer {  
14 -  
15 - @Resource  
16 - private RedisMQTemplate redisMQTemplate;  
17 -  
18 - /**  
19 - * 发送 {@link SensitiveWordRefreshMessage} 消息  
20 - */  
21 - public void sendSensitiveWordRefreshMessage() {  
22 - SensitiveWordRefreshMessage message = new SensitiveWordRefreshMessage();  
23 - redisMQTemplate.send(message);  
24 - }  
25 -  
26 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sms/SmsProducer.java
1 package cn.iocoder.yudao.module.system.mq.producer.sms; 1 package cn.iocoder.yudao.module.system.mq.producer.sms;
2 2
3 import cn.iocoder.yudao.framework.common.core.KeyValue; 3 import cn.iocoder.yudao.framework.common.core.KeyValue;
4 -import cn.iocoder.yudao.module.system.mq.message.sms.SmsChannelRefreshMessage;  
5 -import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage;  
6 -import cn.iocoder.yudao.module.system.mq.message.sms.SmsTemplateRefreshMessage;  
7 import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; 4 import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
  5 +import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage;
8 import lombok.extern.slf4j.Slf4j; 6 import lombok.extern.slf4j.Slf4j;
9 import org.springframework.stereotype.Component; 7 import org.springframework.stereotype.Component;
10 8
@@ -15,7 +13,7 @@ import java.util.List; @@ -15,7 +13,7 @@ import java.util.List;
15 * Sms 短信相关消息的 Producer 13 * Sms 短信相关消息的 Producer
16 * 14 *
17 * @author zzf 15 * @author zzf
18 - * @date 2021/3/9 16:35 16 + * @since 2021/3/9 16:35
19 */ 17 */
20 @Slf4j 18 @Slf4j
21 @Component 19 @Component
@@ -25,22 +23,6 @@ public class SmsProducer { @@ -25,22 +23,6 @@ public class SmsProducer {
25 private RedisMQTemplate redisMQTemplate; 23 private RedisMQTemplate redisMQTemplate;
26 24
27 /** 25 /**
28 - * 发送 {@link SmsChannelRefreshMessage} 消息  
29 - */  
30 - public void sendSmsChannelRefreshMessage() {  
31 - SmsChannelRefreshMessage message = new SmsChannelRefreshMessage();  
32 - redisMQTemplate.send(message);  
33 - }  
34 -  
35 - /**  
36 - * 发送 {@link SmsTemplateRefreshMessage} 消息  
37 - */  
38 - public void sendSmsTemplateRefreshMessage() {  
39 - SmsTemplateRefreshMessage message = new SmsTemplateRefreshMessage();  
40 - redisMQTemplate.send(message);  
41 - }  
42 -  
43 - /**  
44 * 发送 {@link SmsSendMessage} 消息 26 * 发送 {@link SmsSendMessage} 消息
45 * 27 *
46 * @param logId 短信日志编号 28 * @param logId 短信日志编号
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 @@ -7,10 +7,7 @@ import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqV
7 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateReqVO; 7 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateReqVO;
8 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; 8 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
9 9
10 -import java.util.Collection;  
11 -import java.util.Collections;  
12 -import java.util.List;  
13 -import java.util.Map; 10 +import java.util.*;
14 11
15 /** 12 /**
16 * 部门 Service 接口 13 * 部门 Service 接口
@@ -20,11 +17,6 @@ import java.util.Map; @@ -20,11 +17,6 @@ import java.util.Map;
20 public interface DeptService { 17 public interface DeptService {
21 18
22 /** 19 /**
23 - * 初始化部门的本地缓存  
24 - */  
25 - void initLocalCache();  
26 -  
27 - /**  
28 * 创建部门 20 * 创建部门
29 * 21 *
30 * @param reqVO 部门信息 22 * @param reqVO 部门信息
@@ -47,21 +39,12 @@ public interface DeptService { @@ -47,21 +39,12 @@ public interface DeptService {
47 void deleteDept(Long id); 39 void deleteDept(Long id);
48 40
49 /** 41 /**
50 - * 筛选部门列表  
51 - *  
52 - * @param reqVO 筛选条件请求 VO  
53 - * @return 部门列表  
54 - */  
55 - List<DeptDO> getDeptList(DeptListReqVO reqVO);  
56 -  
57 - /**  
58 - * 获得所有子部门,从缓存中 42 + * 获得部门信息
59 * 43 *
60 - * @param parentId 部门编号  
61 - * @param recursive 是否递归获取所有  
62 - * @return 子部门列表 44 + * @param id 部门编号
  45 + * @return 部门信息
63 */ 46 */
64 - List<DeptDO> getDeptListByParentIdFromCache(Long parentId, boolean recursive); 47 + DeptDO getDept(Long id);
65 48
66 /** 49 /**
67 * 获得部门信息数组 50 * 获得部门信息数组
@@ -72,6 +55,14 @@ public interface DeptService { @@ -72,6 +55,14 @@ public interface DeptService {
72 List<DeptDO> getDeptList(Collection<Long> ids); 55 List<DeptDO> getDeptList(Collection<Long> ids);
73 56
74 /** 57 /**
  58 + * 筛选部门列表
  59 + *
  60 + * @param reqVO 筛选条件请求 VO
  61 + * @return 部门列表
  62 + */
  63 + List<DeptDO> getDeptList(DeptListReqVO reqVO);
  64 +
  65 + /**
75 * 获得指定编号的部门 Map 66 * 获得指定编号的部门 Map
76 * 67 *
77 * @param ids 部门编号数组 68 * @param ids 部门编号数组
@@ -86,12 +77,20 @@ public interface DeptService { @@ -86,12 +77,20 @@ public interface DeptService {
86 } 77 }
87 78
88 /** 79 /**
89 - * 获得部门信息 80 + * 获得指定部门的所有子部门
90 * 81 *
91 * @param id 部门编号 82 * @param id 部门编号
92 - * @return 部门信息 83 + * @return 子部门列表
93 */ 84 */
94 - DeptDO getDept(Long id); 85 + List<DeptDO> getChildDeptList(Long id);
  86 +
  87 + /**
  88 + * 获得所有子部门,从缓存中
  89 + *
  90 + * @param id 父部门编号
  91 + * @return 子部门列表
  92 + */
  93 + Set<Long> getChildDeptIdListFromCache(Long id);
95 94
96 /** 95 /**
97 * 校验部门们是否有效。如下情况,视为无效: 96 * 校验部门们是否有效。如下情况,视为无效:
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java
1 package cn.iocoder.yudao.module.system.service.dept; 1 package cn.iocoder.yudao.module.system.service.dept;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjectUtil;
4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;  
6 -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; 6 +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
7 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptCreateReqVO; 7 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptCreateReqVO;
8 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; 8 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
9 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateReqVO; 9 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateReqVO;
10 import cn.iocoder.yudao.module.system.convert.dept.DeptConvert; 10 import cn.iocoder.yudao.module.system.convert.dept.DeptConvert;
11 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; 11 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
12 import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper; 12 import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper;
  13 +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
13 import cn.iocoder.yudao.module.system.enums.dept.DeptIdEnum; 14 import cn.iocoder.yudao.module.system.enums.dept.DeptIdEnum;
14 -import cn.iocoder.yudao.module.system.mq.producer.dept.DeptProducer;  
15 -import com.google.common.collect.ImmutableMap;  
16 -import com.google.common.collect.ImmutableMultimap;  
17 -import com.google.common.collect.Multimap;  
18 -import lombok.Getter; 15 +import com.google.common.annotations.VisibleForTesting;
19 import lombok.extern.slf4j.Slf4j; 16 import lombok.extern.slf4j.Slf4j;
  17 +import org.springframework.cache.annotation.CacheEvict;
  18 +import org.springframework.cache.annotation.Cacheable;
20 import org.springframework.stereotype.Service; 19 import org.springframework.stereotype.Service;
21 import org.springframework.validation.annotation.Validated; 20 import org.springframework.validation.annotation.Validated;
22 21
23 -import javax.annotation.PostConstruct;  
24 import javax.annotation.Resource; 22 import javax.annotation.Resource;
25 import java.util.*; 23 import java.util.*;
26 24
27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  26 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
28 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; 27 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
29 28
30 /** 29 /**
@@ -37,55 +36,12 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @@ -37,55 +36,12 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
37 @Slf4j 36 @Slf4j
38 public class DeptServiceImpl implements DeptService { 37 public class DeptServiceImpl implements DeptService {
39 38
40 - /**  
41 - * 部门缓存  
42 - * key:部门编号 {@link DeptDO#getId()}  
43 - *  
44 - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向  
45 - */  
46 - @Getter  
47 - private volatile Map<Long, DeptDO> deptCache;  
48 - /**  
49 - * 父部门缓存  
50 - * key:部门编号 {@link DeptDO#getParentId()}  
51 - * value: 直接子部门列表  
52 - *  
53 - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向  
54 - */  
55 - @Getter  
56 - private volatile Multimap<Long, DeptDO> parentDeptCache;  
57 -  
58 @Resource 39 @Resource
59 private DeptMapper deptMapper; 40 private DeptMapper deptMapper;
60 41
61 - @Resource  
62 - private DeptProducer deptProducer;  
63 -  
64 - /**  
65 - * 初始化 {@link #parentDeptCache} 和 {@link #deptCache} 缓存  
66 - */  
67 - @Override  
68 - @PostConstruct  
69 - public synchronized void initLocalCache() {  
70 - // 注意:忽略自动多租户,因为要全局初始化缓存  
71 - TenantUtils.executeIgnore(() -> {  
72 - // 第一步:查询数据  
73 - List<DeptDO> depts = deptMapper.selectList();  
74 - log.info("[initLocalCache][缓存部门,数量为:{}]", depts.size());  
75 -  
76 - // 第二步:构建缓存  
77 - ImmutableMap.Builder<Long, DeptDO> builder = ImmutableMap.builder();  
78 - ImmutableMultimap.Builder<Long, DeptDO> parentBuilder = ImmutableMultimap.builder();  
79 - depts.forEach(deptDO -> {  
80 - builder.put(deptDO.getId(), deptDO);  
81 - parentBuilder.put(deptDO.getParentId(), deptDO);  
82 - });  
83 - deptCache = builder.build();  
84 - parentDeptCache = parentBuilder.build();  
85 - });  
86 - }  
87 -  
88 @Override 42 @Override
  43 + @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST,
  44 + allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存
89 public Long createDept(DeptCreateReqVO reqVO) { 45 public Long createDept(DeptCreateReqVO reqVO) {
90 // 校验正确性 46 // 校验正确性
91 if (reqVO.getParentId() == null) { 47 if (reqVO.getParentId() == null) {
@@ -95,12 +51,12 @@ public class DeptServiceImpl implements DeptService { @@ -95,12 +51,12 @@ public class DeptServiceImpl implements DeptService {
95 // 插入部门 51 // 插入部门
96 DeptDO dept = DeptConvert.INSTANCE.convert(reqVO); 52 DeptDO dept = DeptConvert.INSTANCE.convert(reqVO);
97 deptMapper.insert(dept); 53 deptMapper.insert(dept);
98 - // 发送刷新消息  
99 - deptProducer.sendDeptRefreshMessage();  
100 return dept.getId(); 54 return dept.getId();
101 } 55 }
102 56
103 @Override 57 @Override
  58 + @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST,
  59 + allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存
104 public void updateDept(DeptUpdateReqVO reqVO) { 60 public void updateDept(DeptUpdateReqVO reqVO) {
105 // 校验正确性 61 // 校验正确性
106 if (reqVO.getParentId() == null) { 62 if (reqVO.getParentId() == null) {
@@ -110,11 +66,11 @@ public class DeptServiceImpl implements DeptService { @@ -110,11 +66,11 @@ public class DeptServiceImpl implements DeptService {
110 // 更新部门 66 // 更新部门
111 DeptDO updateObj = DeptConvert.INSTANCE.convert(reqVO); 67 DeptDO updateObj = DeptConvert.INSTANCE.convert(reqVO);
112 deptMapper.updateById(updateObj); 68 deptMapper.updateById(updateObj);
113 - // 发送刷新消息  
114 - deptProducer.sendDeptRefreshMessage();  
115 } 69 }
116 70
117 @Override 71 @Override
  72 + @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST,
  73 + allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存
118 public void deleteDept(Long id) { 74 public void deleteDept(Long id) {
119 // 校验是否存在 75 // 校验是否存在
120 validateDeptExists(id); 76 validateDeptExists(id);
@@ -124,70 +80,30 @@ public class DeptServiceImpl implements DeptService { @@ -124,70 +80,30 @@ public class DeptServiceImpl implements DeptService {
124 } 80 }
125 // 删除部门 81 // 删除部门
126 deptMapper.deleteById(id); 82 deptMapper.deleteById(id);
127 - // 发送刷新消息  
128 - deptProducer.sendDeptRefreshMessage();  
129 - }  
130 -  
131 - @Override  
132 - public List<DeptDO> getDeptList(DeptListReqVO reqVO) {  
133 - return deptMapper.selectList(reqVO);  
134 - }  
135 -  
136 - @Override  
137 - public List<DeptDO> getDeptListByParentIdFromCache(Long parentId, boolean recursive) {  
138 - if (parentId == null) {  
139 - return Collections.emptyList();  
140 - }  
141 - List<DeptDO> result = new ArrayList<>();  
142 - // 递归,简单粗暴  
143 - getDeptsByParentIdFromCache(result, parentId,  
144 - recursive ? Integer.MAX_VALUE : 1, // 如果递归获取,则无限;否则,只递归 1 次  
145 - parentDeptCache);  
146 - return result;  
147 - }  
148 -  
149 - /**  
150 - * 递归获取所有的子部门,添加到 result 结果  
151 - *  
152 - * @param result 结果  
153 - * @param parentId 父编号  
154 - * @param recursiveCount 递归次数  
155 - * @param parentDeptMap 父部门 Map,使用缓存,避免变化  
156 - */  
157 - private void getDeptsByParentIdFromCache(List<DeptDO> result, Long parentId, int recursiveCount,  
158 - Multimap<Long, DeptDO> parentDeptMap) {  
159 - // 递归次数为 0,结束!  
160 - if (recursiveCount == 0) {  
161 - return;  
162 - }  
163 -  
164 - // 获得子部门  
165 - Collection<DeptDO> depts = parentDeptMap.get(parentId);  
166 - if (CollUtil.isEmpty(depts)) {  
167 - return;  
168 - }  
169 - // 针对多租户,过滤掉非当前租户的部门  
170 - Long tenantId = TenantContextHolder.getTenantId();  
171 - if (tenantId != null) {  
172 - depts = CollUtil.filterNew(depts, dept -> tenantId.equals(dept.getTenantId()));  
173 - }  
174 - result.addAll(depts);  
175 -  
176 - // 继续递归  
177 - depts.forEach(dept -> getDeptsByParentIdFromCache(result, dept.getId(),  
178 - recursiveCount - 1, parentDeptMap));  
179 } 83 }
180 84
181 private void validateForCreateOrUpdate(Long id, Long parentId, String name) { 85 private void validateForCreateOrUpdate(Long id, Long parentId, String name) {
182 // 校验自己存在 86 // 校验自己存在
183 validateDeptExists(id); 87 validateDeptExists(id);
184 // 校验父部门的有效性 88 // 校验父部门的有效性
185 - validateParentDeptEnable(id, parentId); 89 + validateParentDept(id, parentId);
186 // 校验部门名的唯一性 90 // 校验部门名的唯一性
187 validateDeptNameUnique(id, parentId, name); 91 validateDeptNameUnique(id, parentId, name);
188 } 92 }
189 93
190 - private void validateParentDeptEnable(Long id, Long parentId) { 94 + @VisibleForTesting
  95 + void validateDeptExists(Long id) {
  96 + if (id == null) {
  97 + return;
  98 + }
  99 + DeptDO dept = deptMapper.selectById(id);
  100 + if (dept == null) {
  101 + throw exception(DEPT_NOT_FOUND);
  102 + }
  103 + }
  104 +
  105 + @VisibleForTesting
  106 + void validateParentDept(Long id, Long parentId) {
191 if (parentId == null || DeptIdEnum.ROOT.getId().equals(parentId)) { 107 if (parentId == null || DeptIdEnum.ROOT.getId().equals(parentId)) {
192 return; 108 return;
193 } 109 }
@@ -200,49 +116,71 @@ public class DeptServiceImpl implements DeptService { @@ -200,49 +116,71 @@ public class DeptServiceImpl implements DeptService {
200 if (dept == null) { 116 if (dept == null) {
201 throw exception(DEPT_PARENT_NOT_EXITS); 117 throw exception(DEPT_PARENT_NOT_EXITS);
202 } 118 }
203 - // 父部门被禁用  
204 - if (!CommonStatusEnum.ENABLE.getStatus().equals(dept.getStatus())) {  
205 - throw exception(DEPT_NOT_ENABLE);  
206 - }  
207 // 父部门不能是原来的子部门 119 // 父部门不能是原来的子部门
208 - List<DeptDO> children = getDeptListByParentIdFromCache(id, true); 120 + List<DeptDO> children = getChildDeptList(id);
209 if (children.stream().anyMatch(dept1 -> dept1.getId().equals(parentId))) { 121 if (children.stream().anyMatch(dept1 -> dept1.getId().equals(parentId))) {
210 throw exception(DEPT_PARENT_IS_CHILD); 122 throw exception(DEPT_PARENT_IS_CHILD);
211 } 123 }
212 } 124 }
213 125
214 - private void validateDeptExists(Long id) {  
215 - if (id == null) {  
216 - return;  
217 - }  
218 - DeptDO dept = deptMapper.selectById(id); 126 + @VisibleForTesting
  127 + void validateDeptNameUnique(Long id, Long parentId, String name) {
  128 + DeptDO dept = deptMapper.selectByParentIdAndName(parentId, name);
219 if (dept == null) { 129 if (dept == null) {
220 - throw exception(DEPT_NOT_FOUND);  
221 - }  
222 - }  
223 -  
224 - private void validateDeptNameUnique(Long id, Long parentId, String name) {  
225 - DeptDO menu = deptMapper.selectByParentIdAndName(parentId, name);  
226 - if (menu == null) {  
227 return; 130 return;
228 } 131 }
229 // 如果 id 为空,说明不用比较是否为相同 id 的岗位 132 // 如果 id 为空,说明不用比较是否为相同 id 的岗位
230 if (id == null) { 133 if (id == null) {
231 throw exception(DEPT_NAME_DUPLICATE); 134 throw exception(DEPT_NAME_DUPLICATE);
232 } 135 }
233 - if (!menu.getId().equals(id)) { 136 + if (ObjectUtil.notEqual(dept.getId(), id)) {
234 throw exception(DEPT_NAME_DUPLICATE); 137 throw exception(DEPT_NAME_DUPLICATE);
235 } 138 }
236 } 139 }
237 140
238 @Override 141 @Override
  142 + public DeptDO getDept(Long id) {
  143 + return deptMapper.selectById(id);
  144 + }
  145 +
  146 + @Override
239 public List<DeptDO> getDeptList(Collection<Long> ids) { 147 public List<DeptDO> getDeptList(Collection<Long> ids) {
  148 + if (CollUtil.isEmpty(ids)) {
  149 + return Collections.emptyList();
  150 + }
240 return deptMapper.selectBatchIds(ids); 151 return deptMapper.selectBatchIds(ids);
241 } 152 }
242 153
243 @Override 154 @Override
244 - public DeptDO getDept(Long id) {  
245 - return deptMapper.selectById(id); 155 + public List<DeptDO> getDeptList(DeptListReqVO reqVO) {
  156 + return deptMapper.selectList(reqVO);
  157 + }
  158 +
  159 + @Override
  160 + public List<DeptDO> getChildDeptList(Long id) {
  161 + List<DeptDO> children = new LinkedList<>();
  162 + // 遍历每一层
  163 + Collection<Long> parentIds = Collections.singleton(id);
  164 + for (int i = 0; i < Short.MAX_VALUE; i++) { // 使用 Short.MAX_VALUE 避免 bug 场景下,存在死循环
  165 + // 查询当前层,所有的子部门
  166 + List<DeptDO> depts = deptMapper.selectListByParentId(parentIds);
  167 + // 1. 如果没有子部门,则结束遍历
  168 + if (CollUtil.isEmpty(depts)) {
  169 + break;
  170 + }
  171 + // 2. 如果有子部门,继续遍历
  172 + children.addAll(depts);
  173 + parentIds = convertSet(depts, DeptDO::getId);
  174 + }
  175 + return children;
  176 + }
  177 +
  178 + @Override
  179 + @DataPermission(enable = false) // 禁用数据权限,避免简历不正确的缓存
  180 + @Cacheable(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, key = "#id")
  181 + public Set<Long> getChildDeptIdListFromCache(Long id) {
  182 + List<DeptDO> children = getChildDeptList(id);
  183 + return convertSet(children, DeptDO::getId);
246 } 184 }
247 185
248 @Override 186 @Override
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java
@@ -18,19 +18,6 @@ import java.util.List; @@ -18,19 +18,6 @@ import java.util.List;
18 public interface MailAccountService { 18 public interface MailAccountService {
19 19
20 /** 20 /**
21 - * 初始化邮箱账号的本地缓存  
22 - */  
23 - void initLocalCache();  
24 -  
25 - /**  
26 - * 从缓存中获取邮箱账号  
27 - *  
28 - * @param id 编号  
29 - * @return 邮箱账号  
30 - */  
31 - MailAccountDO getMailAccountFromCache(Long id);  
32 -  
33 - /**  
34 * 创建邮箱账号 21 * 创建邮箱账号
35 * 22 *
36 * @param createReqVO 邮箱账号信息 23 * @param createReqVO 邮箱账号信息
@@ -61,6 +48,14 @@ public interface MailAccountService { @@ -61,6 +48,14 @@ public interface MailAccountService {
61 MailAccountDO getMailAccount(Long id); 48 MailAccountDO getMailAccount(Long id);
62 49
63 /** 50 /**
  51 + * 从缓存中获取邮箱账号
  52 + *
  53 + * @param id 编号
  54 + * @return 邮箱账号
  55 + */
  56 + MailAccountDO getMailAccountFromCache(Long id);
  57 +
  58 + /**
64 * 获取邮箱账号分页信息 59 * 获取邮箱账号分页信息
65 * 60 *
66 * @param pageReqVO 邮箱账号分页参数 61 * @param pageReqVO 邮箱账号分页参数
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 @@ -7,20 +7,18 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccou
7 import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert; 7 import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
8 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; 8 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
9 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper; 9 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper;
10 -import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;  
11 -import lombok.Getter; 10 +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
12 import lombok.extern.slf4j.Slf4j; 11 import lombok.extern.slf4j.Slf4j;
  12 +import org.springframework.cache.annotation.Cacheable;
13 import org.springframework.stereotype.Service; 13 import org.springframework.stereotype.Service;
14 import org.springframework.validation.annotation.Validated; 14 import org.springframework.validation.annotation.Validated;
15 15
16 -import javax.annotation.PostConstruct;  
17 import javax.annotation.Resource; 16 import javax.annotation.Resource;
18 import java.util.List; 17 import java.util.List;
19 -import java.util.Map;  
20 18
21 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
22 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;  
23 -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; 20 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_NOT_EXISTS;
  21 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS;
24 22
25 /** 23 /**
26 * 邮箱账号 Service 实现类 24 * 邮箱账号 Service 实现类
@@ -39,46 +37,16 @@ public class MailAccountServiceImpl implements MailAccountService { @@ -39,46 +37,16 @@ public class MailAccountServiceImpl implements MailAccountService {
39 @Resource 37 @Resource
40 private MailTemplateService mailTemplateService; 38 private MailTemplateService mailTemplateService;
41 39
42 - @Resource  
43 - private MailProducer mailProducer;  
44 -  
45 - /**  
46 - * 邮箱账号缓存  
47 - * key:邮箱账号编码 {@link MailAccountDO#getId()}  
48 - *  
49 - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向  
50 - */  
51 - @Getter  
52 - private volatile Map<Long, MailAccountDO> mailAccountCache;  
53 -  
54 - @Override  
55 - @PostConstruct  
56 - public void initLocalCache() {  
57 - // 第一步:查询数据  
58 - List<MailAccountDO> accounts = mailAccountMapper.selectList();  
59 - log.info("[initLocalCache][缓存邮箱账号,数量:{}]", accounts.size());  
60 -  
61 - // 第二步:构建缓存  
62 - mailAccountCache = convertMap(accounts, MailAccountDO::getId);  
63 - }  
64 -  
65 - @Override  
66 - public MailAccountDO getMailAccountFromCache(Long id) {  
67 - return mailAccountCache.get(id);  
68 - }  
69 -  
70 @Override 40 @Override
71 public Long createMailAccount(MailAccountCreateReqVO createReqVO) { 41 public Long createMailAccount(MailAccountCreateReqVO createReqVO) {
72 // 插入 42 // 插入
73 MailAccountDO account = MailAccountConvert.INSTANCE.convert(createReqVO); 43 MailAccountDO account = MailAccountConvert.INSTANCE.convert(createReqVO);
74 mailAccountMapper.insert(account); 44 mailAccountMapper.insert(account);
75 -  
76 - // 发送刷新消息  
77 - mailProducer.sendMailAccountRefreshMessage();  
78 return account.getId(); 45 return account.getId();
79 } 46 }
80 47
81 @Override 48 @Override
  49 + @Cacheable(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#updateReqVO.id")
82 public void updateMailAccount(MailAccountUpdateReqVO updateReqVO) { 50 public void updateMailAccount(MailAccountUpdateReqVO updateReqVO) {
83 // 校验是否存在 51 // 校验是否存在
84 validateMailAccountExists(updateReqVO.getId()); 52 validateMailAccountExists(updateReqVO.getId());
@@ -86,11 +54,10 @@ public class MailAccountServiceImpl implements MailAccountService { @@ -86,11 +54,10 @@ public class MailAccountServiceImpl implements MailAccountService {
86 // 更新 54 // 更新
87 MailAccountDO updateObj = MailAccountConvert.INSTANCE.convert(updateReqVO); 55 MailAccountDO updateObj = MailAccountConvert.INSTANCE.convert(updateReqVO);
88 mailAccountMapper.updateById(updateObj); 56 mailAccountMapper.updateById(updateObj);
89 - // 发送刷新消息  
90 - mailProducer.sendMailAccountRefreshMessage();  
91 } 57 }
92 58
93 @Override 59 @Override
  60 + @Cacheable(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#id")
94 public void deleteMailAccount(Long id) { 61 public void deleteMailAccount(Long id) {
95 // 校验是否存在账号 62 // 校验是否存在账号
96 validateMailAccountExists(id); 63 validateMailAccountExists(id);
@@ -101,8 +68,6 @@ public class MailAccountServiceImpl implements MailAccountService { @@ -101,8 +68,6 @@ public class MailAccountServiceImpl implements MailAccountService {
101 68
102 // 删除 69 // 删除
103 mailAccountMapper.deleteById(id); 70 mailAccountMapper.deleteById(id);
104 - // 发送刷新消息  
105 - mailProducer.sendMailAccountRefreshMessage();  
106 } 71 }
107 72
108 private void validateMailAccountExists(Long id) { 73 private void validateMailAccountExists(Long id) {
@@ -117,6 +82,12 @@ public class MailAccountServiceImpl implements MailAccountService { @@ -117,6 +82,12 @@ public class MailAccountServiceImpl implements MailAccountService {
117 } 82 }
118 83
119 @Override 84 @Override
  85 + @Cacheable(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#id", unless = "#result == null")
  86 + public MailAccountDO getMailAccountFromCache(Long id) {
  87 + return getMailAccount(id);
  88 + }
  89 +
  90 + @Override
120 public PageResult<MailAccountDO> getMailAccountPage(MailAccountPageReqVO pageReqVO) { 91 public PageResult<MailAccountDO> getMailAccountPage(MailAccountPageReqVO pageReqVO) {
121 return mailAccountMapper.selectPage(pageReqVO); 92 return mailAccountMapper.selectPage(pageReqVO);
122 } 93 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java
@@ -19,11 +19,6 @@ import java.util.Map; @@ -19,11 +19,6 @@ import java.util.Map;
19 public interface MailTemplateService { 19 public interface MailTemplateService {
20 20
21 /** 21 /**
22 - * 初始化邮件模版的本地缓存  
23 - */  
24 - void initLocalCache();  
25 -  
26 - /**  
27 * 邮件模版创建 22 * 邮件模版创建
28 * 23 *
29 * @param createReqVO 邮件信息 24 * @param createReqVO 邮件信息
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 @@ -10,14 +10,14 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemp
10 import cn.iocoder.yudao.module.system.convert.mail.MailTemplateConvert; 10 import cn.iocoder.yudao.module.system.convert.mail.MailTemplateConvert;
11 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; 11 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
12 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper; 12 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
13 -import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer; 13 +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
14 import com.google.common.annotations.VisibleForTesting; 14 import com.google.common.annotations.VisibleForTesting;
15 -import lombok.Getter;  
16 import lombok.extern.slf4j.Slf4j; 15 import lombok.extern.slf4j.Slf4j;
  16 +import org.springframework.cache.annotation.CacheEvict;
  17 +import org.springframework.cache.annotation.Cacheable;
17 import org.springframework.stereotype.Service; 18 import org.springframework.stereotype.Service;
18 import org.springframework.validation.annotation.Validated; 19 import org.springframework.validation.annotation.Validated;
19 20
20 -import javax.annotation.PostConstruct;  
21 import javax.annotation.Resource; 21 import javax.annotation.Resource;
22 import javax.validation.Valid; 22 import javax.validation.Valid;
23 import java.util.List; 23 import java.util.List;
@@ -25,8 +25,8 @@ import java.util.Map; @@ -25,8 +25,8 @@ import java.util.Map;
25 import java.util.regex.Pattern; 25 import java.util.regex.Pattern;
26 26
27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
28 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;  
29 -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; 28 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_CODE_EXISTS;
  29 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS;
30 30
31 /** 31 /**
32 * 邮箱模版 Service 实现类 32 * 邮箱模版 Service 实现类
@@ -47,29 +47,6 @@ public class MailTemplateServiceImpl implements MailTemplateService { @@ -47,29 +47,6 @@ public class MailTemplateServiceImpl implements MailTemplateService {
47 @Resource 47 @Resource
48 private MailTemplateMapper mailTemplateMapper; 48 private MailTemplateMapper mailTemplateMapper;
49 49
50 - @Resource  
51 - private MailProducer mailProducer;  
52 -  
53 - /**  
54 - * 邮件模板缓存  
55 - * key:邮件模版标识 {@link MailTemplateDO#getCode()}  
56 - *  
57 - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向  
58 - */  
59 - @Getter  
60 - private volatile Map<String, MailTemplateDO> mailTemplateCache;  
61 -  
62 - @Override  
63 - @PostConstruct  
64 - public void initLocalCache() {  
65 - // 第一步:查询数据  
66 - List<MailTemplateDO> templates = mailTemplateMapper.selectList();  
67 - log.info("[initLocalCache][缓存邮件模版,数量:{}]", templates.size());  
68 -  
69 - // 第二步:构建缓存  
70 - mailTemplateCache = convertMap(templates, MailTemplateDO::getCode);  
71 - }  
72 -  
73 @Override 50 @Override
74 public Long createMailTemplate(MailTemplateCreateReqVO createReqVO) { 51 public Long createMailTemplate(MailTemplateCreateReqVO createReqVO) {
75 // 校验 code 是否唯一 52 // 校验 code 是否唯一
@@ -79,12 +56,12 @@ public class MailTemplateServiceImpl implements MailTemplateService { @@ -79,12 +56,12 @@ public class MailTemplateServiceImpl implements MailTemplateService {
79 MailTemplateDO template = MailTemplateConvert.INSTANCE.convert(createReqVO) 56 MailTemplateDO template = MailTemplateConvert.INSTANCE.convert(createReqVO)
80 .setParams(parseTemplateContentParams(createReqVO.getContent())); 57 .setParams(parseTemplateContentParams(createReqVO.getContent()));
81 mailTemplateMapper.insert(template); 58 mailTemplateMapper.insert(template);
82 - // 发送刷新消息  
83 - mailProducer.sendMailTemplateRefreshMessage();  
84 return template.getId(); 59 return template.getId();
85 } 60 }
86 61
87 @Override 62 @Override
  63 + @CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE,
  64 + allEntries = true) // allEntries 清空所有缓存,因为可能修改到 code 字段,不好清理
88 public void updateMailTemplate(@Valid MailTemplateUpdateReqVO updateReqVO) { 65 public void updateMailTemplate(@Valid MailTemplateUpdateReqVO updateReqVO) {
89 // 校验是否存在 66 // 校验是否存在
90 validateMailTemplateExists(updateReqVO.getId()); 67 validateMailTemplateExists(updateReqVO.getId());
@@ -95,12 +72,10 @@ public class MailTemplateServiceImpl implements MailTemplateService { @@ -95,12 +72,10 @@ public class MailTemplateServiceImpl implements MailTemplateService {
95 MailTemplateDO updateObj = MailTemplateConvert.INSTANCE.convert(updateReqVO) 72 MailTemplateDO updateObj = MailTemplateConvert.INSTANCE.convert(updateReqVO)
96 .setParams(parseTemplateContentParams(updateReqVO.getContent())); 73 .setParams(parseTemplateContentParams(updateReqVO.getContent()));
97 mailTemplateMapper.updateById(updateObj); 74 mailTemplateMapper.updateById(updateObj);
98 - // 发送刷新消息  
99 - mailProducer.sendMailTemplateRefreshMessage();  
100 } 75 }
101 76
102 @VisibleForTesting 77 @VisibleForTesting
103 - public void validateCodeUnique(Long id, String code) { 78 + void validateCodeUnique(Long id, String code) {
104 MailTemplateDO template = mailTemplateMapper.selectByCode(code); 79 MailTemplateDO template = mailTemplateMapper.selectByCode(code);
105 if (template == null) { 80 if (template == null) {
106 return; 81 return;
@@ -113,14 +88,14 @@ public class MailTemplateServiceImpl implements MailTemplateService { @@ -113,14 +88,14 @@ public class MailTemplateServiceImpl implements MailTemplateService {
113 } 88 }
114 89
115 @Override 90 @Override
  91 + @CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE,
  92 + allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 code,不好清理
116 public void deleteMailTemplate(Long id) { 93 public void deleteMailTemplate(Long id) {
117 // 校验是否存在 94 // 校验是否存在
118 validateMailTemplateExists(id); 95 validateMailTemplateExists(id);
119 96
120 // 删除 97 // 删除
121 mailTemplateMapper.deleteById(id); 98 mailTemplateMapper.deleteById(id);
122 - // 发送刷新消息  
123 - mailProducer.sendMailTemplateRefreshMessage();  
124 } 99 }
125 100
126 private void validateMailTemplateExists(Long id) { 101 private void validateMailTemplateExists(Long id) {
@@ -133,6 +108,12 @@ public class MailTemplateServiceImpl implements MailTemplateService { @@ -133,6 +108,12 @@ public class MailTemplateServiceImpl implements MailTemplateService {
133 public MailTemplateDO getMailTemplate(Long id) {return mailTemplateMapper.selectById(id);} 108 public MailTemplateDO getMailTemplate(Long id) {return mailTemplateMapper.selectById(id);}
134 109
135 @Override 110 @Override
  111 + @Cacheable(value = RedisKeyConstants.MAIL_TEMPLATE, key = "#code", unless = "#result == null")
  112 + public MailTemplateDO getMailTemplateByCodeFromCache(String code) {
  113 + return mailTemplateMapper.selectByCode(code);
  114 + }
  115 +
  116 + @Override
136 public PageResult<MailTemplateDO> getMailTemplatePage(MailTemplatePageReqVO pageReqVO) { 117 public PageResult<MailTemplateDO> getMailTemplatePage(MailTemplatePageReqVO pageReqVO) {
137 return mailTemplateMapper.selectPage(pageReqVO); 118 return mailTemplateMapper.selectPage(pageReqVO);
138 } 119 }
@@ -141,11 +122,6 @@ public class MailTemplateServiceImpl implements MailTemplateService { @@ -141,11 +122,6 @@ public class MailTemplateServiceImpl implements MailTemplateService {
141 public List<MailTemplateDO> getMailTemplateList() {return mailTemplateMapper.selectList();} 122 public List<MailTemplateDO> getMailTemplateList() {return mailTemplateMapper.selectList();}
142 123
143 @Override 124 @Override
144 - public MailTemplateDO getMailTemplateByCodeFromCache(String code) {  
145 - return mailTemplateCache.get(code);  
146 - }  
147 -  
148 - @Override  
149 public String formatMailTemplateContent(String content, Map<String, Object> params) { 125 public String formatMailTemplateContent(String content, Map<String, Object> params) {
150 return StrUtil.format(content, params); 126 return StrUtil.format(content, params);
151 } 127 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java
@@ -17,19 +17,6 @@ import java.util.Map; @@ -17,19 +17,6 @@ import java.util.Map;
17 public interface NotifyTemplateService { 17 public interface NotifyTemplateService {
18 18
19 /** 19 /**
20 - * 初始化站内信模板的本地缓存  
21 - */  
22 - void initLocalCache();  
23 -  
24 - /**  
25 - * 获得站内信模板,从缓存中  
26 - *  
27 - * @param code 模板编码  
28 - * @return 站内信模板  
29 - */  
30 - NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code);  
31 -  
32 - /**  
33 * 创建站内信模版 20 * 创建站内信模版
34 * 21 *
35 * @param createReqVO 创建信息 22 * @param createReqVO 创建信息
@@ -60,6 +47,14 @@ public interface NotifyTemplateService { @@ -60,6 +47,14 @@ public interface NotifyTemplateService {
60 NotifyTemplateDO getNotifyTemplate(Long id); 47 NotifyTemplateDO getNotifyTemplate(Long id);
61 48
62 /** 49 /**
  50 + * 获得站内信模板,从缓存中
  51 + *
  52 + * @param code 模板编码
  53 + * @return 站内信模板
  54 + */
  55 + NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code);
  56 +
  57 + /**
63 * 获得站内信模版分页 58 * 获得站内信模版分页
64 * 59 *
65 * @param pageReqVO 分页查询 60 * @param pageReqVO 分页查询
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; @@ -3,27 +3,28 @@ package cn.iocoder.yudao.module.system.service.notify;
3 import cn.hutool.core.util.ReUtil; 3 import cn.hutool.core.util.ReUtil;
4 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;  
7 import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateCreateReqVO; 6 import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateCreateReqVO;
8 import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO; 7 import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO;
9 import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO; 8 import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO;
10 import cn.iocoder.yudao.module.system.convert.notify.NotifyTemplateConvert; 9 import cn.iocoder.yudao.module.system.convert.notify.NotifyTemplateConvert;
11 import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; 10 import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
12 import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper; 11 import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper;
13 -import cn.iocoder.yudao.module.system.mq.producer.notify.NotifyProducer; 12 +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
14 import com.google.common.annotations.VisibleForTesting; 13 import com.google.common.annotations.VisibleForTesting;
15 import lombok.extern.slf4j.Slf4j; 14 import lombok.extern.slf4j.Slf4j;
  15 +import org.springframework.cache.annotation.CacheEvict;
  16 +import org.springframework.cache.annotation.Cacheable;
16 import org.springframework.stereotype.Service; 17 import org.springframework.stereotype.Service;
17 import org.springframework.validation.annotation.Validated; 18 import org.springframework.validation.annotation.Validated;
18 19
19 -import javax.annotation.PostConstruct;  
20 import javax.annotation.Resource; 20 import javax.annotation.Resource;
21 import java.util.List; 21 import java.util.List;
22 import java.util.Map; 22 import java.util.Map;
23 import java.util.regex.Pattern; 23 import java.util.regex.Pattern;
24 24
25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
26 -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; 26 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_CODE_DUPLICATE;
  27 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS;
27 28
28 /** 29 /**
29 * 站内信模版 Service 实现类 30 * 站内信模版 Service 实现类
@@ -43,36 +44,6 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService { @@ -43,36 +44,6 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
43 @Resource 44 @Resource
44 private NotifyTemplateMapper notifyTemplateMapper; 45 private NotifyTemplateMapper notifyTemplateMapper;
45 46
46 - @Resource  
47 - private NotifyProducer notifyProducer;  
48 -  
49 - /**  
50 - * 站内信模板缓存  
51 - * key:站内信模板编码 {@link NotifyTemplateDO#getCode()}  
52 - * <p>  
53 - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向  
54 - */  
55 - private volatile Map<String, NotifyTemplateDO> notifyTemplateCache;  
56 -  
57 - /**  
58 - * 初始化站内信模板的本地缓存  
59 - */  
60 - @Override  
61 - @PostConstruct  
62 - public void initLocalCache() {  
63 - // 第一步:查询数据  
64 - List<NotifyTemplateDO> templates = notifyTemplateMapper.selectList();  
65 - log.info("[initLocalCache][缓存站内信模版,数量为:{}]", templates.size());  
66 -  
67 - // 第二步:构建缓存  
68 - notifyTemplateCache = CollectionUtils.convertMap(templates, NotifyTemplateDO::getCode);  
69 - }  
70 -  
71 - @Override  
72 - public NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code) {  
73 - return notifyTemplateCache.get(code);  
74 - }  
75 -  
76 @Override 47 @Override
77 public Long createNotifyTemplate(NotifyTemplateCreateReqVO createReqVO) { 48 public Long createNotifyTemplate(NotifyTemplateCreateReqVO createReqVO) {
78 // 校验站内信编码是否重复 49 // 校验站内信编码是否重复
@@ -82,13 +53,12 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService { @@ -82,13 +53,12 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
82 NotifyTemplateDO notifyTemplate = NotifyTemplateConvert.INSTANCE.convert(createReqVO); 53 NotifyTemplateDO notifyTemplate = NotifyTemplateConvert.INSTANCE.convert(createReqVO);
83 notifyTemplate.setParams(parseTemplateContentParams(notifyTemplate.getContent())); 54 notifyTemplate.setParams(parseTemplateContentParams(notifyTemplate.getContent()));
84 notifyTemplateMapper.insert(notifyTemplate); 55 notifyTemplateMapper.insert(notifyTemplate);
85 -  
86 - // 发送刷新消息  
87 - notifyProducer.sendNotifyTemplateRefreshMessage();  
88 return notifyTemplate.getId(); 56 return notifyTemplate.getId();
89 } 57 }
90 58
91 @Override 59 @Override
  60 + @CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE,
  61 + allEntries = true) // allEntries 清空所有缓存,因为可能修改到 code 字段,不好清理
92 public void updateNotifyTemplate(NotifyTemplateUpdateReqVO updateReqVO) { 62 public void updateNotifyTemplate(NotifyTemplateUpdateReqVO updateReqVO) {
93 // 校验存在 63 // 校验存在
94 validateNotifyTemplateExists(updateReqVO.getId()); 64 validateNotifyTemplateExists(updateReqVO.getId());
@@ -99,9 +69,6 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService { @@ -99,9 +69,6 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
99 NotifyTemplateDO updateObj = NotifyTemplateConvert.INSTANCE.convert(updateReqVO); 69 NotifyTemplateDO updateObj = NotifyTemplateConvert.INSTANCE.convert(updateReqVO);
100 updateObj.setParams(parseTemplateContentParams(updateObj.getContent())); 70 updateObj.setParams(parseTemplateContentParams(updateObj.getContent()));
101 notifyTemplateMapper.updateById(updateObj); 71 notifyTemplateMapper.updateById(updateObj);
102 -  
103 - // 发送刷新消息  
104 - notifyProducer.sendNotifyTemplateRefreshMessage();  
105 } 72 }
106 73
107 @VisibleForTesting 74 @VisibleForTesting
@@ -110,13 +77,13 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService { @@ -110,13 +77,13 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
110 } 77 }
111 78
112 @Override 79 @Override
  80 + @CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE,
  81 + allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 code,不好清理
113 public void deleteNotifyTemplate(Long id) { 82 public void deleteNotifyTemplate(Long id) {
114 // 校验存在 83 // 校验存在
115 validateNotifyTemplateExists(id); 84 validateNotifyTemplateExists(id);
116 // 删除 85 // 删除
117 notifyTemplateMapper.deleteById(id); 86 notifyTemplateMapper.deleteById(id);
118 - // 发送刷新消息  
119 - notifyProducer.sendNotifyTemplateRefreshMessage();  
120 } 87 }
121 88
122 private void validateNotifyTemplateExists(Long id) { 89 private void validateNotifyTemplateExists(Long id) {
@@ -131,12 +98,19 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService { @@ -131,12 +98,19 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
131 } 98 }
132 99
133 @Override 100 @Override
  101 + @Cacheable(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, key = "#code",
  102 + unless = "#result == null")
  103 + public NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code) {
  104 + return notifyTemplateMapper.selectByCode(code);
  105 + }
  106 +
  107 + @Override
134 public PageResult<NotifyTemplateDO> getNotifyTemplatePage(NotifyTemplatePageReqVO pageReqVO) { 108 public PageResult<NotifyTemplateDO> getNotifyTemplatePage(NotifyTemplatePageReqVO pageReqVO) {
135 return notifyTemplateMapper.selectPage(pageReqVO); 109 return notifyTemplateMapper.selectPage(pageReqVO);
136 } 110 }
137 111
138 @VisibleForTesting 112 @VisibleForTesting
139 - public void validateNotifyTemplateCodeDuplicate(Long id, String code) { 113 + void validateNotifyTemplateCodeDuplicate(Long id, String code) {
140 NotifyTemplateDO template = notifyTemplateMapper.selectByCode(code); 114 NotifyTemplateDO template = notifyTemplateMapper.selectByCode(code);
141 if (template == null) { 115 if (template == null) {
142 return; 116 return;
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java
@@ -19,11 +19,6 @@ import java.util.Collection; @@ -19,11 +19,6 @@ import java.util.Collection;
19 public interface OAuth2ClientService { 19 public interface OAuth2ClientService {
20 20
21 /** 21 /**
22 - * 初始化 OAuth2Client 的本地缓存  
23 - */  
24 - void initLocalCache();  
25 -  
26 - /**  
27 * 创建 OAuth2 客户端 22 * 创建 OAuth2 客户端
28 * 23 *
29 * @param createReqVO 创建信息 24 * @param createReqVO 创建信息
@@ -54,6 +49,14 @@ public interface OAuth2ClientService { @@ -54,6 +49,14 @@ public interface OAuth2ClientService {
54 OAuth2ClientDO getOAuth2Client(Long id); 49 OAuth2ClientDO getOAuth2Client(Long id);
55 50
56 /** 51 /**
  52 + * 获得 OAuth2 客户端,从缓存中
  53 + *
  54 + * @param clientId 客户端编号
  55 + * @return OAuth2 客户端
  56 + */
  57 + OAuth2ClientDO getOAuth2ClientFromCache(String clientId);
  58 +
  59 + /**
57 * 获得 OAuth2 客户端分页 60 * 获得 OAuth2 客户端分页
58 * 61 *
59 * @param pageReqVO 分页查询 62 * @param pageReqVO 分页查询
@@ -82,7 +85,7 @@ public interface OAuth2ClientService { @@ -82,7 +85,7 @@ public interface OAuth2ClientService {
82 * @param redirectUri 重定向地址 85 * @param redirectUri 重定向地址
83 * @return 客户端 86 * @return 客户端
84 */ 87 */
85 - OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret,  
86 - String authorizedGrantType, Collection<String> scopes, String redirectUri); 88 + OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, String authorizedGrantType,
  89 + Collection<String> scopes, String redirectUri);
87 90
88 } 91 }
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; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.service.oauth2;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjectUtil; 4 import cn.hutool.core.util.ObjectUtil;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
  6 +import cn.hutool.extra.spring.SpringUtil;
6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 import cn.iocoder.yudao.framework.common.util.string.StrUtils; 9 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 @@ -12,22 +13,18 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2Cl
12 import cn.iocoder.yudao.module.system.convert.auth.OAuth2ClientConvert; 13 import cn.iocoder.yudao.module.system.convert.auth.OAuth2ClientConvert;
13 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; 14 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO;
14 import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper; 15 import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper;
15 -import cn.iocoder.yudao.module.system.mq.producer.auth.OAuth2ClientProducer; 16 +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
16 import com.google.common.annotations.VisibleForTesting; 17 import com.google.common.annotations.VisibleForTesting;
17 -import lombok.Getter;  
18 -import lombok.Setter;  
19 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.cache.annotation.CacheEvict;
  20 +import org.springframework.cache.annotation.Cacheable;
20 import org.springframework.stereotype.Service; 21 import org.springframework.stereotype.Service;
21 import org.springframework.validation.annotation.Validated; 22 import org.springframework.validation.annotation.Validated;
22 23
23 -import javax.annotation.PostConstruct;  
24 import javax.annotation.Resource; 24 import javax.annotation.Resource;
25 import java.util.Collection; 25 import java.util.Collection;
26 -import java.util.List;  
27 -import java.util.Map;  
28 26
29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
30 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;  
31 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; 28 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
32 29
33 /** 30 /**
@@ -40,48 +37,21 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @@ -40,48 +37,21 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
40 @Slf4j 37 @Slf4j
41 public class OAuth2ClientServiceImpl implements OAuth2ClientService { 38 public class OAuth2ClientServiceImpl implements OAuth2ClientService {
42 39
43 - /**  
44 - * 客户端缓存  
45 - * key:客户端编号 {@link OAuth2ClientDO#getClientId()} ()}  
46 - *  
47 - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向  
48 - */  
49 - @Getter // 解决单测  
50 - @Setter // 解决单测  
51 - private volatile Map<String, OAuth2ClientDO> clientCache;  
52 -  
53 @Resource 40 @Resource
54 private OAuth2ClientMapper oauth2ClientMapper; 41 private OAuth2ClientMapper oauth2ClientMapper;
55 42
56 - @Resource  
57 - private OAuth2ClientProducer oauth2ClientProducer;  
58 -  
59 - /**  
60 - * 初始化 {@link #clientCache} 缓存  
61 - */  
62 - @Override  
63 - @PostConstruct  
64 - public void initLocalCache() {  
65 - // 第一步:查询数据  
66 - List<OAuth2ClientDO> clients = oauth2ClientMapper.selectList();  
67 - log.info("[initLocalCache][缓存 OAuth2 客户端,数量为:{}]", clients.size());  
68 -  
69 - // 第二步:构建缓存。  
70 - clientCache = convertMap(clients, OAuth2ClientDO::getClientId);  
71 - }  
72 -  
73 @Override 43 @Override
74 public Long createOAuth2Client(OAuth2ClientCreateReqVO createReqVO) { 44 public Long createOAuth2Client(OAuth2ClientCreateReqVO createReqVO) {
75 validateClientIdExists(null, createReqVO.getClientId()); 45 validateClientIdExists(null, createReqVO.getClientId());
76 // 插入 46 // 插入
77 OAuth2ClientDO oauth2Client = OAuth2ClientConvert.INSTANCE.convert(createReqVO); 47 OAuth2ClientDO oauth2Client = OAuth2ClientConvert.INSTANCE.convert(createReqVO);
78 oauth2ClientMapper.insert(oauth2Client); 48 oauth2ClientMapper.insert(oauth2Client);
79 - // 发送刷新消息  
80 - oauth2ClientProducer.sendOAuth2ClientRefreshMessage();  
81 return oauth2Client.getId(); 49 return oauth2Client.getId();
82 } 50 }
83 51
84 @Override 52 @Override
  53 + @CacheEvict(cacheNames = RedisKeyConstants.OAUTH_CLIENT,
  54 + allEntries = true) // allEntries 清空所有缓存,因为可能修改到 clientId 字段,不好清理
85 public void updateOAuth2Client(OAuth2ClientUpdateReqVO updateReqVO) { 55 public void updateOAuth2Client(OAuth2ClientUpdateReqVO updateReqVO) {
86 // 校验存在 56 // 校验存在
87 validateOAuth2ClientExists(updateReqVO.getId()); 57 validateOAuth2ClientExists(updateReqVO.getId());
@@ -91,18 +61,16 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService { @@ -91,18 +61,16 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService {
91 // 更新 61 // 更新
92 OAuth2ClientDO updateObj = OAuth2ClientConvert.INSTANCE.convert(updateReqVO); 62 OAuth2ClientDO updateObj = OAuth2ClientConvert.INSTANCE.convert(updateReqVO);
93 oauth2ClientMapper.updateById(updateObj); 63 oauth2ClientMapper.updateById(updateObj);
94 - // 发送刷新消息  
95 - oauth2ClientProducer.sendOAuth2ClientRefreshMessage();  
96 } 64 }
97 65
98 @Override 66 @Override
  67 + @CacheEvict(cacheNames = RedisKeyConstants.OAUTH_CLIENT,
  68 + allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 key,不好清理
99 public void deleteOAuth2Client(Long id) { 69 public void deleteOAuth2Client(Long id) {
100 // 校验存在 70 // 校验存在
101 validateOAuth2ClientExists(id); 71 validateOAuth2ClientExists(id);
102 // 删除 72 // 删除
103 oauth2ClientMapper.deleteById(id); 73 oauth2ClientMapper.deleteById(id);
104 - // 发送刷新消息  
105 - oauth2ClientProducer.sendOAuth2ClientRefreshMessage();  
106 } 74 }
107 75
108 private void validateOAuth2ClientExists(Long id) { 76 private void validateOAuth2ClientExists(Long id) {
@@ -132,15 +100,22 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService { @@ -132,15 +100,22 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService {
132 } 100 }
133 101
134 @Override 102 @Override
  103 + @Cacheable(cacheNames = RedisKeyConstants.OAUTH_CLIENT, key = "#clientId",
  104 + unless = "#result == null")
  105 + public OAuth2ClientDO getOAuth2ClientFromCache(String clientId) {
  106 + return oauth2ClientMapper.selectByClientId(clientId);
  107 + }
  108 +
  109 + @Override
135 public PageResult<OAuth2ClientDO> getOAuth2ClientPage(OAuth2ClientPageReqVO pageReqVO) { 110 public PageResult<OAuth2ClientDO> getOAuth2ClientPage(OAuth2ClientPageReqVO pageReqVO) {
136 return oauth2ClientMapper.selectPage(pageReqVO); 111 return oauth2ClientMapper.selectPage(pageReqVO);
137 } 112 }
138 113
139 @Override 114 @Override
140 - public OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret,  
141 - String authorizedGrantType, Collection<String> scopes, String redirectUri) { 115 + public OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, String authorizedGrantType,
  116 + Collection<String> scopes, String redirectUri) {
142 // 校验客户端存在、且开启 117 // 校验客户端存在、且开启
143 - OAuth2ClientDO client = clientCache.get(clientId); 118 + OAuth2ClientDO client = getSelf().getOAuth2ClientFromCache(clientId);
144 if (client == null) { 119 if (client == null) {
145 throw exception(OAUTH2_CLIENT_NOT_EXISTS); 120 throw exception(OAUTH2_CLIENT_NOT_EXISTS);
146 } 121 }
@@ -167,4 +142,13 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService { @@ -167,4 +142,13 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService {
167 return client; 142 return client;
168 } 143 }
169 144
  145 + /**
  146 + * 获得自身的代理对象,解决 AOP 生效问题
  147 + *
  148 + * @return 自己
  149 + */
  150 + private OAuth2ClientServiceImpl getSelf() {
  151 + return SpringUtil.getBean(getClass());
  152 + }
  153 +
170 } 154 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuService.java
@@ -16,11 +16,6 @@ import java.util.List; @@ -16,11 +16,6 @@ import java.util.List;
16 public interface MenuService { 16 public interface MenuService {
17 17
18 /** 18 /**
19 - * 初始化菜单的本地缓存  
20 - */  
21 - void initLocalCache();  
22 -  
23 - /**  
24 * 创建菜单 19 * 创建菜单
25 * 20 *
26 * @param reqVO 菜单信息 21 * @param reqVO 菜单信息
@@ -67,36 +62,12 @@ public interface MenuService { @@ -67,36 +62,12 @@ public interface MenuService {
67 List<MenuDO> getMenuList(MenuListReqVO reqVO); 62 List<MenuDO> getMenuList(MenuListReqVO reqVO);
68 63
69 /** 64 /**
70 - * 获得所有菜单,从缓存中  
71 - *  
72 - * 任一参数为空时,则返回为空  
73 - *  
74 - * @param menuTypes 菜单类型数组  
75 - * @param menusStatuses 菜单状态数组  
76 - * @return 菜单列表  
77 - */  
78 - List<MenuDO> getMenuListFromCache(Collection<Integer> menuTypes, Collection<Integer> menusStatuses);  
79 -  
80 - /**  
81 - * 获得指定编号的菜单数组,从缓存中  
82 - *  
83 - * 任一参数为空时,则返回为空  
84 - *  
85 - * @param menuIds 菜单编号数组  
86 - * @param menuTypes 菜单类型数组  
87 - * @param menusStatuses 菜单状态数组  
88 - * @return 菜单数组  
89 - */  
90 - List<MenuDO> getMenuListFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,  
91 - Collection<Integer> menusStatuses);  
92 -  
93 - /**  
94 - * 获得权限对应的菜单数组 65 + * 获得权限对应的菜单编号数组
95 * 66 *
96 * @param permission 权限标识 67 * @param permission 权限标识
97 * @return 数组 68 * @return 数组
98 */ 69 */
99 - List<MenuDO> getMenuListByPermissionFromCache(String permission); 70 + List<Long> getMenuIdListByPermissionFromCache(String permission);
100 71
101 /** 72 /**
102 * 获得菜单 73 * 获得菜单
@@ -106,4 +77,12 @@ public interface MenuService { @@ -106,4 +77,12 @@ public interface MenuService {
106 */ 77 */
107 MenuDO getMenu(Long id); 78 MenuDO getMenu(Long id);
108 79
  80 + /**
  81 + * 获得菜单数组
  82 + *
  83 + * @param ids 菜单编号数组
  84 + * @return 菜单数组
  85 + */
  86 + List<MenuDO> getMenuList(Collection<Long> ids);
  87 +
109 } 88 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java
1 package cn.iocoder.yudao.module.system.service.permission; 1 package cn.iocoder.yudao.module.system.service.permission;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 -import cn.hutool.core.util.StrUtil;  
5 -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;  
6 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuCreateReqVO; 4 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuCreateReqVO;
7 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO; 5 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO;
8 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuUpdateReqVO; 6 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuUpdateReqVO;
9 import cn.iocoder.yudao.module.system.convert.permission.MenuConvert; 7 import cn.iocoder.yudao.module.system.convert.permission.MenuConvert;
10 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; 8 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
11 import cn.iocoder.yudao.module.system.dal.mysql.permission.MenuMapper; 9 import cn.iocoder.yudao.module.system.dal.mysql.permission.MenuMapper;
  10 +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
12 import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; 11 import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;
13 -import cn.iocoder.yudao.module.system.mq.producer.permission.MenuProducer;  
14 import cn.iocoder.yudao.module.system.service.tenant.TenantService; 12 import cn.iocoder.yudao.module.system.service.tenant.TenantService;
15 import com.google.common.annotations.VisibleForTesting; 13 import com.google.common.annotations.VisibleForTesting;
16 -import com.google.common.collect.ImmutableMap;  
17 -import com.google.common.collect.ImmutableMultimap;  
18 -import com.google.common.collect.Multimap;  
19 -import lombok.Getter;  
20 -import lombok.Setter;  
21 import lombok.extern.slf4j.Slf4j; 14 import lombok.extern.slf4j.Slf4j;
  15 +import org.springframework.cache.annotation.CacheEvict;
  16 +import org.springframework.cache.annotation.Cacheable;
22 import org.springframework.context.annotation.Lazy; 17 import org.springframework.context.annotation.Lazy;
23 import org.springframework.stereotype.Service; 18 import org.springframework.stereotype.Service;
24 import org.springframework.transaction.annotation.Transactional; 19 import org.springframework.transaction.annotation.Transactional;
25 -import org.springframework.transaction.support.TransactionSynchronization;  
26 -import org.springframework.transaction.support.TransactionSynchronizationManager;  
27 20
28 -import javax.annotation.PostConstruct;  
29 import javax.annotation.Resource; 21 import javax.annotation.Resource;
30 -import java.util.*;  
31 -import java.util.stream.Collectors; 22 +import java.util.Collection;
  23 +import java.util.List;
32 24
33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  26 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
34 import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT; 27 import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT;
35 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; 28 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
36 29
@@ -43,26 +36,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @@ -43,26 +36,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
43 @Slf4j 36 @Slf4j
44 public class MenuServiceImpl implements MenuService { 37 public class MenuServiceImpl implements MenuService {
45 38
46 - /**  
47 - * 菜单缓存  
48 - * key:菜单编号  
49 - *  
50 - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向  
51 - */  
52 - @Getter  
53 - @Setter  
54 - private volatile Map<Long, MenuDO> menuCache;  
55 - /**  
56 - * 权限与菜单缓存  
57 - * key:权限 {@link MenuDO#getPermission()}  
58 - * value:MenuDO 数组,因为一个权限可能对应多个 MenuDO 对象  
59 - *  
60 - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向  
61 - */  
62 - @Getter  
63 - @Setter  
64 - private volatile Multimap<String, MenuDO> permissionMenuCache;  
65 -  
66 @Resource 39 @Resource
67 private MenuMapper menuMapper; 40 private MenuMapper menuMapper;
68 @Resource 41 @Resource
@@ -71,33 +44,8 @@ public class MenuServiceImpl implements MenuService { @@ -71,33 +44,8 @@ public class MenuServiceImpl implements MenuService {
71 @Lazy // 延迟,避免循环依赖报错 44 @Lazy // 延迟,避免循环依赖报错
72 private TenantService tenantService; 45 private TenantService tenantService;
73 46
74 - @Resource  
75 - private MenuProducer menuProducer;  
76 -  
77 - /**  
78 - * 初始化 {@link #menuCache} 和 {@link #permissionMenuCache} 缓存  
79 - */  
80 - @Override  
81 - @PostConstruct  
82 - public synchronized void initLocalCache() {  
83 - // 第一步:查询数据  
84 - List<MenuDO> menuList = menuMapper.selectList();  
85 - log.info("[initLocalCache][缓存菜单,数量为:{}]", menuList.size());  
86 -  
87 - // 第二步:构建缓存  
88 - ImmutableMap.Builder<Long, MenuDO> menuCacheBuilder = ImmutableMap.builder();  
89 - ImmutableMultimap.Builder<String, MenuDO> permMenuCacheBuilder = ImmutableMultimap.builder();  
90 - menuList.forEach(menuDO -> {  
91 - menuCacheBuilder.put(menuDO.getId(), menuDO);  
92 - if (StrUtil.isNotEmpty(menuDO.getPermission())) { // 会存在 permission 为 null 的情况,导致 put 报 NPE 异常  
93 - permMenuCacheBuilder.put(menuDO.getPermission(), menuDO);  
94 - }  
95 - });  
96 - menuCache = menuCacheBuilder.build();  
97 - permissionMenuCache = permMenuCacheBuilder.build();  
98 - }  
99 -  
100 @Override 47 @Override
  48 + @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, key = "#reqVO.permission")
101 public Long createMenu(MenuCreateReqVO reqVO) { 49 public Long createMenu(MenuCreateReqVO reqVO) {
102 // 校验父菜单存在 50 // 校验父菜单存在
103 validateParentMenu(reqVO.getParentId(), null); 51 validateParentMenu(reqVO.getParentId(), null);
@@ -108,13 +56,13 @@ public class MenuServiceImpl implements MenuService { @@ -108,13 +56,13 @@ public class MenuServiceImpl implements MenuService {
108 MenuDO menu = MenuConvert.INSTANCE.convert(reqVO); 56 MenuDO menu = MenuConvert.INSTANCE.convert(reqVO);
109 initMenuProperty(menu); 57 initMenuProperty(menu);
110 menuMapper.insert(menu); 58 menuMapper.insert(menu);
111 - // 发送刷新消息  
112 - menuProducer.sendMenuRefreshMessage();  
113 // 返回 59 // 返回
114 return menu.getId(); 60 return menu.getId();
115 } 61 }
116 62
117 @Override 63 @Override
  64 + @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST,
  65 + allEntries = true) // allEntries 清空所有缓存,因为 permission 如果变更,涉及到新老两个 permission。直接清理,简单有效
118 public void updateMenu(MenuUpdateReqVO reqVO) { 66 public void updateMenu(MenuUpdateReqVO reqVO) {
119 // 校验更新的菜单是否存在 67 // 校验更新的菜单是否存在
120 if (menuMapper.selectById(reqVO.getId()) == null) { 68 if (menuMapper.selectById(reqVO.getId()) == null) {
@@ -129,34 +77,25 @@ public class MenuServiceImpl implements MenuService { @@ -129,34 +77,25 @@ public class MenuServiceImpl implements MenuService {
129 MenuDO updateObject = MenuConvert.INSTANCE.convert(reqVO); 77 MenuDO updateObject = MenuConvert.INSTANCE.convert(reqVO);
130 initMenuProperty(updateObject); 78 initMenuProperty(updateObject);
131 menuMapper.updateById(updateObject); 79 menuMapper.updateById(updateObject);
132 - // 发送刷新消息  
133 - menuProducer.sendMenuRefreshMessage();  
134 } 80 }
135 81
136 @Override 82 @Override
137 @Transactional(rollbackFor = Exception.class) 83 @Transactional(rollbackFor = Exception.class)
138 - public void deleteMenu(Long menuId) { 84 + @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST,
  85 + allEntries = true) // allEntries 清空所有缓存,因为此时不知道 id 对应的 permission 是多少。直接清理,简单有效
  86 + public void deleteMenu(Long id) {
139 // 校验是否还有子菜单 87 // 校验是否还有子菜单
140 - if (menuMapper.selectCountByParentId(menuId) > 0) { 88 + if (menuMapper.selectCountByParentId(id) > 0) {
141 throw exception(MENU_EXISTS_CHILDREN); 89 throw exception(MENU_EXISTS_CHILDREN);
142 } 90 }
143 // 校验删除的菜单是否存在 91 // 校验删除的菜单是否存在
144 - if (menuMapper.selectById(menuId) == null) { 92 + if (menuMapper.selectById(id) == null) {
145 throw exception(MENU_NOT_EXISTS); 93 throw exception(MENU_NOT_EXISTS);
146 } 94 }
147 // 标记删除 95 // 标记删除
148 - menuMapper.deleteById(menuId); 96 + menuMapper.deleteById(id);
149 // 删除授予给角色的权限 97 // 删除授予给角色的权限
150 - permissionService.processMenuDeleted(menuId);  
151 - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了  
152 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {  
153 -  
154 - @Override  
155 - public void afterCommit() {  
156 - menuProducer.sendMenuRefreshMessage();  
157 - }  
158 -  
159 - }); 98 + permissionService.processMenuDeleted(id);
160 } 99 }
161 100
162 @Override 101 @Override
@@ -178,49 +117,31 @@ public class MenuServiceImpl implements MenuService { @@ -178,49 +117,31 @@ public class MenuServiceImpl implements MenuService {
178 } 117 }
179 118
180 @Override 119 @Override
181 - public List<MenuDO> getMenuListFromCache(Collection<Integer> menuTypes, Collection<Integer> menusStatuses) {  
182 - // 任一一个参数为空,则返回空  
183 - if (CollectionUtils.isAnyEmpty(menuTypes, menusStatuses)) {  
184 - return Collections.emptyList();  
185 - }  
186 - // 创建新数组,避免缓存被修改  
187 - return menuCache.values().stream().filter(menu -> menuTypes.contains(menu.getType())  
188 - && menusStatuses.contains(menu.getStatus()))  
189 - .collect(Collectors.toList());  
190 - }  
191 -  
192 - @Override  
193 - public List<MenuDO> getMenuListFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,  
194 - Collection<Integer> menusStatuses) {  
195 - // 任一一个参数为空,则返回空  
196 - if (CollectionUtils.isAnyEmpty(menuIds, menuTypes, menusStatuses)) {  
197 - return Collections.emptyList();  
198 - }  
199 - return menuCache.values().stream().filter(menu -> menuIds.contains(menu.getId())  
200 - && menuTypes.contains(menu.getType())  
201 - && menusStatuses.contains(menu.getStatus()))  
202 - .collect(Collectors.toList()); 120 + @Cacheable(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, key = "#permission")
  121 + public List<Long> getMenuIdListByPermissionFromCache(String permission) {
  122 + List<MenuDO> menus = menuMapper.selectListByPermission(permission);
  123 + return convertList(menus, MenuDO::getId);
203 } 124 }
204 125
205 @Override 126 @Override
206 - public List<MenuDO> getMenuListByPermissionFromCache(String permission) {  
207 - return new ArrayList<>(permissionMenuCache.get(permission)); 127 + public MenuDO getMenu(Long id) {
  128 + return menuMapper.selectById(id);
208 } 129 }
209 130
210 @Override 131 @Override
211 - public MenuDO getMenu(Long id) {  
212 - return menuMapper.selectById(id); 132 + public List<MenuDO> getMenuList(Collection<Long> ids) {
  133 + return menuMapper.selectBatchIds(ids);
213 } 134 }
214 135
215 /** 136 /**
216 * 校验父菜单是否合法 137 * 校验父菜单是否合法
217 - * 138 + * <p>
218 * 1. 不能设置自己为父菜单 139 * 1. 不能设置自己为父菜单
219 * 2. 父菜单不存在 140 * 2. 父菜单不存在
220 * 3. 父菜单必须是 {@link MenuTypeEnum#MENU} 菜单类型 141 * 3. 父菜单必须是 {@link MenuTypeEnum#MENU} 菜单类型
221 * 142 *
222 * @param parentId 父菜单编号 143 * @param parentId 父菜单编号
223 - * @param childId 当前菜单编号 144 + * @param childId 当前菜单编号
224 */ 145 */
225 @VisibleForTesting 146 @VisibleForTesting
226 void validateParentMenu(Long parentId, Long childId) { 147 void validateParentMenu(Long parentId, Long childId) {
@@ -238,19 +159,19 @@ public class MenuServiceImpl implements MenuService { @@ -238,19 +159,19 @@ public class MenuServiceImpl implements MenuService {
238 } 159 }
239 // 父菜单必须是目录或者菜单类型 160 // 父菜单必须是目录或者菜单类型
240 if (!MenuTypeEnum.DIR.getType().equals(menu.getType()) 161 if (!MenuTypeEnum.DIR.getType().equals(menu.getType())
241 - && !MenuTypeEnum.MENU.getType().equals(menu.getType())) { 162 + && !MenuTypeEnum.MENU.getType().equals(menu.getType())) {
242 throw exception(MENU_PARENT_NOT_DIR_OR_MENU); 163 throw exception(MENU_PARENT_NOT_DIR_OR_MENU);
243 } 164 }
244 } 165 }
245 166
246 /** 167 /**
247 * 校验菜单是否合法 168 * 校验菜单是否合法
248 - * 169 + * <p>
249 * 1. 校验相同父菜单编号下,是否存在相同的菜单名 170 * 1. 校验相同父菜单编号下,是否存在相同的菜单名
250 * 171 *
251 - * @param name 菜单名字 172 + * @param name 菜单名字
252 * @param parentId 父菜单编号 173 * @param parentId 父菜单编号
253 - * @param id 菜单编号 174 + * @param id 菜单编号
254 */ 175 */
255 @VisibleForTesting 176 @VisibleForTesting
256 void validateMenu(Long parentId, String name, Long id) { 177 void validateMenu(Long parentId, String name, Long id) {
@@ -269,7 +190,7 @@ public class MenuServiceImpl implements MenuService { @@ -269,7 +190,7 @@ public class MenuServiceImpl implements MenuService {
269 190
270 /** 191 /**
271 * 初始化菜单的通用属性。 192 * 初始化菜单的通用属性。
272 - * 193 + * <p>
273 * 例如说,只有目录或者菜单类型的菜单,才设置 icon 194 * 例如说,只有目录或者菜单类型的菜单,才设置 icon
274 * 195 *
275 * @param menu 菜单 196 * @param menu 菜单
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionService.java
1 package cn.iocoder.yudao.module.system.service.permission; 1 package cn.iocoder.yudao.module.system.service.permission;
2 2
3 import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO; 3 import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
4 -import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;  
5 -import org.springframework.lang.Nullable;  
6 4
7 import java.util.Collection; 5 import java.util.Collection;
8 -import java.util.List;  
9 import java.util.Set; 6 import java.util.Set;
10 7
  8 +import static java.util.Collections.singleton;
  9 +
11 /** 10 /**
12 * 权限 Service 接口 11 * 权限 Service 接口
13 - * 12 + * <p>
14 * 提供用户-角色、角色-菜单、角色-部门的关联权限处理 13 * 提供用户-角色、角色-菜单、角色-部门的关联权限处理
15 * 14 *
16 * @author 芋道源码 15 * @author 芋道源码
@@ -18,118 +17,123 @@ import java.util.Set; @@ -18,118 +17,123 @@ import java.util.Set;
18 public interface PermissionService { 17 public interface PermissionService {
19 18
20 /** 19 /**
21 - * 初始化权限的本地缓存 20 + * 判断是否有权限,任一一个即可
  21 + *
  22 + * @param userId 用户编号
  23 + * @param permissions 权限
  24 + * @return 是否
22 */ 25 */
23 - void initLocalCache(); 26 + boolean hasAnyPermissions(Long userId, String... permissions);
24 27
25 /** 28 /**
26 - * 获得角色们拥有的菜单列表,从缓存中获取  
27 - *  
28 - * 任一参数为空时,则返回为空 29 + * 判断是否有角色,任一一个即可
29 * 30 *
30 - * @param roleIds 角色编号数组  
31 - * @param menuTypes 菜单类型数组  
32 - * @param menusStatuses 菜单状态数组  
33 - * @return 菜单列表 31 + * @param roles 角色数组
  32 + * @return 是否
34 */ 33 */
35 - List<MenuDO> getRoleMenuListFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,  
36 - Collection<Integer> menusStatuses); 34 + boolean hasAnyRoles(Long userId, String... roles);
  35 +
  36 + // ========== 角色-菜单的相关方法 ==========
37 37
38 /** 38 /**
39 - * 获得用户拥有的角色编号集合,从缓存中获取 39 + * 设置角色菜单
40 * 40 *
41 - * @param userId 用户编号  
42 - * @param roleStatuses 角色状态集合. 允许为空,为空时不过滤  
43 - * @return 角色编号集合 41 + * @param roleId 角色编号
  42 + * @param menuIds 菜单编号集合
44 */ 43 */
45 - Set<Long> getUserRoleIdsFromCache(Long userId, @Nullable Collection<Integer> roleStatuses); 44 + void assignRoleMenu(Long roleId, Set<Long> menuIds);
46 45
47 /** 46 /**
48 - * 获得角色拥有的菜单编号集合 47 + * 处理角色删除时,删除关联授权数据
49 * 48 *
50 * @param roleId 角色编号 49 * @param roleId 角色编号
51 - * @return 菜单编号集合  
52 */ 50 */
53 - Set<Long> getRoleMenuIds(Long roleId); 51 + void processRoleDeleted(Long roleId);
54 52
55 /** 53 /**
56 - * 获得拥有多个角色的用户编号集合 54 + * 处理菜单删除时,删除关联授权数据
57 * 55 *
58 - * @param roleIds 角色编号集合  
59 - * @return 用户编号集合 56 + * @param menuId 菜单编号
60 */ 57 */
61 - Set<Long> getUserRoleIdListByRoleIds(Collection<Long> roleIds); 58 + void processMenuDeleted(Long menuId);
62 59
63 /** 60 /**
64 - * 设置角色菜单 61 + * 获得角色拥有的菜单编号集合
65 * 62 *
66 * @param roleId 角色编号 63 * @param roleId 角色编号
67 - * @param menuIds 菜单编号集合 64 + * @return 菜单编号集合
68 */ 65 */
69 - void assignRoleMenu(Long roleId, Set<Long> menuIds); 66 + default Set<Long> getRoleMenuListByRoleId(Long roleId) {
  67 + return getRoleMenuListByRoleId(singleton(roleId));
  68 + }
70 69
71 /** 70 /**
72 - * 获得用户拥有的角色编号集合 71 + * 获得角色们拥有的菜单编号集合
73 * 72 *
74 - * @param userId 用户编号  
75 - * @return 角色编号集合 73 + * @param roleIds 角色编号数组
  74 + * @return 菜单编号集合
76 */ 75 */
77 - Set<Long> getUserRoleIdListByUserId(Long userId); 76 + Set<Long> getRoleMenuListByRoleId(Collection<Long> roleIds);
78 77
79 /** 78 /**
80 - * 设置用户角色 79 + * 获得拥有指定菜单的角色编号数组,从缓存中获取
81 * 80 *
82 - * @param userId 角色编号  
83 - * @param roleIds 角色编号集合 81 + * @param menuId 菜单编号
  82 + * @return 角色编号数组
84 */ 83 */
85 - void assignUserRole(Long userId, Set<Long> roleIds); 84 + Set<Long> getMenuRoleIdListByMenuIdFromCache(Long menuId);
  85 +
  86 + // ========== 用户-角色的相关方法 ==========
86 87
87 /** 88 /**
88 - * 设置角色的数据权限 89 + * 设置用户角色
89 * 90 *
90 - * @param roleId 角色编号  
91 - * @param dataScope 数据范围  
92 - * @param dataScopeDeptIds 部门编号数组 91 + * @param userId 角色编号
  92 + * @param roleIds 角色编号集合
93 */ 93 */
94 - void assignRoleDataScope(Long roleId, Integer dataScope, Set<Long> dataScopeDeptIds); 94 + void assignUserRole(Long userId, Set<Long> roleIds);
95 95
96 /** 96 /**
97 - * 处理角色删除时,删除关联授权数据 97 + * 处理用户删除时,删除关联授权数据
98 * 98 *
99 - * @param roleId 角色编号 99 + * @param userId 用户编号
100 */ 100 */
101 - void processRoleDeleted(Long roleId); 101 + void processUserDeleted(Long userId);
102 102
103 /** 103 /**
104 - * 处理菜单删除时,删除关联授权数据 104 + * 获得拥有多个角色的用户编号集合
105 * 105 *
106 - * @param menuId 菜单编号 106 + * @param roleIds 角色编号集合
  107 + * @return 用户编号集合
107 */ 108 */
108 - void processMenuDeleted(Long menuId); 109 + Set<Long> getUserRoleIdListByRoleId(Collection<Long> roleIds);
109 110
110 /** 111 /**
111 - * 处理用户删除是,删除关联授权数据 112 + * 获得用户拥有的角色编号集合
112 * 113 *
113 * @param userId 用户编号 114 * @param userId 用户编号
  115 + * @return 角色编号集合
114 */ 116 */
115 - void processUserDeleted(Long userId); 117 + Set<Long> getUserRoleIdListByUserId(Long userId);
116 118
117 /** 119 /**
118 - * 判断是否有权限,任一一个即可 120 + * 获得用户拥有的角色编号集合,从缓存中获取
119 * 121 *
120 * @param userId 用户编号 122 * @param userId 用户编号
121 - * @param permissions 权限  
122 - * @return 是否 123 + * @return 角色编号集合
123 */ 124 */
124 - boolean hasAnyPermissions(Long userId, String... permissions); 125 + Set<Long> getUserRoleIdListByUserIdFromCache(Long userId);
  126 +
  127 + // ========== 用户-部门的相关方法 ==========
125 128
126 /** 129 /**
127 - * 判断是否有角色,任一一个即可 130 + * 设置角色的数据权限
128 * 131 *
129 - * @param roles 角色数组  
130 - * @return 是否 132 + * @param roleId 角色编号
  133 + * @param dataScope 数据范围
  134 + * @param dataScopeDeptIds 部门编号数组
131 */ 135 */
132 - boolean hasAnyRoles(Long userId, String... roles); 136 + void assignRoleDataScope(Long roleId, Integer dataScope, Set<Long> dataScopeDeptIds);
133 137
134 /** 138 /**
135 * 获得登陆用户的部门数据权限 139 * 获得登陆用户的部门数据权限
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; @@ -3,45 +3,38 @@ package cn.iocoder.yudao.module.system.service.permission;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.CollectionUtil; 4 import cn.hutool.core.collection.CollectionUtil;
5 import cn.hutool.core.util.ArrayUtil; 5 import cn.hutool.core.util.ArrayUtil;
  6 +import cn.hutool.extra.spring.SpringUtil;
6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 8 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 -import cn.iocoder.yudao.framework.common.util.collection.MapUtils;  
9 -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;  
10 import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; 9 import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
11 -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;  
12 -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;  
13 import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO; 10 import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
14 -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;  
15 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; 11 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
16 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; 12 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
17 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO; 13 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
18 import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO; 14 import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
19 import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper; 15 import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper;
20 import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper; 16 import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
  17 +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
21 import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; 18 import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
22 -import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;  
23 import cn.iocoder.yudao.module.system.service.dept.DeptService; 19 import cn.iocoder.yudao.module.system.service.dept.DeptService;
24 import cn.iocoder.yudao.module.system.service.user.AdminUserService; 20 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
  21 +import com.baomidou.dynamic.datasource.annotation.DSTransactional;
25 import com.google.common.annotations.VisibleForTesting; 22 import com.google.common.annotations.VisibleForTesting;
26 import com.google.common.base.Suppliers; 23 import com.google.common.base.Suppliers;
27 -import com.google.common.collect.ImmutableMultimap;  
28 -import com.google.common.collect.Multimap;  
29 import com.google.common.collect.Sets; 24 import com.google.common.collect.Sets;
30 -import lombok.Getter;  
31 -import lombok.Setter;  
32 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
  26 +import org.springframework.cache.annotation.CacheEvict;
  27 +import org.springframework.cache.annotation.Cacheable;
  28 +import org.springframework.cache.annotation.Caching;
33 import org.springframework.stereotype.Service; 29 import org.springframework.stereotype.Service;
34 import org.springframework.transaction.annotation.Transactional; 30 import org.springframework.transaction.annotation.Transactional;
35 -import org.springframework.transaction.support.TransactionSynchronization;  
36 -import org.springframework.transaction.support.TransactionSynchronizationManager;  
37 31
38 -import javax.annotation.PostConstruct;  
39 import javax.annotation.Resource; 32 import javax.annotation.Resource;
40 import java.util.*; 33 import java.util.*;
41 import java.util.function.Supplier; 34 import java.util.function.Supplier;
42 35
43 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 36 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
44 -import static java.util.Collections.singleton; 37 +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
45 38
46 /** 39 /**
47 * 权限 Service 实现类 40 * 权限 Service 实现类
@@ -52,38 +45,6 @@ import static java.util.Collections.singleton; @@ -52,38 +45,6 @@ import static java.util.Collections.singleton;
52 @Slf4j 45 @Slf4j
53 public class PermissionServiceImpl implements PermissionService { 46 public class PermissionServiceImpl implements PermissionService {
54 47
55 - /**  
56 - * 角色编号与菜单编号的缓存映射  
57 - * key:角色编号  
58 - * value:菜单编号的数组  
59 - *  
60 - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向  
61 - */  
62 - @Getter  
63 - @Setter // 单元测试需要  
64 - private volatile Multimap<Long, Long> roleMenuCache;  
65 - /**  
66 - * 菜单编号与角色编号的缓存映射  
67 - * key:菜单编号  
68 - * value:角色编号的数组  
69 - *  
70 - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向  
71 - */  
72 - @Getter  
73 - @Setter // 单元测试需要  
74 - private volatile Multimap<Long, Long> menuRoleCache;  
75 -  
76 - /**  
77 - * 用户编号与角色编号的缓存映射  
78 - * key:用户编号  
79 - * value:角色编号的数组  
80 - *  
81 - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向  
82 - */  
83 - @Getter  
84 - @Setter // 单元测试需要  
85 - private volatile Map<Long, Set<Long>> userRoleCache;  
86 -  
87 @Resource 48 @Resource
88 private RoleMenuMapper roleMenuMapper; 49 private RoleMenuMapper roleMenuMapper;
89 @Resource 50 @Resource
@@ -98,115 +59,89 @@ public class PermissionServiceImpl implements PermissionService { @@ -98,115 +59,89 @@ public class PermissionServiceImpl implements PermissionService {
98 @Resource 59 @Resource
99 private AdminUserService userService; 60 private AdminUserService userService;
100 61
101 - @Resource  
102 - private PermissionProducer permissionProducer;  
103 -  
104 @Override 62 @Override
105 - @PostConstruct  
106 - public void initLocalCache() {  
107 - initLocalCacheForRoleMenu();  
108 - initLocalCacheForUserRole();  
109 - } 63 + public boolean hasAnyPermissions(Long userId, String... permissions) {
  64 + // 如果为空,说明已经有权限
  65 + if (ArrayUtil.isEmpty(permissions)) {
  66 + return true;
  67 + }
110 68
111 - /**  
112 - * 刷新 RoleMenu 本地缓存  
113 - */  
114 - @VisibleForTesting  
115 - void initLocalCacheForRoleMenu() {  
116 - // 注意:忽略自动多租户,因为要全局初始化缓存  
117 - TenantUtils.executeIgnore(() -> {  
118 - // 第一步:查询数据  
119 - List<RoleMenuDO> roleMenus = roleMenuMapper.selectList();  
120 - log.info("[initLocalCacheForRoleMenu][缓存角色与菜单,数量为:{}]", roleMenus.size());  
121 -  
122 - // 第二步:构建缓存  
123 - ImmutableMultimap.Builder<Long, Long> roleMenuCacheBuilder = ImmutableMultimap.builder();  
124 - ImmutableMultimap.Builder<Long, Long> menuRoleCacheBuilder = ImmutableMultimap.builder();  
125 - roleMenus.forEach(roleMenuDO -> {  
126 - roleMenuCacheBuilder.put(roleMenuDO.getRoleId(), roleMenuDO.getMenuId());  
127 - menuRoleCacheBuilder.put(roleMenuDO.getMenuId(), roleMenuDO.getRoleId());  
128 - });  
129 - roleMenuCache = roleMenuCacheBuilder.build();  
130 - menuRoleCache = menuRoleCacheBuilder.build();  
131 - }); 69 + // 获得当前登录的角色。如果为空,说明没有权限
  70 + List<RoleDO> roles = getEnableUserRoleListByUserIdFromCache(userId);
  71 + if (CollUtil.isEmpty(roles)) {
  72 + return false;
  73 + }
  74 +
  75 + // 情况一:遍历判断每个权限,如果有一满足,说明有权限
  76 + for (String permission : permissions) {
  77 + if (hasAnyPermission(roles, permission)) {
  78 + return true;
  79 + }
  80 + }
  81 +
  82 + // 情况二:如果是超管,也说明有权限
  83 + return roleService.hasAnySuperAdmin(convertSet(roles, RoleDO::getId));
132 } 84 }
133 85
134 /** 86 /**
135 - * 刷新 UserRole 本地缓存 87 + * 判断指定角色,是否拥有该 permission 权限
  88 + *
  89 + * @param roles 指定角色数组
  90 + * @param permission 权限标识
  91 + * @return 是否拥有
136 */ 92 */
137 - @VisibleForTesting  
138 - void initLocalCacheForUserRole() {  
139 - // 注意:忽略自动多租户,因为要全局初始化缓存  
140 - TenantUtils.executeIgnore(() -> {  
141 - // 第一步:加载数据  
142 - List<UserRoleDO> userRoles = userRoleMapper.selectList();  
143 - log.info("[initLocalCacheForUserRole][缓存用户与角色,数量为:{}]", userRoles.size());  
144 -  
145 - // 第二步:构建缓存。  
146 - ImmutableMultimap.Builder<Long, Long> userRoleCacheBuilder = ImmutableMultimap.builder();  
147 - userRoles.forEach(userRoleDO -> userRoleCacheBuilder.put(userRoleDO.getUserId(), userRoleDO.getRoleId()));  
148 - userRoleCache = CollectionUtils.convertMultiMap2(userRoles, UserRoleDO::getUserId, UserRoleDO::getRoleId);  
149 - });  
150 - }  
151 -  
152 - @Override  
153 - public List<MenuDO> getRoleMenuListFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,  
154 - Collection<Integer> menusStatuses) {  
155 - // 任一一个参数为空时,不返回任何菜单  
156 - if (CollectionUtils.isAnyEmpty(roleIds, menuTypes, menusStatuses)) {  
157 - return Collections.emptyList(); 93 + private boolean hasAnyPermission(List<RoleDO> roles, String permission) {
  94 + List<Long> menuIds = menuService.getMenuIdListByPermissionFromCache(permission);
  95 + // 采用严格模式,如果权限找不到对应的 Menu 的话,也认为没有权限
  96 + if (CollUtil.isEmpty(menuIds)) {
  97 + return false;
158 } 98 }
159 99
160 - // 判断角色是否包含超级管理员。如果是超级管理员,获取到全部  
161 - List<RoleDO> roleList = roleService.getRoleListFromCache(roleIds);  
162 - if (roleService.hasAnySuperAdmin(roleList)) {  
163 - return menuService.getMenuListFromCache(menuTypes, menusStatuses); 100 + // 判断是否有权限
  101 + Set<Long> roleIds = convertSet(roles, RoleDO::getId);
  102 + for (Long menuId : menuIds) {
  103 + // 获得拥有该菜单的角色编号集合
  104 + Set<Long> menuRoleIds = getSelf().getMenuRoleIdListByMenuIdFromCache(menuId);
  105 + // 如果有交集,说明有权限
  106 + if (CollUtil.containsAny(menuRoleIds, roleIds)) {
  107 + return true;
  108 + }
164 } 109 }
165 -  
166 - // 获得角色拥有的菜单关联  
167 - List<Long> menuIds = MapUtils.getList(roleMenuCache, roleIds);  
168 - return menuService.getMenuListFromCache(menuIds, menuTypes, menusStatuses); 110 + return false;
169 } 111 }
170 112
171 @Override 113 @Override
172 - public Set<Long> getUserRoleIdsFromCache(Long userId, Collection<Integer> roleStatuses) {  
173 - Set<Long> cacheRoleIds = userRoleCache.get(userId);  
174 - // 创建用户的时候没有分配角色,会存在空指针异常  
175 - if (CollUtil.isEmpty(cacheRoleIds)) {  
176 - return Collections.emptySet();  
177 - }  
178 - Set<Long> roleIds = new HashSet<>(cacheRoleIds);  
179 - // 过滤角色状态  
180 - if (CollectionUtil.isNotEmpty(roleStatuses)) {  
181 - roleIds.removeIf(roleId -> {  
182 - RoleDO role = roleService.getRoleFromCache(roleId);  
183 - return role == null || !roleStatuses.contains(role.getStatus());  
184 - }); 114 + public boolean hasAnyRoles(Long userId, String... roles) {
  115 + // 如果为空,说明已经有权限
  116 + if (ArrayUtil.isEmpty(roles)) {
  117 + return true;
185 } 118 }
186 - return roleIds;  
187 - }  
188 119
189 - @Override  
190 - public Set<Long> getRoleMenuIds(Long roleId) {  
191 - // 如果是管理员的情况下,获取全部菜单编号  
192 - if (roleService.hasAnySuperAdmin(Collections.singleton(roleId))) {  
193 - return convertSet(menuService.getMenuList(), MenuDO::getId); 120 + // 获得当前登录的角色。如果为空,说明没有权限
  121 + List<RoleDO> roleList = getEnableUserRoleListByUserIdFromCache(userId);
  122 + if (CollUtil.isEmpty(roleList)) {
  123 + return false;
194 } 124 }
195 - // 如果是非管理员的情况下,获得拥有的菜单编号  
196 - return convertSet(roleMenuMapper.selectListByRoleId(roleId), RoleMenuDO::getMenuId); 125 +
  126 + // 判断是否有角色
  127 + Set<String> userRoles = convertSet(roleList, RoleDO::getCode);
  128 + return CollUtil.containsAny(userRoles, Sets.newHashSet(roles));
197 } 129 }
198 130
  131 + // ========== 角色-菜单的相关方法 ==========
  132 +
199 @Override 133 @Override
200 - @Transactional(rollbackFor = Exception.class) 134 + @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
  135 + @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST,
  136 + allEntries = true) // allEntries 清空所有缓存,主要一次更新涉及到的 menuIds 较多,反倒批量会更快
201 public void assignRoleMenu(Long roleId, Set<Long> menuIds) { 137 public void assignRoleMenu(Long roleId, Set<Long> menuIds) {
202 // 获得角色拥有菜单编号 138 // 获得角色拥有菜单编号
203 - Set<Long> dbMenuIds = convertSet(roleMenuMapper.selectListByRoleId(roleId),  
204 - RoleMenuDO::getMenuId); 139 + Set<Long> dbMenuIds = convertSet(roleMenuMapper.selectListByRoleId(roleId), RoleMenuDO::getMenuId);
205 // 计算新增和删除的菜单编号 140 // 计算新增和删除的菜单编号
206 Collection<Long> createMenuIds = CollUtil.subtract(menuIds, dbMenuIds); 141 Collection<Long> createMenuIds = CollUtil.subtract(menuIds, dbMenuIds);
207 Collection<Long> deleteMenuIds = CollUtil.subtract(dbMenuIds, menuIds); 142 Collection<Long> deleteMenuIds = CollUtil.subtract(dbMenuIds, menuIds);
208 // 执行新增和删除。对于已经授权的菜单,不用做任何处理 143 // 执行新增和删除。对于已经授权的菜单,不用做任何处理
209 - if (!CollectionUtil.isEmpty(createMenuIds)) { 144 + if (CollUtil.isNotEmpty(createMenuIds)) {
210 roleMenuMapper.insertBatch(CollectionUtils.convertList(createMenuIds, menuId -> { 145 roleMenuMapper.insertBatch(CollectionUtils.convertList(createMenuIds, menuId -> {
211 RoleMenuDO entity = new RoleMenuDO(); 146 RoleMenuDO entity = new RoleMenuDO();
212 entity.setRoleId(roleId); 147 entity.setRoleId(roleId);
@@ -214,34 +149,57 @@ public class PermissionServiceImpl implements PermissionService { @@ -214,34 +149,57 @@ public class PermissionServiceImpl implements PermissionService {
214 return entity; 149 return entity;
215 })); 150 }));
216 } 151 }
217 - if (!CollectionUtil.isEmpty(deleteMenuIds)) { 152 + if (CollUtil.isNotEmpty(deleteMenuIds)) {
218 roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds); 153 roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds);
219 } 154 }
220 - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了  
221 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { 155 + }
222 156
223 - @Override  
224 - public void afterCommit() {  
225 - permissionProducer.sendRoleMenuRefreshMessage();  
226 - } 157 + @Override
  158 + @Transactional(rollbackFor = Exception.class)
  159 + @Caching(evict = {
  160 + @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST,
  161 + allEntries = true), // allEntries 清空所有缓存,此处无法方便获得 roleId 对应的 menu 缓存们
  162 + @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST,
  163 + allEntries = true) // allEntries 清空所有缓存,此处无法方便获得 roleId 对应的 user 缓存们
  164 + })
  165 + public void processRoleDeleted(Long roleId) {
  166 + // 标记删除 UserRole
  167 + userRoleMapper.deleteListByRoleId(roleId);
  168 + // 标记删除 RoleMenu
  169 + roleMenuMapper.deleteListByRoleId(roleId);
  170 + }
227 171
228 - }); 172 + @Override
  173 + @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST, key = "#menuId")
  174 + public void processMenuDeleted(Long menuId) {
  175 + roleMenuMapper.deleteListByMenuId(menuId);
229 } 176 }
230 177
231 @Override 178 @Override
232 - public Set<Long> getUserRoleIdListByUserId(Long userId) {  
233 - return convertSet(userRoleMapper.selectListByUserId(userId),  
234 - UserRoleDO::getRoleId); 179 + public Set<Long> getRoleMenuListByRoleId(Collection<Long> roleIds) {
  180 + if (CollUtil.isEmpty(roleIds)) {
  181 + return Collections.emptySet();
  182 + }
  183 +
  184 + // 如果是管理员的情况下,获取全部菜单编号
  185 + if (roleService.hasAnySuperAdmin(roleIds)) {
  186 + return convertSet(menuService.getMenuList(), MenuDO::getId);
  187 + }
  188 + // 如果是非管理员的情况下,获得拥有的菜单编号
  189 + return convertSet(roleMenuMapper.selectListByRoleId(roleIds), RoleMenuDO::getMenuId);
235 } 190 }
236 191
237 @Override 192 @Override
238 - public Set<Long> getUserRoleIdListByRoleIds(Collection<Long> roleIds) {  
239 - return convertSet(userRoleMapper.selectListByRoleIds(roleIds),  
240 - UserRoleDO::getUserId); 193 + @Cacheable(value = RedisKeyConstants.MENU_ROLE_ID_LIST, key = "#menuId")
  194 + public Set<Long> getMenuRoleIdListByMenuIdFromCache(Long menuId) {
  195 + return convertSet(roleMenuMapper.selectListByMenuId(menuId), RoleMenuDO::getRoleId);
241 } 196 }
242 197
  198 + // ========== 用户-角色的相关方法 ==========
  199 +
243 @Override 200 @Override
244 - @Transactional(rollbackFor = Exception.class) 201 + @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
  202 + @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId")
245 public void assignUserRole(Long userId, Set<Long> roleIds) { 203 public void assignUserRole(Long userId, Set<Long> roleIds) {
246 // 获得角色拥有角色编号 204 // 获得角色拥有角色编号
247 Set<Long> dbRoleIds = convertSet(userRoleMapper.selectListByUserId(userId), 205 Set<Long> dbRoleIds = convertSet(userRoleMapper.selectListByUserId(userId),
@@ -261,137 +219,68 @@ public class PermissionServiceImpl implements PermissionService { @@ -261,137 +219,68 @@ public class PermissionServiceImpl implements PermissionService {
261 if (!CollectionUtil.isEmpty(deleteMenuIds)) { 219 if (!CollectionUtil.isEmpty(deleteMenuIds)) {
262 userRoleMapper.deleteListByUserIdAndRoleIdIds(userId, deleteMenuIds); 220 userRoleMapper.deleteListByUserIdAndRoleIdIds(userId, deleteMenuIds);
263 } 221 }
264 - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了  
265 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {  
266 -  
267 - @Override  
268 - public void afterCommit() {  
269 - permissionProducer.sendUserRoleRefreshMessage();  
270 - }  
271 -  
272 - });  
273 } 222 }
274 223
275 @Override 224 @Override
276 - public void assignRoleDataScope(Long roleId, Integer dataScope, Set<Long> dataScopeDeptIds) {  
277 - roleService.updateRoleDataScope(roleId, dataScope, dataScopeDeptIds); 225 + @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId")
  226 + public void processUserDeleted(Long userId) {
  227 + userRoleMapper.deleteListByUserId(userId);
278 } 228 }
279 229
280 @Override 230 @Override
281 - @Transactional(rollbackFor = Exception.class)  
282 - public void processRoleDeleted(Long roleId) {  
283 - // 标记删除 UserRole  
284 - userRoleMapper.deleteListByRoleId(roleId);  
285 - // 标记删除 RoleMenu  
286 - roleMenuMapper.deleteListByRoleId(roleId);  
287 - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了  
288 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {  
289 -  
290 - @Override  
291 - public void afterCommit() {  
292 - permissionProducer.sendRoleMenuRefreshMessage();  
293 - permissionProducer.sendUserRoleRefreshMessage();  
294 - }  
295 -  
296 - }); 231 + public Set<Long> getUserRoleIdListByUserId(Long userId) {
  232 + return convertSet(userRoleMapper.selectListByUserId(userId), UserRoleDO::getRoleId);
297 } 233 }
298 234
299 @Override 235 @Override
300 - @Transactional(rollbackFor = Exception.class)  
301 - public void processMenuDeleted(Long menuId) {  
302 - roleMenuMapper.deleteListByMenuId(menuId);  
303 - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了  
304 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {  
305 -  
306 - @Override  
307 - public void afterCommit() {  
308 - permissionProducer.sendRoleMenuRefreshMessage();  
309 - }  
310 -  
311 - }); 236 + @Cacheable(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId")
  237 + public Set<Long> getUserRoleIdListByUserIdFromCache(Long userId) {
  238 + return getUserRoleIdListByUserId(userId);
312 } 239 }
313 240
314 @Override 241 @Override
315 - @Transactional(rollbackFor = Exception.class)  
316 - public void processUserDeleted(Long userId) {  
317 - userRoleMapper.deleteListByUserId(userId);  
318 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {  
319 -  
320 - @Override  
321 - public void afterCommit() {  
322 - permissionProducer.sendUserRoleRefreshMessage();  
323 - }  
324 -  
325 - }); 242 + public Set<Long> getUserRoleIdListByRoleId(Collection<Long> roleIds) {
  243 + return convertSet(userRoleMapper.selectListByRoleIds(roleIds), UserRoleDO::getUserId);
326 } 244 }
327 245
328 - @Override  
329 - public boolean hasAnyPermissions(Long userId, String... permissions) {  
330 - // 如果为空,说明已经有权限  
331 - if (ArrayUtil.isEmpty(permissions)) {  
332 - return true;  
333 - }  
334 -  
335 - // 获得当前登录的角色。如果为空,说明没有权限  
336 - Set<Long> roleIds = getUserRoleIdsFromCache(userId, singleton(CommonStatusEnum.ENABLE.getStatus()));  
337 - if (CollUtil.isEmpty(roleIds)) {  
338 - return false;  
339 - }  
340 - // 判断是否是超管。如果是,当然符合条件  
341 - if (roleService.hasAnySuperAdmin(roleIds)) {  
342 - return true;  
343 - }  
344 -  
345 - // 遍历权限,判断是否有一个满足  
346 - return Arrays.stream(permissions).anyMatch(permission -> {  
347 - List<MenuDO> menuList = menuService.getMenuListByPermissionFromCache(permission);  
348 - // 采用严格模式,如果权限找不到对应的 Menu 的话,认为  
349 - if (CollUtil.isEmpty(menuList)) {  
350 - return false;  
351 - }  
352 - // 获得是否拥有该权限,任一一个  
353 - return menuList.stream().anyMatch(menu -> CollUtil.containsAny(roleIds,  
354 - menuRoleCache.get(menu.getId())));  
355 - }); 246 + /**
  247 + * 获得用户拥有的角色,并且这些角色是开启状态的
  248 + *
  249 + * @param userId 用户编号
  250 + * @return 用户拥有的角色
  251 + */
  252 + @VisibleForTesting
  253 + List<RoleDO> getEnableUserRoleListByUserIdFromCache(Long userId) {
  254 + // 获得用户拥有的角色编号
  255 + Set<Long> roleIds = getSelf().getUserRoleIdListByUserIdFromCache(userId);
  256 + // 获得角色数组,并移除被禁用的
  257 + List<RoleDO> roles = roleService.getRoleListFromCache(roleIds);
  258 + roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus()));
  259 + return roles;
356 } 260 }
357 261
358 - @Override  
359 - public boolean hasAnyRoles(Long userId, String... roles) {  
360 - // 如果为空,说明已经有权限  
361 - if (ArrayUtil.isEmpty(roles)) {  
362 - return true;  
363 - } 262 + // ========== 用户-部门的相关方法 ==========
364 263
365 - // 获得当前登录的角色。如果为空,说明没有权限  
366 - Set<Long> roleIds = getUserRoleIdsFromCache(userId, singleton(CommonStatusEnum.ENABLE.getStatus()));  
367 - if (CollUtil.isEmpty(roleIds)) {  
368 - return false;  
369 - }  
370 - // 判断是否是超管。如果是,当然符合条件  
371 - if (roleService.hasAnySuperAdmin(roleIds)) {  
372 - return true;  
373 - }  
374 - Set<String> userRoles = convertSet(roleService.getRoleListFromCache(roleIds),  
375 - RoleDO::getCode);  
376 - return CollUtil.containsAny(userRoles, Sets.newHashSet(roles)); 264 + @Override
  265 + public void assignRoleDataScope(Long roleId, Integer dataScope, Set<Long> dataScopeDeptIds) {
  266 + roleService.updateRoleDataScope(roleId, dataScope, dataScopeDeptIds);
377 } 267 }
378 268
379 @Override 269 @Override
380 @DataPermission(enable = false) // 关闭数据权限,不然就会出现递归获取数据权限的问题 270 @DataPermission(enable = false) // 关闭数据权限,不然就会出现递归获取数据权限的问题
381 - @TenantIgnore // 忽略多租户的自动过滤。如果不忽略,会导致添加租户时,因为切换租户,导致获取不到 User。即使忽略,本身该方法不存在跨租户的操作,不会存在问题。  
382 public DeptDataPermissionRespDTO getDeptDataPermission(Long userId) { 271 public DeptDataPermissionRespDTO getDeptDataPermission(Long userId) {
383 // 获得用户的角色 272 // 获得用户的角色
384 - Set<Long> roleIds = getUserRoleIdsFromCache(userId, singleton(CommonStatusEnum.ENABLE.getStatus())); 273 + List<RoleDO> roles = getEnableUserRoleListByUserIdFromCache(userId);
  274 +
385 // 如果角色为空,则只能查看自己 275 // 如果角色为空,则只能查看自己
386 DeptDataPermissionRespDTO result = new DeptDataPermissionRespDTO(); 276 DeptDataPermissionRespDTO result = new DeptDataPermissionRespDTO();
387 - if (CollUtil.isEmpty(roleIds)) { 277 + if (CollUtil.isEmpty(roles)) {
388 result.setSelf(true); 278 result.setSelf(true);
389 return result; 279 return result;
390 } 280 }
391 - List<RoleDO> roles = roleService.getRoleListFromCache(roleIds);  
392 281
393 // 获得用户的部门编号的缓存,通过 Guava 的 Suppliers 惰性求值,即有且仅有第一次发起 DB 的查询 282 // 获得用户的部门编号的缓存,通过 Guava 的 Suppliers 惰性求值,即有且仅有第一次发起 DB 的查询
394 - Supplier<Long> userDeptIdCache = Suppliers.memoize(() -> userService.getUser(userId).getDeptId()); 283 + Supplier<Long> userDeptId = Suppliers.memoize(() -> userService.getUser(userId).getDeptId());
395 // 遍历每个角色,计算 284 // 遍历每个角色,计算
396 for (RoleDO role : roles) { 285 for (RoleDO role : roles) {
397 // 为空时,跳过 286 // 为空时,跳过
@@ -408,20 +297,19 @@ public class PermissionServiceImpl implements PermissionService { @@ -408,20 +297,19 @@ public class PermissionServiceImpl implements PermissionService {
408 CollUtil.addAll(result.getDeptIds(), role.getDataScopeDeptIds()); 297 CollUtil.addAll(result.getDeptIds(), role.getDataScopeDeptIds());
409 // 自定义可见部门时,保证可以看到自己所在的部门。否则,一些场景下可能会有问题。 298 // 自定义可见部门时,保证可以看到自己所在的部门。否则,一些场景下可能会有问题。
410 // 例如说,登录时,基于 t_user 的 username 查询会可能被 dept_id 过滤掉 299 // 例如说,登录时,基于 t_user 的 username 查询会可能被 dept_id 过滤掉
411 - CollUtil.addAll(result.getDeptIds(), userDeptIdCache.get()); 300 + CollUtil.addAll(result.getDeptIds(), userDeptId.get());
412 continue; 301 continue;
413 } 302 }
414 // 情况三,DEPT_ONLY 303 // 情况三,DEPT_ONLY
415 if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_ONLY.getScope())) { 304 if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_ONLY.getScope())) {
416 - CollectionUtils.addIfNotNull(result.getDeptIds(), userDeptIdCache.get()); 305 + CollectionUtils.addIfNotNull(result.getDeptIds(), userDeptId.get());
417 continue; 306 continue;
418 } 307 }
419 // 情况四,DEPT_DEPT_AND_CHILD 308 // 情况四,DEPT_DEPT_AND_CHILD
420 if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_AND_CHILD.getScope())) { 309 if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_AND_CHILD.getScope())) {
421 - List<DeptDO> depts = deptService.getDeptListByParentIdFromCache(userDeptIdCache.get(), true);  
422 - CollUtil.addAll(result.getDeptIds(), CollectionUtils.convertList(depts, DeptDO::getId)); 310 + CollUtil.addAll(result.getDeptIds(), deptService.getChildDeptIdListFromCache(userDeptId.get()));
423 // 添加本身部门编号 311 // 添加本身部门编号
424 - CollUtil.addAll(result.getDeptIds(), userDeptIdCache.get()); 312 + CollUtil.addAll(result.getDeptIds(), userDeptId.get());
425 continue; 313 continue;
426 } 314 }
427 // 情况五,SELF 315 // 情况五,SELF
@@ -430,9 +318,18 @@ public class PermissionServiceImpl implements PermissionService { @@ -430,9 +318,18 @@ public class PermissionServiceImpl implements PermissionService {
430 continue; 318 continue;
431 } 319 }
432 // 未知情况,error log 即可 320 // 未知情况,error log 即可
433 - log.error("[getDeptDataPermission][LoginUser({}) role({}) 无法处理]", userId, JsonUtils.toJsonString(result)); 321 + log.error("[getDeptDataPermission][LoginUser({}) role({}) 无法处理]", userId, toJsonString(result));
434 } 322 }
435 return result; 323 return result;
436 } 324 }
437 325
  326 + /**
  327 + * 获得自身的代理对象,解决 AOP 生效问题
  328 + *
  329 + * @return 自己
  330 + */
  331 + private PermissionServiceImpl getSelf() {
  332 + return SpringUtil.getBean(getClass());
  333 + }
  334 +
438 } 335 }
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 @@ -6,7 +6,6 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleEx
6 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; 6 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO;
7 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleUpdateReqVO; 7 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleUpdateReqVO;
8 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; 8 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
9 -import org.springframework.lang.Nullable;  
10 9
11 import javax.validation.Valid; 10 import javax.validation.Valid;
12 import java.util.Collection; 11 import java.util.Collection;
@@ -21,11 +20,6 @@ import java.util.Set; @@ -21,11 +20,6 @@ import java.util.Set;
21 public interface RoleService { 20 public interface RoleService {
22 21
23 /** 22 /**
24 - * 初始化角色的本地缓存  
25 - */  
26 - void initLocalCache();  
27 -  
28 - /**  
29 * 创建角色 23 * 创建角色
30 * 24 *
31 * @param reqVO 创建角色信息 25 * @param reqVO 创建角色信息
@@ -66,6 +60,14 @@ public interface RoleService { @@ -66,6 +60,14 @@ public interface RoleService {
66 void updateRoleDataScope(Long id, Integer dataScope, Set<Long> dataScopeDeptIds); 60 void updateRoleDataScope(Long id, Integer dataScope, Set<Long> dataScopeDeptIds);
67 61
68 /** 62 /**
  63 + * 获得角色
  64 + *
  65 + * @param id 角色编号
  66 + * @return 角色
  67 + */
  68 + RoleDO getRole(Long id);
  69 +
  70 + /**
69 * 获得角色,从缓存中 71 * 获得角色,从缓存中
70 * 72 *
71 * @param id 角色编号 73 * @param id 角色编号
@@ -76,10 +78,10 @@ public interface RoleService { @@ -76,10 +78,10 @@ public interface RoleService {
76 /** 78 /**
77 * 获得角色列表 79 * 获得角色列表
78 * 80 *
79 - * @param statuses 筛选的状态。允许空,空时不筛选 81 + * @param ids 角色编号数组
80 * @return 角色列表 82 * @return 角色列表
81 */ 83 */
82 - List<RoleDO> getRoleListByStatus(@Nullable Collection<Integer> statuses); 84 + List<RoleDO> getRoleList(Collection<Long> ids);
83 85
84 /** 86 /**
85 * 获得角色数组,从缓存中 87 * 获得角色数组,从缓存中
@@ -90,30 +92,19 @@ public interface RoleService { @@ -90,30 +92,19 @@ public interface RoleService {
90 List<RoleDO> getRoleListFromCache(Collection<Long> ids); 92 List<RoleDO> getRoleListFromCache(Collection<Long> ids);
91 93
92 /** 94 /**
93 - * 判断角色数组中,是否有超级管理员  
94 - *  
95 - * @param roleList 角色数组  
96 - * @return 是否有管理员  
97 - */  
98 - boolean hasAnySuperAdmin(Collection<RoleDO> roleList);  
99 -  
100 - /**  
101 - * 判断角色编号数组中,是否有管理员 95 + * 获得角色列表
102 * 96 *
103 - * @param ids 角色编号数组  
104 - * @return 是否有管理员 97 + * @param statuses 筛选的状态
  98 + * @return 角色列表
105 */ 99 */
106 - default boolean hasAnySuperAdmin(Set<Long> ids) {  
107 - return hasAnySuperAdmin(getRoleListFromCache(ids));  
108 - } 100 + List<RoleDO> getRoleListByStatus(Collection<Integer> statuses);
109 101
110 /** 102 /**
111 - * 获得角色 103 + * 获得所有角色列表
112 * 104 *
113 - * @param id 角色编号  
114 - * @return 角色 105 + * @return 角色列表
115 */ 106 */
116 - RoleDO getRole(Long id); 107 + List<RoleDO> getRoleList();
117 108
118 /** 109 /**
119 * 获得角色分页 110 * 获得角色分页
@@ -132,6 +123,14 @@ public interface RoleService { @@ -132,6 +123,14 @@ public interface RoleService {
132 List<RoleDO> getRoleList(RoleExportReqVO reqVO); 123 List<RoleDO> getRoleList(RoleExportReqVO reqVO);
133 124
134 /** 125 /**
  126 + * 判断角色编号数组中,是否有管理员
  127 + *
  128 + * @param ids 角色编号数组
  129 + * @return 是否有管理员
  130 + */
  131 + boolean hasAnySuperAdmin(Collection<Long> ids);
  132 +
  133 + /**
135 * 校验角色们是否有效。如下情况,视为无效: 134 * 校验角色们是否有效。如下情况,视为无效:
136 * 1. 角色编号不存在 135 * 1. 角色编号不存在
137 * 2. 角色被禁用 136 * 2. 角色被禁用
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; @@ -3,9 +3,9 @@ package cn.iocoder.yudao.module.system.service.permission;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.CollectionUtil; 4 import cn.hutool.core.collection.CollectionUtil;
5 import cn.hutool.core.util.ObjectUtil; 5 import cn.hutool.core.util.ObjectUtil;
  6 +import cn.hutool.extra.spring.SpringUtil;
6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;  
9 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleCreateReqVO; 9 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleCreateReqVO;
10 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleExportReqVO; 10 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleExportReqVO;
11 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; 11 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 @@ -13,26 +13,23 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleUp
13 import cn.iocoder.yudao.module.system.convert.permission.RoleConvert; 13 import cn.iocoder.yudao.module.system.convert.permission.RoleConvert;
14 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; 14 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
15 import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper; 15 import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper;
  16 +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
16 import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; 17 import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
17 import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; 18 import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
18 import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; 19 import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
19 -import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer;  
20 import com.google.common.annotations.VisibleForTesting; 20 import com.google.common.annotations.VisibleForTesting;
21 -import lombok.Getter;  
22 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
23 -import org.springframework.lang.Nullable; 22 +import org.springframework.cache.annotation.CacheEvict;
  23 +import org.springframework.cache.annotation.Cacheable;
24 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
25 import org.springframework.transaction.annotation.Transactional; 25 import org.springframework.transaction.annotation.Transactional;
26 -import org.springframework.transaction.support.TransactionSynchronization;  
27 -import org.springframework.transaction.support.TransactionSynchronizationManager;  
28 import org.springframework.util.StringUtils; 26 import org.springframework.util.StringUtils;
29 27
30 -import javax.annotation.PostConstruct;  
31 import javax.annotation.Resource; 28 import javax.annotation.Resource;
32 import java.util.*; 29 import java.util.*;
33 -import java.util.stream.Collectors;  
34 30
35 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 31 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  32 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
36 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; 33 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
37 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; 34 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
38 35
@@ -45,43 +42,14 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @@ -45,43 +42,14 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
45 @Slf4j 42 @Slf4j
46 public class RoleServiceImpl implements RoleService { 43 public class RoleServiceImpl implements RoleService {
47 44
48 - /**  
49 - * 角色缓存  
50 - * key:角色编号 {@link RoleDO#getId()}  
51 - *  
52 - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向  
53 - */  
54 - @Getter  
55 - private volatile Map<Long, RoleDO> roleCache;  
56 -  
57 @Resource 45 @Resource
58 private PermissionService permissionService; 46 private PermissionService permissionService;
59 47
60 @Resource 48 @Resource
61 private RoleMapper roleMapper; 49 private RoleMapper roleMapper;
62 50
63 - @Resource  
64 - private RoleProducer roleProducer;  
65 -  
66 - /**  
67 - * 初始化 {@link #roleCache} 缓存  
68 - */  
69 - @Override  
70 - @PostConstruct  
71 - public void initLocalCache() {  
72 - // 注意:忽略自动多租户,因为要全局初始化缓存  
73 - TenantUtils.executeIgnore(() -> {  
74 - // 第一步:查询数据  
75 - List<RoleDO> roleList = roleMapper.selectList();  
76 - log.info("[initLocalCache][缓存角色,数量为:{}]", roleList.size());  
77 -  
78 - // 第二步:构建缓存  
79 - roleCache = convertMap(roleList, RoleDO::getId);  
80 - });  
81 - }  
82 -  
83 @Override 51 @Override
84 - @Transactional 52 + @Transactional(rollbackFor = Exception.class)
85 public Long createRole(RoleCreateReqVO reqVO, Integer type) { 53 public Long createRole(RoleCreateReqVO reqVO, Integer type) {
86 // 校验角色 54 // 校验角色
87 validateRoleDuplicate(reqVO.getName(), reqVO.getCode(), null); 55 validateRoleDuplicate(reqVO.getName(), reqVO.getCode(), null);
@@ -91,18 +59,12 @@ public class RoleServiceImpl implements RoleService { @@ -91,18 +59,12 @@ public class RoleServiceImpl implements RoleService {
91 role.setStatus(CommonStatusEnum.ENABLE.getStatus()); 59 role.setStatus(CommonStatusEnum.ENABLE.getStatus());
92 role.setDataScope(DataScopeEnum.ALL.getScope()); // 默认可查看所有数据。原因是,可能一些项目不需要项目权限 60 role.setDataScope(DataScopeEnum.ALL.getScope()); // 默认可查看所有数据。原因是,可能一些项目不需要项目权限
93 roleMapper.insert(role); 61 roleMapper.insert(role);
94 - // 发送刷新消息  
95 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {  
96 - @Override  
97 - public void afterCommit() {  
98 - roleProducer.sendRoleRefreshMessage();  
99 - }  
100 - });  
101 // 返回 62 // 返回
102 return role.getId(); 63 return role.getId();
103 } 64 }
104 65
105 @Override 66 @Override
  67 + @CacheEvict(value = RedisKeyConstants.ROLE, key = "#reqVO.id")
106 public void updateRole(RoleUpdateReqVO reqVO) { 68 public void updateRole(RoleUpdateReqVO reqVO) {
107 // 校验是否可以更新 69 // 校验是否可以更新
108 validateRoleForUpdate(reqVO.getId()); 70 validateRoleForUpdate(reqVO.getId());
@@ -112,11 +74,10 @@ public class RoleServiceImpl implements RoleService { @@ -112,11 +74,10 @@ public class RoleServiceImpl implements RoleService {
112 // 更新到数据库 74 // 更新到数据库
113 RoleDO updateObj = RoleConvert.INSTANCE.convert(reqVO); 75 RoleDO updateObj = RoleConvert.INSTANCE.convert(reqVO);
114 roleMapper.updateById(updateObj); 76 roleMapper.updateById(updateObj);
115 - // 发送刷新消息  
116 - roleProducer.sendRoleRefreshMessage();  
117 } 77 }
118 78
119 @Override 79 @Override
  80 + @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id")
120 public void updateRoleStatus(Long id, Integer status) { 81 public void updateRoleStatus(Long id, Integer status) {
121 // 校验是否可以更新 82 // 校验是否可以更新
122 validateRoleForUpdate(id); 83 validateRoleForUpdate(id);
@@ -124,11 +85,10 @@ public class RoleServiceImpl implements RoleService { @@ -124,11 +85,10 @@ public class RoleServiceImpl implements RoleService {
124 // 更新状态 85 // 更新状态
125 RoleDO updateObj = new RoleDO().setId(id).setStatus(status); 86 RoleDO updateObj = new RoleDO().setId(id).setStatus(status);
126 roleMapper.updateById(updateObj); 87 roleMapper.updateById(updateObj);
127 - // 发送刷新消息  
128 - roleProducer.sendRoleRefreshMessage();  
129 } 88 }
130 89
131 @Override 90 @Override
  91 + @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id")
132 public void updateRoleDataScope(Long id, Integer dataScope, Set<Long> dataScopeDeptIds) { 92 public void updateRoleDataScope(Long id, Integer dataScope, Set<Long> dataScopeDeptIds) {
133 // 校验是否可以更新 93 // 校验是否可以更新
134 validateRoleForUpdate(id); 94 validateRoleForUpdate(id);
@@ -139,12 +99,11 @@ public class RoleServiceImpl implements RoleService { @@ -139,12 +99,11 @@ public class RoleServiceImpl implements RoleService {
139 updateObject.setDataScope(dataScope); 99 updateObject.setDataScope(dataScope);
140 updateObject.setDataScopeDeptIds(dataScopeDeptIds); 100 updateObject.setDataScopeDeptIds(dataScopeDeptIds);
141 roleMapper.updateById(updateObject); 101 roleMapper.updateById(updateObject);
142 - // 发送刷新消息  
143 - roleProducer.sendRoleRefreshMessage();  
144 } 102 }
145 103
146 @Override 104 @Override
147 @Transactional(rollbackFor = Exception.class) 105 @Transactional(rollbackFor = Exception.class)
  106 + @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id")
148 public void deleteRole(Long id) { 107 public void deleteRole(Long id) {
149 // 校验是否可以更新 108 // 校验是否可以更新
150 validateRoleForUpdate(id); 109 validateRoleForUpdate(id);
@@ -152,60 +111,6 @@ public class RoleServiceImpl implements RoleService { @@ -152,60 +111,6 @@ public class RoleServiceImpl implements RoleService {
152 roleMapper.deleteById(id); 111 roleMapper.deleteById(id);
153 // 删除相关数据 112 // 删除相关数据
154 permissionService.processRoleDeleted(id); 113 permissionService.processRoleDeleted(id);
155 - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了  
156 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {  
157 -  
158 - @Override  
159 - public void afterCommit() {  
160 - roleProducer.sendRoleRefreshMessage();  
161 - }  
162 -  
163 - });  
164 - }  
165 -  
166 - @Override  
167 - public RoleDO getRoleFromCache(Long id) {  
168 - return roleCache.get(id);  
169 - }  
170 -  
171 - @Override  
172 - public List<RoleDO> getRoleListByStatus(@Nullable Collection<Integer> statuses) {  
173 - if (CollUtil.isEmpty(statuses)) {  
174 - return roleMapper.selectList();  
175 - }  
176 - return roleMapper.selectListByStatus(statuses);  
177 - }  
178 -  
179 - @Override  
180 - public List<RoleDO> getRoleListFromCache(Collection<Long> ids) {  
181 - if (CollectionUtil.isEmpty(ids)) {  
182 - return Collections.emptyList();  
183 - }  
184 - return roleCache.values().stream().filter(roleDO -> ids.contains(roleDO.getId()))  
185 - .collect(Collectors.toList());  
186 - }  
187 -  
188 - @Override  
189 - public boolean hasAnySuperAdmin(Collection<RoleDO> roleList) {  
190 - if (CollectionUtil.isEmpty(roleList)) {  
191 - return false;  
192 - }  
193 - return roleList.stream().anyMatch(role -> RoleCodeEnum.isSuperAdmin(role.getCode()));  
194 - }  
195 -  
196 - @Override  
197 - public RoleDO getRole(Long id) {  
198 - return roleMapper.selectById(id);  
199 - }  
200 -  
201 - @Override  
202 - public PageResult<RoleDO> getRolePage(RolePageReqVO reqVO) {  
203 - return roleMapper.selectPage(reqVO);  
204 - }  
205 -  
206 - @Override  
207 - public List<RoleDO> getRoleList(RoleExportReqVO reqVO) {  
208 - return roleMapper.selectList(reqVO);  
209 } 114 }
210 115
211 /** 116 /**
@@ -258,6 +163,69 @@ public class RoleServiceImpl implements RoleService { @@ -258,6 +163,69 @@ public class RoleServiceImpl implements RoleService {
258 } 163 }
259 164
260 @Override 165 @Override
  166 + public RoleDO getRole(Long id) {
  167 + return roleMapper.selectById(id);
  168 + }
  169 +
  170 + @Override
  171 + @Cacheable(value = RedisKeyConstants.ROLE, key = "#id",
  172 + unless = "#result == null")
  173 + public RoleDO getRoleFromCache(Long id) {
  174 + return roleMapper.selectById(id);
  175 + }
  176 +
  177 +
  178 + @Override
  179 + public List<RoleDO> getRoleListByStatus(Collection<Integer> statuses) {
  180 + return roleMapper.selectListByStatus(statuses);
  181 + }
  182 +
  183 + @Override
  184 + public List<RoleDO> getRoleList() {
  185 + return roleMapper.selectList();
  186 + }
  187 +
  188 + @Override
  189 + public List<RoleDO> getRoleList(Collection<Long> ids) {
  190 + if (CollectionUtil.isEmpty(ids)) {
  191 + return Collections.emptyList();
  192 + }
  193 + return roleMapper.selectBatchIds(ids);
  194 + }
  195 +
  196 + @Override
  197 + public List<RoleDO> getRoleListFromCache(Collection<Long> ids) {
  198 + if (CollectionUtil.isEmpty(ids)) {
  199 + return Collections.emptyList();
  200 + }
  201 + // 这里采用 for 循环从缓存中获取,主要考虑 Spring CacheManager 无法批量操作的问题
  202 + RoleServiceImpl self = getSelf();
  203 + return convertList(ids, self::getRoleFromCache);
  204 + }
  205 +
  206 + @Override
  207 + public PageResult<RoleDO> getRolePage(RolePageReqVO reqVO) {
  208 + return roleMapper.selectPage(reqVO);
  209 + }
  210 +
  211 + @Override
  212 + public List<RoleDO> getRoleList(RoleExportReqVO reqVO) {
  213 + return roleMapper.selectList(reqVO);
  214 + }
  215 +
  216 + @Override
  217 + public boolean hasAnySuperAdmin(Collection<Long> ids) {
  218 + if (CollectionUtil.isEmpty(ids)) {
  219 + return false;
  220 + }
  221 + RoleServiceImpl self = getSelf();
  222 + return ids.stream().anyMatch(id -> {
  223 + RoleDO role = self.getRoleFromCache(id);
  224 + return role != null && RoleCodeEnum.isSuperAdmin(role.getCode());
  225 + });
  226 + }
  227 +
  228 + @Override
261 public void validateRoleList(Collection<Long> ids) { 229 public void validateRoleList(Collection<Long> ids) {
262 if (CollUtil.isEmpty(ids)) { 230 if (CollUtil.isEmpty(ids)) {
263 return; 231 return;
@@ -276,4 +244,14 @@ public class RoleServiceImpl implements RoleService { @@ -276,4 +244,14 @@ public class RoleServiceImpl implements RoleService {
276 } 244 }
277 }); 245 });
278 } 246 }
  247 +
  248 + /**
  249 + * 获得自身的代理对象,解决 AOP 生效问题
  250 + *
  251 + * @return 自己
  252 + */
  253 + private RoleServiceImpl getSelf() {
  254 + return SpringUtil.getBean(getClass());
  255 + }
  256 +
279 } 257 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordService.java
@@ -19,11 +19,6 @@ import java.util.Set; @@ -19,11 +19,6 @@ import java.util.Set;
19 public interface SensitiveWordService { 19 public interface SensitiveWordService {
20 20
21 /** 21 /**
22 - * 初始化本地缓存  
23 - */  
24 - void initLocalCache();  
25 -  
26 - /**  
27 * 创建敏感词 22 * 创建敏感词
28 * 23 *
29 * @param createReqVO 创建信息 24 * @param createReqVO 创建信息
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImpl.java
@@ -11,21 +11,24 @@ import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.Sensitiv @@ -11,21 +11,24 @@ import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.Sensitiv
11 import cn.iocoder.yudao.module.system.convert.sensitiveword.SensitiveWordConvert; 11 import cn.iocoder.yudao.module.system.convert.sensitiveword.SensitiveWordConvert;
12 import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO; 12 import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
13 import cn.iocoder.yudao.module.system.dal.mysql.sensitiveword.SensitiveWordMapper; 13 import cn.iocoder.yudao.module.system.dal.mysql.sensitiveword.SensitiveWordMapper;
14 -import cn.iocoder.yudao.module.system.mq.producer.sensitiveword.SensitiveWordProducer;  
15 import cn.iocoder.yudao.module.system.util.collection.SimpleTrie; 14 import cn.iocoder.yudao.module.system.util.collection.SimpleTrie;
16 import com.google.common.collect.HashMultimap; 15 import com.google.common.collect.HashMultimap;
17 import com.google.common.collect.Multimap; 16 import com.google.common.collect.Multimap;
18 import lombok.Getter; 17 import lombok.Getter;
19 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.scheduling.annotation.Scheduled;
20 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
21 import org.springframework.validation.annotation.Validated; 21 import org.springframework.validation.annotation.Validated;
22 22
23 import javax.annotation.PostConstruct; 23 import javax.annotation.PostConstruct;
24 import javax.annotation.Resource; 24 import javax.annotation.Resource;
  25 +import java.time.LocalDateTime;
25 import java.util.*; 26 import java.util.*;
  27 +import java.util.concurrent.TimeUnit;
26 28
27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
28 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; 30 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
  31 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
29 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_WORD_EXISTS; 32 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_WORD_EXISTS;
30 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_WORD_NOT_EXISTS; 33 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_WORD_NOT_EXISTS;
31 34
@@ -40,6 +43,11 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_ @@ -40,6 +43,11 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_
40 public class SensitiveWordServiceImpl implements SensitiveWordService { 43 public class SensitiveWordServiceImpl implements SensitiveWordService {
41 44
42 /** 45 /**
  46 + * 敏感词列表缓存
  47 + */
  48 + @Getter
  49 + private volatile List<SensitiveWordDO> sensitiveWordCache = Collections.emptyList();
  50 + /**
43 * 敏感词标签缓存 51 * 敏感词标签缓存
44 * key:敏感词编号 {@link SensitiveWordDO#getId()} 52 * key:敏感词编号 {@link SensitiveWordDO#getId()}
45 * <p> 53 * <p>
@@ -51,9 +59,6 @@ public class SensitiveWordServiceImpl implements SensitiveWordService { @@ -51,9 +59,6 @@ public class SensitiveWordServiceImpl implements SensitiveWordService {
51 @Resource 59 @Resource
52 private SensitiveWordMapper sensitiveWordMapper; 60 private SensitiveWordMapper sensitiveWordMapper;
53 61
54 - @Resource  
55 - private SensitiveWordProducer sensitiveWordProducer;  
56 -  
57 /** 62 /**
58 * 默认的敏感词的字典树,包含所有敏感词 63 * 默认的敏感词的字典树,包含所有敏感词
59 */ 64 */
@@ -68,7 +73,6 @@ public class SensitiveWordServiceImpl implements SensitiveWordService { @@ -68,7 +73,6 @@ public class SensitiveWordServiceImpl implements SensitiveWordService {
68 /** 73 /**
69 * 初始化缓存 74 * 初始化缓存
70 */ 75 */
71 - @Override  
72 @PostConstruct 76 @PostConstruct
73 public void initLocalCache() { 77 public void initLocalCache() {
74 // 第一步:查询数据 78 // 第一步:查询数据
@@ -80,6 +84,7 @@ public class SensitiveWordServiceImpl implements SensitiveWordService { @@ -80,6 +84,7 @@ public class SensitiveWordServiceImpl implements SensitiveWordService {
80 Set<String> tags = new HashSet<>(); 84 Set<String> tags = new HashSet<>();
81 sensitiveWords.forEach(word -> tags.addAll(word.getTags())); 85 sensitiveWords.forEach(word -> tags.addAll(word.getTags()));
82 sensitiveWordTagsCache = tags; 86 sensitiveWordTagsCache = tags;
  87 + sensitiveWordCache = sensitiveWords;
83 // 写入 defaultSensitiveWordTrie、tagSensitiveWordTries 缓存 88 // 写入 defaultSensitiveWordTrie、tagSensitiveWordTries 缓存
84 initSensitiveWordTrie(sensitiveWords); 89 initSensitiveWordTrie(sensitiveWords);
85 } 90 }
@@ -105,6 +110,26 @@ public class SensitiveWordServiceImpl implements SensitiveWordService { @@ -105,6 +110,26 @@ public class SensitiveWordServiceImpl implements SensitiveWordService {
105 this.tagSensitiveWordTries = tagSensitiveWordTries; 110 this.tagSensitiveWordTries = tagSensitiveWordTries;
106 } 111 }
107 112
  113 + /**
  114 + * 通过定时任务轮询,刷新缓存
  115 + *
  116 + * 目的:多节点部署时,通过轮询”通知“所有节点,进行刷新
  117 + */
  118 + @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS)
  119 + public void refreshLocalCache() {
  120 + // 情况一:如果缓存里没有数据,则直接刷新缓存
  121 + if (CollUtil.isEmpty(sensitiveWordCache)) {
  122 + initLocalCache();
  123 + return;
  124 + }
  125 +
  126 + // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存
  127 + LocalDateTime maxTime = getMaxValue(sensitiveWordCache, SensitiveWordDO::getUpdateTime);
  128 + if (sensitiveWordMapper.selectCountByUpdateTimeGt(maxTime) > 0) {
  129 + initLocalCache();
  130 + }
  131 + }
  132 +
108 @Override 133 @Override
109 public Long createSensitiveWord(SensitiveWordCreateReqVO createReqVO) { 134 public Long createSensitiveWord(SensitiveWordCreateReqVO createReqVO) {
110 // 校验唯一性 135 // 校验唯一性
@@ -113,8 +138,9 @@ public class SensitiveWordServiceImpl implements SensitiveWordService { @@ -113,8 +138,9 @@ public class SensitiveWordServiceImpl implements SensitiveWordService {
113 // 插入 138 // 插入
114 SensitiveWordDO sensitiveWord = SensitiveWordConvert.INSTANCE.convert(createReqVO); 139 SensitiveWordDO sensitiveWord = SensitiveWordConvert.INSTANCE.convert(createReqVO);
115 sensitiveWordMapper.insert(sensitiveWord); 140 sensitiveWordMapper.insert(sensitiveWord);
116 - // 发送消息,刷新缓存  
117 - sensitiveWordProducer.sendSensitiveWordRefreshMessage(); 141 +
  142 + // 刷新缓存
  143 + initLocalCache();
118 return sensitiveWord.getId(); 144 return sensitiveWord.getId();
119 } 145 }
120 146
@@ -127,8 +153,9 @@ public class SensitiveWordServiceImpl implements SensitiveWordService { @@ -127,8 +153,9 @@ public class SensitiveWordServiceImpl implements SensitiveWordService {
127 // 更新 153 // 更新
128 SensitiveWordDO updateObj = SensitiveWordConvert.INSTANCE.convert(updateReqVO); 154 SensitiveWordDO updateObj = SensitiveWordConvert.INSTANCE.convert(updateReqVO);
129 sensitiveWordMapper.updateById(updateObj); 155 sensitiveWordMapper.updateById(updateObj);
130 - // 发送消息,刷新缓存  
131 - sensitiveWordProducer.sendSensitiveWordRefreshMessage(); 156 +
  157 + // 刷新缓存
  158 + initLocalCache();
132 } 159 }
133 160
134 @Override 161 @Override
@@ -137,8 +164,9 @@ public class SensitiveWordServiceImpl implements SensitiveWordService { @@ -137,8 +164,9 @@ public class SensitiveWordServiceImpl implements SensitiveWordService {
137 validateSensitiveWordExists(id); 164 validateSensitiveWordExists(id);
138 // 删除 165 // 删除
139 sensitiveWordMapper.deleteById(id); 166 sensitiveWordMapper.deleteById(id);
140 - // 发送消息,刷新缓存  
141 - sensitiveWordProducer.sendSensitiveWordRefreshMessage(); 167 +
  168 + // 刷新缓存
  169 + initLocalCache();
142 } 170 }
143 171
144 private void validateSensitiveWordNameUnique(Long id, String name) { 172 private void validateSensitiveWordNameUnique(Long id, String name) {
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java
1 package cn.iocoder.yudao.module.system.service.sms; 1 package cn.iocoder.yudao.module.system.service.sms;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory; 5 import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
5 import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties; 6 import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
@@ -9,15 +10,20 @@ import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannel @@ -9,15 +10,20 @@ import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannel
9 import cn.iocoder.yudao.module.system.convert.sms.SmsChannelConvert; 10 import cn.iocoder.yudao.module.system.convert.sms.SmsChannelConvert;
10 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; 11 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
11 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper; 12 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
12 -import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer; 13 +import lombok.Getter;
13 import lombok.extern.slf4j.Slf4j; 14 import lombok.extern.slf4j.Slf4j;
  15 +import org.springframework.scheduling.annotation.Scheduled;
14 import org.springframework.stereotype.Service; 16 import org.springframework.stereotype.Service;
15 17
16 import javax.annotation.PostConstruct; 18 import javax.annotation.PostConstruct;
17 import javax.annotation.Resource; 19 import javax.annotation.Resource;
  20 +import java.time.LocalDateTime;
  21 +import java.util.Collections;
18 import java.util.List; 22 import java.util.List;
  23 +import java.util.concurrent.TimeUnit;
19 24
20 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  26 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
21 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN; 27 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
22 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS; 28 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
23 29
@@ -30,6 +36,12 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNE @@ -30,6 +36,12 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNE
30 @Slf4j 36 @Slf4j
31 public class SmsChannelServiceImpl implements SmsChannelService { 37 public class SmsChannelServiceImpl implements SmsChannelService {
32 38
  39 + /**
  40 + * 短信渠道列表的缓存
  41 + */
  42 + @Getter
  43 + private volatile List<SmsChannelDO> channelCache = Collections.emptyList();
  44 +
33 @Resource 45 @Resource
34 private SmsClientFactory smsClientFactory; 46 private SmsClientFactory smsClientFactory;
35 47
@@ -39,9 +51,6 @@ public class SmsChannelServiceImpl implements SmsChannelService { @@ -39,9 +51,6 @@ public class SmsChannelServiceImpl implements SmsChannelService {
39 @Resource 51 @Resource
40 private SmsTemplateService smsTemplateService; 52 private SmsTemplateService smsTemplateService;
41 53
42 - @Resource  
43 - private SmsProducer smsProducer;  
44 -  
45 @Override 54 @Override
46 @PostConstruct 55 @PostConstruct
47 public void initLocalCache() { 56 public void initLocalCache() {
@@ -52,6 +61,27 @@ public class SmsChannelServiceImpl implements SmsChannelService { @@ -52,6 +61,27 @@ public class SmsChannelServiceImpl implements SmsChannelService {
52 // 第二步:构建缓存:创建或更新短信 Client 61 // 第二步:构建缓存:创建或更新短信 Client
53 List<SmsChannelProperties> propertiesList = SmsChannelConvert.INSTANCE.convertList02(channels); 62 List<SmsChannelProperties> propertiesList = SmsChannelConvert.INSTANCE.convertList02(channels);
54 propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties)); 63 propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties));
  64 + this.channelCache = channels;
  65 + }
  66 +
  67 + /**
  68 + * 通过定时任务轮询,刷新缓存
  69 + *
  70 + * 目的:多节点部署时,通过轮询”通知“所有节点,进行刷新
  71 + */
  72 + @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS)
  73 + public void refreshLocalCache() {
  74 + // 情况一:如果缓存里没有数据,则直接刷新缓存
  75 + if (CollUtil.isEmpty(channelCache)) {
  76 + initLocalCache();
  77 + return;
  78 + }
  79 +
  80 + // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存
  81 + LocalDateTime maxTime = getMaxValue(channelCache, SmsChannelDO::getUpdateTime);
  82 + if (smsChannelMapper.selectCountByUpdateTimeGt(maxTime) > 0) {
  83 + initLocalCache();
  84 + }
55 } 85 }
56 86
57 @Override 87 @Override
@@ -59,9 +89,9 @@ public class SmsChannelServiceImpl implements SmsChannelService { @@ -59,9 +89,9 @@ public class SmsChannelServiceImpl implements SmsChannelService {
59 // 插入 89 // 插入
60 SmsChannelDO smsChannel = SmsChannelConvert.INSTANCE.convert(createReqVO); 90 SmsChannelDO smsChannel = SmsChannelConvert.INSTANCE.convert(createReqVO);
61 smsChannelMapper.insert(smsChannel); 91 smsChannelMapper.insert(smsChannel);
62 - // 发送刷新消息  
63 - smsProducer.sendSmsChannelRefreshMessage();  
64 - // 返回 92 +
  93 + // 刷新缓存
  94 + initLocalCache();
65 return smsChannel.getId(); 95 return smsChannel.getId();
66 } 96 }
67 97
@@ -72,8 +102,9 @@ public class SmsChannelServiceImpl implements SmsChannelService { @@ -72,8 +102,9 @@ public class SmsChannelServiceImpl implements SmsChannelService {
72 // 更新 102 // 更新
73 SmsChannelDO updateObj = SmsChannelConvert.INSTANCE.convert(updateReqVO); 103 SmsChannelDO updateObj = SmsChannelConvert.INSTANCE.convert(updateReqVO);
74 smsChannelMapper.updateById(updateObj); 104 smsChannelMapper.updateById(updateObj);
75 - // 发送刷新消息  
76 - smsProducer.sendSmsChannelRefreshMessage(); 105 +
  106 + // 刷新缓存
  107 + initLocalCache();
77 } 108 }
78 109
79 @Override 110 @Override
@@ -86,8 +117,9 @@ public class SmsChannelServiceImpl implements SmsChannelService { @@ -86,8 +117,9 @@ public class SmsChannelServiceImpl implements SmsChannelService {
86 } 117 }
87 // 删除 118 // 删除
88 smsChannelMapper.deleteById(id); 119 smsChannelMapper.deleteById(id);
89 - // 发送刷新消息  
90 - smsProducer.sendSmsChannelRefreshMessage(); 120 +
  121 + // 刷新缓存
  122 + initLocalCache();
91 } 123 }
92 124
93 private void validateSmsChannelExists(Long id) { 125 private void validateSmsChannelExists(Long id) {
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; @@ -8,7 +8,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
8 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 9
10 import javax.validation.Valid; 10 import javax.validation.Valid;
11 -import java.util.Collection;  
12 import java.util.List; 11 import java.util.List;
13 import java.util.Map; 12 import java.util.Map;
14 13
@@ -16,41 +15,11 @@ import java.util.Map; @@ -16,41 +15,11 @@ import java.util.Map;
16 * 短信模板 Service 接口 15 * 短信模板 Service 接口
17 * 16 *
18 * @author zzf 17 * @author zzf
19 - * @date 2021/1/25 9:24 18 + * @since 2021/1/25 9:24
20 */ 19 */
21 public interface SmsTemplateService { 20 public interface SmsTemplateService {
22 21
23 /** 22 /**
24 - * 初始化短信模板的本地缓存  
25 - */  
26 - void initLocalCache();  
27 -  
28 - /**  
29 - * 获得短信模板,从缓存中  
30 - *  
31 - * @param code 模板编码  
32 - * @return 短信模板  
33 - */  
34 - SmsTemplateDO getSmsTemplateByCodeFromCache(String code);  
35 -  
36 - /**  
37 - * 格式化短信内容  
38 - *  
39 - * @param content 短信模板的内容  
40 - * @param params 内容的参数  
41 - * @return 格式化后的内容  
42 - */  
43 - String formatSmsTemplateContent(String content, Map<String, Object> params);  
44 -  
45 - /**  
46 - * 获得短信模板  
47 - *  
48 - * @param code 模板编码  
49 - * @return 短信模板  
50 - */  
51 - SmsTemplateDO getSmsTemplateByCode(String code);  
52 -  
53 - /**  
54 * 创建短信模板 23 * 创建短信模板
55 * 24 *
56 * @param createReqVO 创建信息 25 * @param createReqVO 创建信息
@@ -81,12 +50,12 @@ public interface SmsTemplateService { @@ -81,12 +50,12 @@ public interface SmsTemplateService {
81 SmsTemplateDO getSmsTemplate(Long id); 50 SmsTemplateDO getSmsTemplate(Long id);
82 51
83 /** 52 /**
84 - * 获得短信模板列表 53 + * 获得短信模板,从缓存中
85 * 54 *
86 - * @param ids 编号  
87 - * @return 短信模板列表 55 + * @param code 模板编码
  56 + * @return 短信模板
88 */ 57 */
89 - List<SmsTemplateDO> getSmsTemplateList(Collection<Long> ids); 58 + SmsTemplateDO getSmsTemplateByCodeFromCache(String code);
90 59
91 /** 60 /**
92 * 获得短信模板分页 61 * 获得短信模板分页
@@ -112,4 +81,14 @@ public interface SmsTemplateService { @@ -112,4 +81,14 @@ public interface SmsTemplateService {
112 */ 81 */
113 Long countByChannelId(Long channelId); 82 Long countByChannelId(Long channelId);
114 83
  84 +
  85 + /**
  86 + * 格式化短信内容
  87 + *
  88 + * @param content 短信模板的内容
  89 + * @param params 内容的参数
  90 + * @return 格式化后的内容
  91 + */
  92 + String formatSmsTemplateContent(String content, Map<String, Object> params);
  93 +
115 } 94 }
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; @@ -4,7 +4,6 @@ import cn.hutool.core.util.ReUtil;
4 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;  
8 import cn.iocoder.yudao.framework.sms.core.client.SmsClient; 7 import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
9 import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory; 8 import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
10 import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; 9 import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
@@ -17,16 +16,15 @@ import cn.iocoder.yudao.module.system.convert.sms.SmsTemplateConvert; @@ -17,16 +16,15 @@ import cn.iocoder.yudao.module.system.convert.sms.SmsTemplateConvert;
17 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; 16 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
18 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; 17 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
19 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper; 18 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper;
20 -import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer; 19 +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
21 import com.google.common.annotations.VisibleForTesting; 20 import com.google.common.annotations.VisibleForTesting;
22 -import lombok.Getter;  
23 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
  22 +import org.springframework.cache.annotation.CacheEvict;
  23 +import org.springframework.cache.annotation.Cacheable;
24 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
25 import org.springframework.util.Assert; 25 import org.springframework.util.Assert;
26 26
27 -import javax.annotation.PostConstruct;  
28 import javax.annotation.Resource; 27 import javax.annotation.Resource;
29 -import java.util.Collection;  
30 import java.util.List; 28 import java.util.List;
31 import java.util.Map; 29 import java.util.Map;
32 import java.util.Objects; 30 import java.util.Objects;
@@ -59,49 +57,6 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { @@ -59,49 +57,6 @@ public class SmsTemplateServiceImpl implements SmsTemplateService {
59 @Resource 57 @Resource
60 private SmsClientFactory smsClientFactory; 58 private SmsClientFactory smsClientFactory;
61 59
62 - @Resource  
63 - private SmsProducer smsProducer;  
64 -  
65 - /**  
66 - * 短信模板缓存  
67 - * key:短信模板编码 {@link SmsTemplateDO#getCode()}  
68 - *  
69 - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向  
70 - */  
71 - @Getter // 为了方便测试,这里提供 getter 方法  
72 - private volatile Map<String, SmsTemplateDO> smsTemplateCache;  
73 -  
74 - @Override  
75 - @PostConstruct  
76 - public void initLocalCache() {  
77 - // 第一步:查询数据  
78 - List<SmsTemplateDO> smsTemplateList = smsTemplateMapper.selectList();  
79 - log.info("[initLocalCache][缓存短信模版,数量为:{}]", smsTemplateList.size());  
80 -  
81 - // 第二步:构建缓存  
82 - smsTemplateCache = CollectionUtils.convertMap(smsTemplateList, SmsTemplateDO::getCode);  
83 - }  
84 -  
85 - @Override  
86 - public SmsTemplateDO getSmsTemplateByCodeFromCache(String code) {  
87 - return smsTemplateCache.get(code);  
88 - }  
89 -  
90 - @Override  
91 - public String formatSmsTemplateContent(String content, Map<String, Object> params) {  
92 - return StrUtil.format(content, params);  
93 - }  
94 -  
95 - @Override  
96 - public SmsTemplateDO getSmsTemplateByCode(String code) {  
97 - return smsTemplateMapper.selectByCode(code);  
98 - }  
99 -  
100 - @VisibleForTesting  
101 - public List<String> parseTemplateContentParams(String content) {  
102 - return ReUtil.findAllGroup1(PATTERN_PARAMS, content);  
103 - }  
104 -  
105 @Override 60 @Override
106 public Long createSmsTemplate(SmsTemplateCreateReqVO createReqVO) { 61 public Long createSmsTemplate(SmsTemplateCreateReqVO createReqVO) {
107 // 校验短信渠道 62 // 校验短信渠道
@@ -116,13 +71,13 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { @@ -116,13 +71,13 @@ public class SmsTemplateServiceImpl implements SmsTemplateService {
116 template.setParams(parseTemplateContentParams(template.getContent())); 71 template.setParams(parseTemplateContentParams(template.getContent()));
117 template.setChannelCode(channelDO.getCode()); 72 template.setChannelCode(channelDO.getCode());
118 smsTemplateMapper.insert(template); 73 smsTemplateMapper.insert(template);
119 - // 发送刷新消息  
120 - smsProducer.sendSmsTemplateRefreshMessage();  
121 // 返回 74 // 返回
122 return template.getId(); 75 return template.getId();
123 } 76 }
124 77
125 @Override 78 @Override
  79 + @CacheEvict(cacheNames = RedisKeyConstants.SMS_TEMPLATE,
  80 + allEntries = true) // allEntries 清空所有缓存,因为可能修改到 code 字段,不好清理
126 public void updateSmsTemplate(SmsTemplateUpdateReqVO updateReqVO) { 81 public void updateSmsTemplate(SmsTemplateUpdateReqVO updateReqVO) {
127 // 校验存在 82 // 校验存在
128 validateSmsTemplateExists(updateReqVO.getId()); 83 validateSmsTemplateExists(updateReqVO.getId());
@@ -138,18 +93,16 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { @@ -138,18 +93,16 @@ public class SmsTemplateServiceImpl implements SmsTemplateService {
138 updateObj.setParams(parseTemplateContentParams(updateObj.getContent())); 93 updateObj.setParams(parseTemplateContentParams(updateObj.getContent()));
139 updateObj.setChannelCode(channelDO.getCode()); 94 updateObj.setChannelCode(channelDO.getCode());
140 smsTemplateMapper.updateById(updateObj); 95 smsTemplateMapper.updateById(updateObj);
141 - // 发送刷新消息  
142 - smsProducer.sendSmsTemplateRefreshMessage();  
143 } 96 }
144 97
145 @Override 98 @Override
  99 + @CacheEvict(cacheNames = RedisKeyConstants.SMS_TEMPLATE,
  100 + allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 code,不好清理
146 public void deleteSmsTemplate(Long id) { 101 public void deleteSmsTemplate(Long id) {
147 // 校验存在 102 // 校验存在
148 validateSmsTemplateExists(id); 103 validateSmsTemplateExists(id);
149 // 更新 104 // 更新
150 smsTemplateMapper.deleteById(id); 105 smsTemplateMapper.deleteById(id);
151 - // 发送刷新消息  
152 - smsProducer.sendSmsTemplateRefreshMessage();  
153 } 106 }
154 107
155 private void validateSmsTemplateExists(Long id) { 108 private void validateSmsTemplateExists(Long id) {
@@ -164,8 +117,10 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { @@ -164,8 +117,10 @@ public class SmsTemplateServiceImpl implements SmsTemplateService {
164 } 117 }
165 118
166 @Override 119 @Override
167 - public List<SmsTemplateDO> getSmsTemplateList(Collection<Long> ids) {  
168 - return smsTemplateMapper.selectBatchIds(ids); 120 + @Cacheable(cacheNames = RedisKeyConstants.SMS_TEMPLATE, key = "#code",
  121 + unless = "#result == null")
  122 + public SmsTemplateDO getSmsTemplateByCodeFromCache(String code) {
  123 + return smsTemplateMapper.selectByCode(code);
169 } 124 }
170 125
171 @Override 126 @Override
@@ -217,7 +172,7 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { @@ -217,7 +172,7 @@ public class SmsTemplateServiceImpl implements SmsTemplateService {
217 * @param apiTemplateId API 模板编号 172 * @param apiTemplateId API 模板编号
218 */ 173 */
219 @VisibleForTesting 174 @VisibleForTesting
220 - public void validateApiTemplate(Long channelId, String apiTemplateId) { 175 + void validateApiTemplate(Long channelId, String apiTemplateId) {
221 // 获得短信模板 176 // 获得短信模板
222 SmsClient smsClient = smsClientFactory.getSmsClient(channelId); 177 SmsClient smsClient = smsClientFactory.getSmsClient(channelId);
223 Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", channelId)); 178 Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", channelId));
@@ -226,4 +181,14 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { @@ -226,4 +181,14 @@ public class SmsTemplateServiceImpl implements SmsTemplateService {
226 templateResult.checkError(); 181 templateResult.checkError();
227 } 182 }
228 183
  184 + @Override
  185 + public String formatSmsTemplateContent(String content, Map<String, Object> params) {
  186 + return StrUtil.format(content, params);
  187 + }
  188 +
  189 + @VisibleForTesting
  190 + List<String> parseTemplateContentParams(String content) {
  191 + return ReUtil.findAllGroup1(PATTERN_PARAMS, content);
  192 + }
  193 +
229 } 194 }