Commit bf3e848c171c6e63c94f09cbb2df2fee01c2b9b3

Authored by YunaiV
2 parents 6a387387 7a49e45b

Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/mall_product

Showing 169 changed files with 2097 additions and 3939 deletions
Docker-HOWTO.md
1 # Docker Build & Up 1 # Docker Build & Up
2 2
3 目标: 快速部署体验系统,帮助了解系统之间的依赖关系。 3 目标: 快速部署体验系统,帮助了解系统之间的依赖关系。
  4 +依赖:docker compose v2,删除`name: yudao-system`,降低`version`版本为`3.3`以下,支持`docker-compose`。
4 5
5 ## 功能文件列表 6 ## 功能文件列表
6 7
7 ```text 8 ```text
8 . 9 .
9 -├── Docker-HOWTO.md  
10 -├── docker-compose.yml  
11 -├── docker.env 10 +├── Docker-HOWTO.md
  11 +├── docker-compose.yml
  12 +├── docker.env <-- 提供docker-compose环境变量配置
12 ├── yudao-server 13 ├── yudao-server
13 -│ ├── Dockerfile  
14 -│ └── nginx.conf 14 +│ └── Dockerfile
15 └── yudao-ui-admin 15 └── yudao-ui-admin
16 ├── .dockerignore 16 ├── .dockerignore
17 - └── Dockerfile 17 + ├── Dockerfile
  18 + └── nginx.conf <-- 提供基础配置,gzip压缩、api转发
18 ``` 19 ```
19 20
20 -## Maven build (Optional) 21 +## 构建 jar 包
21 22
22 ```shell 23 ```shell
23 # 创建maven缓存volume 24 # 创建maven缓存volume
@@ -30,29 +31,19 @@ docker run -it --rm --name yudao-maven \ @@ -30,29 +31,19 @@ docker run -it --rm --name yudao-maven \
30 maven mvn clean install package '-Dmaven.test.skip=true' 31 maven mvn clean install package '-Dmaven.test.skip=true'
31 ``` 32 ```
32 33
33 -## Docker Compose Build  
34 -  
35 -```shell  
36 -docker compose --env-file docker.env build  
37 -```  
38 -  
39 -## Docker Compose Up 34 +## 构建启动服务
40 35
41 ```shell 36 ```shell
42 docker compose --env-file docker.env up -d 37 docker compose --env-file docker.env up -d
43 ``` 38 ```
44 39
45 -第一次执行,由于数据库未初始化,因此yudao-server容器会运行失败。执行如下命令初始化数据库: 40 +首次运行会自动构建容器。可以通过`docker compose build [service]`来手动构建所有或某个docker镜像
46 41
47 -```shell  
48 -docker compose exec -T mysql \  
49 - sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" --default-character-set=utf8mb4 ruoyi-vue-pro' \  
50 - < ./sql/mysql/ruoyi-vue-pro.sql  
51 -``` 42 +`--env-file docker.env`为可选参数,只是展示了通过`.env`文件配置容器启动的环境变量,`docker-compose.yml`本身已经提供足够的默认参数来正常运行系统。
52 43
53 -## Server:Port 44 +## 服务器的宿主机端口映射
54 45
55 -- admin: http://localhost:8080  
56 -- API: http://localhost:48080  
57 -- mysql: root/123456, port: 3308 46 +- admin ui: http://localhost:8080
  47 +- api server: http://localhost:48080
  48 +- mysql: root/123456, port: 3306
58 - redis: port: 6379 49 - redis: port: 6379
README.md
@@ -102,7 +102,7 @@ @@ -102,7 +102,7 @@
102 102
103 系统内置多种多种业务功能,可以用于快速你的业务系统: 103 系统内置多种多种业务功能,可以用于快速你的业务系统:
104 104
105 -![功能分层](https://static.iocoder.cn/ruoyi-vue-pro-biz.png) 105 +![功能分层](https://static.iocoder.cn/ruoyi-vue-pro-biz.png?imageView2/2/format/webp)
106 106
107 * 系统功能 107 * 系统功能
108 * 基础设施 108 * 基础设施
docker-compose.yml
1 -version: "3.8" 1 +version: "3.4"
2 2
3 name: yudao-system 3 name: yudao-system
4 4
@@ -9,25 +9,22 @@ services: @@ -9,25 +9,22 @@ services:
9 restart: unless-stopped 9 restart: unless-stopped
10 tty: true 10 tty: true
11 ports: 11 ports:
12 - - 13306:3306 12 + - "3306:3306"
13 environment: 13 environment:
14 MYSQL_DATABASE: ${MYSQL_DATABASE:-ruoyi-vue-pro} 14 MYSQL_DATABASE: ${MYSQL_DATABASE:-ruoyi-vue-pro}
15 MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-123456} 15 MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-123456}
16 volumes: 16 volumes:
17 - mysql:/var/lib/mysql/ 17 - mysql:/var/lib/mysql/
18 - networks:  
19 - - yudao-network 18 + - ./sql/mysql/ruoyi-vue-pro.sql:/docker-entrypoint-initdb.d/ruoyi-vue-pro.sql:ro
20 19
21 redis: 20 redis:
22 container_name: yudao-redis 21 container_name: yudao-redis
23 image: redis:6-alpine 22 image: redis:6-alpine
24 restart: unless-stopped 23 restart: unless-stopped
25 ports: 24 ports:
26 - - 16379:6379 25 + - "6379:6379"
27 volumes: 26 volumes:
28 - redis:/data 27 - redis:/data
29 - networks:  
30 - - yudao-network  
31 28
32 server: 29 server:
33 container_name: yudao-server 30 container_name: yudao-server
@@ -36,7 +33,7 @@ services: @@ -36,7 +33,7 @@ services:
36 image: yudao-server 33 image: yudao-server
37 restart: unless-stopped 34 restart: unless-stopped
38 ports: 35 ports:
39 - - 48080:48080 36 + - "48080:48080"
40 environment: 37 environment:
41 # https://github.com/polovyivan/docker-pass-configs-to-container 38 # https://github.com/polovyivan/docker-pass-configs-to-container
42 SPRING_PROFILES_ACTIVE: local 39 SPRING_PROFILES_ACTIVE: local
@@ -54,8 +51,6 @@ services: @@ -54,8 +51,6 @@ services:
54 --spring.datasource.dynamic.datasource.slave.username=${SLAVE_DATASOURCE_USERNAME:-root} 51 --spring.datasource.dynamic.datasource.slave.username=${SLAVE_DATASOURCE_USERNAME:-root}
55 --spring.datasource.dynamic.datasource.slave.password=${SLAVE_DATASOURCE_PASSWORD:-123456} 52 --spring.datasource.dynamic.datasource.slave.password=${SLAVE_DATASOURCE_PASSWORD:-123456}
56 --spring.redis.host=${REDIS_HOST:-yudao-redis} 53 --spring.redis.host=${REDIS_HOST:-yudao-redis}
57 - networks:  
58 - - yudao-network  
59 depends_on: 54 depends_on:
60 - mysql 55 - mysql
61 - redis 56 - redis
@@ -78,16 +73,10 @@ services: @@ -78,16 +73,10 @@ services:
78 image: yudao-admin 73 image: yudao-admin
79 restart: unless-stopped 74 restart: unless-stopped
80 ports: 75 ports:
81 - - 8080:80  
82 - networks:  
83 - - yudao-network 76 + - "8080:80"
84 depends_on: 77 depends_on:
85 - server 78 - server
86 79
87 -networks:  
88 - yudao-network:  
89 - driver: bridge  
90 -  
91 volumes: 80 volumes:
92 mysql: 81 mysql:
93 driver: local 82 driver: local
@@ -30,7 +30,7 @@ @@ -30,7 +30,7 @@
30 <url>https://github.com/YunaiV/ruoyi-vue-pro</url> 30 <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
31 31
32 <properties> 32 <properties>
33 - <revision>1.7.3-snapshot</revision> 33 + <revision>1.8.0-snapshot</revision>
34 <!-- Maven 相关 --> 34 <!-- Maven 相关 -->
35 <java.version>1.8</java.version> 35 <java.version>1.8</java.version>
36 <maven.compiler.source>${java.version}</maven.compiler.source> 36 <maven.compiler.source>${java.version}</maven.compiler.source>
yudao-dependencies/pom.xml
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 <url>https://github.com/YunaiV/ruoyi-vue-pro</url> 14 <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
15 15
16 <properties> 16 <properties>
17 - <revision>1.7.3-snapshot</revision> 17 + <revision>1.8.0-snapshot</revision>
18 <!-- 统一依赖管理 --> 18 <!-- 统一依赖管理 -->
19 <spring.boot.version>2.7.13</spring.boot.version> 19 <spring.boot.version>2.7.13</spring.boot.version>
20 <!-- Web 相关 --> 20 <!-- Web 相关 -->
@@ -71,7 +71,7 @@ @@ -71,7 +71,7 @@
71 <justauth.version>1.0.1</justauth.version> 71 <justauth.version>1.0.1</justauth.version>
72 <jimureport.version>1.5.8</jimureport.version> 72 <jimureport.version>1.5.8</jimureport.version>
73 <xercesImpl.version>2.12.2</xercesImpl.version> 73 <xercesImpl.version>2.12.2</xercesImpl.version>
74 - <wx-java-mp.version>4.5.0</wx-java-mp.version> 74 + <weixin-java.version>4.5.0</weixin-java.version>
75 </properties> 75 </properties>
76 76
77 <dependencyManagement> 77 <dependencyManagement>
@@ -216,10 +216,9 @@ @@ -216,10 +216,9 @@
216 <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 --> 216 <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 -->
217 <version>${dynamic-datasource.version}</version> 217 <version>${dynamic-datasource.version}</version>
218 </dependency> 218 </dependency>
219 -  
220 <dependency> 219 <dependency>
221 <groupId>com.github.yulichang</groupId> 220 <groupId>com.github.yulichang</groupId>
222 - <artifactId>mybatis-plus-join-boot-starter</artifactId> 221 + <artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->
223 <version>${mybatis-plus-join-boot-starter.version}</version> 222 <version>${mybatis-plus-join-boot-starter.version}</version>
224 </dependency> 223 </dependency>
225 224
@@ -601,8 +600,23 @@ @@ -601,8 +600,23 @@
601 600
602 <dependency> 601 <dependency>
603 <groupId>com.github.binarywang</groupId> 602 <groupId>com.github.binarywang</groupId>
  603 + <artifactId>weixin-java-pay</artifactId>
  604 + <version>${weixin-java.version}</version>
  605 + </dependency>
  606 + <dependency>
  607 + <groupId>com.github.binarywang</groupId>
  608 + <artifactId>weixin-java-mp</artifactId>
  609 + <version>${weixin-java.version}</version>
  610 + </dependency>
  611 + <dependency>
  612 + <groupId>com.github.binarywang</groupId>
604 <artifactId>wx-java-mp-spring-boot-starter</artifactId> 613 <artifactId>wx-java-mp-spring-boot-starter</artifactId>
605 - <version>${wx-java-mp.version}</version> 614 + <version>${weixin-java.version}</version>
  615 + </dependency>
  616 + <dependency>
  617 + <groupId>com.github.binarywang</groupId>
  618 + <artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
  619 + <version>${weixin-java.version}</version>
606 </dependency> 620 </dependency>
607 621
608 <!-- 积木报表--> 622 <!-- 积木报表-->
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
@@ -164,7 +164,7 @@ public class CollectionUtils { @@ -164,7 +164,7 @@ public class CollectionUtils {
164 return from.stream().filter(predicate).findFirst().orElse(null); 164 return from.stream().filter(predicate).findFirst().orElse(null);
165 } 165 }
166 166
167 - public static <T, V extends Comparable<? super V>> V getMaxValue(List<T> from, Function<T, V> valueFunc) { 167 + public static <T, V extends Comparable<? super V>> V getMaxValue(Collection<T> from, Function<T, V> valueFunc) {
168 if (CollUtil.isEmpty(from)) { 168 if (CollUtil.isEmpty(from)) {
169 return null; 169 return null;
170 } 170 }
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRuleTest.java
@@ -4,11 +4,11 @@ import cn.hutool.core.collection.CollUtil; @@ -4,11 +4,11 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ReflectUtil; 4 import cn.hutool.core.util.ReflectUtil;
5 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 5 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
6 import cn.iocoder.yudao.framework.common.util.collection.SetUtils; 6 import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
7 -import cn.iocoder.yudao.module.system.api.permission.PermissionApi;  
8 -import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;  
9 import cn.iocoder.yudao.framework.security.core.LoginUser; 7 import cn.iocoder.yudao.framework.security.core.LoginUser;
10 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; 8 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
11 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; 9 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  10 +import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
  11 +import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
12 import net.sf.jsqlparser.expression.Alias; 12 import net.sf.jsqlparser.expression.Alias;
13 import net.sf.jsqlparser.expression.Expression; 13 import net.sf.jsqlparser.expression.Expression;
14 import org.junit.jupiter.api.BeforeEach; 14 import org.junit.jupiter.api.BeforeEach;
@@ -23,6 +23,7 @@ import static cn.iocoder.yudao.framework.datapermission.core.rule.dept.DeptDataP @@ -23,6 +23,7 @@ import static cn.iocoder.yudao.framework.datapermission.core.rule.dept.DeptDataP
23 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; 23 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
24 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; 24 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
25 import static org.junit.jupiter.api.Assertions.*; 25 import static org.junit.jupiter.api.Assertions.*;
  26 +import static org.mockito.ArgumentMatchers.eq;
26 import static org.mockito.ArgumentMatchers.same; 27 import static org.mockito.ArgumentMatchers.same;
27 import static org.mockito.Mockito.mockStatic; 28 import static org.mockito.Mockito.mockStatic;
28 import static org.mockito.Mockito.when; 29 import static org.mockito.Mockito.when;
@@ -73,6 +74,8 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { @@ -73,6 +74,8 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
73 LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) 74 LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
74 .setUserType(UserTypeEnum.ADMIN.getValue())); 75 .setUserType(UserTypeEnum.ADMIN.getValue()));
75 securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); 76 securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);
  77 + // mock 方法(permissionApi 返回 null)
  78 + when(permissionApi.getDeptDataPermission(eq(loginUser.getId()))).thenReturn(null);
