diff --git a/Docker-HOWTO.md b/Docker-HOWTO.md index 404f5fa4d..7a62a68f4 100644 --- a/Docker-HOWTO.md +++ b/Docker-HOWTO.md @@ -1,23 +1,24 @@ # Docker Build & Up 目标: 快速部署体验系统,帮助了解系统之间的依赖关系。 +依赖:docker compose v2,删除`name: yudao-system`,降低`version`版本为`3.3`以下,支持`docker-compose`。 ## 功能文件列表 ```text . -├── Docker-HOWTO.md -├── docker-compose.yml -├── docker.env +├── Docker-HOWTO.md +├── docker-compose.yml +├── docker.env <-- 提供docker-compose环境变量配置 ├── yudao-server -│ ├── Dockerfile -│ └── nginx.conf +│ └── Dockerfile └── yudao-ui-admin ├── .dockerignore - └── Dockerfile + ├── Dockerfile + └── nginx.conf <-- 提供基础配置,gzip压缩、api转发 ``` -## Maven build (Optional) +## 构建 jar 包 ```shell # 创建maven缓存volume @@ -30,29 +31,19 @@ docker run -it --rm --name yudao-maven \ maven mvn clean install package '-Dmaven.test.skip=true' ``` -## Docker Compose Build - -```shell -docker compose --env-file docker.env build -``` - -## Docker Compose Up +## 构建启动服务 ```shell docker compose --env-file docker.env up -d ``` -第一次执行,由于数据库未初始化,因此yudao-server容器会运行失败。执行如下命令初始化数据库: +首次运行会自动构建容器。可以通过`docker compose build [service]`来手动构建所有或某个docker镜像 -```shell -docker compose exec -T mysql \ - sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" --default-character-set=utf8mb4 ruoyi-vue-pro' \ - < ./sql/mysql/ruoyi-vue-pro.sql -``` +`--env-file docker.env`为可选参数,只是展示了通过`.env`文件配置容器启动的环境变量,`docker-compose.yml`本身已经提供足够的默认参数来正常运行系统。 -## Server:Port +## 服务器的宿主机端口映射 -- admin: http://localhost:8080 -- API: http://localhost:48080 -- mysql: root/123456, port: 3308 +- admin ui: http://localhost:8080 +- api server: http://localhost:48080 +- mysql: root/123456, port: 3306 - redis: port: 6379 diff --git a/README.md b/README.md index c17f8ecc1..60310204c 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ 系统内置多种多种业务功能,可以用于快速你的业务系统: -![功能分层](https://static.iocoder.cn/ruoyi-vue-pro-biz.png) +![功能分层](https://static.iocoder.cn/ruoyi-vue-pro-biz.png?imageView2/2/format/webp) * 系统功能 * 基础设施 diff --git a/docker-compose.yml b/docker-compose.yml index cec84d55a..e440d96c3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: "3.8" +version: "3.4" name: yudao-system @@ -9,25 +9,22 @@ services: restart: unless-stopped tty: true ports: - - 13306:3306 + - "3306:3306" environment: MYSQL_DATABASE: ${MYSQL_DATABASE:-ruoyi-vue-pro} MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-123456} volumes: - mysql:/var/lib/mysql/ - networks: - - yudao-network + - ./sql/mysql/ruoyi-vue-pro.sql:/docker-entrypoint-initdb.d/ruoyi-vue-pro.sql:ro redis: container_name: yudao-redis image: redis:6-alpine restart: unless-stopped ports: - - 16379:6379 + - "6379:6379" volumes: - redis:/data - networks: - - yudao-network server: container_name: yudao-server @@ -36,7 +33,7 @@ services: image: yudao-server restart: unless-stopped ports: - - 48080:48080 + - "48080:48080" environment: # https://github.com/polovyivan/docker-pass-configs-to-container SPRING_PROFILES_ACTIVE: local @@ -54,8 +51,6 @@ services: --spring.datasource.dynamic.datasource.slave.username=${SLAVE_DATASOURCE_USERNAME:-root} --spring.datasource.dynamic.datasource.slave.password=${SLAVE_DATASOURCE_PASSWORD:-123456} --spring.redis.host=${REDIS_HOST:-yudao-redis} - networks: - - yudao-network depends_on: - mysql - redis @@ -78,16 +73,10 @@ services: image: yudao-admin restart: unless-stopped ports: - - 8080:80 - networks: - - yudao-network + - "8080:80" depends_on: - server -networks: - yudao-network: - driver: bridge - volumes: mysql: driver: local diff --git a/pom.xml b/pom.xml index 813c1d4bb..b2180e8b7 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ https://github.com/YunaiV/ruoyi-vue-pro - 1.7.3-snapshot + 1.8.0-snapshot 1.8 ${java.version} diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index d51c598e1..19fd0e1a1 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -14,7 +14,7 @@ https://github.com/YunaiV/ruoyi-vue-pro - 1.7.3-snapshot + 1.8.0-snapshot 2.7.13 @@ -71,7 +71,7 @@ 1.0.1 1.5.8 2.12.2 - 4.5.0 + 4.5.0 @@ -216,10 +216,9 @@ dynamic-datasource-spring-boot-starter ${dynamic-datasource.version} - com.github.yulichang - mybatis-plus-join-boot-starter + mybatis-plus-join-boot-starter ${mybatis-plus-join-boot-starter.version} @@ -599,10 +598,25 @@ ${justauth.version} + + com.github.binarywang + weixin-java-pay + ${weixin-java.version} + + + com.github.binarywang + weixin-java-mp + ${weixin-java.version} + com.github.binarywang wx-java-mp-spring-boot-starter - ${wx-java-mp.version} + ${weixin-java.version} + + + com.github.binarywang + wx-java-miniapp-spring-boot-starter + ${weixin-java.version} diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java index 631ef355a..e2e420838 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java @@ -204,7 +204,7 @@ public class CollectionUtils { return from.stream().filter(predicate).findFirst().orElse(null); } - public static > V getMaxValue(List from, Function valueFunc) { + public static > V getMaxValue(Collection from, Function valueFunc) { if (CollUtil.isEmpty(from)) { return null; } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRuleTest.java b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRuleTest.java index 2481b2acb..d0fe496ed 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRuleTest.java +++ b/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; import cn.hutool.core.util.ReflectUtil; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.util.collection.SetUtils; -import cn.iocoder.yudao.module.system.api.permission.PermissionApi; -import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO; import cn.iocoder.yudao.framework.security.core.LoginUser; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.yudao.module.system.api.permission.PermissionApi; +import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO; import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.Expression; import org.junit.jupiter.api.BeforeEach; @@ -23,6 +23,7 @@ import static cn.iocoder.yudao.framework.datapermission.core.rule.dept.DeptDataP import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.when; @@ -73,6 +74,8 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) .setUserType(UserTypeEnum.ADMIN.getValue())); securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); + // mock 方法(permissionApi 返回 null) + when(permissionApi.getDeptDataPermission(eq(loginUser.getId()))).thenReturn(null); // 调用 NullPointerException exception = assertThrows(NullPointerException.class, diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml b/yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml index 3effb1952..a13b7dd3a 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml @@ -63,9 +63,7 @@ com.github.binarywang weixin-java-pay - 4.5.0 - diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java index adad219ed..121aeb087 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java @@ -70,6 +70,7 @@ public class PayClientFactoryImpl implements PayClientFactory { case ALIPAY_APP: return (AbstractPayClient) new AlipayAppPayClient(channelId, (AlipayPayClientConfig) config); case ALIPAY_PC: return (AbstractPayClient) new AlipayPcPayClient(channelId, (AlipayPayClientConfig) config); case ALIPAY_BAR: return (AbstractPayClient) new AlipayBarPayClient(channelId, (AlipayPayClientConfig) config); + // 其它支付 case MOCK: return (AbstractPayClient) new MockPayClient(channelId, (MockPayClientConfig) config); } // 创建失败,错误日志 + 抛出异常 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClient.java index 071737b76..1d3f4d48b 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClient.java @@ -11,7 +11,9 @@ import java.time.LocalDateTime; import java.util.Map; /** - * 模拟支付的 PayClient 实现类, 模拟支付返回结果都是成功 + * 模拟支付的 PayClient 实现类 + * + * 模拟支付返回结果都是成功,方便大家日常流畅 * * @author jason */ @@ -25,31 +27,30 @@ public class MockPayClient extends AbstractPayClient { @Override protected void doInit() { - } @Override protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { - // 模拟支付渠道订单号为空 - return PayOrderRespDTO.successOf("", "", LocalDateTime.now(), reqDTO.getOutTradeNo(), MOCK_RESP_SUCCESS_DATA); + return PayOrderRespDTO.successOf("MOCK-P-" + reqDTO.getOutTradeNo(), "", LocalDateTime.now(), + reqDTO.getOutTradeNo(), MOCK_RESP_SUCCESS_DATA); } @Override protected PayOrderRespDTO doGetOrder(String outTradeNo) { - // 模拟支付渠道订单号为空 - return PayOrderRespDTO.successOf("", "", LocalDateTime.now(), outTradeNo, MOCK_RESP_SUCCESS_DATA); + return PayOrderRespDTO.successOf("MOCK-P-" + outTradeNo, "", LocalDateTime.now(), + outTradeNo, MOCK_RESP_SUCCESS_DATA); } @Override protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) { - // 模拟支付渠道退款单号为空 - return PayRefundRespDTO.successOf("", LocalDateTime.now(), reqDTO.getOutRefundNo(), MOCK_RESP_SUCCESS_DATA); + return PayRefundRespDTO.successOf("MOCK-R-" + reqDTO.getOutRefundNo(), LocalDateTime.now(), + reqDTO.getOutRefundNo(), MOCK_RESP_SUCCESS_DATA); } @Override protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) { - // 模拟支付渠道退款单号为空 - return PayRefundRespDTO.successOf("", LocalDateTime.now(), outRefundNo, MOCK_RESP_SUCCESS_DATA); + return PayRefundRespDTO.successOf("MOCK-R-" + outRefundNo, LocalDateTime.now(), + outRefundNo, MOCK_RESP_SUCCESS_DATA); } @Override @@ -61,4 +62,5 @@ public class MockPayClient extends AbstractPayClient { protected PayOrderRespDTO doParseOrderNotify(Map params, String body) { throw new UnsupportedOperationException("模拟支付无支付回调"); } -} \ No newline at end of file + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClientConfig.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClientConfig.java index 61f68a2c1..3e35c52dc 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClientConfig.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClientConfig.java @@ -14,7 +14,9 @@ import javax.validation.Validator; public class MockPayClientConfig implements PayClientConfig { /** - * 配置名称,如果不加任何属性, JsonUtils.parseObject2 解析会报错. 暂时加个名称 + * 配置名称 + * + * 如果不加任何属性,JsonUtils.parseObject2 解析会报错,所以暂时加个名称 */ private String name; @@ -22,4 +24,5 @@ public class MockPayClientConfig implements PayClientConfig { public void validate(Validator validator) { // 模拟支付配置无需校验 } -} \ No newline at end of file + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisKeyDefine.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisKeyDefine.java deleted file mode 100644 index c78ae44ec..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisKeyDefine.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.yudao.framework.tenant.core.redis; - -import cn.hutool.core.util.ArrayUtil; -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; - -import java.time.Duration; - -/** - * 多租户拓展的 RedisKeyDefine 实现类 - * - * 由于 Redis 不同于 MySQL 有 column 字段,无法通过类似 WHERE tenant_id = ? 的方式过滤 - * 所以需要通过在 Redis Key 上增加后缀的方式,进行租户之间的隔离。具体的步骤是: - * 1. 假设 Redis Key 是 user:%d,示例是 user:1;对应到多租户的 Redis Key 是 user:%d:%d, - * 2. 在 Redis DAO 中,需要使用 {@link #formatKey(Object...)} 方法,进行 Redis Key 的格式化 - * - * 注意,大多数情况下,并不用使用 TenantRedisKeyDefine 实现。主要的使用场景,还是 Redis Key 可能存在冲突的情况。 - * 例如说,租户 1 和 2 都有一个手机号作为 Key,则他们会存在冲突的问题 - * - * @author 芋道源码 - */ -public class TenantRedisKeyDefine extends RedisKeyDefine { - - /** - * 多租户的 KEY 模板 - */ - private static final String KEY_TEMPLATE_SUFFIX = ":%d"; - - public TenantRedisKeyDefine(String memo, String keyTemplate, KeyTypeEnum keyType, Class valueType, Duration timeout) { - super(memo, buildKeyTemplate(keyTemplate), keyType, valueType, timeout); - } - - public TenantRedisKeyDefine(String memo, String keyTemplate, KeyTypeEnum keyType, Class valueType, TimeoutTypeEnum timeoutType) { - super(memo, buildKeyTemplate(keyTemplate), keyType, valueType, timeoutType); - } - - private static String buildKeyTemplate(String keyTemplate) { - return keyTemplate + KEY_TEMPLATE_SUFFIX; - } - - @Override - public String formatKey(Object... args) { - args = ArrayUtil.append(args, TenantContextHolder.getRequiredTenantId()); - return super.formatKey(args); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/test/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisKeyDefineTest.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/test/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisKeyDefineTest.java deleted file mode 100644 index d456e011c..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/test/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisKeyDefineTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.framework.tenant.core.redis; - -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class TenantRedisKeyDefineTest { - - @Test - public void testFormatKey() { - Long tenantId = 30L; - TenantContextHolder.setTenantId(tenantId); - // 准备参数 - TenantRedisKeyDefine define = new TenantRedisKeyDefine("", "user:%d:%d", RedisKeyDefine.KeyTypeEnum.HASH, - Object.class, RedisKeyDefine.TimeoutTypeEnum.FIXED); - Long userId = 10L; - Integer userType = 1; - - // 调用 - String key = define.formatKey(userId, userType); - // 断言 - assertEquals("user:10:1:30", key); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-weixin/pom.xml b/yudao-framework/yudao-spring-boot-starter-biz-weixin/pom.xml index e2f19275e..b67e542cf 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-weixin/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-biz-weixin/pom.xml @@ -34,16 +34,12 @@ com.github.binarywang - wx-java-mp-spring-boot-starter - 4.5.0 com.github.binarywang wx-java-miniapp-spring-boot-starter - 4.5.0 - diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/config/YudaoCaptchaConfiguration.java b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/config/YudaoCaptchaConfiguration.java index 057445519..21f19efe6 100644 --- a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/config/YudaoCaptchaConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/config/YudaoCaptchaConfiguration.java @@ -1,7 +1,5 @@ package cn.iocoder.yudao.framework.captcha.config; -import cn.hutool.core.util.ClassUtil; -import cn.iocoder.yudao.framework.captcha.core.enums.CaptchaRedisKeyConstants; import cn.iocoder.yudao.framework.captcha.core.service.RedisCaptchaServiceImpl; import com.xingyuv.captcha.properties.AjCaptchaProperties; import com.xingyuv.captcha.service.CaptchaCacheService; @@ -15,12 +13,6 @@ import javax.annotation.Resource; @AutoConfiguration public class YudaoCaptchaConfiguration { - static { - // 手动加载 Lock4jRedisKeyConstants 类,因为它不会被使用到 - // 如果不加载,会导致 Redis 监控,看到它的 Redis Key 枚举 - ClassUtil.loadClass(CaptchaRedisKeyConstants.class.getName()); - } - @Resource private StringRedisTemplate stringRedisTemplate; diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/core/enums/CaptchaRedisKeyConstants.java b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/core/enums/CaptchaRedisKeyConstants.java index 3e1147c37..5fa5b5858 100644 --- a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/core/enums/CaptchaRedisKeyConstants.java +++ b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/core/enums/CaptchaRedisKeyConstants.java @@ -1,12 +1,5 @@ package cn.iocoder.yudao.framework.captcha.core.enums; -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; -import com.xingyuv.captcha.model.vo.PointVO; - -import java.time.Duration; - -import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING; - /** * 验证码 Redis Key 枚举类 * @@ -14,12 +7,22 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.S */ public interface CaptchaRedisKeyConstants { - RedisKeyDefine AJ_CAPTCHA_REQ_LIMIT = new RedisKeyDefine("验证码的请求限流", - "AJ.CAPTCHA.REQ.LIMIT-%s-%s", - STRING, Integer.class, Duration.ofSeconds(60)); // 例如说:验证失败 5 次,get 接口锁定 + /** + * 验证码的请求限流 + * + * KEY 格式:AJ.CAPTCHA.REQ.LIMIT-%s-%s + * VALUE 数据类型:String // 例如说:验证失败 5 次,get 接口锁定 + * 过期时间:60 秒 + */ + String AJ_CAPTCHA_REQ_LIMIT = "AJ.CAPTCHA.REQ.LIMIT-%s-%s"; - RedisKeyDefine AJ_CAPTCHA_RUNNING = new RedisKeyDefine("验证码的坐标", - "RUNNING:CAPTCHA:%s", // AbstractCaptchaService.REDIS_CAPTCHA_KEY - STRING, PointVO.class, Duration.ofSeconds(120)); // {"secretKey":"PP1w2Frr2KEejD2m","x":162,"y":5} + /** + * 验证码的坐标 + * + * KEY 格式:RUNNING:CAPTCHA:%s // AbstractCaptchaService.REDIS_CAPTCHA_KEY + * VALUE 数据类型:String // PointVO.class {"secretKey":"PP1w2Frr2KEejD2m","x":162,"y":5} + * 过期时间:120 秒 + */ + String AJ_CAPTCHA_RUNNING = "RUNNING:CAPTCHA:%s"; } diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml index 14a7eaf30..fb89d2673 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml @@ -64,9 +64,8 @@ com.github.yulichang - mybatis-plus-join-boot-starter + mybatis-plus-join-boot-starter - diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java index 614469e92..9eed6d0e0 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java @@ -6,10 +6,10 @@ import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import com.baomidou.mybatisplus.extension.toolkit.Db; -import com.github.yulichang.base.MPJBaseMapper; import org.apache.ibatis.annotations.Param; import java.util.Collection; @@ -17,10 +17,8 @@ import java.util.List; /** * 在 MyBatis Plus 的 BaseMapper 的基础上拓展,提供更多的能力 - *

