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

Too many changes.

To preserve performance only 100 of 169 files are displayed.

Docker-HOWTO.md
1 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 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 13 ├── yudao-server
13   -│ ├── Dockerfile
14   -│ └── nginx.conf
  14 +│ └── Dockerfile
15 15 └── yudao-ui-admin
16 16 ├── .dockerignore
17   - └── Dockerfile
  17 + ├── Dockerfile
  18 + └── nginx.conf <-- 提供基础配置,gzip压缩、api转发
18 19 ```
19 20  
20   -## Maven build (Optional)
  21 +## 构建 jar 包
21 22  
22 23 ```shell
23 24 # 创建maven缓存volume
... ... @@ -30,29 +31,19 @@ docker run -it --rm --name yudao-maven \
30 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 36 ```shell
42 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 49 - redis: port: 6379
... ...
README.md
... ... @@ -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 3 name: yudao-system
4 4  
... ... @@ -9,25 +9,22 @@ services:
9 9 restart: unless-stopped
10 10 tty: true
11 11 ports:
12   - - 13306:3306
  12 + - "3306:3306"
13 13 environment:
14 14 MYSQL_DATABASE: ${MYSQL_DATABASE:-ruoyi-vue-pro}
15 15 MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-123456}
16 16 volumes:
17 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 20 redis:
22 21 container_name: yudao-redis
23 22 image: redis:6-alpine
24 23 restart: unless-stopped
25 24 ports:
26   - - 16379:6379
  25 + - "6379:6379"
27 26 volumes:
28 27 - redis:/data
29   - networks:
30   - - yudao-network
31 28  
32 29 server:
33 30 container_name: yudao-server
... ... @@ -36,7 +33,7 @@ services:
36 33 image: yudao-server
37 34 restart: unless-stopped
38 35 ports:
39   - - 48080:48080
  36 + - "48080:48080"
40 37 environment:
41 38 # https://github.com/polovyivan/docker-pass-configs-to-container
42 39 SPRING_PROFILES_ACTIVE: local
... ... @@ -54,8 +51,6 @@ services:
54 51 --spring.datasource.dynamic.datasource.slave.username=${SLAVE_DATASOURCE_USERNAME:-root}
55 52 --spring.datasource.dynamic.datasource.slave.password=${SLAVE_DATASOURCE_PASSWORD:-123456}
56 53 --spring.redis.host=${REDIS_HOST:-yudao-redis}
57   - networks:
58   - - yudao-network
59 54 depends_on:
60 55 - mysql
61 56 - redis
... ... @@ -78,16 +73,10 @@ services:
78 73 image: yudao-admin
79 74 restart: unless-stopped
80 75 ports:
81   - - 8080:80
82   - networks:
83   - - yudao-network
  76 + - "8080:80"
84 77 depends_on:
85 78 - server
86 79  
87   -networks:
88   - yudao-network:
89   - driver: bridge
90   -
91 80 volumes:
92 81 mysql:
93 82 driver: local
... ...
... ... @@ -30,7 +30,7 @@
30 30 <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
31 31  
32 32 <properties>
33   - <revision>1.7.3-snapshot</revision>
  33 + <revision>1.8.0-snapshot</revision>
34 34 <!-- Maven 相关 -->
35 35 <java.version>1.8</java.version>
36 36 <maven.compiler.source>${java.version}</maven.compiler.source>
... ...
yudao-dependencies/pom.xml
... ... @@ -14,7 +14,7 @@
14 14 <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
15 15  
16 16 <properties>
17   - <revision>1.7.3-snapshot</revision>
  17 + <revision>1.8.0-snapshot</revision>
18 18 <!-- 统一依赖管理 -->
19 19 <spring.boot.version>2.7.13</spring.boot.version>
20 20 <!-- Web 相关 -->
... ... @@ -71,7 +71,7 @@
71 71 <justauth.version>1.0.1</justauth.version>
72 72 <jimureport.version>1.5.8</jimureport.version>
73 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 75 </properties>
76 76  
77 77 <dependencyManagement>
... ... @@ -216,10 +216,9 @@
216 216 <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 -->
217 217 <version>${dynamic-datasource.version}</version>
218 218 </dependency>
219   -
220 219 <dependency>
221 220 <groupId>com.github.yulichang</groupId>
222   - <artifactId>mybatis-plus-join-boot-starter</artifactId>
  221 + <artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->
223 222 <version>${mybatis-plus-join-boot-starter.version}</version>
224 223 </dependency>
225 224  
... ... @@ -601,8 +600,23 @@
601 600  
602 601 <dependency>
603 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 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 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 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 168 if (CollUtil.isEmpty(from)) {
169 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 4 import cn.hutool.core.util.ReflectUtil;
5 5 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
6 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 7 import cn.iocoder.yudao.framework.security.core.LoginUser;
10 8 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
11 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 12 import net.sf.jsqlparser.expression.Alias;
13 13 import net.sf.jsqlparser.expression.Expression;
14 14 import org.junit.jupiter.api.BeforeEach;
... ... @@ -23,6 +23,7 @@ import static cn.iocoder.yudao.framework.datapermission.core.rule.dept.DeptDataP
23 23 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
24 24 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
25 25 import static org.junit.jupiter.api.Assertions.*;
  26 +import static org.mockito.ArgumentMatchers.eq;
26 27 import static org.mockito.ArgumentMatchers.same;
27 28 import static org.mockito.Mockito.mockStatic;
28 29 import static org.mockito.Mockito.when;
... ... @@ -73,6 +74,8 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
73 74 LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
74 75 .setUserType(UserTypeEnum.ADMIN.getValue()));
75 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 81 NullPointerException exception = assertThrows(NullPointerException.class,
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml
... ... @@ -63,9 +63,7 @@
63 63 <dependency>
64 64 <groupId>com.github.binarywang</groupId>
65 65 <artifactId>weixin-java-pay</artifactId>
66   - <version>4.5.0</version>
67 66 </dependency>
68   - <!-- TODO 芋艿:清理 -->
69 67  
70 68 <!-- Test 测试相关 -->
71 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 34 <!-- 三方云服务相关 -->
35 35 <dependency>
36 36 <groupId>com.github.binarywang</groupId>
37   -<!-- <artifactId>weixin-java-mp</artifactId>-->
38 37 <artifactId>wx-java-mp-spring-boot-starter</artifactId>
39   - <version>4.5.0</version>
40 38 </dependency>
41 39 <dependency>
42 40 <groupId>com.github.binarywang</groupId>
43 41 <artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
44   - <version>4.5.0</version>
45 42 </dependency>
46   - <!-- TODO 芋艿:清理 -->
47 43 </dependencies>
48 44  
49 45 </project>
... ...
yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/config/YudaoCaptchaConfiguration.java
1 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 3 import cn.iocoder.yudao.framework.captcha.core.service.RedisCaptchaServiceImpl;
6 4 import com.xingyuv.captcha.properties.AjCaptchaProperties;
7 5 import com.xingyuv.captcha.service.CaptchaCacheService;
... ... @@ -15,12 +13,6 @@ import javax.annotation.Resource;
15 13 @AutoConfiguration
16 14 public class YudaoCaptchaConfiguration {
17 15  
18   - static {
19   - // 手动加载 Lock4jRedisKeyConstants 类,因为它不会被使用到
20   - // 如果不加载,会导致 Redis 监控,看到它的 Redis Key 枚举
21   - ClassUtil.loadClass(CaptchaRedisKeyConstants.class.getName());
22   - }
23   -
24 16 @Resource
25 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 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 4 * 验证码 Redis Key 枚举类
12 5 *
... ... @@ -14,12 +7,22 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.S
14 7 */
15 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 64  
65 65 <dependency>
66 66 <groupId>com.github.yulichang</groupId>
67   - <artifactId>mybatis-plus-join-boot-starter</artifactId>
  67 + <artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->
68 68 </dependency>
69   -
70 69 </dependencies>
71 70  
72 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 6 import com.baomidou.mybatisplus.core.conditions.Wrapper;
7 7 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
8 8 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  9 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
9 10 import com.baomidou.mybatisplus.core.metadata.IPage;
10 11 import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
11 12 import com.baomidou.mybatisplus.extension.toolkit.Db;
12   -import com.github.yulichang.base.MPJBaseMapper;
13 13 import org.apache.ibatis.annotations.Param;
14 14  
15 15 import java.util.Collection;
... ... @@ -17,10 +17,8 @@ import java.util.List;
17 17  
18 18 /**
19 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 23 default PageResult<T> selectPage(PageParam pageParam, @Param("ew") Wrapper<T> queryWrapper) {
26 24 // MyBatis Plus 查询
... ... @@ -46,18 +44,6 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; {
46 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 47 default Long selectCount() {
62 48 return selectCount(new QueryWrapper<T>());
63 49 }
... ... @@ -117,11 +103,6 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; {
117 103 update(update, new QueryWrapper<>());
118 104 }
119 105  
120   - /**
121   - * 根据ID 批量更新,适合大量数据更新
122   - *
123   - * @param entities 实体们
124   - */
125 106 default void updateBatch(Collection<T> entities) {
126 107 Db.updateBatchById(entities);
127 108 }
... ... @@ -130,13 +111,8 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; {
130 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 1 package cn.iocoder.yudao.framework.idempotent.core.redis;
2 2  
3   -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
4 3 import lombok.AllArgsConstructor;
5 4 import org.springframework.data.redis.core.StringRedisTemplate;
6 5  
7 6 import java.util.concurrent.TimeUnit;
8 7  
9   -import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING;
10   -
11 8 /**
12 9 * 幂等 Redis DAO
13 10 *
... ... @@ -16,9 +13,14 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.S
16 13 @AllArgsConstructor
17 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 25 private final StringRedisTemplate redisTemplate;
24 26  
... ... @@ -28,7 +30,7 @@ public class IdempotentRedisDAO {
28 30 }
29 31  
30 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 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 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 5 import org.springframework.boot.autoconfigure.AutoConfiguration;
8 6 import org.springframework.context.annotation.Bean;
9 7  
10 8 @AutoConfiguration(before = LockAutoConfiguration.class)
11 9 public class YudaoLock4jConfiguration {
12 10  
13   - static {
14   - // 手动加载 Lock4jRedisKeyConstants 类,因为它不会被使用到
15   - // 如果不加载,会导致 Redis 监控,看到它的 Redis Key 枚举
16   - ClassUtil.loadClass(Lock4jRedisKeyConstants.class.getName());
17   - }
18   -
19 11 @Bean
20 12 public DefaultLockFailureStrategy lockFailureStrategy() {
21 13 return new DefaultLockFailureStrategy();
... ...
yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/core/Lock4jRedisKeyConstants.java
1 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 4 * Lock4j Redis Key 枚举类
10 5 *
... ... @@ -12,8 +7,13 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.H
12 7 */
13 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 37 <artifactId>netty-all</artifactId>
38 38 </dependency>
39 39  
  40 +
  41 + <dependency>
  42 + <groupId>com.fasterxml.jackson.datatype</groupId>
  43 + <artifactId>jackson-datatype-jsr310</artifactId>
  44 + </dependency>
40 45 </dependencies>
41 46  
42 47 </project>
... ...
yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java
1 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 5 import org.springframework.boot.autoconfigure.AutoConfiguration;
4 6 import org.springframework.boot.autoconfigure.cache.CacheProperties;
5 7 import org.springframework.boot.context.properties.EnableConfigurationProperties;
... ... @@ -7,8 +9,15 @@ import org.springframework.cache.annotation.EnableCaching;
7 9 import org.springframework.context.annotation.Bean;
8 10 import org.springframework.context.annotation.Primary;
9 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 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 23 * Cache 配置类,基于 Redis 实现
... ... @@ -20,15 +29,19 @@ public class YudaoCacheAutoConfiguration {
20 29  
21 30 /**
22 31 * RedisCacheConfiguration Bean
23   - *
  32 + * <p>
24 33 * 参考 org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration 的 createConfiguration 方法
25 34 */
26 35 @Bean
27 36 @Primary
28 37 public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
29   - // 设置使用 JSON 序列化方式
30 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 46 // 设置 CacheProperties.Redis 的属性
34 47 CacheProperties.Redis redisProperties = cacheProperties.getRedis();
... ... @@ -47,4 +60,14 @@ public class YudaoCacheAutoConfiguration {
47 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 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 6 import org.springframework.boot.autoconfigure.AutoConfiguration;
4 7 import org.springframework.context.annotation.Bean;
5 8 import org.springframework.data.redis.connection.RedisConnectionFactory;
... ... @@ -25,9 +28,17 @@ public class YudaoRedisAutoConfiguration {
25 28 template.setKeySerializer(RedisSerializer.string());
26 29 template.setHashKeySerializer(RedisSerializer.string());
27 30 // 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。
28   - template.setValueSerializer(RedisSerializer.json());
29   - template.setHashValueSerializer(RedisSerializer.json());
  31 + template.setValueSerializer(buildRedisSerializer());
  32 + template.setHashValueSerializer(buildRedisSerializer());
30 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 60 /**
61 61 * 自定义的权限映射 Bean 们
62 62 *
63   - * @see #configure(HttpSecurity)
  63 + * @see #filterChain(HttpSecurity)
64 64 */
65 65 @Resource
66 66 private List<AuthorizeRequestsCustomizer> authorizeRequestsCustomizers;
... ... @@ -79,7 +79,7 @@ public class YudaoWebSecurityConfigurerAdapter {
79 79  
80 80 /**
81 81 * 配置 URL 的安全配置
82   - * <p>
  82 + *
83 83 * anyRequest | 匹配所有请求路径
84 84 * access | SpringEl表达式结果为true时可以访问
85 85 * anonymous | 匿名可以访问
... ... @@ -141,7 +141,6 @@ public class YudaoWebSecurityConfigurerAdapter {
141 141  
142 142 // 添加 Token Filter
143 143 httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
144   -
145 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 40  
41 41 // Redis 配置类
42 42 RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
  43 +// RedisAutoConfiguration.class, // Spring Redis 自动配置类
43 44 YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
44 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 52 // 接口信息
53 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 57 securitySchemas.keySet().forEach(key -> openAPI.addSecurityItem(new SecurityRequirement().addList(key)));
57 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 47 @Parameter(name = "processInstanceId", description = "流程实例的编号", required = true)
48 48 @PreAuthorize("@ss.hasPermission('bpm:task:query')")
49 49 public CommonResult<List<BpmTaskRespVO>> getTaskListByProcessInstanceId(
50   - @RequestParam("processInstanceId") String processInstanceId) {
  50 + @RequestParam("processInstanceId") String processInstanceId) {
51 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 73  
74 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 83 BpmTaskTodoPageItemRespVO.ProcessInstance convert(ProcessInstance processInstance, AdminUserRespDTO startUser);
82 84  
83 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 15 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
16 16 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
17 17 import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper;
18   -import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
19 18 import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants;
  19 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
20 20 import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
21 21 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
22 22 import cn.iocoder.yudao.module.system.api.dept.PostApi;
... ... @@ -89,7 +89,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
89 89  
90 90 @Override
91 91 public List<BpmTaskAssignRuleDO> getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId,
92   - String taskDefinitionKey) {
  92 + String taskDefinitionKey) {
93 93 return taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, taskDefinitionKey);
94 94 }
95 95  
... ... @@ -128,14 +128,14 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
128 128 validTaskAssignRuleOptions(reqVO.getType(), reqVO.getOptions());
129 129 // 校验是否已经配置
130 130 BpmTaskAssignRuleDO existRule =
131   - taskRuleMapper.selectListByModelIdAndTaskDefinitionKey(reqVO.getModelId(), reqVO.getTaskDefinitionKey());
  131 + taskRuleMapper.selectListByModelIdAndTaskDefinitionKey(reqVO.getModelId(), reqVO.getTaskDefinitionKey());
132 132 if (existRule != null) {
133 133 throw exception(TASK_ASSIGN_RULE_EXISTS, reqVO.getModelId(), reqVO.getTaskDefinitionKey());
134 134 }
135 135  
136 136 // 存储
137 137 BpmTaskAssignRuleDO rule = BpmTaskAssignRuleConvert.INSTANCE.convert(reqVO)
138   - .setProcessDefinitionId(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL); // 只有流程模型,才允许新建
  138 + .setProcessDefinitionId(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL); // 只有流程模型,才允许新建
139 139 taskRuleMapper.insert(rule);
140 140 return rule.getId();
141 141 }
... ... @@ -169,14 +169,14 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
169 169  
170 170 // 遍历,匹配对应的规则
171 171 Map<String, BpmTaskAssignRuleRespVO> processInstanceRuleMap =
172   - CollectionUtils.convertMap(processInstanceRules, BpmTaskAssignRuleRespVO::getTaskDefinitionKey);
  172 + CollectionUtils.convertMap(processInstanceRules, BpmTaskAssignRuleRespVO::getTaskDefinitionKey);
173 173 for (BpmTaskAssignRuleRespVO modelRule : modelRules) {
174 174 BpmTaskAssignRuleRespVO processInstanceRule = processInstanceRuleMap.get(modelRule.getTaskDefinitionKey());
175 175 if (processInstanceRule == null) {
176 176 return false;
177 177 }
178 178 if (!ObjectUtil.equals(modelRule.getType(), processInstanceRule.getType()) || !ObjectUtil.equal(
179   - modelRule.getOptions(), processInstanceRule.getOptions())) {
  179 + modelRule.getOptions(), processInstanceRule.getOptions())) {
180 180 return false;
181 181 }
182 182 }
... ... @@ -192,7 +192,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
192 192 // 开始复制
193 193 List<BpmTaskAssignRuleDO> newRules = BpmTaskAssignRuleConvert.INSTANCE.convertList2(rules);
194 194 newRules.forEach(rule -> rule.setProcessDefinitionId(toProcessDefinitionId).setId(null).setCreateTime(null)
195   - .setUpdateTime(null));
  195 + .setUpdateTime(null));
196 196 taskRuleMapper.insertBatch(newRules);
197 197 }
198 198  
... ... @@ -215,7 +215,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
215 215 if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.ROLE.getType())) {
216 216 roleApi.validRoleList(options);
217 217 } else if (ObjectUtils.equalsAny(type, BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(),
218   - BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType())) {
  218 + BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType())) {
219 219 deptApi.validateDeptList(options);
220 220 } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.POST.getType())) {
221 221 postApi.validPostList(options);
... ... @@ -225,7 +225,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
225 225 userGroupService.validUserGroups(options);
226 226 } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.SCRIPT.getType())) {
227 227 dictDataApi.validateDictDataList(DictTypeConstants.TASK_ASSIGN_SCRIPT,
228   - CollectionUtils.convertSet(options, String::valueOf));
  228 + CollectionUtils.convertSet(options, String::valueOf));
229 229 } else {
230 230 throw new IllegalArgumentException(format("未知的规则类型({})", type));
231 231 }
... ... @@ -298,7 +298,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
298 298 }
299 299  
300 300 private Set<Long> calculateTaskCandidateUsersByPost(BpmTaskAssignRuleDO rule) {
301   - List<AdminUserRespDTO> users = adminUserApi.getUsersByPostIds(rule.getOptions());
  301 + List<AdminUserRespDTO> users = adminUserApi.getUserListByPostIds(rule.getOptions());
302 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 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 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 40 // mock 方法(startUser)
41 41 AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptId(10L));
42 42 when(adminUserApi.getUser(eq(1L))).thenReturn(startUser);
  43 + // mock 方法(getStartUserDept)没有部门
  44 + when(deptApi.getDept(eq(10L))).thenReturn(null);
43 45  
44 46 // 调用
45 47 Set<Long> result = script.calculateTaskCandidateUsers(execution);
... ... @@ -56,7 +58,9 @@ public class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest {
56 58 when(adminUserApi.getUser(eq(1L))).thenReturn(startUser);
57 59 DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L)
58 60 .setLeaderUserId(20L));
  61 + // mock 方法(getDept)
59 62 when(deptApi.getDept(eq(10L))).thenReturn(startUserDept);
  63 + when(deptApi.getDept(eq(100L))).thenReturn(null);
60 64  
61 65 // 调用
62 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 120 // mock 方法
121 121 List<AdminUserRespDTO> users = CollectionUtils.convertList(asSet(11L, 22L),
122 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 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 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 30 @Import(BpmUserGroupServiceImpl.class)
31 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 2 GET {{baseUrl}}/infra/redis/get-monitor-info
3 3 Authorization: Bearer {{token}}
4 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 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 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 4 import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisMonitorRespVO;
11 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 6 import io.swagger.v3.oas.annotations.Operation;
  7 +import io.swagger.v3.oas.annotations.tags.Tag;
15 8 import org.springframework.data.redis.connection.RedisServerCommands;
16   -import org.springframework.data.redis.core.Cursor;
17 9 import org.springframework.data.redis.core.RedisCallback;
18   -import org.springframework.data.redis.core.ScanOptions;
19 10 import org.springframework.data.redis.core.StringRedisTemplate;
20 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 16 import javax.annotation.Resource;
24   -import java.util.*;
  17 +import java.util.Properties;
25 18  
26 19 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
27 20  
... ... @@ -47,66 +40,4 @@ public class RedisController {
47 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 1 package cn.iocoder.yudao.module.infra.convert.redis;
2 2  
3 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 4 import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisMonitorRespVO;
7 5 import org.mapstruct.Mapper;
8 6 import org.mapstruct.factory.Mappers;
9 7  
10 8 import java.util.ArrayList;
11   -import java.util.List;
12 9 import java.util.Properties;
13 10  
14 11 @Mapper
... ... @@ -29,6 +26,4 @@ public interface RedisConvert {
29 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 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO;
7 7 import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
8 8 import org.apache.ibatis.annotations.Mapper;
  9 +import org.apache.ibatis.annotations.Select;
  10 +
  11 +import java.time.LocalDateTime;
9 12  
10 13 @Mapper
11 14 public interface FileConfigMapper extends BaseMapperX<FileConfigDO> {
... ... @@ -18,4 +21,7 @@ public interface FileConfigMapper extends BaseMapperX&lt;FileConfigDO&gt; {
18 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 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 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 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 17 public interface FileConfigService {
18 18  
19 19 /**
20   - * 初始化文件客户端
21   - */
22   - void initLocalCache();
23   -
24   - /**
25 20 * 创建文件配置
26 21 *
27 22 * @param createReqVO 创建信息
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java
1 1 package cn.iocoder.yudao.module.infra.service.file;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.io.resource.ResourceUtil;
4 5 import cn.hutool.core.util.IdUtil;
5 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
6 8 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
7 9 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
8 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 17 import cn.iocoder.yudao.module.infra.convert.file.FileConfigConvert;
16 18 import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
17 19 import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper;
18   -import cn.iocoder.yudao.module.infra.mq.producer.file.FileConfigProducer;
19 20 import lombok.Getter;
20 21 import lombok.extern.slf4j.Slf4j;
  22 +import org.springframework.scheduling.annotation.Scheduled;
21 23 import org.springframework.stereotype.Service;
22 24 import org.springframework.transaction.annotation.Transactional;
23   -import org.springframework.transaction.support.TransactionSynchronization;
24   -import org.springframework.transaction.support.TransactionSynchronizationManager;
25 25 import org.springframework.validation.annotation.Validated;
26 26  
27 27 import javax.annotation.PostConstruct;
28 28 import javax.annotation.Resource;
29 29 import javax.validation.Validator;
  30 +import java.time.LocalDateTime;
30 31 import java.util.List;
31 32 import java.util.Map;
  33 +import java.util.concurrent.TimeUnit;
32 34  
33 35 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
34 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 48  
47 49 @Resource
48 50 private FileClientFactory fileClientFactory;
  51 +
  52 + /**
  53 + * 文件配置的缓存
  54 + */
  55 + @Getter
  56 + private List<FileConfigDO> fileConfigCache;
49 57 /**
50 58 * Master FileClient 对象,有且仅有一个,即 {@link FileConfigDO#getMaster()} 对应的
51 59 */
... ... @@ -56,12 +64,8 @@ public class FileConfigServiceImpl implements FileConfigService {
56 64 private FileConfigMapper fileConfigMapper;
57 65  
58 66 @Resource
59   - private FileConfigProducer fileConfigProducer;
60   -
61   - @Resource
62 67 private Validator validator;
63 68  
64   - @Override
65 69 @PostConstruct
66 70 public void initLocalCache() {
67 71 // 第一步:查询数据
... ... @@ -76,6 +80,27 @@ public class FileConfigServiceImpl implements FileConfigService {
76 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 106 @Override
... ... @@ -85,9 +110,9 @@ public class FileConfigServiceImpl implements FileConfigService {
85 110 .setConfig(parseClientConfig(createReqVO.getStorage(), createReqVO.getConfig()))
86 111 .setMaster(false); // 默认非 master
87 112 fileConfigMapper.insert(fileConfig);
88   - // 发送刷新配置的消息
89   - fileConfigProducer.sendFileConfigRefreshMessage();
90   - // 返回
  113 +
  114 + // 刷新缓存
  115 + initLocalCache();
91 116 return fileConfig.getId();
92 117 }
93 118  
... ... @@ -99,8 +124,9 @@ public class FileConfigServiceImpl implements FileConfigService {
99 124 FileConfigDO updateObj = FileConfigConvert.INSTANCE.convert(updateReqVO)
100 125 .setConfig(parseClientConfig(config.getStorage(), updateReqVO.getConfig()));
101 126 fileConfigMapper.updateById(updateObj);
102   - // 发送刷新配置的消息
103   - fileConfigProducer.sendFileConfigRefreshMessage();
  127 +
  128 + // 刷新缓存
  129 + initLocalCache();
104 130 }
105 131  
106 132 @Override
... ... @@ -112,15 +138,9 @@ public class FileConfigServiceImpl implements FileConfigService {
112 138 fileConfigMapper.updateBatch(new FileConfigDO().setMaster(false));
113 139 // 更新
114 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 146 private FileClientConfig parseClientConfig(Integer storage, Map<String, Object> config) {
... ... @@ -143,8 +163,9 @@ public class FileConfigServiceImpl implements FileConfigService {
143 163 }
144 164 // 删除
145 165 fileConfigMapper.deleteById(id);
146   - // 发送刷新配置的消息
147   - fileConfigProducer.sendFileConfigRefreshMessage();
  166 +
  167 + // 刷新缓存
  168 + initLocalCache();
148 169 }
149 170  
150 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 16 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigUpdateReqVO;
17 17 import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
18 18 import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper;
19   -import cn.iocoder.yudao.module.infra.mq.producer.file.FileConfigProducer;
20 19 import lombok.Data;
21 20 import org.junit.jupiter.api.Test;
22 21 import org.springframework.boot.test.mock.mockito.MockBean;
... ... @@ -56,8 +55,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
56 55 private FileConfigMapper fileConfigMapper;
57 56  
58 57 @MockBean
59   - private FileConfigProducer fileConfigProducer;
60   - @MockBean
61 58 private Validator validator;
62 59 @MockBean
63 60 private FileClientFactory fileClientFactory;
... ... @@ -81,6 +78,10 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
81 78 verify(fileClientFactory).createOrUpdateFileClient(eq(2L),
82 79 eq(configDO2.getStorage()), eq(configDO2.getConfig()));
83 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 87 @Test
... ... @@ -101,8 +102,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
101 102 assertFalse(fileConfig.getMaster());
102 103 assertEquals("/yunai", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath());
103 104 assertEquals("https://www.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain());
104   - // verify 调用
105   - verify(fileConfigProducer).sendFileConfigRefreshMessage();
106 105 }
107 106  
108 107 @Test
... ... @@ -126,8 +125,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
126 125 assertPojoEquals(reqVO, fileConfig, "config");
127 126 assertEquals("/yunai2", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath());
128 127 assertEquals("https://doc.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain());
129   - // verify 调用
130   - verify(fileConfigProducer).sendFileConfigRefreshMessage();
131 128 }
132 129  
133 130 @Test
... ... @@ -152,8 +149,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
152 149 // 断言数据
153 150 assertTrue(fileConfigMapper.selectById(dbFileConfig.getId()).getMaster());
154 151 assertFalse(fileConfigMapper.selectById(masterFileConfig.getId()).getMaster());
155   - // verify 调用
156   - verify(fileConfigProducer).sendFileConfigRefreshMessage();
157 152 }
158 153  
159 154 @Test
... ... @@ -174,8 +169,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
174 169 fileConfigService.deleteFileConfig(id);
175 170 // 校验数据不存在了
176 171 assertNull(fileConfigMapper.selectById(id));
177   - // verify 调用
178   - verify(fileConfigProducer).sendFileConfigRefreshMessage();
179 172 }
180 173  
181 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 19  
20 20 default TradeCartDO selectByUserIdAndSkuId(Long userId, Long skuId,
21 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 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 import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountPageReqVO;
7 7 import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
8 8 import org.apache.ibatis.annotations.Mapper;
  9 +import org.apache.ibatis.annotations.Select;
  10 +
  11 +import java.time.LocalDateTime;
9 12  
10 13 @Mapper
11 14 public interface MpAccountMapper extends BaseMapperX<MpAccountDO> {
... ... @@ -22,4 +25,7 @@ public interface MpAccountMapper extends BaseMapperX&lt;MpAccountDO&gt; {
22 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 1 package cn.iocoder.yudao.module.mp.service.account;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.util.ObjUtil;
4 5 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
5 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
7 7 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
8 8 import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountCreateReqVO;
9 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 13 import cn.iocoder.yudao.module.mp.dal.mysql.account.MpAccountMapper;
14 14 import cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants;
15 15 import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory;
16   -import cn.iocoder.yudao.module.mp.mq.producer.MpAccountProducer;
17 16 import com.google.common.annotations.VisibleForTesting;
18 17 import lombok.Getter;
19 18 import lombok.extern.slf4j.Slf4j;
... ... @@ -21,15 +20,20 @@ import me.chanjar.weixin.common.error.WxErrorException;
21 20 import me.chanjar.weixin.mp.api.WxMpService;
22 21 import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
23 22 import org.springframework.context.annotation.Lazy;
  23 +import org.springframework.scheduling.annotation.Scheduled;
24 24 import org.springframework.stereotype.Service;
25 25 import org.springframework.validation.annotation.Validated;
26 26  
27 27 import javax.annotation.PostConstruct;
28 28 import javax.annotation.Resource;
  29 +import java.time.LocalDateTime;
29 30 import java.util.List;
30 31 import java.util.Map;
  32 +import java.util.concurrent.TimeUnit;
31 33  
32 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 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 62 @Lazy // 延迟加载,解决循环依赖的问题
59 63 private MpServiceFactory mpServiceFactory;
60 64  
61   - @Resource
62   - private MpAccountProducer mpAccountProducer;
63   -
64 65 @Override
65 66 @PostConstruct
66 67 public void initLocalCache() {
... ... @@ -72,7 +73,30 @@ public class MpAccountServiceImpl implements MpAccountService {
72 73  
73 74 // 第二步:构建缓存。创建或更新支付 Client
74 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 109 MpAccountDO account = MpAccountConvert.INSTANCE.convert(createReqVO);
86 110 mpAccountMapper.insert(account);
87 111  
88   - // 发送刷新消息
89   - mpAccountProducer.sendAccountRefreshMessage();
  112 + // 刷新缓存
  113 + initLocalCache();
90 114 return account.getId();
91 115 }
92 116  
... ... @@ -101,8 +125,8 @@ public class MpAccountServiceImpl implements MpAccountService {
101 125 MpAccountDO updateObj = MpAccountConvert.INSTANCE.convert(updateReqVO);
102 126 mpAccountMapper.updateById(updateObj);
103 127  
104   - // 发送刷新消息
105   - mpAccountProducer.sendAccountRefreshMessage();
  128 + // 刷新缓存
  129 + initLocalCache();
106 130 }
107 131  
108 132 @Override
... ... @@ -112,8 +136,8 @@ public class MpAccountServiceImpl implements MpAccountService {
112 136 // 删除
113 137 mpAccountMapper.deleteById(id);
114 138  
115   - // 发送刷新消息
116   - mpAccountProducer.sendAccountRefreshMessage();
  139 + // 刷新缓存
  140 + initLocalCache();
117 141 }
118 142  
119 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 6 /**
7 7 * 支付单信息 Response DTO
8 8 *
9   - * TODO 芋艿:还没定好字段
10   - *
11 9 * @author 芋道源码
12 10 */
13 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 /**
9 9 * 退款单信息 Response DTO
10 10 *
11   - * TODO 芋艿:还没定好字段
12   - *
13 11 * @author 芋道源码
14 12 */
15 13 @Data
... ...
yudao-module-pay/yudao-module-pay-biz/pom.xml
... ... @@ -61,12 +61,6 @@
61 61 <artifactId>yudao-spring-boot-starter-job</artifactId>
62 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 64 <!-- Test 测试相关 -->
71 65 <dependency>
72 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 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 4 * 支付 Redis Key 枚举类
8 5 *
... ... @@ -10,9 +7,14 @@ import org.redisson.api.RLock;
10 7 */
11 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 33 }
34 34  
35 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 15 import cn.iocoder.yudao.module.pay.convert.channel.PayChannelConvert;
16 16 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
17 17 import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper;
18   -import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants;
19 18 import lombok.Getter;
20 19 import lombok.Setter;
21 20 import lombok.extern.slf4j.Slf4j;
... ... @@ -112,7 +111,7 @@ public class PayChannelServiceImpl implements PayChannelService {
112 111 channelMapper.insert(channel);
113 112  
114 113 // 刷新缓存
115   - refreshLocalCache();
  114 + initLocalCache();
116 115 return channel.getId();
117 116 }
118 117  
... ... @@ -127,7 +126,7 @@ public class PayChannelServiceImpl implements PayChannelService {
127 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 159 channelMapper.deleteById(id);
161 160  
162 161 // 刷新缓存
163   - refreshLocalCache();
  162 + initLocalCache();
164 163 }
165 164  
166 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 44 * @param postIds 岗位数组
45 45 * @return 用户数组
46 46 */
47   - List<AdminUserRespDTO> getUsersByPostIds(Collection<Long> postIds);
  47 + List<AdminUserRespDTO> getUserListByPostIds(Collection<Long> postIds);
48 48  
49 49 /**
50 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 21  
22 22 @Override
23 23 public Set<Long> getUserRoleIdListByRoleIds(Collection<Long> roleIds) {
24   - return permissionService.getUserRoleIdListByRoleIds(roleIds);
  24 + return permissionService.getUserRoleIdListByRoleId(roleIds);
25 25 }
26 26  
27 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 40 }
41 41  
42 42 @Override
43   - public List<AdminUserRespDTO> getUsersByPostIds(Collection<Long> postIds) {
  43 + public List<AdminUserRespDTO> getUserListByPostIds(Collection<Long> postIds) {
44 44 List<AdminUserDO> users = userService.getUserListByPostIds(postIds);
45 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 2 POST {{baseUrl}}/system/auth/login
3 3 Content-Type: application/json
4 4 tenant-id: {{adminTenentId}}
  5 +tag: Yunai.local
5 6  
6 7 {
7 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 3 import cn.hutool.core.util.StrUtil;
4 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6   -import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
7 6 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
8 7 import cn.iocoder.yudao.framework.security.config.SecurityProperties;
9 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 11 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
13 12 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
14 13 import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
15   -import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;
16 14 import cn.iocoder.yudao.module.system.service.auth.AdminAuthService;
  15 +import cn.iocoder.yudao.module.system.service.permission.MenuService;
17 16 import cn.iocoder.yudao.module.system.service.permission.PermissionService;
18 17 import cn.iocoder.yudao.module.system.service.permission.RoleService;
19 18 import cn.iocoder.yudao.module.system.service.social.SocialUserService;
... ... @@ -34,9 +33,9 @@ import java.util.List;
34 33 import java.util.Set;
35 34  
36 35 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  36 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
37 37 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
38 38 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.obtainAuthorization;
39   -import static java.util.Collections.singleton;
40 39  
41 40 @Tag(name = "管理后台 - 认证")
42 41 @RestController
... ... @@ -52,6 +51,8 @@ public class AuthController {
52 51 @Resource
53 52 private RoleService roleService;
54 53 @Resource
  54 + private MenuService menuService;
  55 + @Resource
55 56 private PermissionService permissionService;
56 57 @Resource
57 58 private SocialUserService socialUserService;
... ... @@ -91,33 +92,24 @@ public class AuthController {
91 92 @GetMapping("/get-permission-info")
92 93 @Operation(summary = "获取登录用户的权限信息")
93 94 public CommonResult<AuthPermissionInfoRespVO> getPermissionInfo() {
94   - // 获得用户信息
  95 + // 1.1 获得用户信息
95 96 AdminUserDO user = userService.getUser(getLoginUserId());
96 97 if (user == null) {
97 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 141 @Parameter(name = "redirectUri", description = "回调路径")
150 142 })
151 143 public CommonResult<String> socialLogin(@RequestParam("type") Integer type,
152   - @RequestParam("redirectUri") String redirectUri) {
  144 + @RequestParam("redirectUri") String redirectUri) {
153 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 6 import lombok.Data;
7 7 import lombok.NoArgsConstructor;
8 8  
  9 +import java.util.List;
9 10 import java.util.Set;
10 11  
11   -@Schema(description = "管理后台 - 登录用户的权限信息 Response VO,额外包括用户信息和角色列表")
  12 +@Schema(description = "管理后台 - 登录用户的权限信息 Response VO,额外包括用户信息和角色列表")
12 13 @Data
13 14 @NoArgsConstructor
14 15 @AllArgsConstructor
... ... @@ -24,6 +25,9 @@ public class AuthPermissionInfoRespVO {
24 25 @Schema(description = "操作权限数组", requiredMode = Schema.RequiredMode.REQUIRED)
25 26 private Set<String> permissions;
26 27  
  28 + @Schema(description = "菜单树", required = true)
  29 + private List<MenuVO> menus;
  30 +
27 31 @Schema(description = "用户信息 VO")
28 32 @Data
29 33 @NoArgsConstructor
... ... @@ -37,9 +41,53 @@ public class AuthPermissionInfoRespVO {
37 41 @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
38 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 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 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 17 @Data
18 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 37  
38 38 @Operation(summary = "获得角色拥有的菜单编号")
39 39 @Parameter(name = "roleId", description = "角色编号", required = true)
40   - @GetMapping("/list-role-resources")
  40 + @GetMapping("/list-role-menus")
41 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 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 40 GET {{baseUrl}}/system/role/page?pageNo=1&pageSize=10
41 41 Authorization: Bearer {{token}}
42 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 20 // TODO 这里要多一个枚举校验
21 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 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 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 13 @Data
14 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 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 13 @Data
14 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 25 @Length(min = 11, max = 11, message = "手机号长度必须 11 位")
26 26 private String mobile;
27 27  
28   - @Schema(description = "用户性别-参见 SexEnum 枚举类", example = "1")
  28 + @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1")
29 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 29 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
30 30 private LocalDateTime[] createTime;
31 31  
32   - @Schema(description = "部门编号,同时筛选子部门", example = "1024")
  32 + @Schema(description = "部门编号,同时筛选子部门", example = "1024")
33 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 6 import lombok.EqualsAndHashCode;
7 7 import lombok.NoArgsConstructor;
8 8  
9   -@Schema(description = "管理后台 - 用户分页时的信息 Response VO,相比用户基本信息来说,会多部门信息")
  9 +@Schema(description = "管理后台 - 用户分页时的信息 Response VO,相比用户基本信息来说,会多部门信息")
10 10 @Data
11 11 @NoArgsConstructor
12 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 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 5 import io.swagger.v3.oas.annotations.media.Schema;
4 6 import lombok.Data;
5 7  
... ... @@ -13,9 +15,9 @@ public class UserUpdateStatusReqVO {
13 15 @NotNull(message = "角色编号不能为空")
14 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 19 @NotNull(message = "状态不能为空")
18   -// @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
  20 + @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
19 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 8 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
9 9 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
10 10 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
  11 +import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;
11 12 import org.mapstruct.Mapper;
12 13 import org.mapstruct.factory.Mappers;
13 14 import org.slf4j.LoggerFactory;
... ... @@ -27,13 +28,16 @@ public interface AuthConvert {
27 28  
28 29 default AuthPermissionInfoRespVO convert(AdminUserDO user, List<RoleDO> roleList, List<MenuDO> menuList) {
29 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 45 * @param menuList 菜单列表
42 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 52 menuList.sort(Comparator.comparing(MenuDO::getSort));
  53 +
47 54 // 构建菜单树
48 55 // 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。
49   - Map<Long, AuthMenuRespVO> treeNodeMap = new LinkedHashMap<>();
  56 + Map<Long, AuthPermissionInfoRespVO.MenuVO> treeNodeMap = new LinkedHashMap<>();
50 57 menuList.forEach(menu -> treeNodeMap.put(menu.getId(), AuthConvert.INSTANCE.convertTreeNode(menu)));
51 58 // 处理父子关系
52 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 62 if (parentNode == null) {
56 63 LoggerFactory.getLogger(getClass()).error("[buildRouterTree][resource({}) 找不到父资源({})]",
57   - childNode.getId(), childNode.getParentId());
  64 + childNode.getId(), childNode.getParentId());
58 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 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
7 7 import org.apache.ibatis.annotations.Mapper;
8 8  
  9 +import java.util.Collection;
9 10 import java.util.List;
10 11  
11 12 @Mapper
... ... @@ -25,4 +26,8 @@ public interface DeptMapper extends BaseMapperX&lt;DeptDO&gt; {
25 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 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 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 4 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
5 5 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
6   -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
7 6 import org.apache.ibatis.annotations.Mapper;
8   -import org.springframework.stereotype.Repository;
9 7  
10 8 import java.util.Collection;
11 9 import java.util.List;
... ... @@ -13,14 +11,18 @@ import java.util.List;
13 11 @Mapper
14 12 public interface RoleMenuMapper extends BaseMapperX<RoleMenuDO> {
15 13  
16   - @Repository
17   - class BatchInsertMapper extends ServiceImpl<RoleMenuMapper, RoleMenuDO> {
18   - }
19   -
20 14 default List<RoleMenuDO> selectListByRoleId(Long roleId) {
21 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 26 default void deleteListByRoleIdAndMenuIds(Long roleId, Collection<Long> menuIds) {
25 27 delete(new LambdaQueryWrapper<RoleMenuDO>()
26 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 import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO;
8 8 import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
9 9 import org.apache.ibatis.annotations.Mapper;
  10 +import org.apache.ibatis.annotations.Select;
10 11  
  12 +import java.time.LocalDateTime;
11 13 import java.util.List;
12 14  
13 15 /**
... ... @@ -40,4 +42,7 @@ public interface SensitiveWordMapper extends BaseMapperX&lt;SensitiveWordDO&gt; {
40 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 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
7 7 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
8 8 import org.apache.ibatis.annotations.Mapper;
  9 +import org.apache.ibatis.annotations.Select;
  10 +
  11 +import java.time.LocalDateTime;
9 12  
10 13 @Mapper
11 14 public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> {
... ... @@ -18,4 +21,7 @@ public interface SmsChannelMapper extends BaseMapperX&lt;SmsChannelDO&gt; {
18 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 1 package cn.iocoder.yudao.module.system.dal.redis;
2 2  
3   -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
4 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 6 * System Redis Key 枚举类
12 7 *
... ... @@ -14,16 +9,93 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.S
14 9 */
15 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 53 }
54 54  
55 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   -}