76 79
77 // 调用 80 // 调用
78 NullPointerException exception = assertThrows(NullPointerException.class, 81 NullPointerException exception = assertThrows(NullPointerException.class,
yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml
@@ -63,9 +63,7 @@ @@ -63,9 +63,7 @@
63 <dependency> 63 <dependency>
64 <groupId>com.github.binarywang</groupId> 64 <groupId>com.github.binarywang</groupId>
65 <artifactId>weixin-java-pay</artifactId> 65 <artifactId>weixin-java-pay</artifactId>
66 - <version>4.5.0</version>  
67 </dependency> 66 </dependency>
68 - <!-- TODO 芋艿:清理 -->  
69 67
70 <!-- Test 测试相关 --> 68 <!-- Test 测试相关 -->
71 <dependency> 69 <dependency>
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-biz-weixin/pom.xml
@@ -34,16 +34,12 @@ @@ -34,16 +34,12 @@
34 <!-- 三方云服务相关 --> 34 <!-- 三方云服务相关 -->
35 <dependency> 35 <dependency>
36 <groupId>com.github.binarywang</groupId> 36 <groupId>com.github.binarywang</groupId>
37 -<!-- <artifactId>weixin-java-mp</artifactId>-->  
38 <artifactId>wx-java-mp-spring-boot-starter</artifactId> 37 <artifactId>wx-java-mp-spring-boot-starter</artifactId>
39 - <version>4.5.0</version>  
40 </dependency> 38 </dependency>
41 <dependency> 39 <dependency>
42 <groupId>com.github.binarywang</groupId> 40 <groupId>com.github.binarywang</groupId>
43 <artifactId>wx-java-miniapp-spring-boot-starter</artifactId> 41 <artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
44 - <version>4.5.0</version>  
45 </dependency> 42 </dependency>
46 - <!-- TODO 芋艿:清理 -->  
47 </dependencies> 43 </dependencies>
48 44
49 </project> 45 </project>
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/pom.xml
@@ -64,9 +64,8 @@ @@ -64,9 +64,8 @@
64 64
65 <dependency> 65 <dependency>
66 <groupId>com.github.yulichang</groupId> 66 <groupId>com.github.yulichang</groupId>
67 - <artifactId>mybatis-plus-join-boot-starter</artifactId> 67 + <artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->
68 </dependency> 68 </dependency>
69 -  
70 </dependencies> 69 </dependencies>
71 70
72 </project> 71 </project>
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
@@ -6,10 +6,10 @@ import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; @@ -6,10 +6,10 @@ import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
6 import com.baomidou.mybatisplus.core.conditions.Wrapper; 6 import com.baomidou.mybatisplus.core.conditions.Wrapper;
7 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 7 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
8 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 8 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  9 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
9 import com.baomidou.mybatisplus.core.metadata.IPage; 10 import com.baomidou.mybatisplus.core.metadata.IPage;
10 import com.baomidou.mybatisplus.core.toolkit.support.SFunction; 11 import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
11 import com.baomidou.mybatisplus.extension.toolkit.Db; 12 import com.baomidou.mybatisplus.extension.toolkit.Db;
12 -import com.github.yulichang.base.MPJBaseMapper;  
13 import org.apache.ibatis.annotations.Param; 13 import org.apache.ibatis.annotations.Param;
14 14
15 import java.util.Collection; 15 import java.util.Collection;
@@ -17,10 +17,8 @@ import java.util.List; @@ -17,10 +17,8 @@ import java.util.List;
17 17
18 /** 18 /**
19 * 在 MyBatis Plus 的 BaseMapper 的基础上拓展,提供更多的能力 19 * 在 MyBatis Plus 的 BaseMapper 的基础上拓展,提供更多的能力
20 - * <p>  
21 - * 为什么继承 MPJBaseMapper 接口?支持 MyBatis Plus 多表 Join 的能力。  
22 */ 20 */
23 -public interface BaseMapperX<T> extends MPJBaseMapper<T> { 21 +public interface BaseMapperX<T> extends BaseMapper<T> {
24 22
25 default PageResult<T> selectPage(PageParam pageParam, @Param("ew") Wrapper<T> queryWrapper) { 23 default PageResult<T> selectPage(PageParam pageParam, @Param("ew") Wrapper<T> queryWrapper) {
26 // MyBatis Plus 查询 24 // MyBatis Plus 查询
@@ -46,18 +44,6 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; { @@ -46,18 +44,6 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; {
46 return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)); 44 return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));
47 } 45 }
48 46
49 - default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,  
50 - SFunction<T, ?> field3, Object value3) {  
51 - return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)  
52 - .eq(field3, value3));  
53 - }  
54 -  
55 - default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,  
56 - SFunction<T, ?> field3, Object value3, SFunction<T, ?> field4, Object value4) {  
57 - return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)  
58 - .eq(field3, value3).eq(field4, value4));  
59 - }  
60 -  
61 default Long selectCount() { 47 default Long selectCount() {
62 return selectCount(new QueryWrapper<T>()); 48 return selectCount(new QueryWrapper<T>());
63 } 49 }
@@ -117,11 +103,6 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; { @@ -117,11 +103,6 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; {
117 update(update, new QueryWrapper<>()); 103 update(update, new QueryWrapper<>());
118 } 104 }
119 105
120 - /**  
121 - * 根据ID 批量更新,适合大量数据更新  
122 - *  
123 - * @param entities 实体们  
124 - */  
125 default void updateBatch(Collection<T> entities) { 106 default void updateBatch(Collection<T> entities) {
126 Db.updateBatchById(entities); 107 Db.updateBatchById(entities);
127 } 108 }
@@ -130,13 +111,8 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; { @@ -130,13 +111,8 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; {
130 Db.updateBatchById(entities, size); 111 Db.updateBatchById(entities, size);
131 } 112 }
132 113
133 - /**  
134 - * 批量修改插入, 会根据实体的主键是否为空,更新还是修改。默认为 1000  
135 - *  
136 - * @param entities 实体们  
137 - */  
138 - default void saveOrUpdateBatch(Collection<T> entities){  
139 - Db.saveOrUpdateBatch(entities); 114 + default void saveOrUpdateBatch(Collection<T> collection) {
  115 + Db.saveOrUpdateBatch(collection);
140 } 116 }
141 117
142 } 118 }
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-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
@@ -60,7 +60,7 @@ public class YudaoWebSecurityConfigurerAdapter { @@ -60,7 +60,7 @@ public class YudaoWebSecurityConfigurerAdapter {
60 /** 60 /**
61 * 自定义的权限映射 Bean 们 61 * 自定义的权限映射 Bean 们
62 * 62 *
63 - * @see #configure(HttpSecurity) 63 + * @see #filterChain(HttpSecurity)
64 */ 64 */
65 @Resource 65 @Resource
66 private List<AuthorizeRequestsCustomizer> authorizeRequestsCustomizers; 66 private List<AuthorizeRequestsCustomizer> authorizeRequestsCustomizers;
@@ -79,7 +79,7 @@ public class YudaoWebSecurityConfigurerAdapter { @@ -79,7 +79,7 @@ public class YudaoWebSecurityConfigurerAdapter {
79 79
80 /** 80 /**
81 * 配置 URL 的安全配置 81 * 配置 URL 的安全配置
82 - * <p> 82 + *
83 * anyRequest | 匹配所有请求路径 83 * anyRequest | 匹配所有请求路径
84 * access | SpringEl表达式结果为true时可以访问 84 * access | SpringEl表达式结果为true时可以访问
85 * anonymous | 匿名可以访问 85 * anonymous | 匿名可以访问
@@ -141,7 +141,6 @@ public class YudaoWebSecurityConfigurerAdapter { @@ -141,7 +141,6 @@ public class YudaoWebSecurityConfigurerAdapter {
141 141
142 // 添加 Token Filter 142 // 添加 Token Filter
143 httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); 143 httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
144 -  
145 return httpSecurity.build(); 144 return httpSecurity.build();
146 } 145 }
147 146
yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbAndRedisUnitTest.java
@@ -40,6 +40,7 @@ public class BaseDbAndRedisUnitTest { @@ -40,6 +40,7 @@ public class BaseDbAndRedisUnitTest {
40 40
41 // Redis 配置类 41 // Redis 配置类
42 RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer 42 RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
  43 +// RedisAutoConfiguration.class, // Spring Redis 自动配置类
43 YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类 44 YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
44 RedissonAutoConfiguration.class, // Redisson 自动高配置类 45 RedissonAutoConfiguration.class, // Redisson 自动高配置类
45 }) 46 })
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java
@@ -52,7 +52,8 @@ public class YudaoSwaggerAutoConfiguration { @@ -52,7 +52,8 @@ public class YudaoSwaggerAutoConfiguration {
52 // 接口信息 52 // 接口信息
53 .info(buildInfo(properties)) 53 .info(buildInfo(properties))
54 // 接口安全配置 54 // 接口安全配置
55 - .components(new Components().securitySchemes(securitySchemas)); 55 + .components(new Components().securitySchemes(securitySchemas))
  56 + .addSecurityItem(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION));
56 securitySchemas.keySet().forEach(key -> openAPI.addSecurityItem(new SecurityRequirement().addList(key))); 57 securitySchemas.keySet().forEach(key -> openAPI.addSecurityItem(new SecurityRequirement().addList(key)));
57 return openAPI; 58 return openAPI;
58 } 59 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
@@ -47,7 +47,7 @@ public class BpmTaskController { @@ -47,7 +47,7 @@ public class BpmTaskController {
47 @Parameter(name = "processInstanceId", description = "流程实例的编号", required = true) 47 @Parameter(name = "processInstanceId", description = "流程实例的编号", required = true)
48 @PreAuthorize("@ss.hasPermission('bpm:task:query')") 48 @PreAuthorize("@ss.hasPermission('bpm:task:query')")
49 public CommonResult<List<BpmTaskRespVO>> getTaskListByProcessInstanceId( 49 public CommonResult<List<BpmTaskRespVO>> getTaskListByProcessInstanceId(
50 - @RequestParam("processInstanceId") String processInstanceId) { 50 + @RequestParam("processInstanceId") String processInstanceId) {
51 return success(taskService.getTaskListByProcessInstanceId(processInstanceId)); 51 return success(taskService.getTaskListByProcessInstanceId(processInstanceId));
52 } 52 }
53 53
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
@@ -73,11 +73,13 @@ public interface BpmTaskConvert { @@ -73,11 +73,13 @@ public interface BpmTaskConvert {
73 73
74 BpmTaskDonePageItemRespVO convert2(HistoricTaskInstance bean); 74 BpmTaskDonePageItemRespVO convert2(HistoricTaskInstance bean);
75 75
76 - @Mappings({@Mapping(source = "processInstance.id", target = "id"),  
77 - @Mapping(source = "processInstance.name", target = "name"),  
78 - @Mapping(source = "processInstance.startUserId", target = "startUserId"),  
79 - @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"),  
80 - @Mapping(source = "startUser.nickname", target = "startUserNickname")}) 76 + @Mappings({
  77 + @Mapping(source = "processInstance.id", target = "id"),
  78 + @Mapping(source = "processInstance.name", target = "name"),
  79 + @Mapping(source = "processInstance.startUserId", target = "startUserId"),
  80 + @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"),
  81 + @Mapping(source = "startUser.nickname", target = "startUserNickname")
  82 + })
81 BpmTaskTodoPageItemRespVO.ProcessInstance convert(ProcessInstance processInstance, AdminUserRespDTO startUser); 83 BpmTaskTodoPageItemRespVO.ProcessInstance convert(ProcessInstance processInstance, AdminUserRespDTO startUser);
82 84
83 default List<BpmTaskRespVO> convertList3(List<HistoricTaskInstance> tasks, 85 default List<BpmTaskRespVO> convertList3(List<HistoricTaskInstance> tasks,
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java
@@ -15,8 +15,8 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmTaskAssignRuleConvert; @@ -15,8 +15,8 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmTaskAssignRuleConvert;
15 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO; 15 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
16 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO; 16 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
17 import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper; 17 import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper;
18 -import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;  
19 import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants; 18 import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants;
  19 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
20 import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript; 20 import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
21 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 21 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
22 import cn.iocoder.yudao.module.system.api.dept.PostApi; 22 import cn.iocoder.yudao.module.system.api.dept.PostApi;
@@ -89,7 +89,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { @@ -89,7 +89,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
89 89
90 @Override 90 @Override
91 public List<BpmTaskAssignRuleDO> getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId, 91 public List<BpmTaskAssignRuleDO> getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId,
92 - String taskDefinitionKey) { 92 + String taskDefinitionKey) {
93 return taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, taskDefinitionKey); 93 return taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, taskDefinitionKey);
94 } 94 }
95 95
@@ -128,14 +128,14 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { @@ -128,14 +128,14 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
128 validTaskAssignRuleOptions(reqVO.getType(), reqVO.getOptions()); 128 validTaskAssignRuleOptions(reqVO.getType(), reqVO.getOptions());
129 // 校验是否已经配置 129 // 校验是否已经配置
130 BpmTaskAssignRuleDO existRule = 130 BpmTaskAssignRuleDO existRule =
131 - taskRuleMapper.selectListByModelIdAndTaskDefinitionKey(reqVO.getModelId(), reqVO.getTaskDefinitionKey()); 131 + taskRuleMapper.selectListByModelIdAndTaskDefinitionKey(reqVO.getModelId(), reqVO.getTaskDefinitionKey());
132 if (existRule != null) { 132 if (existRule != null) {
133 throw exception(TASK_ASSIGN_RULE_EXISTS, reqVO.getModelId(), reqVO.getTaskDefinitionKey()); 133 throw exception(TASK_ASSIGN_RULE_EXISTS, reqVO.getModelId(), reqVO.getTaskDefinitionKey());
134 } 134 }
135 135
136 // 存储 136 // 存储
137 BpmTaskAssignRuleDO rule = BpmTaskAssignRuleConvert.INSTANCE.convert(reqVO) 137 BpmTaskAssignRuleDO rule = BpmTaskAssignRuleConvert.INSTANCE.convert(reqVO)
138 - .setProcessDefinitionId(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL); // 只有流程模型,才允许新建 138 + .setProcessDefinitionId(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL); // 只有流程模型,才允许新建
139 taskRuleMapper.insert(rule); 139 taskRuleMapper.insert(rule);
140 return rule.getId(); 140 return rule.getId();
141 } 141 }
@@ -169,14 +169,14 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { @@ -169,14 +169,14 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
169 169
170 // 遍历,匹配对应的规则 170 // 遍历,匹配对应的规则
171 Map<String, BpmTaskAssignRuleRespVO> processInstanceRuleMap = 171 Map<String, BpmTaskAssignRuleRespVO> processInstanceRuleMap =
172 - CollectionUtils.convertMap(processInstanceRules, BpmTaskAssignRuleRespVO::getTaskDefinitionKey); 172 + CollectionUtils.convertMap(processInstanceRules, BpmTaskAssignRuleRespVO::getTaskDefinitionKey);
173 for (BpmTaskAssignRuleRespVO modelRule : modelRules) { 173 for (BpmTaskAssignRuleRespVO modelRule : modelRules) {
174 BpmTaskAssignRuleRespVO processInstanceRule = processInstanceRuleMap.get(modelRule.getTaskDefinitionKey()); 174 BpmTaskAssignRuleRespVO processInstanceRule = processInstanceRuleMap.get(modelRule.getTaskDefinitionKey());
175 if (processInstanceRule == null) { 175 if (processInstanceRule == null) {
176 return false; 176 return false;
177 } 177 }
178 if (!ObjectUtil.equals(modelRule.getType(), processInstanceRule.getType()) || !ObjectUtil.equal( 178 if (!ObjectUtil.equals(modelRule.getType(), processInstanceRule.getType()) || !ObjectUtil.equal(
179 - modelRule.getOptions(), processInstanceRule.getOptions())) { 179 + modelRule.getOptions(), processInstanceRule.getOptions())) {
180 return false; 180 return false;
181 } 181 }
182 } 182 }
@@ -192,7 +192,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { @@ -192,7 +192,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
192 // 开始复制 192 // 开始复制
193 List<BpmTaskAssignRuleDO> newRules = BpmTaskAssignRuleConvert.INSTANCE.convertList2(rules); 193 List<BpmTaskAssignRuleDO> newRules = BpmTaskAssignRuleConvert.INSTANCE.convertList2(rules);
194 newRules.forEach(rule -> rule.setProcessDefinitionId(toProcessDefinitionId).setId(null).setCreateTime(null) 194 newRules.forEach(rule -> rule.setProcessDefinitionId(toProcessDefinitionId).setId(null).setCreateTime(null)
195 - .setUpdateTime(null)); 195 + .setUpdateTime(null));
196 taskRuleMapper.insertBatch(newRules); 196 taskRuleMapper.insertBatch(newRules);
197 } 197 }
198 198
@@ -215,7 +215,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { @@ -215,7 +215,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
215 if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.ROLE.getType())) { 215 if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.ROLE.getType())) {
216 roleApi.validRoleList(options); 216 roleApi.validRoleList(options);
217 } else if (ObjectUtils.equalsAny(type, BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), 217 } else if (ObjectUtils.equalsAny(type, BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(),
218 - BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType())) { 218 + BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType())) {
219 deptApi.validateDeptList(options); 219 deptApi.validateDeptList(options);
220 } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.POST.getType())) { 220 } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.POST.getType())) {
221 postApi.validPostList(options); 221 postApi.validPostList(options);
@@ -225,7 +225,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { @@ -225,7 +225,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
225 userGroupService.validUserGroups(options); 225 userGroupService.validUserGroups(options);
226 } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.SCRIPT.getType())) { 226 } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.SCRIPT.getType())) {
227 dictDataApi.validateDictDataList(DictTypeConstants.TASK_ASSIGN_SCRIPT, 227 dictDataApi.validateDictDataList(DictTypeConstants.TASK_ASSIGN_SCRIPT,
228 - CollectionUtils.convertSet(options, String::valueOf)); 228 + CollectionUtils.convertSet(options, String::valueOf));
229 } else { 229 } else {
230 throw new IllegalArgumentException(format("未知的规则类型({})", type)); 230 throw new IllegalArgumentException(format("未知的规则类型({})", type));
231 } 231 }
@@ -298,7 +298,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { @@ -298,7 +298,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
298 } 298 }
299 299
300 private Set<Long> calculateTaskCandidateUsersByPost(BpmTaskAssignRuleDO rule) { 300 private Set<Long> calculateTaskCandidateUsersByPost(BpmTaskAssignRuleDO rule) {
301 - List<AdminUserRespDTO> users = adminUserApi.getUsersByPostIds(rule.getOptions()); 301 + List<AdminUserRespDTO> users = adminUserApi.getUserListByPostIds(rule.getOptions());
302 return convertSet(users, AdminUserRespDTO::getId); 302 return convertSet(users, AdminUserRespDTO::getId);
303 } 303 }
304 304
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
1 -package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO; import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import javax.validation.Valid; import java.time.LocalDateTime; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private BpmProcessInstanceExtMapper processInstanceExtMapper; @Resource @Lazy // 解决循环依赖 private BpmTaskService taskService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private HistoryService historyService; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @Resource private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private BpmMessageService messageService; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 PageResult<BpmProcessInstanceExtDO> pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return new PageResult<>(pageResult.getTotal()); } // 获得流程 Task Map List<String> processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId); Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds); // 转换返回 return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey()); } @Override public BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id); Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id); // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt( processInstance.getProcessDefinitionId()); Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id); String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); // 获得 User AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())); DeptRespDTO dept = null; if (startUser != null) { dept = deptApi.getDept(startUser.getDeptId()); } // 拼接结果 return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @Override public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询 deleteProcessInstance(cancelReqVO.getId(), BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason())); } /** * 获得历史的流程实例 * * @param id 流程实例的编号 * @return 历史的流程实例 */ @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public void createProcessInstanceExt(ProcessInstance instance) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId()); // 插入 BpmProcessInstanceExtDO 对象 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getId()) .setProcessDefinitionId(definition.getId()) .setName(instance.getProcessDefinitionName()) .setStartUserId(Long.valueOf(instance.getStartUserId())) .setCategory(definition.getCategory()) .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus()) .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); processInstanceExtMapper.insert(instanceExtDO); } @Override public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) { // 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceExtReject 方法,已经进行更新了 if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String)event.getCause())) { return; } // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(event.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override public void updateProcessInstanceExtComplete(ProcessInstance instance) { // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过 processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被通过的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceExtReject(String id, String reason) { // 需要主动查询,因为 instance 只有 id 属性 ProcessInstance processInstance = getProcessInstance(id); // 删除流程实例,以实现驳回任务时,取消整个审批流程 deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason))); // 更新 status + result // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法, // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id) .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被不通过的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 创建流程实例 ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); // 设置流程名字 runtimeService.setProcessInstanceName(instance.getId(), definition.getName()); // 补全流程实例的拓展表 processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId()) .setFormVariables(variables)); return instance.getId(); } }  
2 \ No newline at end of file 1 \ No newline at end of file
  2 +package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO; import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import javax.validation.Valid; import java.time.LocalDateTime; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private BpmProcessInstanceExtMapper processInstanceExtMapper; @Resource @Lazy // 解决循环依赖 private BpmTaskService taskService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private HistoryService historyService; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @Resource private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private BpmMessageService messageService; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 PageResult<BpmProcessInstanceExtDO> pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return new PageResult<>(pageResult.getTotal()); } // 获得流程 Task Map List<String> processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId); Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds); // 转换返回 return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey()); } @Override public BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id); Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id); // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt( processInstance.getProcessDefinitionId()); Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id); String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); // 获得 User AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())); DeptRespDTO dept = null; if (startUser != null) { dept = deptApi.getDept(startUser.getDeptId()); } // 拼接结果 return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @Override public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询 deleteProcessInstance(cancelReqVO.getId(), BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason())); } /** * 获得历史的流程实例 * * @param id 流程实例的编号 * @return 历史的流程实例 */ @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public void createProcessInstanceExt(ProcessInstance instance) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId()); // 插入 BpmProcessInstanceExtDO 对象 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getId()) .setProcessDefinitionId(definition.getId()) .setName(instance.getProcessDefinitionName()) .setStartUserId(Long.valueOf(instance.getStartUserId())) .setCategory(definition.getCategory()) .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus()) .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); processInstanceExtMapper.insert(instanceExtDO); } @Override public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) { // 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceExtReject 方法,已经进行更新了 if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String)event.getCause())) { return; } // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(event.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override public void updateProcessInstanceExtComplete(ProcessInstance instance) { // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过 processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被通过的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceExtReject(String id, String reason) { // 需要主动查询,因为 instance 只有 id 属性 ProcessInstance processInstance = getProcessInstance(id); // 删除流程实例,以实现驳回任务时,取消整个审批流程 deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason))); // 更新 status + result // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法, // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id) .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被不通过的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 创建流程实例 ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); // 设置流程名字 runtimeService.setProcessInstanceName(instance.getId(), definition.getName()); // 补全流程实例的拓展表 processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId()) .setFormVariables(variables)); return instance.getId(); } }
3 \ No newline at end of file 3 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2ScriptTest.java
@@ -40,6 +40,8 @@ public class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest { @@ -40,6 +40,8 @@ public class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest {
40 // mock 方法(startUser) 40 // mock 方法(startUser)
41 AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptId(10L)); 41 AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptId(10L));
42 when(adminUserApi.getUser(eq(1L))).thenReturn(startUser); 42 when(adminUserApi.getUser(eq(1L))).thenReturn(startUser);
  43 + // mock 方法(getStartUserDept)没有部门
  44 + when(deptApi.getDept(eq(10L))).thenReturn(null);
43 45
44 // 调用 46 // 调用
45 Set<Long> result = script.calculateTaskCandidateUsers(execution); 47 Set<Long> result = script.calculateTaskCandidateUsers(execution);
@@ -56,7 +58,9 @@ public class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest { @@ -56,7 +58,9 @@ public class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest {
56 when(adminUserApi.getUser(eq(1L))).thenReturn(startUser); 58 when(adminUserApi.getUser(eq(1L))).thenReturn(startUser);
57 DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L) 59 DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L)
58 .setLeaderUserId(20L)); 60 .setLeaderUserId(20L));
  61 + // mock 方法(getDept)
59 when(deptApi.getDept(eq(10L))).thenReturn(startUserDept); 62 when(deptApi.getDept(eq(10L))).thenReturn(startUserDept);
  63 + when(deptApi.getDept(eq(100L))).thenReturn(null);
60 64
61 // 调用 65 // 调用
62 Set<Long> result = script.calculateTaskCandidateUsers(execution); 66 Set<Long> result = script.calculateTaskCandidateUsers(execution);
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImplTest.java
@@ -120,7 +120,7 @@ public class BpmTaskAssignRuleServiceImplTest extends BaseDbUnitTest { @@ -120,7 +120,7 @@ public class BpmTaskAssignRuleServiceImplTest extends BaseDbUnitTest {
120 // mock 方法 120 // mock 方法
121 List<AdminUserRespDTO> users = CollectionUtils.convertList(asSet(11L, 22L), 121 List<AdminUserRespDTO> users = CollectionUtils.convertList(asSet(11L, 22L),
122 id -> new AdminUserRespDTO().setId(id)); 122 id -> new AdminUserRespDTO().setId(id));
123 - when(adminUserApi.getUsersByPostIds(eq(rule.getOptions()))).thenReturn(users); 123 + when(adminUserApi.getUserListByPostIds(eq(rule.getOptions()))).thenReturn(users);
124 mockGetUserMap(asSet(11L, 22L)); 124 mockGetUserMap(asSet(11L, 22L));
125 125
126 // 调用 126 // 调用
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupServiceTest.java
@@ -23,10 +23,10 @@ import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgn @@ -23,10 +23,10 @@ import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgn
23 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.USER_GROUP_NOT_EXISTS; 23 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.USER_GROUP_NOT_EXISTS;
24 24
25 /** 25 /**
26 -* {@link BpmUserGroupServiceImpl} 的单元测试类  
27 -*  
28 -* @author 芋道源码  
29 -*/ 26 + * {@link BpmUserGroupServiceImpl} 的单元测试类
  27 + *
  28 + * @author 芋道源码
  29 + */
30 @Import(BpmUserGroupServiceImpl.class) 30 @Import(BpmUserGroupServiceImpl.class)
31 public class BpmUserGroupServiceTest extends BaseDbUnitTest { 31 public class BpmUserGroupServiceTest extends BaseDbUnitTest {
32 32
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-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartMapper.java
@@ -19,10 +19,10 @@ public interface TradeCartMapper extends BaseMapperX&lt;TradeCartDO&gt; { @@ -19,10 +19,10 @@ public interface TradeCartMapper extends BaseMapperX&lt;TradeCartDO&gt; {
19 19
20 default TradeCartDO selectByUserIdAndSkuId(Long userId, Long skuId, 20 default TradeCartDO selectByUserIdAndSkuId(Long userId, Long skuId,
21 Boolean addStatus, Boolean orderStatus) { 21 Boolean addStatus, Boolean orderStatus) {
22 - return selectOne(TradeCartDO::getUserId, userId,  
23 - TradeCartDO::getSkuId, skuId,  
24 - TradeCartDO::getAddStatus, addStatus,  
25 - TradeCartDO::getOrderStatus, orderStatus); 22 + return selectOne(new LambdaQueryWrapper<TradeCartDO>().eq(TradeCartDO::getUserId, userId)
  23 + .eq(TradeCartDO::getSkuId, skuId)
  24 + .eq(TradeCartDO::getAddStatus, addStatus)
  25 + .eq(TradeCartDO::getOrderStatus, orderStatus));
26 } 26 }
27 27
28 default Integer selectSumByUserId(Long userId) { 28 default Integer selectSumByUserId(Long userId) {
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/account/MpAccountMapper.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.mp.controller.admin.account.vo.MpAccountPageReqVO; 6 import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountPageReqVO;
7 import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; 7 import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
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 MpAccountMapper extends BaseMapperX<MpAccountDO> { 14 public interface MpAccountMapper extends BaseMapperX<MpAccountDO> {
@@ -22,4 +25,7 @@ public interface MpAccountMapper extends BaseMapperX&lt;MpAccountDO&gt; { @@ -22,4 +25,7 @@ public interface MpAccountMapper extends BaseMapperX&lt;MpAccountDO&gt; {
22 return selectOne(MpAccountDO::getAppId, appId); 25 return selectOne(MpAccountDO::getAppId, appId);
23 } 26 }
24 27
  28 + @Select("SELECT COUNT(*) FROM mp_account WHERE update_time > #{maxUpdateTime}")
  29 + Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
  30 +
25 } 31 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/consumer/MpAccountRefreshConsumer.java deleted
1 -package cn.iocoder.yudao.module.mp.mq.consumer;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;  
4 -import cn.iocoder.yudao.module.mp.mq.message.MpAccountRefreshMessage;  
5 -import cn.iocoder.yudao.module.mp.service.account.MpAccountService;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import org.springframework.stereotype.Component;  
8 -  
9 -import javax.annotation.Resource;  
10 -  
11 -/**  
12 - * 针对 {@link MpAccountRefreshMessage} 的消费者  
13 - *  
14 - * @author 芋道源码  
15 - */  
16 -@Component  
17 -@Slf4j  
18 -public class MpAccountRefreshConsumer extends AbstractChannelMessageListener<MpAccountRefreshMessage> {  
19 -  
20 - @Resource  
21 - private MpAccountService mpAccountService;  
22 -  
23 - @Override  
24 - public void onMessage(MpAccountRefreshMessage message) {  
25 - log.info("[onMessage][收到 Account 刷新消息]");  
26 - mpAccountService.initLocalCache();  
27 - }  
28 -  
29 -}  
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/message/MpAccountRefreshMessage.java deleted
1 -package cn.iocoder.yudao.module.mp.mq.message;  
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 MpAccountRefreshMessage extends AbstractChannelMessage {  
15 -  
16 - @Override  
17 - public String getChannel() {  
18 - return "mp.account.refresh";  
19 - }  
20 -  
21 -}  
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/producer/MpAccountProducer.java deleted
1 -package cn.iocoder.yudao.module.mp.mq.producer;  
2 -  
3 -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;  
4 -import cn.iocoder.yudao.module.mp.mq.message.MpAccountRefreshMessage;  
5 -import org.springframework.stereotype.Component;  
6 -  
7 -import javax.annotation.Resource;  
8 -  
9 -/**  
10 - * 公众号账号 Producer  
11 - *  
12 - * @author 芋道源码  
13 - */  
14 -@Component  
15 -public class MpAccountProducer {  
16 -  
17 - @Resource  
18 - private RedisMQTemplate redisMQTemplate;  
19 -  
20 - /**  
21 - * 发送 {@link MpAccountRefreshMessage} 消息  
22 - */  
23 - public void sendAccountRefreshMessage() {  
24 - MpAccountRefreshMessage message = new MpAccountRefreshMessage();  
25 - redisMQTemplate.send(message);  
26 - }  
27 -  
28 -}  
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/account/MpAccountServiceImpl.java
1 package cn.iocoder.yudao.module.mp.service.account; 1 package cn.iocoder.yudao.module.mp.service.account;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.hutool.core.util.ObjUtil; 4 import cn.hutool.core.util.ObjUtil;
4 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; 5 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;  
7 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; 7 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
8 import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountCreateReqVO; 8 import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountCreateReqVO;
9 import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountPageReqVO; 9 import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountPageReqVO;
@@ -13,7 +13,6 @@ import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; @@ -13,7 +13,6 @@ import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
13 import cn.iocoder.yudao.module.mp.dal.mysql.account.MpAccountMapper; 13 import cn.iocoder.yudao.module.mp.dal.mysql.account.MpAccountMapper;
14 import cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants; 14 import cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants;
15 import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; 15 import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory;
16 -import cn.iocoder.yudao.module.mp.mq.producer.MpAccountProducer;  
17 import com.google.common.annotations.VisibleForTesting; 16 import com.google.common.annotations.VisibleForTesting;
18 import lombok.Getter; 17 import lombok.Getter;
19 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
@@ -21,15 +20,20 @@ import me.chanjar.weixin.common.error.WxErrorException; @@ -21,15 +20,20 @@ import me.chanjar.weixin.common.error.WxErrorException;
21 import me.chanjar.weixin.mp.api.WxMpService; 20 import me.chanjar.weixin.mp.api.WxMpService;
22 import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; 21 import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
23 import org.springframework.context.annotation.Lazy; 22 import org.springframework.context.annotation.Lazy;
  23 +import org.springframework.scheduling.annotation.Scheduled;
24 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
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 java.time.LocalDateTime;
29 import java.util.List; 30 import java.util.List;
30 import java.util.Map; 31 import java.util.Map;
  32 +import java.util.concurrent.TimeUnit;
31 33
32 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 34 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  35 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  36 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
33 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_USERNAME_EXISTS; 37 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_USERNAME_EXISTS;
34 38
35 /** 39 /**
@@ -58,9 +62,6 @@ public class MpAccountServiceImpl implements MpAccountService { @@ -58,9 +62,6 @@ public class MpAccountServiceImpl implements MpAccountService {
58 @Lazy // 延迟加载,解决循环依赖的问题 62 @Lazy // 延迟加载,解决循环依赖的问题
59 private MpServiceFactory mpServiceFactory; 63 private MpServiceFactory mpServiceFactory;
60 64
61 - @Resource  
62 - private MpAccountProducer mpAccountProducer;  
63 -  
64 @Override 65 @Override
65 @PostConstruct 66 @PostConstruct
66 public void initLocalCache() { 67 public void initLocalCache() {
@@ -72,7 +73,30 @@ public class MpAccountServiceImpl implements MpAccountService { @@ -72,7 +73,30 @@ public class MpAccountServiceImpl implements MpAccountService {
72 73
73 // 第二步:构建缓存。创建或更新支付 Client 74 // 第二步:构建缓存。创建或更新支付 Client
74 mpServiceFactory.init(accounts); 75 mpServiceFactory.init(accounts);
75 - accountCache = CollectionUtils.convertMap(accounts, MpAccountDO::getAppId); 76 + accountCache = convertMap(accounts, MpAccountDO::getAppId);
  77 + });
  78 + }
  79 +
  80 + /**
  81 + * 通过定时任务轮询,刷新缓存
  82 + *
  83 + * 目的:多节点部署时,通过轮询”通知“所有节点,进行刷新
  84 + */
  85 + @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS)
  86 + public void refreshLocalCache() {
  87 + // 注意:忽略自动多租户,因为要全局初始化缓存
  88 + TenantUtils.executeIgnore(() -> {
  89 + // 情况一:如果缓存里没有数据,则直接刷新缓存
  90 + if (CollUtil.isEmpty(accountCache)) {
  91 + initLocalCache();
  92 + return;
  93 + }
  94 +
  95 + // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存
  96 + LocalDateTime maxTime = getMaxValue(accountCache.values(), MpAccountDO::getUpdateTime);
  97 + if (mpAccountMapper.selectCountByUpdateTimeGt(maxTime) > 0) {
  98 + initLocalCache();
  99 + }
76 }); 100 });
77 } 101 }
78 102
@@ -85,8 +109,8 @@ public class MpAccountServiceImpl implements MpAccountService { @@ -85,8 +109,8 @@ public class MpAccountServiceImpl implements MpAccountService {
85 MpAccountDO account = MpAccountConvert.INSTANCE.convert(createReqVO); 109 MpAccountDO account = MpAccountConvert.INSTANCE.convert(createReqVO);
86 mpAccountMapper.insert(account); 110 mpAccountMapper.insert(account);
87 111
88 - // 发送刷新消息  
89 - mpAccountProducer.sendAccountRefreshMessage(); 112 + // 刷新缓存
  113 + initLocalCache();
90 return account.getId(); 114 return account.getId();
91 } 115 }
92 116
@@ -101,8 +125,8 @@ public class MpAccountServiceImpl implements MpAccountService { @@ -101,8 +125,8 @@ public class MpAccountServiceImpl implements MpAccountService {
101 MpAccountDO updateObj = MpAccountConvert.INSTANCE.convert(updateReqVO); 125 MpAccountDO updateObj = MpAccountConvert.INSTANCE.convert(updateReqVO);
102 mpAccountMapper.updateById(updateObj); 126 mpAccountMapper.updateById(updateObj);
103 127
104 - // 发送刷新消息  
105 - mpAccountProducer.sendAccountRefreshMessage(); 128 + // 刷新缓存
  129 + initLocalCache();
106 } 130 }
107 131
108 @Override 132 @Override
@@ -112,8 +136,8 @@ public class MpAccountServiceImpl implements MpAccountService { @@ -112,8 +136,8 @@ public class MpAccountServiceImpl implements MpAccountService {
112 // 删除 136 // 删除
113 mpAccountMapper.deleteById(id); 137 mpAccountMapper.deleteById(id);
114 138
115 - // 发送刷新消息  
116 - mpAccountProducer.sendAccountRefreshMessage(); 139 + // 刷新缓存
  140 + initLocalCache();
117 } 141 }
118 142
119 private MpAccountDO validateAccountExists(Long id) { 143 private MpAccountDO validateAccountExists(Long id) {
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/dto/PayOrderRespDTO.java
@@ -6,8 +6,6 @@ import lombok.Data; @@ -6,8 +6,6 @@ import lombok.Data;
6 /** 6 /**
7 * 支付单信息 Response DTO 7 * 支付单信息 Response DTO
8 * 8 *
9 - * TODO 芋艿:还没定好字段  
10 - *  
11 * @author 芋道源码 9 * @author 芋道源码
12 */ 10 */
13 @Data 11 @Data
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/refund/dto/PayRefundRespDTO.java
@@ -8,8 +8,6 @@ import java.time.LocalDateTime; @@ -8,8 +8,6 @@ import java.time.LocalDateTime;
8 /** 8 /**
9 * 退款单信息 Response DTO 9 * 退款单信息 Response DTO
10 * 10 *
11 - * TODO 芋艿:还没定好字段  
12 - *  
13 * @author 芋道源码 11 * @author 芋道源码
14 */ 12 */
15 @Data 13 @Data
yudao-module-pay/yudao-module-pay-biz/pom.xml
@@ -61,12 +61,6 @@ @@ -61,12 +61,6 @@
61 <artifactId>yudao-spring-boot-starter-job</artifactId> 61 <artifactId>yudao-spring-boot-starter-job</artifactId>
62 </dependency> 62 </dependency>
63 63
64 - <!-- 消息队列相关 -->  
65 - <dependency>  
66 - <groupId>cn.iocoder.boot</groupId>  
67 - <artifactId>yudao-spring-boot-starter-mq</artifactId>  
68 - </dependency>  
69 -  
70 <!-- Test 测试相关 --> 64 <!-- Test 测试相关 -->
71 <dependency> 65 <dependency>
72 <groupId>cn.iocoder.boot</groupId> 66 <groupId>cn.iocoder.boot</groupId>
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-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java
@@ -15,7 +15,6 @@ import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateR @@ -15,7 +15,6 @@ import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateR
15 import cn.iocoder.yudao.module.pay.convert.channel.PayChannelConvert; 15 import cn.iocoder.yudao.module.pay.convert.channel.PayChannelConvert;
16 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; 16 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
17 import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper; 17 import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper;
18 -import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants;  
19 import lombok.Getter; 18 import lombok.Getter;
20 import lombok.Setter; 19 import lombok.Setter;
21 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
@@ -112,7 +111,7 @@ public class PayChannelServiceImpl implements PayChannelService { @@ -112,7 +111,7 @@ public class PayChannelServiceImpl implements PayChannelService {
112 channelMapper.insert(channel); 111 channelMapper.insert(channel);
113 112
114 // 刷新缓存 113 // 刷新缓存
115 - refreshLocalCache(); 114 + initLocalCache();
116 return channel.getId(); 115 return channel.getId();
117 } 116 }
118 117
@@ -127,7 +126,7 @@ public class PayChannelServiceImpl implements PayChannelService { @@ -127,7 +126,7 @@ public class PayChannelServiceImpl implements PayChannelService {
127 channelMapper.updateById(channel); 126 channelMapper.updateById(channel);
128 127
129 // 刷新缓存 128 // 刷新缓存
130 - refreshLocalCache(); 129 + initLocalCache();
131 } 130 }
132 131
133 /** 132 /**
@@ -160,7 +159,7 @@ public class PayChannelServiceImpl implements PayChannelService { @@ -160,7 +159,7 @@ public class PayChannelServiceImpl implements PayChannelService {
160 channelMapper.deleteById(id); 159 channelMapper.deleteById(id);
161 160
162 // 刷新缓存 161 // 刷新缓存
163 - refreshLocalCache(); 162 + initLocalCache();
164 } 163 }
165 164
166 private PayChannelDO validateChannelExists(Long id) { 165 private PayChannelDO validateChannelExists(Long id) {
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java
@@ -44,7 +44,7 @@ public interface AdminUserApi { @@ -44,7 +44,7 @@ public interface AdminUserApi {
44 * @param postIds 岗位数组 44 * @param postIds 岗位数组
45 * @return 用户数组 45 * @return 用户数组
46 */ 46 */
47 - List<AdminUserRespDTO> getUsersByPostIds(Collection<Long> postIds); 47 + List<AdminUserRespDTO> getUserListByPostIds(Collection<Long> postIds);
48 48
49 /** 49 /**
50 * 获得用户 Map 50 * 获得用户 Map
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/api/user/AdminUserApiImpl.java
@@ -40,7 +40,7 @@ public class AdminUserApiImpl implements AdminUserApi { @@ -40,7 +40,7 @@ public class AdminUserApiImpl implements AdminUserApi {
40 } 40 }
41 41
42 @Override 42 @Override
43 - public List<AdminUserRespDTO> getUsersByPostIds(Collection<Long> postIds) { 43 + public List<AdminUserRespDTO> getUserListByPostIds(Collection<Long> postIds) {
44 List<AdminUserDO> users = userService.getUserListByPostIds(postIds); 44 List<AdminUserDO> users = userService.getUserListByPostIds(postIds);
45 return UserConvert.INSTANCE.convertList4(users); 45 return UserConvert.INSTANCE.convertList4(users);
46 } 46 }
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 // ========== 短信登录相关 ==========
@@ -149,7 +141,7 @@ public class AuthController { @@ -149,7 +141,7 @@ public class AuthController {
149 @Parameter(name = "redirectUri", description = "回调路径") 141 @Parameter(name = "redirectUri", description = "回调路径")
150 }) 142 })
151 public CommonResult<String> socialLogin(@RequestParam("type") Integer type, 143 public CommonResult<String> socialLogin(@RequestParam("type") Integer type,
152 - @RequestParam("redirectUri") String redirectUri) { 144 + @RequestParam("redirectUri") String redirectUri) {
153 return CommonResult.success(socialUserService.getAuthorizeUrl(type, redirectUri)); 145 return CommonResult.success(socialUserService.getAuthorizeUrl(type, redirectUri));
154 } 146 }
155 147
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java
@@ -6,9 +6,10 @@ import lombok.Builder; @@ -6,9 +6,10 @@ 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,额外包括用户信息和角色列表")
12 @Data 13 @Data
13 @NoArgsConstructor 14 @NoArgsConstructor
14 @AllArgsConstructor 15 @AllArgsConstructor
@@ -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
@@ -37,9 +41,53 @@ public class AuthPermissionInfoRespVO { @@ -37,9 +41,53 @@ public class AuthPermissionInfoRespVO {
37 @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") 41 @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
38 private String nickname; 42 private String nickname;
39 43
40 - @Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://www.iocoder.cn/xx.jpg") 44 + @Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.jpg")
41 private String avatar; 45 private String avatar;
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/mail/vo/log/MailLogBaseVO.java
@@ -11,9 +11,9 @@ import java.util.Map; @@ -11,9 +11,9 @@ import java.util.Map;
11 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; 11 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
12 12
13 /** 13 /**
14 -* 邮件日志 Base VO,提供给添加、修改、详细的子 VO 使用  
15 -* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成  
16 -*/ 14 + * 邮件日志 Base VO,提供给添加、修改、详细的子 VO 使用
  15 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  16 + */
17 @Data 17 @Data
18 public class MailLogBaseVO { 18 public class MailLogBaseVO {
19 19
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/controller/admin/permission/RoleController.http
@@ -40,6 +40,3 @@ tenant-id: {{adminTenentId}} @@ -40,6 +40,3 @@ tenant-id: {{adminTenentId}}
40 GET {{baseUrl}}/system/role/page?pageNo=1&pageSize=10 40 GET {{baseUrl}}/system/role/page?pageNo=1&pageSize=10
41 Authorization: Bearer {{token}} 41 Authorization: Bearer {{token}}
42 tenant-id: {{adminTenentId}} 42 tenant-id: {{adminTenentId}}
43 -  
44 -###  
45 -  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignRoleDataScopeReqVO.java
@@ -20,7 +20,7 @@ public class PermissionAssignRoleDataScopeReqVO { @@ -20,7 +20,7 @@ public class PermissionAssignRoleDataScopeReqVO {
20 // TODO 这里要多一个枚举校验 20 // TODO 这里要多一个枚举校验
21 private Integer dataScope; 21 private Integer dataScope;
22 22
23 - @Schema(description = "部门编号列表,只有范围类型为 DEPT_CUSTOM 时,该字段才需要", example = "1,3,5") 23 + @Schema(description = "部门编号列表,只有范围类型为 DEPT_CUSTOM 时,该字段才需要", example = "1,3,5")
24 private Set<Long> dataScopeDeptIds = Collections.emptySet(); // 兜底 24 private Set<Long> dataScopeDeptIds = Collections.emptySet(); // 兜底
25 25
26 } 26 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/vo/SensitiveWordBaseVO.java
@@ -7,9 +7,9 @@ import javax.validation.constraints.NotNull; @@ -7,9 +7,9 @@ import javax.validation.constraints.NotNull;
7 import java.util.List; 7 import java.util.List;
8 8
9 /** 9 /**
10 -* 敏感词 Base VO,提供给添加、修改、详细的子 VO 使用  
11 -* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成  
12 -*/ 10 + * 敏感词 Base VO,提供给添加、修改、详细的子 VO 使用
  11 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  12 + */
13 @Data 13 @Data
14 public class SensitiveWordBaseVO { 14 public class SensitiveWordBaseVO {
15 15
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantBaseVO.java
@@ -7,9 +7,9 @@ import javax.validation.constraints.*; @@ -7,9 +7,9 @@ import javax.validation.constraints.*;
7 import java.time.LocalDateTime; 7 import java.time.LocalDateTime;
8 8
9 /** 9 /**
10 -* 租户 Base VO,提供给添加、修改、详细的子 VO 使用  
11 -* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成  
12 -*/ 10 + * 租户 Base VO,提供给添加、修改、详细的子 VO 使用
  11 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  12 + */
13 @Data 13 @Data
14 public class TenantBaseVO { 14 public class TenantBaseVO {
15 15
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdateReqVO.java
@@ -25,7 +25,7 @@ public class UserProfileUpdateReqVO { @@ -25,7 +25,7 @@ public class UserProfileUpdateReqVO {
25 @Length(min = 11, max = 11, message = "手机号长度必须 11 位") 25 @Length(min = 11, max = 11, message = "手机号长度必须 11 位")
26 private String mobile; 26 private String mobile;
27 27
28 - @Schema(description = "用户性别-参见 SexEnum 枚举类", example = "1") 28 + @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1")
29 private Integer sex; 29 private Integer sex;
30 30
31 } 31 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserExportReqVO.java
@@ -29,7 +29,7 @@ public class UserExportReqVO { @@ -29,7 +29,7 @@ public class UserExportReqVO {
29 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) 29 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
30 private LocalDateTime[] createTime; 30 private LocalDateTime[] createTime;
31 31
32 - @Schema(description = "部门编号,同时筛选子部门", example = "1024") 32 + @Schema(description = "部门编号,同时筛选子部门", example = "1024")
33 private Long deptId; 33 private Long deptId;
34 34
35 } 35 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserPageItemRespVO.java
@@ -6,7 +6,7 @@ import lombok.Data; @@ -6,7 +6,7 @@ import lombok.Data;
6 import lombok.EqualsAndHashCode; 6 import lombok.EqualsAndHashCode;
7 import lombok.NoArgsConstructor; 7 import lombok.NoArgsConstructor;
8 8
9 -@Schema(description = "管理后台 - 用户分页时的信息 Response VO,相比用户基本信息来说,会多部门信息") 9 +@Schema(description = "管理后台 - 用户分页时的信息 Response VO,相比用户基本信息来说,会多部门信息")
10 @Data 10 @Data
11 @NoArgsConstructor 11 @NoArgsConstructor
12 @AllArgsConstructor 12 @AllArgsConstructor
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdateStatusReqVO.java
1 package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; 1 package cn.iocoder.yudao.module.system.controller.admin.user.vo.user;
2 2
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
3 import io.swagger.v3.oas.annotations.media.Schema; 5 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 6 import lombok.Data;
5 7
@@ -13,9 +15,9 @@ public class UserUpdateStatusReqVO { @@ -13,9 +15,9 @@ public class UserUpdateStatusReqVO {
13 @NotNull(message = "角色编号不能为空") 15 @NotNull(message = "角色编号不能为空")
14 private Long id; 16 private Long id;
15 17
16 - @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 18 + @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
17 @NotNull(message = "状态不能为空") 19 @NotNull(message = "状态不能为空")
18 -// @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") 20 + @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
19 private Integer status; 21 private Integer status;
20 22
21 } 23 }
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,13 +28,16 @@ public interface AuthConvert { @@ -27,13 +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()).avatar(user.getAvatar()).build())  
31 - .roles(convertSet(roleList, RoleDO::getCode))  
32 - .permissions(convertSet(menuList, MenuDO::getPermission))  
33 - .build(); 31 + .user(AuthPermissionInfoRespVO.UserVO.builder().id(user.getId()).nickname(user.getNickname()).avatar(user.getAvatar()).build())
  32 + .roles(convertSet(roleList, RoleDO::getCode))
  33 + // 权限标识信息
  34 + .permissions(convertSet(menuList, MenuDO::getPermission))
  35 + // 菜单树
  36 + .menus(buildMenuTree(menuList))
  37 + .build();
34 } 38 }
35 39
36 - AuthMenuRespVO convertTreeNode(MenuDO menu); 40 + AuthPermissionInfoRespVO.MenuVO convertTreeNode(MenuDO menu);
37 41
38 /** 42 /**
39 * 将菜单列表,构建成菜单树 43 * 将菜单列表,构建成菜单树
@@ -41,20 +45,23 @@ public interface AuthConvert { @@ -41,20 +45,23 @@ public interface AuthConvert {
41 * @param menuList 菜单列表 45 * @param menuList 菜单列表
42 * @return 菜单树 46 * @return 菜单树
43 */ 47 */
44 - 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()));
45 // 排序,保证菜单的有序性 51 // 排序,保证菜单的有序性
46 menuList.sort(Comparator.comparing(MenuDO::getSort)); 52 menuList.sort(Comparator.comparing(MenuDO::getSort));
  53 +
47 // 构建菜单树 54 // 构建菜单树
48 // 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。 55 // 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。
49 - Map<Long, AuthMenuRespVO> treeNodeMap = new LinkedHashMap<>(); 56 + Map<Long, AuthPermissionInfoRespVO.MenuVO> treeNodeMap = new LinkedHashMap<>();
50 menuList.forEach(menu -> treeNodeMap.put(menu.getId(), AuthConvert.INSTANCE.convertTreeNode(menu))); 57 menuList.forEach(menu -> treeNodeMap.put(menu.getId(), AuthConvert.INSTANCE.convertTreeNode(menu)));
51 // 处理父子关系 58 // 处理父子关系
52 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 -> {
53 // 获得父节点 60 // 获得父节点
54 - AuthMenuRespVO parentNode = treeNodeMap.get(childNode.getParentId()); 61 + AuthPermissionInfoRespVO.MenuVO parentNode = treeNodeMap.get(childNode.getParentId());
55 if (parentNode == null) { 62 if (parentNode == null) {
56 LoggerFactory.getLogger(getClass()).error("[buildRouterTree][resource({}) 找不到父资源({})]", 63 LoggerFactory.getLogger(getClass()).error("[buildRouterTree][resource({}) 找不到父资源({})]",
57 - childNode.getId(), childNode.getParentId()); 64 + childNode.getId(), childNode.getParentId());
58 return; 65 return;
59 } 66 }
60 // 将自己添加到父节点中 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 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java
@@ -10,9 +10,9 @@ import cn.iocoder.yudao.module.system.convert.tenant.TenantPackageConvert; @@ -10,9 +10,9 @@ import cn.iocoder.yudao.module.system.convert.tenant.TenantPackageConvert;
10 import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; 10 import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
11 import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; 11 import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO;
12 import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantPackageMapper; 12 import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantPackageMapper;
  13 +import com.baomidou.dynamic.datasource.annotation.DSTransactional;
13 import org.springframework.context.annotation.Lazy; 14 import org.springframework.context.annotation.Lazy;
14 import org.springframework.stereotype.Service; 15 import org.springframework.stereotype.Service;
15 -import org.springframework.transaction.annotation.Transactional;  
16 import org.springframework.validation.annotation.Validated; 16 import org.springframework.validation.annotation.Validated;
17 17
18 import javax.annotation.Resource; 18 import javax.annotation.Resource;
@@ -47,7 +47,7 @@ public class TenantPackageServiceImpl implements TenantPackageService { @@ -47,7 +47,7 @@ public class TenantPackageServiceImpl implements TenantPackageService {
47 } 47 }
48 48
49 @Override 49 @Override
50 - @Transactional(rollbackFor = Exception.class) 50 + @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
51 public void updateTenantPackage(TenantPackageUpdateReqVO updateReqVO) { 51 public void updateTenantPackage(TenantPackageUpdateReqVO updateReqVO) {
52 // 校验存在 52 // 校验存在
53 TenantPackageDO tenantPackage = validateTenantPackageExists(updateReqVO.getId()); 53 TenantPackageDO tenantPackage = validateTenantPackageExists(updateReqVO.getId());
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java
@@ -29,6 +29,7 @@ import cn.iocoder.yudao.module.system.service.permission.RoleService; @@ -29,6 +29,7 @@ import cn.iocoder.yudao.module.system.service.permission.RoleService;
29 import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler; 29 import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler;
30 import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler; 30 import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler;
31 import cn.iocoder.yudao.module.system.service.user.AdminUserService; 31 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
  32 +import com.baomidou.dynamic.datasource.annotation.DSTransactional;
32 import lombok.extern.slf4j.Slf4j; 33 import lombok.extern.slf4j.Slf4j;
33 import org.springframework.beans.factory.annotation.Autowired; 34 import org.springframework.beans.factory.annotation.Autowired;
34 import org.springframework.context.annotation.Lazy; 35 import org.springframework.context.annotation.Lazy;
@@ -95,7 +96,7 @@ public class TenantServiceImpl implements TenantService { @@ -95,7 +96,7 @@ public class TenantServiceImpl implements TenantService {
95 } 96 }
96 97
97 @Override 98 @Override
98 - @Transactional(rollbackFor = Exception.class) 99 + @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
99 public Long createTenant(TenantCreateReqVO createReqVO) { 100 public Long createTenant(TenantCreateReqVO createReqVO) {
100 // 校验租户名称是否重复 101 // 校验租户名称是否重复
101 validTenantNameDuplicate(createReqVO.getName(), null); 102 validTenantNameDuplicate(createReqVO.getName(), null);
@@ -137,7 +138,7 @@ public class TenantServiceImpl implements TenantService { @@ -137,7 +138,7 @@ public class TenantServiceImpl implements TenantService {
137 } 138 }
138 139
139 @Override 140 @Override
140 - @Transactional(rollbackFor = Exception.class) 141 + @DSTransactional
141 public void updateTenant(TenantUpdateReqVO updateReqVO) { 142 public void updateTenant(TenantUpdateReqVO updateReqVO) {
142 // 校验存在 143 // 校验存在
143 TenantDO tenant = validateUpdateTenant(updateReqVO.getId()); 144 TenantDO tenant = validateUpdateTenant(updateReqVO.getId());
@@ -170,7 +171,7 @@ public class TenantServiceImpl implements TenantService { @@ -170,7 +171,7 @@ public class TenantServiceImpl implements TenantService {
170 } 171 }
171 172
172 @Override 173 @Override
173 - @Transactional(rollbackFor = Exception.class) 174 + @DSTransactional
174 public void updateTenantRoleMenu(Long tenantId, Set<Long> menuIds) { 175 public void updateTenantRoleMenu(Long tenantId, Set<Long> menuIds) {
175 TenantUtils.execute(tenantId, () -> { 176 TenantUtils.execute(tenantId, () -> {
176 // 获得所有角色 177 // 获得所有角色
@@ -186,7 +187,7 @@ public class TenantServiceImpl implements TenantService { @@ -186,7 +187,7 @@ public class TenantServiceImpl implements TenantService {
186 return; 187 return;
187 } 188 }
188 // 如果是其他角色,则去掉超过套餐的权限 189 // 如果是其他角色,则去掉超过套餐的权限
189 - Set<Long> roleMenuIds = permissionService.getRoleMenuIds(role.getId()); 190 + Set<Long> roleMenuIds = permissionService.getRoleMenuListByRoleId(role.getId());
190 roleMenuIds = CollUtil.intersectionDistinct(roleMenuIds, menuIds); 191 roleMenuIds = CollUtil.intersectionDistinct(roleMenuIds, menuIds);
191 permissionService.assignRoleMenu(role.getId(), roleMenuIds); 192 permissionService.assignRoleMenu(role.getId(), roleMenuIds);
192 log.info("[updateTenantRoleMenu][角色({}/{}) 的权限修改为({})]", role.getId(), role.getTenantId(), roleMenuIds); 193 log.info("[updateTenantRoleMenu][角色({}/{}) 的权限修改为({})]", role.getId(), role.getTenantId(), roleMenuIds);
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
@@ -290,14 +290,13 @@ public class AdminUserServiceImpl implements AdminUserService { @@ -290,14 +290,13 @@ public class AdminUserServiceImpl implements AdminUserService {
290 if (deptId == null) { 290 if (deptId == null) {
291 return Collections.emptySet(); 291 return Collections.emptySet();
292 } 292 }
293 - Set<Long> deptIds = convertSet(deptService.getDeptListByParentIdFromCache(  
294 - deptId, true), DeptDO::getId); 293 + Set<Long> deptIds = convertSet(deptService.getChildDeptList(deptId), DeptDO::getId);
295 deptIds.add(deptId); // 包括自身 294 deptIds.add(deptId); // 包括自身
296 return deptIds; 295 return deptIds;
297 } 296 }
298 297
299 private void validateUserForCreateOrUpdate(Long id, String username, String mobile, String email, 298 private void validateUserForCreateOrUpdate(Long id, String username, String mobile, String email,
300 - Long deptId, Set<Long> postIds) { 299 + Long deptId, Set<Long> postIds) {
301 // 关闭数据权限,避免因为没有数据权限,查询不到数据,进而导致唯一校验不正确 300 // 关闭数据权限,避免因为没有数据权限,查询不到数据,进而导致唯一校验不正确
302 DataPermissionUtils.executeIgnore(() -> { 301 DataPermissionUtils.executeIgnore(() -> {
303 // 校验用户存在 302 // 校验用户存在
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenControllerTest.java
@@ -39,6 +39,9 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic @@ -39,6 +39,9 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic
39 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; 39 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
40 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; 40 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
41 import static java.util.Arrays.asList; 41 import static java.util.Arrays.asList;
  42 +import static org.hamcrest.CoreMatchers.anyOf;
  43 +import static org.hamcrest.CoreMatchers.is;
  44 +import static org.hamcrest.MatcherAssert.assertThat;
42 import static org.junit.jupiter.api.Assertions.*; 45 import static org.junit.jupiter.api.Assertions.*;
43 import static org.mockito.ArgumentMatchers.eq; 46 import static org.mockito.ArgumentMatchers.eq;
44 import static org.mockito.ArgumentMatchers.isNull; 47 import static org.mockito.ArgumentMatchers.isNull;
@@ -289,7 +292,10 @@ public class OAuth2OpenControllerTest extends BaseMockitoUnitTest { @@ -289,7 +292,10 @@ public class OAuth2OpenControllerTest extends BaseMockitoUnitTest {
289 scope, redirectUri, true, state); 292 scope, redirectUri, true, state);
290 // 断言 293 // 断言
291 assertEquals(0, result.getCode()); 294 assertEquals(0, result.getCode());
292 - assertEquals("https://www.iocoder.cn#access_token=test_access_token&token_type=bearer&state=test&expires_in=30&scope=read", result.getData()); 295 + assertThat(result.getData(), anyOf( // 29 和 30 都有一定概率,主要是时间计算
  296 + is("https://www.iocoder.cn#access_token=test_access_token&token_type=bearer&state=test&expires_in=29&scope=read"),
  297 + is("https://www.iocoder.cn#access_token=test_access_token&token_type=bearer&state=test&expires_in=30&scope=read")
  298 + ));
293 } 299 }
294 300
295 @Test // autoApprove = false,通过 + code 301 @Test // autoApprove = false,通过 + code
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java
@@ -148,7 +148,7 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest { @@ -148,7 +148,7 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
148 ); 148 );
149 } 149 }
150 150
151 - @Test 151 + @Test
152 public void testLogin_success() { 152 public void testLogin_success() {
153 // 准备参数 153 // 准备参数
154 AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o -> 154 AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o ->
@@ -174,9 +174,9 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest { @@ -174,9 +174,9 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
174 assertPojoEquals(accessTokenDO, loginRespVO); 174 assertPojoEquals(accessTokenDO, loginRespVO);
175 // 校验调用参数 175 // 校验调用参数
176 verify(loginLogService).createLoginLog( 176 verify(loginLogService).createLoginLog(
177 - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())  
178 - && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())  
179 - && o.getUserId().equals(user.getId())) 177 + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
  178 + && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())
  179 + && o.getUserId().equals(user.getId()))
180 ); 180 );
181 verify(socialUserService).bindSocialUser(eq(new SocialUserBindReqDTO( 181 verify(socialUserService).bindSocialUser(eq(new SocialUserBindReqDTO(
182 user.getId(), UserTypeEnum.ADMIN.getValue(), 182 user.getId(), UserTypeEnum.ADMIN.getValue(),
@@ -317,8 +317,8 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest { @@ -317,8 +317,8 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
317 assertServiceException(() -> authService.validateCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_CODE_ERROR, "就是不对"); 317 assertServiceException(() -> authService.validateCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_CODE_ERROR, "就是不对");
318 // 校验调用参数 318 // 校验调用参数
319 verify(loginLogService).createLoginLog( 319 verify(loginLogService).createLoginLog(
320 - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())  
321 - && o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult())) 320 + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
  321 + && o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult()))
322 ); 322 );
323 } 323 }
324 324
@@ -349,8 +349,9 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest { @@ -349,8 +349,9 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
349 // 调用 349 // 调用
350 authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); 350 authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
351 // 校验调用参数 351 // 校验调用参数
352 - verify(loginLogService).createLoginLog(argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType())  
353 - && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())) 352 + verify(loginLogService).createLoginLog(
  353 + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType())
  354 + && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()))
354 ); 355 );
355 // 调用,并校验 356 // 调用,并校验
356 357
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImplTest.java
1 package cn.iocoder.yudao.module.system.service.dept; 1 package cn.iocoder.yudao.module.system.service.dept;
2 2
3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 -import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;  
5 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 4 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
6 -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;  
7 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; 5 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
8 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptCreateReqVO; 6 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptCreateReqVO;
9 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; 7 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
@@ -11,27 +9,20 @@ import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateRe @@ -11,27 +9,20 @@ import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateRe
11 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; 9 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
12 import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper; 10 import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper;
13 import cn.iocoder.yudao.module.system.enums.dept.DeptIdEnum; 11 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.Multimap;  
16 -import org.junit.jupiter.api.BeforeEach;  
17 import org.junit.jupiter.api.Test; 12 import org.junit.jupiter.api.Test;
18 -import org.springframework.boot.test.mock.mockito.MockBean;  
19 import org.springframework.context.annotation.Import; 13 import org.springframework.context.annotation.Import;
20 14
21 import javax.annotation.Resource; 15 import javax.annotation.Resource;
22 import java.util.Arrays; 16 import java.util.Arrays;
23 import java.util.List; 17 import java.util.List;
24 -import java.util.Map;  
25 -import java.util.function.Consumer; 18 +import java.util.Set;
26 19
27 -import static cn.hutool.core.util.RandomUtil.randomEle;  
28 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; 20 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
29 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; 21 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
30 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; 22 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
31 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; 23 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
32 import static java.util.Collections.singletonList; 24 import static java.util.Collections.singletonList;
33 import static org.junit.jupiter.api.Assertions.*; 25 import static org.junit.jupiter.api.Assertions.*;
34 -import static org.mockito.Mockito.verify;  
35 26
36 /** 27 /**
37 * {@link DeptServiceImpl} 的单元测试类 28 * {@link DeptServiceImpl} 的单元测试类
@@ -45,63 +36,9 @@ public class DeptServiceImplTest extends BaseDbUnitTest { @@ -45,63 +36,9 @@ public class DeptServiceImplTest extends BaseDbUnitTest {
45 private DeptServiceImpl deptService; 36 private DeptServiceImpl deptService;
46 @Resource 37 @Resource
47 private DeptMapper deptMapper; 38 private DeptMapper deptMapper;
48 - @MockBean  
49 - private DeptProducer deptProducer;  
50 -  
51 - @BeforeEach  
52 - public void setUp() {  
53 - // 清理租户上下文  
54 - TenantContextHolder.clear();  
55 - }  
56 -  
57 - @Test  
58 - public void testInitLocalCache() {  
59 - // mock 数据  
60 - DeptDO deptDO1 = randomDeptDO();  
61 - deptMapper.insert(deptDO1);  
62 - DeptDO deptDO2 = randomDeptDO();  
63 - deptMapper.insert(deptDO2);  
64 -  
65 - // 调用  
66 - deptService.initLocalCache();  
67 - // 断言 deptCache 缓存  
68 - Map<Long, DeptDO> deptCache = deptService.getDeptCache();  
69 - assertEquals(2, deptCache.size());  
70 - assertPojoEquals(deptDO1, deptCache.get(deptDO1.getId()));  
71 - assertPojoEquals(deptDO2, deptCache.get(deptDO2.getId()));  
72 - // 断言 parentDeptCache 缓存  
73 - Multimap<Long, DeptDO> parentDeptCache = deptService.getParentDeptCache();  
74 - assertEquals(2, parentDeptCache.size());  
75 - assertPojoEquals(deptDO1, parentDeptCache.get(deptDO1.getParentId()));  
76 - assertPojoEquals(deptDO2, parentDeptCache.get(deptDO2.getParentId()));  
77 - }  
78 -  
79 - @Test  
80 - public void testListDepts() {  
81 - // mock 数据  
82 - DeptDO dept = randomPojo(DeptDO.class, o -> { // 等会查询到  
83 - o.setName("开发部");  
84 - o.setStatus(CommonStatusEnum.ENABLE.getStatus());  
85 - });  
86 - deptMapper.insert(dept);  
87 - // 测试 name 不匹配  
88 - deptMapper.insert(ObjectUtils.cloneIgnoreId(dept, o -> o.setName("发")));  
89 - // 测试 status 不匹配  
90 - deptMapper.insert(ObjectUtils.cloneIgnoreId(dept, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));  
91 - // 准备参数  
92 - DeptListReqVO reqVO = new DeptListReqVO();  
93 - reqVO.setName("开");  
94 - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());  
95 -  
96 - // 调用  
97 - List<DeptDO> sysDeptDOS = deptService.getDeptList(reqVO);  
98 - // 断言  
99 - assertEquals(1, sysDeptDOS.size());  
100 - assertPojoEquals(dept, sysDeptDOS.get(0));  
101 - }  
102 39
103 @Test 40 @Test
104 - public void testCreateDept_success() { 41 + public void testCreateDept() {
105 // 准备参数 42 // 准备参数
106 DeptCreateReqVO reqVO = randomPojo(DeptCreateReqVO.class, o -> { 43 DeptCreateReqVO reqVO = randomPojo(DeptCreateReqVO.class, o -> {
107 o.setParentId(DeptIdEnum.ROOT.getId()); 44 o.setParentId(DeptIdEnum.ROOT.getId());
@@ -115,12 +52,10 @@ public class DeptServiceImplTest extends BaseDbUnitTest { @@ -115,12 +52,10 @@ public class DeptServiceImplTest extends BaseDbUnitTest {
115 // 校验记录的属性是否正确 52 // 校验记录的属性是否正确
116 DeptDO deptDO = deptMapper.selectById(deptId); 53 DeptDO deptDO = deptMapper.selectById(deptId);
117 assertPojoEquals(reqVO, deptDO); 54 assertPojoEquals(reqVO, deptDO);
118 - // 校验调用  
119 - verify(deptProducer).sendDeptRefreshMessage();  
120 } 55 }
121 56
122 @Test 57 @Test
123 - public void testUpdateDept_success() { 58 + public void testUpdateDept() {
124 // mock 数据 59 // mock 数据
125 DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus())); 60 DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus()));
126 deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 61 deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据
@@ -137,14 +72,12 @@ public class DeptServiceImplTest extends BaseDbUnitTest { @@ -137,14 +72,12 @@ public class DeptServiceImplTest extends BaseDbUnitTest {
137 // 校验是否更新正确 72 // 校验是否更新正确
138 DeptDO deptDO = deptMapper.selectById(reqVO.getId()); // 获取最新的 73 DeptDO deptDO = deptMapper.selectById(reqVO.getId()); // 获取最新的
139 assertPojoEquals(reqVO, deptDO); 74 assertPojoEquals(reqVO, deptDO);
140 - // 校验调用  
141 - verify(deptProducer).sendDeptRefreshMessage();  
142 } 75 }
143 76
144 @Test 77 @Test
145 public void testDeleteDept_success() { 78 public void testDeleteDept_success() {
146 // mock 数据 79 // mock 数据
147 - DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus())); 80 + DeptDO dbDeptDO = randomPojo(DeptDO.class);
148 deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 81 deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据
149 // 准备参数 82 // 准备参数
150 Long id = dbDeptDO.getId(); 83 Long id = dbDeptDO.getId();
@@ -153,134 +86,99 @@ public class DeptServiceImplTest extends BaseDbUnitTest { @@ -153,134 +86,99 @@ public class DeptServiceImplTest extends BaseDbUnitTest {
153 deptService.deleteDept(id); 86 deptService.deleteDept(id);
154 // 校验数据不存在了 87 // 校验数据不存在了
155 assertNull(deptMapper.selectById(id)); 88 assertNull(deptMapper.selectById(id));
156 - // 校验调用  
157 - verify(deptProducer).sendDeptRefreshMessage();  
158 } 89 }
159 90
160 @Test 91 @Test
161 - public void testValidateDept_nameDuplicateForUpdate() { 92 + public void testDeleteDept_exitsChildren() {
162 // mock 数据 93 // mock 数据
163 - DeptDO deptDO = randomDeptDO();  
164 - // 设置根节点部门  
165 - deptDO.setParentId(DeptIdEnum.ROOT.getId());  
166 - deptMapper.insert(deptDO);  
167 - // mock 数据 稍后模拟重复它的 name  
168 - DeptDO nameDeptDO = randomDeptDO();  
169 - // 设置根节点部门  
170 - nameDeptDO.setParentId(DeptIdEnum.ROOT.getId());  
171 - deptMapper.insert(nameDeptDO); 94 + DeptDO parentDept = randomPojo(DeptDO.class);
  95 + deptMapper.insert(parentDept);// @Sql: 先插入出一条存在的数据
172 // 准备参数 96 // 准备参数
173 - DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class, o -> {  
174 - // 设置根节点部门  
175 - o.setParentId(DeptIdEnum.ROOT.getId());  
176 - // 设置更新的 ID  
177 - o.setId(deptDO.getId());  
178 - // 模拟 name 重复  
179 - o.setName(nameDeptDO.getName()); 97 + DeptDO childrenDeptDO = randomPojo(DeptDO.class, o -> {
  98 + o.setParentId(parentDept.getId());
  99 + o.setStatus(randomCommonStatus());
180 }); 100 });
  101 + // 插入子部门
  102 + deptMapper.insert(childrenDeptDO);
181 103
182 // 调用, 并断言异常 104 // 调用, 并断言异常
183 - assertServiceException(() -> deptService.updateDept(reqVO), DEPT_NAME_DUPLICATE); 105 + assertServiceException(() -> deptService.deleteDept(parentDept.getId()), DEPT_EXITS_CHILDREN);
184 } 106 }
185 107
186 @Test 108 @Test
187 - public void testValidateDept_parentNotExitsForCreate() { 109 + public void testValidateDeptExists_notFound() {
188 // 准备参数 110 // 准备参数
189 - DeptCreateReqVO reqVO = randomPojo(DeptCreateReqVO.class,  
190 - o -> o.setStatus(randomCommonStatus())); 111 + Long id = randomLongId();
191 112
192 - // 调用,并断言异常  
193 - assertServiceException(() -> deptService.createDept(reqVO), DEPT_PARENT_NOT_EXITS); 113 + // 调用, 并断言异常
  114 + assertServiceException(() -> deptService.validateDeptExists(id), DEPT_NOT_FOUND);
194 } 115 }
195 116
196 @Test 117 @Test
197 - public void testValidateDept_notFoundForDelete() { 118 + public void testValidateParentDept_parentError() {
198 // 准备参数 119 // 准备参数
199 Long id = randomLongId(); 120 Long id = randomLongId();
200 121
201 // 调用, 并断言异常 122 // 调用, 并断言异常
202 - assertServiceException(() -> deptService.deleteDept(id), DEPT_NOT_FOUND); 123 + assertServiceException(() -> deptService.validateParentDept(id, id),
  124 + DEPT_PARENT_ERROR);
203 } 125 }
204 126
205 @Test 127 @Test
206 - public void testValidateDept_exitsChildrenForDelete() {  
207 - // mock 数据  
208 - DeptDO parentDept = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus()));  
209 - deptMapper.insert(parentDept);// @Sql: 先插入出一条存在的数据  
210 - // 准备参数  
211 - DeptDO childrenDeptDO = randomPojo(DeptDO.class, o -> { 128 + public void testValidateParentDept_parentIsChild() {
  129 + // mock 数据(父节点)
  130 + DeptDO parentDept = randomPojo(DeptDO.class);
  131 + deptMapper.insert(parentDept);
  132 + // mock 数据(子节点)
  133 + DeptDO childDept = randomPojo(DeptDO.class, o -> {
212 o.setParentId(parentDept.getId()); 134 o.setParentId(parentDept.getId());
213 - o.setStatus(randomCommonStatus());  
214 }); 135 });
215 - // 插入子部门  
216 - deptMapper.insert(childrenDeptDO);  
217 - // 调用, 并断言异常  
218 - assertServiceException(() -> deptService.deleteDept(parentDept.getId()), DEPT_EXITS_CHILDREN);  
219 - } 136 + deptMapper.insert(childDept);
220 137
221 - @Test  
222 - public void testValidateDept_parentErrorForUpdate() {  
223 - // mock 数据  
224 - DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus()));  
225 - deptMapper.insert(dbDeptDO);  
226 // 准备参数 138 // 准备参数
227 - DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class, o -> {  
228 - // 设置自己为父部门  
229 - o.setParentId(dbDeptDO.getId());  
230 - // 设置更新的 ID  
231 - o.setId(dbDeptDO.getId());  
232 - }); 139 + Long id = parentDept.getId();
  140 + Long parentId = childDept.getId();
233 141
234 // 调用, 并断言异常 142 // 调用, 并断言异常
235 - assertServiceException(() -> deptService.updateDept(reqVO), DEPT_PARENT_ERROR); 143 + assertServiceException(() -> deptService.validateParentDept(id, parentId), DEPT_PARENT_IS_CHILD);
236 } 144 }
237 145
238 @Test 146 @Test
239 - public void testValidateDept_notEnableForCreate() { 147 + public void testValidateNameUnique_duplicate() {
240 // mock 数据 148 // mock 数据
241 - DeptDO deptDO = randomPojo(DeptDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); 149 + DeptDO deptDO = randomPojo(DeptDO.class);
242 deptMapper.insert(deptDO); 150 deptMapper.insert(deptDO);
  151 +
243 // 准备参数 152 // 准备参数
244 - DeptCreateReqVO reqVO = randomPojo(DeptCreateReqVO.class, o -> {  
245 - // 设置未启用的部门为父部门  
246 - o.setParentId(deptDO.getId());  
247 - }); 153 + Long id = randomLongId();
  154 + Long parentId = deptDO.getParentId();
  155 + String name = deptDO.getName();
248 156
249 // 调用, 并断言异常 157 // 调用, 并断言异常
250 - assertServiceException(() -> deptService.createDept(reqVO), DEPT_NOT_ENABLE); 158 + assertServiceException(() -> deptService.validateDeptNameUnique(id, parentId, name),
  159 + DEPT_NAME_DUPLICATE);
251 } 160 }
252 161
253 @Test 162 @Test
254 - public void testCheckDept_parentIsChildForUpdate() { 163 + public void testGetDept() {
255 // mock 数据 164 // mock 数据
256 - DeptDO parentDept = randomPojo(DeptDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));  
257 - deptMapper.insert(parentDept);  
258 - DeptDO childDept = randomPojo(DeptDO.class, o -> {  
259 - o.setStatus(CommonStatusEnum.ENABLE.getStatus());  
260 - o.setParentId(parentDept.getId());  
261 - });  
262 - deptMapper.insert(childDept);  
263 - // 初始化本地缓存  
264 - deptService.initLocalCache();  
265 - 165 + DeptDO deptDO = randomPojo(DeptDO.class);
  166 + deptMapper.insert(deptDO);
266 // 准备参数 167 // 准备参数
267 - DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class, o -> {  
268 - // 设置自己的子部门为父部门  
269 - o.setParentId(childDept.getId());  
270 - // 设置更新的 ID  
271 - o.setId(parentDept.getId());  
272 - }); 168 + Long id = deptDO.getId();
273 169
274 - // 调用, 并断言异常  
275 - assertServiceException(() -> deptService.updateDept(reqVO), DEPT_PARENT_IS_CHILD); 170 + // 调用
  171 + DeptDO dbDept = deptService.getDept(id);
  172 + // 断言
  173 + assertEquals(deptDO, dbDept);
276 } 174 }
277 175
278 @Test 176 @Test
279 - public void testGetDeptList() { 177 + public void testGetDeptList_ids() {
280 // mock 数据 178 // mock 数据
281 - DeptDO deptDO01 = randomDeptDO(); 179 + DeptDO deptDO01 = randomPojo(DeptDO.class);
282 deptMapper.insert(deptDO01); 180 deptMapper.insert(deptDO01);
283 - DeptDO deptDO02 = randomDeptDO(); 181 + DeptDO deptDO02 = randomPojo(DeptDO.class);
284 deptMapper.insert(deptDO02); 182 deptMapper.insert(deptDO02);
285 // 准备参数 183 // 准备参数
286 List<Long> ids = Arrays.asList(deptDO01.getId(), deptDO02.getId()); 184 List<Long> ids = Arrays.asList(deptDO01.getId(), deptDO02.getId());
@@ -294,23 +192,79 @@ public class DeptServiceImplTest extends BaseDbUnitTest { @@ -294,23 +192,79 @@ public class DeptServiceImplTest extends BaseDbUnitTest {
294 } 192 }
295 193
296 @Test 194 @Test
297 - public void testGetDept() { 195 + public void testGetDeptList_reqVO() {
298 // mock 数据 196 // mock 数据
299 - DeptDO deptDO = randomDeptDO();  
300 - deptMapper.insert(deptDO); 197 + DeptDO dept = randomPojo(DeptDO.class, o -> { // 等会查询到
  198 + o.setName("开发部");
  199 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  200 + });
  201 + deptMapper.insert(dept);
  202 + // 测试 name 不匹配
  203 + deptMapper.insert(ObjectUtils.cloneIgnoreId(dept, o -> o.setName("发")));
  204 + // 测试 status 不匹配
  205 + deptMapper.insert(ObjectUtils.cloneIgnoreId(dept, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
301 // 准备参数 206 // 准备参数
302 - Long id = deptDO.getId(); 207 + DeptListReqVO reqVO = new DeptListReqVO();
  208 + reqVO.setName("开");
  209 + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
303 210
304 // 调用 211 // 调用
305 - DeptDO dbDept = deptService.getDept(id); 212 + List<DeptDO> sysDeptDOS = deptService.getDeptList(reqVO);
306 // 断言 213 // 断言
307 - assertEquals(deptDO, dbDept); 214 + assertEquals(1, sysDeptDOS.size());
  215 + assertPojoEquals(dept, sysDeptDOS.get(0));
  216 + }
  217 +
  218 + @Test
  219 + public void testGetChildDeptList() {
  220 + // mock 数据(1 级别子节点)
  221 + DeptDO dept1 = randomPojo(DeptDO.class, o -> o.setName("1"));
  222 + deptMapper.insert(dept1);
  223 + DeptDO dept2 = randomPojo(DeptDO.class, o -> o.setName("2"));
  224 + deptMapper.insert(dept2);
  225 + // mock 数据(2 级子节点)
  226 + DeptDO dept1a = randomPojo(DeptDO.class, o -> o.setName("1-a").setParentId(dept1.getId()));
  227 + deptMapper.insert(dept1a);
  228 + DeptDO dept2a = randomPojo(DeptDO.class, o -> o.setName("2-a").setParentId(dept2.getId()));
  229 + deptMapper.insert(dept2a);
  230 + // 准备参数
  231 + Long id = dept1.getParentId();
  232 +
  233 + // 调用
  234 + List<DeptDO> result = deptService.getChildDeptList(id);
  235 + // 断言
  236 + assertEquals(result.size(), 2);
  237 + assertPojoEquals(dept1, result.get(0));
  238 + assertPojoEquals(dept1a, result.get(1));
  239 + }
  240 +
  241 + @Test
  242 + public void testGetChildDeptListFromCache() {
  243 + // mock 数据(1 级别子节点)
  244 + DeptDO dept1 = randomPojo(DeptDO.class, o -> o.setName("1"));
  245 + deptMapper.insert(dept1);
  246 + DeptDO dept2 = randomPojo(DeptDO.class, o -> o.setName("2"));
  247 + deptMapper.insert(dept2);
  248 + // mock 数据(2 级子节点)
  249 + DeptDO dept1a = randomPojo(DeptDO.class, o -> o.setName("1-a").setParentId(dept1.getId()));
  250 + deptMapper.insert(dept1a);
  251 + DeptDO dept2a = randomPojo(DeptDO.class, o -> o.setName("2-a").setParentId(dept2.getId()));
  252 + deptMapper.insert(dept2a);
  253 + // 准备参数
  254 + Long id = dept1.getParentId();
  255 +
  256 + // 调用
  257 + Set<Long> result = deptService.getChildDeptIdListFromCache(id);
  258 + // 断言
  259 + assertEquals(result.size(), 2);
  260 + assertTrue(result.contains(dept1.getId()));
  261 + assertTrue(result.contains(dept1a.getId()));
308 } 262 }
309 263
310 @Test 264 @Test
311 public void testValidateDeptList_success() { 265 public void testValidateDeptList_success() {
312 // mock 数据 266 // mock 数据
313 - DeptDO deptDO = randomDeptDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); 267 + DeptDO deptDO = randomPojo(DeptDO.class).setStatus(CommonStatusEnum.ENABLE.getStatus());
314 deptMapper.insert(deptDO); 268 deptMapper.insert(deptDO);
315 // 准备参数 269 // 准备参数
316 List<Long> ids = singletonList(deptDO.getId()); 270 List<Long> ids = singletonList(deptDO.getId());
@@ -331,7 +285,7 @@ public class DeptServiceImplTest extends BaseDbUnitTest { @@ -331,7 +285,7 @@ public class DeptServiceImplTest extends BaseDbUnitTest {
331 @Test 285 @Test
332 public void testValidateDeptList_notEnable() { 286 public void testValidateDeptList_notEnable() {
333 // mock 数据 287 // mock 数据
334 - DeptDO deptDO = randomDeptDO().setStatus(CommonStatusEnum.DISABLE.getStatus()); 288 + DeptDO deptDO = randomPojo(DeptDO.class).setStatus(CommonStatusEnum.DISABLE.getStatus());
335 deptMapper.insert(deptDO); 289 deptMapper.insert(deptDO);
336 // 准备参数 290 // 准备参数
337 List<Long> ids = singletonList(deptDO.getId()); 291 List<Long> ids = singletonList(deptDO.getId());
@@ -340,12 +294,4 @@ public class DeptServiceImplTest extends BaseDbUnitTest { @@ -340,12 +294,4 @@ public class DeptServiceImplTest extends BaseDbUnitTest {
340 assertServiceException(() -> deptService.validateDeptList(ids), DEPT_NOT_ENABLE, deptDO.getName()); 294 assertServiceException(() -> deptService.validateDeptList(ids), DEPT_NOT_ENABLE, deptDO.getName());
341 } 295 }
342 296
343 - @SafeVarargs  
344 - private static DeptDO randomDeptDO(Consumer<DeptDO>... consumers) {  
345 - Consumer<DeptDO> consumer = (o) -> {  
346 - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围  
347 - };  
348 - return randomPojo(DeptDO.class, ArrayUtils.append(consumer, consumers));  
349 - }  
350 -  
351 } 297 }
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java
@@ -7,14 +7,12 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccou @@ -7,14 +7,12 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccou
7 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountUpdateReqVO; 7 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountUpdateReqVO;
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 org.junit.jupiter.api.Test; 10 import org.junit.jupiter.api.Test;
12 import org.springframework.boot.test.mock.mockito.MockBean; 11 import org.springframework.boot.test.mock.mockito.MockBean;
13 import org.springframework.context.annotation.Import; 12 import org.springframework.context.annotation.Import;
14 13
15 import javax.annotation.Resource; 14 import javax.annotation.Resource;
16 import java.util.List; 15 import java.util.List;
17 -import java.util.Map;  
18 16
19 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; 17 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
20 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; 18 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
@@ -23,14 +21,13 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; @@ -23,14 +21,13 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
23 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_NOT_EXISTS;
24 import static org.junit.jupiter.api.Assertions.*; 22 import static org.junit.jupiter.api.Assertions.*;
25 import static org.mockito.ArgumentMatchers.eq; 23 import static org.mockito.ArgumentMatchers.eq;
26 -import static org.mockito.Mockito.verify;  
27 import static org.mockito.Mockito.when; 24 import static org.mockito.Mockito.when;
28 25
29 /** 26 /**
30 -* {@link MailAccountServiceImpl} 的单元测试类  
31 -*  
32 -* @author 芋道源码  
33 -*/ 27 + * {@link MailAccountServiceImpl} 的单元测试类
  28 + *
  29 + * @author 芋道源码
  30 + */
34 @Import(MailAccountServiceImpl.class) 31 @Import(MailAccountServiceImpl.class)
35 public class MailAccountServiceImplTest extends BaseDbUnitTest { 32 public class MailAccountServiceImplTest extends BaseDbUnitTest {
36 33
@@ -42,23 +39,6 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { @@ -42,23 +39,6 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest {
42 39
43 @MockBean 40 @MockBean
44 private MailTemplateService mailTemplateService; 41 private MailTemplateService mailTemplateService;
45 - @MockBean  
46 - private MailProducer mailProducer;  
47 -  
48 - @Test  
49 - public void testInitLocalCache() {  
50 - MailAccountDO accountDO1 = randomPojo(MailAccountDO.class);  
51 - mailAccountMapper.insert(accountDO1);  
52 - MailAccountDO accountDO02 = randomPojo(MailAccountDO.class);  
53 - mailAccountMapper.insert(accountDO02);  
54 -  
55 - // 调用  
56 - mailAccountService.initLocalCache();  
57 - // 断言 mailAccountCache 缓存  
58 - Map<Long, MailAccountDO> mailAccountCache = mailAccountService.getMailAccountCache();  
59 - assertPojoEquals(accountDO1, mailAccountCache.get(accountDO1.getId()));  
60 - assertPojoEquals(accountDO02, mailAccountCache.get(accountDO02.getId()));  
61 - }  
62 42
63 @Test 43 @Test
64 public void testCreateMailAccount_success() { 44 public void testCreateMailAccount_success() {
@@ -72,7 +52,6 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { @@ -72,7 +52,6 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest {
72 // 校验记录的属性是否正确 52 // 校验记录的属性是否正确
73 MailAccountDO mailAccount = mailAccountMapper.selectById(mailAccountId); 53 MailAccountDO mailAccount = mailAccountMapper.selectById(mailAccountId);
74 assertPojoEquals(reqVO, mailAccount); 54 assertPojoEquals(reqVO, mailAccount);
75 - verify(mailProducer).sendMailAccountRefreshMessage();  
76 } 55 }
77 56
78 @Test 57 @Test
@@ -91,7 +70,6 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { @@ -91,7 +70,6 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest {
91 // 校验是否更新正确 70 // 校验是否更新正确
92 MailAccountDO mailAccount = mailAccountMapper.selectById(reqVO.getId()); // 获取最新的 71 MailAccountDO mailAccount = mailAccountMapper.selectById(reqVO.getId()); // 获取最新的
93 assertPojoEquals(reqVO, mailAccount); 72 assertPojoEquals(reqVO, mailAccount);
94 - verify(mailProducer).sendMailAccountRefreshMessage();  
95 } 73 }
96 74
97 @Test 75 @Test
@@ -115,9 +93,8 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { @@ -115,9 +93,8 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest {
115 93
116 // 调用 94 // 调用
117 mailAccountService.deleteMailAccount(id); 95 mailAccountService.deleteMailAccount(id);
118 - // 校验数据不存在了  
119 - assertNull(mailAccountMapper.selectById(id));  
120 - verify(mailProducer).sendMailAccountRefreshMessage(); 96 + // 校验数据不存在了
  97 + assertNull(mailAccountMapper.selectById(id));
121 } 98 }
122 99
123 @Test 100 @Test
@@ -125,7 +102,6 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { @@ -125,7 +102,6 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest {
125 // mock 数据 102 // mock 数据
126 MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class); 103 MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class);
127 mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 104 mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据
128 - mailAccountService.initLocalCache();  
129 // 准备参数 105 // 准备参数
130 Long id = dbMailAccount.getId(); 106 Long id = dbMailAccount.getId();
131 107
@@ -146,27 +122,27 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { @@ -146,27 +122,27 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest {
146 122
147 @Test 123 @Test
148 public void testGetMailAccountPage() { 124 public void testGetMailAccountPage() {
149 - // mock 数据  
150 - MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class, o -> { // 等会查询到  
151 - o.setMail("768@qq.com");  
152 - o.setUsername("yunai");  
153 - });  
154 - mailAccountMapper.insert(dbMailAccount);  
155 - // 测试 mail 不匹配  
156 - mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setMail("788@qq.com")));  
157 - // 测试 username 不匹配  
158 - mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setUsername("tudou")));  
159 - // 准备参数  
160 - MailAccountPageReqVO reqVO = new MailAccountPageReqVO();  
161 - reqVO.setMail("768");  
162 - reqVO.setUsername("yu");  
163 -  
164 - // 调用  
165 - PageResult<MailAccountDO> pageResult = mailAccountService.getMailAccountPage(reqVO);  
166 - // 断言  
167 - assertEquals(1, pageResult.getTotal());  
168 - assertEquals(1, pageResult.getList().size());  
169 - assertPojoEquals(dbMailAccount, pageResult.getList().get(0)); 125 + // mock 数据
  126 + MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class, o -> { // 等会查询到
  127 + o.setMail("768@qq.com");
  128 + o.setUsername("yunai");
  129 + });
  130 + mailAccountMapper.insert(dbMailAccount);
  131 + // 测试 mail 不匹配
  132 + mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setMail("788@qq.com")));
  133 + // 测试 username 不匹配
  134 + mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setUsername("tudou")));
  135 + // 准备参数
  136 + MailAccountPageReqVO reqVO = new MailAccountPageReqVO();
  137 + reqVO.setMail("768");
  138 + reqVO.setUsername("yu");
  139 +
  140 + // 调用
  141 + PageResult<MailAccountDO> pageResult = mailAccountService.getMailAccountPage(reqVO);
  142 + // 断言
  143 + assertEquals(1, pageResult.getTotal());
  144 + assertEquals(1, pageResult.getList().size());
  145 + assertPojoEquals(dbMailAccount, pageResult.getList().get(0));
170 } 146 }
171 147
172 @Test 148 @Test
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java
@@ -25,10 +25,10 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; @@ -25,10 +25,10 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
25 import static org.junit.jupiter.api.Assertions.*; 25 import static org.junit.jupiter.api.Assertions.*;
26 26
27 /** 27 /**
28 -* {@link MailLogServiceImpl} 的单元测试类  
29 -*  
30 -* @author 芋道源码  
31 -*/ 28 + * {@link MailLogServiceImpl} 的单元测试类
  29 + *
  30 + * @author 芋道源码
  31 + */
32 @Import(MailLogServiceImpl.class) 32 @Import(MailLogServiceImpl.class)
33 public class MailLogServiceImplTest extends BaseDbUnitTest { 33 public class MailLogServiceImplTest extends BaseDbUnitTest {
34 34
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java
@@ -33,7 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @@ -33,7 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
33 import static org.mockito.ArgumentMatchers.eq; 33 import static org.mockito.ArgumentMatchers.eq;
34 import static org.mockito.Mockito.*; 34 import static org.mockito.Mockito.*;
35 35
36 -class MailSendServiceImplTest extends BaseMockitoUnitTest { 36 +public class MailSendServiceImplTest extends BaseMockitoUnitTest {
37 37
38 @InjectMocks 38 @InjectMocks
39 private MailSendServiceImpl mailSendService; 39 private MailSendServiceImpl mailSendService;
@@ -278,16 +278,17 @@ class MailSendServiceImplTest extends BaseMockitoUnitTest { @@ -278,16 +278,17 @@ class MailSendServiceImplTest extends BaseMockitoUnitTest {
278 278
279 // mock 方法(发送邮件) 279 // mock 方法(发送邮件)
280 String messageId = randomString(); 280 String messageId = randomString();
281 - mailUtilMock.when(() -> MailUtil.send(argThat(mailAccount -> {  
282 - assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom());  
283 - assertTrue(mailAccount.isAuth());  
284 - assertEquals(account.getUsername(), mailAccount.getUser());  
285 - assertEquals(account.getPassword(), mailAccount.getPass());  
286 - assertEquals(account.getHost(), mailAccount.getHost());  
287 - assertEquals(account.getPort(), mailAccount.getPort());  
288 - assertEquals(account.getSslEnable(), mailAccount.isSslEnable());  
289 - return true;  
290 - }), eq(message.getMail()), eq(message.getTitle()), eq(message.getContent()), eq(true))) 281 + mailUtilMock.when(() -> MailUtil.send(
  282 + argThat(mailAccount -> {
  283 + assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom());
  284 + assertTrue(mailAccount.isAuth());
  285 + assertEquals(account.getUsername(), mailAccount.getUser());
  286 + assertEquals(account.getPassword(), mailAccount.getPass());
  287 + assertEquals(account.getHost(), mailAccount.getHost());
  288 + assertEquals(account.getPort(), mailAccount.getPort());
  289 + assertEquals(account.getSslEnable(), mailAccount.isSslEnable());
  290 + return true;
  291 + }), eq(message.getMail()), eq(message.getTitle()), eq(message.getContent()), eq(true)))
291 .thenReturn(messageId); 292 .thenReturn(messageId);
292 293
293 // 调用 294 // 调用
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java
@@ -8,9 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemp @@ -8,9 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemp
8 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateUpdateReqVO; 8 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateUpdateReqVO;
9 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; 9 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
10 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper; 10 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
11 -import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;  
12 import org.junit.jupiter.api.Test; 11 import org.junit.jupiter.api.Test;
13 -import org.springframework.boot.test.mock.mockito.MockBean;  
14 import org.springframework.context.annotation.Import; 12 import org.springframework.context.annotation.Import;
15 13
16 import javax.annotation.Resource; 14 import javax.annotation.Resource;
@@ -27,13 +25,12 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId @@ -27,13 +25,12 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId
27 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; 25 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
28 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS; 26 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS;
29 import static org.junit.jupiter.api.Assertions.*; 27 import static org.junit.jupiter.api.Assertions.*;
30 -import static org.mockito.Mockito.verify;  
31 28
32 /** 29 /**
33 -* {@link MailTemplateServiceImpl} 的单元测试类  
34 -*  
35 -* @author 芋道源码  
36 -*/ 30 + * {@link MailTemplateServiceImpl} 的单元测试类
  31 + *
  32 + * @author 芋道源码
  33 + */
37 @Import(MailTemplateServiceImpl.class) 34 @Import(MailTemplateServiceImpl.class)
38 public class MailTemplateServiceImplTest extends BaseDbUnitTest { 35 public class MailTemplateServiceImplTest extends BaseDbUnitTest {
39 36
@@ -43,24 +40,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @@ -43,24 +40,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest {
43 @Resource 40 @Resource
44 private MailTemplateMapper mailTemplateMapper; 41 private MailTemplateMapper mailTemplateMapper;
45 42
46 - @MockBean  
47 - private MailProducer mailProducer;  
48 -  
49 - @Test  
50 - public void testInitLocalCache() {  
51 - MailTemplateDO templateDO01 = randomPojo(MailTemplateDO.class);  
52 - mailTemplateMapper.insert(templateDO01);  
53 - MailTemplateDO templateDO02 = randomPojo(MailTemplateDO.class);  
54 - mailTemplateMapper.insert(templateDO02);  
55 -  
56 - // 调用  
57 - mailTemplateService.initLocalCache();  
58 - // 断言 mailTemplateCache 缓存  
59 - Map<String, MailTemplateDO> mailTemplateCache = mailTemplateService.getMailTemplateCache();  
60 - assertPojoEquals(templateDO01, mailTemplateCache.get(templateDO01.getCode()));  
61 - assertPojoEquals(templateDO02, mailTemplateCache.get(templateDO02.getCode()));  
62 - }  
63 -  
64 @Test 43 @Test
65 public void testCreateMailTemplate_success() { 44 public void testCreateMailTemplate_success() {
66 // 准备参数 45 // 准备参数
@@ -73,7 +52,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @@ -73,7 +52,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest {
73 // 校验记录的属性是否正确 52 // 校验记录的属性是否正确
74 MailTemplateDO mailTemplate = mailTemplateMapper.selectById(mailTemplateId); 53 MailTemplateDO mailTemplate = mailTemplateMapper.selectById(mailTemplateId);
75 assertPojoEquals(reqVO, mailTemplate); 54 assertPojoEquals(reqVO, mailTemplate);
76 - verify(mailProducer).sendMailTemplateRefreshMessage();  
77 } 55 }
78 56
79 @Test 57 @Test
@@ -91,7 +69,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @@ -91,7 +69,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest {
91 // 校验是否更新正确 69 // 校验是否更新正确
92 MailTemplateDO mailTemplate = mailTemplateMapper.selectById(reqVO.getId()); // 获取最新的 70 MailTemplateDO mailTemplate = mailTemplateMapper.selectById(reqVO.getId()); // 获取最新的
93 assertPojoEquals(reqVO, mailTemplate); 71 assertPojoEquals(reqVO, mailTemplate);
94 - verify(mailProducer).sendMailTemplateRefreshMessage();  
95 } 72 }
96 73
97 @Test 74 @Test
@@ -115,7 +92,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @@ -115,7 +92,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest {
115 mailTemplateService.deleteMailTemplate(id); 92 mailTemplateService.deleteMailTemplate(id);
116 // 校验数据不存在了 93 // 校验数据不存在了
117 assertNull(mailTemplateMapper.selectById(id)); 94 assertNull(mailTemplateMapper.selectById(id));
118 - verify(mailProducer).sendMailTemplateRefreshMessage();  
119 } 95 }
120 96
121 @Test 97 @Test
@@ -129,39 +105,39 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @@ -129,39 +105,39 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest {
129 105
130 @Test 106 @Test
131 public void testGetMailTemplatePage() { 107 public void testGetMailTemplatePage() {
132 - // mock 数据  
133 - MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class, o -> { // 等会查询到  
134 - o.setName("源码");  
135 - o.setCode("test_01");  
136 - o.setAccountId(1L);  
137 - o.setStatus(CommonStatusEnum.ENABLE.getStatus());  
138 - o.setCreateTime(buildTime(2023, 2, 3));  
139 - });  
140 - mailTemplateMapper.insert(dbMailTemplate);  
141 - // 测试 name 不匹配  
142 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setName("芋道")));  
143 - // 测试 code 不匹配  
144 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCode("test_02")));  
145 - // 测试 accountId 不匹配  
146 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setAccountId(2L)));  
147 - // 测试 status 不匹配  
148 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));  
149 - // 测试 createTime 不匹配  
150 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCreateTime(buildTime(2023, 1, 5))));  
151 - // 准备参数  
152 - MailTemplatePageReqVO reqVO = new MailTemplatePageReqVO();  
153 - reqVO.setName("源");  
154 - reqVO.setCode("est_01");  
155 - reqVO.setAccountId(1L);  
156 - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());  
157 - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 5));  
158 -  
159 - // 调用  
160 - PageResult<MailTemplateDO> pageResult = mailTemplateService.getMailTemplatePage(reqVO);  
161 - // 断言  
162 - assertEquals(1, pageResult.getTotal());  
163 - assertEquals(1, pageResult.getList().size());  
164 - assertPojoEquals(dbMailTemplate, pageResult.getList().get(0)); 108 + // mock 数据
  109 + MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class, o -> { // 等会查询到
  110 + o.setName("源码");
  111 + o.setCode("test_01");
  112 + o.setAccountId(1L);
  113 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  114 + o.setCreateTime(buildTime(2023, 2, 3));
  115 + });
  116 + mailTemplateMapper.insert(dbMailTemplate);
  117 + // 测试 name 不匹配
  118 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setName("芋道")));
  119 + // 测试 code 不匹配
  120 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCode("test_02")));
  121 + // 测试 accountId 不匹配
  122 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setAccountId(2L)));
  123 + // 测试 status 不匹配
  124 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
  125 + // 测试 createTime 不匹配
  126 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCreateTime(buildTime(2023, 1, 5))));
  127 + // 准备参数
  128 + MailTemplatePageReqVO reqVO = new MailTemplatePageReqVO();
  129 + reqVO.setName("源");
  130 + reqVO.setCode("est_01");
  131 + reqVO.setAccountId(1L);
  132 + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
  133 + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 5));
  134 +
  135 + // 调用
  136 + PageResult<MailTemplateDO> pageResult = mailTemplateService.getMailTemplatePage(reqVO);
  137 + // 断言
  138 + assertEquals(1, pageResult.getTotal());
  139 + assertEquals(1, pageResult.getList().size());
  140 + assertPojoEquals(dbMailTemplate, pageResult.getList().get(0));
