Commit bf3e848c171c6e63c94f09cbb2df2fee01c2b9b3
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
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 | ... | ... |
pom.xml
| ... | ... | @@ -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
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<T> extends MPJBaseMapper<T> { |
| 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<T> extends MPJBaseMapper<T> { |
| 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<T> extends MPJBaseMapper<T> { |
| 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
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<FileConfigDO> { |
| 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
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
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
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java
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<TradeCartDO> { |
| 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<MpAccountDO> { |
| 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
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/refund/dto/PayRefundRespDTO.java
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
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
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
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<DeptDO> { |
| 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<MenuDO> { |
| 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<SensitiveWordDO> { |
| 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<SmsChannelDO> { |
| 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 | -} |