- * 为什么继承 MPJBaseMapper 接口?支持 MyBatis Plus 多表 Join 的能力。 */ -public interface BaseMapperX extends MPJBaseMapper { +public interface BaseMapperX extends BaseMapper { default PageResult selectPage(PageParam pageParam, @Param("ew") Wrapper queryWrapper) { // MyBatis Plus 查询 @@ -46,18 +44,6 @@ public interface BaseMapperX extends MPJBaseMapper { return selectOne(new LambdaQueryWrapper().eq(field1, value1).eq(field2, value2)); } - default T selectOne(SFunction field1, Object value1, SFunction field2, Object value2, - SFunction field3, Object value3) { - return selectOne(new LambdaQueryWrapper().eq(field1, value1).eq(field2, value2) - .eq(field3, value3)); - } - - default T selectOne(SFunction field1, Object value1, SFunction field2, Object value2, - SFunction field3, Object value3, SFunction field4, Object value4) { - return selectOne(new LambdaQueryWrapper().eq(field1, value1).eq(field2, value2) - .eq(field3, value3).eq(field4, value4)); - } - default Long selectCount() { return selectCount(new QueryWrapper()); } @@ -117,11 +103,6 @@ public interface BaseMapperX extends MPJBaseMapper { update(update, new QueryWrapper<>()); } - /** - * 根据ID 批量更新,适合大量数据更新 - * - * @param entities 实体们 - */ default void updateBatch(Collection entities) { Db.updateBatchById(entities); } @@ -130,13 +111,8 @@ public interface BaseMapperX extends MPJBaseMapper { Db.updateBatchById(entities, size); } - /** - * 批量修改插入, 会根据实体的主键是否为空,更新还是修改。默认为 1000 - * - * @param entities 实体们 - */ - default void saveOrUpdateBatch(Collection entities){ - Db.saveOrUpdateBatch(entities); + default void saveOrUpdateBatch(Collection collection) { + Db.saveOrUpdateBatch(collection); } } diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/redis/IdempotentRedisDAO.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/redis/IdempotentRedisDAO.java index 05e1c6bd2..e3a79414d 100644 --- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/redis/IdempotentRedisDAO.java +++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/redis/IdempotentRedisDAO.java @@ -1,13 +1,10 @@ package cn.iocoder.yudao.framework.idempotent.core.redis; -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; import lombok.AllArgsConstructor; import org.springframework.data.redis.core.StringRedisTemplate; import java.util.concurrent.TimeUnit; -import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING; - /** * 幂等 Redis DAO * @@ -16,9 +13,14 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.S @AllArgsConstructor public class IdempotentRedisDAO { - private static final RedisKeyDefine IDEMPOTENT = new RedisKeyDefine("幂等操作", - "idempotent:%s", // 参数为 uuid - STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); + /** + * 幂等操作 + * + * KEY 格式:idempotent:%s // 参数为 uuid + * VALUE 格式:String + * 过期时间:不固定 + */ + private static final String IDEMPOTENT = "idempotent:%s"; private final StringRedisTemplate redisTemplate; @@ -28,7 +30,7 @@ public class IdempotentRedisDAO { } private static String formatKey(String key) { - return String.format(IDEMPOTENT.getKeyTemplate(), key); + return String.format(IDEMPOTENT, key); } } diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/config/YudaoLock4jConfiguration.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/config/YudaoLock4jConfiguration.java index 2986da6c8..2fc7f21ea 100644 --- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/config/YudaoLock4jConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/config/YudaoLock4jConfiguration.java @@ -1,21 +1,13 @@ package cn.iocoder.yudao.framework.lock4j.config; -import cn.hutool.core.util.ClassUtil; -import com.baomidou.lock.spring.boot.autoconfigure.LockAutoConfiguration; import cn.iocoder.yudao.framework.lock4j.core.DefaultLockFailureStrategy; -import cn.iocoder.yudao.framework.lock4j.core.Lock4jRedisKeyConstants; +import com.baomidou.lock.spring.boot.autoconfigure.LockAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.context.annotation.Bean; @AutoConfiguration(before = LockAutoConfiguration.class) public class YudaoLock4jConfiguration { - static { - // 手动加载 Lock4jRedisKeyConstants 类,因为它不会被使用到 - // 如果不加载,会导致 Redis 监控,看到它的 Redis Key 枚举 - ClassUtil.loadClass(Lock4jRedisKeyConstants.class.getName()); - } - @Bean public DefaultLockFailureStrategy lockFailureStrategy() { return new DefaultLockFailureStrategy(); diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/core/Lock4jRedisKeyConstants.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/core/Lock4jRedisKeyConstants.java index cc01a253b..693d05252 100644 --- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/core/Lock4jRedisKeyConstants.java +++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/core/Lock4jRedisKeyConstants.java @@ -1,10 +1,5 @@ package cn.iocoder.yudao.framework.lock4j.core; -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; -import org.redisson.api.RLock; - -import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.HASH; - /** * Lock4j Redis Key 枚举类 * @@ -12,8 +7,13 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.H */ public interface Lock4jRedisKeyConstants { - RedisKeyDefine LOCK4J = new RedisKeyDefine("分布式锁", - "lock4j:%s", // 参数来自 DefaultLockKeyBuilder 类 - HASH, RLock.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); // Redisson 的 Lock 锁,使用 Hash 数据结构 + /** + * 分布式锁 + * + * KEY 格式:lock4j:%s // 参数来自 DefaultLockKeyBuilder 类 + * VALUE 数据格式:HASH // RLock.class:Redisson 的 Lock 锁,使用 Hash 数据结构 + * 过期时间:不固定 + */ + String LOCK4J = "lock4j:%s"; } diff --git a/yudao-framework/yudao-spring-boot-starter-redis/pom.xml b/yudao-framework/yudao-spring-boot-starter-redis/pom.xml index 430ede255..c1a728b35 100644 --- a/yudao-framework/yudao-spring-boot-starter-redis/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-redis/pom.xml @@ -37,6 +37,11 @@ netty-all + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + diff --git a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java index 0b837569e..1442e8a83 100644 --- a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.framework.redis.config; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.redis.core.TimeoutRedisCacheManager; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.cache.CacheProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -7,8 +9,15 @@ import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.cache.RedisCacheWriter; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.RedisSerializationContext; -import org.springframework.data.redis.serializer.RedisSerializer; + +import java.util.Objects; + +import static cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration.buildRedisSerializer; /** * Cache 配置类,基于 Redis 实现 @@ -20,15 +29,19 @@ public class YudaoCacheAutoConfiguration { /** * RedisCacheConfiguration Bean - * + *

* 参考 org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration 的 createConfiguration 方法 */ @Bean @Primary public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) { - // 设置使用 JSON 序列化方式 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); - config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json())); + // 设置使用 : 单冒号,而不是双 :: 冒号,避免 Redis Desktop Manager 多余空格 + // 详细可见 https://blog.csdn.net/chuixue24/article/details/103928965 博客 + config = config.computePrefixWith(cacheName -> cacheName + StrUtil.COLON); + // 设置使用 JSON 序列化方式 + config = config.serializeValuesWith( + RedisSerializationContext.SerializationPair.fromSerializer(buildRedisSerializer())); // 设置 CacheProperties.Redis 的属性 CacheProperties.Redis redisProperties = cacheProperties.getRedis(); @@ -47,4 +60,14 @@ public class YudaoCacheAutoConfiguration { return config; } + @Bean + public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate, + RedisCacheConfiguration redisCacheConfiguration) { + // 创建 RedisCacheWriter 对象 + RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory()); + RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory); + // 创建 TenantRedisCacheManager 对象 + return new TimeoutRedisCacheManager(cacheWriter, redisCacheConfiguration); + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java index 901830c69..5904a3a2b 100644 --- a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java @@ -1,5 +1,8 @@ package cn.iocoder.yudao.framework.redis.config; +import cn.hutool.core.util.ReflectUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.connection.RedisConnectionFactory; @@ -25,9 +28,17 @@ public class YudaoRedisAutoConfiguration { template.setKeySerializer(RedisSerializer.string()); template.setHashKeySerializer(RedisSerializer.string()); // 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。 - template.setValueSerializer(RedisSerializer.json()); - template.setHashValueSerializer(RedisSerializer.json()); + template.setValueSerializer(buildRedisSerializer()); + template.setHashValueSerializer(buildRedisSerializer()); return template; } + public static RedisSerializer buildRedisSerializer() { + RedisSerializer json = RedisSerializer.json(); + // 解决 LocalDateTime 的序列化 + ObjectMapper objectMapper = (ObjectMapper) ReflectUtil.getFieldValue(json, "mapper"); + objectMapper.registerModules(new JavaTimeModule()); + return json; + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/RedisKeyDefine.java b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/RedisKeyDefine.java deleted file mode 100644 index ba4fccb66..000000000 --- a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/RedisKeyDefine.java +++ /dev/null @@ -1,113 +0,0 @@ -package cn.iocoder.yudao.framework.redis.core; - -import com.fasterxml.jackson.annotation.JsonValue; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.Getter; - -import java.time.Duration; - -/** - * Redis Key 定义类 - * - * @author 芋道源码 - */ -@Data -public class RedisKeyDefine { - - @Getter - @AllArgsConstructor - public enum KeyTypeEnum { - - STRING("String"), - LIST("List"), - HASH("Hash"), - SET("Set"), - ZSET("Sorted Set"), - STREAM("Stream"), - PUBSUB("Pub/Sub"); - - /** - * 类型 - */ - @JsonValue - private final String type; - - } - - @Getter - @AllArgsConstructor - public enum TimeoutTypeEnum { - - FOREVER(1), // 永不超时 - DYNAMIC(2), // 动态超时 - FIXED(3); // 固定超时 - - /** - * 类型 - */ - @JsonValue - private final Integer type; - - } - - /** - * Key 模板 - */ - private final String keyTemplate; - /** - * Key 类型的枚举 - */ - private final KeyTypeEnum keyType; - /** - * Value 类型 - * - * 如果是使用分布式锁,设置为 {@link java.util.concurrent.locks.Lock} 类型 - */ - private final Class valueType; - /** - * 超时类型 - */ - private final TimeoutTypeEnum timeoutType; - /** - * 过期时间 - */ - private final Duration timeout; - /** - * 备注 - */ - private final String memo; - - private RedisKeyDefine(String memo, String keyTemplate, KeyTypeEnum keyType, Class valueType, - TimeoutTypeEnum timeoutType, Duration timeout) { - this.memo = memo; - this.keyTemplate = keyTemplate; - this.keyType = keyType; - this.valueType = valueType; - this.timeout = timeout; - this.timeoutType = timeoutType; - // 添加注册表 - RedisKeyRegistry.add(this); - } - - public RedisKeyDefine(String memo, String keyTemplate, KeyTypeEnum keyType, Class valueType, Duration timeout) { - this(memo, keyTemplate, keyType, valueType, TimeoutTypeEnum.FIXED, timeout); - } - - public RedisKeyDefine(String memo, String keyTemplate, KeyTypeEnum keyType, Class valueType, TimeoutTypeEnum timeoutType) { - this(memo, keyTemplate, keyType, valueType, timeoutType, Duration.ZERO); - } - - /** - * 格式化 Key - * - * 注意,内部采用 {@link String#format(String, Object...)} 实现 - * - * @param args 格式化的参数 - * @return Key - */ - public String formatKey(Object... args) { - return String.format(keyTemplate, args); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/RedisKeyRegistry.java b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/RedisKeyRegistry.java deleted file mode 100644 index 4ef3b92f3..000000000 --- a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/RedisKeyRegistry.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.framework.redis.core; - -import java.util.ArrayList; -import java.util.List; - -/** - * {@link RedisKeyDefine} 注册表 - */ -public class RedisKeyRegistry { - - /** - * Redis RedisKeyDefine 数组 - */ - private static final List DEFINES = new ArrayList<>(); - - public static void add(RedisKeyDefine define) { - DEFINES.add(define); - } - - public static List list() { - return DEFINES; - } - - public static int size() { - return DEFINES.size(); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/TimeoutRedisCacheManager.java b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/TimeoutRedisCacheManager.java new file mode 100644 index 000000000..cfdee653d --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/TimeoutRedisCacheManager.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.framework.redis.core; + +import cn.hutool.core.util.StrUtil; +import org.springframework.boot.convert.DurationStyle; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.redis.cache.RedisCache; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.cache.RedisCacheWriter; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +/** + * 支持自定义过期时间的 {@link RedisCacheManager} 实现类 + * + * 在 {@link Cacheable#cacheNames()} 格式为 "key#ttl" 时,# 后面的 ttl 为过期时间,单位为秒 + * + * @author 芋道源码 + */ +public class TimeoutRedisCacheManager extends RedisCacheManager { + + private static final String SPLIT = "#"; + + public TimeoutRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) { + super(cacheWriter, defaultCacheConfiguration); + } + + @Override + protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) { + if (StrUtil.isEmpty(name)) { + return super.createRedisCache(name, cacheConfig); + } + // 如果使用 # 分隔,大小不为 2,则说明不使用自定义过期时间 + String[] names = StrUtil.splitToArray(name, SPLIT); + if (names.length != 2) { + return super.createRedisCache(name, cacheConfig); + } + + // 核心:通过修改 cacheConfig 的过期时间,实现自定义过期时间 + if (cacheConfig != null) { + // 移除 # 后面的 : 以及后面的内容,避免影响解析 + names[1] = StrUtil.subBefore(names[1], StrUtil.COLON, false); + // 解析时间 + Duration duration = DurationStyle.detectAndParse(names[1], ChronoUnit.SECONDS); + cacheConfig = cacheConfig.entryTtl(duration); + } + return super.createRedisCache(names[0], cacheConfig); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java index 082d84756..78aa328ad 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java +++ b/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 { /** * 自定义的权限映射 Bean 们 * - * @see #configure(HttpSecurity) + * @see #filterChain(HttpSecurity) */ @Resource private List authorizeRequestsCustomizers; @@ -79,7 +79,7 @@ public class YudaoWebSecurityConfigurerAdapter { /** * 配置 URL 的安全配置 - *

+ * * anyRequest | 匹配所有请求路径 * access | SpringEl表达式结果为true时可以访问 * anonymous | 匿名可以访问 @@ -141,7 +141,6 @@ public class YudaoWebSecurityConfigurerAdapter { // 添加 Token Filter httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); - return httpSecurity.build(); } diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbAndRedisUnitTest.java b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbAndRedisUnitTest.java index 059070d9d..f5de235f6 100644 --- a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbAndRedisUnitTest.java +++ b/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 { // Redis 配置类 RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer +// RedisAutoConfiguration.class, // Spring Redis 自动配置类 YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类 RedissonAutoConfiguration.class, // Redisson 自动高配置类 }) diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java index 32acb33cc..2481d2b74 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java +++ b/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 { // 接口信息 .info(buildInfo(properties)) // 接口安全配置 - .components(new Components().securitySchemes(securitySchemas)); + .components(new Components().securitySchemes(securitySchemas)) + .addSecurityItem(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION)); securitySchemas.keySet().forEach(key -> openAPI.addSecurityItem(new SecurityRequirement().addList(key))); return openAPI; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java index 885b03143..74b2ceb34 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java +++ b/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 { @Parameter(name = "processInstanceId", description = "流程实例的编号", required = true) @PreAuthorize("@ss.hasPermission('bpm:task:query')") public CommonResult> getTaskListByProcessInstanceId( - @RequestParam("processInstanceId") String processInstanceId) { + @RequestParam("processInstanceId") String processInstanceId) { return success(taskService.getTaskListByProcessInstanceId(processInstanceId)); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java index 1131135db..b85c4e533 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java +++ b/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 { BpmTaskDonePageItemRespVO convert2(HistoricTaskInstance bean); - @Mappings({@Mapping(source = "processInstance.id", target = "id"), - @Mapping(source = "processInstance.name", target = "name"), - @Mapping(source = "processInstance.startUserId", target = "startUserId"), - @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"), - @Mapping(source = "startUser.nickname", target = "startUserNickname")}) + @Mappings({ + @Mapping(source = "processInstance.id", target = "id"), + @Mapping(source = "processInstance.name", target = "name"), + @Mapping(source = "processInstance.startUserId", target = "startUserId"), + @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"), + @Mapping(source = "startUser.nickname", target = "startUserNickname") + }) BpmTaskTodoPageItemRespVO.ProcessInstance convert(ProcessInstance processInstance, AdminUserRespDTO startUser); default List convertList3(List tasks, diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java index 508af962d..5d6c265c5 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java +++ b/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; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO; import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper; -import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum; import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.PostApi; @@ -89,7 +89,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { @Override public List getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId, - String taskDefinitionKey) { + String taskDefinitionKey) { return taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, taskDefinitionKey); } @@ -128,14 +128,14 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { validTaskAssignRuleOptions(reqVO.getType(), reqVO.getOptions()); // 校验是否已经配置 BpmTaskAssignRuleDO existRule = - taskRuleMapper.selectListByModelIdAndTaskDefinitionKey(reqVO.getModelId(), reqVO.getTaskDefinitionKey()); + taskRuleMapper.selectListByModelIdAndTaskDefinitionKey(reqVO.getModelId(), reqVO.getTaskDefinitionKey()); if (existRule != null) { throw exception(TASK_ASSIGN_RULE_EXISTS, reqVO.getModelId(), reqVO.getTaskDefinitionKey()); } // 存储 BpmTaskAssignRuleDO rule = BpmTaskAssignRuleConvert.INSTANCE.convert(reqVO) - .setProcessDefinitionId(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL); // 只有流程模型,才允许新建 + .setProcessDefinitionId(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL); // 只有流程模型,才允许新建 taskRuleMapper.insert(rule); return rule.getId(); } @@ -169,14 +169,14 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { // 遍历,匹配对应的规则 Map processInstanceRuleMap = - CollectionUtils.convertMap(processInstanceRules, BpmTaskAssignRuleRespVO::getTaskDefinitionKey); + CollectionUtils.convertMap(processInstanceRules, BpmTaskAssignRuleRespVO::getTaskDefinitionKey); for (BpmTaskAssignRuleRespVO modelRule : modelRules) { BpmTaskAssignRuleRespVO processInstanceRule = processInstanceRuleMap.get(modelRule.getTaskDefinitionKey()); if (processInstanceRule == null) { return false; } if (!ObjectUtil.equals(modelRule.getType(), processInstanceRule.getType()) || !ObjectUtil.equal( - modelRule.getOptions(), processInstanceRule.getOptions())) { + modelRule.getOptions(), processInstanceRule.getOptions())) { return false; } } @@ -192,7 +192,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { // 开始复制 List newRules = BpmTaskAssignRuleConvert.INSTANCE.convertList2(rules); newRules.forEach(rule -> rule.setProcessDefinitionId(toProcessDefinitionId).setId(null).setCreateTime(null) - .setUpdateTime(null)); + .setUpdateTime(null)); taskRuleMapper.insertBatch(newRules); } @@ -215,7 +215,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.ROLE.getType())) { roleApi.validRoleList(options); } else if (ObjectUtils.equalsAny(type, BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), - BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType())) { + BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType())) { deptApi.validateDeptList(options); } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.POST.getType())) { postApi.validPostList(options); @@ -225,7 +225,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { userGroupService.validUserGroups(options); } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.SCRIPT.getType())) { dictDataApi.validateDictDataList(DictTypeConstants.TASK_ASSIGN_SCRIPT, - CollectionUtils.convertSet(options, String::valueOf)); + CollectionUtils.convertSet(options, String::valueOf)); } else { throw new IllegalArgumentException(format("未知的规则类型({})", type)); } @@ -298,7 +298,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { } private Set calculateTaskCandidateUsersByPost(BpmTaskAssignRuleDO rule) { - List users = adminUserApi.getUsersByPostIds(rule.getOptions()); + List users = adminUserApi.getUserListByPostIds(rule.getOptions()); return convertSet(users, AdminUserRespDTO::getId); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index dfe145aea..56115d79b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -1 +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. * * HistoricProcessInstance & ProcessInstance 的关系: * 1. * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @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 getProcessInstances(Set ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 PageResult pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return new PageResult<>(pageResult.getTotal()); } // 获得流程 Task Map List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId); Map> 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 getHistoricProcessInstances(Set 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 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(); } } \ No newline at end of file +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. * * HistoricProcessInstance & ProcessInstance 的关系: * 1. * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @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 getProcessInstances(Set ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 PageResult pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return new PageResult<>(pageResult.getTotal()); } // 获得流程 Task Map List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId); Map> 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 getHistoricProcessInstances(Set 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 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(); } } \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2ScriptTest.java b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2ScriptTest.java index 4e88b9c58..f46278083 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2ScriptTest.java +++ b/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 { // mock 方法(startUser) AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptId(10L)); when(adminUserApi.getUser(eq(1L))).thenReturn(startUser); + // mock 方法(getStartUserDept)没有部门 + when(deptApi.getDept(eq(10L))).thenReturn(null); // 调用 Set result = script.calculateTaskCandidateUsers(execution); @@ -56,7 +58,9 @@ public class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest { when(adminUserApi.getUser(eq(1L))).thenReturn(startUser); DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L) .setLeaderUserId(20L)); + // mock 方法(getDept) when(deptApi.getDept(eq(10L))).thenReturn(startUserDept); + when(deptApi.getDept(eq(100L))).thenReturn(null); // 调用 Set result = script.calculateTaskCandidateUsers(execution); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImplTest.java b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImplTest.java index a00bbbf86..7408b5425 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImplTest.java +++ b/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 { // mock 方法 List users = CollectionUtils.convertList(asSet(11L, 22L), id -> new AdminUserRespDTO().setId(id)); - when(adminUserApi.getUsersByPostIds(eq(rule.getOptions()))).thenReturn(users); + when(adminUserApi.getUserListByPostIds(eq(rule.getOptions()))).thenReturn(users); mockGetUserMap(asSet(11L, 22L)); // 调用 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupServiceTest.java b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupServiceTest.java index 36541f1ab..f67d2201a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupServiceTest.java +++ b/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 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.USER_GROUP_NOT_EXISTS; /** -* {@link BpmUserGroupServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ + * {@link BpmUserGroupServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ @Import(BpmUserGroupServiceImpl.class) public class BpmUserGroupServiceTest extends BaseDbUnitTest { diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.http b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.http index 0310dea58..8a0e70fd3 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.http +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.http @@ -2,8 +2,3 @@ GET {{baseUrl}}/infra/redis/get-monitor-info Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} - -### 请求 /infra/redis/get-key-list 接口 => 成功 -GET {{baseUrl}}/infra/redis/get-key-list -Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.java index c82b21cda..57a3d6b89 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.java @@ -1,27 +1,20 @@ package cn.iocoder.yudao.module.infra.controller.admin.redis; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; -import cn.iocoder.yudao.framework.redis.core.RedisKeyRegistry; -import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisKeyDefineRespVO; -import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisKeyValueRespVO; import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisMonitorRespVO; import cn.iocoder.yudao.module.infra.convert.redis.RedisConvert; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.data.redis.connection.RedisServerCommands; -import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.RedisCallback; -import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; -import java.util.*; +import java.util.Properties; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -47,66 +40,4 @@ public class RedisController { return success(RedisConvert.INSTANCE.build(info, dbSize, commandStats)); } - @GetMapping("/get-key-define-list") - @Operation(summary = "获得 Redis Key 模板列表") - @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')") - public CommonResult> getKeyDefineList() { - List keyDefines = RedisKeyRegistry.list(); - return success(RedisConvert.INSTANCE.convertList(keyDefines)); - } - - @GetMapping("/get-key-list") - @Operation(summary = "获得 Redis keys 键名列表") - @Parameter(name = "keyTemplate", description = "Redis Key 定义", example = "true") - @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')") - public CommonResult> getKeyDefineList(@RequestParam("keyTemplate") String keyTemplate) { - return success(getKeyDefineList0(keyTemplate)); - } - - private Set getKeyDefineList0(String keyTemplate) { - // key 格式化 - String key = StrUtil.replace(keyTemplate, "%[s|c|b|d|x|o|f|a|e|g]", parameter -> "*"); - // scan 扫描 key - Set keys = new LinkedHashSet<>(); - stringRedisTemplate.execute((RedisCallback>) connection -> { - try (Cursor cursor = connection.scan(ScanOptions.scanOptions().match(key).count(100).build())) { - cursor.forEachRemaining(value -> keys.add(StrUtil.utf8Str(value))); - } catch (Exception e) { - throw new RuntimeException(e); - } - return keys; - }); - return keys; - } - - @GetMapping("/get-key-value") - @Operation(summary = "获得 Redis key 内容") - @Parameter(name = "key", description = "Redis Key", example = "oauth2_access_token:233") - @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')") - public CommonResult getKeyValue(@RequestParam("key") String key) { - String value = stringRedisTemplate.opsForValue().get(key); - return success(new RedisKeyValueRespVO(key, value)); - } - - @DeleteMapping("/delete-key") - @Operation(summary = "删除 Redis Key") - @Parameter(name = "key", description = "Redis Key", example = "oauth2_access_token:233") - @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')") - public CommonResult deleteKey(@RequestParam("key") String key) { - stringRedisTemplate.delete(key); - return success(Boolean.TRUE); - } - - @DeleteMapping("/delete-keys") - @Operation(summary = "删除 Redis Key 根据模板") - @Parameter(name = "keyTemplate", description = "Redis Key 定义", example = "true") - @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')") - public CommonResult deleteKeys(@RequestParam("keyTemplate") String keyTemplate) { - Set keys = getKeyDefineList0(keyTemplate); - if (CollUtil.isNotEmpty(keys)) { - stringRedisTemplate.delete(keys); - } - return success(Boolean.TRUE); - } - } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/vo/RedisKeyDefineRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/vo/RedisKeyDefineRespVO.java deleted file mode 100644 index 07864e0bb..000000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/vo/RedisKeyDefineRespVO.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.redis.vo; - -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; - -import java.time.Duration; - -@Schema(description = "管理后台 - Redis Key 信息 Response VO") -@Data -@Builder -@AllArgsConstructor -public class RedisKeyDefineRespVO { - - @Schema(description = "Key 模板", requiredMode = Schema.RequiredMode.REQUIRED, example = "login_user:%s") - private String keyTemplate; - - @Schema(description = "Key 类型的枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "String") - private RedisKeyDefine.KeyTypeEnum keyType; - - @Schema(description = "Value 类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "java.lang.String") - private Class valueType; - - @Schema(description = "超时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private RedisKeyDefine.TimeoutTypeEnum timeoutType; - - @Schema(description = "过期时间,单位:毫秒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Duration timeout; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "啦啦啦啦~") - private String memo; - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/vo/RedisKeyValueRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/vo/RedisKeyValueRespVO.java deleted file mode 100644 index 393e2d199..000000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/vo/RedisKeyValueRespVO.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.redis.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; - -@Schema(description = "管理后台 - 单个 Redis Key Value Response VO") -@Data -@AllArgsConstructor -public class RedisKeyValueRespVO { - - @Schema(description = "c5f6990767804a928f4bb96ca249febf", requiredMode = Schema.RequiredMode.REQUIRED, example = "String") - private String key; - - @Schema(requiredMode = Schema.RequiredMode.REQUIRED, example = "String") - private String value; - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/redis/RedisConvert.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/redis/RedisConvert.java index af31f7e9d..0c8ca98fe 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/redis/RedisConvert.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/redis/RedisConvert.java @@ -1,14 +1,11 @@ package cn.iocoder.yudao.module.infra.convert.redis; import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; -import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisKeyDefineRespVO; import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisMonitorRespVO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; import java.util.ArrayList; -import java.util.List; import java.util.Properties; @Mapper @@ -29,6 +26,4 @@ public interface RedisConvert { return respVO; } - List convertList(List list); - } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java index a2bf16be4..3bec97e0b 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java +++ b/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; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO; import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.time.LocalDateTime; @Mapper public interface FileConfigMapper extends BaseMapperX { @@ -18,4 +21,7 @@ public interface FileConfigMapper extends BaseMapperX { .orderByDesc(FileConfigDO::getId)); } + @Select("SELECT COUNT(*) FROM infra_file_config WHERE update_time > #{maxUpdateTime}") + Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime); + } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/file/FileConfigRefreshConsumer.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/file/FileConfigRefreshConsumer.java deleted file mode 100644 index a3b41197f..000000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/file/FileConfigRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.infra.mq.consumer.file; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; -import cn.iocoder.yudao.module.infra.mq.message.file.FileConfigRefreshMessage; -import cn.iocoder.yudao.module.infra.service.file.FileConfigService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link FileConfigRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class FileConfigRefreshConsumer extends AbstractChannelMessageListener { - - @Resource - private FileConfigService fileConfigService; - - @Override - public void onMessage(FileConfigRefreshMessage message) { - log.info("[onMessage][收到 FileConfig 刷新消息]"); - fileConfigService.initLocalCache(); - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/package-info.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/package-info.java index faba2d870..5ba526413 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/package-info.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/package-info.java @@ -1,4 +1,4 @@ /** - * 占位符,避免缩进 + * 消息队列的消费者 */ package cn.iocoder.yudao.module.infra.mq.consumer; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/file/FileConfigRefreshMessage.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/file/FileConfigRefreshMessage.java deleted file mode 100644 index 7cc120de3..000000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/file/FileConfigRefreshMessage.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.iocoder.yudao.module.infra.mq.message.file; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; -import lombok.Data; - -/** - * 文件配置数据刷新 Message - */ -@Data -public class FileConfigRefreshMessage extends AbstractChannelMessage { - - @Override - public String getChannel() { - return "infra.file-config.refresh"; - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/package-info.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/package-info.java index 380693339..19edb0241 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/package-info.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/package-info.java @@ -1,4 +1,4 @@ /** - * 占位符,避免缩进 + * 消息队列的消息 */ package cn.iocoder.yudao.module.infra.mq.message; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/file/FileConfigProducer.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/file/FileConfigProducer.java deleted file mode 100644 index a666b185d..000000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/file/FileConfigProducer.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.infra.mq.producer.file; - -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; -import cn.iocoder.yudao.module.infra.mq.message.file.FileConfigRefreshMessage; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 文件配置相关消息的 Producer - */ -@Component -public class FileConfigProducer { - - @Resource - private RedisMQTemplate redisMQTemplate; - - /** - * 发送 {@link FileConfigRefreshMessage} 消息 - */ - public void sendFileConfigRefreshMessage() { - FileConfigRefreshMessage message = new FileConfigRefreshMessage(); - redisMQTemplate.send(message); - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/package-info.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/package-info.java index f1f53a91c..343179cb8 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/package-info.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/package-info.java @@ -1,4 +1,4 @@ /** - * 占位符,避免缩进 + * 消息队列的生产者 */ package cn.iocoder.yudao.module.infra.mq.producer; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java index 43ab5bc68..1b279391a 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java @@ -16,11 +16,6 @@ import javax.validation.Valid; */ public interface FileConfigService { - /** - * 初始化文件客户端 - */ - void initLocalCache(); - /** * 创建文件配置 * diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java index 6236df6ff..628874bf2 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java @@ -1,8 +1,10 @@ package cn.iocoder.yudao.module.infra.service.file; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.util.IdUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; 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 import cn.iocoder.yudao.module.infra.convert.file.FileConfigConvert; import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO; import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper; -import cn.iocoder.yudao.module.infra.mq.producer.file.FileConfigProducer; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionSynchronization; -import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.validation.annotation.Validated; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.validation.Validator; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG_DELETE_FAIL_MASTER; @@ -46,6 +48,12 @@ public class FileConfigServiceImpl implements FileConfigService { @Resource private FileClientFactory fileClientFactory; + + /** + * 文件配置的缓存 + */ + @Getter + private List fileConfigCache; /** * Master FileClient 对象,有且仅有一个,即 {@link FileConfigDO#getMaster()} 对应的 */ @@ -55,13 +63,9 @@ public class FileConfigServiceImpl implements FileConfigService { @Resource private FileConfigMapper fileConfigMapper; - @Resource - private FileConfigProducer fileConfigProducer; - @Resource private Validator validator; - @Override @PostConstruct public void initLocalCache() { // 第一步:查询数据 @@ -76,6 +80,27 @@ public class FileConfigServiceImpl implements FileConfigService { masterFileClient = fileClientFactory.getFileClient(config.getId()); } }); + this.fileConfigCache = configs; + } + + /** + * 通过定时任务轮询,刷新缓存 + * + * 目的:多节点部署时,通过轮询”通知“所有节点,进行刷新 + */ + @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS) + public void refreshLocalCache() { + // 情况一:如果缓存里没有数据,则直接刷新缓存 + if (CollUtil.isEmpty(fileConfigCache)) { + initLocalCache(); + return; + } + + // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存 + LocalDateTime maxTime = CollectionUtils.getMaxValue(fileConfigCache, FileConfigDO::getUpdateTime); + if (fileConfigMapper.selectCountByUpdateTimeGt(maxTime) > 0) { + initLocalCache(); + } } @Override @@ -85,9 +110,9 @@ public class FileConfigServiceImpl implements FileConfigService { .setConfig(parseClientConfig(createReqVO.getStorage(), createReqVO.getConfig())) .setMaster(false); // 默认非 master fileConfigMapper.insert(fileConfig); - // 发送刷新配置的消息 - fileConfigProducer.sendFileConfigRefreshMessage(); - // 返回 + + // 刷新缓存 + initLocalCache(); return fileConfig.getId(); } @@ -99,8 +124,9 @@ public class FileConfigServiceImpl implements FileConfigService { FileConfigDO updateObj = FileConfigConvert.INSTANCE.convert(updateReqVO) .setConfig(parseClientConfig(config.getStorage(), updateReqVO.getConfig())); fileConfigMapper.updateById(updateObj); - // 发送刷新配置的消息 - fileConfigProducer.sendFileConfigRefreshMessage(); + + // 刷新缓存 + initLocalCache(); } @Override @@ -112,15 +138,9 @@ public class FileConfigServiceImpl implements FileConfigService { fileConfigMapper.updateBatch(new FileConfigDO().setMaster(false)); // 更新 fileConfigMapper.updateById(new FileConfigDO().setId(id).setMaster(true)); - // 发送刷新配置的消息 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - @Override - public void afterCommit() { - fileConfigProducer.sendFileConfigRefreshMessage(); - } - - }); + // 刷新缓存 + initLocalCache(); } private FileClientConfig parseClientConfig(Integer storage, Map config) { @@ -143,8 +163,9 @@ public class FileConfigServiceImpl implements FileConfigService { } // 删除 fileConfigMapper.deleteById(id); - // 发送刷新配置的消息 - fileConfigProducer.sendFileConfigRefreshMessage(); + + // 刷新缓存 + initLocalCache(); } private FileConfigDO validateFileConfigExists(Long id) { diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java index 656955194..079054864 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java +++ b/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 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigUpdateReqVO; import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO; import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper; -import cn.iocoder.yudao.module.infra.mq.producer.file.FileConfigProducer; import lombok.Data; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; @@ -55,8 +54,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { @Resource private FileConfigMapper fileConfigMapper; - @MockBean - private FileConfigProducer fileConfigProducer; @MockBean private Validator validator; @MockBean @@ -81,6 +78,10 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { verify(fileClientFactory).createOrUpdateFileClient(eq(2L), eq(configDO2.getStorage()), eq(configDO2.getConfig())); assertSame(masterFileClient, fileConfigService.getMasterFileClient()); + // 断言 fileConfigCache 缓存 + assertEquals(2, fileConfigService.getFileConfigCache().size()); + assertEquals(configDO1, fileConfigService.getFileConfigCache().get(0)); + assertEquals(configDO2, fileConfigService.getFileConfigCache().get(1)); } @Test @@ -101,8 +102,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { assertFalse(fileConfig.getMaster()); assertEquals("/yunai", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath()); assertEquals("https://www.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain()); - // verify 调用 - verify(fileConfigProducer).sendFileConfigRefreshMessage(); } @Test @@ -126,8 +125,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { assertPojoEquals(reqVO, fileConfig, "config"); assertEquals("/yunai2", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath()); assertEquals("https://doc.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain()); - // verify 调用 - verify(fileConfigProducer).sendFileConfigRefreshMessage(); } @Test @@ -152,8 +149,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { // 断言数据 assertTrue(fileConfigMapper.selectById(dbFileConfig.getId()).getMaster()); assertFalse(fileConfigMapper.selectById(masterFileConfig.getId()).getMaster()); - // verify 调用 - verify(fileConfigProducer).sendFileConfigRefreshMessage(); } @Test @@ -174,8 +169,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { fileConfigService.deleteFileConfig(id); // 校验数据不存在了 assertNull(fileConfigMapper.selectById(id)); - // verify 调用 - verify(fileConfigProducer).sendFileConfigRefreshMessage(); } @Test diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartMapper.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartMapper.java index b4727117e..64e2f99ea 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartMapper.java +++ b/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 { default TradeCartDO selectByUserIdAndSkuId(Long userId, Long skuId, Boolean addStatus, Boolean orderStatus) { - return selectOne(TradeCartDO::getUserId, userId, - TradeCartDO::getSkuId, skuId, - TradeCartDO::getAddStatus, addStatus, - TradeCartDO::getOrderStatus, orderStatus); + return selectOne(new LambdaQueryWrapper().eq(TradeCartDO::getUserId, userId) + .eq(TradeCartDO::getSkuId, skuId) + .eq(TradeCartDO::getAddStatus, addStatus) + .eq(TradeCartDO::getOrderStatus, orderStatus)); } default Integer selectSumByUserId(Long userId) { diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/account/MpAccountMapper.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/account/MpAccountMapper.java index 8050aea34..30c54fd51 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/account/MpAccountMapper.java +++ b/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; import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountPageReqVO; import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.time.LocalDateTime; @Mapper public interface MpAccountMapper extends BaseMapperX { @@ -22,4 +25,7 @@ public interface MpAccountMapper extends BaseMapperX { return selectOne(MpAccountDO::getAppId, appId); } + @Select("SELECT COUNT(*) FROM mp_account WHERE update_time > #{maxUpdateTime}") + Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime); + } diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/consumer/MpAccountRefreshConsumer.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/consumer/MpAccountRefreshConsumer.java deleted file mode 100644 index 89b2b8f6d..000000000 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/consumer/MpAccountRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.mp.mq.consumer; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; -import cn.iocoder.yudao.module.mp.mq.message.MpAccountRefreshMessage; -import cn.iocoder.yudao.module.mp.service.account.MpAccountService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link MpAccountRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class MpAccountRefreshConsumer extends AbstractChannelMessageListener { - - @Resource - private MpAccountService mpAccountService; - - @Override - public void onMessage(MpAccountRefreshMessage message) { - log.info("[onMessage][收到 Account 刷新消息]"); - mpAccountService.initLocalCache(); - } - -} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/message/MpAccountRefreshMessage.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/message/MpAccountRefreshMessage.java deleted file mode 100644 index 3088f6bbc..000000000 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/message/MpAccountRefreshMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.mp.mq.message; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 公众号账号刷新 Message - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class MpAccountRefreshMessage extends AbstractChannelMessage { - - @Override - public String getChannel() { - return "mp.account.refresh"; - } - -} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/producer/MpAccountProducer.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/producer/MpAccountProducer.java deleted file mode 100644 index 47d8a1b52..000000000 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/producer/MpAccountProducer.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.mp.mq.producer; - -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; -import cn.iocoder.yudao.module.mp.mq.message.MpAccountRefreshMessage; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 公众号账号 Producer - * - * @author 芋道源码 - */ -@Component -public class MpAccountProducer { - - @Resource - private RedisMQTemplate redisMQTemplate; - - /** - * 发送 {@link MpAccountRefreshMessage} 消息 - */ - public void sendAccountRefreshMessage() { - MpAccountRefreshMessage message = new MpAccountRefreshMessage(); - redisMQTemplate.send(message); - } - -} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/account/MpAccountServiceImpl.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/account/MpAccountServiceImpl.java index 319d345ed..af359b02a 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/account/MpAccountServiceImpl.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/account/MpAccountServiceImpl.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.mp.service.account; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountCreateReqVO; 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; import cn.iocoder.yudao.module.mp.dal.mysql.account.MpAccountMapper; import cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; -import cn.iocoder.yudao.module.mp.mq.producer.MpAccountProducer; import com.google.common.annotations.VisibleForTesting; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -21,15 +20,20 @@ import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; import org.springframework.context.annotation.Lazy; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import javax.annotation.PostConstruct; import javax.annotation.Resource; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_USERNAME_EXISTS; /** @@ -58,9 +62,6 @@ public class MpAccountServiceImpl implements MpAccountService { @Lazy // 延迟加载,解决循环依赖的问题 private MpServiceFactory mpServiceFactory; - @Resource - private MpAccountProducer mpAccountProducer; - @Override @PostConstruct public void initLocalCache() { @@ -72,7 +73,30 @@ public class MpAccountServiceImpl implements MpAccountService { // 第二步:构建缓存。创建或更新支付 Client mpServiceFactory.init(accounts); - accountCache = CollectionUtils.convertMap(accounts, MpAccountDO::getAppId); + accountCache = convertMap(accounts, MpAccountDO::getAppId); + }); + } + + /** + * 通过定时任务轮询,刷新缓存 + * + * 目的:多节点部署时,通过轮询”通知“所有节点,进行刷新 + */ + @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS) + public void refreshLocalCache() { + // 注意:忽略自动多租户,因为要全局初始化缓存 + TenantUtils.executeIgnore(() -> { + // 情况一:如果缓存里没有数据,则直接刷新缓存 + if (CollUtil.isEmpty(accountCache)) { + initLocalCache(); + return; + } + + // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存 + LocalDateTime maxTime = getMaxValue(accountCache.values(), MpAccountDO::getUpdateTime); + if (mpAccountMapper.selectCountByUpdateTimeGt(maxTime) > 0) { + initLocalCache(); + } }); } @@ -85,8 +109,8 @@ public class MpAccountServiceImpl implements MpAccountService { MpAccountDO account = MpAccountConvert.INSTANCE.convert(createReqVO); mpAccountMapper.insert(account); - // 发送刷新消息 - mpAccountProducer.sendAccountRefreshMessage(); + // 刷新缓存 + initLocalCache(); return account.getId(); } @@ -101,8 +125,8 @@ public class MpAccountServiceImpl implements MpAccountService { MpAccountDO updateObj = MpAccountConvert.INSTANCE.convert(updateReqVO); mpAccountMapper.updateById(updateObj); - // 发送刷新消息 - mpAccountProducer.sendAccountRefreshMessage(); + // 刷新缓存 + initLocalCache(); } @Override @@ -112,8 +136,8 @@ public class MpAccountServiceImpl implements MpAccountService { // 删除 mpAccountMapper.deleteById(id); - // 发送刷新消息 - mpAccountProducer.sendAccountRefreshMessage(); + // 刷新缓存 + initLocalCache(); } private MpAccountDO validateAccountExists(Long id) { diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/dto/PayOrderRespDTO.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/dto/PayOrderRespDTO.java index dbc62a643..583d6d54d 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/dto/PayOrderRespDTO.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/dto/PayOrderRespDTO.java @@ -6,8 +6,6 @@ import lombok.Data; /** * 支付单信息 Response DTO * - * TODO 芋艿:还没定好字段 - * * @author 芋道源码 */ @Data diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/refund/dto/PayRefundRespDTO.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/refund/dto/PayRefundRespDTO.java index c98eaa4f6..bcc2b7ffa 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/refund/dto/PayRefundRespDTO.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/refund/dto/PayRefundRespDTO.java @@ -8,8 +8,6 @@ import java.time.LocalDateTime; /** * 退款单信息 Response DTO * - * TODO 芋艿:还没定好字段 - * * @author 芋道源码 */ @Data diff --git a/yudao-module-pay/yudao-module-pay-biz/pom.xml b/yudao-module-pay/yudao-module-pay-biz/pom.xml index 191b6fe09..35948df65 100644 --- a/yudao-module-pay/yudao-module-pay-biz/pom.xml +++ b/yudao-module-pay/yudao-module-pay-biz/pom.xml @@ -61,12 +61,6 @@ yudao-spring-boot-starter-job - - - cn.iocoder.boot - yudao-spring-boot-starter-mq - - cn.iocoder.boot diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/RedisKeyConstants.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/RedisKeyConstants.java index f4abe7032..fc4c94933 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/RedisKeyConstants.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/RedisKeyConstants.java @@ -1,8 +1,5 @@ package cn.iocoder.yudao.module.pay.dal.redis; -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; -import org.redisson.api.RLock; - /** * 支付 Redis Key 枚举类 * @@ -10,9 +7,14 @@ import org.redisson.api.RLock; */ public interface RedisKeyConstants { - RedisKeyDefine PAY_NOTIFY_LOCK = new RedisKeyDefine("通知任务的分布式锁", - "pay_notify:lock:%d", // 参数来自 DefaultLockKeyBuilder 类 - RedisKeyDefine.KeyTypeEnum.HASH, RLock.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); // Redisson 的 Lock 锁,使用 Hash 数据结构 + /** + * 通知任务的分布式锁 + * + * KEY 格式:pay_notify:lock:%d // 参数来自 DefaultLockKeyBuilder 类 + * VALUE 数据格式:HASH // RLock.class:Redisson 的 Lock 锁,使用 Hash 数据结构 + * 过期时间:不固定 + */ + String PAY_NOTIFY_LOCK = "pay_notify:lock:%d"; /** * 支付序号的缓存 diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/notify/PayNotifyLockRedisDAO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/notify/PayNotifyLockRedisDAO.java index 2d1f13f16..69fab20dc 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/notify/PayNotifyLockRedisDAO.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/notify/PayNotifyLockRedisDAO.java @@ -33,7 +33,7 @@ public class PayNotifyLockRedisDAO { } private static String formatKey(Long id) { - return String.format(PAY_NOTIFY_LOCK.getKeyTemplate(), id); + return String.format(PAY_NOTIFY_LOCK, id); } } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java index e76e4b35d..3eb996c4a 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java +++ b/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 import cn.iocoder.yudao.module.pay.convert.channel.PayChannelConvert; import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper; -import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -112,7 +111,7 @@ public class PayChannelServiceImpl implements PayChannelService { channelMapper.insert(channel); // 刷新缓存 - refreshLocalCache(); + initLocalCache(); return channel.getId(); } @@ -127,7 +126,7 @@ public class PayChannelServiceImpl implements PayChannelService { channelMapper.updateById(channel); // 刷新缓存 - refreshLocalCache(); + initLocalCache(); } /** @@ -160,7 +159,7 @@ public class PayChannelServiceImpl implements PayChannelService { channelMapper.deleteById(id); // 刷新缓存 - refreshLocalCache(); + initLocalCache(); } private PayChannelDO validateChannelExists(Long id) { diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java index 35e11f02d..21bd5e895 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java +++ b/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 { * @param postIds 岗位数组 * @return 用户数组 */ - List getUsersByPostIds(Collection postIds); + List getUserListByPostIds(Collection postIds); /** * 获得用户 Map diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApiImpl.java index 52e30ea04..253934301 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApiImpl.java @@ -21,7 +21,7 @@ public class PermissionApiImpl implements PermissionApi { @Override public Set getUserRoleIdListByRoleIds(Collection roleIds) { - return permissionService.getUserRoleIdListByRoleIds(roleIds); + return permissionService.getUserRoleIdListByRoleId(roleIds); } @Override diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java index 2271420c4..adbc694da 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java +++ b/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 { } @Override - public List getUsersByPostIds(Collection postIds) { + public List getUserListByPostIds(Collection postIds) { List users = userService.getUserListByPostIds(postIds); return UserConvert.INSTANCE.convertList4(users); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http index c2715634e..00ae2ba2b 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http @@ -2,6 +2,7 @@ POST {{baseUrl}}/system/auth/login Content-Type: application/json tenant-id: {{adminTenentId}} +tag: Yunai.local { "username": "admin", diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java index 59aab4f04..57a70b80f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.system.controller.admin.auth; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.collection.SetUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.security.config.SecurityProperties; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; @@ -12,8 +11,8 @@ import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; -import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; +import cn.iocoder.yudao.module.system.service.permission.MenuService; import cn.iocoder.yudao.module.system.service.permission.PermissionService; import cn.iocoder.yudao.module.system.service.permission.RoleService; import cn.iocoder.yudao.module.system.service.social.SocialUserService; @@ -34,9 +33,9 @@ import java.util.List; import java.util.Set; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.obtainAuthorization; -import static java.util.Collections.singleton; @Tag(name = "管理后台 - 认证") @RestController @@ -52,6 +51,8 @@ public class AuthController { @Resource private RoleService roleService; @Resource + private MenuService menuService; + @Resource private PermissionService permissionService; @Resource private SocialUserService socialUserService; @@ -91,33 +92,24 @@ public class AuthController { @GetMapping("/get-permission-info") @Operation(summary = "获取登录用户的权限信息") public CommonResult getPermissionInfo() { - // 获得用户信息 + // 1.1 获得用户信息 AdminUserDO user = userService.getUser(getLoginUserId()); if (user == null) { return null; } - // 获得角色列表 - Set roleIds = permissionService.getUserRoleIdsFromCache(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus())); - List roleList = roleService.getRoleListFromCache(roleIds); - // 获得菜单列表 - List menuList = permissionService.getRoleMenuListFromCache(roleIds, - SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType(), MenuTypeEnum.BUTTON.getType()), - singleton(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的 - // 拼接结果返回 - return success(AuthConvert.INSTANCE.convert(user, roleList, menuList)); - } - @GetMapping("/list-menus") - @Operation(summary = "获得登录用户的菜单列表") - public CommonResult> getMenuList() { - // 获得角色列表 - Set roleIds = permissionService.getUserRoleIdsFromCache(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus())); - // 获得用户拥有的菜单列表 - List menuList = permissionService.getRoleMenuListFromCache(roleIds, - SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型 - singleton(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的 - // 转换成 Tree 结构返回 - return success(AuthConvert.INSTANCE.buildMenuTree(menuList)); + // 1.2 获得角色列表 + Set roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId()); + List roles = roleService.getRoleList(roleIds); + roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色 + + // 1.3 获得菜单列表 + Set menuIds = permissionService.getRoleMenuListByRoleId(convertSet(roles, RoleDO::getId)); + List menuList = menuService.getMenuList(menuIds); + menuList.removeIf(menu -> !CommonStatusEnum.ENABLE.getStatus().equals(menu.getStatus())); // 移除禁用的菜单 + + // 2. 拼接结果返回 + return success(AuthConvert.INSTANCE.convert(user, roles, menuList)); } // ========== 短信登录相关 ========== @@ -149,7 +141,7 @@ public class AuthController { @Parameter(name = "redirectUri", description = "回调路径") }) public CommonResult socialLogin(@RequestParam("type") Integer type, - @RequestParam("redirectUri") String redirectUri) { + @RequestParam("redirectUri") String redirectUri) { return CommonResult.success(socialUserService.getAuthorizeUrl(type, redirectUri)); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java index f5fd6da69..7b3a4938d 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java @@ -6,9 +6,10 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.util.List; import java.util.Set; -@Schema(description = "管理后台 - 登录用户的权限信息 Response VO,额外包括用户信息和角色列表") +@Schema(description = "管理后台 - 登录用户的权限信息 Response VO,额外包括用户信息和角色列表") @Data @NoArgsConstructor @AllArgsConstructor @@ -24,6 +25,9 @@ public class AuthPermissionInfoRespVO { @Schema(description = "操作权限数组", requiredMode = Schema.RequiredMode.REQUIRED) private Set permissions; + @Schema(description = "菜单树", required = true) + private List menus; + @Schema(description = "用户信息 VO") @Data @NoArgsConstructor @@ -37,9 +41,53 @@ public class AuthPermissionInfoRespVO { @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") private String nickname; - @Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://www.iocoder.cn/xx.jpg") + @Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.jpg") private String avatar; } + @Schema(description = "管理后台 - 登录用户的菜单信息 Response VO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class MenuVO { + + @Schema(description = "菜单名称", required = true, example = "芋道") + private Long id; + + @Schema(description = "父菜单 ID", required = true, example = "1024") + private Long parentId; + + @Schema(description = "菜单名称", required = true, example = "芋道") + private String name; + + @Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post") + private String path; + + @Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index") + private String component; + + @Schema(description = "组件名", example = "SystemUser") + private String componentName; + + @Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list") + private String icon; + + @Schema(description = "是否可见", required = true, example = "false") + private Boolean visible; + + @Schema(description = "是否缓存", required = true, example = "false") + private Boolean keepAlive; + + @Schema(description = "是否总是显示", example = "false") + private Boolean alwaysShow; + + /** + * 子路由 + */ + private List children; + + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogBaseVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogBaseVO.java index 63d4224e6..7ba58b70f 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogBaseVO.java +++ b/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; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; /** -* 邮件日志 Base VO,提供给添加、修改、详细的子 VO 使用 -* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 -*/ + * 邮件日志 Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + */ @Data public class MailLogBaseVO { diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/PermissionController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/PermissionController.java index ca768d14f..91f5d4266 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/PermissionController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/PermissionController.java @@ -37,10 +37,10 @@ public class PermissionController { @Operation(summary = "获得角色拥有的菜单编号") @Parameter(name = "roleId", description = "角色编号", required = true) - @GetMapping("/list-role-resources") + @GetMapping("/list-role-menus") @PreAuthorize("@ss.hasPermission('system:permission:assign-role-menu')") - public CommonResult> listRoleMenus(Long roleId) { - return success(permissionService.getRoleMenuIds(roleId)); + public CommonResult> getRoleMenuList(Long roleId) { + return success(permissionService.getRoleMenuListByRoleId(roleId)); } @PostMapping("/assign-role-menu") diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.http index c28725d86..c68b86b75 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.http @@ -40,6 +40,3 @@ tenant-id: {{adminTenentId}} GET {{baseUrl}}/system/role/page?pageNo=1&pageSize=10 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} - -### - diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignRoleDataScopeReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignRoleDataScopeReqVO.java index 8e457c32e..2957898e8 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignRoleDataScopeReqVO.java +++ b/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 { // TODO 这里要多一个枚举校验 private Integer dataScope; - @Schema(description = "部门编号列表,只有范围类型为 DEPT_CUSTOM 时,该字段才需要", example = "1,3,5") + @Schema(description = "部门编号列表,只有范围类型为 DEPT_CUSTOM 时,该字段才需要", example = "1,3,5") private Set dataScopeDeptIds = Collections.emptySet(); // 兜底 } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/vo/SensitiveWordBaseVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/vo/SensitiveWordBaseVO.java index e940bf55b..b2ae46d17 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/vo/SensitiveWordBaseVO.java +++ b/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; import java.util.List; /** -* 敏感词 Base VO,提供给添加、修改、详细的子 VO 使用 -* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 -*/ + * 敏感词 Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + */ @Data public class SensitiveWordBaseVO { diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantBaseVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantBaseVO.java index 76899be85..a54784c68 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantBaseVO.java +++ b/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.*; import java.time.LocalDateTime; /** -* 租户 Base VO,提供给添加、修改、详细的子 VO 使用 -* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 -*/ + * 租户 Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + */ @Data public class TenantBaseVO { diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdateReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdateReqVO.java index 562c9ae34..f1e54ac90 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdateReqVO.java +++ b/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 { @Length(min = 11, max = 11, message = "手机号长度必须 11 位") private String mobile; - @Schema(description = "用户性别-参见 SexEnum 枚举类", example = "1") + @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1") private Integer sex; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserExportReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserExportReqVO.java index a3203ed3d..f74771121 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserExportReqVO.java +++ b/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 { @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] createTime; - @Schema(description = "部门编号,同时筛选子部门", example = "1024") + @Schema(description = "部门编号,同时筛选子部门", example = "1024") private Long deptId; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserPageItemRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserPageItemRespVO.java index 4f609de46..3c01ca9bb 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserPageItemRespVO.java +++ b/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; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -@Schema(description = "管理后台 - 用户分页时的信息 Response VO,相比用户基本信息来说,会多部门信息") +@Schema(description = "管理后台 - 用户分页时的信息 Response VO,相比用户基本信息来说,会多部门信息") @Data @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdateStatusReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdateStatusReqVO.java index af1b73cdb..e5a113e18 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdateStatusReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdateStatusReqVO.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -13,9 +15,9 @@ public class UserUpdateStatusReqVO { @NotNull(message = "角色编号不能为空") private Long id; - @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "状态不能为空") -// @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") + @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") private Integer status; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java index a35fa8231..1b79036fa 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; import org.slf4j.LoggerFactory; @@ -27,13 +28,16 @@ public interface AuthConvert { default AuthPermissionInfoRespVO convert(AdminUserDO user, List roleList, List menuList) { return AuthPermissionInfoRespVO.builder() - .user(AuthPermissionInfoRespVO.UserVO.builder().id(user.getId()).nickname(user.getNickname()).avatar(user.getAvatar()).build()) - .roles(convertSet(roleList, RoleDO::getCode)) - .permissions(convertSet(menuList, MenuDO::getPermission)) - .build(); + .user(AuthPermissionInfoRespVO.UserVO.builder().id(user.getId()).nickname(user.getNickname()).avatar(user.getAvatar()).build()) + .roles(convertSet(roleList, RoleDO::getCode)) + // 权限标识信息 + .permissions(convertSet(menuList, MenuDO::getPermission)) + // 菜单树 + .menus(buildMenuTree(menuList)) + .build(); } - AuthMenuRespVO convertTreeNode(MenuDO menu); + AuthPermissionInfoRespVO.MenuVO convertTreeNode(MenuDO menu); /** * 将菜单列表,构建成菜单树 @@ -41,20 +45,23 @@ public interface AuthConvert { * @param menuList 菜单列表 * @return 菜单树 */ - default List buildMenuTree(List menuList) { + default List buildMenuTree(List menuList) { + // 移除按钮 + menuList.removeIf(menu -> menu.getType().equals(MenuTypeEnum.BUTTON.getType())); // 排序,保证菜单的有序性 menuList.sort(Comparator.comparing(MenuDO::getSort)); + // 构建菜单树 // 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。 - Map treeNodeMap = new LinkedHashMap<>(); + Map treeNodeMap = new LinkedHashMap<>(); menuList.forEach(menu -> treeNodeMap.put(menu.getId(), AuthConvert.INSTANCE.convertTreeNode(menu))); // 处理父子关系 treeNodeMap.values().stream().filter(node -> !node.getParentId().equals(ID_ROOT)).forEach(childNode -> { // 获得父节点 - AuthMenuRespVO parentNode = treeNodeMap.get(childNode.getParentId()); + AuthPermissionInfoRespVO.MenuVO parentNode = treeNodeMap.get(childNode.getParentId()); if (parentNode == null) { LoggerFactory.getLogger(getClass()).error("[buildRouterTree][resource({}) 找不到父资源({})]", - childNode.getId(), childNode.getParentId()); + childNode.getId(), childNode.getParentId()); return; } // 将自己添加到父节点中 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java index def79df69..cc4f334e6 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqV import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import org.apache.ibatis.annotations.Mapper; +import java.util.Collection; import java.util.List; @Mapper @@ -25,4 +26,8 @@ public interface DeptMapper extends BaseMapperX { return selectCount(DeptDO::getParentId, parentId); } + default List selectListByParentId(Collection parentIds) { + return selectList(DeptDO::getParentId, parentIds); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java index 3d645bdf2..8458faa67 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java @@ -25,4 +25,7 @@ public interface MenuMapper extends BaseMapperX { .eqIfPresent(MenuDO::getStatus, reqVO.getStatus())); } + default List selectListByPermission(String permission) { + return selectList(MenuDO::getPermission, permission); + } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMenuMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMenuMapper.java index 20f418676..02cb6b8a4 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMenuMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMenuMapper.java @@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.system.dal.mysql.permission; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.apache.ibatis.annotations.Mapper; -import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.List; @@ -13,14 +11,18 @@ import java.util.List; @Mapper public interface RoleMenuMapper extends BaseMapperX { - @Repository - class BatchInsertMapper extends ServiceImpl { - } - default List selectListByRoleId(Long roleId) { return selectList(RoleMenuDO::getRoleId, roleId); } + default List selectListByRoleId(Collection roleIds) { + return selectList(RoleMenuDO::getRoleId, roleIds); + } + + default List selectListByMenuId(Long menuId) { + return selectList(RoleMenuDO::getMenuId, menuId); + } + default void deleteListByRoleIdAndMenuIds(Long roleId, Collection menuIds) { delete(new LambdaQueryWrapper() .eq(RoleMenuDO::getRoleId, roleId) diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sensitiveword/SensitiveWordMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sensitiveword/SensitiveWordMapper.java index 831188761..646e8f2b3 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sensitiveword/SensitiveWordMapper.java +++ b/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 import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +import java.time.LocalDateTime; import java.util.List; /** @@ -40,4 +42,7 @@ public interface SensitiveWordMapper extends BaseMapperX { return selectOne(SensitiveWordDO::getName, name); } + @Select("SELECT COUNT(*) FROM system_sensitive_word WHERE update_time > #{maxUpdateTime}") + Long selectCountByUpdateTimeGt(LocalDateTime maxTime); + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsChannelMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsChannelMapper.java index 4af731fc6..04fbd3f76 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsChannelMapper.java +++ b/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; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.time.LocalDateTime; @Mapper public interface SmsChannelMapper extends BaseMapperX { @@ -18,4 +21,7 @@ public interface SmsChannelMapper extends BaseMapperX { .orderByDesc(SmsChannelDO::getId)); } + @Select("SELECT COUNT(*) FROM system_sms_channel WHERE update_time > #{maxUpdateTime}") + Long selectCountByUpdateTimeGt(LocalDateTime maxTime); + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java index 5760e3c0f..cda6dab4b 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java @@ -1,12 +1,7 @@ package cn.iocoder.yudao.module.system.dal.redis; -import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import java.time.Duration; - -import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING; - /** * System Redis Key 枚举类 * @@ -14,16 +9,93 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.S */ public interface RedisKeyConstants { - RedisKeyDefine CAPTCHA_CODE = new RedisKeyDefine("验证码的缓存", - "captcha_code:%s", // 参数为 uuid - STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); + /** + * 指定部门的所有子部门编号数组的缓存 + *

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

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

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

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

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

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

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

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

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

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

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

+ * KEY 格式:sms_template:{id} + * VALUE 数据格式:String 模版信息 + */ + String SMS_TEMPLATE = "sms_template"; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/common/CaptchaRedisDAO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/common/CaptchaRedisDAO.java deleted file mode 100644 index bfcb7878b..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/common/CaptchaRedisDAO.java +++ /dev/null @@ -1,41 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.redis.common; - -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Repository; - -import javax.annotation.Resource; -import java.time.Duration; - -import static cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants.CAPTCHA_CODE; - -/** - * 验证码的 Redis DAO - * - * @author 芋道源码 - */ -@Repository -public class CaptchaRedisDAO { - - @Resource - private StringRedisTemplate stringRedisTemplate; - - public String get(String uuid) { - String redisKey = formatKey(uuid); - return stringRedisTemplate.opsForValue().get(redisKey); - } - - public void set(String uuid, String code, Duration timeout) { - String redisKey = formatKey(uuid); - stringRedisTemplate.opsForValue().set(redisKey, code, timeout); - } - - public void delete(String uuid) { - String redisKey = formatKey(uuid); - stringRedisTemplate.delete(redisKey); - } - - private static String formatKey(String uuid) { - return String.format(CAPTCHA_CODE.getKeyTemplate(), uuid); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java index d57beb881..7827dfa10 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java @@ -53,7 +53,7 @@ public class OAuth2AccessTokenRedisDAO { } private static String formatKey(String accessToken) { - return String.format(OAUTH2_ACCESS_TOKEN.getKeyTemplate(), accessToken); + return String.format(OAUTH2_ACCESS_TOKEN, accessToken); } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/auth/OAuth2ClientRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/auth/OAuth2ClientRefreshConsumer.java deleted file mode 100644 index fc765c425..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/auth/OAuth2ClientRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.auth; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; -import cn.iocoder.yudao.module.system.mq.message.auth.OAuth2ClientRefreshMessage; -import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link OAuth2ClientRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class OAuth2ClientRefreshConsumer extends AbstractChannelMessageListener { - - @Resource - private OAuth2ClientService oauth2ClientService; - - @Override - public void onMessage(OAuth2ClientRefreshMessage message) { - log.info("[onMessage][收到 OAuth2Client 刷新消息]"); - oauth2ClientService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/dept/DeptRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/dept/DeptRefreshConsumer.java deleted file mode 100644 index 981244d90..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/dept/DeptRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.dept; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; -import cn.iocoder.yudao.module.system.mq.message.dept.DeptRefreshMessage; -import cn.iocoder.yudao.module.system.service.dept.DeptService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link DeptRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class DeptRefreshConsumer extends AbstractChannelMessageListener { - - @Resource - private DeptService deptService; - - @Override - public void onMessage(DeptRefreshMessage message) { - log.info("[onMessage][收到 Dept 刷新消息]"); - deptService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailAccountRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailAccountRefreshConsumer.java deleted file mode 100644 index 69ddfce33..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailAccountRefreshConsumer.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.mail; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; -import cn.iocoder.yudao.module.system.mq.message.mail.MailAccountRefreshMessage; -import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage; -import cn.iocoder.yudao.module.system.service.mail.MailAccountService; -import cn.iocoder.yudao.module.system.service.mail.MailTemplateService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link MailAccountRefreshMessage} 的消费者 - * - * @author wangjingyi - */ -@Component -@Slf4j -public class MailAccountRefreshConsumer extends AbstractChannelMessageListener { - - @Resource - private MailAccountService mailAccountService; - - @Override - public void onMessage(MailAccountRefreshMessage message) { - log.info("[onMessage][收到 Mail Account 刷新信息]"); - mailAccountService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailTemplateRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailTemplateRefreshConsumer.java deleted file mode 100644 index 35da1edec..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailTemplateRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.mail; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; -import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage; -import cn.iocoder.yudao.module.system.service.mail.MailTemplateService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link MailTemplateRefreshMessage} 的消费者 - * - * @author wangjingyi - */ -@Component -@Slf4j -public class MailTemplateRefreshConsumer extends AbstractChannelMessageListener { - - @Resource - private MailTemplateService mailTemplateService; - - @Override - public void onMessage(MailTemplateRefreshMessage message) { - log.info("[onMessage][收到 Mail Template 刷新信息]"); - mailTemplateService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/notify/NotifyTemplateRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/notify/NotifyTemplateRefreshConsumer.java deleted file mode 100644 index c2d8133b3..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/notify/NotifyTemplateRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.notify; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; -import cn.iocoder.yudao.module.system.mq.message.notify.NotifyTemplateRefreshMessage; -import cn.iocoder.yudao.module.system.service.notify.NotifyTemplateService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link NotifyTemplateRefreshMessage} 的消费者 - * - * @author xrcoder - */ -@Component -@Slf4j -public class NotifyTemplateRefreshConsumer extends AbstractChannelMessageListener { - - @Resource - private NotifyTemplateService notifyTemplateService; - - @Override - public void onMessage(NotifyTemplateRefreshMessage message) { - log.info("[onMessage][收到 NotifyTemplate 刷新消息]"); - notifyTemplateService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/MenuRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/MenuRefreshConsumer.java deleted file mode 100644 index a4b633512..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/MenuRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.permission; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; -import cn.iocoder.yudao.module.system.mq.message.permission.MenuRefreshMessage; -import cn.iocoder.yudao.module.system.service.permission.MenuService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link MenuRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class MenuRefreshConsumer extends AbstractChannelMessageListener { - - @Resource - private MenuService menuService; - - @Override - public void onMessage(MenuRefreshMessage message) { - log.info("[onMessage][收到 Menu 刷新消息]"); - menuService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/RoleMenuRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/RoleMenuRefreshConsumer.java deleted file mode 100644 index d9f0e9220..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/RoleMenuRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.permission; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; -import cn.iocoder.yudao.module.system.mq.message.permission.RoleMenuRefreshMessage; -import cn.iocoder.yudao.module.system.service.permission.PermissionService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link RoleMenuRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class RoleMenuRefreshConsumer extends AbstractChannelMessageListener { - - @Resource - private PermissionService permissionService; - - @Override - public void onMessage(RoleMenuRefreshMessage message) { - log.info("[onMessage][收到 Role 与 Menu 的关联刷新消息]"); - permissionService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/RoleRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/RoleRefreshConsumer.java deleted file mode 100644 index bb53b7499..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/RoleRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.permission; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; -import cn.iocoder.yudao.module.system.mq.message.permission.RoleRefreshMessage; -import cn.iocoder.yudao.module.system.service.permission.RoleService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link RoleRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class RoleRefreshConsumer extends AbstractChannelMessageListener { - - @Resource - private RoleService roleService; - - @Override - public void onMessage(RoleRefreshMessage message) { - log.info("[onMessage][收到 Role 刷新消息]"); - roleService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/UserRoleRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/UserRoleRefreshConsumer.java deleted file mode 100644 index d580f58e2..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/permission/UserRoleRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.permission; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; -import cn.iocoder.yudao.module.system.mq.message.permission.UserRoleRefreshMessage; -import cn.iocoder.yudao.module.system.service.permission.PermissionService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link UserRoleRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class UserRoleRefreshConsumer extends AbstractChannelMessageListener { - - @Resource - private PermissionService permissionService; - - @Override - public void onMessage(UserRoleRefreshMessage message) { - log.info("[onMessage][收到 User 与 Role 的关联刷新消息]"); - permissionService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sensitiveword/SensitiveWordRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sensitiveword/SensitiveWordRefreshConsumer.java deleted file mode 100644 index dc3a06236..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sensitiveword/SensitiveWordRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.sensitiveword; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; -import cn.iocoder.yudao.module.system.mq.message.sensitiveword.SensitiveWordRefreshMessage; -import cn.iocoder.yudao.module.system.service.sensitiveword.SensitiveWordService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link SensitiveWordRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class SensitiveWordRefreshConsumer extends AbstractChannelMessageListener { - - @Resource - private SensitiveWordService sensitiveWordService; - - @Override - public void onMessage(SensitiveWordRefreshMessage message) { - log.info("[onMessage][收到 SensitiveWord 刷新消息]"); - sensitiveWordService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsChannelRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsChannelRefreshConsumer.java deleted file mode 100644 index 585eb57d7..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsChannelRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.sms; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; -import cn.iocoder.yudao.module.system.mq.message.sms.SmsChannelRefreshMessage; -import cn.iocoder.yudao.module.system.service.sms.SmsChannelService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link SmsChannelRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class SmsChannelRefreshConsumer extends AbstractChannelMessageListener { - - @Resource - private SmsChannelService smsChannelService; - - @Override - public void onMessage(SmsChannelRefreshMessage message) { - log.info("[onMessage][收到 SmsChannel 刷新消息]"); - smsChannelService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsTemplateRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsTemplateRefreshConsumer.java deleted file mode 100644 index 02bc59984..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsTemplateRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.sms; - -import cn.iocoder.yudao.module.system.mq.message.sms.SmsTemplateRefreshMessage; -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; -import cn.iocoder.yudao.module.system.service.sms.SmsTemplateService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link SmsTemplateRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class SmsTemplateRefreshConsumer extends AbstractChannelMessageListener { - - @Resource - private SmsTemplateService smsTemplateService; - - @Override - public void onMessage(SmsTemplateRefreshMessage message) { - log.info("[onMessage][收到 SmsTemplate 刷新消息]"); - smsTemplateService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/auth/OAuth2ClientRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/auth/OAuth2ClientRefreshMessage.java deleted file mode 100644 index 3d18df150..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/auth/OAuth2ClientRefreshMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.auth; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * OAuth 2.0 客户端的数据刷新 Message - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class OAuth2ClientRefreshMessage extends AbstractChannelMessage { - - @Override - public String getChannel() { - return "system.oauth2-client.refresh"; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/dept/DeptRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/dept/DeptRefreshMessage.java deleted file mode 100644 index 80d3c8c39..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/dept/DeptRefreshMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.dept; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 部门数据刷新 Message - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class DeptRefreshMessage extends AbstractChannelMessage { - - @Override - public String getChannel() { - return "system.dept.refresh"; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailAccountRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailAccountRefreshMessage.java deleted file mode 100644 index d6e9d08a9..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailAccountRefreshMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.mail; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 邮箱账号的数据刷新 Message - * - * @author wangjingyi - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class MailAccountRefreshMessage extends AbstractChannelMessage { - - @Override - public String getChannel() { - return "system.mail-account.refresh"; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailTemplateRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailTemplateRefreshMessage.java deleted file mode 100644 index f6ff0925a..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailTemplateRefreshMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.mail; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 邮箱模板的数据刷新 Message - * - * @author wangjingyi - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class MailTemplateRefreshMessage extends AbstractChannelMessage { - - @Override - public String getChannel() { - return "system.mail-template.refresh"; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/notify/NotifyTemplateRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/notify/NotifyTemplateRefreshMessage.java deleted file mode 100644 index 3eddf0bc6..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/notify/NotifyTemplateRefreshMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.notify; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 站内信模板的数据刷新 Message - * - * @author xrcoder - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class NotifyTemplateRefreshMessage extends AbstractChannelMessage { - - @Override - public String getChannel() { - return "system.notify-template.refresh"; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/MenuRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/MenuRefreshMessage.java deleted file mode 100644 index abd75dbaa..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/MenuRefreshMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.permission; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 菜单数据刷新 Message - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class MenuRefreshMessage extends AbstractChannelMessage { - - @Override - public String getChannel() { - return "system.menu.refresh"; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/RoleMenuRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/RoleMenuRefreshMessage.java deleted file mode 100644 index 0982775bc..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/RoleMenuRefreshMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.permission; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 角色与菜单数据刷新 Message - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class RoleMenuRefreshMessage extends AbstractChannelMessage { - - @Override - public String getChannel() { - return "system.role-menu.refresh"; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/RoleRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/RoleRefreshMessage.java deleted file mode 100644 index e80d8f30c..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/RoleRefreshMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.permission; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 角色数据刷新 Message - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class RoleRefreshMessage extends AbstractChannelMessage { - - @Override - public String getChannel() { - return "system.role.refresh"; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/UserRoleRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/UserRoleRefreshMessage.java deleted file mode 100644 index 1644f5c00..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/permission/UserRoleRefreshMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.permission; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 用户与角色的数据刷新 Message - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class UserRoleRefreshMessage extends AbstractChannelMessage { - - @Override - public String getChannel() { - return "system.user-role.refresh"; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/sensitiveword/SensitiveWordRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/sensitiveword/SensitiveWordRefreshMessage.java deleted file mode 100644 index 13ebf425f..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/sensitiveword/SensitiveWordRefreshMessage.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.sensitiveword; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 敏感词的刷新 Message - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class SensitiveWordRefreshMessage extends AbstractChannelMessage { - - @Override - public String getChannel() { - return "system.sensitive-word.refresh"; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/sms/SmsChannelRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/sms/SmsChannelRefreshMessage.java deleted file mode 100644 index 0841cdee0..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/sms/SmsChannelRefreshMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.sms; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 短信渠道的数据刷新 Message - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class SmsChannelRefreshMessage extends AbstractChannelMessage { - - @Override - public String getChannel() { - return "system.sms-channel.refresh"; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/sms/SmsTemplateRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/sms/SmsTemplateRefreshMessage.java deleted file mode 100644 index 4873c06c4..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/sms/SmsTemplateRefreshMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.sms; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 短信模板的数据刷新 Message - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class SmsTemplateRefreshMessage extends AbstractChannelMessage { - - @Override - public String getChannel() { - return "system.sms-template.refresh"; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/auth/OAuth2ClientProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/auth/OAuth2ClientProducer.java deleted file mode 100644 index 1a849efc6..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/auth/OAuth2ClientProducer.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.auth; - -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; -import cn.iocoder.yudao.module.system.mq.message.auth.OAuth2ClientRefreshMessage; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * OAuth 2.0 客户端相关消息的 Producer - */ -@Component -public class OAuth2ClientProducer { - - @Resource - private RedisMQTemplate redisMQTemplate; - - /** - * 发送 {@link OAuth2ClientRefreshMessage} 消息 - */ - public void sendOAuth2ClientRefreshMessage() { - OAuth2ClientRefreshMessage message = new OAuth2ClientRefreshMessage(); - redisMQTemplate.send(message); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/dept/DeptProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/dept/DeptProducer.java deleted file mode 100644 index 9a2ca1b9c..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/dept/DeptProducer.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.dept; - -import cn.iocoder.yudao.module.system.mq.message.dept.DeptRefreshMessage; -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * Dept 部门相关消息的 Producer - */ -@Component -public class DeptProducer { - - @Resource - private RedisMQTemplate redisMQTemplate; - - /** - * 发送 {@link DeptRefreshMessage} 消息 - */ - public void sendDeptRefreshMessage() { - DeptRefreshMessage message = new DeptRefreshMessage(); - redisMQTemplate.send(message); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java index afa958cd7..51016e240 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java @@ -1,9 +1,7 @@ package cn.iocoder.yudao.module.system.mq.producer.mail; import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; -import cn.iocoder.yudao.module.system.mq.message.mail.MailAccountRefreshMessage; import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; -import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -22,22 +20,6 @@ public class MailProducer { @Resource private RedisMQTemplate redisMQTemplate; - /** - * 发送 {@link MailTemplateRefreshMessage} 消息 - */ - public void sendMailTemplateRefreshMessage() { - MailTemplateRefreshMessage message = new MailTemplateRefreshMessage(); - redisMQTemplate.send(message); - } - - /** - * 发送 {@link MailAccountRefreshMessage} 消息 - */ - public void sendMailAccountRefreshMessage() { - MailAccountRefreshMessage message = new MailAccountRefreshMessage(); - redisMQTemplate.send(message); - } - /** * 发送 {@link MailSendMessage} 消息 * diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/notify/NotifyProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/notify/NotifyProducer.java deleted file mode 100644 index 3affe2c2f..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/notify/NotifyProducer.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.notify; - -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; -import cn.iocoder.yudao.module.system.mq.message.notify.NotifyTemplateRefreshMessage; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * Notify 站内信相关消息的 Producer - * - * @author xrcoder - * @since 2022-08-06 - */ -@Slf4j -@Component -public class NotifyProducer { - - @Resource - private RedisMQTemplate redisMQTemplate; - - - /** - * 发送 {@link NotifyTemplateRefreshMessage} 消息 - */ - public void sendNotifyTemplateRefreshMessage() { - NotifyTemplateRefreshMessage message = new NotifyTemplateRefreshMessage(); - redisMQTemplate.send(message); - } - - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/package-info.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/package-info.java deleted file mode 100644 index 255ab310c..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 占位 - */ -package cn.iocoder.yudao.module.system.mq.producer; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/MenuProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/MenuProducer.java deleted file mode 100644 index 5764c872a..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/MenuProducer.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.permission; - -import cn.iocoder.yudao.module.system.mq.message.permission.MenuRefreshMessage; -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * Menu 菜单相关消息的 Producer - */ -@Component -public class MenuProducer { - - @Resource - private RedisMQTemplate redisMQTemplate; - - /** - * 发送 {@link MenuRefreshMessage} 消息 - */ - public void sendMenuRefreshMessage() { - MenuRefreshMessage message = new MenuRefreshMessage(); - redisMQTemplate.send(message); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/PermissionProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/PermissionProducer.java deleted file mode 100644 index e3c4047c2..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/PermissionProducer.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.permission; - -import cn.iocoder.yudao.module.system.mq.message.permission.RoleMenuRefreshMessage; -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; -import cn.iocoder.yudao.module.system.mq.message.permission.UserRoleRefreshMessage; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * Permission 权限相关消息的 Producer - */ -@Component -public class PermissionProducer { - - @Resource - private RedisMQTemplate redisMQTemplate; - - /** - * 发送 {@link RoleMenuRefreshMessage} 消息 - */ - public void sendRoleMenuRefreshMessage() { - RoleMenuRefreshMessage message = new RoleMenuRefreshMessage(); - redisMQTemplate.send(message); - } - - /** - * 发送 {@link UserRoleRefreshMessage} 消息 - */ - public void sendUserRoleRefreshMessage() { - UserRoleRefreshMessage message = new UserRoleRefreshMessage(); - redisMQTemplate.send(message); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/RoleProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/RoleProducer.java deleted file mode 100644 index c249d964e..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/permission/RoleProducer.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.permission; - -import cn.iocoder.yudao.module.system.mq.message.permission.RoleRefreshMessage; -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * Role 角色相关消息的 Producer - * - * @author 芋道源码 - */ -@Component -public class RoleProducer { - - @Resource - private RedisMQTemplate redisMQTemplate; - - /** - * 发送 {@link RoleRefreshMessage} 消息 - */ - public void sendRoleRefreshMessage() { - RoleRefreshMessage message = new RoleRefreshMessage(); - redisMQTemplate.send(message); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sensitiveword/SensitiveWordProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sensitiveword/SensitiveWordProducer.java deleted file mode 100644 index 3c43eca3b..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sensitiveword/SensitiveWordProducer.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.sensitiveword; - -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; -import cn.iocoder.yudao.module.system.mq.message.sensitiveword.SensitiveWordRefreshMessage; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 敏感词相关的 Producer - */ -@Component -public class SensitiveWordProducer { - - @Resource - private RedisMQTemplate redisMQTemplate; - - /** - * 发送 {@link SensitiveWordRefreshMessage} 消息 - */ - public void sendSensitiveWordRefreshMessage() { - SensitiveWordRefreshMessage message = new SensitiveWordRefreshMessage(); - redisMQTemplate.send(message); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sms/SmsProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sms/SmsProducer.java index e14fb953b..32bbde369 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sms/SmsProducer.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sms/SmsProducer.java @@ -1,10 +1,8 @@ package cn.iocoder.yudao.module.system.mq.producer.sms; import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.module.system.mq.message.sms.SmsChannelRefreshMessage; -import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; -import cn.iocoder.yudao.module.system.mq.message.sms.SmsTemplateRefreshMessage; import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; +import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -15,7 +13,7 @@ import java.util.List; * Sms 短信相关消息的 Producer * * @author zzf - * @date 2021/3/9 16:35 + * @since 2021/3/9 16:35 */ @Slf4j @Component @@ -24,22 +22,6 @@ public class SmsProducer { @Resource private RedisMQTemplate redisMQTemplate; - /** - * 发送 {@link SmsChannelRefreshMessage} 消息 - */ - public void sendSmsChannelRefreshMessage() { - SmsChannelRefreshMessage message = new SmsChannelRefreshMessage(); - redisMQTemplate.send(message); - } - - /** - * 发送 {@link SmsTemplateRefreshMessage} 消息 - */ - public void sendSmsTemplateRefreshMessage() { - SmsTemplateRefreshMessage message = new SmsTemplateRefreshMessage(); - redisMQTemplate.send(message); - } - /** * 发送 {@link SmsSendMessage} 消息 * diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java index 87033d4b3..1029b424c 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java @@ -7,10 +7,7 @@ import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqV import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; /** * 部门 Service 接口 @@ -19,11 +16,6 @@ import java.util.Map; */ public interface DeptService { - /** - * 初始化部门的本地缓存 - */ - void initLocalCache(); - /** * 创建部门 * @@ -47,21 +39,12 @@ public interface DeptService { void deleteDept(Long id); /** - * 筛选部门列表 + * 获得部门信息 * - * @param reqVO 筛选条件请求 VO - * @return 部门列表 + * @param id 部门编号 + * @return 部门信息 */ - List getDeptList(DeptListReqVO reqVO); - - /** - * 获得所有子部门,从缓存中 - * - * @param parentId 部门编号 - * @param recursive 是否递归获取所有 - * @return 子部门列表 - */ - List getDeptListByParentIdFromCache(Long parentId, boolean recursive); + DeptDO getDept(Long id); /** * 获得部门信息数组 @@ -71,6 +54,14 @@ public interface DeptService { */ List getDeptList(Collection ids); + /** + * 筛选部门列表 + * + * @param reqVO 筛选条件请求 VO + * @return 部门列表 + */ + List getDeptList(DeptListReqVO reqVO); + /** * 获得指定编号的部门 Map * @@ -86,12 +77,20 @@ public interface DeptService { } /** - * 获得部门信息 + * 获得指定部门的所有子部门 * * @param id 部门编号 - * @return 部门信息 + * @return 子部门列表 */ - DeptDO getDept(Long id); + List getChildDeptList(Long id); + + /** + * 获得所有子部门,从缓存中 + * + * @param id 父部门编号 + * @return 子部门列表 + */ + Set getChildDeptIdListFromCache(Long id); /** * 校验部门们是否有效。如下情况,视为无效: diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java index 8dc52fdf8..d088242fd 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java @@ -1,30 +1,29 @@ package cn.iocoder.yudao.module.system.service.dept; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateReqVO; import cn.iocoder.yudao.module.system.convert.dept.DeptConvert; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; import cn.iocoder.yudao.module.system.enums.dept.DeptIdEnum; -import cn.iocoder.yudao.module.system.mq.producer.dept.DeptProducer; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Multimap; -import lombok.Getter; +import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; /** @@ -37,55 +36,12 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @Slf4j public class DeptServiceImpl implements DeptService { - /** - * 部门缓存 - * key:部门编号 {@link DeptDO#getId()} - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter - private volatile Map deptCache; - /** - * 父部门缓存 - * key:部门编号 {@link DeptDO#getParentId()} - * value: 直接子部门列表 - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter - private volatile Multimap parentDeptCache; - @Resource private DeptMapper deptMapper; - @Resource - private DeptProducer deptProducer; - - /** - * 初始化 {@link #parentDeptCache} 和 {@link #deptCache} 缓存 - */ - @Override - @PostConstruct - public synchronized void initLocalCache() { - // 注意:忽略自动多租户,因为要全局初始化缓存 - TenantUtils.executeIgnore(() -> { - // 第一步:查询数据 - List depts = deptMapper.selectList(); - log.info("[initLocalCache][缓存部门,数量为:{}]", depts.size()); - - // 第二步:构建缓存 - ImmutableMap.Builder builder = ImmutableMap.builder(); - ImmutableMultimap.Builder parentBuilder = ImmutableMultimap.builder(); - depts.forEach(deptDO -> { - builder.put(deptDO.getId(), deptDO); - parentBuilder.put(deptDO.getParentId(), deptDO); - }); - deptCache = builder.build(); - parentDeptCache = parentBuilder.build(); - }); - } - @Override + @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 public Long createDept(DeptCreateReqVO reqVO) { // 校验正确性 if (reqVO.getParentId() == null) { @@ -95,12 +51,12 @@ public class DeptServiceImpl implements DeptService { // 插入部门 DeptDO dept = DeptConvert.INSTANCE.convert(reqVO); deptMapper.insert(dept); - // 发送刷新消息 - deptProducer.sendDeptRefreshMessage(); return dept.getId(); } @Override + @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 public void updateDept(DeptUpdateReqVO reqVO) { // 校验正确性 if (reqVO.getParentId() == null) { @@ -110,11 +66,11 @@ public class DeptServiceImpl implements DeptService { // 更新部门 DeptDO updateObj = DeptConvert.INSTANCE.convert(reqVO); deptMapper.updateById(updateObj); - // 发送刷新消息 - deptProducer.sendDeptRefreshMessage(); } @Override + @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 public void deleteDept(Long id) { // 校验是否存在 validateDeptExists(id); @@ -124,70 +80,30 @@ public class DeptServiceImpl implements DeptService { } // 删除部门 deptMapper.deleteById(id); - // 发送刷新消息 - deptProducer.sendDeptRefreshMessage(); - } - - @Override - public List getDeptList(DeptListReqVO reqVO) { - return deptMapper.selectList(reqVO); - } - - @Override - public List getDeptListByParentIdFromCache(Long parentId, boolean recursive) { - if (parentId == null) { - return Collections.emptyList(); - } - List result = new ArrayList<>(); - // 递归,简单粗暴 - getDeptsByParentIdFromCache(result, parentId, - recursive ? Integer.MAX_VALUE : 1, // 如果递归获取,则无限;否则,只递归 1 次 - parentDeptCache); - return result; - } - - /** - * 递归获取所有的子部门,添加到 result 结果 - * - * @param result 结果 - * @param parentId 父编号 - * @param recursiveCount 递归次数 - * @param parentDeptMap 父部门 Map,使用缓存,避免变化 - */ - private void getDeptsByParentIdFromCache(List result, Long parentId, int recursiveCount, - Multimap parentDeptMap) { - // 递归次数为 0,结束! - if (recursiveCount == 0) { - return; - } - - // 获得子部门 - Collection depts = parentDeptMap.get(parentId); - if (CollUtil.isEmpty(depts)) { - return; - } - // 针对多租户,过滤掉非当前租户的部门 - Long tenantId = TenantContextHolder.getTenantId(); - if (tenantId != null) { - depts = CollUtil.filterNew(depts, dept -> tenantId.equals(dept.getTenantId())); - } - result.addAll(depts); - - // 继续递归 - depts.forEach(dept -> getDeptsByParentIdFromCache(result, dept.getId(), - recursiveCount - 1, parentDeptMap)); } private void validateForCreateOrUpdate(Long id, Long parentId, String name) { // 校验自己存在 validateDeptExists(id); // 校验父部门的有效性 - validateParentDeptEnable(id, parentId); + validateParentDept(id, parentId); // 校验部门名的唯一性 validateDeptNameUnique(id, parentId, name); } - private void validateParentDeptEnable(Long id, Long parentId) { + @VisibleForTesting + void validateDeptExists(Long id) { + if (id == null) { + return; + } + DeptDO dept = deptMapper.selectById(id); + if (dept == null) { + throw exception(DEPT_NOT_FOUND); + } + } + + @VisibleForTesting + void validateParentDept(Long id, Long parentId) { if (parentId == null || DeptIdEnum.ROOT.getId().equals(parentId)) { return; } @@ -200,49 +116,71 @@ public class DeptServiceImpl implements DeptService { if (dept == null) { throw exception(DEPT_PARENT_NOT_EXITS); } - // 父部门被禁用 - if (!CommonStatusEnum.ENABLE.getStatus().equals(dept.getStatus())) { - throw exception(DEPT_NOT_ENABLE); - } // 父部门不能是原来的子部门 - List children = getDeptListByParentIdFromCache(id, true); + List children = getChildDeptList(id); if (children.stream().anyMatch(dept1 -> dept1.getId().equals(parentId))) { throw exception(DEPT_PARENT_IS_CHILD); } } - private void validateDeptExists(Long id) { - if (id == null) { - return; - } - DeptDO dept = deptMapper.selectById(id); + @VisibleForTesting + void validateDeptNameUnique(Long id, Long parentId, String name) { + DeptDO dept = deptMapper.selectByParentIdAndName(parentId, name); if (dept == null) { - throw exception(DEPT_NOT_FOUND); - } - } - - private void validateDeptNameUnique(Long id, Long parentId, String name) { - DeptDO menu = deptMapper.selectByParentIdAndName(parentId, name); - if (menu == null) { return; } // 如果 id 为空,说明不用比较是否为相同 id 的岗位 if (id == null) { throw exception(DEPT_NAME_DUPLICATE); } - if (!menu.getId().equals(id)) { + if (ObjectUtil.notEqual(dept.getId(), id)) { throw exception(DEPT_NAME_DUPLICATE); } } + @Override + public DeptDO getDept(Long id) { + return deptMapper.selectById(id); + } + @Override public List getDeptList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } return deptMapper.selectBatchIds(ids); } @Override - public DeptDO getDept(Long id) { - return deptMapper.selectById(id); + public List getDeptList(DeptListReqVO reqVO) { + return deptMapper.selectList(reqVO); + } + + @Override + public List getChildDeptList(Long id) { + List children = new LinkedList<>(); + // 遍历每一层 + Collection parentIds = Collections.singleton(id); + for (int i = 0; i < Short.MAX_VALUE; i++) { // 使用 Short.MAX_VALUE 避免 bug 场景下,存在死循环 + // 查询当前层,所有的子部门 + List depts = deptMapper.selectListByParentId(parentIds); + // 1. 如果没有子部门,则结束遍历 + if (CollUtil.isEmpty(depts)) { + break; + } + // 2. 如果有子部门,继续遍历 + children.addAll(depts); + parentIds = convertSet(depts, DeptDO::getId); + } + return children; + } + + @Override + @DataPermission(enable = false) // 禁用数据权限,避免简历不正确的缓存 + @Cacheable(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, key = "#id") + public Set getChildDeptIdListFromCache(Long id) { + List children = getChildDeptList(id); + return convertSet(children, DeptDO::getId); } @Override diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java index b0e02cb4b..62e97a829 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java @@ -17,19 +17,6 @@ import java.util.List; */ public interface MailAccountService { - /** - * 初始化邮箱账号的本地缓存 - */ - void initLocalCache(); - - /** - * 从缓存中获取邮箱账号 - * - * @param id 编号 - * @return 邮箱账号 - */ - MailAccountDO getMailAccountFromCache(Long id); - /** * 创建邮箱账号 * @@ -60,6 +47,14 @@ public interface MailAccountService { */ MailAccountDO getMailAccount(Long id); + /** + * 从缓存中获取邮箱账号 + * + * @param id 编号 + * @return 邮箱账号 + */ + MailAccountDO getMailAccountFromCache(Long id); + /** * 获取邮箱账号分页信息 * diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImpl.java index 4da029e47..457056f40 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImpl.java @@ -7,20 +7,18 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccou import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert; import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper; -import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer; -import lombok.Getter; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.List; -import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_NOT_EXISTS; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS; /** * 邮箱账号 Service 实现类 @@ -39,46 +37,16 @@ public class MailAccountServiceImpl implements MailAccountService { @Resource private MailTemplateService mailTemplateService; - @Resource - private MailProducer mailProducer; - - /** - * 邮箱账号缓存 - * key:邮箱账号编码 {@link MailAccountDO#getId()} - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter - private volatile Map mailAccountCache; - - @Override - @PostConstruct - public void initLocalCache() { - // 第一步:查询数据 - List accounts = mailAccountMapper.selectList(); - log.info("[initLocalCache][缓存邮箱账号,数量:{}]", accounts.size()); - - // 第二步:构建缓存 - mailAccountCache = convertMap(accounts, MailAccountDO::getId); - } - - @Override - public MailAccountDO getMailAccountFromCache(Long id) { - return mailAccountCache.get(id); - } - @Override public Long createMailAccount(MailAccountCreateReqVO createReqVO) { // 插入 MailAccountDO account = MailAccountConvert.INSTANCE.convert(createReqVO); mailAccountMapper.insert(account); - - // 发送刷新消息 - mailProducer.sendMailAccountRefreshMessage(); return account.getId(); } @Override + @Cacheable(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#updateReqVO.id") public void updateMailAccount(MailAccountUpdateReqVO updateReqVO) { // 校验是否存在 validateMailAccountExists(updateReqVO.getId()); @@ -86,11 +54,10 @@ public class MailAccountServiceImpl implements MailAccountService { // 更新 MailAccountDO updateObj = MailAccountConvert.INSTANCE.convert(updateReqVO); mailAccountMapper.updateById(updateObj); - // 发送刷新消息 - mailProducer.sendMailAccountRefreshMessage(); } @Override + @Cacheable(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#id") public void deleteMailAccount(Long id) { // 校验是否存在账号 validateMailAccountExists(id); @@ -101,8 +68,6 @@ public class MailAccountServiceImpl implements MailAccountService { // 删除 mailAccountMapper.deleteById(id); - // 发送刷新消息 - mailProducer.sendMailAccountRefreshMessage(); } private void validateMailAccountExists(Long id) { @@ -116,6 +81,12 @@ public class MailAccountServiceImpl implements MailAccountService { return mailAccountMapper.selectById(id); } + @Override + @Cacheable(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#id", unless = "#result == null") + public MailAccountDO getMailAccountFromCache(Long id) { + return getMailAccount(id); + } + @Override public PageResult getMailAccountPage(MailAccountPageReqVO pageReqVO) { return mailAccountMapper.selectPage(pageReqVO); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java index cb9dc61ef..69e02c810 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java @@ -18,11 +18,6 @@ import java.util.Map; */ public interface MailTemplateService { - /** - * 初始化邮件模版的本地缓存 - */ - void initLocalCache(); - /** * 邮件模版创建 * diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java index 62baa7e83..6d17c2b59 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java @@ -10,14 +10,14 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemp import cn.iocoder.yudao.module.system.convert.mail.MailTemplateConvert; import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper; -import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; import com.google.common.annotations.VisibleForTesting; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.validation.Valid; import java.util.List; @@ -25,8 +25,8 @@ import java.util.Map; import java.util.regex.Pattern; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_CODE_EXISTS; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS; /** * 邮箱模版 Service 实现类 @@ -47,29 +47,6 @@ public class MailTemplateServiceImpl implements MailTemplateService { @Resource private MailTemplateMapper mailTemplateMapper; - @Resource - private MailProducer mailProducer; - - /** - * 邮件模板缓存 - * key:邮件模版标识 {@link MailTemplateDO#getCode()} - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter - private volatile Map mailTemplateCache; - - @Override - @PostConstruct - public void initLocalCache() { - // 第一步:查询数据 - List templates = mailTemplateMapper.selectList(); - log.info("[initLocalCache][缓存邮件模版,数量:{}]", templates.size()); - - // 第二步:构建缓存 - mailTemplateCache = convertMap(templates, MailTemplateDO::getCode); - } - @Override public Long createMailTemplate(MailTemplateCreateReqVO createReqVO) { // 校验 code 是否唯一 @@ -79,12 +56,12 @@ public class MailTemplateServiceImpl implements MailTemplateService { MailTemplateDO template = MailTemplateConvert.INSTANCE.convert(createReqVO) .setParams(parseTemplateContentParams(createReqVO.getContent())); mailTemplateMapper.insert(template); - // 发送刷新消息 - mailProducer.sendMailTemplateRefreshMessage(); return template.getId(); } @Override + @CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, + allEntries = true) // allEntries 清空所有缓存,因为可能修改到 code 字段,不好清理 public void updateMailTemplate(@Valid MailTemplateUpdateReqVO updateReqVO) { // 校验是否存在 validateMailTemplateExists(updateReqVO.getId()); @@ -95,12 +72,10 @@ public class MailTemplateServiceImpl implements MailTemplateService { MailTemplateDO updateObj = MailTemplateConvert.INSTANCE.convert(updateReqVO) .setParams(parseTemplateContentParams(updateReqVO.getContent())); mailTemplateMapper.updateById(updateObj); - // 发送刷新消息 - mailProducer.sendMailTemplateRefreshMessage(); } @VisibleForTesting - public void validateCodeUnique(Long id, String code) { + void validateCodeUnique(Long id, String code) { MailTemplateDO template = mailTemplateMapper.selectByCode(code); if (template == null) { return; @@ -113,14 +88,14 @@ public class MailTemplateServiceImpl implements MailTemplateService { } @Override + @CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, + allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 code,不好清理 public void deleteMailTemplate(Long id) { // 校验是否存在 validateMailTemplateExists(id); // 删除 mailTemplateMapper.deleteById(id); - // 发送刷新消息 - mailProducer.sendMailTemplateRefreshMessage(); } private void validateMailTemplateExists(Long id) { @@ -132,6 +107,12 @@ public class MailTemplateServiceImpl implements MailTemplateService { @Override public MailTemplateDO getMailTemplate(Long id) {return mailTemplateMapper.selectById(id);} + @Override + @Cacheable(value = RedisKeyConstants.MAIL_TEMPLATE, key = "#code", unless = "#result == null") + public MailTemplateDO getMailTemplateByCodeFromCache(String code) { + return mailTemplateMapper.selectByCode(code); + } + @Override public PageResult getMailTemplatePage(MailTemplatePageReqVO pageReqVO) { return mailTemplateMapper.selectPage(pageReqVO); @@ -140,11 +121,6 @@ public class MailTemplateServiceImpl implements MailTemplateService { @Override public List getMailTemplateList() {return mailTemplateMapper.selectList();} - @Override - public MailTemplateDO getMailTemplateByCodeFromCache(String code) { - return mailTemplateCache.get(code); - } - @Override public String formatMailTemplateContent(String content, Map params) { return StrUtil.format(content, params); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java index 260159b79..2657e0dd9 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java @@ -16,19 +16,6 @@ import java.util.Map; */ public interface NotifyTemplateService { - /** - * 初始化站内信模板的本地缓存 - */ - void initLocalCache(); - - /** - * 获得站内信模板,从缓存中 - * - * @param code 模板编码 - * @return 站内信模板 - */ - NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code); - /** * 创建站内信模版 * @@ -59,6 +46,14 @@ public interface NotifyTemplateService { */ NotifyTemplateDO getNotifyTemplate(Long id); + /** + * 获得站内信模板,从缓存中 + * + * @param code 模板编码 + * @return 站内信模板 + */ + NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code); + /** * 获得站内信模版分页 * diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java index 5b63ac720..c28e42d7c 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java @@ -3,27 +3,28 @@ package cn.iocoder.yudao.module.system.service.notify; import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO; import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO; import cn.iocoder.yudao.module.system.convert.notify.NotifyTemplateConvert; import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper; -import cn.iocoder.yudao.module.system.mq.producer.notify.NotifyProducer; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_CODE_DUPLICATE; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS; /** * 站内信模版 Service 实现类 @@ -43,36 +44,6 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService { @Resource private NotifyTemplateMapper notifyTemplateMapper; - @Resource - private NotifyProducer notifyProducer; - - /** - * 站内信模板缓存 - * key:站内信模板编码 {@link NotifyTemplateDO#getCode()} - *

- * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - private volatile Map notifyTemplateCache; - - /** - * 初始化站内信模板的本地缓存 - */ - @Override - @PostConstruct - public void initLocalCache() { - // 第一步:查询数据 - List templates = notifyTemplateMapper.selectList(); - log.info("[initLocalCache][缓存站内信模版,数量为:{}]", templates.size()); - - // 第二步:构建缓存 - notifyTemplateCache = CollectionUtils.convertMap(templates, NotifyTemplateDO::getCode); - } - - @Override - public NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code) { - return notifyTemplateCache.get(code); - } - @Override public Long createNotifyTemplate(NotifyTemplateCreateReqVO createReqVO) { // 校验站内信编码是否重复 @@ -82,13 +53,12 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService { NotifyTemplateDO notifyTemplate = NotifyTemplateConvert.INSTANCE.convert(createReqVO); notifyTemplate.setParams(parseTemplateContentParams(notifyTemplate.getContent())); notifyTemplateMapper.insert(notifyTemplate); - - // 发送刷新消息 - notifyProducer.sendNotifyTemplateRefreshMessage(); return notifyTemplate.getId(); } @Override + @CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, + allEntries = true) // allEntries 清空所有缓存,因为可能修改到 code 字段,不好清理 public void updateNotifyTemplate(NotifyTemplateUpdateReqVO updateReqVO) { // 校验存在 validateNotifyTemplateExists(updateReqVO.getId()); @@ -99,9 +69,6 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService { NotifyTemplateDO updateObj = NotifyTemplateConvert.INSTANCE.convert(updateReqVO); updateObj.setParams(parseTemplateContentParams(updateObj.getContent())); notifyTemplateMapper.updateById(updateObj); - - // 发送刷新消息 - notifyProducer.sendNotifyTemplateRefreshMessage(); } @VisibleForTesting @@ -110,13 +77,13 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService { } @Override + @CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, + allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 code,不好清理 public void deleteNotifyTemplate(Long id) { // 校验存在 validateNotifyTemplateExists(id); // 删除 notifyTemplateMapper.deleteById(id); - // 发送刷新消息 - notifyProducer.sendNotifyTemplateRefreshMessage(); } private void validateNotifyTemplateExists(Long id) { @@ -130,13 +97,20 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService { return notifyTemplateMapper.selectById(id); } + @Override + @Cacheable(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, key = "#code", + unless = "#result == null") + public NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code) { + return notifyTemplateMapper.selectByCode(code); + } + @Override public PageResult getNotifyTemplatePage(NotifyTemplatePageReqVO pageReqVO) { return notifyTemplateMapper.selectPage(pageReqVO); } @VisibleForTesting - public void validateNotifyTemplateCodeDuplicate(Long id, String code) { + void validateNotifyTemplateCodeDuplicate(Long id, String code) { NotifyTemplateDO template = notifyTemplateMapper.selectByCode(code); if (template == null) { return; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java index 60e9f2298..de826c261 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java @@ -18,11 +18,6 @@ import java.util.Collection; */ public interface OAuth2ClientService { - /** - * 初始化 OAuth2Client 的本地缓存 - */ - void initLocalCache(); - /** * 创建 OAuth2 客户端 * @@ -53,6 +48,14 @@ public interface OAuth2ClientService { */ OAuth2ClientDO getOAuth2Client(Long id); + /** + * 获得 OAuth2 客户端,从缓存中 + * + * @param clientId 客户端编号 + * @return OAuth2 客户端 + */ + OAuth2ClientDO getOAuth2ClientFromCache(String clientId); + /** * 获得 OAuth2 客户端分页 * @@ -82,7 +85,7 @@ public interface OAuth2ClientService { * @param redirectUri 重定向地址 * @return 客户端 */ - OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, - String authorizedGrantType, Collection scopes, String redirectUri); + OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, String authorizedGrantType, + Collection scopes, String redirectUri); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java index 970387bbb..0f795495a 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.service.oauth2; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.string.StrUtils; @@ -12,22 +13,18 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2Cl import cn.iocoder.yudao.module.system.convert.auth.OAuth2ClientConvert; import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper; -import cn.iocoder.yudao.module.system.mq.producer.auth.OAuth2ClientProducer; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; import com.google.common.annotations.VisibleForTesting; -import lombok.Getter; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.Collection; -import java.util.List; -import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; /** @@ -40,48 +37,21 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @Slf4j public class OAuth2ClientServiceImpl implements OAuth2ClientService { - /** - * 客户端缓存 - * key:客户端编号 {@link OAuth2ClientDO#getClientId()} ()} - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter // 解决单测 - @Setter // 解决单测 - private volatile Map clientCache; - @Resource private OAuth2ClientMapper oauth2ClientMapper; - @Resource - private OAuth2ClientProducer oauth2ClientProducer; - - /** - * 初始化 {@link #clientCache} 缓存 - */ - @Override - @PostConstruct - public void initLocalCache() { - // 第一步:查询数据 - List clients = oauth2ClientMapper.selectList(); - log.info("[initLocalCache][缓存 OAuth2 客户端,数量为:{}]", clients.size()); - - // 第二步:构建缓存。 - clientCache = convertMap(clients, OAuth2ClientDO::getClientId); - } - @Override public Long createOAuth2Client(OAuth2ClientCreateReqVO createReqVO) { validateClientIdExists(null, createReqVO.getClientId()); // 插入 OAuth2ClientDO oauth2Client = OAuth2ClientConvert.INSTANCE.convert(createReqVO); oauth2ClientMapper.insert(oauth2Client); - // 发送刷新消息 - oauth2ClientProducer.sendOAuth2ClientRefreshMessage(); return oauth2Client.getId(); } @Override + @CacheEvict(cacheNames = RedisKeyConstants.OAUTH_CLIENT, + allEntries = true) // allEntries 清空所有缓存,因为可能修改到 clientId 字段,不好清理 public void updateOAuth2Client(OAuth2ClientUpdateReqVO updateReqVO) { // 校验存在 validateOAuth2ClientExists(updateReqVO.getId()); @@ -91,18 +61,16 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService { // 更新 OAuth2ClientDO updateObj = OAuth2ClientConvert.INSTANCE.convert(updateReqVO); oauth2ClientMapper.updateById(updateObj); - // 发送刷新消息 - oauth2ClientProducer.sendOAuth2ClientRefreshMessage(); } @Override + @CacheEvict(cacheNames = RedisKeyConstants.OAUTH_CLIENT, + allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 key,不好清理 public void deleteOAuth2Client(Long id) { // 校验存在 validateOAuth2ClientExists(id); // 删除 oauth2ClientMapper.deleteById(id); - // 发送刷新消息 - oauth2ClientProducer.sendOAuth2ClientRefreshMessage(); } private void validateOAuth2ClientExists(Long id) { @@ -131,16 +99,23 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService { return oauth2ClientMapper.selectById(id); } + @Override + @Cacheable(cacheNames = RedisKeyConstants.OAUTH_CLIENT, key = "#clientId", + unless = "#result == null") + public OAuth2ClientDO getOAuth2ClientFromCache(String clientId) { + return oauth2ClientMapper.selectByClientId(clientId); + } + @Override public PageResult getOAuth2ClientPage(OAuth2ClientPageReqVO pageReqVO) { return oauth2ClientMapper.selectPage(pageReqVO); } @Override - public OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, - String authorizedGrantType, Collection scopes, String redirectUri) { + public OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, String authorizedGrantType, + Collection scopes, String redirectUri) { // 校验客户端存在、且开启 - OAuth2ClientDO client = clientCache.get(clientId); + OAuth2ClientDO client = getSelf().getOAuth2ClientFromCache(clientId); if (client == null) { throw exception(OAUTH2_CLIENT_NOT_EXISTS); } @@ -167,4 +142,13 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService { return client; } + /** + * 获得自身的代理对象,解决 AOP 生效问题 + * + * @return 自己 + */ + private OAuth2ClientServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuService.java index 02418c077..e627ba1f4 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuService.java @@ -15,11 +15,6 @@ import java.util.List; */ public interface MenuService { - /** - * 初始化菜单的本地缓存 - */ - void initLocalCache(); - /** * 创建菜单 * @@ -67,36 +62,12 @@ public interface MenuService { List getMenuList(MenuListReqVO reqVO); /** - * 获得所有菜单,从缓存中 - * - * 任一参数为空时,则返回为空 - * - * @param menuTypes 菜单类型数组 - * @param menusStatuses 菜单状态数组 - * @return 菜单列表 - */ - List getMenuListFromCache(Collection menuTypes, Collection menusStatuses); - - /** - * 获得指定编号的菜单数组,从缓存中 - * - * 任一参数为空时,则返回为空 - * - * @param menuIds 菜单编号数组 - * @param menuTypes 菜单类型数组 - * @param menusStatuses 菜单状态数组 - * @return 菜单数组 - */ - List getMenuListFromCache(Collection menuIds, Collection menuTypes, - Collection menusStatuses); - - /** - * 获得权限对应的菜单数组 + * 获得权限对应的菜单编号数组 * * @param permission 权限标识 * @return 数组 */ - List getMenuListByPermissionFromCache(String permission); + List getMenuIdListByPermissionFromCache(String permission); /** * 获得菜单 @@ -106,4 +77,12 @@ public interface MenuService { */ MenuDO getMenu(Long id); + /** + * 获得菜单数组 + * + * @param ids 菜单编号数组 + * @return 菜单数组 + */ + List getMenuList(Collection ids); + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java index 103284dcc..a108e8f77 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java @@ -1,36 +1,29 @@ package cn.iocoder.yudao.module.system.service.permission; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuUpdateReqVO; import cn.iocoder.yudao.module.system.convert.permission.MenuConvert; import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; import cn.iocoder.yudao.module.system.dal.mysql.permission.MenuMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; -import cn.iocoder.yudao.module.system.mq.producer.permission.MenuProducer; import cn.iocoder.yudao.module.system.service.tenant.TenantService; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Multimap; -import lombok.Getter; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionSynchronization; -import org.springframework.transaction.support.TransactionSynchronizationManager; -import javax.annotation.PostConstruct; import javax.annotation.Resource; -import java.util.*; -import java.util.stream.Collectors; +import java.util.Collection; +import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @@ -43,26 +36,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @Slf4j public class MenuServiceImpl implements MenuService { - /** - * 菜单缓存 - * key:菜单编号 - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter - @Setter - private volatile Map menuCache; - /** - * 权限与菜单缓存 - * key:权限 {@link MenuDO#getPermission()} - * value:MenuDO 数组,因为一个权限可能对应多个 MenuDO 对象 - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter - @Setter - private volatile Multimap permissionMenuCache; - @Resource private MenuMapper menuMapper; @Resource @@ -71,33 +44,8 @@ public class MenuServiceImpl implements MenuService { @Lazy // 延迟,避免循环依赖报错 private TenantService tenantService; - @Resource - private MenuProducer menuProducer; - - /** - * 初始化 {@link #menuCache} 和 {@link #permissionMenuCache} 缓存 - */ - @Override - @PostConstruct - public synchronized void initLocalCache() { - // 第一步:查询数据 - List menuList = menuMapper.selectList(); - log.info("[initLocalCache][缓存菜单,数量为:{}]", menuList.size()); - - // 第二步:构建缓存 - ImmutableMap.Builder menuCacheBuilder = ImmutableMap.builder(); - ImmutableMultimap.Builder permMenuCacheBuilder = ImmutableMultimap.builder(); - menuList.forEach(menuDO -> { - menuCacheBuilder.put(menuDO.getId(), menuDO); - if (StrUtil.isNotEmpty(menuDO.getPermission())) { // 会存在 permission 为 null 的情况,导致 put 报 NPE 异常 - permMenuCacheBuilder.put(menuDO.getPermission(), menuDO); - } - }); - menuCache = menuCacheBuilder.build(); - permissionMenuCache = permMenuCacheBuilder.build(); - } - @Override + @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, key = "#reqVO.permission") public Long createMenu(MenuCreateReqVO reqVO) { // 校验父菜单存在 validateParentMenu(reqVO.getParentId(), null); @@ -108,13 +56,13 @@ public class MenuServiceImpl implements MenuService { MenuDO menu = MenuConvert.INSTANCE.convert(reqVO); initMenuProperty(menu); menuMapper.insert(menu); - // 发送刷新消息 - menuProducer.sendMenuRefreshMessage(); // 返回 return menu.getId(); } @Override + @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,因为 permission 如果变更,涉及到新老两个 permission。直接清理,简单有效 public void updateMenu(MenuUpdateReqVO reqVO) { // 校验更新的菜单是否存在 if (menuMapper.selectById(reqVO.getId()) == null) { @@ -129,34 +77,25 @@ public class MenuServiceImpl implements MenuService { MenuDO updateObject = MenuConvert.INSTANCE.convert(reqVO); initMenuProperty(updateObject); menuMapper.updateById(updateObject); - // 发送刷新消息 - menuProducer.sendMenuRefreshMessage(); } @Override @Transactional(rollbackFor = Exception.class) - public void deleteMenu(Long menuId) { + @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,因为此时不知道 id 对应的 permission 是多少。直接清理,简单有效 + public void deleteMenu(Long id) { // 校验是否还有子菜单 - if (menuMapper.selectCountByParentId(menuId) > 0) { + if (menuMapper.selectCountByParentId(id) > 0) { throw exception(MENU_EXISTS_CHILDREN); } // 校验删除的菜单是否存在 - if (menuMapper.selectById(menuId) == null) { + if (menuMapper.selectById(id) == null) { throw exception(MENU_NOT_EXISTS); } // 标记删除 - menuMapper.deleteById(menuId); + menuMapper.deleteById(id); // 删除授予给角色的权限 - permissionService.processMenuDeleted(menuId); - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - @Override - public void afterCommit() { - menuProducer.sendMenuRefreshMessage(); - } - - }); + permissionService.processMenuDeleted(id); } @Override @@ -178,33 +117,10 @@ public class MenuServiceImpl implements MenuService { } @Override - public List getMenuListFromCache(Collection menuTypes, Collection menusStatuses) { - // 任一一个参数为空,则返回空 - if (CollectionUtils.isAnyEmpty(menuTypes, menusStatuses)) { - return Collections.emptyList(); - } - // 创建新数组,避免缓存被修改 - return menuCache.values().stream().filter(menu -> menuTypes.contains(menu.getType()) - && menusStatuses.contains(menu.getStatus())) - .collect(Collectors.toList()); - } - - @Override - public List getMenuListFromCache(Collection menuIds, Collection menuTypes, - Collection menusStatuses) { - // 任一一个参数为空,则返回空 - if (CollectionUtils.isAnyEmpty(menuIds, menuTypes, menusStatuses)) { - return Collections.emptyList(); - } - return menuCache.values().stream().filter(menu -> menuIds.contains(menu.getId()) - && menuTypes.contains(menu.getType()) - && menusStatuses.contains(menu.getStatus())) - .collect(Collectors.toList()); - } - - @Override - public List getMenuListByPermissionFromCache(String permission) { - return new ArrayList<>(permissionMenuCache.get(permission)); + @Cacheable(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, key = "#permission") + public List getMenuIdListByPermissionFromCache(String permission) { + List menus = menuMapper.selectListByPermission(permission); + return convertList(menus, MenuDO::getId); } @Override @@ -212,15 +128,20 @@ public class MenuServiceImpl implements MenuService { return menuMapper.selectById(id); } + @Override + public List getMenuList(Collection ids) { + return menuMapper.selectBatchIds(ids); + } + /** * 校验父菜单是否合法 - * + *

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

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

* 例如说,只有目录或者菜单类型的菜单,才设置 icon * * @param menu 菜单 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionService.java index 97174d629..efc471431 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionService.java @@ -1,16 +1,15 @@ package cn.iocoder.yudao.module.system.service.permission; import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; -import org.springframework.lang.Nullable; import java.util.Collection; -import java.util.List; import java.util.Set; +import static java.util.Collections.singleton; + /** * 权限 Service 接口 - * + *

* 提供用户-角色、角色-菜单、角色-部门的关联权限处理 * * @author 芋道源码 @@ -18,81 +17,32 @@ import java.util.Set; public interface PermissionService { /** - * 初始化权限的本地缓存 + * 判断是否有权限,任一一个即可 + * + * @param userId 用户编号 + * @param permissions 权限 + * @return 是否 */ - void initLocalCache(); + boolean hasAnyPermissions(Long userId, String... permissions); /** - * 获得角色们拥有的菜单列表,从缓存中获取 + * 判断是否有角色,任一一个即可 * - * 任一参数为空时,则返回为空 - * - * @param roleIds 角色编号数组 - * @param menuTypes 菜单类型数组 - * @param menusStatuses 菜单状态数组 - * @return 菜单列表 + * @param roles 角色数组 + * @return 是否 */ - List getRoleMenuListFromCache(Collection roleIds, Collection menuTypes, - Collection menusStatuses); + boolean hasAnyRoles(Long userId, String... roles); - /** - * 获得用户拥有的角色编号集合,从缓存中获取 - * - * @param userId 用户编号 - * @param roleStatuses 角色状态集合. 允许为空,为空时不过滤 - * @return 角色编号集合 - */ - Set getUserRoleIdsFromCache(Long userId, @Nullable Collection roleStatuses); - - /** - * 获得角色拥有的菜单编号集合 - * - * @param roleId 角色编号 - * @return 菜单编号集合 - */ - Set getRoleMenuIds(Long roleId); - - /** - * 获得拥有多个角色的用户编号集合 - * - * @param roleIds 角色编号集合 - * @return 用户编号集合 - */ - Set getUserRoleIdListByRoleIds(Collection roleIds); + // ========== 角色-菜单的相关方法 ========== /** * 设置角色菜单 * - * @param roleId 角色编号 + * @param roleId 角色编号 * @param menuIds 菜单编号集合 */ void assignRoleMenu(Long roleId, Set menuIds); - /** - * 获得用户拥有的角色编号集合 - * - * @param userId 用户编号 - * @return 角色编号集合 - */ - Set getUserRoleIdListByUserId(Long userId); - - /** - * 设置用户角色 - * - * @param userId 角色编号 - * @param roleIds 角色编号集合 - */ - void assignUserRole(Long userId, Set roleIds); - - /** - * 设置角色的数据权限 - * - * @param roleId 角色编号 - * @param dataScope 数据范围 - * @param dataScopeDeptIds 部门编号数组 - */ - void assignRoleDataScope(Long roleId, Integer dataScope, Set dataScopeDeptIds); - /** * 处理角色删除时,删除关联授权数据 * @@ -108,28 +58,82 @@ public interface PermissionService { void processMenuDeleted(Long menuId); /** - * 处理用户删除是,删除关联授权数据 + * 获得角色拥有的菜单编号集合 + * + * @param roleId 角色编号 + * @return 菜单编号集合 + */ + default Set getRoleMenuListByRoleId(Long roleId) { + return getRoleMenuListByRoleId(singleton(roleId)); + } + + /** + * 获得角色们拥有的菜单编号集合 + * + * @param roleIds 角色编号数组 + * @return 菜单编号集合 + */ + Set getRoleMenuListByRoleId(Collection roleIds); + + /** + * 获得拥有指定菜单的角色编号数组,从缓存中获取 + * + * @param menuId 菜单编号 + * @return 角色编号数组 + */ + Set getMenuRoleIdListByMenuIdFromCache(Long menuId); + + // ========== 用户-角色的相关方法 ========== + + /** + * 设置用户角色 + * + * @param userId 角色编号 + * @param roleIds 角色编号集合 + */ + void assignUserRole(Long userId, Set roleIds); + + /** + * 处理用户删除时,删除关联授权数据 * * @param userId 用户编号 */ void processUserDeleted(Long userId); /** - * 判断是否有权限,任一一个即可 + * 获得拥有多个角色的用户编号集合 * - * @param userId 用户编号 - * @param permissions 权限 - * @return 是否 + * @param roleIds 角色编号集合 + * @return 用户编号集合 */ - boolean hasAnyPermissions(Long userId, String... permissions); + Set getUserRoleIdListByRoleId(Collection roleIds); /** - * 判断是否有角色,任一一个即可 + * 获得用户拥有的角色编号集合 * - * @param roles 角色数组 - * @return 是否 + * @param userId 用户编号 + * @return 角色编号集合 */ - boolean hasAnyRoles(Long userId, String... roles); + Set getUserRoleIdListByUserId(Long userId); + + /** + * 获得用户拥有的角色编号集合,从缓存中获取 + * + * @param userId 用户编号 + * @return 角色编号集合 + */ + Set getUserRoleIdListByUserIdFromCache(Long userId); + + // ========== 用户-部门的相关方法 ========== + + /** + * 设置角色的数据权限 + * + * @param roleId 角色编号 + * @param dataScope 数据范围 + * @param dataScopeDeptIds 部门编号数组 + */ + void assignRoleDataScope(Long roleId, Integer dataScope, Set dataScopeDeptIds); /** * 获得登陆用户的部门数据权限 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java index 43365cf46..81bfcfc1f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java @@ -3,45 +3,38 @@ package cn.iocoder.yudao.module.system.service.permission; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ArrayUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO; import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper; import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; -import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer; import cn.iocoder.yudao.module.system.service.dept.DeptService; import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import com.baomidou.dynamic.datasource.annotation.DSTransactional; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Suppliers; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Multimap; import com.google.common.collect.Sets; -import lombok.Getter; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionSynchronization; -import org.springframework.transaction.support.TransactionSynchronizationManager; -import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.*; import java.util.function.Supplier; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static java.util.Collections.singleton; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; /** * 权限 Service 实现类 @@ -52,38 +45,6 @@ import static java.util.Collections.singleton; @Slf4j public class PermissionServiceImpl implements PermissionService { - /** - * 角色编号与菜单编号的缓存映射 - * key:角色编号 - * value:菜单编号的数组 - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter - @Setter // 单元测试需要 - private volatile Multimap roleMenuCache; - /** - * 菜单编号与角色编号的缓存映射 - * key:菜单编号 - * value:角色编号的数组 - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter - @Setter // 单元测试需要 - private volatile Multimap menuRoleCache; - - /** - * 用户编号与角色编号的缓存映射 - * key:用户编号 - * value:角色编号的数组 - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter - @Setter // 单元测试需要 - private volatile Map> userRoleCache; - @Resource private RoleMenuMapper roleMenuMapper; @Resource @@ -98,115 +59,89 @@ public class PermissionServiceImpl implements PermissionService { @Resource private AdminUserService userService; - @Resource - private PermissionProducer permissionProducer; - @Override - @PostConstruct - public void initLocalCache() { - initLocalCacheForRoleMenu(); - initLocalCacheForUserRole(); + public boolean hasAnyPermissions(Long userId, String... permissions) { + // 如果为空,说明已经有权限 + if (ArrayUtil.isEmpty(permissions)) { + return true; + } + + // 获得当前登录的角色。如果为空,说明没有权限 + List roles = getEnableUserRoleListByUserIdFromCache(userId); + if (CollUtil.isEmpty(roles)) { + return false; + } + + // 情况一:遍历判断每个权限,如果有一满足,说明有权限 + for (String permission : permissions) { + if (hasAnyPermission(roles, permission)) { + return true; + } + } + + // 情况二:如果是超管,也说明有权限 + return roleService.hasAnySuperAdmin(convertSet(roles, RoleDO::getId)); } /** - * 刷新 RoleMenu 本地缓存 + * 判断指定角色,是否拥有该 permission 权限 + * + * @param roles 指定角色数组 + * @param permission 权限标识 + * @return 是否拥有 */ - @VisibleForTesting - void initLocalCacheForRoleMenu() { - // 注意:忽略自动多租户,因为要全局初始化缓存 - TenantUtils.executeIgnore(() -> { - // 第一步:查询数据 - List roleMenus = roleMenuMapper.selectList(); - log.info("[initLocalCacheForRoleMenu][缓存角色与菜单,数量为:{}]", roleMenus.size()); + private boolean hasAnyPermission(List roles, String permission) { + List menuIds = menuService.getMenuIdListByPermissionFromCache(permission); + // 采用严格模式,如果权限找不到对应的 Menu 的话,也认为没有权限 + if (CollUtil.isEmpty(menuIds)) { + return false; + } - // 第二步:构建缓存 - ImmutableMultimap.Builder roleMenuCacheBuilder = ImmutableMultimap.builder(); - ImmutableMultimap.Builder menuRoleCacheBuilder = ImmutableMultimap.builder(); - roleMenus.forEach(roleMenuDO -> { - roleMenuCacheBuilder.put(roleMenuDO.getRoleId(), roleMenuDO.getMenuId()); - menuRoleCacheBuilder.put(roleMenuDO.getMenuId(), roleMenuDO.getRoleId()); - }); - roleMenuCache = roleMenuCacheBuilder.build(); - menuRoleCache = menuRoleCacheBuilder.build(); - }); - } - - /** - * 刷新 UserRole 本地缓存 - */ - @VisibleForTesting - void initLocalCacheForUserRole() { - // 注意:忽略自动多租户,因为要全局初始化缓存 - TenantUtils.executeIgnore(() -> { - // 第一步:加载数据 - List userRoles = userRoleMapper.selectList(); - log.info("[initLocalCacheForUserRole][缓存用户与角色,数量为:{}]", userRoles.size()); - - // 第二步:构建缓存。 - ImmutableMultimap.Builder userRoleCacheBuilder = ImmutableMultimap.builder(); - userRoles.forEach(userRoleDO -> userRoleCacheBuilder.put(userRoleDO.getUserId(), userRoleDO.getRoleId())); - userRoleCache = CollectionUtils.convertMultiMap2(userRoles, UserRoleDO::getUserId, UserRoleDO::getRoleId); - }); + // 判断是否有权限 + Set roleIds = convertSet(roles, RoleDO::getId); + for (Long menuId : menuIds) { + // 获得拥有该菜单的角色编号集合 + Set menuRoleIds = getSelf().getMenuRoleIdListByMenuIdFromCache(menuId); + // 如果有交集,说明有权限 + if (CollUtil.containsAny(menuRoleIds, roleIds)) { + return true; + } + } + return false; } @Override - public List getRoleMenuListFromCache(Collection roleIds, Collection menuTypes, - Collection menusStatuses) { - // 任一一个参数为空时,不返回任何菜单 - if (CollectionUtils.isAnyEmpty(roleIds, menuTypes, menusStatuses)) { - return Collections.emptyList(); + public boolean hasAnyRoles(Long userId, String... roles) { + // 如果为空,说明已经有权限 + if (ArrayUtil.isEmpty(roles)) { + return true; } - // 判断角色是否包含超级管理员。如果是超级管理员,获取到全部 - List roleList = roleService.getRoleListFromCache(roleIds); - if (roleService.hasAnySuperAdmin(roleList)) { - return menuService.getMenuListFromCache(menuTypes, menusStatuses); + // 获得当前登录的角色。如果为空,说明没有权限 + List roleList = getEnableUserRoleListByUserIdFromCache(userId); + if (CollUtil.isEmpty(roleList)) { + return false; } - // 获得角色拥有的菜单关联 - List menuIds = MapUtils.getList(roleMenuCache, roleIds); - return menuService.getMenuListFromCache(menuIds, menuTypes, menusStatuses); + // 判断是否有角色 + Set userRoles = convertSet(roleList, RoleDO::getCode); + return CollUtil.containsAny(userRoles, Sets.newHashSet(roles)); } - @Override - public Set getUserRoleIdsFromCache(Long userId, Collection roleStatuses) { - Set cacheRoleIds = userRoleCache.get(userId); - // 创建用户的时候没有分配角色,会存在空指针异常 - if (CollUtil.isEmpty(cacheRoleIds)) { - return Collections.emptySet(); - } - Set roleIds = new HashSet<>(cacheRoleIds); - // 过滤角色状态 - if (CollectionUtil.isNotEmpty(roleStatuses)) { - roleIds.removeIf(roleId -> { - RoleDO role = roleService.getRoleFromCache(roleId); - return role == null || !roleStatuses.contains(role.getStatus()); - }); - } - return roleIds; - } + // ========== 角色-菜单的相关方法 ========== @Override - public Set getRoleMenuIds(Long roleId) { - // 如果是管理员的情况下,获取全部菜单编号 - if (roleService.hasAnySuperAdmin(Collections.singleton(roleId))) { - return convertSet(menuService.getMenuList(), MenuDO::getId); - } - // 如果是非管理员的情况下,获得拥有的菜单编号 - return convertSet(roleMenuMapper.selectListByRoleId(roleId), RoleMenuDO::getMenuId); - } - - @Override - @Transactional(rollbackFor = Exception.class) + @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 + @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,主要一次更新涉及到的 menuIds 较多,反倒批量会更快 public void assignRoleMenu(Long roleId, Set menuIds) { // 获得角色拥有菜单编号 - Set dbMenuIds = convertSet(roleMenuMapper.selectListByRoleId(roleId), - RoleMenuDO::getMenuId); + Set dbMenuIds = convertSet(roleMenuMapper.selectListByRoleId(roleId), RoleMenuDO::getMenuId); // 计算新增和删除的菜单编号 Collection createMenuIds = CollUtil.subtract(menuIds, dbMenuIds); Collection deleteMenuIds = CollUtil.subtract(dbMenuIds, menuIds); // 执行新增和删除。对于已经授权的菜单,不用做任何处理 - if (!CollectionUtil.isEmpty(createMenuIds)) { + if (CollUtil.isNotEmpty(createMenuIds)) { roleMenuMapper.insertBatch(CollectionUtils.convertList(createMenuIds, menuId -> { RoleMenuDO entity = new RoleMenuDO(); entity.setRoleId(roleId); @@ -214,34 +149,57 @@ public class PermissionServiceImpl implements PermissionService { return entity; })); } - if (!CollectionUtil.isEmpty(deleteMenuIds)) { + if (CollUtil.isNotEmpty(deleteMenuIds)) { roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds); } - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - @Override - public void afterCommit() { - permissionProducer.sendRoleMenuRefreshMessage(); - } - - }); - } - - @Override - public Set getUserRoleIdListByUserId(Long userId) { - return convertSet(userRoleMapper.selectListByUserId(userId), - UserRoleDO::getRoleId); - } - - @Override - public Set getUserRoleIdListByRoleIds(Collection roleIds) { - return convertSet(userRoleMapper.selectListByRoleIds(roleIds), - UserRoleDO::getUserId); } @Override @Transactional(rollbackFor = Exception.class) + @Caching(evict = { + @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST, + allEntries = true), // allEntries 清空所有缓存,此处无法方便获得 roleId 对应的 menu 缓存们 + @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,此处无法方便获得 roleId 对应的 user 缓存们 + }) + public void processRoleDeleted(Long roleId) { + // 标记删除 UserRole + userRoleMapper.deleteListByRoleId(roleId); + // 标记删除 RoleMenu + roleMenuMapper.deleteListByRoleId(roleId); + } + + @Override + @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST, key = "#menuId") + public void processMenuDeleted(Long menuId) { + roleMenuMapper.deleteListByMenuId(menuId); + } + + @Override + public Set getRoleMenuListByRoleId(Collection roleIds) { + if (CollUtil.isEmpty(roleIds)) { + return Collections.emptySet(); + } + + // 如果是管理员的情况下,获取全部菜单编号 + if (roleService.hasAnySuperAdmin(roleIds)) { + return convertSet(menuService.getMenuList(), MenuDO::getId); + } + // 如果是非管理员的情况下,获得拥有的菜单编号 + return convertSet(roleMenuMapper.selectListByRoleId(roleIds), RoleMenuDO::getMenuId); + } + + @Override + @Cacheable(value = RedisKeyConstants.MENU_ROLE_ID_LIST, key = "#menuId") + public Set getMenuRoleIdListByMenuIdFromCache(Long menuId) { + return convertSet(roleMenuMapper.selectListByMenuId(menuId), RoleMenuDO::getRoleId); + } + + // ========== 用户-角色的相关方法 ========== + + @Override + @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 + @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId") public void assignUserRole(Long userId, Set roleIds) { // 获得角色拥有角色编号 Set dbRoleIds = convertSet(userRoleMapper.selectListByUserId(userId), @@ -261,137 +219,68 @@ public class PermissionServiceImpl implements PermissionService { if (!CollectionUtil.isEmpty(deleteMenuIds)) { userRoleMapper.deleteListByUserIdAndRoleIdIds(userId, deleteMenuIds); } - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - @Override - public void afterCommit() { - permissionProducer.sendUserRoleRefreshMessage(); - } - - }); } + @Override + @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId") + public void processUserDeleted(Long userId) { + userRoleMapper.deleteListByUserId(userId); + } + + @Override + public Set getUserRoleIdListByUserId(Long userId) { + return convertSet(userRoleMapper.selectListByUserId(userId), UserRoleDO::getRoleId); + } + + @Override + @Cacheable(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId") + public Set getUserRoleIdListByUserIdFromCache(Long userId) { + return getUserRoleIdListByUserId(userId); + } + + @Override + public Set getUserRoleIdListByRoleId(Collection roleIds) { + return convertSet(userRoleMapper.selectListByRoleIds(roleIds), UserRoleDO::getUserId); + } + + /** + * 获得用户拥有的角色,并且这些角色是开启状态的 + * + * @param userId 用户编号 + * @return 用户拥有的角色 + */ + @VisibleForTesting + List getEnableUserRoleListByUserIdFromCache(Long userId) { + // 获得用户拥有的角色编号 + Set roleIds = getSelf().getUserRoleIdListByUserIdFromCache(userId); + // 获得角色数组,并移除被禁用的 + List roles = roleService.getRoleListFromCache(roleIds); + roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); + return roles; + } + + // ========== 用户-部门的相关方法 ========== + @Override public void assignRoleDataScope(Long roleId, Integer dataScope, Set dataScopeDeptIds) { roleService.updateRoleDataScope(roleId, dataScope, dataScopeDeptIds); } - @Override - @Transactional(rollbackFor = Exception.class) - public void processRoleDeleted(Long roleId) { - // 标记删除 UserRole - userRoleMapper.deleteListByRoleId(roleId); - // 标记删除 RoleMenu - roleMenuMapper.deleteListByRoleId(roleId); - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - @Override - public void afterCommit() { - permissionProducer.sendRoleMenuRefreshMessage(); - permissionProducer.sendUserRoleRefreshMessage(); - } - - }); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void processMenuDeleted(Long menuId) { - roleMenuMapper.deleteListByMenuId(menuId); - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - @Override - public void afterCommit() { - permissionProducer.sendRoleMenuRefreshMessage(); - } - - }); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void processUserDeleted(Long userId) { - userRoleMapper.deleteListByUserId(userId); - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - @Override - public void afterCommit() { - permissionProducer.sendUserRoleRefreshMessage(); - } - - }); - } - - @Override - public boolean hasAnyPermissions(Long userId, String... permissions) { - // 如果为空,说明已经有权限 - if (ArrayUtil.isEmpty(permissions)) { - return true; - } - - // 获得当前登录的角色。如果为空,说明没有权限 - Set roleIds = getUserRoleIdsFromCache(userId, singleton(CommonStatusEnum.ENABLE.getStatus())); - if (CollUtil.isEmpty(roleIds)) { - return false; - } - // 判断是否是超管。如果是,当然符合条件 - if (roleService.hasAnySuperAdmin(roleIds)) { - return true; - } - - // 遍历权限,判断是否有一个满足 - return Arrays.stream(permissions).anyMatch(permission -> { - List menuList = menuService.getMenuListByPermissionFromCache(permission); - // 采用严格模式,如果权限找不到对应的 Menu 的话,认为 - if (CollUtil.isEmpty(menuList)) { - return false; - } - // 获得是否拥有该权限,任一一个 - return menuList.stream().anyMatch(menu -> CollUtil.containsAny(roleIds, - menuRoleCache.get(menu.getId()))); - }); - } - - @Override - public boolean hasAnyRoles(Long userId, String... roles) { - // 如果为空,说明已经有权限 - if (ArrayUtil.isEmpty(roles)) { - return true; - } - - // 获得当前登录的角色。如果为空,说明没有权限 - Set roleIds = getUserRoleIdsFromCache(userId, singleton(CommonStatusEnum.ENABLE.getStatus())); - if (CollUtil.isEmpty(roleIds)) { - return false; - } - // 判断是否是超管。如果是,当然符合条件 - if (roleService.hasAnySuperAdmin(roleIds)) { - return true; - } - Set userRoles = convertSet(roleService.getRoleListFromCache(roleIds), - RoleDO::getCode); - return CollUtil.containsAny(userRoles, Sets.newHashSet(roles)); - } - @Override @DataPermission(enable = false) // 关闭数据权限,不然就会出现递归获取数据权限的问题 - @TenantIgnore // 忽略多租户的自动过滤。如果不忽略,会导致添加租户时,因为切换租户,导致获取不到 User。即使忽略,本身该方法不存在跨租户的操作,不会存在问题。 public DeptDataPermissionRespDTO getDeptDataPermission(Long userId) { // 获得用户的角色 - Set roleIds = getUserRoleIdsFromCache(userId, singleton(CommonStatusEnum.ENABLE.getStatus())); + List roles = getEnableUserRoleListByUserIdFromCache(userId); + // 如果角色为空,则只能查看自己 DeptDataPermissionRespDTO result = new DeptDataPermissionRespDTO(); - if (CollUtil.isEmpty(roleIds)) { + if (CollUtil.isEmpty(roles)) { result.setSelf(true); return result; } - List roles = roleService.getRoleListFromCache(roleIds); // 获得用户的部门编号的缓存,通过 Guava 的 Suppliers 惰性求值,即有且仅有第一次发起 DB 的查询 - Supplier userDeptIdCache = Suppliers.memoize(() -> userService.getUser(userId).getDeptId()); + Supplier userDeptId = Suppliers.memoize(() -> userService.getUser(userId).getDeptId()); // 遍历每个角色,计算 for (RoleDO role : roles) { // 为空时,跳过 @@ -408,20 +297,19 @@ public class PermissionServiceImpl implements PermissionService { CollUtil.addAll(result.getDeptIds(), role.getDataScopeDeptIds()); // 自定义可见部门时,保证可以看到自己所在的部门。否则,一些场景下可能会有问题。 // 例如说,登录时,基于 t_user 的 username 查询会可能被 dept_id 过滤掉 - CollUtil.addAll(result.getDeptIds(), userDeptIdCache.get()); + CollUtil.addAll(result.getDeptIds(), userDeptId.get()); continue; } // 情况三,DEPT_ONLY if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_ONLY.getScope())) { - CollectionUtils.addIfNotNull(result.getDeptIds(), userDeptIdCache.get()); + CollectionUtils.addIfNotNull(result.getDeptIds(), userDeptId.get()); continue; } // 情况四,DEPT_DEPT_AND_CHILD if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_AND_CHILD.getScope())) { - List depts = deptService.getDeptListByParentIdFromCache(userDeptIdCache.get(), true); - CollUtil.addAll(result.getDeptIds(), CollectionUtils.convertList(depts, DeptDO::getId)); + CollUtil.addAll(result.getDeptIds(), deptService.getChildDeptIdListFromCache(userDeptId.get())); // 添加本身部门编号 - CollUtil.addAll(result.getDeptIds(), userDeptIdCache.get()); + CollUtil.addAll(result.getDeptIds(), userDeptId.get()); continue; } // 情况五,SELF @@ -430,9 +318,18 @@ public class PermissionServiceImpl implements PermissionService { continue; } // 未知情况,error log 即可 - log.error("[getDeptDataPermission][LoginUser({}) role({}) 无法处理]", userId, JsonUtils.toJsonString(result)); + log.error("[getDeptDataPermission][LoginUser({}) role({}) 无法处理]", userId, toJsonString(result)); } return result; } + /** + * 获得自身的代理对象,解决 AOP 生效问题 + * + * @return 自己 + */ + private PermissionServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java index 70acbdcb7..d837d1e8b 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java @@ -6,7 +6,6 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleEx import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; -import org.springframework.lang.Nullable; import javax.validation.Valid; import java.util.Collection; @@ -20,11 +19,6 @@ import java.util.Set; */ public interface RoleService { - /** - * 初始化角色的本地缓存 - */ - void initLocalCache(); - /** * 创建角色 * @@ -65,6 +59,14 @@ public interface RoleService { */ void updateRoleDataScope(Long id, Integer dataScope, Set dataScopeDeptIds); + /** + * 获得角色 + * + * @param id 角色编号 + * @return 角色 + */ + RoleDO getRole(Long id); + /** * 获得角色,从缓存中 * @@ -76,10 +78,10 @@ public interface RoleService { /** * 获得角色列表 * - * @param statuses 筛选的状态。允许空,空时不筛选 + * @param ids 角色编号数组 * @return 角色列表 */ - List getRoleListByStatus(@Nullable Collection statuses); + List getRoleList(Collection ids); /** * 获得角色数组,从缓存中 @@ -90,30 +92,19 @@ public interface RoleService { List getRoleListFromCache(Collection ids); /** - * 判断角色数组中,是否有超级管理员 + * 获得角色列表 * - * @param roleList 角色数组 - * @return 是否有管理员 + * @param statuses 筛选的状态 + * @return 角色列表 */ - boolean hasAnySuperAdmin(Collection roleList); + List getRoleListByStatus(Collection statuses); /** - * 判断角色编号数组中,是否有管理员 + * 获得所有角色列表 * - * @param ids 角色编号数组 - * @return 是否有管理员 + * @return 角色列表 */ - default boolean hasAnySuperAdmin(Set ids) { - return hasAnySuperAdmin(getRoleListFromCache(ids)); - } - - /** - * 获得角色 - * - * @param id 角色编号 - * @return 角色 - */ - RoleDO getRole(Long id); + List getRoleList(); /** * 获得角色分页 @@ -131,6 +122,14 @@ public interface RoleService { */ List getRoleList(RoleExportReqVO reqVO); + /** + * 判断角色编号数组中,是否有管理员 + * + * @param ids 角色编号数组 + * @return 是否有管理员 + */ + boolean hasAnySuperAdmin(Collection ids); + /** * 校验角色们是否有效。如下情况,视为无效: * 1. 角色编号不存在 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java index 30ac2daa0..c8ae9f343 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java @@ -3,9 +3,9 @@ package cn.iocoder.yudao.module.system.service.permission; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleExportReqVO; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; @@ -13,26 +13,23 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleUp import cn.iocoder.yudao.module.system.convert.permission.RoleConvert; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; -import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer; import com.google.common.annotations.VisibleForTesting; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.springframework.lang.Nullable; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionSynchronization; -import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.StringUtils; -import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.*; -import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @@ -45,43 +42,14 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @Slf4j public class RoleServiceImpl implements RoleService { - /** - * 角色缓存 - * key:角色编号 {@link RoleDO#getId()} - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter - private volatile Map roleCache; - @Resource private PermissionService permissionService; @Resource private RoleMapper roleMapper; - @Resource - private RoleProducer roleProducer; - - /** - * 初始化 {@link #roleCache} 缓存 - */ @Override - @PostConstruct - public void initLocalCache() { - // 注意:忽略自动多租户,因为要全局初始化缓存 - TenantUtils.executeIgnore(() -> { - // 第一步:查询数据 - List roleList = roleMapper.selectList(); - log.info("[initLocalCache][缓存角色,数量为:{}]", roleList.size()); - - // 第二步:构建缓存 - roleCache = convertMap(roleList, RoleDO::getId); - }); - } - - @Override - @Transactional + @Transactional(rollbackFor = Exception.class) public Long createRole(RoleCreateReqVO reqVO, Integer type) { // 校验角色 validateRoleDuplicate(reqVO.getName(), reqVO.getCode(), null); @@ -91,18 +59,12 @@ public class RoleServiceImpl implements RoleService { role.setStatus(CommonStatusEnum.ENABLE.getStatus()); role.setDataScope(DataScopeEnum.ALL.getScope()); // 默认可查看所有数据。原因是,可能一些项目不需要项目权限 roleMapper.insert(role); - // 发送刷新消息 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - @Override - public void afterCommit() { - roleProducer.sendRoleRefreshMessage(); - } - }); // 返回 return role.getId(); } @Override + @CacheEvict(value = RedisKeyConstants.ROLE, key = "#reqVO.id") public void updateRole(RoleUpdateReqVO reqVO) { // 校验是否可以更新 validateRoleForUpdate(reqVO.getId()); @@ -112,11 +74,10 @@ public class RoleServiceImpl implements RoleService { // 更新到数据库 RoleDO updateObj = RoleConvert.INSTANCE.convert(reqVO); roleMapper.updateById(updateObj); - // 发送刷新消息 - roleProducer.sendRoleRefreshMessage(); } @Override + @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id") public void updateRoleStatus(Long id, Integer status) { // 校验是否可以更新 validateRoleForUpdate(id); @@ -124,11 +85,10 @@ public class RoleServiceImpl implements RoleService { // 更新状态 RoleDO updateObj = new RoleDO().setId(id).setStatus(status); roleMapper.updateById(updateObj); - // 发送刷新消息 - roleProducer.sendRoleRefreshMessage(); } @Override + @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id") public void updateRoleDataScope(Long id, Integer dataScope, Set dataScopeDeptIds) { // 校验是否可以更新 validateRoleForUpdate(id); @@ -139,12 +99,11 @@ public class RoleServiceImpl implements RoleService { updateObject.setDataScope(dataScope); updateObject.setDataScopeDeptIds(dataScopeDeptIds); roleMapper.updateById(updateObject); - // 发送刷新消息 - roleProducer.sendRoleRefreshMessage(); } @Override @Transactional(rollbackFor = Exception.class) + @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id") public void deleteRole(Long id) { // 校验是否可以更新 validateRoleForUpdate(id); @@ -152,60 +111,6 @@ public class RoleServiceImpl implements RoleService { roleMapper.deleteById(id); // 删除相关数据 permissionService.processRoleDeleted(id); - // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - @Override - public void afterCommit() { - roleProducer.sendRoleRefreshMessage(); - } - - }); - } - - @Override - public RoleDO getRoleFromCache(Long id) { - return roleCache.get(id); - } - - @Override - public List getRoleListByStatus(@Nullable Collection statuses) { - if (CollUtil.isEmpty(statuses)) { - return roleMapper.selectList(); - } - return roleMapper.selectListByStatus(statuses); - } - - @Override - public List getRoleListFromCache(Collection ids) { - if (CollectionUtil.isEmpty(ids)) { - return Collections.emptyList(); - } - return roleCache.values().stream().filter(roleDO -> ids.contains(roleDO.getId())) - .collect(Collectors.toList()); - } - - @Override - public boolean hasAnySuperAdmin(Collection roleList) { - if (CollectionUtil.isEmpty(roleList)) { - return false; - } - return roleList.stream().anyMatch(role -> RoleCodeEnum.isSuperAdmin(role.getCode())); - } - - @Override - public RoleDO getRole(Long id) { - return roleMapper.selectById(id); - } - - @Override - public PageResult getRolePage(RolePageReqVO reqVO) { - return roleMapper.selectPage(reqVO); - } - - @Override - public List getRoleList(RoleExportReqVO reqVO) { - return roleMapper.selectList(reqVO); } /** @@ -257,6 +162,69 @@ public class RoleServiceImpl implements RoleService { } } + @Override + public RoleDO getRole(Long id) { + return roleMapper.selectById(id); + } + + @Override + @Cacheable(value = RedisKeyConstants.ROLE, key = "#id", + unless = "#result == null") + public RoleDO getRoleFromCache(Long id) { + return roleMapper.selectById(id); + } + + + @Override + public List getRoleListByStatus(Collection statuses) { + return roleMapper.selectListByStatus(statuses); + } + + @Override + public List getRoleList() { + return roleMapper.selectList(); + } + + @Override + public List getRoleList(Collection ids) { + if (CollectionUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return roleMapper.selectBatchIds(ids); + } + + @Override + public List getRoleListFromCache(Collection ids) { + if (CollectionUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + // 这里采用 for 循环从缓存中获取,主要考虑 Spring CacheManager 无法批量操作的问题 + RoleServiceImpl self = getSelf(); + return convertList(ids, self::getRoleFromCache); + } + + @Override + public PageResult getRolePage(RolePageReqVO reqVO) { + return roleMapper.selectPage(reqVO); + } + + @Override + public List getRoleList(RoleExportReqVO reqVO) { + return roleMapper.selectList(reqVO); + } + + @Override + public boolean hasAnySuperAdmin(Collection ids) { + if (CollectionUtil.isEmpty(ids)) { + return false; + } + RoleServiceImpl self = getSelf(); + return ids.stream().anyMatch(id -> { + RoleDO role = self.getRoleFromCache(id); + return role != null && RoleCodeEnum.isSuperAdmin(role.getCode()); + }); + } + @Override public void validateRoleList(Collection ids) { if (CollUtil.isEmpty(ids)) { @@ -276,4 +244,14 @@ public class RoleServiceImpl implements RoleService { } }); } + + /** + * 获得自身的代理对象,解决 AOP 生效问题 + * + * @return 自己 + */ + private RoleServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordService.java index a852ec9cf..658039cd2 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordService.java @@ -18,11 +18,6 @@ import java.util.Set; */ public interface SensitiveWordService { - /** - * 初始化本地缓存 - */ - void initLocalCache(); - /** * 创建敏感词 * diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImpl.java index b82807855..08779ea3e 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImpl.java @@ -11,21 +11,24 @@ import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.Sensitiv import cn.iocoder.yudao.module.system.convert.sensitiveword.SensitiveWordConvert; import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO; import cn.iocoder.yudao.module.system.dal.mysql.sensitiveword.SensitiveWordMapper; -import cn.iocoder.yudao.module.system.mq.producer.sensitiveword.SensitiveWordProducer; import cn.iocoder.yudao.module.system.util.collection.SimpleTrie; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import javax.annotation.PostConstruct; import javax.annotation.Resource; +import java.time.LocalDateTime; import java.util.*; +import java.util.concurrent.TimeUnit; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_WORD_EXISTS; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_WORD_NOT_EXISTS; @@ -39,6 +42,11 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_ @Validated public class SensitiveWordServiceImpl implements SensitiveWordService { + /** + * 敏感词列表缓存 + */ + @Getter + private volatile List sensitiveWordCache = Collections.emptyList(); /** * 敏感词标签缓存 * key:敏感词编号 {@link SensitiveWordDO#getId()} @@ -51,9 +59,6 @@ public class SensitiveWordServiceImpl implements SensitiveWordService { @Resource private SensitiveWordMapper sensitiveWordMapper; - @Resource - private SensitiveWordProducer sensitiveWordProducer; - /** * 默认的敏感词的字典树,包含所有敏感词 */ @@ -68,7 +73,6 @@ public class SensitiveWordServiceImpl implements SensitiveWordService { /** * 初始化缓存 */ - @Override @PostConstruct public void initLocalCache() { // 第一步:查询数据 @@ -80,6 +84,7 @@ public class SensitiveWordServiceImpl implements SensitiveWordService { Set tags = new HashSet<>(); sensitiveWords.forEach(word -> tags.addAll(word.getTags())); sensitiveWordTagsCache = tags; + sensitiveWordCache = sensitiveWords; // 写入 defaultSensitiveWordTrie、tagSensitiveWordTries 缓存 initSensitiveWordTrie(sensitiveWords); } @@ -105,6 +110,26 @@ public class SensitiveWordServiceImpl implements SensitiveWordService { this.tagSensitiveWordTries = tagSensitiveWordTries; } + /** + * 通过定时任务轮询,刷新缓存 + * + * 目的:多节点部署时,通过轮询”通知“所有节点,进行刷新 + */ + @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS) + public void refreshLocalCache() { + // 情况一:如果缓存里没有数据,则直接刷新缓存 + if (CollUtil.isEmpty(sensitiveWordCache)) { + initLocalCache(); + return; + } + + // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存 + LocalDateTime maxTime = getMaxValue(sensitiveWordCache, SensitiveWordDO::getUpdateTime); + if (sensitiveWordMapper.selectCountByUpdateTimeGt(maxTime) > 0) { + initLocalCache(); + } + } + @Override public Long createSensitiveWord(SensitiveWordCreateReqVO createReqVO) { // 校验唯一性 @@ -113,8 +138,9 @@ public class SensitiveWordServiceImpl implements SensitiveWordService { // 插入 SensitiveWordDO sensitiveWord = SensitiveWordConvert.INSTANCE.convert(createReqVO); sensitiveWordMapper.insert(sensitiveWord); - // 发送消息,刷新缓存 - sensitiveWordProducer.sendSensitiveWordRefreshMessage(); + + // 刷新缓存 + initLocalCache(); return sensitiveWord.getId(); } @@ -127,8 +153,9 @@ public class SensitiveWordServiceImpl implements SensitiveWordService { // 更新 SensitiveWordDO updateObj = SensitiveWordConvert.INSTANCE.convert(updateReqVO); sensitiveWordMapper.updateById(updateObj); - // 发送消息,刷新缓存 - sensitiveWordProducer.sendSensitiveWordRefreshMessage(); + + // 刷新缓存 + initLocalCache(); } @Override @@ -137,8 +164,9 @@ public class SensitiveWordServiceImpl implements SensitiveWordService { validateSensitiveWordExists(id); // 删除 sensitiveWordMapper.deleteById(id); - // 发送消息,刷新缓存 - sensitiveWordProducer.sendSensitiveWordRefreshMessage(); + + // 刷新缓存 + initLocalCache(); } private void validateSensitiveWordNameUnique(Long id, String name) { diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java index d6f8aa004..684287cb3 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.system.service.sms; +import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory; import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties; @@ -9,15 +10,20 @@ import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannel import cn.iocoder.yudao.module.system.convert.sms.SmsChannelConvert; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper; -import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.Collections; import java.util.List; +import java.util.concurrent.TimeUnit; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS; @@ -30,6 +36,12 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNE @Slf4j public class SmsChannelServiceImpl implements SmsChannelService { + /** + * 短信渠道列表的缓存 + */ + @Getter + private volatile List channelCache = Collections.emptyList(); + @Resource private SmsClientFactory smsClientFactory; @@ -39,9 +51,6 @@ public class SmsChannelServiceImpl implements SmsChannelService { @Resource private SmsTemplateService smsTemplateService; - @Resource - private SmsProducer smsProducer; - @Override @PostConstruct public void initLocalCache() { @@ -52,6 +61,27 @@ public class SmsChannelServiceImpl implements SmsChannelService { // 第二步:构建缓存:创建或更新短信 Client List propertiesList = SmsChannelConvert.INSTANCE.convertList02(channels); propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties)); + this.channelCache = channels; + } + + /** + * 通过定时任务轮询,刷新缓存 + * + * 目的:多节点部署时,通过轮询”通知“所有节点,进行刷新 + */ + @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS) + public void refreshLocalCache() { + // 情况一:如果缓存里没有数据,则直接刷新缓存 + if (CollUtil.isEmpty(channelCache)) { + initLocalCache(); + return; + } + + // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存 + LocalDateTime maxTime = getMaxValue(channelCache, SmsChannelDO::getUpdateTime); + if (smsChannelMapper.selectCountByUpdateTimeGt(maxTime) > 0) { + initLocalCache(); + } } @Override @@ -59,9 +89,9 @@ public class SmsChannelServiceImpl implements SmsChannelService { // 插入 SmsChannelDO smsChannel = SmsChannelConvert.INSTANCE.convert(createReqVO); smsChannelMapper.insert(smsChannel); - // 发送刷新消息 - smsProducer.sendSmsChannelRefreshMessage(); - // 返回 + + // 刷新缓存 + initLocalCache(); return smsChannel.getId(); } @@ -72,8 +102,9 @@ public class SmsChannelServiceImpl implements SmsChannelService { // 更新 SmsChannelDO updateObj = SmsChannelConvert.INSTANCE.convert(updateReqVO); smsChannelMapper.updateById(updateObj); - // 发送刷新消息 - smsProducer.sendSmsChannelRefreshMessage(); + + // 刷新缓存 + initLocalCache(); } @Override @@ -86,8 +117,9 @@ public class SmsChannelServiceImpl implements SmsChannelService { } // 删除 smsChannelMapper.deleteById(id); - // 发送刷新消息 - smsProducer.sendSmsChannelRefreshMessage(); + + // 刷新缓存 + initLocalCache(); } private void validateSmsChannelExists(Long id) { diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java index 997586c73..f3f886f55 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java @@ -8,7 +8,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; import cn.iocoder.yudao.framework.common.pojo.PageResult; import javax.validation.Valid; -import java.util.Collection; import java.util.List; import java.util.Map; @@ -16,40 +15,10 @@ import java.util.Map; * 短信模板 Service 接口 * * @author zzf - * @date 2021/1/25 9:24 + * @since 2021/1/25 9:24 */ public interface SmsTemplateService { - /** - * 初始化短信模板的本地缓存 - */ - void initLocalCache(); - - /** - * 获得短信模板,从缓存中 - * - * @param code 模板编码 - * @return 短信模板 - */ - SmsTemplateDO getSmsTemplateByCodeFromCache(String code); - - /** - * 格式化短信内容 - * - * @param content 短信模板的内容 - * @param params 内容的参数 - * @return 格式化后的内容 - */ - String formatSmsTemplateContent(String content, Map params); - - /** - * 获得短信模板 - * - * @param code 模板编码 - * @return 短信模板 - */ - SmsTemplateDO getSmsTemplateByCode(String code); - /** * 创建短信模板 * @@ -81,12 +50,12 @@ public interface SmsTemplateService { SmsTemplateDO getSmsTemplate(Long id); /** - * 获得短信模板列表 + * 获得短信模板,从缓存中 * - * @param ids 编号 - * @return 短信模板列表 + * @param code 模板编码 + * @return 短信模板 */ - List getSmsTemplateList(Collection ids); + SmsTemplateDO getSmsTemplateByCodeFromCache(String code); /** * 获得短信模板分页 @@ -112,4 +81,14 @@ public interface SmsTemplateService { */ Long countByChannelId(Long channelId); + + /** + * 格式化短信内容 + * + * @param content 短信模板的内容 + * @param params 内容的参数 + * @return 格式化后的内容 + */ + String formatSmsTemplateContent(String content, Map params); + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java index 20c4ec3f3..0f276a915 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java @@ -4,7 +4,6 @@ import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.sms.core.client.SmsClient; import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory; import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; @@ -17,16 +16,15 @@ import cn.iocoder.yudao.module.system.convert.sms.SmsTemplateConvert; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper; -import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; import com.google.common.annotations.VisibleForTesting; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.util.Assert; -import javax.annotation.PostConstruct; import javax.annotation.Resource; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; @@ -59,49 +57,6 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { @Resource private SmsClientFactory smsClientFactory; - @Resource - private SmsProducer smsProducer; - - /** - * 短信模板缓存 - * key:短信模板编码 {@link SmsTemplateDO#getCode()} - * - * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 - */ - @Getter // 为了方便测试,这里提供 getter 方法 - private volatile Map smsTemplateCache; - - @Override - @PostConstruct - public void initLocalCache() { - // 第一步:查询数据 - List smsTemplateList = smsTemplateMapper.selectList(); - log.info("[initLocalCache][缓存短信模版,数量为:{}]", smsTemplateList.size()); - - // 第二步:构建缓存 - smsTemplateCache = CollectionUtils.convertMap(smsTemplateList, SmsTemplateDO::getCode); - } - - @Override - public SmsTemplateDO getSmsTemplateByCodeFromCache(String code) { - return smsTemplateCache.get(code); - } - - @Override - public String formatSmsTemplateContent(String content, Map params) { - return StrUtil.format(content, params); - } - - @Override - public SmsTemplateDO getSmsTemplateByCode(String code) { - return smsTemplateMapper.selectByCode(code); - } - - @VisibleForTesting - public List parseTemplateContentParams(String content) { - return ReUtil.findAllGroup1(PATTERN_PARAMS, content); - } - @Override public Long createSmsTemplate(SmsTemplateCreateReqVO createReqVO) { // 校验短信渠道 @@ -116,13 +71,13 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { template.setParams(parseTemplateContentParams(template.getContent())); template.setChannelCode(channelDO.getCode()); smsTemplateMapper.insert(template); - // 发送刷新消息 - smsProducer.sendSmsTemplateRefreshMessage(); // 返回 return template.getId(); } @Override + @CacheEvict(cacheNames = RedisKeyConstants.SMS_TEMPLATE, + allEntries = true) // allEntries 清空所有缓存,因为可能修改到 code 字段,不好清理 public void updateSmsTemplate(SmsTemplateUpdateReqVO updateReqVO) { // 校验存在 validateSmsTemplateExists(updateReqVO.getId()); @@ -138,18 +93,16 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { updateObj.setParams(parseTemplateContentParams(updateObj.getContent())); updateObj.setChannelCode(channelDO.getCode()); smsTemplateMapper.updateById(updateObj); - // 发送刷新消息 - smsProducer.sendSmsTemplateRefreshMessage(); } @Override + @CacheEvict(cacheNames = RedisKeyConstants.SMS_TEMPLATE, + allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 code,不好清理 public void deleteSmsTemplate(Long id) { // 校验存在 validateSmsTemplateExists(id); // 更新 smsTemplateMapper.deleteById(id); - // 发送刷新消息 - smsProducer.sendSmsTemplateRefreshMessage(); } private void validateSmsTemplateExists(Long id) { @@ -164,8 +117,10 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { } @Override - public List getSmsTemplateList(Collection ids) { - return smsTemplateMapper.selectBatchIds(ids); + @Cacheable(cacheNames = RedisKeyConstants.SMS_TEMPLATE, key = "#code", + unless = "#result == null") + public SmsTemplateDO getSmsTemplateByCodeFromCache(String code) { + return smsTemplateMapper.selectByCode(code); } @Override @@ -217,7 +172,7 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { * @param apiTemplateId API 模板编号 */ @VisibleForTesting - public void validateApiTemplate(Long channelId, String apiTemplateId) { + void validateApiTemplate(Long channelId, String apiTemplateId) { // 获得短信模板 SmsClient smsClient = smsClientFactory.getSmsClient(channelId); Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", channelId)); @@ -226,4 +181,14 @@ public class SmsTemplateServiceImpl implements SmsTemplateService { templateResult.checkError(); } + @Override + public String formatSmsTemplateContent(String content, Map params) { + return StrUtil.format(content, params); + } + + @VisibleForTesting + List parseTemplateContentParams(String content) { + return ReUtil.findAllGroup1(PATTERN_PARAMS, content); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java index 4e6e60198..8a65ab1f0 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java @@ -10,9 +10,9 @@ import cn.iocoder.yudao.module.system.convert.tenant.TenantPackageConvert; import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantPackageMapper; +import com.baomidou.dynamic.datasource.annotation.DSTransactional; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; @@ -47,7 +47,7 @@ public class TenantPackageServiceImpl implements TenantPackageService { } @Override - @Transactional(rollbackFor = Exception.class) + @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 public void updateTenantPackage(TenantPackageUpdateReqVO updateReqVO) { // 校验存在 TenantPackageDO tenantPackage = validateTenantPackageExists(updateReqVO.getId()); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java index 1664ff6e3..226c939e4 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java @@ -29,6 +29,7 @@ import cn.iocoder.yudao.module.system.service.permission.RoleService; import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler; import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler; import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import com.baomidou.dynamic.datasource.annotation.DSTransactional; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; @@ -95,7 +96,7 @@ public class TenantServiceImpl implements TenantService { } @Override - @Transactional(rollbackFor = Exception.class) + @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 public Long createTenant(TenantCreateReqVO createReqVO) { // 校验租户名称是否重复 validTenantNameDuplicate(createReqVO.getName(), null); @@ -137,7 +138,7 @@ public class TenantServiceImpl implements TenantService { } @Override - @Transactional(rollbackFor = Exception.class) + @DSTransactional public void updateTenant(TenantUpdateReqVO updateReqVO) { // 校验存在 TenantDO tenant = validateUpdateTenant(updateReqVO.getId()); @@ -170,7 +171,7 @@ public class TenantServiceImpl implements TenantService { } @Override - @Transactional(rollbackFor = Exception.class) + @DSTransactional public void updateTenantRoleMenu(Long tenantId, Set menuIds) { TenantUtils.execute(tenantId, () -> { // 获得所有角色 @@ -186,7 +187,7 @@ public class TenantServiceImpl implements TenantService { return; } // 如果是其他角色,则去掉超过套餐的权限 - Set roleMenuIds = permissionService.getRoleMenuIds(role.getId()); + Set roleMenuIds = permissionService.getRoleMenuListByRoleId(role.getId()); roleMenuIds = CollUtil.intersectionDistinct(roleMenuIds, menuIds); permissionService.assignRoleMenu(role.getId(), roleMenuIds); log.info("[updateTenantRoleMenu][角色({}/{}) 的权限修改为({})]", role.getId(), role.getTenantId(), roleMenuIds); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java index bb43a89c2..dbfc02ed3 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java @@ -290,14 +290,13 @@ public class AdminUserServiceImpl implements AdminUserService { if (deptId == null) { return Collections.emptySet(); } - Set deptIds = convertSet(deptService.getDeptListByParentIdFromCache( - deptId, true), DeptDO::getId); + Set deptIds = convertSet(deptService.getChildDeptList(deptId), DeptDO::getId); deptIds.add(deptId); // 包括自身 return deptIds; } private void validateUserForCreateOrUpdate(Long id, String username, String mobile, String email, - Long deptId, Set postIds) { + Long deptId, Set postIds) { // 关闭数据权限,避免因为没有数据权限,查询不到数据,进而导致唯一校验不正确 DataPermissionUtils.executeIgnore(() -> { // 校验用户存在 diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenControllerTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenControllerTest.java index cfadfc747..128e7593e 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenControllerTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenControllerTest.java @@ -39,6 +39,9 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; import static java.util.Arrays.asList; +import static org.hamcrest.CoreMatchers.anyOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; @@ -289,7 +292,10 @@ public class OAuth2OpenControllerTest extends BaseMockitoUnitTest { scope, redirectUri, true, state); // 断言 assertEquals(0, result.getCode()); - assertEquals("https://www.iocoder.cn#access_token=test_access_token&token_type=bearer&state=test&expires_in=30&scope=read", result.getData()); + assertThat(result.getData(), anyOf( // 29 和 30 都有一定概率,主要是时间计算 + is("https://www.iocoder.cn#access_token=test_access_token&token_type=bearer&state=test&expires_in=29&scope=read"), + is("https://www.iocoder.cn#access_token=test_access_token&token_type=bearer&state=test&expires_in=30&scope=read") + )); } @Test // autoApprove = false,通过 + code diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java index 16e48ec4a..b0331cff2 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java @@ -148,7 +148,7 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest { ); } - @Test + @Test public void testLogin_success() { // 准备参数 AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o -> @@ -174,9 +174,9 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest { assertPojoEquals(accessTokenDO, loginRespVO); // 校验调用参数 verify(loginLogService).createLoginLog( - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) - && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()) - && o.getUserId().equals(user.getId())) + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) + && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()) + && o.getUserId().equals(user.getId())) ); verify(socialUserService).bindSocialUser(eq(new SocialUserBindReqDTO( user.getId(), UserTypeEnum.ADMIN.getValue(), @@ -317,8 +317,8 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest { assertServiceException(() -> authService.validateCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_CODE_ERROR, "就是不对"); // 校验调用参数 verify(loginLogService).createLoginLog( - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) - && o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult())) + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) + && o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult())) ); } @@ -349,8 +349,9 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest { // 调用 authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); // 校验调用参数 - verify(loginLogService).createLoginLog(argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType()) - && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())) + verify(loginLogService).createLoginLog( + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType()) + && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())) ); // 调用,并校验 diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImplTest.java index 3771fc33e..cf9dc5a35 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImplTest.java @@ -1,9 +1,7 @@ package cn.iocoder.yudao.module.system.service.dept; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; @@ -11,27 +9,20 @@ import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateRe import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper; import cn.iocoder.yudao.module.system.enums.dept.DeptIdEnum; -import cn.iocoder.yudao.module.system.mq.producer.dept.DeptProducer; -import com.google.common.collect.Multimap; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; import java.util.Arrays; import java.util.List; -import java.util.Map; -import java.util.function.Consumer; +import java.util.Set; -import static cn.hutool.core.util.RandomUtil.randomEle; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.verify; /** * {@link DeptServiceImpl} 的单元测试类 @@ -45,39 +36,163 @@ public class DeptServiceImplTest extends BaseDbUnitTest { private DeptServiceImpl deptService; @Resource private DeptMapper deptMapper; - @MockBean - private DeptProducer deptProducer; - - @BeforeEach - public void setUp() { - // 清理租户上下文 - TenantContextHolder.clear(); - } @Test - public void testInitLocalCache() { - // mock 数据 - DeptDO deptDO1 = randomDeptDO(); - deptMapper.insert(deptDO1); - DeptDO deptDO2 = randomDeptDO(); - deptMapper.insert(deptDO2); + public void testCreateDept() { + // 准备参数 + DeptCreateReqVO reqVO = randomPojo(DeptCreateReqVO.class, o -> { + o.setParentId(DeptIdEnum.ROOT.getId()); + o.setStatus(randomCommonStatus()); + }); // 调用 - deptService.initLocalCache(); - // 断言 deptCache 缓存 - Map deptCache = deptService.getDeptCache(); - assertEquals(2, deptCache.size()); - assertPojoEquals(deptDO1, deptCache.get(deptDO1.getId())); - assertPojoEquals(deptDO2, deptCache.get(deptDO2.getId())); - // 断言 parentDeptCache 缓存 - Multimap parentDeptCache = deptService.getParentDeptCache(); - assertEquals(2, parentDeptCache.size()); - assertPojoEquals(deptDO1, parentDeptCache.get(deptDO1.getParentId())); - assertPojoEquals(deptDO2, parentDeptCache.get(deptDO2.getParentId())); + Long deptId = deptService.createDept(reqVO); + // 断言 + assertNotNull(deptId); + // 校验记录的属性是否正确 + DeptDO deptDO = deptMapper.selectById(deptId); + assertPojoEquals(reqVO, deptDO); } @Test - public void testListDepts() { + public void testUpdateDept() { + // mock 数据 + DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus())); + deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 + // 准备参数 + DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class, o -> { + // 设置更新的 ID + o.setParentId(DeptIdEnum.ROOT.getId()); + o.setId(dbDeptDO.getId()); + o.setStatus(randomCommonStatus()); + }); + + // 调用 + deptService.updateDept(reqVO); + // 校验是否更新正确 + DeptDO deptDO = deptMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, deptDO); + } + + @Test + public void testDeleteDept_success() { + // mock 数据 + DeptDO dbDeptDO = randomPojo(DeptDO.class); + deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbDeptDO.getId(); + + // 调用 + deptService.deleteDept(id); + // 校验数据不存在了 + assertNull(deptMapper.selectById(id)); + } + + @Test + public void testDeleteDept_exitsChildren() { + // mock 数据 + DeptDO parentDept = randomPojo(DeptDO.class); + deptMapper.insert(parentDept);// @Sql: 先插入出一条存在的数据 + // 准备参数 + DeptDO childrenDeptDO = randomPojo(DeptDO.class, o -> { + o.setParentId(parentDept.getId()); + o.setStatus(randomCommonStatus()); + }); + // 插入子部门 + deptMapper.insert(childrenDeptDO); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.deleteDept(parentDept.getId()), DEPT_EXITS_CHILDREN); + } + + @Test + public void testValidateDeptExists_notFound() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.validateDeptExists(id), DEPT_NOT_FOUND); + } + + @Test + public void testValidateParentDept_parentError() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.validateParentDept(id, id), + DEPT_PARENT_ERROR); + } + + @Test + public void testValidateParentDept_parentIsChild() { + // mock 数据(父节点) + DeptDO parentDept = randomPojo(DeptDO.class); + deptMapper.insert(parentDept); + // mock 数据(子节点) + DeptDO childDept = randomPojo(DeptDO.class, o -> { + o.setParentId(parentDept.getId()); + }); + deptMapper.insert(childDept); + + // 准备参数 + Long id = parentDept.getId(); + Long parentId = childDept.getId(); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.validateParentDept(id, parentId), DEPT_PARENT_IS_CHILD); + } + + @Test + public void testValidateNameUnique_duplicate() { + // mock 数据 + DeptDO deptDO = randomPojo(DeptDO.class); + deptMapper.insert(deptDO); + + // 准备参数 + Long id = randomLongId(); + Long parentId = deptDO.getParentId(); + String name = deptDO.getName(); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.validateDeptNameUnique(id, parentId, name), + DEPT_NAME_DUPLICATE); + } + + @Test + public void testGetDept() { + // mock 数据 + DeptDO deptDO = randomPojo(DeptDO.class); + deptMapper.insert(deptDO); + // 准备参数 + Long id = deptDO.getId(); + + // 调用 + DeptDO dbDept = deptService.getDept(id); + // 断言 + assertEquals(deptDO, dbDept); + } + + @Test + public void testGetDeptList_ids() { + // mock 数据 + DeptDO deptDO01 = randomPojo(DeptDO.class); + deptMapper.insert(deptDO01); + DeptDO deptDO02 = randomPojo(DeptDO.class); + deptMapper.insert(deptDO02); + // 准备参数 + List ids = Arrays.asList(deptDO01.getId(), deptDO02.getId()); + + // 调用 + List deptDOList = deptService.getDeptList(ids); + // 断言 + assertEquals(2, deptDOList.size()); + assertEquals(deptDO01, deptDOList.get(0)); + assertEquals(deptDO02, deptDOList.get(1)); + } + + @Test + public void testGetDeptList_reqVO() { // mock 数据 DeptDO dept = randomPojo(DeptDO.class, o -> { // 等会查询到 o.setName("开发部"); @@ -101,216 +216,55 @@ public class DeptServiceImplTest extends BaseDbUnitTest { } @Test - public void testCreateDept_success() { + public void testGetChildDeptList() { + // mock 数据(1 级别子节点) + DeptDO dept1 = randomPojo(DeptDO.class, o -> o.setName("1")); + deptMapper.insert(dept1); + DeptDO dept2 = randomPojo(DeptDO.class, o -> o.setName("2")); + deptMapper.insert(dept2); + // mock 数据(2 级子节点) + DeptDO dept1a = randomPojo(DeptDO.class, o -> o.setName("1-a").setParentId(dept1.getId())); + deptMapper.insert(dept1a); + DeptDO dept2a = randomPojo(DeptDO.class, o -> o.setName("2-a").setParentId(dept2.getId())); + deptMapper.insert(dept2a); // 准备参数 - DeptCreateReqVO reqVO = randomPojo(DeptCreateReqVO.class, o -> { - o.setParentId(DeptIdEnum.ROOT.getId()); - o.setStatus(randomCommonStatus()); - }); + Long id = dept1.getParentId(); // 调用 - Long deptId = deptService.createDept(reqVO); + List result = deptService.getChildDeptList(id); // 断言 - assertNotNull(deptId); - // 校验记录的属性是否正确 - DeptDO deptDO = deptMapper.selectById(deptId); - assertPojoEquals(reqVO, deptDO); - // 校验调用 - verify(deptProducer).sendDeptRefreshMessage(); + assertEquals(result.size(), 2); + assertPojoEquals(dept1, result.get(0)); + assertPojoEquals(dept1a, result.get(1)); } @Test - public void testUpdateDept_success() { - // mock 数据 - DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus())); - deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 + public void testGetChildDeptListFromCache() { + // mock 数据(1 级别子节点) + DeptDO dept1 = randomPojo(DeptDO.class, o -> o.setName("1")); + deptMapper.insert(dept1); + DeptDO dept2 = randomPojo(DeptDO.class, o -> o.setName("2")); + deptMapper.insert(dept2); + // mock 数据(2 级子节点) + DeptDO dept1a = randomPojo(DeptDO.class, o -> o.setName("1-a").setParentId(dept1.getId())); + deptMapper.insert(dept1a); + DeptDO dept2a = randomPojo(DeptDO.class, o -> o.setName("2-a").setParentId(dept2.getId())); + deptMapper.insert(dept2a); // 准备参数 - DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class, o -> { - // 设置更新的 ID - o.setParentId(DeptIdEnum.ROOT.getId()); - o.setId(dbDeptDO.getId()); - o.setStatus(randomCommonStatus()); - }); + Long id = dept1.getParentId(); // 调用 - deptService.updateDept(reqVO); - // 校验是否更新正确 - DeptDO deptDO = deptMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, deptDO); - // 校验调用 - verify(deptProducer).sendDeptRefreshMessage(); - } - - @Test - public void testDeleteDept_success() { - // mock 数据 - DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus())); - deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbDeptDO.getId(); - - // 调用 - deptService.deleteDept(id); - // 校验数据不存在了 - assertNull(deptMapper.selectById(id)); - // 校验调用 - verify(deptProducer).sendDeptRefreshMessage(); - } - - @Test - public void testValidateDept_nameDuplicateForUpdate() { - // mock 数据 - DeptDO deptDO = randomDeptDO(); - // 设置根节点部门 - deptDO.setParentId(DeptIdEnum.ROOT.getId()); - deptMapper.insert(deptDO); - // mock 数据 稍后模拟重复它的 name - DeptDO nameDeptDO = randomDeptDO(); - // 设置根节点部门 - nameDeptDO.setParentId(DeptIdEnum.ROOT.getId()); - deptMapper.insert(nameDeptDO); - // 准备参数 - DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class, o -> { - // 设置根节点部门 - o.setParentId(DeptIdEnum.ROOT.getId()); - // 设置更新的 ID - o.setId(deptDO.getId()); - // 模拟 name 重复 - o.setName(nameDeptDO.getName()); - }); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.updateDept(reqVO), DEPT_NAME_DUPLICATE); - } - - @Test - public void testValidateDept_parentNotExitsForCreate() { - // 准备参数 - DeptCreateReqVO reqVO = randomPojo(DeptCreateReqVO.class, - o -> o.setStatus(randomCommonStatus())); - - // 调用,并断言异常 - assertServiceException(() -> deptService.createDept(reqVO), DEPT_PARENT_NOT_EXITS); - } - - @Test - public void testValidateDept_notFoundForDelete() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.deleteDept(id), DEPT_NOT_FOUND); - } - - @Test - public void testValidateDept_exitsChildrenForDelete() { - // mock 数据 - DeptDO parentDept = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus())); - deptMapper.insert(parentDept);// @Sql: 先插入出一条存在的数据 - // 准备参数 - DeptDO childrenDeptDO = randomPojo(DeptDO.class, o -> { - o.setParentId(parentDept.getId()); - o.setStatus(randomCommonStatus()); - }); - // 插入子部门 - deptMapper.insert(childrenDeptDO); - // 调用, 并断言异常 - assertServiceException(() -> deptService.deleteDept(parentDept.getId()), DEPT_EXITS_CHILDREN); - } - - @Test - public void testValidateDept_parentErrorForUpdate() { - // mock 数据 - DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus())); - deptMapper.insert(dbDeptDO); - // 准备参数 - DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class, o -> { - // 设置自己为父部门 - o.setParentId(dbDeptDO.getId()); - // 设置更新的 ID - o.setId(dbDeptDO.getId()); - }); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.updateDept(reqVO), DEPT_PARENT_ERROR); - } - - @Test - public void testValidateDept_notEnableForCreate() { - // mock 数据 - DeptDO deptDO = randomPojo(DeptDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); - deptMapper.insert(deptDO); - // 准备参数 - DeptCreateReqVO reqVO = randomPojo(DeptCreateReqVO.class, o -> { - // 设置未启用的部门为父部门 - o.setParentId(deptDO.getId()); - }); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.createDept(reqVO), DEPT_NOT_ENABLE); - } - - @Test - public void testCheckDept_parentIsChildForUpdate() { - // mock 数据 - DeptDO parentDept = randomPojo(DeptDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - deptMapper.insert(parentDept); - DeptDO childDept = randomPojo(DeptDO.class, o -> { - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setParentId(parentDept.getId()); - }); - deptMapper.insert(childDept); - // 初始化本地缓存 - deptService.initLocalCache(); - - // 准备参数 - DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class, o -> { - // 设置自己的子部门为父部门 - o.setParentId(childDept.getId()); - // 设置更新的 ID - o.setId(parentDept.getId()); - }); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.updateDept(reqVO), DEPT_PARENT_IS_CHILD); - } - - @Test - public void testGetDeptList() { - // mock 数据 - DeptDO deptDO01 = randomDeptDO(); - deptMapper.insert(deptDO01); - DeptDO deptDO02 = randomDeptDO(); - deptMapper.insert(deptDO02); - // 准备参数 - List ids = Arrays.asList(deptDO01.getId(), deptDO02.getId()); - - // 调用 - List deptDOList = deptService.getDeptList(ids); + Set result = deptService.getChildDeptIdListFromCache(id); // 断言 - assertEquals(2, deptDOList.size()); - assertEquals(deptDO01, deptDOList.get(0)); - assertEquals(deptDO02, deptDOList.get(1)); - } - - @Test - public void testGetDept() { - // mock 数据 - DeptDO deptDO = randomDeptDO(); - deptMapper.insert(deptDO); - // 准备参数 - Long id = deptDO.getId(); - - // 调用 - DeptDO dbDept = deptService.getDept(id); - // 断言 - assertEquals(deptDO, dbDept); + assertEquals(result.size(), 2); + assertTrue(result.contains(dept1.getId())); + assertTrue(result.contains(dept1a.getId())); } @Test public void testValidateDeptList_success() { // mock 数据 - DeptDO deptDO = randomDeptDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); + DeptDO deptDO = randomPojo(DeptDO.class).setStatus(CommonStatusEnum.ENABLE.getStatus()); deptMapper.insert(deptDO); // 准备参数 List ids = singletonList(deptDO.getId()); @@ -331,7 +285,7 @@ public class DeptServiceImplTest extends BaseDbUnitTest { @Test public void testValidateDeptList_notEnable() { // mock 数据 - DeptDO deptDO = randomDeptDO().setStatus(CommonStatusEnum.DISABLE.getStatus()); + DeptDO deptDO = randomPojo(DeptDO.class).setStatus(CommonStatusEnum.DISABLE.getStatus()); deptMapper.insert(deptDO); // 准备参数 List ids = singletonList(deptDO.getId()); @@ -340,12 +294,4 @@ public class DeptServiceImplTest extends BaseDbUnitTest { assertServiceException(() -> deptService.validateDeptList(ids), DEPT_NOT_ENABLE, deptDO.getName()); } - @SafeVarargs - private static DeptDO randomDeptDO(Consumer... consumers) { - Consumer consumer = (o) -> { - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 - }; - return randomPojo(DeptDO.class, ArrayUtils.append(consumer, consumers)); - } - } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java index a0e41f75c..ea2725e25 100755 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java @@ -7,14 +7,12 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccou import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper; -import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; import java.util.List; -import java.util.Map; import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; @@ -23,14 +21,13 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_NOT_EXISTS; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** -* {@link MailAccountServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ + * {@link MailAccountServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ @Import(MailAccountServiceImpl.class) public class MailAccountServiceImplTest extends BaseDbUnitTest { @@ -42,23 +39,6 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { @MockBean private MailTemplateService mailTemplateService; - @MockBean - private MailProducer mailProducer; - - @Test - public void testInitLocalCache() { - MailAccountDO accountDO1 = randomPojo(MailAccountDO.class); - mailAccountMapper.insert(accountDO1); - MailAccountDO accountDO02 = randomPojo(MailAccountDO.class); - mailAccountMapper.insert(accountDO02); - - // 调用 - mailAccountService.initLocalCache(); - // 断言 mailAccountCache 缓存 - Map mailAccountCache = mailAccountService.getMailAccountCache(); - assertPojoEquals(accountDO1, mailAccountCache.get(accountDO1.getId())); - assertPojoEquals(accountDO02, mailAccountCache.get(accountDO02.getId())); - } @Test public void testCreateMailAccount_success() { @@ -72,7 +52,6 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { // 校验记录的属性是否正确 MailAccountDO mailAccount = mailAccountMapper.selectById(mailAccountId); assertPojoEquals(reqVO, mailAccount); - verify(mailProducer).sendMailAccountRefreshMessage(); } @Test @@ -91,7 +70,6 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { // 校验是否更新正确 MailAccountDO mailAccount = mailAccountMapper.selectById(reqVO.getId()); // 获取最新的 assertPojoEquals(reqVO, mailAccount); - verify(mailProducer).sendMailAccountRefreshMessage(); } @Test @@ -115,9 +93,8 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { // 调用 mailAccountService.deleteMailAccount(id); - // 校验数据不存在了 - assertNull(mailAccountMapper.selectById(id)); - verify(mailProducer).sendMailAccountRefreshMessage(); + // 校验数据不存在了 + assertNull(mailAccountMapper.selectById(id)); } @Test @@ -125,7 +102,6 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { // mock 数据 MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class); mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 - mailAccountService.initLocalCache(); // 准备参数 Long id = dbMailAccount.getId(); @@ -146,27 +122,27 @@ public class MailAccountServiceImplTest extends BaseDbUnitTest { @Test public void testGetMailAccountPage() { - // mock 数据 - MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class, o -> { // 等会查询到 - o.setMail("768@qq.com"); - o.setUsername("yunai"); - }); - mailAccountMapper.insert(dbMailAccount); - // 测试 mail 不匹配 - mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setMail("788@qq.com"))); - // 测试 username 不匹配 - mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setUsername("tudou"))); - // 准备参数 - MailAccountPageReqVO reqVO = new MailAccountPageReqVO(); - reqVO.setMail("768"); - reqVO.setUsername("yu"); + // mock 数据 + MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class, o -> { // 等会查询到 + o.setMail("768@qq.com"); + o.setUsername("yunai"); + }); + mailAccountMapper.insert(dbMailAccount); + // 测试 mail 不匹配 + mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setMail("788@qq.com"))); + // 测试 username 不匹配 + mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setUsername("tudou"))); + // 准备参数 + MailAccountPageReqVO reqVO = new MailAccountPageReqVO(); + reqVO.setMail("768"); + reqVO.setUsername("yu"); - // 调用 - PageResult pageResult = mailAccountService.getMailAccountPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbMailAccount, pageResult.getList().get(0)); + // 调用 + PageResult pageResult = mailAccountService.getMailAccountPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbMailAccount, pageResult.getList().get(0)); } @Test diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java index 9c2a4806e..152c6d862 100755 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java @@ -25,10 +25,10 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; import static org.junit.jupiter.api.Assertions.*; /** -* {@link MailLogServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ + * {@link MailLogServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ @Import(MailLogServiceImpl.class) public class MailLogServiceImplTest extends BaseDbUnitTest { diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java index 265798b27..56590dbdc 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java @@ -33,7 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; -class MailSendServiceImplTest extends BaseMockitoUnitTest { +public class MailSendServiceImplTest extends BaseMockitoUnitTest { @InjectMocks private MailSendServiceImpl mailSendService; @@ -278,16 +278,17 @@ class MailSendServiceImplTest extends BaseMockitoUnitTest { // mock 方法(发送邮件) String messageId = randomString(); - mailUtilMock.when(() -> MailUtil.send(argThat(mailAccount -> { - assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom()); - assertTrue(mailAccount.isAuth()); - assertEquals(account.getUsername(), mailAccount.getUser()); - assertEquals(account.getPassword(), mailAccount.getPass()); - assertEquals(account.getHost(), mailAccount.getHost()); - assertEquals(account.getPort(), mailAccount.getPort()); - assertEquals(account.getSslEnable(), mailAccount.isSslEnable()); - return true; - }), eq(message.getMail()), eq(message.getTitle()), eq(message.getContent()), eq(true))) + mailUtilMock.when(() -> MailUtil.send( + argThat(mailAccount -> { + assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom()); + assertTrue(mailAccount.isAuth()); + assertEquals(account.getUsername(), mailAccount.getUser()); + assertEquals(account.getPassword(), mailAccount.getPass()); + assertEquals(account.getHost(), mailAccount.getHost()); + assertEquals(account.getPort(), mailAccount.getPort()); + assertEquals(account.getSslEnable(), mailAccount.isSslEnable()); + return true; + }), eq(message.getMail()), eq(message.getTitle()), eq(message.getContent()), eq(true))) .thenReturn(messageId); // 调用 diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java index 83f23e92e..8776595ea 100755 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java @@ -8,9 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemp import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper; -import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer; import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; @@ -27,13 +25,12 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.verify; /** -* {@link MailTemplateServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ + * {@link MailTemplateServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ @Import(MailTemplateServiceImpl.class) public class MailTemplateServiceImplTest extends BaseDbUnitTest { @@ -43,24 +40,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @Resource private MailTemplateMapper mailTemplateMapper; - @MockBean - private MailProducer mailProducer; - - @Test - public void testInitLocalCache() { - MailTemplateDO templateDO01 = randomPojo(MailTemplateDO.class); - mailTemplateMapper.insert(templateDO01); - MailTemplateDO templateDO02 = randomPojo(MailTemplateDO.class); - mailTemplateMapper.insert(templateDO02); - - // 调用 - mailTemplateService.initLocalCache(); - // 断言 mailTemplateCache 缓存 - Map mailTemplateCache = mailTemplateService.getMailTemplateCache(); - assertPojoEquals(templateDO01, mailTemplateCache.get(templateDO01.getCode())); - assertPojoEquals(templateDO02, mailTemplateCache.get(templateDO02.getCode())); - } - @Test public void testCreateMailTemplate_success() { // 准备参数 @@ -73,7 +52,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { // 校验记录的属性是否正确 MailTemplateDO mailTemplate = mailTemplateMapper.selectById(mailTemplateId); assertPojoEquals(reqVO, mailTemplate); - verify(mailProducer).sendMailTemplateRefreshMessage(); } @Test @@ -91,7 +69,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { // 校验是否更新正确 MailTemplateDO mailTemplate = mailTemplateMapper.selectById(reqVO.getId()); // 获取最新的 assertPojoEquals(reqVO, mailTemplate); - verify(mailProducer).sendMailTemplateRefreshMessage(); } @Test @@ -115,7 +92,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { mailTemplateService.deleteMailTemplate(id); // 校验数据不存在了 assertNull(mailTemplateMapper.selectById(id)); - verify(mailProducer).sendMailTemplateRefreshMessage(); } @Test @@ -129,39 +105,39 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @Test public void testGetMailTemplatePage() { - // mock 数据 - MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class, o -> { // 等会查询到 - o.setName("源码"); - o.setCode("test_01"); - o.setAccountId(1L); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCreateTime(buildTime(2023, 2, 3)); - }); - mailTemplateMapper.insert(dbMailTemplate); - // 测试 name 不匹配 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setName("芋道"))); - // 测试 code 不匹配 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCode("test_02"))); - // 测试 accountId 不匹配 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setAccountId(2L))); - // 测试 status 不匹配 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 createTime 不匹配 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCreateTime(buildTime(2023, 1, 5)))); - // 准备参数 - MailTemplatePageReqVO reqVO = new MailTemplatePageReqVO(); - reqVO.setName("源"); - reqVO.setCode("est_01"); - reqVO.setAccountId(1L); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 5)); + // mock 数据 + MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class, o -> { // 等会查询到 + o.setName("源码"); + o.setCode("test_01"); + o.setAccountId(1L); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2023, 2, 3)); + }); + mailTemplateMapper.insert(dbMailTemplate); + // 测试 name 不匹配 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setName("芋道"))); + // 测试 code 不匹配 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCode("test_02"))); + // 测试 accountId 不匹配 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setAccountId(2L))); + // 测试 status 不匹配 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime 不匹配 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCreateTime(buildTime(2023, 1, 5)))); + // 准备参数 + MailTemplatePageReqVO reqVO = new MailTemplatePageReqVO(); + reqVO.setName("源"); + reqVO.setCode("est_01"); + reqVO.setAccountId(1L); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 5)); - // 调用 - PageResult pageResult = mailTemplateService.getMailTemplatePage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbMailTemplate, pageResult.getList().get(0)); + // 调用 + PageResult pageResult = mailTemplateService.getMailTemplatePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbMailTemplate, pageResult.getList().get(0)); } @Test @@ -199,7 +175,6 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { // mock 数据 MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); mailTemplateMapper.insert(dbMailTemplate); - mailTemplateService.initLocalCache(); // 准备参数 String code = dbMailTemplate.getCode(); diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java index 28f6f9a38..04c410fb7 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java @@ -8,9 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.Notify import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper; -import cn.iocoder.yudao.module.system.mq.producer.notify.NotifyProducer; import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; @@ -25,13 +23,12 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.verify; /** -* {@link NotifyTemplateServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ + * {@link NotifyTemplateServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ @Import(NotifyTemplateServiceImpl.class) public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { @@ -41,9 +38,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { @Resource private NotifyTemplateMapper notifyTemplateMapper; - @MockBean - private NotifyProducer notifyProducer; - @Test public void testCreateNotifyTemplate_success() { // 准备参数 @@ -57,7 +51,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { // 校验记录的属性是否正确 NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(notifyTemplateId); assertPojoEquals(reqVO, notifyTemplate); - verify(notifyProducer).sendNotifyTemplateRefreshMessage(); } @Test @@ -76,7 +69,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { // 校验是否更新正确 NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(reqVO.getId()); // 获取最新的 assertPojoEquals(reqVO, notifyTemplate); - verify(notifyProducer).sendNotifyTemplateRefreshMessage(); } @Test @@ -98,9 +90,8 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { // 调用 notifyTemplateService.deleteNotifyTemplate(id); - // 校验数据不存在了 - assertNull(notifyTemplateMapper.selectById(id)); - verify(notifyProducer).sendNotifyTemplateRefreshMessage(); + // 校验数据不存在了 + assertNull(notifyTemplateMapper.selectById(id)); } @Test @@ -114,35 +105,35 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { @Test public void testGetNotifyTemplatePage() { - // mock 数据 - NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class, o -> { // 等会查询到 - o.setName("芋头"); - o.setCode("test_01"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCreateTime(buildTime(2022, 2, 3)); - }); - notifyTemplateMapper.insert(dbNotifyTemplate); - // 测试 name 不匹配 - notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setName("投"))); - // 测试 code 不匹配 - notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCode("test_02"))); - // 测试 status 不匹配 - notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 createTime 不匹配 - notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCreateTime(buildTime(2022, 1, 5)))); - // 准备参数 - NotifyTemplatePageReqVO reqVO = new NotifyTemplatePageReqVO(); - reqVO.setName("芋"); - reqVO.setCode("est_01"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 5)); + // mock 数据 + NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class, o -> { // 等会查询到 + o.setName("芋头"); + o.setCode("test_01"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2022, 2, 3)); + }); + notifyTemplateMapper.insert(dbNotifyTemplate); + // 测试 name 不匹配 + notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setName("投"))); + // 测试 code 不匹配 + notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCode("test_02"))); + // 测试 status 不匹配 + notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime 不匹配 + notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCreateTime(buildTime(2022, 1, 5)))); + // 准备参数 + NotifyTemplatePageReqVO reqVO = new NotifyTemplatePageReqVO(); + reqVO.setName("芋"); + reqVO.setCode("est_01"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 5)); - // 调用 - PageResult pageResult = notifyTemplateService.getNotifyTemplatePage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbNotifyTemplate, pageResult.getList().get(0)); + // 调用 + PageResult pageResult = notifyTemplateService.getNotifyTemplatePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbNotifyTemplate, pageResult.getList().get(0)); } @Test @@ -164,7 +155,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { // mock 数据 NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class); notifyTemplateMapper.insert(dbNotifyTemplate); - notifyTemplateService.initLocalCache(); // 准备参数 String code = dbNotifyTemplate.getCode(); @@ -173,7 +163,7 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { // 断言 assertPojoEquals(dbNotifyTemplate, notifyTemplate); } - + @Test public void testFormatNotifyTemplateContent() { // 准备参数 diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImplTest.java index 3a671b0b9..25b31220c 100755 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImplTest.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.system.service.oauth2; -import cn.hutool.core.map.MapUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; @@ -9,14 +9,12 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2Cl import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper; -import cn.iocoder.yudao.module.system.mq.producer.auth.OAuth2ClientProducer; import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.mockito.MockedStatic; import org.springframework.context.annotation.Import; import javax.annotation.Resource; import java.util.Collections; -import java.util.Map; import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; @@ -24,13 +22,14 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.verify; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mockStatic; /** -* {@link OAuth2ClientServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ + * {@link OAuth2ClientServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ @Import(OAuth2ClientServiceImpl.class) public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { @@ -40,26 +39,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { @Resource private OAuth2ClientMapper oauth2ClientMapper; - @MockBean - private OAuth2ClientProducer oauth2ClientProducer; - - @Test - public void testInitLocalCache() { - // mock 数据 - OAuth2ClientDO clientDO1 = randomPojo(OAuth2ClientDO.class); - oauth2ClientMapper.insert(clientDO1); - OAuth2ClientDO clientDO2 = randomPojo(OAuth2ClientDO.class); - oauth2ClientMapper.insert(clientDO2); - - // 调用 - oauth2ClientService.initLocalCache(); - // 断言 clientCache 缓存 - Map clientCache = oauth2ClientService.getClientCache(); - assertEquals(2, clientCache.size()); - assertPojoEquals(clientDO1, clientCache.get(clientDO1.getClientId())); - assertPojoEquals(clientDO2, clientCache.get(clientDO2.getClientId())); - } - @Test public void testCreateOAuth2Client_success() { // 准备参数 @@ -73,7 +52,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { // 校验记录的属性是否正确 OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(oauth2ClientId); assertPojoEquals(reqVO, oAuth2Client); - verify(oauth2ClientProducer).sendOAuth2ClientRefreshMessage(); } @Test @@ -92,7 +70,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { // 校验是否更新正确 OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(reqVO.getId()); // 获取最新的 assertPojoEquals(reqVO, oAuth2Client); - verify(oauth2ClientProducer).sendOAuth2ClientRefreshMessage(); } @Test @@ -116,7 +93,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { oauth2ClientService.deleteOAuth2Client(id); // 校验数据不存在了 assertNull(oauth2ClientMapper.selectById(id)); - verify(oauth2ClientProducer).sendOAuth2ClientRefreshMessage(); } @Test @@ -167,62 +143,78 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { } @Test - public void testGetOAuth2ClientPage() { - // mock 数据 - OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class, o -> { // 等会查询到 - o.setName("潜龙"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - }); - oauth2ClientMapper.insert(dbOAuth2Client); - // 测试 name 不匹配 - oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setName("凤凰"))); - // 测试 status 不匹配 - oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 准备参数 - OAuth2ClientPageReqVO reqVO = new OAuth2ClientPageReqVO(); - reqVO.setName("龙"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + public void testGetOAuth2ClientFromCache() { + // mock 数据 + OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class); + oauth2ClientMapper.insert(clientDO); + // 准备参数 + String clientId = clientDO.getClientId(); - // 调用 - PageResult pageResult = oauth2ClientService.getOAuth2ClientPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbOAuth2Client, pageResult.getList().get(0)); + // 调用,并断言 + OAuth2ClientDO dbClientDO = oauth2ClientService.getOAuth2ClientFromCache(clientId); + assertPojoEquals(clientDO, dbClientDO); + } + + @Test + public void testGetOAuth2ClientPage() { + // mock 数据 + OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class, o -> { // 等会查询到 + o.setName("潜龙"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + oauth2ClientMapper.insert(dbOAuth2Client); + // 测试 name 不匹配 + oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setName("凤凰"))); + // 测试 status 不匹配 + oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 准备参数 + OAuth2ClientPageReqVO reqVO = new OAuth2ClientPageReqVO(); + reqVO.setName("龙"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + + // 调用 + PageResult pageResult = oauth2ClientService.getOAuth2ClientPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbOAuth2Client, pageResult.getList().get(0)); } @Test public void testValidOAuthClientFromCache() { - // mock 方法 - OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("default") - .setStatus(CommonStatusEnum.ENABLE.getStatus()); - OAuth2ClientDO client02 = randomPojo(OAuth2ClientDO.class).setClientId("disable") - .setStatus(CommonStatusEnum.DISABLE.getStatus()); - Map clientCache = MapUtil.builder() - .put(client.getClientId(), client) - .put(client02.getClientId(), client02).build(); - oauth2ClientService.setClientCache(clientCache); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(OAuth2ClientServiceImpl.class))) + .thenReturn(oauth2ClientService); - // 调用,并断言 - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache(randomString(), - null, null, null, null), OAUTH2_CLIENT_NOT_EXISTS); - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("disable", - null, null, null, null), OAUTH2_CLIENT_DISABLE); - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", - randomString(), null, null, null), OAUTH2_CLIENT_CLIENT_SECRET_ERROR); - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", - null, randomString(), null, null), OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS); - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", - null, null, Collections.singleton(randomString()), null), OAUTH2_CLIENT_SCOPE_OVER); - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", - null, null, null, "test"), OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH, "test"); - // 成功调用(1:参数完整) - OAuth2ClientDO result = oauth2ClientService.validOAuthClientFromCache(client.getClientId(), client.getSecret(), - client.getAuthorizedGrantTypes().get(0), client.getScopes(), client.getRedirectUris().get(0)); - assertPojoEquals(client, result); - // 成功调用(2:只有 clientId 参数) - result = oauth2ClientService.validOAuthClientFromCache(client.getClientId()); - assertPojoEquals(client, result); + // mock 方法 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("default") + .setStatus(CommonStatusEnum.ENABLE.getStatus()); + oauth2ClientMapper.insert(client); + OAuth2ClientDO client02 = randomPojo(OAuth2ClientDO.class).setClientId("disable") + .setStatus(CommonStatusEnum.DISABLE.getStatus()); + oauth2ClientMapper.insert(client02); + + // 调用,并断言 + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache(randomString(), + null, null, null, null), OAUTH2_CLIENT_NOT_EXISTS); + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("disable", + null, null, null, null), OAUTH2_CLIENT_DISABLE); + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", + randomString(), null, null, null), OAUTH2_CLIENT_CLIENT_SECRET_ERROR); + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", + null, randomString(), null, null), OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS); + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", + null, null, Collections.singleton(randomString()), null), OAUTH2_CLIENT_SCOPE_OVER); + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", + null, null, null, "test"), OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH, "test"); + // 成功调用(1:参数完整) + OAuth2ClientDO result = oauth2ClientService.validOAuthClientFromCache(client.getClientId(), client.getSecret(), + client.getAuthorizedGrantTypes().get(0), client.getScopes(), client.getRedirectUris().get(0)); + assertPojoEquals(client, result); + // 成功调用(2:只有 clientId 参数) + result = oauth2ClientService.validOAuthClientFromCache(client.getClientId()); + assertPojoEquals(client, result); + } } } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java index d643c5501..a0c17b3fb 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java @@ -8,10 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuUp import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; import cn.iocoder.yudao.module.system.dal.mysql.permission.MenuMapper; import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; -import cn.iocoder.yudao.module.system.mq.producer.permission.MenuProducer; import cn.iocoder.yudao.module.system.service.tenant.TenantService; -import com.google.common.collect.LinkedListMultimap; -import com.google.common.collect.Multimap; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; @@ -26,8 +23,6 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.argThat; @@ -46,35 +41,12 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @MockBean private PermissionService permissionService; @MockBean - private MenuProducer menuProducer; - @MockBean private TenantService tenantService; - @Test - public void testInitLocalCache_success() { - MenuDO menuDO1 = randomPojo(MenuDO.class); - menuMapper.insert(menuDO1); - MenuDO menuDO2 = randomPojo(MenuDO.class); - menuMapper.insert(menuDO2); - - // 调用 - menuService.initLocalCache(); - // 校验 menuCache 缓存 - Map menuCache = menuService.getMenuCache(); - assertEquals(2, menuCache.size()); - assertPojoEquals(menuDO1, menuCache.get(menuDO1.getId())); - assertPojoEquals(menuDO2, menuCache.get(menuDO2.getId())); - // 校验 permissionMenuCache 缓存 - Multimap permissionMenuCache = menuService.getPermissionMenuCache(); - assertEquals(2, permissionMenuCache.size()); - assertPojoEquals(menuDO1, permissionMenuCache.get(menuDO1.getPermission())); - assertPojoEquals(menuDO2, permissionMenuCache.get(menuDO2.getPermission())); - } - @Test public void testCreateMenu_success() { // mock 数据(构造父菜单) - MenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, + MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", 0L); menuMapper.insert(menuDO); Long parentId = menuDO.getId(); @@ -89,14 +61,12 @@ public class MenuServiceImplTest extends BaseDbUnitTest { // 校验记录的属性是否正确 MenuDO dbMenu = menuMapper.selectById(menuId); assertPojoEquals(reqVO, dbMenu); - // 校验调用 - verify(menuProducer).sendMenuRefreshMessage(); } @Test public void testUpdateMenu_success() { // mock 数据(构造父子菜单) - MenuDO sonMenuDO = initParentAndSonMenu(); + MenuDO sonMenuDO = createParentAndSonMenu(); Long sonId = sonMenuDO.getId(); // 准备参数 MenuUpdateReqVO reqVO = randomPojo(MenuUpdateReqVO.class, o -> { @@ -111,8 +81,6 @@ public class MenuServiceImplTest extends BaseDbUnitTest { // 校验记录的属性是否正确 MenuDO dbMenu = menuMapper.selectById(sonId); assertPojoEquals(reqVO, dbMenu); - // 校验调用 - verify(menuProducer).sendMenuRefreshMessage(); } @Test @@ -137,7 +105,6 @@ public class MenuServiceImplTest extends BaseDbUnitTest { MenuDO dbMenuDO = menuMapper.selectById(id); assertNull(dbMenuDO); verify(permissionService).processMenuDeleted(id); - verify(menuProducer).sendMenuRefreshMessage(); } @Test @@ -149,7 +116,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @Test public void testDeleteMenu_existChildren() { // mock 数据(构造父子菜单) - MenuDO sonMenu = initParentAndSonMenu(); + MenuDO sonMenu = createParentAndSonMenu(); // 准备参数 Long parentId = sonMenu.getParentId(); @@ -218,85 +185,6 @@ public class MenuServiceImplTest extends BaseDbUnitTest { assertPojoEquals(menu100, result.get(0)); } - @Test - public void testListMenusFromCache_withoutId() { - // mock 缓存 - Map menuCache = new HashMap<>(); - // 可被匹配 - MenuDO menuDO = randomPojo(MenuDO.class, o -> o.setId(1L) - .setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus())); - menuCache.put(menuDO.getId(), menuDO); - // 测试 type 不匹配 - menuCache.put(3L, randomPojo(MenuDO.class, o -> o.setId(3L) - .setType(MenuTypeEnum.BUTTON.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus()))); - // 测试 status 不匹配 - menuCache.put(4L, randomPojo(MenuDO.class, o -> o.setId(4L) - .setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.DISABLE.getStatus()))); - menuService.setMenuCache(menuCache); - // 准备参数 - Collection menuTypes = singletonList(MenuTypeEnum.MENU.getType()); - Collection menusStatuses = singletonList(CommonStatusEnum.ENABLE.getStatus()); - - // 调用 - List list = menuService.getMenuListFromCache(menuTypes, menusStatuses); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(menuDO, list.get(0)); - } - - @Test - public void testListMenusFromCache_withId() { - // mock 缓存 - Map menuCache = new HashMap<>(); - // 可被匹配 - MenuDO menuDO = randomPojo(MenuDO.class, o -> o.setId(1L) - .setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus())); - menuCache.put(menuDO.getId(), menuDO); - // 测试 id 不匹配 - menuCache.put(2L, randomPojo(MenuDO.class, o -> o.setId(2L) - .setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus()))); - // 测试 type 不匹配 - menuCache.put(3L, randomPojo(MenuDO.class, o -> o.setId(3L) - .setType(MenuTypeEnum.BUTTON.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus()))); - // 测试 status 不匹配 - menuCache.put(4L, randomPojo(MenuDO.class, o -> o.setId(4L) - .setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.DISABLE.getStatus()))); - menuService.setMenuCache(menuCache); - // 准备参数 - Collection menuIds = asList(1L, 3L, 4L); - Collection menuTypes = singletonList(MenuTypeEnum.MENU.getType()); - Collection menusStatuses = singletonList(CommonStatusEnum.ENABLE.getStatus()); - - // 调用 - List list = menuService.getMenuListFromCache(menuIds, menuTypes, menusStatuses); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(menuDO, list.get(0)); - } - - @Test - public void testGetMenuListByPermissionFromCache() { - // mock 缓存 - Multimap permissionMenuCache = LinkedListMultimap.create(); - // 可被匹配 - MenuDO menuDO01 = randomPojo(MenuDO.class, o -> o.setId(1L).setPermission("123")); - permissionMenuCache.put(menuDO01.getPermission(), menuDO01); - MenuDO menuDO02 = randomPojo(MenuDO.class, o -> o.setId(2L).setPermission("123")); - permissionMenuCache.put(menuDO02.getPermission(), menuDO02); - // 不可匹配 - permissionMenuCache.put("456", randomPojo(MenuDO.class, o -> o.setId(3L).setPermission("456"))); - menuService.setPermissionMenuCache(permissionMenuCache); - // 准备参数 - String permission = "123"; - - // 调用 - List list = menuService.getMenuListByPermissionFromCache(permission); - // 断言 - assertEquals(2, list.size()); - assertPojoEquals(menuDO01, list.get(0)); - assertPojoEquals(menuDO02, list.get(1)); - } - @Test public void testGetMenu() { // mock 数据 @@ -314,7 +202,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @Test public void testValidateParentMenu_success() { // mock 数据 - MenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, "parent", 0L); + MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", 0L); menuMapper.insert(menuDO); // 准备参数 Long parentId = menuDO.getId(); @@ -340,7 +228,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @Test public void testValidateParentMenu_parentTypeError() { // mock 数据 - MenuDO menuDO = createMenuDO(MenuTypeEnum.BUTTON, "parent", 0L); + MenuDO menuDO = buildMenuDO(MenuTypeEnum.BUTTON, "parent", 0L); menuMapper.insert(menuDO); // 准备参数 Long parentId = menuDO.getId(); @@ -353,7 +241,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @Test public void testValidateMenu_success() { // mock 父子菜单 - MenuDO sonMenu = initParentAndSonMenu(); + MenuDO sonMenu = createParentAndSonMenu(); // 准备参数 Long parentId = sonMenu.getParentId(); Long otherSonMenuId = randomLongId(); @@ -366,7 +254,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { @Test public void testValidateMenu_sonMenuNameDuplicate() { // mock 父子菜单 - MenuDO sonMenu = initParentAndSonMenu(); + MenuDO sonMenu = createParentAndSonMenu(); // 准备参数 Long parentId = sonMenu.getParentId(); Long otherSonMenuId = randomLongId(); @@ -380,26 +268,26 @@ public class MenuServiceImplTest extends BaseDbUnitTest { // ====================== 初始化方法 ====================== /** - * 构造父子菜单,返回子菜单 + * 插入父子菜单,返回子菜单 * * @return 子菜单 */ - private MenuDO initParentAndSonMenu() { + private MenuDO createParentAndSonMenu() { // 构造父子菜单 - MenuDO parentMenuDO = createMenuDO(MenuTypeEnum.MENU, "parent", ID_ROOT); + MenuDO parentMenuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", ID_ROOT); menuMapper.insert(parentMenuDO); // 构建子菜单 - MenuDO sonMenuDO = createMenuDO(MenuTypeEnum.MENU, "testSonName", + MenuDO sonMenuDO = buildMenuDO(MenuTypeEnum.MENU, "testSonName", parentMenuDO.getParentId()); menuMapper.insert(sonMenuDO); return sonMenuDO; } - private MenuDO createMenuDO(MenuTypeEnum type, String name, Long parentId) { - return createMenuDO(type, name, parentId, randomCommonStatus()); + private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId) { + return buildMenuDO(type, name, parentId, randomCommonStatus()); } - private MenuDO createMenuDO(MenuTypeEnum type, String name, Long parentId, Integer status) { + private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId, Integer status) { return randomPojo(MenuDO.class, o -> o.setId(null).setName(name).setParentId(parentId) .setType(type.getType()).setStatus(status)); } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java index 35b250208..442abd741 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.system.service.permission; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.map.MapUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO; @@ -14,32 +14,28 @@ import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper; import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper; import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; -import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer; import cn.iocoder.yudao.module.system.service.dept.DeptService; import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Multimap; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.Set; +import static cn.hutool.core.collection.ListUtil.toList; import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static java.util.Arrays.asList; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @Import({PermissionServiceImpl.class}) public class PermissionServiceTest extends BaseDbUnitTest { @@ -61,131 +57,74 @@ public class PermissionServiceTest extends BaseDbUnitTest { @MockBean private AdminUserService userService; - @MockBean - private PermissionProducer permissionProducer; - @Test - public void testInitLocalCacheForRoleMenu() { - // mock 数据 - RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(1L).setMenuId(10L)); - roleMenuMapper.insert(roleMenuDO01); - RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(1L).setMenuId(20L)); - roleMenuMapper.insert(roleMenuDO02); + public void testHasAnyPermissions_superAdmin() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); - // 调用 - permissionService.initLocalCacheForRoleMenu(); - // 断言 roleMenuCache 缓存 - assertEquals(1, permissionService.getRoleMenuCache().keySet().size()); - assertEquals(asList(10L, 20L), permissionService.getRoleMenuCache().get(1L)); - // 断言 menuRoleCache 缓存 - assertEquals(2, permissionService.getMenuRoleCache().size()); - assertEquals(singletonList(1L), permissionService.getMenuRoleCache().get(10L)); - assertEquals(singletonList(1L), permissionService.getMenuRoleCache().get(20L)); + // 准备参数 + Long userId = 1L; + String[] roles = new String[]{"system:user:query", "system:user:create"}; + // mock 用户登录的角色 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); + RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); + // mock 其它方法 + when(roleService.hasAnySuperAdmin(eq(asSet(100L)))).thenReturn(true); + + // 调用,并断言 + assertTrue(permissionService.hasAnyPermissions(userId, roles)); + } } @Test - public void testInitLocalCacheForUserRole() { - // mock 数据 - UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); - userRoleMapper.insert(userRoleDO01); - UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); - userRoleMapper.insert(roleMenuDO02); + public void testHasAnyPermissions_normal() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); - // 调用 - permissionService.initLocalCacheForUserRole(); - // 断言 roleMenuCache 缓存 - assertEquals(1, permissionService.getUserRoleCache().size()); - assertEquals(asSet(10L, 20L), permissionService.getUserRoleCache().get(1L)); + // 准备参数 + Long userId = 1L; + String[] roles = new String[]{"system:user:query", "system:user:create"}; + // mock 用户登录的角色 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); + RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); + // mock 菜单 + Long menuId = 1000L; + when(menuService.getMenuIdListByPermissionFromCache( + eq("system:user:create"))).thenReturn(singletonList(menuId)); + roleMenuMapper.insert(randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1000L)); + + // 调用,并断言 + assertTrue(permissionService.hasAnyPermissions(userId, roles)); + } } @Test - public void testGetRoleMenuListFromCache_superAdmin() { - // 准备参数 - Collection roleIds = singletonList(100L); - Collection menuTypes = asList(2, 3); - Collection menusStatuses = asList(0, 1); - // mock 方法 - List roleList = singletonList(randomPojo(RoleDO.class, o -> o.setId(100L))); - when(roleService.getRoleListFromCache(eq(roleIds))).thenReturn(roleList); - when(roleService.hasAnySuperAdmin(same(roleList))).thenReturn(true); - List menuList = randomPojoList(MenuDO.class); - when(menuService.getMenuListFromCache(eq(menuTypes), eq(menusStatuses))).thenReturn(menuList); + public void testHasAnyRoles() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); - // 调用 - List result = permissionService.getRoleMenuListFromCache(roleIds, menuTypes, menusStatuses); - // 断言 - assertSame(menuList, result); + // 准备参数 + Long userId = 1L; + String[] roles = new String[]{"yunai", "tudou"}; + // mock 用户与角色的缓存 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); + RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L).setCode("tudou") + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); + + // 调用,并断言 + assertTrue(permissionService.hasAnyRoles(userId, roles)); + } } - @Test - public void testGetRoleMenuListFromCache_normal() { - // 准备参数 - Collection roleIds = asSet(100L, 200L); - Collection menuTypes = asList(2, 3); - Collection menusStatuses = asList(0, 1); - // mock 方法 - Multimap roleMenuCache = ImmutableMultimap.builder().put(100L, 1000L) - .put(200L, 2000L).put(200L, 2001L).build(); - permissionService.setRoleMenuCache(roleMenuCache); - List menuList = randomPojoList(MenuDO.class); - when(menuService.getMenuListFromCache(eq(asList(1000L, 2000L, 2001L)), eq(menuTypes), eq(menusStatuses))).thenReturn(menuList); - - // 调用 - List result = permissionService.getRoleMenuListFromCache(roleIds, menuTypes, menusStatuses); - // 断言 - assertSame(menuList, result); - } - - @Test - public void testGetUserRoleIdsFromCache() { - // 准备参数 - Long userId = 1L; - Collection roleStatuses = singleton(CommonStatusEnum.ENABLE.getStatus()); - // mock 方法 - Map> userRoleCache = MapUtil.>builder() - .put(1L, asSet(10L, 20L)).build(); - permissionService.setUserRoleCache(userRoleCache); - RoleDO roleDO01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleFromCache(eq(10L))).thenReturn(roleDO01); - RoleDO roleDO02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); - when(roleService.getRoleFromCache(eq(20L))).thenReturn(roleDO02); - - // 调用 - Set roleIds = permissionService.getUserRoleIdsFromCache(userId, roleStatuses); - // 断言 - assertEquals(asSet(10L), roleIds); - } - - @Test - public void testGetRoleMenuIds_superAdmin() { - // 准备参数 - Long roleId = 100L; - // mock 方法 - when(roleService.hasAnySuperAdmin(eq(singleton(100L)))).thenReturn(true); - List menuList = singletonList(randomPojo(MenuDO.class).setId(1L)); - when(menuService.getMenuList()).thenReturn(menuList); - - // 调用 - Set menuIds = permissionService.getRoleMenuIds(roleId); - // 断言 - assertEquals(singleton(1L), menuIds); - } - - @Test - public void testGetRoleMenuIds_normal() { - // 准备参数 - Long roleId = 100L; - // mock 数据 - RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1L); - roleMenuMapper.insert(roleMenu01); - RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(2L); - roleMenuMapper.insert(roleMenu02); - - // 调用 - Set menuIds = permissionService.getRoleMenuIds(roleId); - // 断言 - assertEquals(asSet(1L, 2L), menuIds); - } + // ========== 角色-菜单的相关方法 ========== @Test public void testAssignRoleMenu() { @@ -207,75 +146,6 @@ public class PermissionServiceTest extends BaseDbUnitTest { assertEquals(200L, roleMenuList.get(0).getMenuId()); assertEquals(1L, roleMenuList.get(1).getRoleId()); assertEquals(300L, roleMenuList.get(1).getMenuId()); - verify(permissionProducer).sendRoleMenuRefreshMessage(); - } - - @Test - public void testAssignUserRole() { - // 准备参数 - Long userId = 1L; - Set roleIds = asSet(200L, 300L); - // mock 数据 - UserRoleDO userRole01 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(100L); - userRoleMapper.insert(userRole01); - UserRoleDO userRole02 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(200L); - userRoleMapper.insert(userRole02); - - // 调用 - permissionService.assignUserRole(userId, roleIds); - // 断言 - List userRoleDOList = userRoleMapper.selectList(); - assertEquals(2, userRoleDOList.size()); - assertEquals(1L, userRoleDOList.get(0).getUserId()); - assertEquals(200L, userRoleDOList.get(0).getRoleId()); - assertEquals(1L, userRoleDOList.get(1).getUserId()); - assertEquals(300L, userRoleDOList.get(1).getRoleId()); - verify(permissionProducer).sendUserRoleRefreshMessage(); - } - - @Test - public void testGetUserRoleIdListByUserId() { - // 准备参数 - Long userId = 1L; - // mock 数据 - UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); - userRoleMapper.insert(userRoleDO01); - UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); - userRoleMapper.insert(roleMenuDO02); - - // 调用 - Set result = permissionService.getUserRoleIdListByUserId(userId); - // 断言 - assertEquals(asSet(10L, 20L), result); - } - - @Test - public void testGetUserRoleIdListByRoleIds() { - // 准备参数 - Collection roleIds = asSet(10L, 20L); - // mock 数据 - UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); - userRoleMapper.insert(userRoleDO01); - UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(2L).setRoleId(20L)); - userRoleMapper.insert(roleMenuDO02); - - // 调用 - Set result = permissionService.getUserRoleIdListByRoleIds(roleIds); - // 断言 - assertEquals(asSet(1L, 2L), result); - } - - @Test - public void testAssignRoleDataScope() { - // 准备参数 - Long roleId = 1L; - Integer dataScope = 2; - Set dataScopeDeptIds = asSet(10L, 20L); - - // 调用 - permissionService.assignRoleDataScope(roleId, dataScope, dataScopeDeptIds); - // 断言 - verify(roleService).updateRoleDataScope(eq(roleId), eq(dataScope), eq(dataScopeDeptIds)); } @Test @@ -303,9 +173,6 @@ public class PermissionServiceTest extends BaseDbUnitTest { List dbUserRoles = userRoleMapper.selectList(); assertEquals(1, dbUserRoles.size()); assertPojoEquals(dbUserRoles.get(0), userRoleDO02); - // 断言调用 - verify(permissionProducer).sendRoleMenuRefreshMessage(); - verify(permissionProducer).sendUserRoleRefreshMessage(); } @Test @@ -324,8 +191,77 @@ public class PermissionServiceTest extends BaseDbUnitTest { List dbRoleMenus = roleMenuMapper.selectList(); assertEquals(1, dbRoleMenus.size()); assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02); - // 断言调用 - verify(permissionProducer).sendRoleMenuRefreshMessage(); + } + + @Test + public void testGetRoleMenuIds_superAdmin() { + // 准备参数 + Long roleId = 100L; + // mock 方法 + when(roleService.hasAnySuperAdmin(eq(singleton(100L)))).thenReturn(true); + List menuList = singletonList(randomPojo(MenuDO.class).setId(1L)); + when(menuService.getMenuList()).thenReturn(menuList); + + // 调用 + Set menuIds = permissionService.getRoleMenuListByRoleId(roleId); + // 断言 + assertEquals(singleton(1L), menuIds); + } + + @Test + public void testGetRoleMenuIds_normal() { + // 准备参数 + Long roleId = 100L; + // mock 数据 + RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1L); + roleMenuMapper.insert(roleMenu01); + RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(2L); + roleMenuMapper.insert(roleMenu02); + + // 调用 + Set menuIds = permissionService.getRoleMenuListByRoleId(roleId); + // 断言 + assertEquals(asSet(1L, 2L), menuIds); + } + + @Test + public void testGetMenuRoleIdListByMenuIdFromCache() { + // 准备参数 + Long menuId = 1L; + // mock 数据 + RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1L); + roleMenuMapper.insert(roleMenu01); + RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(200L).setMenuId(1L); + roleMenuMapper.insert(roleMenu02); + + // 调用 + Set roleIds = permissionService.getMenuRoleIdListByMenuIdFromCache(menuId); + // 断言 + assertEquals(asSet(100L, 200L), roleIds); + } + + // ========== 用户-角色的相关方法 ========== + + @Test + public void testAssignUserRole() { + // 准备参数 + Long userId = 1L; + Set roleIds = asSet(200L, 300L); + // mock 数据 + UserRoleDO userRole01 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(100L); + userRoleMapper.insert(userRole01); + UserRoleDO userRole02 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(200L); + userRoleMapper.insert(userRole02); + + // 调用 + permissionService.assignUserRole(userId, roleIds); + // 断言 + List userRoleDOList = userRoleMapper.selectList(); + assertEquals(2, userRoleDOList.size()); + assertEquals(1L, userRoleDOList.get(0).getUserId()); + assertEquals(200L, userRoleDOList.get(0).getRoleId()); + assertEquals(1L, userRoleDOList.get(1).getUserId()); + assertEquals(300L, userRoleDOList.get(1).getRoleId()); } @Test @@ -344,202 +280,248 @@ public class PermissionServiceTest extends BaseDbUnitTest { List dbUserRoles = userRoleMapper.selectList(); assertEquals(1, dbUserRoles.size()); assertPojoEquals(dbUserRoles.get(0), userRoleDO02); - // 断言调用 - verify(permissionProducer).sendUserRoleRefreshMessage(); } @Test - public void testHasAnyPermissions_superAdmin() { + public void testGetUserRoleIdListByUserId() { // 准备参数 Long userId = 1L; - String[] roles = new String[]{"system:user:query", "system:user:create"}; - // mock 用户与角色的缓存 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(100L)).build()); - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleFromCache(eq(100L))).thenReturn(role); - // mock 其它方法 - when(roleService.hasAnySuperAdmin(eq(asSet(100L)))).thenReturn(true); + // mock 数据 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); + userRoleMapper.insert(userRoleDO01); + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); + userRoleMapper.insert(roleMenuDO02); // 调用 - boolean has = permissionService.hasAnyPermissions(userId, roles); + Set result = permissionService.getUserRoleIdListByUserId(userId); // 断言 - assertTrue(has); + assertEquals(asSet(10L, 20L), result); } @Test - public void testHasAnyPermissions_normal() { + public void testGetUserRoleIdListByUserIdFromCache() { // 准备参数 Long userId = 1L; - String[] roles = new String[]{"system:user:query", "system:user:create"}; - // mock 用户与角色的缓存 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(100L)).build()); - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleFromCache(eq(100L))).thenReturn(role); - // mock 其它方法 - MenuDO menu = randomPojo(MenuDO.class, o -> o.setId(1000L)); - when(menuService.getMenuListByPermissionFromCache(eq("system:user:create"))).thenReturn(singletonList(menu)); - permissionService.setMenuRoleCache(ImmutableMultimap.builder().put(1000L, 100L).build()); - + // mock 数据 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); + userRoleMapper.insert(userRoleDO01); + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); + userRoleMapper.insert(roleMenuDO02); // 调用 - boolean has = permissionService.hasAnyPermissions(userId, roles); + Set result = permissionService.getUserRoleIdListByUserIdFromCache(userId); // 断言 - assertTrue(has); + assertEquals(asSet(10L, 20L), result); } @Test - public void testHasAnyRoles_superAdmin() { + public void testGetUserRoleIdsFromCache() { // 准备参数 Long userId = 1L; - String[] roles = new String[]{"yunai", "tudou"}; - // mock 用户与角色的缓存 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(100L)).build()); - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleFromCache(eq(100L))).thenReturn(role); - // mock 其它方法 - when(roleService.hasAnySuperAdmin(eq(asSet(100L)))).thenReturn(true); + // mock 数据 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); + userRoleMapper.insert(userRoleDO01); + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); + userRoleMapper.insert(roleMenuDO02); // 调用 - boolean has = permissionService.hasAnyRoles(userId, roles); + Set result = permissionService.getUserRoleIdListByUserIdFromCache(userId); // 断言 - assertTrue(has); + assertEquals(asSet(10L, 20L), result); } @Test - public void testHasAnyRoles_normal() { + public void testGetUserRoleIdListByRoleId() { // 准备参数 - Long userId = 1L; - String[] roles = new String[]{"yunai", "tudou"}; - // mock 用户与角色的缓存 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(100L)).build()); - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L).setCode("yunai") - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleFromCache(eq(100L))).thenReturn(role); - // mock 其它方法 - when(roleService.getRoleListFromCache(eq(asSet(100L)))).thenReturn(singletonList(role)); + Collection roleIds = asSet(10L, 20L); + // mock 数据 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); + userRoleMapper.insert(userRoleDO01); + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(2L).setRoleId(20L)); + userRoleMapper.insert(roleMenuDO02); // 调用 - boolean has = permissionService.hasAnyRoles(userId, roles); + Set result = permissionService.getUserRoleIdListByRoleId(roleIds); // 断言 - assertTrue(has); + assertEquals(asSet(1L, 2L), result); + } + + @Test + public void testGetEnableUserRoleListByUserIdFromCache() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); + + // 准备参数 + Long userId = 1L; + // mock 用户登录的角色 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(200L)); + RoleDO role01 = randomPojo(RoleDO.class, o -> o.setId(100L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + RoleDO role02 = randomPojo(RoleDO.class, o -> o.setId(200L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(asSet(100L, 200L)))) + .thenReturn(toList(role01, role02)); + + // 调用 + List result = permissionService.getEnableUserRoleListByUserIdFromCache(userId); + // 断言 + assertEquals(1, result.size()); + assertPojoEquals(role01, result.get(0)); + } + } + + // ========== 用户-部门的相关方法 ========== + + @Test + public void testAssignRoleDataScope() { + // 准备参数 + Long roleId = 1L; + Integer dataScope = 2; + Set dataScopeDeptIds = asSet(10L, 20L); + + // 调用 + permissionService.assignRoleDataScope(roleId, dataScope, dataScopeDeptIds); + // 断言 + verify(roleService).updateRoleDataScope(eq(roleId), eq(dataScope), eq(dataScopeDeptIds)); } @Test public void testGetDeptDataPermission_All() { - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(2L)).build()); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO)); - when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertTrue(result.getAll()); - assertFalse(result.getSelf()); - assertTrue(CollUtil.isEmpty(result.getDeptIds())); + // 准备参数 + Long userId = 1L; + // mock 用户的角色编号 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); + // mock 获得用户的角色 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope()) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); + + // 调用 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); + // 断言 + assertTrue(result.getAll()); + assertFalse(result.getSelf()); + assertTrue(CollUtil.isEmpty(result.getDeptIds())); + } } @Test public void testGetDeptDataPermission_DeptCustom() { - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(2L)).build()); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO)); - when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO); - // mock 部门的返回 - when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的,看看会不会重复调用 + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertFalse(result.getAll()); - assertFalse(result.getSelf()); - assertEquals(roleDO.getDataScopeDeptIds().size() + 1, result.getDeptIds().size()); - assertTrue(CollUtil.containsAll(result.getDeptIds(), roleDO.getDataScopeDeptIds())); - assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); + // 准备参数 + Long userId = 1L; + // mock 用户的角色编号 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); + // mock 获得用户的角色 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope()) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); + // mock 部门的返回 + when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), + null, null); // 最后返回 null 的目的,看看会不会重复调用 + + // 调用 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); + // 断言 + assertFalse(result.getAll()); + assertFalse(result.getSelf()); + assertEquals(roleDO.getDataScopeDeptIds().size() + 1, result.getDeptIds().size()); + assertTrue(CollUtil.containsAll(result.getDeptIds(), roleDO.getDataScopeDeptIds())); + assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); + } } @Test public void testGetDeptDataPermission_DeptOnly() { - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(2L)).build()); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO)); - when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO); - // mock 部门的返回 - when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的,看看会不会重复调用 + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertFalse(result.getAll()); - assertFalse(result.getSelf()); - assertEquals(1, result.getDeptIds().size()); - assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); + // 准备参数 + Long userId = 1L; + // mock 用户的角色编号 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); + // mock 获得用户的角色 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope()) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); + // mock 部门的返回 + when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), + null, null); // 最后返回 null 的目的,看看会不会重复调用 + + // 调用 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); + // 断言 + assertFalse(result.getAll()); + assertFalse(result.getSelf()); + assertEquals(1, result.getDeptIds().size()); + assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); + } } @Test public void testGetDeptDataPermission_DeptAndChild() { - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(2L)).build()); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO)); - when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO); - // mock 部门的返回 - when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的,看看会不会重复调用 - // mock 方法(部门) - DeptDO deptDO = randomPojo(DeptDO.class); - when(deptService.getDeptListByParentIdFromCache(eq(3L), eq(true))) - .thenReturn(singletonList(deptDO)); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertFalse(result.getAll()); - assertFalse(result.getSelf()); - assertEquals(2, result.getDeptIds().size()); - assertTrue(CollUtil.contains(result.getDeptIds(), deptDO.getId())); - assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); + // 准备参数 + Long userId = 1L; + // mock 用户的角色编号 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); + // mock 获得用户的角色 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope()) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); + // mock 部门的返回 + when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), + null, null); // 最后返回 null 的目的,看看会不会重复调用 + // mock 方法(部门) + DeptDO deptDO = randomPojo(DeptDO.class); + when(deptService.getChildDeptIdListFromCache(eq(3L))).thenReturn(singleton(deptDO.getId())); + + // 调用 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); + // 断言 + assertFalse(result.getAll()); + assertFalse(result.getSelf()); + assertEquals(2, result.getDeptIds().size()); + assertTrue(CollUtil.contains(result.getDeptIds(), deptDO.getId())); + assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); + } } @Test public void testGetDeptDataPermission_Self() { - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - permissionService.setUserRoleCache(MapUtil.>builder().put(1L, asSet(2L)).build()); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO)); - when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertFalse(result.getAll()); - assertTrue(result.getSelf()); - assertTrue(CollUtil.isEmpty(result.getDeptIds())); + // 准备参数 + Long userId = 1L; + // mock 用户的角色编号 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); + // mock 获得用户的角色 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope()) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); + + // 调用 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); + // 断言 + assertFalse(result.getAll()); + assertTrue(result.getSelf()); + assertTrue(CollUtil.isEmpty(result.getDeptIds())); + } } } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java index a114556c4..5c7e96398 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.system.service.permission; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; @@ -11,15 +12,14 @@ import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper; import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; -import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.Set; import static cn.hutool.core.util.RandomUtil.randomEle; @@ -33,6 +33,8 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.verify; @Import(RoleServiceImpl.class) @@ -46,26 +48,9 @@ public class RoleServiceImplTest extends BaseDbUnitTest { @MockBean private PermissionService permissionService; - @MockBean - private RoleProducer roleProducer; @Test - public void testInitLocalCache() { - RoleDO roleDO1 = randomPojo(RoleDO.class); - roleMapper.insert(roleDO1); - RoleDO roleDO2 = randomPojo(RoleDO.class); - roleMapper.insert(roleDO2); - - // 调用 - roleService.initLocalCache(); - // 断言 roleCache 缓存 - Map roleCache = roleService.getRoleCache(); - assertPojoEquals(roleDO1, roleCache.get(roleDO1.getId())); - assertPojoEquals(roleDO2, roleCache.get(roleDO2.getId())); - } - - @Test - public void testCreateRole_success() { + public void testCreateRole() { // 准备参数 RoleCreateReqVO reqVO = randomPojo(RoleCreateReqVO.class); @@ -77,12 +62,10 @@ public class RoleServiceImplTest extends BaseDbUnitTest { assertEquals(RoleTypeEnum.CUSTOM.getType(), roleDO.getType()); assertEquals(CommonStatusEnum.ENABLE.getStatus(), roleDO.getStatus()); assertEquals(DataScopeEnum.ALL.getScope(), roleDO.getDataScope()); - // verify 发送刷新消息 - verify(roleProducer).sendRoleRefreshMessage(); } @Test - public void testUpdateRole_success() { + public void testUpdateRole() { // mock 数据 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); roleMapper.insert(roleDO); @@ -95,12 +78,10 @@ public class RoleServiceImplTest extends BaseDbUnitTest { // 断言 RoleDO newRoleDO = roleMapper.selectById(id); assertPojoEquals(reqVO, newRoleDO); - // verify 发送刷新消息 - verify(roleProducer).sendRoleRefreshMessage(); } @Test - public void testUpdateRoleStatus_success() { + public void testUpdateRoleStatus() { // mock 数据 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()) .setType(RoleTypeEnum.CUSTOM.getType())); @@ -114,12 +95,10 @@ public class RoleServiceImplTest extends BaseDbUnitTest { // 断言 RoleDO dbRoleDO = roleMapper.selectById(roleId); assertEquals(CommonStatusEnum.DISABLE.getStatus(), dbRoleDO.getStatus()); - // verify 发送刷新消息 - verify(roleProducer).sendRoleRefreshMessage(); } @Test - public void testUpdateRoleDataScope_success() { + public void testUpdateRoleDataScope() { // mock 数据 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); roleMapper.insert(roleDO); @@ -134,12 +113,10 @@ public class RoleServiceImplTest extends BaseDbUnitTest { RoleDO dbRoleDO = roleMapper.selectById(id); assertEquals(dataScope, dbRoleDO.getDataScope()); assertEquals(dataScopeRoleIds, dbRoleDO.getDataScopeDeptIds()); - // verify 发送刷新消息 - verify(roleProducer).sendRoleRefreshMessage(); } @Test - public void testDeleteRole_success() { + public void testDeleteRole() { // mock 数据 RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); roleMapper.insert(roleDO); @@ -152,23 +129,65 @@ public class RoleServiceImplTest extends BaseDbUnitTest { assertNull(roleMapper.selectById(id)); // verify 删除相关数据 verify(permissionService).processRoleDeleted(id); - // verify 发送刷新消息 - verify(roleProducer).sendRoleRefreshMessage(); } @Test - public void testGetRoleFromCache() { - // mock 数据(缓存) + public void testValidateRoleDuplicate_success() { + // 调用,不会抛异常 + roleService.validateRoleDuplicate(randomString(), randomString(), null); + } + + @Test + public void testValidateRoleDuplicate_nameDuplicate() { + // mock 数据 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setName("role_name")); + roleMapper.insert(roleDO); + // 准备参数 + String name = "role_name"; + + // 调用,并断言异常 + assertServiceException(() -> roleService.validateRoleDuplicate(name, randomString(), null), + ROLE_NAME_DUPLICATE, name); + } + + @Test + public void testValidateRoleDuplicate_codeDuplicate() { + // mock 数据 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setCode("code")); + roleMapper.insert(roleDO); + // 准备参数 + String code = "code"; + + // 调用,并断言异常 + assertServiceException(() -> roleService.validateRoleDuplicate(randomString(), code, null), + ROLE_CODE_DUPLICATE, code); + } + + @Test + public void testValidateUpdateRole_success() { RoleDO roleDO = randomPojo(RoleDO.class); roleMapper.insert(roleDO); - roleService.initLocalCache(); - // 参数准备 + // 准备参数 Long id = roleDO.getId(); - // 调用 - RoleDO dbRoleDO = roleService.getRoleFromCache(id); - // 断言 - assertPojoEquals(roleDO, dbRoleDO); + // 调用,无异常 + roleService.validateRoleForUpdate(id); + } + + @Test + public void testValidateUpdateRole_roleIdNotExist() { + assertServiceException(() -> roleService.validateRoleForUpdate(randomLongId()), ROLE_NOT_EXISTS); + } + + @Test + public void testValidateUpdateRole_systemRoleCanNotBeUpdate() { + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.SYSTEM.getType())); + roleMapper.insert(roleDO); + // 准备参数 + Long id = roleDO.getId(); + + assertServiceException(() -> roleService.validateRoleForUpdate(id), + ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE); } @Test @@ -186,22 +205,21 @@ public class RoleServiceImplTest extends BaseDbUnitTest { } @Test - public void testGetRoleListByStatus_statusNotEmpty() { - // mock 数据 - RoleDO dbRole = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - roleMapper.insert(dbRole); - // 测试 status 不匹配 - roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + public void testGetRoleFromCache() { + // mock 数据(缓存) + RoleDO roleDO = randomPojo(RoleDO.class); + roleMapper.insert(roleDO); + // 参数准备 + Long id = roleDO.getId(); // 调用 - List list = roleService.getRoleListByStatus(singleton(CommonStatusEnum.ENABLE.getStatus())); + RoleDO dbRoleDO = roleService.getRoleFromCache(id); // 断言 - assertEquals(1, list.size()); - assertPojoEquals(dbRole, list.get(0)); + assertPojoEquals(roleDO, dbRoleDO); } @Test - public void testGetRoleListByStatus_statusEmpty() { + public void testGetRoleListByStatus() { // mock 数据 RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); roleMapper.insert(dbRole01); @@ -209,29 +227,33 @@ public class RoleServiceImplTest extends BaseDbUnitTest { roleMapper.insert(dbRole02); // 调用 - List list = roleService.getRoleListByStatus(null); + List list = roleService.getRoleListByStatus( + singleton(CommonStatusEnum.ENABLE.getStatus())); // 断言 - assertEquals(2, list.size()); + assertEquals(1, list.size()); assertPojoEquals(dbRole01, list.get(0)); - assertPojoEquals(dbRole02, list.get(1)); } @Test public void testGetRoleListFromCache() { - // mock 数据 - RoleDO dbRole = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - roleMapper.insert(dbRole); - // 测试 id 不匹配 - roleMapper.insert(cloneIgnoreId(dbRole, o -> {})); - roleService.initLocalCache(); - // 准备参数 - Collection ids = singleton(dbRole.getId()); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class))) + .thenReturn(roleService); - // 调用 - List list = roleService.getRoleListFromCache(ids); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(dbRole, list.get(0)); + // mock 数据 + RoleDO dbRole = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); + roleMapper.insert(dbRole); + // 测试 id 不匹配 + roleMapper.insert(cloneIgnoreId(dbRole, o -> {})); + // 准备参数 + Collection ids = singleton(dbRole.getId()); + + // 调用 + List list = roleService.getRoleListFromCache(ids); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbRole, list.get(0)); + } } @Test @@ -296,72 +318,37 @@ public class RoleServiceImplTest extends BaseDbUnitTest { } @Test - public void testHasAnySuperAdmin() { - // 是超级 - assertTrue(roleService.hasAnySuperAdmin(singletonList(randomPojo(RoleDO.class, - o -> o.setCode("super_admin"))))); - // 非超级 - assertFalse(roleService.hasAnySuperAdmin(singletonList(randomPojo(RoleDO.class, - o -> o.setCode("tenant_admin"))))); + public void testHasAnySuperAdmin_true() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class))) + .thenReturn(roleService); + + // mock 数据 + RoleDO dbRole = randomPojo(RoleDO.class).setCode("super_admin"); + roleMapper.insert(dbRole); + // 准备参数 + Long id = dbRole.getId(); + + // 调用,并调用 + assertTrue(roleService.hasAnySuperAdmin(singletonList(id))); + } } @Test - public void testValidateRoleDuplicate_success() { - // 调用,不会抛异常 - roleService.validateRoleDuplicate(randomString(), randomString(), null); - } + public void testHasAnySuperAdmin_false() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class))) + .thenReturn(roleService); - @Test - public void testValidateRoleDuplicate_nameDuplicate() { - // mock 数据 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setName("role_name")); - roleMapper.insert(roleDO); - // 准备参数 - String name = "role_name"; + // mock 数据 + RoleDO dbRole = randomPojo(RoleDO.class).setCode("tenant_admin"); + roleMapper.insert(dbRole); + // 准备参数 + Long id = dbRole.getId(); - // 调用,并断言异常 - assertServiceException(() -> roleService.validateRoleDuplicate(name, randomString(), null), - ROLE_NAME_DUPLICATE, name); - } - - @Test - public void testValidateRoleDuplicate_codeDuplicate() { - // mock 数据 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setCode("code")); - roleMapper.insert(roleDO); - // 准备参数 - String code = "code"; - - // 调用,并断言异常 - assertServiceException(() -> roleService.validateRoleDuplicate(randomString(), code, null), - ROLE_CODE_DUPLICATE, code); - } - - @Test - public void testValidateUpdateRole_success() { - RoleDO roleDO = randomPojo(RoleDO.class); - roleMapper.insert(roleDO); - // 准备参数 - Long id = roleDO.getId(); - - // 调用,无异常 - roleService.validateRoleForUpdate(id); - } - - @Test - public void testValidateUpdateRole_roleIdNotExist() { - assertServiceException(() -> roleService.validateRoleForUpdate(randomLongId()), ROLE_NOT_EXISTS); - } - - @Test - public void testValidateUpdateRole_systemRoleCanNotBeUpdate() { - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.SYSTEM.getType())); - roleMapper.insert(roleDO); - // 准备参数 - Long id = roleDO.getId(); - - assertServiceException(() -> roleService.validateRoleForUpdate(id), - ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE); + // 调用,并调用 + assertFalse(roleService.hasAnySuperAdmin(singletonList(id))); + } } @Test diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImplTest.java index 141236987..73c95a7de 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImplTest.java @@ -10,9 +10,7 @@ import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.Sensitiv import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO; import cn.iocoder.yudao.module.system.dal.mysql.sensitiveword.SensitiveWordMapper; -import cn.iocoder.yudao.module.system.mq.producer.sensitiveword.SensitiveWordProducer; import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; @@ -29,7 +27,6 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_WORD_NOT_EXISTS; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.verify; /** * {@link SensitiveWordServiceImpl} 的单元测试类 @@ -45,9 +42,6 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest { @Resource private SensitiveWordMapper sensitiveWordMapper; - @MockBean - private SensitiveWordProducer sensitiveWordProducer; - @Test public void testInitLocalCache() { SensitiveWordDO wordDO1 = randomPojo(SensitiveWordDO.class, o -> o.setName("傻瓜") @@ -61,6 +55,10 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest { sensitiveWordService.initLocalCache(); // 断言 sensitiveWordTagsCache 缓存 assertEquals(SetUtils.asSet("论坛", "蔬菜"), sensitiveWordService.getSensitiveWordTagSet()); + // 断言 sensitiveWordCache + assertEquals(2, sensitiveWordService.getSensitiveWordCache().size()); + assertPojoEquals(wordDO1, sensitiveWordService.getSensitiveWordCache().get(0)); + assertPojoEquals(wordDO2, sensitiveWordService.getSensitiveWordCache().get(1)); // 断言 tagSensitiveWordTries 缓存 assertNotNull(sensitiveWordService.getDefaultSensitiveWordTrie()); assertEquals(2, sensitiveWordService.getTagSensitiveWordTries().size()); @@ -80,7 +78,6 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest { // 校验记录的属性是否正确 SensitiveWordDO sensitiveWord = sensitiveWordMapper.selectById(sensitiveWordId); assertPojoEquals(reqVO, sensitiveWord); - verify(sensitiveWordProducer).sendSensitiveWordRefreshMessage(); } @Test @@ -98,7 +95,6 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest { // 校验是否更新正确 SensitiveWordDO sensitiveWord = sensitiveWordMapper.selectById(reqVO.getId()); // 获取最新的 assertPojoEquals(reqVO, sensitiveWord); - verify(sensitiveWordProducer).sendSensitiveWordRefreshMessage(); } @Test @@ -122,7 +118,6 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest { sensitiveWordService.deleteSensitiveWord(id); // 校验数据不存在了 assertNull(sensitiveWordMapper.selectById(id)); - verify(sensitiveWordProducer).sendSensitiveWordRefreshMessage(); } @Test diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java index dabb9aa75..1d93d2243 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java @@ -9,13 +9,11 @@ import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannel import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper; -import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; - import java.util.List; import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; @@ -42,8 +40,6 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { private SmsClientFactory smsClientFactory; @MockBean private SmsTemplateService smsTemplateService; - @MockBean - private SmsProducer smsProducer; @Test public void testInitLocalCache_success() { @@ -60,6 +56,10 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { argThat(properties -> isPojoEquals(smsChannelDO01, properties))); verify(smsClientFactory, times(1)).createOrUpdateSmsClient( argThat(properties -> isPojoEquals(smsChannelDO02, properties))); + // 断言 channelCache 缓存 + assertEquals(2, smsChannelService.getChannelCache().size()); + assertPojoEquals(smsChannelDO01, smsChannelService.getChannelCache().get(0)); + assertPojoEquals(smsChannelDO02, smsChannelService.getChannelCache().get(1)); } @Test @@ -74,8 +74,6 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { // 校验记录的属性是否正确 SmsChannelDO smsChannel = smsChannelMapper.selectById(smsChannelId); assertPojoEquals(reqVO, smsChannel); - // 校验调用 - verify(smsProducer, times(1)).sendSmsChannelRefreshMessage(); } @Test @@ -95,8 +93,6 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { // 校验是否更新正确 SmsChannelDO smsChannel = smsChannelMapper.selectById(reqVO.getId()); // 获取最新的 assertPojoEquals(reqVO, smsChannel); - // 校验调用 - verify(smsProducer, times(1)).sendSmsChannelRefreshMessage(); } @Test @@ -118,10 +114,8 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { // 调用 smsChannelService.deleteSmsChannel(id); - // 校验数据不存在了 - assertNull(smsChannelMapper.selectById(id)); - // 校验调用 - verify(smsProducer, times(1)).sendSmsChannelRefreshMessage(); + // 校验数据不存在了 + assertNull(smsChannelMapper.selectById(id)); } @Test diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java index 3b9b577a2..5aaf43b30 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java @@ -18,7 +18,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper; import cn.iocoder.yudao.module.system.enums.sms.SmsTemplateTypeEnum; -import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer; import com.google.common.collect.Lists; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; @@ -26,7 +25,6 @@ import org.springframework.context.annotation.Import; import javax.annotation.Resource; import java.util.List; -import java.util.Map; import java.util.function.Consumer; import static cn.hutool.core.util.RandomUtil.randomEle; @@ -55,25 +53,6 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { private SmsClientFactory smsClientFactory; @MockBean private SmsClient smsClient; - @MockBean - private SmsProducer smsProducer; - - @Test - void testInitLocalCache() { - // mock 数据 - SmsTemplateDO smsTemplate01 = randomSmsTemplateDO(); - smsTemplateMapper.insert(smsTemplate01); - SmsTemplateDO smsTemplate02 = randomSmsTemplateDO(); - smsTemplateMapper.insert(smsTemplate02); - - // 调用 - smsTemplateService.initLocalCache(); - // 断言 deptCache 缓存 - Map smsTemplateCache = smsTemplateService.getSmsTemplateCache(); - assertEquals(2, smsTemplateCache.size()); - assertPojoEquals(smsTemplate01, smsTemplateCache.get(smsTemplate01.getCode())); - assertPojoEquals(smsTemplate02, smsTemplateCache.get(smsTemplate02.getCode())); - } @Test public void testParseTemplateContentParams() { @@ -116,8 +95,6 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { assertPojoEquals(reqVO, smsTemplate); assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams()); assertEquals(channelDO.getCode(), smsTemplate.getChannelCode()); - // 校验调用 - verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage(); } @Test @@ -151,8 +128,6 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { assertPojoEquals(reqVO, smsTemplate); assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams()); assertEquals(channelDO.getCode(), smsTemplate.getChannelCode()); - // 校验调用 - verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage(); } @Test @@ -174,10 +149,8 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { // 调用 smsTemplateService.deleteSmsTemplate(id); - // 校验数据不存在了 - assertNull(smsTemplateMapper.selectById(id)); - // 校验调用 - verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage(); + // 校验数据不存在了 + assertNull(smsTemplateMapper.selectById(id)); } @Test @@ -191,47 +164,47 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { @Test public void testGetSmsTemplatePage() { - // mock 数据 - SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> { // 等会查询到 - o.setType(SmsTemplateTypeEnum.PROMOTION.getType()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCode("tudou"); - o.setContent("芋道源码"); - o.setApiTemplateId("yunai"); - o.setChannelId(1L); - o.setCreateTime(buildTime(2021, 11, 11)); - }); - smsTemplateMapper.insert(dbSmsTemplate); - // 测试 type 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setType(SmsTemplateTypeEnum.VERIFICATION_CODE.getType()))); - // 测试 status 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 code 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCode("yuanma"))); - // 测试 content 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setContent("源码"))); - // 测试 apiTemplateId 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setApiTemplateId("nai"))); - // 测试 channelId 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setChannelId(2L))); - // 测试 createTime 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCreateTime(buildTime(2021, 12, 12)))); - // 准备参数 - SmsTemplatePageReqVO reqVO = new SmsTemplatePageReqVO(); - reqVO.setType(SmsTemplateTypeEnum.PROMOTION.getType()); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCode("tu"); - reqVO.setContent("芋道"); - reqVO.setApiTemplateId("yu"); - reqVO.setChannelId(1L); - reqVO.setCreateTime(buildBetweenTime(2021, 11, 1, 2021, 12, 1)); + // mock 数据 + SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> { // 等会查询到 + o.setType(SmsTemplateTypeEnum.PROMOTION.getType()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCode("tudou"); + o.setContent("芋道源码"); + o.setApiTemplateId("yunai"); + o.setChannelId(1L); + o.setCreateTime(buildTime(2021, 11, 11)); + }); + smsTemplateMapper.insert(dbSmsTemplate); + // 测试 type 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setType(SmsTemplateTypeEnum.VERIFICATION_CODE.getType()))); + // 测试 status 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 code 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCode("yuanma"))); + // 测试 content 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setContent("源码"))); + // 测试 apiTemplateId 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setApiTemplateId("nai"))); + // 测试 channelId 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setChannelId(2L))); + // 测试 createTime 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCreateTime(buildTime(2021, 12, 12)))); + // 准备参数 + SmsTemplatePageReqVO reqVO = new SmsTemplatePageReqVO(); + reqVO.setType(SmsTemplateTypeEnum.PROMOTION.getType()); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCode("tu"); + reqVO.setContent("芋道"); + reqVO.setApiTemplateId("yu"); + reqVO.setChannelId(1L); + reqVO.setCreateTime(buildBetweenTime(2021, 11, 1, 2021, 12, 1)); - // 调用 - PageResult pageResult = smsTemplateService.getSmsTemplatePage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbSmsTemplate, pageResult.getList().get(0)); + // 调用 + PageResult pageResult = smsTemplateService.getSmsTemplatePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbSmsTemplate, pageResult.getList().get(0)); } @Test @@ -271,11 +244,11 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { reqVO.setChannelId(1L); reqVO.setCreateTime(buildBetweenTime(2021, 11, 1, 2021, 12, 1)); - // 调用 - List list = smsTemplateService.getSmsTemplateList(reqVO); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(dbSmsTemplate, list.get(0)); + // 调用 + List list = smsTemplateService.getSmsTemplateList(reqVO); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbSmsTemplate, list.get(0)); } @Test diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java index 38357b280..1fb9c4fca 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java @@ -199,7 +199,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest { role101.setTenantId(dbTenant.getId()); when(roleService.getRoleListByStatus(isNull())).thenReturn(asList(role100, role101)); // mock 每个角色的权限 - when(permissionService.getRoleMenuIds(eq(101L))).thenReturn(asSet(201L, 202L)); + when(permissionService.getRoleMenuListByRoleId(eq(101L))).thenReturn(asSet(201L, 202L)); // 调用 tenantService.updateTenant(reqVO); @@ -454,7 +454,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest { TenantContextHolder.setTenantId(dbTenant.getId()); // mock 菜单 when(menuService.getMenuList()).thenReturn(Arrays.asList(randomPojo(MenuDO.class, o -> o.setId(100L)), - randomPojo(MenuDO.class, o -> o.setId(101L)))); + randomPojo(MenuDO.class, o -> o.setId(101L)))); // 调用 tenantService.handleTenantMenu(handler); diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java index a5e0183a4..acc4357cc 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java @@ -345,7 +345,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { reqVO.setDeptId(1L); // 其中,1L 是 2L 的父部门 // mock 方法 List deptList = newArrayList(randomPojo(DeptDO.class, o -> o.setId(2L))); - when(deptService.getDeptListByParentIdFromCache(eq(reqVO.getDeptId()), eq(true))).thenReturn(deptList); + when(deptService.getChildDeptList(eq(reqVO.getDeptId()))).thenReturn(deptList); // 调用 PageResult pageResult = userService.getUserPage(reqVO); @@ -368,7 +368,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { reqVO.setDeptId(1L); // 其中,1L 是 2L 的父部门 // mock 方法 List deptList = newArrayList(randomPojo(DeptDO.class, o -> o.setId(2L))); - when(deptService.getDeptListByParentIdFromCache(eq(reqVO.getDeptId()), eq(true))).thenReturn(deptList); + when(deptService.getChildDeptList(eq(reqVO.getDeptId()))).thenReturn(deptList); // 调用 List list = userService.getUserList(reqVO); diff --git a/yudao-server/src/main/resources/application-dev.yaml b/yudao-server/src/main/resources/application-dev.yaml index ddacc5485..f4e7bc020 100644 --- a/yudao-server/src/main/resources/application-dev.yaml +++ b/yudao-server/src/main/resources/application-dev.yaml @@ -147,14 +147,14 @@ wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-sta # 存储配置,解决 AccessToken 的跨节点的共享 config-storage: type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 - key-prefix: wx # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置 + key-prefix: wx # Redis Key 的前缀 http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档 appid: wx63c280fe3248a3e7 secret: 6f270509224a7ae1296bbf1c8cb97aed config-storage: type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 - key-prefix: wa # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置 + key-prefix: wa # Redis Key 的前缀 http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 --- #################### 芋道相关配置 #################### @@ -167,7 +167,8 @@ yudao: - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 pay: - callback-url: http://yunai.natapp1.cc/admin-api/pay/notify/callback + order-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/order # 支付渠道的【支付】回调地址 + refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址 demo: true # 开启演示模式 justauth: diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 08937ee1f..4cddf8065 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -170,14 +170,14 @@ wx: # 存储配置,解决 AccessToken 的跨节点的共享 config-storage: type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 - key-prefix: wx # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置 + key-prefix: wx # Redis Key 的前缀 http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档 appid: wx62056c0d5e8db250 secret: 333ae72f41552af1e998fe1f54e1584a config-storage: type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 - key-prefix: wa # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置 + key-prefix: wa # Redis Key 的前缀 http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 --- #################### 芋道相关配置 #################### diff --git a/yudao-ui-admin/package.json b/yudao-ui-admin/package.json index e8acd5c97..301e894a9 100644 --- a/yudao-ui-admin/package.json +++ b/yudao-ui-admin/package.json @@ -1,6 +1,6 @@ { "name": "yudao-ui-admin", - "version": "1.7.3-snapshot", + "version": "1.8.0-snapshot", "description": "芋道管理系统", "author": "芋道", "license": "MIT", diff --git a/yudao-ui-admin/src/api/menu.js b/yudao-ui-admin/src/api/menu.js deleted file mode 100644 index 0939be757..000000000 --- a/yudao-ui-admin/src/api/menu.js +++ /dev/null @@ -1,9 +0,0 @@ -import request from '@/utils/request' - -// 获取路由 -export const getRouters = () => { - return request({ - url: '/system/auth/list-menus', - method: 'get' - }) -} diff --git a/yudao-ui-admin/src/api/system/permission.js b/yudao-ui-admin/src/api/system/permission.js index b76405640..1eee42a34 100644 --- a/yudao-ui-admin/src/api/system/permission.js +++ b/yudao-ui-admin/src/api/system/permission.js @@ -3,7 +3,7 @@ import request from '@/utils/request' // 查询角色拥有的菜单数组 export function listRoleMenus(roleId) { return request({ - url: '/system/permission/list-role-resources?roleId=' + roleId, + url: '/system/permission/list-role-menus?roleId=' + roleId, method: 'get' }) } diff --git a/yudao-ui-admin/src/components/Crontab/week.vue b/yudao-ui-admin/src/components/Crontab/week.vue index c84f3b3b5..5ebf0de5d 100644 --- a/yudao-ui-admin/src/components/Crontab/week.vue +++ b/yudao-ui-admin/src/components/Crontab/week.vue @@ -185,7 +185,7 @@ export default { averageTotal: function () { this.average01 = this.checkNum(this.average01, 1, 4) this.average02 = this.checkNum(this.average02, 1, 7) - return this.average02 + '#' + this.average01; + return this.average01 + '#' + this.average02; }, // 最近的工作日(格式) weekdayCheck: function () { diff --git a/yudao-ui-admin/src/permission.js b/yudao-ui-admin/src/permission.js index 7125eeaa7..301fee65f 100644 --- a/yudao-ui-admin/src/permission.js +++ b/yudao-ui-admin/src/permission.js @@ -25,9 +25,10 @@ router.beforeEach((to, from, next) => { // 获取字典数据 add by 芋艿 store.dispatch('dict/loadDictDatas') // 判断当前用户是否已拉取完 user_info 信息 - store.dispatch('GetInfo').then(() => { + store.dispatch('GetInfo').then(userInfo => { isRelogin.show = false - store.dispatch('GenerateRoutes').then(accessRoutes => { + // 触发 GenerateRoutes 事件时,将 menus 菜单树传递进去 + store.dispatch('GenerateRoutes', userInfo.menus).then(accessRoutes => { // 根据 roles 权限生成可访问的路由表 router.addRoutes(accessRoutes) // 动态添加可访问路由表 next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 diff --git a/yudao-ui-admin/src/store/modules/permission.js b/yudao-ui-admin/src/store/modules/permission.js index 2da339e2f..ed6109552 100644 --- a/yudao-ui-admin/src/store/modules/permission.js +++ b/yudao-ui-admin/src/store/modules/permission.js @@ -1,5 +1,4 @@ import {constantRoutes} from '@/router' -import {getRouters} from '@/api/menu' import Layout from '@/layout/index' import ParentView from '@/components/ParentView'; import {toCamelCase} from "@/utils"; @@ -27,22 +26,25 @@ const permission = { }, }, actions: { - // 生成路由 - GenerateRoutes({commit}) { + /** + * 生成路由 + * + * @param commit commit 函数 + * @param menus 路由参数 + */ + GenerateRoutes({commit}, menus) { return new Promise(resolve => { - // 向后端请求路由数据(菜单) - getRouters().then(res => { - const sdata = JSON.parse(JSON.stringify(res.data)) // 【重要】用于菜单中的数据 - const rdata = JSON.parse(JSON.stringify(res.data)) // 用于最后添加到 Router 中的数据 - const sidebarRoutes = filterAsyncRouter(sdata) - const rewriteRoutes = filterAsyncRouter(rdata, false, true) - rewriteRoutes.push({path: '*', redirect: '/404', hidden: true}) - commit('SET_ROUTES', rewriteRoutes) - commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes)) - commit('SET_DEFAULT_ROUTES', sidebarRoutes) - commit('SET_TOPBAR_ROUTES', sidebarRoutes) - resolve(rewriteRoutes) - }) + // 将 menus 菜单,转换为 route 路由数组 + const sdata = JSON.parse(JSON.stringify(menus)) // 【重要】用于菜单中的数据 + const rdata = JSON.parse(JSON.stringify(menus)) // 用于最后添加到 Router 中的数据 + const sidebarRoutes = filterAsyncRouter(sdata) + const rewriteRoutes = filterAsyncRouter(rdata, false, true) + rewriteRoutes.push({path: '*', redirect: '/404', hidden: true}) + commit('SET_ROUTES', rewriteRoutes) + commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes)) + commit('SET_DEFAULT_ROUTES', sidebarRoutes) + commit('SET_TOPBAR_ROUTES', sidebarRoutes) + resolve(rewriteRoutes) }) } } diff --git a/yudao-ui-admin/src/views/pay/app/components/mockChannelForm.vue b/yudao-ui-admin/src/views/pay/app/components/mockChannelForm.vue index d3b053c70..e2d6162f1 100644 --- a/yudao-ui-admin/src/views/pay/app/components/mockChannelForm.vue +++ b/yudao-ui-admin/src/views/pay/app/components/mockChannelForm.vue @@ -10,7 +10,6 @@ -