165 } 141 }
166 142
167 @Test 143 @Test
@@ -199,7 +175,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @@ -199,7 +175,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest {
199 // mock 数据 175 // mock 数据
200 MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); 176 MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class);
201 mailTemplateMapper.insert(dbMailTemplate); 177 mailTemplateMapper.insert(dbMailTemplate);
202 - mailTemplateService.initLocalCache();  
203 // 准备参数 178 // 准备参数
204 String code = dbMailTemplate.getCode(); 179 String code = dbMailTemplate.getCode();
205 180
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java
@@ -8,9 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.Notify @@ -8,9 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.Notify
8 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;
9 import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; 9 import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
10 import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper; 10 import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper;
11 -import cn.iocoder.yudao.module.system.mq.producer.notify.NotifyProducer;  
12 import org.junit.jupiter.api.Test; 11 import org.junit.jupiter.api.Test;
13 -import org.springframework.boot.test.mock.mockito.MockBean;  
14 import org.springframework.context.annotation.Import; 12 import org.springframework.context.annotation.Import;
15 13
16 import javax.annotation.Resource; 14 import javax.annotation.Resource;
@@ -25,13 +23,12 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic @@ -25,13 +23,12 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic
25 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; 23 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
26 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS; 24 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS;
27 import static org.junit.jupiter.api.Assertions.*; 25 import static org.junit.jupiter.api.Assertions.*;
28 -import static org.mockito.Mockito.verify;  
29 26
30 /** 27 /**
31 -* {@link NotifyTemplateServiceImpl} 的单元测试类  
32 -*  
33 -* @author 芋道源码  
34 -*/ 28 + * {@link NotifyTemplateServiceImpl} 的单元测试类
  29 + *
  30 + * @author 芋道源码
  31 + */
35 @Import(NotifyTemplateServiceImpl.class) 32 @Import(NotifyTemplateServiceImpl.class)
36 public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { 33 public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
37 34
@@ -41,9 +38,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { @@ -41,9 +38,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
41 @Resource 38 @Resource
42 private NotifyTemplateMapper notifyTemplateMapper; 39 private NotifyTemplateMapper notifyTemplateMapper;
43 40
44 - @MockBean  
45 - private NotifyProducer notifyProducer;  
46 -  
47 @Test 41 @Test
48 public void testCreateNotifyTemplate_success() { 42 public void testCreateNotifyTemplate_success() {
49 // 准备参数 43 // 准备参数
@@ -57,7 +51,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { @@ -57,7 +51,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
57 // 校验记录的属性是否正确 51 // 校验记录的属性是否正确
58 NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(notifyTemplateId); 52 NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(notifyTemplateId);
59 assertPojoEquals(reqVO, notifyTemplate); 53 assertPojoEquals(reqVO, notifyTemplate);
60 - verify(notifyProducer).sendNotifyTemplateRefreshMessage();  
61 } 54 }
62 55
63 @Test 56 @Test
@@ -76,7 +69,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { @@ -76,7 +69,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
76 // 校验是否更新正确 69 // 校验是否更新正确
77 NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(reqVO.getId()); // 获取最新的 70 NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(reqVO.getId()); // 获取最新的
78 assertPojoEquals(reqVO, notifyTemplate); 71 assertPojoEquals(reqVO, notifyTemplate);
79 - verify(notifyProducer).sendNotifyTemplateRefreshMessage();  
80 } 72 }
81 73
82 @Test 74 @Test
@@ -98,9 +90,8 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { @@ -98,9 +90,8 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
98 90
99 // 调用 91 // 调用
100 notifyTemplateService.deleteNotifyTemplate(id); 92 notifyTemplateService.deleteNotifyTemplate(id);
101 - // 校验数据不存在了  
102 - assertNull(notifyTemplateMapper.selectById(id));  
103 - verify(notifyProducer).sendNotifyTemplateRefreshMessage(); 93 + // 校验数据不存在了
  94 + assertNull(notifyTemplateMapper.selectById(id));
104 } 95 }
105 96
106 @Test 97 @Test
@@ -114,35 +105,35 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { @@ -114,35 +105,35 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
114 105
115 @Test 106 @Test
116 public void testGetNotifyTemplatePage() { 107 public void testGetNotifyTemplatePage() {
117 - // mock 数据  
118 - NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class, o -> { // 等会查询到  
119 - o.setName("芋头");  
120 - o.setCode("test_01");  
121 - o.setStatus(CommonStatusEnum.ENABLE.getStatus());  
122 - o.setCreateTime(buildTime(2022, 2, 3));  
123 - });  
124 - notifyTemplateMapper.insert(dbNotifyTemplate);  
125 - // 测试 name 不匹配  
126 - notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setName("投")));  
127 - // 测试 code 不匹配  
128 - notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCode("test_02")));  
129 - // 测试 status 不匹配  
130 - notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));  
131 - // 测试 createTime 不匹配  
132 - notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCreateTime(buildTime(2022, 1, 5))));  
133 - // 准备参数  
134 - NotifyTemplatePageReqVO reqVO = new NotifyTemplatePageReqVO();  
135 - reqVO.setName("芋");  
136 - reqVO.setCode("est_01");  
137 - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());  
138 - reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 5));  
139 -  
140 - // 调用  
141 - PageResult<NotifyTemplateDO> pageResult = notifyTemplateService.getNotifyTemplatePage(reqVO);  
142 - // 断言  
143 - assertEquals(1, pageResult.getTotal());  
144 - assertEquals(1, pageResult.getList().size());  
145 - assertPojoEquals(dbNotifyTemplate, pageResult.getList().get(0)); 108 + // mock 数据
  109 + NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class, o -> { // 等会查询到
  110 + o.setName("芋头");
  111 + o.setCode("test_01");
  112 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  113 + o.setCreateTime(buildTime(2022, 2, 3));
  114 + });
  115 + notifyTemplateMapper.insert(dbNotifyTemplate);
  116 + // 测试 name 不匹配
  117 + notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setName("投")));
  118 + // 测试 code 不匹配
  119 + notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCode("test_02")));
  120 + // 测试 status 不匹配
  121 + notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
  122 + // 测试 createTime 不匹配
  123 + notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCreateTime(buildTime(2022, 1, 5))));
  124 + // 准备参数
  125 + NotifyTemplatePageReqVO reqVO = new NotifyTemplatePageReqVO();
  126 + reqVO.setName("芋");
  127 + reqVO.setCode("est_01");
  128 + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
  129 + reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 5));
  130 +
  131 + // 调用
  132 + PageResult<NotifyTemplateDO> pageResult = notifyTemplateService.getNotifyTemplatePage(reqVO);
  133 + // 断言
  134 + assertEquals(1, pageResult.getTotal());
  135 + assertEquals(1, pageResult.getList().size());
  136 + assertPojoEquals(dbNotifyTemplate, pageResult.getList().get(0));
146 } 137 }
147 138
148 @Test 139 @Test
@@ -164,7 +155,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { @@ -164,7 +155,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
164 // mock 数据 155 // mock 数据
165 NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class); 156 NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class);
166 notifyTemplateMapper.insert(dbNotifyTemplate); 157 notifyTemplateMapper.insert(dbNotifyTemplate);
167 - notifyTemplateService.initLocalCache();  
168 // 准备参数 158 // 准备参数
169 String code = dbNotifyTemplate.getCode(); 159 String code = dbNotifyTemplate.getCode();
170 160
@@ -173,7 +163,7 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { @@ -173,7 +163,7 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
173 // 断言 163 // 断言
174 assertPojoEquals(dbNotifyTemplate, notifyTemplate); 164 assertPojoEquals(dbNotifyTemplate, notifyTemplate);
175 } 165 }
176 - 166 +
177 @Test 167 @Test
178 public void testFormatNotifyTemplateContent() { 168 public void testFormatNotifyTemplateContent() {
179 // 准备参数 169 // 准备参数
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImplTest.java
1 package cn.iocoder.yudao.module.system.service.oauth2; 1 package cn.iocoder.yudao.module.system.service.oauth2;
2 2
3 -import cn.hutool.core.map.MapUtil; 3 +import cn.hutool.extra.spring.SpringUtil;
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.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; 6 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
@@ -9,14 +9,12 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2Cl @@ -9,14 +9,12 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2Cl
9 import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; 9 import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO;
10 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; 10 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO;
11 import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper; 11 import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper;
12 -import cn.iocoder.yudao.module.system.mq.producer.auth.OAuth2ClientProducer;  
13 import org.junit.jupiter.api.Test; 12 import org.junit.jupiter.api.Test;
14 -import org.springframework.boot.test.mock.mockito.MockBean; 13 +import org.mockito.MockedStatic;
15 import org.springframework.context.annotation.Import; 14 import org.springframework.context.annotation.Import;
16 15
17 import javax.annotation.Resource; 16 import javax.annotation.Resource;
18 import java.util.Collections; 17 import java.util.Collections;
19 -import java.util.Map;  
20 18
21 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; 19 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
22 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; 20 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
@@ -24,13 +22,14 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic @@ -24,13 +22,14 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic
24 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; 22 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
25 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; 23 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
26 import static org.junit.jupiter.api.Assertions.*; 24 import static org.junit.jupiter.api.Assertions.*;
27 -import static org.mockito.Mockito.verify; 25 +import static org.mockito.ArgumentMatchers.eq;
  26 +import static org.mockito.Mockito.mockStatic;
28 27
29 /** 28 /**
30 -* {@link OAuth2ClientServiceImpl} 的单元测试类  
31 -*  
32 -* @author 芋道源码  
33 -*/ 29 + * {@link OAuth2ClientServiceImpl} 的单元测试类
  30 + *
  31 + * @author 芋道源码
  32 + */
34 @Import(OAuth2ClientServiceImpl.class) 33 @Import(OAuth2ClientServiceImpl.class)
35 public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { 34 public class OAuth2ClientServiceImplTest extends BaseDbUnitTest {
36 35
@@ -40,26 +39,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { @@ -40,26 +39,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest {
40 @Resource 39 @Resource
41 private OAuth2ClientMapper oauth2ClientMapper; 40 private OAuth2ClientMapper oauth2ClientMapper;
42 41
43 - @MockBean  
44 - private OAuth2ClientProducer oauth2ClientProducer;  
45 -  
46 - @Test  
47 - public void testInitLocalCache() {  
48 - // mock 数据  
49 - OAuth2ClientDO clientDO1 = randomPojo(OAuth2ClientDO.class);  
50 - oauth2ClientMapper.insert(clientDO1);  
51 - OAuth2ClientDO clientDO2 = randomPojo(OAuth2ClientDO.class);  
52 - oauth2ClientMapper.insert(clientDO2);  
53 -  
54 - // 调用  
55 - oauth2ClientService.initLocalCache();  
56 - // 断言 clientCache 缓存  
57 - Map<String, OAuth2ClientDO> clientCache = oauth2ClientService.getClientCache();  
58 - assertEquals(2, clientCache.size());  
59 - assertPojoEquals(clientDO1, clientCache.get(clientDO1.getClientId()));  
60 - assertPojoEquals(clientDO2, clientCache.get(clientDO2.getClientId()));  
61 - }  
62 -  
63 @Test 42 @Test
64 public void testCreateOAuth2Client_success() { 43 public void testCreateOAuth2Client_success() {
65 // 准备参数 44 // 准备参数
@@ -73,7 +52,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { @@ -73,7 +52,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest {
73 // 校验记录的属性是否正确 52 // 校验记录的属性是否正确
74 OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(oauth2ClientId); 53 OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(oauth2ClientId);
75 assertPojoEquals(reqVO, oAuth2Client); 54 assertPojoEquals(reqVO, oAuth2Client);
76 - verify(oauth2ClientProducer).sendOAuth2ClientRefreshMessage();  
77 } 55 }
78 56
79 @Test 57 @Test
@@ -92,7 +70,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { @@ -92,7 +70,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest {
92 // 校验是否更新正确 70 // 校验是否更新正确
93 OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(reqVO.getId()); // 获取最新的 71 OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(reqVO.getId()); // 获取最新的
94 assertPojoEquals(reqVO, oAuth2Client); 72 assertPojoEquals(reqVO, oAuth2Client);
95 - verify(oauth2ClientProducer).sendOAuth2ClientRefreshMessage();  
96 } 73 }
97 74
98 @Test 75 @Test
@@ -116,7 +93,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { @@ -116,7 +93,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest {
116 oauth2ClientService.deleteOAuth2Client(id); 93 oauth2ClientService.deleteOAuth2Client(id);
117 // 校验数据不存在了 94 // 校验数据不存在了
118 assertNull(oauth2ClientMapper.selectById(id)); 95 assertNull(oauth2ClientMapper.selectById(id));
119 - verify(oauth2ClientProducer).sendOAuth2ClientRefreshMessage();  
120 } 96 }
121 97
122 @Test 98 @Test
@@ -167,62 +143,78 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { @@ -167,62 +143,78 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest {
167 } 143 }
168 144
169 @Test 145 @Test
  146 + public void testGetOAuth2ClientFromCache() {
  147 + // mock 数据
  148 + OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class);
  149 + oauth2ClientMapper.insert(clientDO);
  150 + // 准备参数
  151 + String clientId = clientDO.getClientId();
  152 +
  153 + // 调用,并断言
  154 + OAuth2ClientDO dbClientDO = oauth2ClientService.getOAuth2ClientFromCache(clientId);
  155 + assertPojoEquals(clientDO, dbClientDO);
  156 + }
  157 +
  158 + @Test
170 public void testGetOAuth2ClientPage() { 159 public void testGetOAuth2ClientPage() {
171 - // mock 数据  
172 - OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class, o -> { // 等会查询到  
173 - o.setName("潜龙");  
174 - o.setStatus(CommonStatusEnum.ENABLE.getStatus());  
175 - });  
176 - oauth2ClientMapper.insert(dbOAuth2Client);  
177 - // 测试 name 不匹配  
178 - oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setName("凤凰")));  
179 - // 测试 status 不匹配  
180 - oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));  
181 - // 准备参数  
182 - OAuth2ClientPageReqVO reqVO = new OAuth2ClientPageReqVO();  
183 - reqVO.setName("龙");  
184 - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());  
185 -  
186 - // 调用  
187 - PageResult<OAuth2ClientDO> pageResult = oauth2ClientService.getOAuth2ClientPage(reqVO);  
188 - // 断言  
189 - assertEquals(1, pageResult.getTotal());  
190 - assertEquals(1, pageResult.getList().size());  
191 - assertPojoEquals(dbOAuth2Client, pageResult.getList().get(0)); 160 + // mock 数据
  161 + OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class, o -> { // 等会查询到
  162 + o.setName("潜龙");
  163 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  164 + });
  165 + oauth2ClientMapper.insert(dbOAuth2Client);
  166 + // 测试 name 不匹配
  167 + oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setName("凤凰")));
  168 + // 测试 status 不匹配
  169 + oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
  170 + // 准备参数
  171 + OAuth2ClientPageReqVO reqVO = new OAuth2ClientPageReqVO();
  172 + reqVO.setName("龙");
  173 + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
  174 +
  175 + // 调用
  176 + PageResult<OAuth2ClientDO> pageResult = oauth2ClientService.getOAuth2ClientPage(reqVO);
  177 + // 断言
  178 + assertEquals(1, pageResult.getTotal());
  179 + assertEquals(1, pageResult.getList().size());
  180 + assertPojoEquals(dbOAuth2Client, pageResult.getList().get(0));
192 } 181 }
193 182
194 @Test 183 @Test
195 public void testValidOAuthClientFromCache() { 184 public void testValidOAuthClientFromCache() {
196 - // mock 方法  
197 - OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("default")  
198 - .setStatus(CommonStatusEnum.ENABLE.getStatus());  
199 - OAuth2ClientDO client02 = randomPojo(OAuth2ClientDO.class).setClientId("disable")  
200 - .setStatus(CommonStatusEnum.DISABLE.getStatus());  
201 - Map<String, OAuth2ClientDO> clientCache = MapUtil.<String, OAuth2ClientDO>builder()  
202 - .put(client.getClientId(), client)  
203 - .put(client02.getClientId(), client02).build();  
204 - oauth2ClientService.setClientCache(clientCache);  
205 -  
206 - // 调用,并断言  
207 - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache(randomString(),  
208 - null, null, null, null), OAUTH2_CLIENT_NOT_EXISTS);  
209 - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("disable",  
210 - null, null, null, null), OAUTH2_CLIENT_DISABLE);  
211 - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default",  
212 - randomString(), null, null, null), OAUTH2_CLIENT_CLIENT_SECRET_ERROR);  
213 - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default",  
214 - null, randomString(), null, null), OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS);  
215 - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default",  
216 - null, null, Collections.singleton(randomString()), null), OAUTH2_CLIENT_SCOPE_OVER);  
217 - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default",  
218 - null, null, null, "test"), OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH, "test");  
219 - // 成功调用(1:参数完整)  
220 - OAuth2ClientDO result = oauth2ClientService.validOAuthClientFromCache(client.getClientId(), client.getSecret(),  
221 - client.getAuthorizedGrantTypes().get(0), client.getScopes(), client.getRedirectUris().get(0));  
222 - assertPojoEquals(client, result);  
223 - // 成功调用(2:只有 clientId 参数)  
224 - result = oauth2ClientService.validOAuthClientFromCache(client.getClientId());  
225 - assertPojoEquals(client, result); 185 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  186 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(OAuth2ClientServiceImpl.class)))
  187 + .thenReturn(oauth2ClientService);
  188 +
  189 + // mock 方法
  190 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("default")
  191 + .setStatus(CommonStatusEnum.ENABLE.getStatus());
  192 + oauth2ClientMapper.insert(client);
  193 + OAuth2ClientDO client02 = randomPojo(OAuth2ClientDO.class).setClientId("disable")
  194 + .setStatus(CommonStatusEnum.DISABLE.getStatus());
  195 + oauth2ClientMapper.insert(client02);
  196 +
  197 + // 调用,并断言
  198 + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache(randomString(),
  199 + null, null, null, null), OAUTH2_CLIENT_NOT_EXISTS);
  200 + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("disable",
  201 + null, null, null, null), OAUTH2_CLIENT_DISABLE);
  202 + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default",
  203 + randomString(), null, null, null), OAUTH2_CLIENT_CLIENT_SECRET_ERROR);
  204 + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default",
  205 + null, randomString(), null, null), OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS);
  206 + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default",
  207 + null, null, Collections.singleton(randomString()), null), OAUTH2_CLIENT_SCOPE_OVER);
  208 + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default",
  209 + null, null, null, "test"), OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH, "test");
  210 + // 成功调用(1:参数完整)
  211 + OAuth2ClientDO result = oauth2ClientService.validOAuthClientFromCache(client.getClientId(), client.getSecret(),
  212 + client.getAuthorizedGrantTypes().get(0), client.getScopes(), client.getRedirectUris().get(0));
  213 + assertPojoEquals(client, result);
  214 + // 成功调用(2:只有 clientId 参数)
  215 + result = oauth2ClientService.validOAuthClientFromCache(client.getClientId());
  216 + assertPojoEquals(client, result);
  217 + }
226 } 218 }
227 219
228 } 220 }
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java
@@ -8,10 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuUp @@ -8,10 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuUp
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.mysql.permission.MenuMapper; 9 import cn.iocoder.yudao.module.system.dal.mysql.permission.MenuMapper;
10 import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; 10 import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;
11 -import cn.iocoder.yudao.module.system.mq.producer.permission.MenuProducer;  
12 import cn.iocoder.yudao.module.system.service.tenant.TenantService; 11 import cn.iocoder.yudao.module.system.service.tenant.TenantService;
13 -import com.google.common.collect.LinkedListMultimap;  
14 -import com.google.common.collect.Multimap;  
15 import org.junit.jupiter.api.Test; 12 import org.junit.jupiter.api.Test;
16 import org.springframework.boot.test.mock.mockito.MockBean; 13 import org.springframework.boot.test.mock.mockito.MockBean;
17 import org.springframework.context.annotation.Import; 14 import org.springframework.context.annotation.Import;
@@ -26,8 +23,6 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic @@ -26,8 +23,6 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic
26 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; 23 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
27 import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT; 24 import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT;
28 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; 25 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
29 -import static java.util.Arrays.asList;  
30 -import static java.util.Collections.singletonList;  
31 import static org.junit.jupiter.api.Assertions.assertEquals; 26 import static org.junit.jupiter.api.Assertions.assertEquals;
32 import static org.junit.jupiter.api.Assertions.assertNull; 27 import static org.junit.jupiter.api.Assertions.assertNull;
33 import static org.mockito.ArgumentMatchers.argThat; 28 import static org.mockito.ArgumentMatchers.argThat;
@@ -46,35 +41,12 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @@ -46,35 +41,12 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
46 @MockBean 41 @MockBean
47 private PermissionService permissionService; 42 private PermissionService permissionService;
48 @MockBean 43 @MockBean
49 - private MenuProducer menuProducer;  
50 - @MockBean  
51 private TenantService tenantService; 44 private TenantService tenantService;
52 45
53 @Test 46 @Test
54 - public void testInitLocalCache_success() {  
55 - MenuDO menuDO1 = randomPojo(MenuDO.class);  
56 - menuMapper.insert(menuDO1);  
57 - MenuDO menuDO2 = randomPojo(MenuDO.class);  
58 - menuMapper.insert(menuDO2);  
59 -  
60 - // 调用  
61 - menuService.initLocalCache();  
62 - // 校验 menuCache 缓存  
63 - Map<Long, MenuDO> menuCache = menuService.getMenuCache();  
64 - assertEquals(2, menuCache.size());  
65 - assertPojoEquals(menuDO1, menuCache.get(menuDO1.getId()));  
66 - assertPojoEquals(menuDO2, menuCache.get(menuDO2.getId()));  
67 - // 校验 permissionMenuCache 缓存  
68 - Multimap<String, MenuDO> permissionMenuCache = menuService.getPermissionMenuCache();  
69 - assertEquals(2, permissionMenuCache.size());  
70 - assertPojoEquals(menuDO1, permissionMenuCache.get(menuDO1.getPermission()));  
71 - assertPojoEquals(menuDO2, permissionMenuCache.get(menuDO2.getPermission()));  
72 - }  
73 -  
74 - @Test  
75 public void testCreateMenu_success() { 47 public void testCreateMenu_success() {
76 // mock 数据(构造父菜单) 48 // mock 数据(构造父菜单)
77 - MenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, 49 + MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU,
78 "parent", 0L); 50 "parent", 0L);
79 menuMapper.insert(menuDO); 51 menuMapper.insert(menuDO);
80 Long parentId = menuDO.getId(); 52 Long parentId = menuDO.getId();
@@ -89,14 +61,12 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @@ -89,14 +61,12 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
89 // 校验记录的属性是否正确 61 // 校验记录的属性是否正确
90 MenuDO dbMenu = menuMapper.selectById(menuId); 62 MenuDO dbMenu = menuMapper.selectById(menuId);
91 assertPojoEquals(reqVO, dbMenu); 63 assertPojoEquals(reqVO, dbMenu);
92 - // 校验调用  
93 - verify(menuProducer).sendMenuRefreshMessage();  
94 } 64 }
95 65
96 @Test 66 @Test
97 public void testUpdateMenu_success() { 67 public void testUpdateMenu_success() {
98 // mock 数据(构造父子菜单) 68 // mock 数据(构造父子菜单)
99 - MenuDO sonMenuDO = initParentAndSonMenu(); 69 + MenuDO sonMenuDO = createParentAndSonMenu();
100 Long sonId = sonMenuDO.getId(); 70 Long sonId = sonMenuDO.getId();
101 // 准备参数 71 // 准备参数
102 MenuUpdateReqVO reqVO = randomPojo(MenuUpdateReqVO.class, o -> { 72 MenuUpdateReqVO reqVO = randomPojo(MenuUpdateReqVO.class, o -> {
@@ -111,8 +81,6 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @@ -111,8 +81,6 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
111 // 校验记录的属性是否正确 81 // 校验记录的属性是否正确
112 MenuDO dbMenu = menuMapper.selectById(sonId); 82 MenuDO dbMenu = menuMapper.selectById(sonId);
113 assertPojoEquals(reqVO, dbMenu); 83 assertPojoEquals(reqVO, dbMenu);
114 - // 校验调用  
115 - verify(menuProducer).sendMenuRefreshMessage();  
116 } 84 }
117 85
118 @Test 86 @Test
@@ -137,7 +105,6 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @@ -137,7 +105,6 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
137 MenuDO dbMenuDO = menuMapper.selectById(id); 105 MenuDO dbMenuDO = menuMapper.selectById(id);
138 assertNull(dbMenuDO); 106 assertNull(dbMenuDO);
139 verify(permissionService).processMenuDeleted(id); 107 verify(permissionService).processMenuDeleted(id);
140 - verify(menuProducer).sendMenuRefreshMessage();  
141 } 108 }
142 109
143 @Test 110 @Test
@@ -149,7 +116,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @@ -149,7 +116,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
149 @Test 116 @Test
150 public void testDeleteMenu_existChildren() { 117 public void testDeleteMenu_existChildren() {
151 // mock 数据(构造父子菜单) 118 // mock 数据(构造父子菜单)
152 - MenuDO sonMenu = initParentAndSonMenu(); 119 + MenuDO sonMenu = createParentAndSonMenu();
153 // 准备参数 120 // 准备参数
154 Long parentId = sonMenu.getParentId(); 121 Long parentId = sonMenu.getParentId();
155 122
@@ -219,85 +186,6 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @@ -219,85 +186,6 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
219 } 186 }
220 187
221 @Test 188 @Test
222 - public void testListMenusFromCache_withoutId() {  
223 - // mock 缓存  
224 - Map<Long, MenuDO> menuCache = new HashMap<>();  
225 - // 可被匹配  
226 - MenuDO menuDO = randomPojo(MenuDO.class, o -> o.setId(1L)  
227 - .setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus()));  
228 - menuCache.put(menuDO.getId(), menuDO);  
229 - // 测试 type 不匹配  
230 - menuCache.put(3L, randomPojo(MenuDO.class, o -> o.setId(3L)  
231 - .setType(MenuTypeEnum.BUTTON.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus())));  
232 - // 测试 status 不匹配  
233 - menuCache.put(4L, randomPojo(MenuDO.class, o -> o.setId(4L)  
234 - .setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.DISABLE.getStatus())));  
235 - menuService.setMenuCache(menuCache);  
236 - // 准备参数  
237 - Collection<Integer> menuTypes = singletonList(MenuTypeEnum.MENU.getType());  
238 - Collection<Integer> menusStatuses = singletonList(CommonStatusEnum.ENABLE.getStatus());  
239 -  
240 - // 调用  
241 - List<MenuDO> list = menuService.getMenuListFromCache(menuTypes, menusStatuses);  
242 - // 断言  
243 - assertEquals(1, list.size());  
244 - assertPojoEquals(menuDO, list.get(0));  
245 - }  
246 -  
247 - @Test  
248 - public void testListMenusFromCache_withId() {  
249 - // mock 缓存  
250 - Map<Long, MenuDO> menuCache = new HashMap<>();  
251 - // 可被匹配  
252 - MenuDO menuDO = randomPojo(MenuDO.class, o -> o.setId(1L)  
253 - .setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus()));  
254 - menuCache.put(menuDO.getId(), menuDO);  
255 - // 测试 id 不匹配  
256 - menuCache.put(2L, randomPojo(MenuDO.class, o -> o.setId(2L)  
257 - .setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus())));  
258 - // 测试 type 不匹配  
259 - menuCache.put(3L, randomPojo(MenuDO.class, o -> o.setId(3L)  
260 - .setType(MenuTypeEnum.BUTTON.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus())));  
261 - // 测试 status 不匹配  
262 - menuCache.put(4L, randomPojo(MenuDO.class, o -> o.setId(4L)  
263 - .setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.DISABLE.getStatus())));  
264 - menuService.setMenuCache(menuCache);  
265 - // 准备参数  
266 - Collection<Long> menuIds = asList(1L, 3L, 4L);  
267 - Collection<Integer> menuTypes = singletonList(MenuTypeEnum.MENU.getType());  
268 - Collection<Integer> menusStatuses = singletonList(CommonStatusEnum.ENABLE.getStatus());  
269 -  
270 - // 调用  
271 - List<MenuDO> list = menuService.getMenuListFromCache(menuIds, menuTypes, menusStatuses);  
272 - // 断言  
273 - assertEquals(1, list.size());  
274 - assertPojoEquals(menuDO, list.get(0));  
275 - }  
276 -  
277 - @Test  
278 - public void testGetMenuListByPermissionFromCache() {  
279 - // mock 缓存  
280 - Multimap<String, MenuDO> permissionMenuCache = LinkedListMultimap.create();  
281 - // 可被匹配  
282 - MenuDO menuDO01 = randomPojo(MenuDO.class, o -> o.setId(1L).setPermission("123"));  
283 - permissionMenuCache.put(menuDO01.getPermission(), menuDO01);  
284 - MenuDO menuDO02 = randomPojo(MenuDO.class, o -> o.setId(2L).setPermission("123"));  
285 - permissionMenuCache.put(menuDO02.getPermission(), menuDO02);  
286 - // 不可匹配  
287 - permissionMenuCache.put("456", randomPojo(MenuDO.class, o -> o.setId(3L).setPermission("456")));  
288 - menuService.setPermissionMenuCache(permissionMenuCache);  
289 - // 准备参数  
290 - String permission = "123";  
291 -  
292 - // 调用  
293 - List<MenuDO> list = menuService.getMenuListByPermissionFromCache(permission);  
294 - // 断言  
295 - assertEquals(2, list.size());  
296 - assertPojoEquals(menuDO01, list.get(0));  
297 - assertPojoEquals(menuDO02, list.get(1));  
298 - }  
299 -  
300 - @Test  
301 public void testGetMenu() { 189 public void testGetMenu() {
302 // mock 数据 190 // mock 数据
303 MenuDO menu = randomPojo(MenuDO.class); 191 MenuDO menu = randomPojo(MenuDO.class);
@@ -314,7 +202,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @@ -314,7 +202,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
314 @Test 202 @Test
315 public void testValidateParentMenu_success() { 203 public void testValidateParentMenu_success() {
316 // mock 数据 204 // mock 数据
317 - MenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, "parent", 0L); 205 + MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", 0L);
318 menuMapper.insert(menuDO); 206 menuMapper.insert(menuDO);
319 // 准备参数 207 // 准备参数
320 Long parentId = menuDO.getId(); 208 Long parentId = menuDO.getId();
@@ -340,7 +228,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @@ -340,7 +228,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
340 @Test 228 @Test
341 public void testValidateParentMenu_parentTypeError() { 229 public void testValidateParentMenu_parentTypeError() {
342 // mock 数据 230 // mock 数据
343 - MenuDO menuDO = createMenuDO(MenuTypeEnum.BUTTON, "parent", 0L); 231 + MenuDO menuDO = buildMenuDO(MenuTypeEnum.BUTTON, "parent", 0L);
344 menuMapper.insert(menuDO); 232 menuMapper.insert(menuDO);
345 // 准备参数 233 // 准备参数
346 Long parentId = menuDO.getId(); 234 Long parentId = menuDO.getId();
@@ -353,7 +241,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @@ -353,7 +241,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
353 @Test 241 @Test
354 public void testValidateMenu_success() { 242 public void testValidateMenu_success() {
355 // mock 父子菜单 243 // mock 父子菜单
356 - MenuDO sonMenu = initParentAndSonMenu(); 244 + MenuDO sonMenu = createParentAndSonMenu();
357 // 准备参数 245 // 准备参数
358 Long parentId = sonMenu.getParentId(); 246 Long parentId = sonMenu.getParentId();
359 Long otherSonMenuId = randomLongId(); 247 Long otherSonMenuId = randomLongId();
@@ -366,7 +254,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @@ -366,7 +254,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
366 @Test 254 @Test
367 public void testValidateMenu_sonMenuNameDuplicate() { 255 public void testValidateMenu_sonMenuNameDuplicate() {
368 // mock 父子菜单 256 // mock 父子菜单
369 - MenuDO sonMenu = initParentAndSonMenu(); 257 + MenuDO sonMenu = createParentAndSonMenu();
370 // 准备参数 258 // 准备参数
371 Long parentId = sonMenu.getParentId(); 259 Long parentId = sonMenu.getParentId();
372 Long otherSonMenuId = randomLongId(); 260 Long otherSonMenuId = randomLongId();
@@ -380,26 +268,26 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @@ -380,26 +268,26 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
380 // ====================== 初始化方法 ====================== 268 // ====================== 初始化方法 ======================
381 269
382 /** 270 /**
383 - * 构造父子菜单,返回子菜单 271 + * 插入父子菜单,返回子菜单
384 * 272 *
385 * @return 子菜单 273 * @return 子菜单
386 */ 274 */
387 - private MenuDO initParentAndSonMenu() { 275 + private MenuDO createParentAndSonMenu() {
388 // 构造父子菜单 276 // 构造父子菜单
389 - MenuDO parentMenuDO = createMenuDO(MenuTypeEnum.MENU, "parent", ID_ROOT); 277 + MenuDO parentMenuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", ID_ROOT);
390 menuMapper.insert(parentMenuDO); 278 menuMapper.insert(parentMenuDO);
391 // 构建子菜单 279 // 构建子菜单
392 - MenuDO sonMenuDO = createMenuDO(MenuTypeEnum.MENU, "testSonName", 280 + MenuDO sonMenuDO = buildMenuDO(MenuTypeEnum.MENU, "testSonName",
393 parentMenuDO.getParentId()); 281 parentMenuDO.getParentId());
394 menuMapper.insert(sonMenuDO); 282 menuMapper.insert(sonMenuDO);
395 return sonMenuDO; 283 return sonMenuDO;
396 } 284 }
397 285
398 - private MenuDO createMenuDO(MenuTypeEnum type, String name, Long parentId) {  
399 - return createMenuDO(type, name, parentId, randomCommonStatus()); 286 + private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId) {
  287 + return buildMenuDO(type, name, parentId, randomCommonStatus());
400 } 288 }
401 289
402 - private MenuDO createMenuDO(MenuTypeEnum type, String name, Long parentId, Integer status) { 290 + private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId, Integer status) {
403 return randomPojo(MenuDO.class, o -> o.setId(null).setName(name).setParentId(parentId) 291 return randomPojo(MenuDO.class, o -> o.setId(null).setName(name).setParentId(parentId)
404 .setType(type.getType()).setStatus(status)); 292 .setType(type.getType()).setStatus(status));
405 } 293 }
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.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.map.MapUtil; 4 +import cn.hutool.extra.spring.SpringUtil;
5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; 6 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
7 import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO; 7 import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
@@ -14,32 +14,28 @@ import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; @@ -14,32 +14,28 @@ import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
14 import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper; 14 import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper;
15 import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper; 15 import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
16 import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; 16 import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
17 -import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;  
18 import cn.iocoder.yudao.module.system.service.dept.DeptService; 17 import cn.iocoder.yudao.module.system.service.dept.DeptService;
19 import cn.iocoder.yudao.module.system.service.user.AdminUserService; 18 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
20 -import com.google.common.collect.ImmutableMultimap;  
21 -import com.google.common.collect.Multimap;  
22 import org.junit.jupiter.api.Test; 19 import org.junit.jupiter.api.Test;
  20 +import org.mockito.MockedStatic;
23 import org.springframework.boot.test.mock.mockito.MockBean; 21 import org.springframework.boot.test.mock.mockito.MockBean;
24 import org.springframework.context.annotation.Import; 22 import org.springframework.context.annotation.Import;
25 23
26 import javax.annotation.Resource; 24 import javax.annotation.Resource;
27 import java.util.Collection; 25 import java.util.Collection;
28 import java.util.List; 26 import java.util.List;
29 -import java.util.Map;  
30 import java.util.Set; 27 import java.util.Set;
31 28
  29 +import static cn.hutool.core.collection.ListUtil.toList;
32 import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; 30 import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
33 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; 31 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
34 -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;  
35 -import static java.util.Arrays.asList; 32 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
  33 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
36 import static java.util.Collections.singleton; 34 import static java.util.Collections.singleton;
37 import static java.util.Collections.singletonList; 35 import static java.util.Collections.singletonList;
38 import static org.junit.jupiter.api.Assertions.*; 36 import static org.junit.jupiter.api.Assertions.*;
39 import static org.mockito.ArgumentMatchers.eq; 37 import static org.mockito.ArgumentMatchers.eq;
40 -import static org.mockito.ArgumentMatchers.same;  
41 -import static org.mockito.Mockito.verify;  
42 -import static org.mockito.Mockito.when; 38 +import static org.mockito.Mockito.*;
43 39
44 @Import({PermissionServiceImpl.class}) 40 @Import({PermissionServiceImpl.class})
45 public class PermissionServiceTest extends BaseDbUnitTest { 41 public class PermissionServiceTest extends BaseDbUnitTest {
@@ -61,99 +57,140 @@ public class PermissionServiceTest extends BaseDbUnitTest { @@ -61,99 +57,140 @@ public class PermissionServiceTest extends BaseDbUnitTest {
61 @MockBean 57 @MockBean
62 private AdminUserService userService; 58 private AdminUserService userService;
63 59
64 - @MockBean  
65 - private PermissionProducer permissionProducer;  
66 -  
67 @Test 60 @Test
68 - public void testInitLocalCacheForRoleMenu() {  
69 - // mock 数据  
70 - RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(1L).setMenuId(10L));  
71 - roleMenuMapper.insert(roleMenuDO01);  
72 - RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(1L).setMenuId(20L));  
73 - roleMenuMapper.insert(roleMenuDO02);  
74 -  
75 - // 调用  
76 - permissionService.initLocalCacheForRoleMenu();  
77 - // 断言 roleMenuCache 缓存  
78 - assertEquals(1, permissionService.getRoleMenuCache().keySet().size());  
79 - assertEquals(asList(10L, 20L), permissionService.getRoleMenuCache().get(1L));  
80 - // 断言 menuRoleCache 缓存  
81 - assertEquals(2, permissionService.getMenuRoleCache().size());  
82 - assertEquals(singletonList(1L), permissionService.getMenuRoleCache().get(10L));  
83 - assertEquals(singletonList(1L), permissionService.getMenuRoleCache().get(20L)); 61 + public void testHasAnyPermissions_superAdmin() {
  62 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  63 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
  64 + .thenReturn(permissionService);
  65 +
  66 + // 准备参数
  67 + Long userId = 1L;
  68 + String[] roles = new String[]{"system:user:query", "system:user:create"};
  69 + // mock 用户登录的角色
  70 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L));
  71 + RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L)
  72 + .setStatus(CommonStatusEnum.ENABLE.getStatus()));
  73 + when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role));
  74 + // mock 其它方法
  75 + when(roleService.hasAnySuperAdmin(eq(asSet(100L)))).thenReturn(true);
  76 +
  77 + // 调用,并断言
  78 + assertTrue(permissionService.hasAnyPermissions(userId, roles));
  79 + }
84 } 80 }
85 81
86 @Test 82 @Test
87 - public void testInitLocalCacheForUserRole() {  
88 - // mock 数据  
89 - UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L));  
90 - userRoleMapper.insert(userRoleDO01);  
91 - UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L));  
92 - userRoleMapper.insert(roleMenuDO02); 83 + public void testHasAnyPermissions_normal() {
  84 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  85 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
  86 + .thenReturn(permissionService);
  87 +
  88 + // 准备参数
  89 + Long userId = 1L;
  90 + String[] roles = new String[]{"system:user:query", "system:user:create"};
  91 + // mock 用户登录的角色
  92 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L));
  93 + RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L)
  94 + .setStatus(CommonStatusEnum.ENABLE.getStatus()));
  95 + when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role));
  96 + // mock 菜单
  97 + Long menuId = 1000L;
  98 + when(menuService.getMenuIdListByPermissionFromCache(
  99 + eq("system:user:create"))).thenReturn(singletonList(menuId));
  100 + roleMenuMapper.insert(randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1000L));
  101 +
  102 + // 调用,并断言
  103 + assertTrue(permissionService.hasAnyPermissions(userId, roles));
  104 + }
  105 + }
93 106
94 - // 调用  
95 - permissionService.initLocalCacheForUserRole();  
96 - // 断言 roleMenuCache 缓存  
97 - assertEquals(1, permissionService.getUserRoleCache().size());  
98 - assertEquals(asSet(10L, 20L), permissionService.getUserRoleCache().get(1L)); 107 + @Test
  108 + public void testHasAnyRoles() {
  109 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  110 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
  111 + .thenReturn(permissionService);
  112 +
  113 + // 准备参数
  114 + Long userId = 1L;
  115 + String[] roles = new String[]{"yunai", "tudou"};
  116 + // mock 用户与角色的缓存
  117 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L));
  118 + RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L).setCode("tudou")
  119 + .setStatus(CommonStatusEnum.ENABLE.getStatus()));
  120 + when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role));
  121 +
  122 + // 调用,并断言
  123 + assertTrue(permissionService.hasAnyRoles(userId, roles));
  124 + }
99 } 125 }
100 126
  127 + // ========== 角色-菜单的相关方法 ==========
  128 +
101 @Test 129 @Test
102 - public void testGetRoleMenuListFromCache_superAdmin() { 130 + public void testAssignRoleMenu() {
103 // 准备参数 131 // 准备参数
104 - Collection<Long> roleIds = singletonList(100L);  
105 - Collection<Integer> menuTypes = asList(2, 3);  
106 - Collection<Integer> menusStatuses = asList(0, 1);  
107 - // mock 方法  
108 - List<RoleDO> roleList = singletonList(randomPojo(RoleDO.class, o -> o.setId(100L)));  
109 - when(roleService.getRoleListFromCache(eq(roleIds))).thenReturn(roleList);  
110 - when(roleService.hasAnySuperAdmin(same(roleList))).thenReturn(true);  
111 - List<MenuDO> menuList = randomPojoList(MenuDO.class);  
112 - when(menuService.getMenuListFromCache(eq(menuTypes), eq(menusStatuses))).thenReturn(menuList); 132 + Long roleId = 1L;
  133 + Set<Long> menuIds = asSet(200L, 300L);
  134 + // mock 数据
  135 + RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(1L).setMenuId(100L);
  136 + roleMenuMapper.insert(roleMenu01);
  137 + RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(1L).setMenuId(200L);
  138 + roleMenuMapper.insert(roleMenu02);
113 139
114 // 调用 140 // 调用
115 - List<MenuDO> result = permissionService.getRoleMenuListFromCache(roleIds, menuTypes, menusStatuses); 141 + permissionService.assignRoleMenu(roleId, menuIds);
116 // 断言 142 // 断言
117 - assertSame(menuList, result); 143 + List<RoleMenuDO> roleMenuList = roleMenuMapper.selectList();
  144 + assertEquals(2, roleMenuList.size());
  145 + assertEquals(1L, roleMenuList.get(0).getRoleId());
  146 + assertEquals(200L, roleMenuList.get(0).getMenuId());
  147 + assertEquals(1L, roleMenuList.get(1).getRoleId());
  148 + assertEquals(300L, roleMenuList.get(1).getMenuId());
118 } 149 }
119 150
120 @Test 151 @Test
121 - public void testGetRoleMenuListFromCache_normal() { 152 + public void testProcessRoleDeleted() {
122 // 准备参数 153 // 准备参数
123 - Collection<Long> roleIds = asSet(100L, 200L);  
124 - Collection<Integer> menuTypes = asList(2, 3);  
125 - Collection<Integer> menusStatuses = asList(0, 1);  
126 - // mock 方法  
127 - Multimap<Long, Long> roleMenuCache = ImmutableMultimap.<Long, Long>builder().put(100L, 1000L)  
128 - .put(200L, 2000L).put(200L, 2001L).build();  
129 - permissionService.setRoleMenuCache(roleMenuCache);  
130 - List<MenuDO> menuList = randomPojoList(MenuDO.class);  
131 - when(menuService.getMenuListFromCache(eq(asList(1000L, 2000L, 2001L)), eq(menuTypes), eq(menusStatuses))).thenReturn(menuList); 154 + Long roleId = randomLongId();
  155 + // mock 数据 UserRole
  156 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setRoleId(roleId)); // 被删除
  157 + userRoleMapper.insert(userRoleDO01);
  158 + UserRoleDO userRoleDO02 = randomPojo(UserRoleDO.class); // 不被删除
  159 + userRoleMapper.insert(userRoleDO02);
  160 + // mock 数据 RoleMenu
  161 + RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(roleId)); // 被删除
  162 + roleMenuMapper.insert(roleMenuDO01);
  163 + RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class); // 不被删除
  164 + roleMenuMapper.insert(roleMenuDO02);
132 165
133 // 调用 166 // 调用
134 - List<MenuDO> result = permissionService.getRoleMenuListFromCache(roleIds, menuTypes, menusStatuses);  
135 - // 断言  
136 - assertSame(menuList, result); 167 + permissionService.processRoleDeleted(roleId);
  168 + // 断言数据 RoleMenuDO
  169 + List<RoleMenuDO> dbRoleMenus = roleMenuMapper.selectList();
  170 + assertEquals(1, dbRoleMenus.size());
  171 + assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02);
  172 + // 断言数据 UserRoleDO
  173 + List<UserRoleDO> dbUserRoles = userRoleMapper.selectList();
  174 + assertEquals(1, dbUserRoles.size());
  175 + assertPojoEquals(dbUserRoles.get(0), userRoleDO02);
137 } 176 }
138 177
139 @Test 178 @Test
140 - public void testGetUserRoleIdsFromCache() { 179 + public void testProcessMenuDeleted() {
141 // 准备参数 180 // 准备参数
142 - Long userId = 1L;  
143 - Collection<Integer> roleStatuses = singleton(CommonStatusEnum.ENABLE.getStatus());  
144 - // mock 方法  
145 - Map<Long, Set<Long>> userRoleCache = MapUtil.<Long, Set<Long>>builder()  
146 - .put(1L, asSet(10L, 20L)).build();  
147 - permissionService.setUserRoleCache(userRoleCache);  
148 - RoleDO roleDO01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));  
149 - when(roleService.getRoleFromCache(eq(10L))).thenReturn(roleDO01);  
150 - RoleDO roleDO02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));  
151 - when(roleService.getRoleFromCache(eq(20L))).thenReturn(roleDO02); 181 + Long menuId = randomLongId();
  182 + // mock 数据
  183 + RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setMenuId(menuId)); // 被删除
  184 + roleMenuMapper.insert(roleMenuDO01);
  185 + RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class); // 不被删除
  186 + roleMenuMapper.insert(roleMenuDO02);
152 187
153 // 调用 188 // 调用
154 - Set<Long> roleIds = permissionService.getUserRoleIdsFromCache(userId, roleStatuses);  
155 - // 断言  
156 - assertEquals(asSet(10L), roleIds); 189 + permissionService.processMenuDeleted(menuId);
  190 + // 断言数据
  191 + List<RoleMenuDO> dbRoleMenus = roleMenuMapper.selectList();
  192 + assertEquals(1, dbRoleMenus.size());
  193 + assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02);
157 } 194 }
158 195
159 @Test 196 @Test
@@ -166,7 +203,7 @@ public class PermissionServiceTest extends BaseDbUnitTest { @@ -166,7 +203,7 @@ public class PermissionServiceTest extends BaseDbUnitTest {
166 when(menuService.getMenuList()).thenReturn(menuList); 203 when(menuService.getMenuList()).thenReturn(menuList);
167 204
168 // 调用 205 // 调用
169 - Set<Long> menuIds = permissionService.getRoleMenuIds(roleId); 206 + Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(roleId);
170 // 断言 207 // 断言
171 assertEquals(singleton(1L), menuIds); 208 assertEquals(singleton(1L), menuIds);
172 } 209 }
@@ -182,34 +219,29 @@ public class PermissionServiceTest extends BaseDbUnitTest { @@ -182,34 +219,29 @@ public class PermissionServiceTest extends BaseDbUnitTest {
182 roleMenuMapper.insert(roleMenu02); 219 roleMenuMapper.insert(roleMenu02);
183 220
184 // 调用 221 // 调用
185 - Set<Long> menuIds = permissionService.getRoleMenuIds(roleId); 222 + Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(roleId);
186 // 断言 223 // 断言
187 assertEquals(asSet(1L, 2L), menuIds); 224 assertEquals(asSet(1L, 2L), menuIds);
188 } 225 }
189 226
190 @Test 227 @Test
191 - public void testAssignRoleMenu() { 228 + public void testGetMenuRoleIdListByMenuIdFromCache() {
192 // 准备参数 229 // 准备参数
193 - Long roleId = 1L;  
194 - Set<Long> menuIds = asSet(200L, 300L); 230 + Long menuId = 1L;
195 // mock 数据 231 // mock 数据
196 - RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(1L).setMenuId(100L); 232 + RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1L);
197 roleMenuMapper.insert(roleMenu01); 233 roleMenuMapper.insert(roleMenu01);
198 - RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(1L).setMenuId(200L); 234 + RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(200L).setMenuId(1L);
199 roleMenuMapper.insert(roleMenu02); 235 roleMenuMapper.insert(roleMenu02);
200 236
201 // 调用 237 // 调用
202 - permissionService.assignRoleMenu(roleId, menuIds); 238 + Set<Long> roleIds = permissionService.getMenuRoleIdListByMenuIdFromCache(menuId);
203 // 断言 239 // 断言
204 - List<RoleMenuDO> roleMenuList = roleMenuMapper.selectList();  
205 - assertEquals(2, roleMenuList.size());  
206 - assertEquals(1L, roleMenuList.get(0).getRoleId());  
207 - assertEquals(200L, roleMenuList.get(0).getMenuId());  
208 - assertEquals(1L, roleMenuList.get(1).getRoleId());  
209 - assertEquals(300L, roleMenuList.get(1).getMenuId());  
210 - verify(permissionProducer).sendRoleMenuRefreshMessage(); 240 + assertEquals(asSet(100L, 200L), roleIds);
211 } 241 }
212 242
  243 + // ========== 用户-角色的相关方法 ==========
  244 +
213 @Test 245 @Test
214 public void testAssignUserRole() { 246 public void testAssignUserRole() {
215 // 准备参数 247 // 准备参数
@@ -230,7 +262,24 @@ public class PermissionServiceTest extends BaseDbUnitTest { @@ -230,7 +262,24 @@ public class PermissionServiceTest extends BaseDbUnitTest {
230 assertEquals(200L, userRoleDOList.get(0).getRoleId()); 262 assertEquals(200L, userRoleDOList.get(0).getRoleId());
231 assertEquals(1L, userRoleDOList.get(1).getUserId()); 263 assertEquals(1L, userRoleDOList.get(1).getUserId());
232 assertEquals(300L, userRoleDOList.get(1).getRoleId()); 264 assertEquals(300L, userRoleDOList.get(1).getRoleId());
233 - verify(permissionProducer).sendUserRoleRefreshMessage(); 265 + }
  266 +
  267 + @Test
  268 + public void testProcessUserDeleted() {
  269 + // 准备参数
  270 + Long userId = randomLongId();
  271 + // mock 数据
  272 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(userId)); // 被删除
  273 + userRoleMapper.insert(userRoleDO01);
  274 + UserRoleDO userRoleDO02 = randomPojo(UserRoleDO.class); // 不被删除
  275 + userRoleMapper.insert(userRoleDO02);
  276 +
  277 + // 调用
  278 + permissionService.processUserDeleted(userId);
  279 + // 断言数据
  280 + List<UserRoleDO> dbUserRoles = userRoleMapper.selectList();
  281 + assertEquals(1, dbUserRoles.size());
  282 + assertPojoEquals(dbUserRoles.get(0), userRoleDO02);
234 } 283 }
235 284
236 @Test 285 @Test
@@ -250,296 +299,229 @@ public class PermissionServiceTest extends BaseDbUnitTest { @@ -250,296 +299,229 @@ public class PermissionServiceTest extends BaseDbUnitTest {
250 } 299 }
251 300
252 @Test 301 @Test
253 - public void testGetUserRoleIdListByRoleIds() { 302 + public void testGetUserRoleIdListByUserIdFromCache() {
254 // 准备参数 303 // 准备参数
255 - Collection<Long> roleIds = asSet(10L, 20L); 304 + Long userId = 1L;
256 // mock 数据 305 // mock 数据
257 UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); 306 UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L));
258 userRoleMapper.insert(userRoleDO01); 307 userRoleMapper.insert(userRoleDO01);
259 - UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(2L).setRoleId(20L)); 308 + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L));
260 userRoleMapper.insert(roleMenuDO02); 309 userRoleMapper.insert(roleMenuDO02);
261 310
262 // 调用 311 // 调用
263 - Set<Long> result = permissionService.getUserRoleIdListByRoleIds(roleIds); 312 + Set<Long> result = permissionService.getUserRoleIdListByUserIdFromCache(userId);
264 // 断言 313 // 断言
265 - assertEquals(asSet(1L, 2L), result);  
266 - }  
267 -  
268 - @Test  
269 - public void testAssignRoleDataScope() {  
270 - // 准备参数  
271 - Long roleId = 1L;  
272 - Integer dataScope = 2;  
273 - Set<Long> dataScopeDeptIds = asSet(10L, 20L);  
274 -  
275 - // 调用  
276 - permissionService.assignRoleDataScope(roleId, dataScope, dataScopeDeptIds);  
277 - // 断言  
278 - verify(roleService).updateRoleDataScope(eq(roleId), eq(dataScope), eq(dataScopeDeptIds));  
279 - }  
280 -  
281 - @Test  
282 - public void testProcessRoleDeleted() {  
283 - // 准备参数  
284 - Long roleId = randomLongId();  
285 - // mock 数据 UserRole  
286 - UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setRoleId(roleId)); // 被删除  
287 - userRoleMapper.insert(userRoleDO01);  
288 - UserRoleDO userRoleDO02 = randomPojo(UserRoleDO.class); // 不被删除  
289 - userRoleMapper.insert(userRoleDO02);  
290 - // mock 数据 RoleMenu  
291 - RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(roleId)); // 被删除  
292 - roleMenuMapper.insert(roleMenuDO01);  
293 - RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class); // 不被删除  
294 - roleMenuMapper.insert(roleMenuDO02);  
295 -  
296 - // 调用  
297 - permissionService.processRoleDeleted(roleId);  
298 - // 断言数据 RoleMenuDO  
299 - List<RoleMenuDO> dbRoleMenus = roleMenuMapper.selectList();  
300 - assertEquals(1, dbRoleMenus.size());  
301 - assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02);  
302 - // 断言数据 UserRoleDO  
303 - List<UserRoleDO> dbUserRoles = userRoleMapper.selectList();  
304 - assertEquals(1, dbUserRoles.size());  
305 - assertPojoEquals(dbUserRoles.get(0), userRoleDO02);  
306 - // 断言调用  
307 - verify(permissionProducer).sendRoleMenuRefreshMessage();  
308 - verify(permissionProducer).sendUserRoleRefreshMessage(); 314 + assertEquals(asSet(10L, 20L), result);
309 } 315 }
310 316
311 @Test 317 @Test
312 - public void testProcessMenuDeleted() { 318 + public void testGetUserRoleIdsFromCache() {
313 // 准备参数 319 // 准备参数
314 - Long menuId = randomLongId(); 320 + Long userId = 1L;
315 // mock 数据 321 // mock 数据
316 - RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setMenuId(menuId)); // 被删除  
317 - roleMenuMapper.insert(roleMenuDO01);  
318 - RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class); // 不被删除  
319 - roleMenuMapper.insert(roleMenuDO02); 322 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L));
  323 + userRoleMapper.insert(userRoleDO01);
  324 + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L));
  325 + userRoleMapper.insert(roleMenuDO02);
320 326
321 // 调用 327 // 调用
322 - permissionService.processMenuDeleted(menuId);  
323 - // 断言数据  
324 - List<RoleMenuDO> dbRoleMenus = roleMenuMapper.selectList();  
325 - assertEquals(1, dbRoleMenus.size());  
326 - assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02);  
327 - // 断言调用  
328 - verify(permissionProducer).sendRoleMenuRefreshMessage(); 328 + Set<Long> result = permissionService.getUserRoleIdListByUserIdFromCache(userId);
  329 + // 断言
  330 + assertEquals(asSet(10L, 20L), result);
329 } 331 }
330 332
331 @Test 333 @Test
332 - public void testProcessUserDeleted() { 334 + public void testGetUserRoleIdListByRoleId() {
333 // 准备参数 335 // 准备参数
334 - Long userId = randomLongId(); 336 + Collection<Long> roleIds = asSet(10L, 20L);
335 // mock 数据 337 // mock 数据
336 - UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(userId)); // 被删除 338 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L));
337 userRoleMapper.insert(userRoleDO01); 339 userRoleMapper.insert(userRoleDO01);
338 - UserRoleDO userRoleDO02 = randomPojo(UserRoleDO.class); // 不被删除  
339 - userRoleMapper.insert(userRoleDO02);  
340 -  
341 - // 调用  
342 - permissionService.processUserDeleted(userId);  
343 - // 断言数据  
344 - List<UserRoleDO> dbUserRoles = userRoleMapper.selectList();  
345 - assertEquals(1, dbUserRoles.size());  
346 - assertPojoEquals(dbUserRoles.get(0), userRoleDO02);  
347 - // 断言调用  
348 - verify(permissionProducer).sendUserRoleRefreshMessage();  
349 - }  
350 -  
351 - @Test  
352 - public void testHasAnyPermissions_superAdmin() {  
353 - // 准备参数  
354 - Long userId = 1L;  
355 - String[] roles = new String[]{"system:user:query", "system:user:create"};  
356 - // mock 用户与角色的缓存  
357 - permissionService.setUserRoleCache(MapUtil.<Long, Set<Long>>builder().put(1L, asSet(100L)).build());  
358 - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L)  
359 - .setStatus(CommonStatusEnum.ENABLE.getStatus()));  
360 - when(roleService.getRoleFromCache(eq(100L))).thenReturn(role);  
361 - // mock 其它方法  
362 - when(roleService.hasAnySuperAdmin(eq(asSet(100L)))).thenReturn(true); 340 + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(2L).setRoleId(20L));
  341 + userRoleMapper.insert(roleMenuDO02);
363 342
364 // 调用 343 // 调用
365 - boolean has = permissionService.hasAnyPermissions(userId, roles); 344 + Set<Long> result = permissionService.getUserRoleIdListByRoleId(roleIds);
366 // 断言 345 // 断言
367 - assertTrue(has); 346 + assertEquals(asSet(1L, 2L), result);
368 } 347 }
369 348
370 @Test 349 @Test
371 - public void testHasAnyPermissions_normal() {  
372 - // 准备参数  
373 - Long userId = 1L;  
374 - String[] roles = new String[]{"system:user:query", "system:user:create"};  
375 - // mock 用户与角色的缓存  
376 - permissionService.setUserRoleCache(MapUtil.<Long, Set<Long>>builder().put(1L, asSet(100L)).build());  
377 - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L)  
378 - .setStatus(CommonStatusEnum.ENABLE.getStatus()));  
379 - when(roleService.getRoleFromCache(eq(100L))).thenReturn(role);  
380 - // mock 其它方法  
381 - MenuDO menu = randomPojo(MenuDO.class, o -> o.setId(1000L));  
382 - when(menuService.getMenuListByPermissionFromCache(eq("system:user:create"))).thenReturn(singletonList(menu));  
383 - permissionService.setMenuRoleCache(ImmutableMultimap.<Long, Long>builder().put(1000L, 100L).build());  
384 -  
385 -  
386 - // 调用  
387 - boolean has = permissionService.hasAnyPermissions(userId, roles);  
388 - // 断言  
389 - assertTrue(has); 350 + public void testGetEnableUserRoleListByUserIdFromCache() {
  351 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  352 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
  353 + .thenReturn(permissionService);
  354 +
  355 + // 准备参数
  356 + Long userId = 1L;
  357 + // mock 用户登录的角色
  358 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L));
  359 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(200L));
  360 + RoleDO role01 = randomPojo(RoleDO.class, o -> o.setId(100L)
  361 + .setStatus(CommonStatusEnum.ENABLE.getStatus()));
  362 + RoleDO role02 = randomPojo(RoleDO.class, o -> o.setId(200L)
  363 + .setStatus(CommonStatusEnum.DISABLE.getStatus()));
  364 + when(roleService.getRoleListFromCache(eq(asSet(100L, 200L))))
  365 + .thenReturn(toList(role01, role02));
  366 +
  367 + // 调用
  368 + List<RoleDO> result = permissionService.getEnableUserRoleListByUserIdFromCache(userId);
  369 + // 断言
  370 + assertEquals(1, result.size());
  371 + assertPojoEquals(role01, result.get(0));
  372 + }
390 } 373 }
391 374
392 - @Test  
393 - public void testHasAnyRoles_superAdmin() {  
394 - // 准备参数  
395 - Long userId = 1L;  
396 - String[] roles = new String[]{"yunai", "tudou"};  
397 - // mock 用户与角色的缓存  
398 - permissionService.setUserRoleCache(MapUtil.<Long, Set<Long>>builder().put(1L, asSet(100L)).build());  
399 - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L)  
400 - .setStatus(CommonStatusEnum.ENABLE.getStatus()));  
401 - when(roleService.getRoleFromCache(eq(100L))).thenReturn(role);  
402 - // mock 其它方法  
403 - when(roleService.hasAnySuperAdmin(eq(asSet(100L)))).thenReturn(true);  
404 -  
405 - // 调用  
406 - boolean has = permissionService.hasAnyRoles(userId, roles);  
407 - // 断言  
408 - assertTrue(has);  
409 - } 375 + // ========== 用户-部门的相关方法 ==========
410 376
411 @Test 377 @Test
412 - public void testHasAnyRoles_normal() { 378 + public void testAssignRoleDataScope() {
413 // 准备参数 379 // 准备参数
414 - Long userId = 1L;  
415 - String[] roles = new String[]{"yunai", "tudou"};  
416 - // mock 用户与角色的缓存  
417 - permissionService.setUserRoleCache(MapUtil.<Long, Set<Long>>builder().put(1L, asSet(100L)).build());  
418 - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L).setCode("yunai")  
419 - .setStatus(CommonStatusEnum.ENABLE.getStatus()));  
420 - when(roleService.getRoleFromCache(eq(100L))).thenReturn(role);  
421 - // mock 其它方法  
422 - when(roleService.getRoleListFromCache(eq(asSet(100L)))).thenReturn(singletonList(role)); 380 + Long roleId = 1L;
  381 + Integer dataScope = 2;
  382 + Set<Long> dataScopeDeptIds = asSet(10L, 20L);
423 383
424 // 调用 384 // 调用
425 - boolean has = permissionService.hasAnyRoles(userId, roles); 385 + permissionService.assignRoleDataScope(roleId, dataScope, dataScopeDeptIds);
426 // 断言 386 // 断言
427 - assertTrue(has); 387 + verify(roleService).updateRoleDataScope(eq(roleId), eq(dataScope), eq(dataScopeDeptIds));
428 } 388 }
429 389
430 @Test 390 @Test
431 public void testGetDeptDataPermission_All() { 391 public void testGetDeptDataPermission_All() {
432 - // 准备参数  
433 - Long userId = 1L;  
434 - // mock 用户的角色编号  
435 - permissionService.setUserRoleCache(MapUtil.<Long, Set<Long>>builder().put(1L, asSet(2L)).build());  
436 - // mock 获得用户的角色  
437 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope())  
438 - .setStatus(CommonStatusEnum.ENABLE.getStatus()));  
439 - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO));  
440 - when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO);  
441 -  
442 - // 调用  
443 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);  
444 - // 断言  
445 - assertTrue(result.getAll());  
446 - assertFalse(result.getSelf());  
447 - assertTrue(CollUtil.isEmpty(result.getDeptIds())); 392 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  393 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
  394 + .thenReturn(permissionService);
  395 +
  396 + // 准备参数
  397 + Long userId = 1L;
  398 + // mock 用户的角色编号
  399 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L));
  400 + // mock 获得用户的角色
  401 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope())
  402 + .setStatus(CommonStatusEnum.ENABLE.getStatus()));
  403 + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO));
  404 +
  405 + // 调用
  406 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);
  407 + // 断言
  408 + assertTrue(result.getAll());
  409 + assertFalse(result.getSelf());
  410 + assertTrue(CollUtil.isEmpty(result.getDeptIds()));
  411 + }
448 } 412 }
449 413
450 @Test 414 @Test
451 public void testGetDeptDataPermission_DeptCustom() { 415 public void testGetDeptDataPermission_DeptCustom() {
452 - // 准备参数  
453 - Long userId = 1L;  
454 - // mock 用户的角色编号  
455 - permissionService.setUserRoleCache(MapUtil.<Long, Set<Long>>builder().put(1L, asSet(2L)).build());  
456 - // mock 获得用户的角色  
457 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope())  
458 - .setStatus(CommonStatusEnum.ENABLE.getStatus()));  
459 - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO));  
460 - when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO);  
461 - // mock 部门的返回  
462 - when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的,看看会不会重复调用  
463 -  
464 - // 调用  
465 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);  
466 - // 断言  
467 - assertFalse(result.getAll());  
468 - assertFalse(result.getSelf());  
469 - assertEquals(roleDO.getDataScopeDeptIds().size() + 1, result.getDeptIds().size());  
470 - assertTrue(CollUtil.containsAll(result.getDeptIds(), roleDO.getDataScopeDeptIds()));  
471 - assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); 416 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  417 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
  418 + .thenReturn(permissionService);
  419 +
  420 + // 准备参数
  421 + Long userId = 1L;
  422 + // mock 用户的角色编号
  423 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L));
  424 + // mock 获得用户的角色
  425 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope())
  426 + .setStatus(CommonStatusEnum.ENABLE.getStatus()));
  427 + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO));
  428 + // mock 部门的返回
  429 + when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L),
  430 + null, null); // 最后返回 null 的目的,看看会不会重复调用
  431 +
  432 + // 调用
  433 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);
  434 + // 断言
  435 + assertFalse(result.getAll());
  436 + assertFalse(result.getSelf());
  437 + assertEquals(roleDO.getDataScopeDeptIds().size() + 1, result.getDeptIds().size());
  438 + assertTrue(CollUtil.containsAll(result.getDeptIds(), roleDO.getDataScopeDeptIds()));
  439 + assertTrue(CollUtil.contains(result.getDeptIds(), 3L));
  440 + }
472 } 441 }
473 442
474 @Test 443 @Test
475 public void testGetDeptDataPermission_DeptOnly() { 444 public void testGetDeptDataPermission_DeptOnly() {
476 - // 准备参数  
477 - Long userId = 1L;  
478 - // mock 用户的角色编号  
479 - permissionService.setUserRoleCache(MapUtil.<Long, Set<Long>>builder().put(1L, asSet(2L)).build());  
480 - // mock 获得用户的角色  
481 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope())  
482 - .setStatus(CommonStatusEnum.ENABLE.getStatus()));  
483 - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO));  
484 - when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO);  
485 - // mock 部门的返回  
486 - when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的,看看会不会重复调用  
487 -  
488 - // 调用  
489 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);  
490 - // 断言  
491 - assertFalse(result.getAll());  
492 - assertFalse(result.getSelf());  
493 - assertEquals(1, result.getDeptIds().size());  
494 - assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); 445 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  446 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
  447 + .thenReturn(permissionService);
  448 +
  449 + // 准备参数
  450 + Long userId = 1L;
  451 + // mock 用户的角色编号
  452 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L));
  453 + // mock 获得用户的角色
  454 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope())
  455 + .setStatus(CommonStatusEnum.ENABLE.getStatus()));
  456 + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO));
  457 + // mock 部门的返回
  458 + when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L),
  459 + null, null); // 最后返回 null 的目的,看看会不会重复调用
  460 +
  461 + // 调用
  462 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);
  463 + // 断言
  464 + assertFalse(result.getAll());
  465 + assertFalse(result.getSelf());
  466 + assertEquals(1, result.getDeptIds().size());
  467 + assertTrue(CollUtil.contains(result.getDeptIds(), 3L));
  468 + }
495 } 469 }
496 470
497 @Test 471 @Test
498 public void testGetDeptDataPermission_DeptAndChild() { 472 public void testGetDeptDataPermission_DeptAndChild() {
499 - // 准备参数  
500 - Long userId = 1L;  
501 - // mock 用户的角色编号  
502 - permissionService.setUserRoleCache(MapUtil.<Long, Set<Long>>builder().put(1L, asSet(2L)).build());  
503 - // mock 获得用户的角色  
504 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope())  
505 - .setStatus(CommonStatusEnum.ENABLE.getStatus()));  
506 - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO));  
507 - when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO);  
508 - // mock 部门的返回  
509 - when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的,看看会不会重复调用  
510 - // mock 方法(部门)  
511 - DeptDO deptDO = randomPojo(DeptDO.class);  
512 - when(deptService.getDeptListByParentIdFromCache(eq(3L), eq(true)))  
513 - .thenReturn(singletonList(deptDO));  
514 -  
515 - // 调用  
516 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);  
517 - // 断言  
518 - assertFalse(result.getAll());  
519 - assertFalse(result.getSelf());  
520 - assertEquals(2, result.getDeptIds().size());  
521 - assertTrue(CollUtil.contains(result.getDeptIds(), deptDO.getId()));  
522 - assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); 473 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  474 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
  475 + .thenReturn(permissionService);
  476 +
  477 + // 准备参数
  478 + Long userId = 1L;
  479 + // mock 用户的角色编号
  480 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L));
  481 + // mock 获得用户的角色
  482 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope())
  483 + .setStatus(CommonStatusEnum.ENABLE.getStatus()));
  484 + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO));
  485 + // mock 部门的返回
  486 + when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L),
  487 + null, null); // 最后返回 null 的目的,看看会不会重复调用
  488 + // mock 方法(部门)
  489 + DeptDO deptDO = randomPojo(DeptDO.class);
  490 + when(deptService.getChildDeptIdListFromCache(eq(3L))).thenReturn(singleton(deptDO.getId()));
  491 +
  492 + // 调用
  493 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);
  494 + // 断言
  495 + assertFalse(result.getAll());
  496 + assertFalse(result.getSelf());
  497 + assertEquals(2, result.getDeptIds().size());
  498 + assertTrue(CollUtil.contains(result.getDeptIds(), deptDO.getId()));
  499 + assertTrue(CollUtil.contains(result.getDeptIds(), 3L));
  500 + }
523 } 501 }
524 502
525 @Test 503 @Test
526 public void testGetDeptDataPermission_Self() { 504 public void testGetDeptDataPermission_Self() {
527 - // 准备参数  
528 - Long userId = 1L;  
529 - // mock 用户的角色编号  
530 - permissionService.setUserRoleCache(MapUtil.<Long, Set<Long>>builder().put(1L, asSet(2L)).build());  
531 - // mock 获得用户的角色  
532 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope())  
533 - .setStatus(CommonStatusEnum.ENABLE.getStatus()));  
534 - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO));  
535 - when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO);  
536 -  
537 - // 调用  
538 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);  
539 - // 断言  
540 - assertFalse(result.getAll());  
541 - assertTrue(result.getSelf());  
542 - assertTrue(CollUtil.isEmpty(result.getDeptIds())); 505 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  506 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
  507 + .thenReturn(permissionService);
  508 +
  509 + // 准备参数
  510 + Long userId = 1L;
  511 + // mock 用户的角色编号
  512 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L));
  513 + // mock 获得用户的角色
  514 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope())
  515 + .setStatus(CommonStatusEnum.ENABLE.getStatus()));
  516 + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO));
  517 +
  518 + // 调用
  519 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);
  520 + // 断言
  521 + assertFalse(result.getAll());
  522 + assertTrue(result.getSelf());
  523 + assertTrue(CollUtil.isEmpty(result.getDeptIds()));
  524 + }
543 } 525 }
544 526
545 } 527 }
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.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.extra.spring.SpringUtil;
3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; 6 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
@@ -11,15 +12,14 @@ import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; @@ -11,15 +12,14 @@ import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
11 import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper; 12 import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper;
12 import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; 13 import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
13 import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; 14 import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
14 -import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer;  
15 import org.junit.jupiter.api.Test; 15 import org.junit.jupiter.api.Test;
  16 +import org.mockito.MockedStatic;
16 import org.springframework.boot.test.mock.mockito.MockBean; 17 import org.springframework.boot.test.mock.mockito.MockBean;
17 import org.springframework.context.annotation.Import; 18 import org.springframework.context.annotation.Import;
18 19
19 import javax.annotation.Resource; 20 import javax.annotation.Resource;
20 import java.util.Collection; 21 import java.util.Collection;
21 import java.util.List; 22 import java.util.List;
22 -import java.util.Map;  
23 import java.util.Set; 23 import java.util.Set;
24 24
25 import static cn.hutool.core.util.RandomUtil.randomEle; 25 import static cn.hutool.core.util.RandomUtil.randomEle;
@@ -33,6 +33,8 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @@ -33,6 +33,8 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
33 import static java.util.Collections.singleton; 33 import static java.util.Collections.singleton;
34 import static java.util.Collections.singletonList; 34 import static java.util.Collections.singletonList;
35 import static org.junit.jupiter.api.Assertions.*; 35 import static org.junit.jupiter.api.Assertions.*;
  36 +import static org.mockito.ArgumentMatchers.eq;
  37 +import static org.mockito.Mockito.mockStatic;
36 import static org.mockito.Mockito.verify; 38 import static org.mockito.Mockito.verify;
37 39
38 @Import(RoleServiceImpl.class) 40 @Import(RoleServiceImpl.class)
@@ -46,26 +48,9 @@ public class RoleServiceImplTest extends BaseDbUnitTest { @@ -46,26 +48,9 @@ public class RoleServiceImplTest extends BaseDbUnitTest {
46 48
47 @MockBean 49 @MockBean
48 private PermissionService permissionService; 50 private PermissionService permissionService;
49 - @MockBean  
50 - private RoleProducer roleProducer;  
51 51
52 @Test 52 @Test
53 - public void testInitLocalCache() {  
54 - RoleDO roleDO1 = randomPojo(RoleDO.class);  
55 - roleMapper.insert(roleDO1);  
56 - RoleDO roleDO2 = randomPojo(RoleDO.class);  
57 - roleMapper.insert(roleDO2);  
58 -  
59 - // 调用  
60 - roleService.initLocalCache();  
61 - // 断言 roleCache 缓存  
62 - Map<Long, RoleDO> roleCache = roleService.getRoleCache();  
63 - assertPojoEquals(roleDO1, roleCache.get(roleDO1.getId()));  
64 - assertPojoEquals(roleDO2, roleCache.get(roleDO2.getId()));  
65 - }  
66 -  
67 - @Test  
68 - public void testCreateRole_success() { 53 + public void testCreateRole() {
69 // 准备参数 54 // 准备参数
70 RoleCreateReqVO reqVO = randomPojo(RoleCreateReqVO.class); 55 RoleCreateReqVO reqVO = randomPojo(RoleCreateReqVO.class);
71 56
@@ -77,12 +62,10 @@ public class RoleServiceImplTest extends BaseDbUnitTest { @@ -77,12 +62,10 @@ public class RoleServiceImplTest extends BaseDbUnitTest {
77 assertEquals(RoleTypeEnum.CUSTOM.getType(), roleDO.getType()); 62 assertEquals(RoleTypeEnum.CUSTOM.getType(), roleDO.getType());
78 assertEquals(CommonStatusEnum.ENABLE.getStatus(), roleDO.getStatus()); 63 assertEquals(CommonStatusEnum.ENABLE.getStatus(), roleDO.getStatus());
79 assertEquals(DataScopeEnum.ALL.getScope(), roleDO.getDataScope()); 64 assertEquals(DataScopeEnum.ALL.getScope(), roleDO.getDataScope());
80 - // verify 发送刷新消息  
81 - verify(roleProducer).sendRoleRefreshMessage();  
82 } 65 }
83 66
84 @Test 67 @Test
85 - public void testUpdateRole_success() { 68 + public void testUpdateRole() {
86 // mock 数据 69 // mock 数据
87 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); 70 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType()));
88 roleMapper.insert(roleDO); 71 roleMapper.insert(roleDO);
@@ -95,12 +78,10 @@ public class RoleServiceImplTest extends BaseDbUnitTest { @@ -95,12 +78,10 @@ public class RoleServiceImplTest extends BaseDbUnitTest {
95 // 断言 78 // 断言
96 RoleDO newRoleDO = roleMapper.selectById(id); 79 RoleDO newRoleDO = roleMapper.selectById(id);
97 assertPojoEquals(reqVO, newRoleDO); 80 assertPojoEquals(reqVO, newRoleDO);
98 - // verify 发送刷新消息  
99 - verify(roleProducer).sendRoleRefreshMessage();  
100 } 81 }
101 82
102 @Test 83 @Test
103 - public void testUpdateRoleStatus_success() { 84 + public void testUpdateRoleStatus() {
104 // mock 数据 85 // mock 数据
105 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()) 86 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())
106 .setType(RoleTypeEnum.CUSTOM.getType())); 87 .setType(RoleTypeEnum.CUSTOM.getType()));
@@ -114,12 +95,10 @@ public class RoleServiceImplTest extends BaseDbUnitTest { @@ -114,12 +95,10 @@ public class RoleServiceImplTest extends BaseDbUnitTest {
114 // 断言 95 // 断言
115 RoleDO dbRoleDO = roleMapper.selectById(roleId); 96 RoleDO dbRoleDO = roleMapper.selectById(roleId);
116 assertEquals(CommonStatusEnum.DISABLE.getStatus(), dbRoleDO.getStatus()); 97 assertEquals(CommonStatusEnum.DISABLE.getStatus(), dbRoleDO.getStatus());
117 - // verify 发送刷新消息  
118 - verify(roleProducer).sendRoleRefreshMessage();  
119 } 98 }
120 99
121 @Test 100 @Test
122 - public void testUpdateRoleDataScope_success() { 101 + public void testUpdateRoleDataScope() {
123 // mock 数据 102 // mock 数据
124 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); 103 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType()));
125 roleMapper.insert(roleDO); 104 roleMapper.insert(roleDO);
@@ -134,12 +113,10 @@ public class RoleServiceImplTest extends BaseDbUnitTest { @@ -134,12 +113,10 @@ public class RoleServiceImplTest extends BaseDbUnitTest {
134 RoleDO dbRoleDO = roleMapper.selectById(id); 113 RoleDO dbRoleDO = roleMapper.selectById(id);
135 assertEquals(dataScope, dbRoleDO.getDataScope()); 114 assertEquals(dataScope, dbRoleDO.getDataScope());
136 assertEquals(dataScopeRoleIds, dbRoleDO.getDataScopeDeptIds()); 115 assertEquals(dataScopeRoleIds, dbRoleDO.getDataScopeDeptIds());
137 - // verify 发送刷新消息  
138 - verify(roleProducer).sendRoleRefreshMessage();  
139 } 116 }
140 117
141 @Test 118 @Test
142 - public void testDeleteRole_success() { 119 + public void testDeleteRole() {
143 // mock 数据 120 // mock 数据
144 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); 121 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType()));
145 roleMapper.insert(roleDO); 122 roleMapper.insert(roleDO);
@@ -152,23 +129,65 @@ public class RoleServiceImplTest extends BaseDbUnitTest { @@ -152,23 +129,65 @@ public class RoleServiceImplTest extends BaseDbUnitTest {
152 assertNull(roleMapper.selectById(id)); 129 assertNull(roleMapper.selectById(id));
153 // verify 删除相关数据 130 // verify 删除相关数据
154 verify(permissionService).processRoleDeleted(id); 131 verify(permissionService).processRoleDeleted(id);
155 - // verify 发送刷新消息  
156 - verify(roleProducer).sendRoleRefreshMessage();  
157 } 132 }
158 133
159 @Test 134 @Test
160 - public void testGetRoleFromCache() {  
161 - // mock 数据(缓存) 135 + public void testValidateRoleDuplicate_success() {
  136 + // 调用,不会抛异常
  137 + roleService.validateRoleDuplicate(randomString(), randomString(), null);
  138 + }
  139 +
  140 + @Test
  141 + public void testValidateRoleDuplicate_nameDuplicate() {
  142 + // mock 数据
  143 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setName("role_name"));
  144 + roleMapper.insert(roleDO);
  145 + // 准备参数
  146 + String name = "role_name";
  147 +
  148 + // 调用,并断言异常
  149 + assertServiceException(() -> roleService.validateRoleDuplicate(name, randomString(), null),
  150 + ROLE_NAME_DUPLICATE, name);
  151 + }
  152 +
  153 + @Test
  154 + public void testValidateRoleDuplicate_codeDuplicate() {
  155 + // mock 数据
  156 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setCode("code"));
  157 + roleMapper.insert(roleDO);
  158 + // 准备参数
  159 + String code = "code";
  160 +
  161 + // 调用,并断言异常
  162 + assertServiceException(() -> roleService.validateRoleDuplicate(randomString(), code, null),
  163 + ROLE_CODE_DUPLICATE, code);
  164 + }
  165 +
  166 + @Test
  167 + public void testValidateUpdateRole_success() {
162 RoleDO roleDO = randomPojo(RoleDO.class); 168 RoleDO roleDO = randomPojo(RoleDO.class);
163 roleMapper.insert(roleDO); 169 roleMapper.insert(roleDO);
164 - roleService.initLocalCache();  
165 - // 参数准备 170 + // 准备参数
166 Long id = roleDO.getId(); 171 Long id = roleDO.getId();
167 172
168 - // 调用  
169 - RoleDO dbRoleDO = roleService.getRoleFromCache(id);  
170 - // 断言  
171 - assertPojoEquals(roleDO, dbRoleDO); 173 + // 调用,无异常
  174 + roleService.validateRoleForUpdate(id);
  175 + }
  176 +
  177 + @Test
  178 + public void testValidateUpdateRole_roleIdNotExist() {
  179 + assertServiceException(() -> roleService.validateRoleForUpdate(randomLongId()), ROLE_NOT_EXISTS);
  180 + }
  181 +
  182 + @Test
  183 + public void testValidateUpdateRole_systemRoleCanNotBeUpdate() {
  184 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.SYSTEM.getType()));
  185 + roleMapper.insert(roleDO);
  186 + // 准备参数
  187 + Long id = roleDO.getId();
  188 +
  189 + assertServiceException(() -> roleService.validateRoleForUpdate(id),
  190 + ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE);
172 } 191 }
173 192
174 @Test 193 @Test
@@ -186,22 +205,21 @@ public class RoleServiceImplTest extends BaseDbUnitTest { @@ -186,22 +205,21 @@ public class RoleServiceImplTest extends BaseDbUnitTest {
186 } 205 }
187 206
188 @Test 207 @Test
189 - public void testGetRoleListByStatus_statusNotEmpty() {  
190 - // mock 数据  
191 - RoleDO dbRole = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));  
192 - roleMapper.insert(dbRole);  
193 - // 测试 status 不匹配  
194 - roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); 208 + public void testGetRoleFromCache() {
  209 + // mock 数据(缓存)
  210 + RoleDO roleDO = randomPojo(RoleDO.class);
  211 + roleMapper.insert(roleDO);
  212 + // 参数准备
  213 + Long id = roleDO.getId();
195 214
196 // 调用 215 // 调用
197 - List<RoleDO> list = roleService.getRoleListByStatus(singleton(CommonStatusEnum.ENABLE.getStatus())); 216 + RoleDO dbRoleDO = roleService.getRoleFromCache(id);
198 // 断言 217 // 断言
199 - assertEquals(1, list.size());  
200 - assertPojoEquals(dbRole, list.get(0)); 218 + assertPojoEquals(roleDO, dbRoleDO);
201 } 219 }
202 220
203 @Test 221 @Test
204 - public void testGetRoleListByStatus_statusEmpty() { 222 + public void testGetRoleListByStatus() {
205 // mock 数据 223 // mock 数据
206 RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); 224 RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
207 roleMapper.insert(dbRole01); 225 roleMapper.insert(dbRole01);
@@ -209,29 +227,33 @@ public class RoleServiceImplTest extends BaseDbUnitTest { @@ -209,29 +227,33 @@ public class RoleServiceImplTest extends BaseDbUnitTest {
209 roleMapper.insert(dbRole02); 227 roleMapper.insert(dbRole02);
210 228
211 // 调用 229 // 调用
212 - List<RoleDO> list = roleService.getRoleListByStatus(null); 230 + List<RoleDO> list = roleService.getRoleListByStatus(
  231 + singleton(CommonStatusEnum.ENABLE.getStatus()));
213 // 断言 232 // 断言
214 - assertEquals(2, list.size()); 233 + assertEquals(1, list.size());
215 assertPojoEquals(dbRole01, list.get(0)); 234 assertPojoEquals(dbRole01, list.get(0));
216 - assertPojoEquals(dbRole02, list.get(1));  
217 } 235 }
218 236
219 @Test 237 @Test
220 public void testGetRoleListFromCache() { 238 public void testGetRoleListFromCache() {
221 - // mock 数据  
222 - RoleDO dbRole = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));  
223 - roleMapper.insert(dbRole);  
224 - // 测试 id 不匹配  
225 - roleMapper.insert(cloneIgnoreId(dbRole, o -> {}));  
226 - roleService.initLocalCache();  
227 - // 准备参数  
228 - Collection<Long> ids = singleton(dbRole.getId());  
229 -  
230 - // 调用  
231 - List<RoleDO> list = roleService.getRoleListFromCache(ids);  
232 - // 断言  
233 - assertEquals(1, list.size());  
234 - assertPojoEquals(dbRole, list.get(0)); 239 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  240 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class)))
  241 + .thenReturn(roleService);
  242 +
  243 + // mock 数据
  244 + RoleDO dbRole = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
  245 + roleMapper.insert(dbRole);
  246 + // 测试 id 不匹配
  247 + roleMapper.insert(cloneIgnoreId(dbRole, o -> {}));
  248 + // 准备参数
  249 + Collection<Long> ids = singleton(dbRole.getId());
  250 +
  251 + // 调用
  252 + List<RoleDO> list = roleService.getRoleListFromCache(ids);
  253 + // 断言
  254 + assertEquals(1, list.size());
  255 + assertPojoEquals(dbRole, list.get(0));
  256 + }
235 } 257 }
236 258
237 @Test 259 @Test
@@ -296,72 +318,37 @@ public class RoleServiceImplTest extends BaseDbUnitTest { @@ -296,72 +318,37 @@ public class RoleServiceImplTest extends BaseDbUnitTest {
296 } 318 }
297 319
298 @Test 320 @Test
299 - public void testHasAnySuperAdmin() {  
300 - // 是超级  
301 - assertTrue(roleService.hasAnySuperAdmin(singletonList(randomPojo(RoleDO.class,  
302 - o -> o.setCode("super_admin")))));  
303 - // 非超级  
304 - assertFalse(roleService.hasAnySuperAdmin(singletonList(randomPojo(RoleDO.class,  
305 - o -> o.setCode("tenant_admin")))));  
306 - }  
307 -  
308 - @Test  
309 - public void testValidateRoleDuplicate_success() {  
310 - // 调用,不会抛异常  
311 - roleService.validateRoleDuplicate(randomString(), randomString(), null);  
312 - }  
313 -  
314 - @Test  
315 - public void testValidateRoleDuplicate_nameDuplicate() {  
316 - // mock 数据  
317 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setName("role_name"));  
318 - roleMapper.insert(roleDO);  
319 - // 准备参数  
320 - String name = "role_name";  
321 -  
322 - // 调用,并断言异常  
323 - assertServiceException(() -> roleService.validateRoleDuplicate(name, randomString(), null),  
324 - ROLE_NAME_DUPLICATE, name); 321 + public void testHasAnySuperAdmin_true() {
  322 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  323 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class)))
  324 + .thenReturn(roleService);
  325 +
  326 + // mock 数据
  327 + RoleDO dbRole = randomPojo(RoleDO.class).setCode("super_admin");
  328 + roleMapper.insert(dbRole);
  329 + // 准备参数
  330 + Long id = dbRole.getId();
  331 +
  332 + // 调用,并调用
  333 + assertTrue(roleService.hasAnySuperAdmin(singletonList(id)));
  334 + }
325 } 335 }
326 336
327 @Test 337 @Test
328 - public void testValidateRoleDuplicate_codeDuplicate() {  
329 - // mock 数据  
330 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setCode("code"));  
331 - roleMapper.insert(roleDO);  
332 - // 准备参数  
333 - String code = "code";  
334 -  
335 - // 调用,并断言异常  
336 - assertServiceException(() -> roleService.validateRoleDuplicate(randomString(), code, null),  
337 - ROLE_CODE_DUPLICATE, code);  
338 - }  
339 -  
340 - @Test  
341 - public void testValidateUpdateRole_success() {  
342 - RoleDO roleDO = randomPojo(RoleDO.class);  
343 - roleMapper.insert(roleDO);  
344 - // 准备参数  
345 - Long id = roleDO.getId();  
346 -  
347 - // 调用,无异常  
348 - roleService.validateRoleForUpdate(id);  
349 - }  
350 -  
351 - @Test  
352 - public void testValidateUpdateRole_roleIdNotExist() {  
353 - assertServiceException(() -> roleService.validateRoleForUpdate(randomLongId()), ROLE_NOT_EXISTS);  
354 - }  
355 -  
356 - @Test  
357 - public void testValidateUpdateRole_systemRoleCanNotBeUpdate() {  
358 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.SYSTEM.getType()));  
359 - roleMapper.insert(roleDO);  
360 - // 准备参数  
361 - Long id = roleDO.getId();  
362 -  
363 - assertServiceException(() -> roleService.validateRoleForUpdate(id),  
364 - ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE); 338 + public void testHasAnySuperAdmin_false() {
  339 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  340 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class)))
  341 + .thenReturn(roleService);
  342 +
  343 + // mock 数据
  344 + RoleDO dbRole = randomPojo(RoleDO.class).setCode("tenant_admin");
  345 + roleMapper.insert(dbRole);
  346 + // 准备参数
  347 + Long id = dbRole.getId();
  348 +
  349 + // 调用,并调用
  350 + assertFalse(roleService.hasAnySuperAdmin(singletonList(id)));
  351 + }
365 } 352 }
366 353
367 @Test 354 @Test
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImplTest.java
@@ -10,9 +10,7 @@ import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.Sensitiv @@ -10,9 +10,7 @@ import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.Sensitiv
10 import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordUpdateReqVO; 10 import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordUpdateReqVO;
11 import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO; 11 import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
12 import cn.iocoder.yudao.module.system.dal.mysql.sensitiveword.SensitiveWordMapper; 12 import cn.iocoder.yudao.module.system.dal.mysql.sensitiveword.SensitiveWordMapper;
13 -import cn.iocoder.yudao.module.system.mq.producer.sensitiveword.SensitiveWordProducer;  
14 import org.junit.jupiter.api.Test; 13 import org.junit.jupiter.api.Test;
15 -import org.springframework.boot.test.mock.mockito.MockBean;  
16 import org.springframework.context.annotation.Import; 14 import org.springframework.context.annotation.Import;
17 15
18 import javax.annotation.Resource; 16 import javax.annotation.Resource;
@@ -29,7 +27,6 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; @@ -29,7 +27,6 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
29 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_WORD_NOT_EXISTS; 27 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_WORD_NOT_EXISTS;
30 import static java.util.Collections.singletonList; 28 import static java.util.Collections.singletonList;
31 import static org.junit.jupiter.api.Assertions.*; 29 import static org.junit.jupiter.api.Assertions.*;
32 -import static org.mockito.Mockito.verify;  
33 30
34 /** 31 /**
35 * {@link SensitiveWordServiceImpl} 的单元测试类 32 * {@link SensitiveWordServiceImpl} 的单元测试类
@@ -45,9 +42,6 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest { @@ -45,9 +42,6 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest {
45 @Resource 42 @Resource
46 private SensitiveWordMapper sensitiveWordMapper; 43 private SensitiveWordMapper sensitiveWordMapper;
47 44
48 - @MockBean  
49 - private SensitiveWordProducer sensitiveWordProducer;  
50 -  
51 @Test 45 @Test
52 public void testInitLocalCache() { 46 public void testInitLocalCache() {
53 SensitiveWordDO wordDO1 = randomPojo(SensitiveWordDO.class, o -> o.setName("傻瓜") 47 SensitiveWordDO wordDO1 = randomPojo(SensitiveWordDO.class, o -> o.setName("傻瓜")
@@ -61,6 +55,10 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest { @@ -61,6 +55,10 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest {
61 sensitiveWordService.initLocalCache(); 55 sensitiveWordService.initLocalCache();
62 // 断言 sensitiveWordTagsCache 缓存 56 // 断言 sensitiveWordTagsCache 缓存
63 assertEquals(SetUtils.asSet("论坛", "蔬菜"), sensitiveWordService.getSensitiveWordTagSet()); 57 assertEquals(SetUtils.asSet("论坛", "蔬菜"), sensitiveWordService.getSensitiveWordTagSet());
  58 + // 断言 sensitiveWordCache
  59 + assertEquals(2, sensitiveWordService.getSensitiveWordCache().size());
  60 + assertPojoEquals(wordDO1, sensitiveWordService.getSensitiveWordCache().get(0));
  61 + assertPojoEquals(wordDO2, sensitiveWordService.getSensitiveWordCache().get(1));
64 // 断言 tagSensitiveWordTries 缓存 62 // 断言 tagSensitiveWordTries 缓存
65 assertNotNull(sensitiveWordService.getDefaultSensitiveWordTrie()); 63 assertNotNull(sensitiveWordService.getDefaultSensitiveWordTrie());
66 assertEquals(2, sensitiveWordService.getTagSensitiveWordTries().size()); 64 assertEquals(2, sensitiveWordService.getTagSensitiveWordTries().size());
@@ -80,7 +78,6 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest { @@ -80,7 +78,6 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest {
80 // 校验记录的属性是否正确 78 // 校验记录的属性是否正确
81 SensitiveWordDO sensitiveWord = sensitiveWordMapper.selectById(sensitiveWordId); 79 SensitiveWordDO sensitiveWord = sensitiveWordMapper.selectById(sensitiveWordId);
82 assertPojoEquals(reqVO, sensitiveWord); 80 assertPojoEquals(reqVO, sensitiveWord);
83 - verify(sensitiveWordProducer).sendSensitiveWordRefreshMessage();  
84 } 81 }
85 82
86 @Test 83 @Test
@@ -98,7 +95,6 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest { @@ -98,7 +95,6 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest {
98 // 校验是否更新正确 95 // 校验是否更新正确
99 SensitiveWordDO sensitiveWord = sensitiveWordMapper.selectById(reqVO.getId()); // 获取最新的 96 SensitiveWordDO sensitiveWord = sensitiveWordMapper.selectById(reqVO.getId()); // 获取最新的
100 assertPojoEquals(reqVO, sensitiveWord); 97 assertPojoEquals(reqVO, sensitiveWord);
101 - verify(sensitiveWordProducer).sendSensitiveWordRefreshMessage();  
102 } 98 }
103 99
104 @Test 100 @Test
@@ -122,7 +118,6 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest { @@ -122,7 +118,6 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest {
122 sensitiveWordService.deleteSensitiveWord(id); 118 sensitiveWordService.deleteSensitiveWord(id);
123 // 校验数据不存在了 119 // 校验数据不存在了
124 assertNull(sensitiveWordMapper.selectById(id)); 120 assertNull(sensitiveWordMapper.selectById(id));
125 - verify(sensitiveWordProducer).sendSensitiveWordRefreshMessage();  
126 } 121 }
127 122
128 @Test 123 @Test
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java
@@ -9,13 +9,11 @@ import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannel @@ -9,13 +9,11 @@ import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannel
9 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO; 9 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO;
10 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; 10 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
11 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper; 11 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
12 -import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;  
13 import org.junit.jupiter.api.Test; 12 import org.junit.jupiter.api.Test;
14 import org.springframework.boot.test.mock.mockito.MockBean; 13 import org.springframework.boot.test.mock.mockito.MockBean;
15 import org.springframework.context.annotation.Import; 14 import org.springframework.context.annotation.Import;
16 15
17 import javax.annotation.Resource; 16 import javax.annotation.Resource;
18 -  
19 import java.util.List; 17 import java.util.List;
20 18
21 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; 19 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
@@ -42,8 +40,6 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { @@ -42,8 +40,6 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
42 private SmsClientFactory smsClientFactory; 40 private SmsClientFactory smsClientFactory;
43 @MockBean 41 @MockBean
44 private SmsTemplateService smsTemplateService; 42 private SmsTemplateService smsTemplateService;
45 - @MockBean  
46 - private SmsProducer smsProducer;  
47 43
48 @Test 44 @Test
49 public void testInitLocalCache_success() { 45 public void testInitLocalCache_success() {
@@ -60,6 +56,10 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { @@ -60,6 +56,10 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
60 argThat(properties -> isPojoEquals(smsChannelDO01, properties))); 56 argThat(properties -> isPojoEquals(smsChannelDO01, properties)));
61 verify(smsClientFactory, times(1)).createOrUpdateSmsClient( 57 verify(smsClientFactory, times(1)).createOrUpdateSmsClient(
62 argThat(properties -> isPojoEquals(smsChannelDO02, properties))); 58 argThat(properties -> isPojoEquals(smsChannelDO02, properties)));
  59 + // 断言 channelCache 缓存
  60 + assertEquals(2, smsChannelService.getChannelCache().size());
  61 + assertPojoEquals(smsChannelDO01, smsChannelService.getChannelCache().get(0));
  62 + assertPojoEquals(smsChannelDO02, smsChannelService.getChannelCache().get(1));
63 } 63 }
64 64
65 @Test 65 @Test
@@ -74,8 +74,6 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { @@ -74,8 +74,6 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
74 // 校验记录的属性是否正确 74 // 校验记录的属性是否正确
75 SmsChannelDO smsChannel = smsChannelMapper.selectById(smsChannelId); 75 SmsChannelDO smsChannel = smsChannelMapper.selectById(smsChannelId);
76 assertPojoEquals(reqVO, smsChannel); 76 assertPojoEquals(reqVO, smsChannel);
77 - // 校验调用  
78 - verify(smsProducer, times(1)).sendSmsChannelRefreshMessage();  
79 } 77 }
80 78
81 @Test 79 @Test
@@ -95,8 +93,6 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { @@ -95,8 +93,6 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
95 // 校验是否更新正确 93 // 校验是否更新正确
96 SmsChannelDO smsChannel = smsChannelMapper.selectById(reqVO.getId()); // 获取最新的 94 SmsChannelDO smsChannel = smsChannelMapper.selectById(reqVO.getId()); // 获取最新的
97 assertPojoEquals(reqVO, smsChannel); 95 assertPojoEquals(reqVO, smsChannel);
98 - // 校验调用  
99 - verify(smsProducer, times(1)).sendSmsChannelRefreshMessage();  
100 } 96 }
101 97
102 @Test 98 @Test
@@ -118,10 +114,8 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { @@ -118,10 +114,8 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
118 114
119 // 调用 115 // 调用
120 smsChannelService.deleteSmsChannel(id); 116 smsChannelService.deleteSmsChannel(id);
121 - // 校验数据不存在了  
122 - assertNull(smsChannelMapper.selectById(id));  
123 - // 校验调用  
124 - verify(smsProducer, times(1)).sendSmsChannelRefreshMessage(); 117 + // 校验数据不存在了
  118 + assertNull(smsChannelMapper.selectById(id));
125 } 119 }
126 120
127 @Test 121 @Test
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java
@@ -18,7 +18,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; @@ -18,7 +18,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
18 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; 18 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
19 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper; 19 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper;
20 import cn.iocoder.yudao.module.system.enums.sms.SmsTemplateTypeEnum; 20 import cn.iocoder.yudao.module.system.enums.sms.SmsTemplateTypeEnum;
21 -import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;  
22 import com.google.common.collect.Lists; 21 import com.google.common.collect.Lists;
23 import org.junit.jupiter.api.Test; 22 import org.junit.jupiter.api.Test;
24 import org.springframework.boot.test.mock.mockito.MockBean; 23 import org.springframework.boot.test.mock.mockito.MockBean;
@@ -26,7 +25,6 @@ import org.springframework.context.annotation.Import; @@ -26,7 +25,6 @@ import org.springframework.context.annotation.Import;
26 25
27 import javax.annotation.Resource; 26 import javax.annotation.Resource;
28 import java.util.List; 27 import java.util.List;
29 -import java.util.Map;  
30 import java.util.function.Consumer; 28 import java.util.function.Consumer;
31 29
32 import static cn.hutool.core.util.RandomUtil.randomEle; 30 import static cn.hutool.core.util.RandomUtil.randomEle;
@@ -55,25 +53,6 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { @@ -55,25 +53,6 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
55 private SmsClientFactory smsClientFactory; 53 private SmsClientFactory smsClientFactory;
56 @MockBean 54 @MockBean
57 private SmsClient smsClient; 55 private SmsClient smsClient;
58 - @MockBean  
59 - private SmsProducer smsProducer;  
60 -  
61 - @Test  
62 - void testInitLocalCache() {  
63 - // mock 数据  
64 - SmsTemplateDO smsTemplate01 = randomSmsTemplateDO();  
65 - smsTemplateMapper.insert(smsTemplate01);  
66 - SmsTemplateDO smsTemplate02 = randomSmsTemplateDO();  
67 - smsTemplateMapper.insert(smsTemplate02);  
68 -  
69 - // 调用  
70 - smsTemplateService.initLocalCache();  
71 - // 断言 deptCache 缓存  
72 - Map<String, SmsTemplateDO> smsTemplateCache = smsTemplateService.getSmsTemplateCache();  
73 - assertEquals(2, smsTemplateCache.size());  
74 - assertPojoEquals(smsTemplate01, smsTemplateCache.get(smsTemplate01.getCode()));  
75 - assertPojoEquals(smsTemplate02, smsTemplateCache.get(smsTemplate02.getCode()));  
76 - }  
77 56
78 @Test 57 @Test
79 public void testParseTemplateContentParams() { 58 public void testParseTemplateContentParams() {
@@ -116,8 +95,6 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { @@ -116,8 +95,6 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
116 assertPojoEquals(reqVO, smsTemplate); 95 assertPojoEquals(reqVO, smsTemplate);
117 assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams()); 96 assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams());
118 assertEquals(channelDO.getCode(), smsTemplate.getChannelCode()); 97 assertEquals(channelDO.getCode(), smsTemplate.getChannelCode());
119 - // 校验调用  
120 - verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage();  
121 } 98 }
122 99
123 @Test 100 @Test
@@ -151,8 +128,6 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { @@ -151,8 +128,6 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
151 assertPojoEquals(reqVO, smsTemplate); 128 assertPojoEquals(reqVO, smsTemplate);
152 assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams()); 129 assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams());
153 assertEquals(channelDO.getCode(), smsTemplate.getChannelCode()); 130 assertEquals(channelDO.getCode(), smsTemplate.getChannelCode());
154 - // 校验调用  
155 - verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage();  
156 } 131 }
157 132
158 @Test 133 @Test
@@ -174,10 +149,8 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { @@ -174,10 +149,8 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
174 149
175 // 调用 150 // 调用
176 smsTemplateService.deleteSmsTemplate(id); 151 smsTemplateService.deleteSmsTemplate(id);
177 - // 校验数据不存在了  
178 - assertNull(smsTemplateMapper.selectById(id));  
179 - // 校验调用  
180 - verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage(); 152 + // 校验数据不存在了
  153 + assertNull(smsTemplateMapper.selectById(id));
181 } 154 }
182 155
183 @Test 156 @Test
@@ -191,47 +164,47 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { @@ -191,47 +164,47 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
191 164
192 @Test 165 @Test
193 public void testGetSmsTemplatePage() { 166 public void testGetSmsTemplatePage() {
194 - // mock 数据  
195 - SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> { // 等会查询到  
196 - o.setType(SmsTemplateTypeEnum.PROMOTION.getType());  
197 - o.setStatus(CommonStatusEnum.ENABLE.getStatus());  
198 - o.setCode("tudou");  
199 - o.setContent("芋道源码");  
200 - o.setApiTemplateId("yunai");  
201 - o.setChannelId(1L);  
202 - o.setCreateTime(buildTime(2021, 11, 11));  
203 - });  
204 - smsTemplateMapper.insert(dbSmsTemplate);  
205 - // 测试 type 不匹配  
206 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setType(SmsTemplateTypeEnum.VERIFICATION_CODE.getType())));  
207 - // 测试 status 不匹配  
208 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));  
209 - // 测试 code 不匹配  
210 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCode("yuanma")));  
211 - // 测试 content 不匹配  
212 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setContent("源码")));  
213 - // 测试 apiTemplateId 不匹配  
214 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setApiTemplateId("nai")));  
215 - // 测试 channelId 不匹配  
216 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setChannelId(2L)));  
217 - // 测试 createTime 不匹配  
218 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCreateTime(buildTime(2021, 12, 12))));  
219 - // 准备参数  
220 - SmsTemplatePageReqVO reqVO = new SmsTemplatePageReqVO();  
221 - reqVO.setType(SmsTemplateTypeEnum.PROMOTION.getType());  
222 - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());  
223 - reqVO.setCode("tu");  
224 - reqVO.setContent("芋道");  
225 - reqVO.setApiTemplateId("yu");  
226 - reqVO.setChannelId(1L);  
227 - reqVO.setCreateTime(buildBetweenTime(2021, 11, 1, 2021, 12, 1));  
228 -  
229 - // 调用  
230 - PageResult<SmsTemplateDO> pageResult = smsTemplateService.getSmsTemplatePage(reqVO);  
231 - // 断言  
232 - assertEquals(1, pageResult.getTotal());  
233 - assertEquals(1, pageResult.getList().size());  
234 - assertPojoEquals(dbSmsTemplate, pageResult.getList().get(0)); 167 + // mock 数据
  168 + SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> { // 等会查询到
  169 + o.setType(SmsTemplateTypeEnum.PROMOTION.getType());
  170 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  171 + o.setCode("tudou");
  172 + o.setContent("芋道源码");
  173 + o.setApiTemplateId("yunai");
  174 + o.setChannelId(1L);
  175 + o.setCreateTime(buildTime(2021, 11, 11));
  176 + });
  177 + smsTemplateMapper.insert(dbSmsTemplate);
  178 + // 测试 type 不匹配
  179 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setType(SmsTemplateTypeEnum.VERIFICATION_CODE.getType())));
  180 + // 测试 status 不匹配
  181 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
  182 + // 测试 code 不匹配
  183 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCode("yuanma")));
  184 + // 测试 content 不匹配
  185 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setContent("源码")));
  186 + // 测试 apiTemplateId 不匹配
  187 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setApiTemplateId("nai")));
  188 + // 测试 channelId 不匹配
  189 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setChannelId(2L)));
  190 + // 测试 createTime 不匹配
  191 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCreateTime(buildTime(2021, 12, 12))));
  192 + // 准备参数
  193 + SmsTemplatePageReqVO reqVO = new SmsTemplatePageReqVO();
  194 + reqVO.setType(SmsTemplateTypeEnum.PROMOTION.getType());
  195 + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
  196 + reqVO.setCode("tu");
  197 + reqVO.setContent("芋道");
  198 + reqVO.setApiTemplateId("yu");
  199 + reqVO.setChannelId(1L);
  200 + reqVO.setCreateTime(buildBetweenTime(2021, 11, 1, 2021, 12, 1));
  201 +
  202 + // 调用
  203 + PageResult<SmsTemplateDO> pageResult = smsTemplateService.getSmsTemplatePage(reqVO);
  204 + // 断言
  205 + assertEquals(1, pageResult.getTotal());
  206 + assertEquals(1, pageResult.getList().size());
  207 + assertPojoEquals(dbSmsTemplate, pageResult.getList().get(0));
235 } 208 }
236 209
237 @Test 210 @Test
@@ -271,11 +244,11 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { @@ -271,11 +244,11 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
271 reqVO.setChannelId(1L); 244 reqVO.setChannelId(1L);
272 reqVO.setCreateTime(buildBetweenTime(2021, 11, 1, 2021, 12, 1)); 245 reqVO.setCreateTime(buildBetweenTime(2021, 11, 1, 2021, 12, 1));
273 246
274 - // 调用  
275 - List<SmsTemplateDO> list = smsTemplateService.getSmsTemplateList(reqVO);  
276 - // 断言  
277 - assertEquals(1, list.size());  
278 - assertPojoEquals(dbSmsTemplate, list.get(0)); 247 + // 调用
  248 + List<SmsTemplateDO> list = smsTemplateService.getSmsTemplateList(reqVO);
  249 + // 断言
  250 + assertEquals(1, list.size());
  251 + assertPojoEquals(dbSmsTemplate, list.get(0));
279 } 252 }
280 253
281 @Test 254 @Test
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java
@@ -199,7 +199,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest { @@ -199,7 +199,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
199 role101.setTenantId(dbTenant.getId()); 199 role101.setTenantId(dbTenant.getId());
200 when(roleService.getRoleListByStatus(isNull())).thenReturn(asList(role100, role101)); 200 when(roleService.getRoleListByStatus(isNull())).thenReturn(asList(role100, role101));
201 // mock 每个角色的权限 201 // mock 每个角色的权限
202 - when(permissionService.getRoleMenuIds(eq(101L))).thenReturn(asSet(201L, 202L)); 202 + when(permissionService.getRoleMenuListByRoleId(eq(101L))).thenReturn(asSet(201L, 202L));
203 203
204 // 调用 204 // 调用
205 tenantService.updateTenant(reqVO); 205 tenantService.updateTenant(reqVO);
@@ -454,7 +454,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest { @@ -454,7 +454,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
454 TenantContextHolder.setTenantId(dbTenant.getId()); 454 TenantContextHolder.setTenantId(dbTenant.getId());
455 // mock 菜单 455 // mock 菜单
456 when(menuService.getMenuList()).thenReturn(Arrays.asList(randomPojo(MenuDO.class, o -> o.setId(100L)), 456 when(menuService.getMenuList()).thenReturn(Arrays.asList(randomPojo(MenuDO.class, o -> o.setId(100L)),
457 - randomPojo(MenuDO.class, o -> o.setId(101L)))); 457 + randomPojo(MenuDO.class, o -> o.setId(101L))));
458 458
459 // 调用 459 // 调用
460 tenantService.handleTenantMenu(handler); 460 tenantService.handleTenantMenu(handler);
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java
@@ -345,7 +345,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { @@ -345,7 +345,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
345 reqVO.setDeptId(1L); // 其中,1L 是 2L 的父部门 345 reqVO.setDeptId(1L); // 其中,1L 是 2L 的父部门
346 // mock 方法 346 // mock 方法
347 List<DeptDO> deptList = newArrayList(randomPojo(DeptDO.class, o -> o.setId(2L))); 347 List<DeptDO> deptList = newArrayList(randomPojo(DeptDO.class, o -> o.setId(2L)));
348 - when(deptService.getDeptListByParentIdFromCache(eq(reqVO.getDeptId()), eq(true))).thenReturn(deptList); 348 + when(deptService.getChildDeptList(eq(reqVO.getDeptId()))).thenReturn(deptList);
349 349
350 // 调用 350 // 调用
351 PageResult<AdminUserDO> pageResult = userService.getUserPage(reqVO); 351 PageResult<AdminUserDO> pageResult = userService.getUserPage(reqVO);
@@ -368,7 +368,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { @@ -368,7 +368,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
368 reqVO.setDeptId(1L); // 其中,1L 是 2L 的父部门 368 reqVO.setDeptId(1L); // 其中,1L 是 2L 的父部门
369 // mock 方法 369 // mock 方法
370 List<DeptDO> deptList = newArrayList(randomPojo(DeptDO.class, o -> o.setId(2L))); 370 List<DeptDO> deptList = newArrayList(randomPojo(DeptDO.class, o -> o.setId(2L)));
371 - when(deptService.getDeptListByParentIdFromCache(eq(reqVO.getDeptId()), eq(true))).thenReturn(deptList); 371 + when(deptService.getChildDeptList(eq(reqVO.getDeptId()))).thenReturn(deptList);
372 372
373 // 调用 373 // 调用
374 List<AdminUserDO> list = userService.getUserList(reqVO); 374 List<AdminUserDO> list = userService.getUserList(reqVO);
yudao-server/src/main/resources/application-dev.yaml
@@ -147,14 +147,14 @@ wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-sta @@ -147,14 +147,14 @@ wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-sta
147 # 存储配置,解决 AccessToken 的跨节点的共享 147 # 存储配置,解决 AccessToken 的跨节点的共享
148 config-storage: 148 config-storage:
149 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 149 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
150 - key-prefix: wx # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置 150 + key-prefix: wx # Redis Key 的前缀
151 http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 151 http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
152 miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档 152 miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
153 appid: wx63c280fe3248a3e7 153 appid: wx63c280fe3248a3e7
154 secret: 6f270509224a7ae1296bbf1c8cb97aed 154 secret: 6f270509224a7ae1296bbf1c8cb97aed
155 config-storage: 155 config-storage:
156 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 156 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
157 - key-prefix: wa # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置 157 + key-prefix: wa # Redis Key 的前缀
158 http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 158 http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
159 159
160 --- #################### 芋道相关配置 #################### 160 --- #################### 芋道相关配置 ####################
@@ -167,7 +167,8 @@ yudao: @@ -167,7 +167,8 @@ yudao:
167 - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 167 - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
168 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 168 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
169 pay: 169 pay:
170 - callback-url: http://yunai.natapp1.cc/admin-api/pay/notify/callback 170 + order-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/order # 支付渠道的【支付】回调地址
  171 + refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址
171 demo: true # 开启演示模式 172 demo: true # 开启演示模式
172 173
173 justauth: 174 justauth:
yudao-server/src/main/resources/application-local.yaml
@@ -170,14 +170,14 @@ wx: @@ -170,14 +170,14 @@ wx:
170 # 存储配置,解决 AccessToken 的跨节点的共享 170 # 存储配置,解决 AccessToken 的跨节点的共享
171 config-storage: 171 config-storage:
172 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 172 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
173 - key-prefix: wx # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置 173 + key-prefix: wx # Redis Key 的前缀
174 http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 174 http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
175 miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档 175 miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
176 appid: wx62056c0d5e8db250 176 appid: wx62056c0d5e8db250
177 secret: 333ae72f41552af1e998fe1f54e1584a 177 secret: 333ae72f41552af1e998fe1f54e1584a
178 config-storage: 178 config-storage:
179 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 179 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
180 - key-prefix: wa # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置 180 + key-prefix: wa # Redis Key 的前缀
181 http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 181 http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
182 182
183 --- #################### 芋道相关配置 #################### 183 --- #################### 芋道相关配置 ####################
yudao-ui-admin/package.json
1 { 1 {
2 "name": "yudao-ui-admin", 2 "name": "yudao-ui-admin",
3 - "version": "1.7.3-snapshot", 3 + "version": "1.8.0-snapshot",
4 "description": "芋道管理系统", 4 "description": "芋道管理系统",
5 "author": "芋道", 5 "author": "芋道",
6 "license": "MIT", 6 "license": "MIT",
yudao-ui-admin/src/api/menu.js deleted
1 -import request from '@/utils/request'  
2 -  
3 -// 获取路由  
4 -export const getRouters = () => {  
5 - return request({  
6 - url: '/system/auth/list-menus',  
7 - method: 'get'  
8 - })  
9 -}  
yudao-ui-admin/src/api/system/permission.js
@@ -3,7 +3,7 @@ import request from &#39;@/utils/request&#39; @@ -3,7 +3,7 @@ import request from &#39;@/utils/request&#39;
3 // 查询角色拥有的菜单数组 3 // 查询角色拥有的菜单数组
4 export function listRoleMenus(roleId) { 4 export function listRoleMenus(roleId) {
5 return request({ 5 return request({
6 - url: '/system/permission/list-role-resources?roleId=' + roleId, 6 + url: '/system/permission/list-role-menus?roleId=' + roleId,
7 method: 'get' 7 method: 'get'
8 }) 8 })
9 } 9 }
yudao-ui-admin/src/components/Crontab/week.vue
@@ -185,7 +185,7 @@ export default { @@ -185,7 +185,7 @@ export default {
185 averageTotal: function () { 185 averageTotal: function () {
186 this.average01 = this.checkNum(this.average01, 1, 4) 186 this.average01 = this.checkNum(this.average01, 1, 4)
187 this.average02 = this.checkNum(this.average02, 1, 7) 187 this.average02 = this.checkNum(this.average02, 1, 7)
188 - return this.average02 + '#' + this.average01; 188 + return this.average01 + '#' + this.average02;
189 }, 189 },
190 // 最近的工作日(格式) 190 // 最近的工作日(格式)
191 weekdayCheck: function () { 191 weekdayCheck: function () {
yudao-ui-admin/src/permission.js
@@ -25,9 +25,10 @@ router.beforeEach((to, from, next) =&gt; { @@ -25,9 +25,10 @@ router.beforeEach((to, from, next) =&gt; {
25 // 获取字典数据 add by 芋艿 25 // 获取字典数据 add by 芋艿
26 store.dispatch('dict/loadDictDatas') 26 store.dispatch('dict/loadDictDatas')
27 // 判断当前用户是否已拉取完 user_info 信息 27 // 判断当前用户是否已拉取完 user_info 信息
28 - store.dispatch('GetInfo').then(() => { 28 + store.dispatch('GetInfo').then(userInfo => {
29 isRelogin.show = false 29 isRelogin.show = false
30 - store.dispatch('GenerateRoutes').then(accessRoutes => { 30 + // 触发 GenerateRoutes 事件时,将 menus 菜单树传递进去
  31 + store.dispatch('GenerateRoutes', userInfo.menus).then(accessRoutes => {
31 // 根据 roles 权限生成可访问的路由表 32 // 根据 roles 权限生成可访问的路由表
32 router.addRoutes(accessRoutes) // 动态添加可访问路由表 33 router.addRoutes(accessRoutes) // 动态添加可访问路由表
33 next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 34 next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
yudao-ui-admin/src/store/modules/permission.js
1 import {constantRoutes} from '@/router' 1 import {constantRoutes} from '@/router'
2 -import {getRouters} from '@/api/menu'  
3 import Layout from '@/layout/index' 2 import Layout from '@/layout/index'
4 import ParentView from '@/components/ParentView'; 3 import ParentView from '@/components/ParentView';
5 import {toCamelCase} from "@/utils"; 4 import {toCamelCase} from "@/utils";
@@ -27,22 +26,25 @@ const permission = { @@ -27,22 +26,25 @@ const permission = {
27 }, 26 },
28 }, 27 },
29 actions: { 28 actions: {
30 - // 生成路由  
31 - GenerateRoutes({commit}) { 29 + /**
  30 + * 生成路由
  31 + *
  32 + * @param commit commit 函数
  33 + * @param menus 路由参数
  34 + */
  35 + GenerateRoutes({commit}, menus) {
32 return new Promise(resolve => { 36 return new Promise(resolve => {
33 - // 向后端请求路由数据(菜单)  
34 - getRouters().then(res => {  
35 - const sdata = JSON.parse(JSON.stringify(res.data)) // 【重要】用于菜单中的数据  
36 - const rdata = JSON.parse(JSON.stringify(res.data)) // 用于最后添加到 Router 中的数据  
37 - const sidebarRoutes = filterAsyncRouter(sdata)  
38 - const rewriteRoutes = filterAsyncRouter(rdata, false, true)  
39 - rewriteRoutes.push({path: '*', redirect: '/404', hidden: true})  
40 - commit('SET_ROUTES', rewriteRoutes)  
41 - commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))  
42 - commit('SET_DEFAULT_ROUTES', sidebarRoutes)  
43 - commit('SET_TOPBAR_ROUTES', sidebarRoutes)  
44 - resolve(rewriteRoutes)  
45 - }) 37 + // 将 menus 菜单,转换为 route 路由数组
  38 + const sdata = JSON.parse(JSON.stringify(menus)) // 【重要】用于菜单中的数据
  39 + const rdata = JSON.parse(JSON.stringify(menus)) // 用于最后添加到 Router 中的数据
  40 + const sidebarRoutes = filterAsyncRouter(sdata)
  41 + const rewriteRoutes = filterAsyncRouter(rdata, false, true)
  42 + rewriteRoutes.push({path: '*', redirect: '/404', hidden: true})
  43 + commit('SET_ROUTES', rewriteRoutes)
  44 + commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
  45 + commit('SET_DEFAULT_ROUTES', sidebarRoutes)
  46 + commit('SET_TOPBAR_ROUTES', sidebarRoutes)
  47 + resolve(rewriteRoutes)
46 }) 48 })
47 } 49 }
48 } 50 }