From c65f4e735993ef57c45c8305c0768737588f28cd Mon Sep 17 00:00:00 2001 From: zhouhaibin Date: Tue, 3 Sep 2024 09:58:00 +0800 Subject: [PATCH] =?UTF-8?q?=E7=89=88=E6=9C=AC=E6=9B=B4=E6=96=B0=E5=88=B05.?= =?UTF-8?q?2.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitee/ISSUE_TEMPLATE/bug.yml | 5 +- .run/ruoyi-monitor-admin.run.xml | 2 +- .run/ruoyi-server.run.xml | 6 +- .run/ruoyi-snailjob-server.run.xml | 2 +- README.md | 6 +- pom.xml | 71 +++--- ruoyi-admin/Dockerfile | 6 +- ruoyi-admin/pom.xml | 37 +-- .../web/controller/AuthController.java | 38 ++- .../web/listener/UserActionListener.java | 17 +- .../dromara/web/service/SysLoginService.java | 22 +- .../web/service/impl/EmailAuthStrategy.java | 36 ++- .../service/impl/PasswordAuthStrategy.java | 36 ++- .../web/service/impl/SmsAuthStrategy.java | 36 ++- .../web/service/impl/SocialAuthStrategy.java | 35 ++- .../src/main/resources/application-dev.yml | 27 +- .../src/main/resources/application-prod.yml | 11 +- .../src/main/resources/application.yml | 14 +- ruoyi-common/pom.xml | 1 + ruoyi-common/ruoyi-common-bom/pom.xml | 9 +- ruoyi-common/ruoyi-common-core/pom.xml | 5 - .../common/core/constant/CacheConstants.java | 5 + .../common/core/constant/GlobalConstants.java | 5 - .../common/core/constant/UserConstants.java | 10 + .../common/core/service/UserService.java | 16 ++ .../common/core/utils/StreamUtils.java | 29 +++ .../common/doc/handler/OpenApiHandler.java | 3 +- ruoyi-common/ruoyi-common-excel/pom.xml | 5 + .../common/excel/core/CellMergeStrategy.java | 7 +- .../common/mail/config/MailConfig.java | 2 +- .../dromara/common/mail/utils/MailUtils.java | 12 +- .../common/mybatis/annotation/DataColumn.java | 20 +- .../mybatis/annotation/DataPermission.java | 14 +- .../mybatis/core/domain/BaseEntity.java | 1 - .../mybatis/core/mapper/BaseMapperPlus.java | 162 ++++++++++-- .../common/mybatis/core/page/PageQuery.java | 6 +- .../mybatis/core/page/TableDataInfo.java | 10 +- .../common/mybatis/enums/DataBaseType.java | 11 +- .../common/mybatis/enums/DataScopeType.java | 29 ++- .../handler/InjectionMetaObjectHandler.java | 36 ++- .../handler/MybatisExceptionHandler.java | 5 +- .../handler/PlusDataPermissionHandler.java | 71 +++++- .../common/mybatis/helper/DataBaseHelper.java | 5 +- .../mybatis/helper/DataPermissionHelper.java | 61 ++++- .../PlusDataPermissionInterceptor.java | 70 +++++- .../src/main/resources/spy.properties | 2 +- .../dromara/common/oss/core/OssClient.java | 2 + .../aspectj/RateLimiterAspect.java | 4 +- .../redis/manager/CaffeineCacheDecorator.java | 8 +- .../redis/manager/PlusSpringCacheManager.java | 4 +- .../common/redis/utils/QueueUtils.java | 7 +- .../common/satoken/utils/LoginHelper.java | 24 +- .../security/config/SecurityConfig.java | 20 ++ .../sensitive/core/SensitiveStrategy.java | 52 +++- .../common/social/utils/SocialUtils.java | 4 +- ruoyi-common/ruoyi-common-sse/pom.xml | 36 +++ .../sse/config/SseAutoConfiguration.java | 36 +++ .../common/sse/config/SseProperties.java | 21 ++ .../common/sse/controller/SseController.java | 87 +++++++ .../common/sse/core/SseEmitterManager.java | 160 ++++++++++++ .../dromara/common/sse/dto/SseMessageDto.java | 29 +++ .../common/sse/listener/SseTopicListener.java | 48 ++++ .../common/sse/utils/SseMessageUtils.java | 58 +++++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + .../common/tenant/helper/TenantHelper.java | 44 +++- .../manager/TenantSpringCacheManager.java | 9 + ruoyi-common/ruoyi-common-web/pom.xml | 13 + .../common/web/config/UndertowConfig.java | 6 +- .../web/handler/GlobalExceptionHandler.java | 17 ++ .../PlusWebInvokeTimeInterceptor.java | 55 ++--- .../interceptor/PlusWebSocketInterceptor.java | 35 +-- .../websocket/utils/WebSocketUtils.java | 2 +- ruoyi-extend/ruoyi-monitor-admin/Dockerfile | 6 +- ruoyi-extend/ruoyi-monitor-admin/pom.xml | 13 +- .../monitor/admin/config/SecurityConfig.java | 4 +- .../admin/notifier/CustomNotifier.java | 21 +- .../src/main/resources/application.yml | 3 + ruoyi-extend/ruoyi-snailjob-server/Dockerfile | 8 +- .../starter/filter/ActuatorAuthFilter.java | 64 +++++ .../server/starter/filter/SecurityConfig.java | 29 +++ .../src/main/resources/application-dev.yml | 5 +- .../src/main/resources/application-prod.yml | 5 +- .../queue/DelayedQueueController.java | 9 +- .../dromara/demo/mapper/TestDemoMapper.java | 13 +- .../service/impl/TestDemoServiceImpl.java | 11 +- .../service/impl/TestTreeServiceImpl.java | 2 +- ruoyi-modules/ruoyi-generator/pom.xml | 32 +++ .../config/MyBatisDataSourceMonitor.java | 105 ++++++++ .../generator/domain/GenTableColumn.java | 1 - .../mapper/GenTableColumnMapper.java | 13 - .../generator/mapper/GenTableMapper.java | 16 -- .../service/GenTableServiceImpl.java | 133 +++++++++- .../generator/service/IGenTableService.java | 9 + .../dromara/generator/util/VelocityUtils.java | 3 + .../mapper/generator/GenTableColumnMapper.xml | 83 ------- .../mapper/generator/GenTableMapper.xml | 230 +----------------- .../resources/vm/java/serviceImpl.java.vm | 2 +- .../src/main/resources/vm/java/vo.java.vm | 7 + .../src/main/resources/vm/ts/types.ts.vm | 6 + .../main/resources/vm/vue/index-tree.vue.vm | 4 +- .../src/main/resources/vm/vue/index.vue.vm | 4 +- ruoyi-modules/ruoyi-system/pom.xml | 5 + .../monitor/SysLogininforController.java | 8 +- .../controller/system/SysDeptController.java | 9 +- .../system/SysNoticeController.java | 4 +- .../system/SysProfileController.java | 4 +- .../system/SysTenantController.java | 2 + .../system/SysTenantPackageController.java | 8 + .../dromara/system/domain/vo/SysUserVo.java | 4 +- .../dromara/system/mapper/SysMenuMapper.java | 7 - .../system/service/ISysDeptService.java | 8 - .../system/service/ISysPostService.java | 8 + .../service/ISysTenantPackageService.java | 11 +- .../service/impl/SysClientServiceImpl.java | 2 +- .../service/impl/SysConfigServiceImpl.java | 2 +- .../service/impl/SysDataScopeServiceImpl.java | 29 ++- .../service/impl/SysDeptServiceImpl.java | 16 +- .../service/impl/SysDictTypeServiceImpl.java | 2 +- .../impl/SysLogininforServiceImpl.java | 2 +- .../service/impl/SysMenuServiceImpl.java | 12 +- .../service/impl/SysNoticeServiceImpl.java | 2 +- .../service/impl/SysOperLogServiceImpl.java | 2 +- .../service/impl/SysOssConfigServiceImpl.java | 2 +- .../service/impl/SysOssServiceImpl.java | 8 +- .../service/impl/SysPostServiceImpl.java | 13 +- .../service/impl/SysRoleServiceImpl.java | 19 +- .../impl/SysTenantPackageServiceImpl.java | 14 +- .../service/impl/SysTenantServiceImpl.java | 2 +- .../service/impl/SysUserServiceImpl.java | 48 ++-- .../resources/mapper/system/SysMenuMapper.xml | 33 +-- .../resources/mapper/system/SysRoleMapper.xml | 4 +- ruoyi-modules/ruoyi-workflow/pom.xml | 2 +- .../controller/ActModelController.java | 5 +- .../controller/ActTaskController.java | 6 +- .../cmd/DeleteSequenceMultiInstanceCmd.java | 3 +- .../service/impl/ActModelServiceImpl.java | 4 +- .../impl/ActProcessDefinitionServiceImpl.java | 11 +- .../impl/ActProcessInstanceServiceImpl.java | 45 ++-- .../service/impl/ActTaskServiceImpl.java | 34 ++- .../service/impl/TestLeaveServiceImpl.java | 2 +- .../service/impl/WfCategoryServiceImpl.java | 7 +- .../impl/WfDefinitionConfigServiceImpl.java | 2 +- .../service/impl/WfFormManageServiceImpl.java | 2 +- .../service/impl/WfNodeConfigServiceImpl.java | 2 +- .../impl/WfTaskBackNodeServiceImpl.java | 5 +- .../service/impl/WorkflowServiceImpl.java | 4 +- script/docker/docker-compose.yml | 10 +- script/docker/nginx/conf/nginx.conf | 7 +- script/sql/oracle/snail_job_oracle.sql | 31 ++- script/sql/postgres/flowable.sql | 73 +----- script/sql/postgres/snail_job_postgre.sql | 23 +- script/sql/snail_job.sql | 17 +- script/sql/sqlserver/snail_job_sqlserver.sql | 70 +++++- 153 files changed, 2279 insertions(+), 1018 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-sse/pom.xml create mode 100644 ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/config/SseAutoConfiguration.java create mode 100644 ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/config/SseProperties.java create mode 100644 ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/controller/SseController.java create mode 100644 ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java create mode 100644 ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/dto/SseMessageDto.java create mode 100644 ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/listener/SseTopicListener.java create mode 100644 ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/utils/SseMessageUtils.java create mode 100644 ruoyi-common/ruoyi-common-sse/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/ActuatorAuthFilter.java create mode 100644 ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/SecurityConfig.java create mode 100644 ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/config/MyBatisDataSourceMonitor.java diff --git a/.gitee/ISSUE_TEMPLATE/bug.yml b/.gitee/ISSUE_TEMPLATE/bug.yml index 8a5d065..0450013 100644 --- a/.gitee/ISSUE_TEMPLATE/bug.yml +++ b/.gitee/ISSUE_TEMPLATE/bug.yml @@ -9,8 +9,9 @@ body: label: 版本 description: 你当前正在使用我们软件的哪个版本(pom文件内的版本号)? value: | - jdk版本(带上尾号): 例如 17.0.8 - 框架版本(项目启动时输出的版本号): 例如 5.1.1 + 注意: 未填写版本号不予处理直接关闭或删除 + jdk版本(带上尾号): + 框架版本(项目启动时输出的版本号): 其他依赖版本(你觉得有必要的): validations: required: true diff --git a/.run/ruoyi-monitor-admin.run.xml b/.run/ruoyi-monitor-admin.run.xml index 095b3d7..478b4f3 100644 --- a/.run/ruoyi-monitor-admin.run.xml +++ b/.run/ruoyi-monitor-admin.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-server.run.xml b/.run/ruoyi-server.run.xml index 0463c34..541800d 100644 --- a/.run/ruoyi-server.run.xml +++ b/.run/ruoyi-server.run.xml @@ -1,12 +1,12 @@ - + - - + \ No newline at end of file diff --git a/.run/ruoyi-snailjob-server.run.xml b/.run/ruoyi-snailjob-server.run.xml index 761915e..5221eef 100644 --- a/.run/ruoyi-snailjob-server.run.xml +++ b/.run/ruoyi-snailjob-server.run.xml @@ -2,7 +2,7 @@ - diff --git a/README.md b/README.md index 7ac5448..eeb3f7b 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE) [![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
-[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.2.0-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) +[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.2.2-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.2-blue.svg)]() [![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]() [![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]() @@ -56,11 +56,12 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow
| 数据加解密 | 采用 注解 + mybatis 拦截器 对存取数据期间自动加解密
支持多种策略 如BASE64、AES、RSA、SM2、SM4等 | 无 | | 接口传输加密 | 采用 动态 AES + RSA 加密请求 body 每一次请求秘钥都不同大幅度降低可破解性 | 无 | | 数据翻译 | 采用 注解 + jackson 序列化期间动态修改数据 数据进行翻译
支持多种模式: `映射翻译` `直接翻译` `其他扩展条件翻译` 接口化两步即可完成自定义扩展 内置多种翻译实现 | 无 | -| 多数据源框架 | 采用 dynamic-datasource 支持世面大部分数据库
通过yml配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源
支持spel表达式从请求头参数等条件切换数据源 | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差 | +| 多数据源框架 | 采用 dynamic-datasource 支持市面大部分数据库
通过yml配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源
支持spel表达式从请求头参数等条件切换数据源 | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差 | | 多数据源事务 | 采用 dynamic-datasource 支持多数据源不同种类的数据库事务回滚 | 不支持 | | 数据库连接池 | 采用 HikariCP Spring官方内置连接池 配置简单 以性能与稳定性闻名天下 | 采用 druid bug众多 社区维护差 活跃度低 配置众多繁琐性能一般 | | 数据库主键 | 采用 雪花ID 基于时间戳的 有序增长 唯一ID 再也不用为分库分表 数据合并主键冲突重复而发愁 | 采用 数据库自增ID 支持数据量有限 不支持多数据源主键唯一 | | WebSocket协议 | 基于 Spring 封装的 WebSocket 协议 扩展了Token鉴权与分布式会话同步 不再只是基于单机的废物 | 无 | +| SSE推送 | 采用 Spring SSE 实现 扩展了Token鉴权与分布式会话同步 | 无 | | 序列化 | 采用 Jackson Spring官方内置序列化 靠谱!!! | 采用 fastjson bugjson 远近闻名 | | 分布式幂等 | 参考美团GTIS防重系统简化实现(细节可看文档) | 手动编写注解基于aop实现 | | 分布式锁 | 采用 Lock4j 底层基于 Redisson | 无 | @@ -72,6 +73,7 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow
| 接口文档 | 采用 SpringDoc、javadoc 无注解零入侵基于java注释
只需把注释写好 无需再写一大堆的文档注解了 | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成 | | 校验框架 | 采用 Validation 支持注解与工具类校验 注解支持国际化 | 仅支持注解 且注解不支持国际化 | | Excel框架 | 采用 Alibaba EasyExcel 基于插件化
框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等 | 基于 POI 手写实现 功能有限 复杂 扩展性差 | +| 工作流支持 | 支持各种复杂审批 转办 委派 加减签 会签 或签 票签 等功能 | 无 | | 工具类框架 | 采用 Hutool、Lombok 上百种工具覆盖90%的使用需求 基于注解自动生成 get set 等简化框架大量代码 | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等 | | 监控框架 | 采用 SpringBoot-Admin 基于SpringBoot官方 actuator 探针机制
实时监控服务状态 框架还为其扩展了在线日志查看监控 | 无 | | 链路追踪 | 采用 Apache SkyWalking 还在为请求不知道去哪了 到哪出了问题而烦恼吗
用了它即可实时查看请求经过的每一处每一个节点 | 无 | diff --git a/pom.xml b/pom.xml index 900b61b..750673b 100644 --- a/pom.xml +++ b/pom.xml @@ -13,45 +13,46 @@ RuoYi-Vue-Plus多租户管理系统 - 5.2.0 - 3.2.6 + 5.2.2 + 3.2.9 UTF-8 UTF-8 17 3.5.16 - 2.5.0 + 2.6.0 0.15.0 - 5.2.3 - 3.3.4 + 4.0.2 2.3 1.38.0 3.5.7 3.9.1 - 5.8.27 + 5.8.31 4.10.0 3.2.3 - 3.31.0 + 3.34.1 2.2.7 4.3.1 - 2.14.4 - 1.0.1 - 1.3.6 + 1.1.2 + 1.4.4 0.2.0 - 1.18.32 + 1.18.34 1.76 1.16.6 2.7.0 + 2.3.15.Final 2.25.15 0.29.13 - 3.2.1 + 3.3.2 1.2.83 + + 8.7.2-20240808 - 7.0.0 + 7.0.1 3.2.2 @@ -155,26 +156,10 @@ ${lombok.version} - - org.apache.poi - poi - ${poi.version} - - - org.apache.poi - poi-ooxml - ${poi.version} - com.alibaba easyexcel ${easyexcel.version} - - - org.apache.poi - poi-ooxml-schemas - - @@ -307,12 +292,6 @@ ${snailjob.version} - - com.alibaba - transmittable-thread-local - ${alibaba-ttl.version} - - org.bouncycastle @@ -333,6 +312,28 @@ ${ip2region.version} + + io.undertow + undertow-core + ${undertow.version} + + + io.undertow + undertow-servlet + ${undertow.version} + + + io.undertow + undertow-websockets-jsr + ${undertow.version} + + + + commons-compress + org.apache.commons + 1.26.2 + + com.alibaba fastjson diff --git a/ruoyi-admin/Dockerfile b/ruoyi-admin/Dockerfile index 609b04a..737cbfc 100644 --- a/ruoyi-admin/Dockerfile +++ b/ruoyi-admin/Dockerfile @@ -1,7 +1,9 @@ +# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/ +FROM bellsoft/liberica-openjdk-debian:17.0.11-cds +#FROM bellsoft/liberica-openjdk-debian:21.0.3-cds #FROM findepi/graalvm:java17-native -FROM openjdk:17.0.2-oraclelinux8 -MAINTAINER Lion Li +LABEL maintainer="Lion Li" RUN mkdir -p /ruoyi/server/logs \ /ruoyi/server/temp \ diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index 610e9d7..9e97804 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -22,21 +22,28 @@ com.mysql mysql-connector-j - - - com.oracle.database.jdbc - ojdbc8 - - - - org.postgresql - postgresql - - - - com.microsoft.sqlserver - mssql-jdbc - + + + + + + + + + + + + + + + + + + + + + + org.dromara diff --git a/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java b/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java index d99af5b..b561693 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java @@ -1,6 +1,7 @@ package org.dromara.web.controller; import cn.dev33.satoken.annotation.SaIgnore; +import cn.dev33.satoken.exception.NotLoginException; import cn.hutool.core.codec.Base64; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; @@ -23,9 +24,9 @@ import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.social.config.properties.SocialLoginConfigProperties; import org.dromara.common.social.config.properties.SocialProperties; import org.dromara.common.social.utils.SocialUtils; +import org.dromara.common.sse.dto.SseMessageDto; +import org.dromara.common.sse.utils.SseMessageUtils; import org.dromara.common.tenant.helper.TenantHelper; -import org.dromara.common.websocket.dto.WebSocketMessageDto; -import org.dromara.common.websocket.utils.WebSocketUtils; import org.dromara.system.domain.bo.SysTenantBo; import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysTenantVo; @@ -101,11 +102,11 @@ public class AuthController { Long userId = LoginHelper.getUserId(); scheduledExecutorService.schedule(() -> { - WebSocketMessageDto dto = new WebSocketMessageDto(); + SseMessageDto dto = new SseMessageDto(); dto.setMessage("欢迎登录RuoYi-Vue-Plus后台管理系统"); - dto.setSessionKeys(List.of(userId)); - WebSocketUtils.publishMessage(dto); - }, 3, TimeUnit.SECONDS); + dto.setUserIds(List.of(userId)); + SseMessageUtils.publishMessage(dto); + }, 5, TimeUnit.SECONDS); return R.ok(loginVo); } @@ -194,8 +195,26 @@ public class AuthController { */ @GetMapping("/tenant/list") public R tenantList(HttpServletRequest request) throws Exception { + // 返回对象 + LoginTenantVo result = new LoginTenantVo(); + boolean enable = TenantHelper.isEnable(); + result.setTenantEnabled(enable); + // 如果未开启租户这直接返回 + if (!enable) { + return R.ok(result); + } + List tenantList = tenantService.queryList(new SysTenantBo()); List voList = MapstructUtils.convert(tenantList, TenantListVo.class); + try { + // 如果只超管返回所有租户 + if (LoginHelper.isSuperAdmin()) { + result.setVoList(voList); + return R.ok(result); + } + } catch (NotLoginException ignored) { + } + // 获取域名 String host; String referer = request.getHeader("referer"); @@ -208,11 +227,8 @@ public class AuthController { // 根据域名进行筛选 List list = StreamUtils.filter(voList, vo -> StringUtils.equals(vo.getDomain(), host)); - // 返回对象 - LoginTenantVo vo = new LoginTenantVo(); - vo.setVoList(CollUtil.isNotEmpty(list) ? list : voList); - vo.setTenantEnabled(TenantHelper.isEnable()); - return R.ok(vo); + result.setVoList(CollUtil.isNotEmpty(list) ? list : voList); + return R.ok(result); } } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java b/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java index a472404..07595e0 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java @@ -3,6 +3,8 @@ package org.dromara.web.listener; import cn.dev33.satoken.config.SaTokenConfig; import cn.dev33.satoken.listener.SaTokenListener; import cn.dev33.satoken.stp.SaLoginModel; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.convert.Convert; import cn.hutool.http.useragent.UserAgent; import cn.hutool.http.useragent.UserAgentUtil; import lombok.RequiredArgsConstructor; @@ -81,7 +83,10 @@ public class UserActionListener implements SaTokenListener { */ @Override public void doLogout(String loginType, Object loginId, String tokenValue) { - RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); + String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); + TenantHelper.dynamic(tenantId, () -> { + RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); + }); log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue); } @@ -90,7 +95,10 @@ public class UserActionListener implements SaTokenListener { */ @Override public void doKickout(String loginType, Object loginId, String tokenValue) { - RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); + String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); + TenantHelper.dynamic(tenantId, () -> { + RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); + }); log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue); } @@ -99,7 +107,10 @@ public class UserActionListener implements SaTokenListener { */ @Override public void doReplaced(String loginType, Object loginId, String tokenValue) { - RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); + String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); + TenantHelper.dynamic(tenantId, () -> { + RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); + }); log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue); } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java b/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java index af6e7f5..c7ad917 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java @@ -4,13 +4,14 @@ import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Opt; import cn.hutool.core.util.ObjectUtil; import com.baomidou.lock.annotation.Lock4j; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.zhyd.oauth.model.AuthUser; +import org.dromara.common.core.constant.CacheConstants; import org.dromara.common.core.constant.Constants; -import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.constant.TenantConstants; import org.dromara.common.core.domain.dto.RoleDTO; import org.dromara.common.core.domain.model.LoginUser; @@ -155,16 +156,13 @@ public class SysLoginService { loginUser.setUserType(user.getUserType()); loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId())); loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId())); - TenantHelper.dynamic(user.getTenantId(), () -> { - SysDeptVo dept = null; - if (ObjectUtil.isNotNull(user.getDeptId())) { - dept = deptService.selectDeptById(user.getDeptId()); - } - loginUser.setDeptName(ObjectUtil.isNull(dept) ? "" : dept.getDeptName()); - loginUser.setDeptCategory(ObjectUtil.isNull(dept) ? "" : dept.getDeptCategory()); - List roles = roleService.selectRolesByUserId(user.getUserId()); - loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class)); - }); + if (ObjectUtil.isNotNull(user.getDeptId())) { + Opt deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById); + loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY)); + loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY)); + } + List roles = roleService.selectRolesByUserId(user.getUserId()); + loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class)); return loginUser; } @@ -186,7 +184,7 @@ public class SysLoginService { * 登录校验 */ public void checkLogin(LoginType loginType, String tenantId, String username, Supplier supplier) { - String errorKey = GlobalConstants.PWD_ERR_CNT_KEY + username; + String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username; String loginFail = Constants.LOGIN_FAIL; // 获取用户登录错误次数,默认为0 (可自定义限制策略 例如: key + username + ip) diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java index 38fdc44..b5a2497 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java @@ -21,7 +21,6 @@ import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.tenant.helper.TenantHelper; -import org.dromara.system.domain.SysClient; import org.dromara.system.domain.SysUser; import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysUserVo; @@ -51,13 +50,12 @@ public class EmailAuthStrategy implements IAuthStrategy { String tenantId = loginBody.getTenantId(); String email = loginBody.getEmail(); String emailCode = loginBody.getEmailCode(); - - // 通过邮箱查找用户 - SysUserVo user = loadUserByEmail(tenantId, email); - - loginService.checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode)); - // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 - LoginUser loginUser = loginService.buildLoginUser(user); + LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> { + SysUserVo user = loadUserByEmail(email); + loginService.checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode)); + // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 + return loginService.buildLoginUser(user); + }); loginUser.setClientKey(client.getClientKey()); loginUser.setDeviceType(client.getDeviceType()); SaLoginModel model = new SaLoginModel(); @@ -89,18 +87,16 @@ public class EmailAuthStrategy implements IAuthStrategy { return code.equals(emailCode); } - private SysUserVo loadUserByEmail(String tenantId, String email) { - return TenantHelper.dynamic(tenantId, () -> { - SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getEmail, email)); - if (ObjectUtil.isNull(user)) { - log.info("登录用户:{} 不存在.", email); - throw new UserException("user.not.exists", email); - } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { - log.info("登录用户:{} 已被停用.", email); - throw new UserException("user.blocked", email); - } - return user; - }); + private SysUserVo loadUserByEmail(String email) { + SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getEmail, email)); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", email); + throw new UserException("user.not.exists", email); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", email); + throw new UserException("user.blocked", email); + } + return user; } } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java index cd33ea4..f28024f 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java @@ -24,7 +24,6 @@ import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.common.web.config.properties.CaptchaProperties; -import org.dromara.system.domain.SysClient; import org.dromara.system.domain.SysUser; import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysUserVo; @@ -63,11 +62,12 @@ public class PasswordAuthStrategy implements IAuthStrategy { if (captchaEnabled) { validateCaptcha(tenantId, username, code, uuid); } - - SysUserVo user = loadUserByUsername(tenantId, username); - loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword())); - // 此处可根据登录用户的数据不同 自行创建 loginUser - LoginUser loginUser = loginService.buildLoginUser(user); + LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> { + SysUserVo user = loadUserByUsername(username); + loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword())); + // 此处可根据登录用户的数据不同 自行创建 loginUser + return loginService.buildLoginUser(user); + }); loginUser.setClientKey(client.getClientKey()); loginUser.setDeviceType(client.getDeviceType()); SaLoginModel model = new SaLoginModel(); @@ -95,7 +95,7 @@ public class PasswordAuthStrategy implements IAuthStrategy { * @param uuid 唯一标识 */ private void validateCaptcha(String tenantId, String username, String code, String uuid) { - String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, ""); + String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, ""); String captcha = RedisUtils.getCacheObject(verifyKey); RedisUtils.deleteObject(verifyKey); if (captcha == null) { @@ -108,18 +108,16 @@ public class PasswordAuthStrategy implements IAuthStrategy { } } - private SysUserVo loadUserByUsername(String tenantId, String username) { - return TenantHelper.dynamic(tenantId, () -> { - SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getUserName, username)); - if (ObjectUtil.isNull(user)) { - log.info("登录用户:{} 不存在.", username); - throw new UserException("user.not.exists", username); - } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { - log.info("登录用户:{} 已被停用.", username); - throw new UserException("user.blocked", username); - } - return user; - }); + private SysUserVo loadUserByUsername(String username) { + SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getUserName, username)); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", username); + throw new UserException("user.not.exists", username); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", username); + throw new UserException("user.blocked", username); + } + return user; } } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java index f883632..89f8462 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java @@ -21,7 +21,6 @@ import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.tenant.helper.TenantHelper; -import org.dromara.system.domain.SysClient; import org.dromara.system.domain.SysUser; import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysUserVo; @@ -51,13 +50,12 @@ public class SmsAuthStrategy implements IAuthStrategy { String tenantId = loginBody.getTenantId(); String phonenumber = loginBody.getPhonenumber(); String smsCode = loginBody.getSmsCode(); - - // 通过手机号查找用户 - SysUserVo user = loadUserByPhonenumber(tenantId, phonenumber); - - loginService.checkLogin(LoginType.SMS, tenantId, user.getUserName(), () -> !validateSmsCode(tenantId, phonenumber, smsCode)); - // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 - LoginUser loginUser = loginService.buildLoginUser(user); + LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> { + SysUserVo user = loadUserByPhonenumber(phonenumber); + loginService.checkLogin(LoginType.SMS, tenantId, user.getUserName(), () -> !validateSmsCode(tenantId, phonenumber, smsCode)); + // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 + return loginService.buildLoginUser(user); + }); loginUser.setClientKey(client.getClientKey()); loginUser.setDeviceType(client.getDeviceType()); SaLoginModel model = new SaLoginModel(); @@ -89,18 +87,16 @@ public class SmsAuthStrategy implements IAuthStrategy { return code.equals(smsCode); } - private SysUserVo loadUserByPhonenumber(String tenantId, String phonenumber) { - return TenantHelper.dynamic(tenantId, () -> { - SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getPhonenumber, phonenumber)); - if (ObjectUtil.isNull(user)) { - log.info("登录用户:{} 不存在.", phonenumber); - throw new UserException("user.not.exists", phonenumber); - } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { - log.info("登录用户:{} 已被停用.", phonenumber); - throw new UserException("user.blocked", phonenumber); - } - return user; - }); + private SysUserVo loadUserByPhonenumber(String phonenumber) { + SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getPhonenumber, phonenumber)); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", phonenumber); + throw new UserException("user.not.exists", phonenumber); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", phonenumber); + throw new UserException("user.blocked", phonenumber); + } + return user; } } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java index 6694165..8463026 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java @@ -16,6 +16,7 @@ import org.dromara.common.core.domain.model.SocialLoginBody; import org.dromara.common.core.enums.UserStatus; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.user.UserException; +import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.satoken.utils.LoginHelper; @@ -83,7 +84,7 @@ public class SocialAuthStrategy implements IAuthStrategy { } SysSocialVo social; if (TenantHelper.isEnable()) { - Optional opt = list.stream().filter(x -> x.getTenantId().equals(loginBody.getTenantId())).findAny(); + Optional opt = StreamUtils.findAny(list, x -> x.getTenantId().equals(loginBody.getTenantId())); if (opt.isEmpty()) { throw new ServiceException("对不起,你没有权限登录当前租户!"); } @@ -91,11 +92,11 @@ public class SocialAuthStrategy implements IAuthStrategy { } else { social = list.get(0); } - // 查找用户 - SysUserVo user = loadUser(social.getTenantId(), social.getUserId()); - - // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 - LoginUser loginUser = loginService.buildLoginUser(user); + LoginUser loginUser = TenantHelper.dynamic(social.getTenantId(), () -> { + SysUserVo user = loadUser(social.getUserId()); + // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 + return loginService.buildLoginUser(user); + }); loginUser.setClientKey(client.getClientKey()); loginUser.setDeviceType(client.getDeviceType()); SaLoginModel model = new SaLoginModel(); @@ -115,18 +116,16 @@ public class SocialAuthStrategy implements IAuthStrategy { return loginVo; } - private SysUserVo loadUser(String tenantId, Long userId) { - return TenantHelper.dynamic(tenantId, () -> { - SysUserVo user = userMapper.selectVoById(userId); - if (ObjectUtil.isNull(user)) { - log.info("登录用户:{} 不存在.", ""); - throw new UserException("user.not.exists", ""); - } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { - log.info("登录用户:{} 已被停用.", ""); - throw new UserException("user.blocked", ""); - } - return user; - }); + private SysUserVo loadUser(Long userId) { + SysUserVo user = userMapper.selectVoById(userId); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", ""); + throw new UserException("user.not.exists", ""); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", ""); + throw new UserException("user.blocked", ""); + } + return user; } } diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index cccd9cd..5e20dae 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -5,19 +5,22 @@ spring.boot.admin.client: url: http://localhost:9090/admin instance: service-host-type: IP + metadata: + username: ${spring.boot.admin.client.username} + userpassword: ${spring.boot.admin.client.password} username: ruoyi password: 123456 --- # snail-job 配置 snail-job: - enabled: false + enabled: true # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 group: "ruoyi_group" # SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表 token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" server: host: 127.0.0.1 - port: 1788 + port: 17888 # 详见 script/sql/snail_job.sql `sj_namespace` 表 namespace: ${spring.profiles.active} # 随主应用端口飘逸 @@ -42,17 +45,17 @@ spring: driverClassName: com.mysql.cj.jdbc.Driver # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) - url: jdbc:mysql://localhost:3306/zaojia?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true username: root password: root # 从库数据源 -# slave: -# lazy: true -# type: ${spring.datasource.type} -# driverClassName: com.mysql.cj.jdbc.Driver -# url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true -# username: -# password: + slave: + lazy: true + type: ${spring.datasource.type} + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + username: + password: # oracle: # type: ${spring.datasource.type} # driverClassName: oracle.jdbc.OracleDriver @@ -96,8 +99,8 @@ spring.data: port: 6379 # 数据库索引 database: 0 - # 密码(如没有密码请注释掉) - # password: + # redis 密码必须配置 + password: ruoyi123 # 连接超时时间 timeout: 10s # 是否开启ssl diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml index 0192d59..2823bba 100644 --- a/ruoyi-admin/src/main/resources/application-prod.yml +++ b/ruoyi-admin/src/main/resources/application-prod.yml @@ -8,19 +8,22 @@ spring.boot.admin.client: url: http://localhost:9090/admin instance: service-host-type: IP + metadata: + username: ${spring.boot.admin.client.username} + userpassword: ${spring.boot.admin.client.password} username: ruoyi password: 123456 --- # snail-job 配置 snail-job: - enabled: false + enabled: true # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 group: "ruoyi_group" # SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表 token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" server: host: 127.0.0.1 - port: 1788 + port: 17888 # 详见 script/sql/snail_job.sql `sj_namespace` 表 namespace: ${spring.profiles.active} # 随主应用端口飘逸 @@ -99,8 +102,8 @@ spring.data: port: 6379 # 数据库索引 database: 0 - # 密码(如没有密码请注释掉) - # password: + # redis 密码必须配置 + password: ruoyi123 # 连接超时时间 timeout: 10s # 是否开启ssl diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 27de286..5d94bef 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -121,9 +121,6 @@ security: # swagger 文档配置 - /*/api-docs - /*/api-docs/** - # actuator 监控配置 - - /actuator - - /actuator/** # 多租户配置 tenant: @@ -259,10 +256,15 @@ management: logfile: external-file: ./logs/sys-console.log +--- # 默认/推荐使用sse推送 +sse: + enabled: true + path: /resource/sse + --- # websocket websocket: # 如果关闭 需要和前端开关一起关闭 - enabled: true + enabled: false # 路径 path: /resource/websocket # 设置访问源地址 @@ -270,6 +272,10 @@ websocket: --- #flowable配置 flowable: + # 开关 用于启动/停用工作流 + enabled: true + process.enabled: ${flowable.enabled} + eventregistry.enabled: ${flowable.enabled} async-executor-activate: false #关闭定时任务JOB # 将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。 database-schema-update: true diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index 45493d3..2930fd0 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -33,6 +33,7 @@ ruoyi-common-encrypt ruoyi-common-tenant ruoyi-common-websocket + ruoyi-common-sse ruoyi-common diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml index d546275..455408d 100644 --- a/ruoyi-common/ruoyi-common-bom/pom.xml +++ b/ruoyi-common/ruoyi-common-bom/pom.xml @@ -14,7 +14,7 @@ - 5.2.0 + 5.2.2 @@ -172,6 +172,13 @@ ${revision} + + + org.dromara + ruoyi-common-sse + ${revision} + + diff --git a/ruoyi-common/ruoyi-common-core/pom.xml b/ruoyi-common/ruoyi-common-core/pom.xml index 5925c9b..ad37e90 100644 --- a/ruoyi-common/ruoyi-common-core/pom.xml +++ b/ruoyi-common/ruoyi-common-core/pom.xml @@ -94,11 +94,6 @@ ip2region - - com.alibaba - transmittable-thread-local - - diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheConstants.java index 67bc8e4..ceb8370 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheConstants.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheConstants.java @@ -22,4 +22,9 @@ public interface CacheConstants { */ String SYS_DICT_KEY = "sys_dict:"; + /** + * 登录账户密码错误次数 redis key + */ + String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/GlobalConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/GlobalConstants.java index ae9bc2e..5352b11 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/GlobalConstants.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/GlobalConstants.java @@ -27,11 +27,6 @@ public interface GlobalConstants { */ String RATE_LIMIT_KEY = GLOBAL_REDIS_KEY + "rate_limit:"; - /** - * 登录账户密码错误次数 redis key - */ - String PWD_ERR_CNT_KEY = GLOBAL_REDIS_KEY + "pwd_err_cnt:"; - /** * 三方认证 redis key */ diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/UserConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/UserConstants.java index 6f3b0b9..76f6dd4 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/UserConstants.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/UserConstants.java @@ -67,6 +67,16 @@ public interface UserConstants { */ String DICT_NORMAL = "0"; + /** + * 通用存在标志 + */ + String DEL_FLAG_NORMAL = "0"; + + /** + * 通用删除标志 + */ + String DEL_FLAG_REMOVED = "2"; + /** * 是否为系统默认(是) */ diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java index 0f2878d..43aef28 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java @@ -66,4 +66,20 @@ public interface UserService { * @return 用户ids */ List selectUserIdsByRoleIds(List roleIds); + + /** + * 通过角色ID查询用户 + * + * @param roleIds 角色ids + * @return 用户 + */ + List selectUsersByRoleIds(List roleIds); + + /** + * 通过部门ID查询用户 + * + * @param deptIds 部门ids + * @return 用户 + */ + List selectUsersByDeptIds(List deptIds); } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StreamUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StreamUtils.java index 967612e..1342deb 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StreamUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StreamUtils.java @@ -7,6 +7,7 @@ import lombok.NoArgsConstructor; import java.util.*; import java.util.function.BiFunction; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -34,6 +35,34 @@ public class StreamUtils { return collection.stream().filter(function).collect(Collectors.toList()); } + /** + * 找到流中满足条件的第一个元素 + * + * @param collection 需要查询的集合 + * @param function 过滤方法 + * @return 找到符合条件的第一个元素,没有则返回null + */ + public static E findFirst(Collection collection, Predicate function) { + if (CollUtil.isEmpty(collection)) { + return null; + } + return collection.stream().filter(function).findFirst().orElse(null); + } + + /** + * 找到流中任意一个满足条件的元素 + * + * @param collection 需要查询的集合 + * @param function 过滤方法 + * @return 找到符合条件的任意一个元素,没有则返回null + */ + public static Optional findAny(Collection collection, Predicate function) { + if (CollUtil.isEmpty(collection)) { + return Optional.empty(); + } + return collection.stream().filter(function).findAny(); + } + /** * 将collection拼接 * diff --git a/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/handler/OpenApiHandler.java b/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/handler/OpenApiHandler.java index a35cc64..56b7369 100644 --- a/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/handler/OpenApiHandler.java +++ b/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/handler/OpenApiHandler.java @@ -11,6 +11,7 @@ import io.swagger.v3.oas.models.Paths; import io.swagger.v3.oas.models.tags.Tag; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.dromara.common.core.utils.StreamUtils; import org.springdoc.core.customizers.OpenApiBuilderCustomizer; import org.springdoc.core.customizers.ServerBaseUrlCustomizer; import org.springdoc.core.properties.SpringDocConfigProperties; @@ -230,7 +231,7 @@ public class OpenApiHandler extends OpenAPIService { .flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet()); methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class)); if (!CollectionUtils.isEmpty(methodTags)) { - tagsStr.addAll(methodTags.stream().map(tag -> propertyResolverUtils.resolve(tag.name(), locale)).collect(Collectors.toSet())); + tagsStr.addAll(StreamUtils.toSet(methodTags, tag -> propertyResolverUtils.resolve(tag.name(), locale))); List allTags = new ArrayList<>(methodTags); addTags(allTags, tags, locale); } diff --git a/ruoyi-common/ruoyi-common-excel/pom.xml b/ruoyi-common/ruoyi-common-excel/pom.xml index dd4a5ee..14b9410 100644 --- a/ruoyi-common/ruoyi-common-excel/pom.xml +++ b/ruoyi-common/ruoyi-common-excel/pom.xml @@ -25,6 +25,11 @@ com.alibaba easyexcel + + commons-compress + org.apache.commons + 1.26.2 + diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java index 7c0a48b..7c7721c 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java @@ -107,7 +107,7 @@ public class CellMergeStrategy extends AbstractMergeStrategy implements Workbook } if (!cellValue.equals(val)) { - if ((i - repeatCell.getCurrent() > 1) && isMerge(list, i, field)) { + if ((i - repeatCell.getCurrent() > 1)) { cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); } map.put(field, new RepeatCell(val, i)); @@ -115,6 +115,11 @@ public class CellMergeStrategy extends AbstractMergeStrategy implements Workbook if (i > repeatCell.getCurrent() && isMerge(list, i, field)) { cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); } + } else if (!isMerge(list, i, field)) { + if ((i - repeatCell.getCurrent() > 1)) { + cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); + } + map.put(field, new RepeatCell(val, i)); } } } diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/MailConfig.java b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/MailConfig.java index 1b51c27..0ea3007 100644 --- a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/MailConfig.java +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/MailConfig.java @@ -1,7 +1,7 @@ package org.dromara.common.mail.config; +import cn.hutool.extra.mail.MailAccount; import org.dromara.common.mail.config.properties.MailProperties; -import org.dromara.common.mail.utils.MailAccount; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java index 040cc57..a28701f 100644 --- a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java @@ -5,6 +5,9 @@ import cn.hutool.core.io.IoUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.mail.JakartaMail; +import cn.hutool.extra.mail.JakartaUserPassAuthenticator; +import cn.hutool.extra.mail.MailAccount; import jakarta.mail.Authenticator; import jakarta.mail.Session; import lombok.AccessLevel; @@ -17,7 +20,7 @@ import java.io.InputStream; import java.util.Collection; import java.util.List; import java.util.Map; - +import java.util.Map.Entry; /** * 邮件工具类 @@ -385,7 +388,7 @@ public class MailUtils { public static Session getSession(MailAccount mailAccount, boolean isSingleton) { Authenticator authenticator = null; if (mailAccount.isAuth()) { - authenticator = new UserPassAuthenticator(mailAccount.getUser(), mailAccount.getPass()); + authenticator = new JakartaUserPassAuthenticator(mailAccount.getUser(), mailAccount.getPass()); } return isSingleton ? Session.getDefaultInstance(mailAccount.getSmtpProps(), authenticator) // @@ -412,7 +415,7 @@ public class MailUtils { */ private static String send(MailAccount mailAccount, boolean useGlobalSession, Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, boolean isHtml, File... files) { - final Mail mail = Mail.create(mailAccount).setUseGlobalSession(useGlobalSession); + final JakartaMail mail = JakartaMail.create(mailAccount).setUseGlobalSession(useGlobalSession); // 可选抄送人 if (CollUtil.isNotEmpty(ccs)) { @@ -431,7 +434,7 @@ public class MailUtils { // 图片 if (MapUtil.isNotEmpty(imageMap)) { - for (Map.Entry entry : imageMap.entrySet()) { + for (Entry entry : imageMap.entrySet()) { mail.addImage(entry.getKey(), entry.getValue()); // 关闭流 IoUtil.close(entry.getValue()); @@ -463,5 +466,4 @@ public class MailUtils { return result; } // ------------------------------------------------------------------------------------------------------------------------ Private method end - } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/annotation/DataColumn.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/annotation/DataColumn.java index aca470f..2879b9d 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/annotation/DataColumn.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/annotation/DataColumn.java @@ -3,9 +3,10 @@ package org.dromara.common.mybatis.annotation; import java.lang.annotation.*; /** - * 数据权限 - * + * 数据权限注解,用于标记数据权限的占位符关键字和替换值 + *

* 一个注解只能对应一个模板 + *

* * @author Lion Li * @version 3.5.0 @@ -16,13 +17,24 @@ import java.lang.annotation.*; public @interface DataColumn { /** - * 占位符关键字 + * 数据权限模板的占位符关键字,默认为 "deptName" + * + * @return 占位符关键字数组 */ String[] key() default "deptName"; /** - * 占位符替换值 + * 数据权限模板的占位符替换值,默认为 "dept_id" + * + * @return 占位符替换值数组 */ String[] value() default "dept_id"; + /** + * 权限标识符 用于通过菜单权限标识符来获取数据权限 + * 拥有此标识符的角色 将不会拼接此角色的数据过滤sql + * + * @return 权限标识符 + */ + String permission() default ""; } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/annotation/DataPermission.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/annotation/DataPermission.java index f4351e3..f5f22d5 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/annotation/DataPermission.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/annotation/DataPermission.java @@ -3,7 +3,7 @@ package org.dromara.common.mybatis.annotation; import java.lang.annotation.*; /** - * 数据权限组 + * 数据权限组注解,用于标记数据权限配置数组 * * @author Lion Li * @version 3.5.0 @@ -13,6 +13,18 @@ import java.lang.annotation.*; @Documented public @interface DataPermission { + /** + * 数据权限配置数组,用于指定数据权限的占位符关键字和替换值 + * + * @return 数据权限配置数组 + */ DataColumn[] value(); + /** + * 权限拼接标识符(用于指定连接语句的sql符号) + * 如不填 默认 select 用 OR 其他语句用 AND + * 内容 OR 或者 AND + */ + String joinStr() default ""; + } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/domain/BaseEntity.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/domain/BaseEntity.java index 820b49a..13a7941 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/domain/BaseEntity.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/domain/BaseEntity.java @@ -17,7 +17,6 @@ import java.util.Map; * * @author Lion Li */ - @Data public class BaseEntity implements Serializable { diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java index 3889250..956be9f 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java @@ -12,6 +12,7 @@ import com.baomidou.mybatisplus.extension.toolkit.Db; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StreamUtils; import java.io.Serializable; import java.util.Collection; @@ -34,20 +35,38 @@ public interface BaseMapperPlus extends BaseMapper { Log log = LogFactory.getLog(BaseMapperPlus.class); + /** + * 获取当前实例对象关联的泛型类型 V 的 Class 对象 + * + * @return 返回当前实例对象关联的泛型类型 V 的 Class 对象 + */ default Class currentVoClass() { return (Class) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[1]; } + /** + * 获取当前实例对象关联的泛型类型 T 的 Class 对象 + * + * @return 返回当前实例对象关联的泛型类型 T 的 Class 对象 + */ default Class currentModelClass() { return (Class) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[0]; } + /** + * 使用默认的查询条件查询并返回结果列表 + * + * @return 返回查询结果的列表 + */ default List selectList() { return this.selectList(new QueryWrapper<>()); } /** - * 批量插入 + * 批量插入实体对象集合 + * + * @param entityList 实体对象集合 + * @return 插入操作是否成功的布尔值 */ default boolean insertBatch(Collection entityList) { Db.saveBatch(entityList); @@ -56,7 +75,10 @@ public interface BaseMapperPlus extends BaseMapper { } /** - * 批量更新 + * 批量根据ID更新实体对象集合 + * + * @param entityList 实体对象集合 + * @return 更新操作是否成功的布尔值 */ default boolean updateBatchById(Collection entityList) { Db.updateBatchById(entityList); @@ -65,7 +87,10 @@ public interface BaseMapperPlus extends BaseMapper { } /** - * 批量插入或更新 + * 批量插入或更新实体对象集合 + * + * @param entityList 实体对象集合 + * @return 插入或更新操作是否成功的布尔值 */ default boolean insertOrUpdateBatch(Collection entityList) { Db.saveOrUpdateBatch(entityList); @@ -74,7 +99,11 @@ public interface BaseMapperPlus extends BaseMapper { } /** - * 批量插入(包含限制条数) + * 批量插入实体对象集合并指定批处理大小 + * + * @param entityList 实体对象集合 + * @param batchSize 批处理大小 + * @return 插入操作是否成功的布尔值 */ default boolean insertBatch(Collection entityList, int batchSize) { Db.saveBatch(entityList, batchSize); @@ -83,7 +112,11 @@ public interface BaseMapperPlus extends BaseMapper { } /** - * 批量更新(包含限制条数) + * 批量根据ID更新实体对象集合并指定批处理大小 + * + * @param entityList 实体对象集合 + * @param batchSize 批处理大小 + * @return 更新操作是否成功的布尔值 */ default boolean updateBatchById(Collection entityList, int batchSize) { Db.updateBatchById(entityList, batchSize); @@ -92,7 +125,11 @@ public interface BaseMapperPlus extends BaseMapper { } /** - * 批量插入或更新(包含限制条数) + * 批量插入或更新实体对象集合并指定批处理大小 + * + * @param entityList 实体对象集合 + * @param batchSize 批处理大小 + * @return 插入或更新操作是否成功的布尔值 */ default boolean insertOrUpdateBatch(Collection entityList, int batchSize) { Db.saveOrUpdateBatch(entityList, batchSize); @@ -100,12 +137,23 @@ public interface BaseMapperPlus extends BaseMapper { return true; } + /** + * 根据ID查询单个VO对象 + * + * @param id 主键ID + * @return 查询到的单个VO对象 + */ default V selectVoById(Serializable id) { return selectVoById(id, this.currentVoClass()); } /** - * 根据 ID 查询 + * 根据ID查询单个VO对象并将其转换为指定的VO类 + * + * @param id 主键ID + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @return 查询到的单个VO对象,经过转换为指定的VO类后返回 */ default C selectVoById(Serializable id, Class voClass) { T obj = this.selectById(id); @@ -115,12 +163,23 @@ public interface BaseMapperPlus extends BaseMapper { return MapstructUtils.convert(obj, voClass); } + /** + * 根据ID集合批量查询VO对象列表 + * + * @param idList 主键ID集合 + * @return 查询到的VO对象列表 + */ default List selectVoBatchIds(Collection idList) { return selectVoBatchIds(idList, this.currentVoClass()); } /** - * 查询(根据ID 批量查询) + * 根据ID集合批量查询实体对象列表,并将其转换为指定的VO对象列表 + * + * @param idList 主键ID集合 + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @return 查询到的VO对象列表,经过转换为指定的VO类后返回 */ default List selectVoBatchIds(Collection idList, Class voClass) { List list = this.selectBatchIds(idList); @@ -130,12 +189,23 @@ public interface BaseMapperPlus extends BaseMapper { return MapstructUtils.convert(list, voClass); } + /** + * 根据查询条件Map查询VO对象列表 + * + * @param map 查询条件Map + * @return 查询到的VO对象列表 + */ default List selectVoByMap(Map map) { return selectVoByMap(map, this.currentVoClass()); } /** - * 查询(根据 columnMap 条件) + * 根据查询条件Map查询实体对象列表,并将其转换为指定的VO对象列表 + * + * @param map 查询条件Map + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @return 查询到的VO对象列表,经过转换为指定的VO类后返回 */ default List selectVoByMap(Map map, Class voClass) { List list = this.selectByMap(map); @@ -145,23 +215,47 @@ public interface BaseMapperPlus extends BaseMapper { return MapstructUtils.convert(list, voClass); } + /** + * 根据条件查询单个VO对象 + * + * @param wrapper 查询条件Wrapper + * @return 查询到的单个VO对象 + */ default V selectVoOne(Wrapper wrapper) { return selectVoOne(wrapper, this.currentVoClass()); } + /** + * 根据条件查询单个VO对象,并根据需要决定是否抛出异常 + * + * @param wrapper 查询条件Wrapper + * @param throwEx 是否抛出异常的标志 + * @return 查询到的单个VO对象 + */ default V selectVoOne(Wrapper wrapper, boolean throwEx) { return selectVoOne(wrapper, this.currentVoClass(), throwEx); } /** - * 根据 entity 条件,查询一条记录 + * 根据条件查询单个VO对象,并指定返回的VO对象的类型 + * + * @param wrapper 查询条件Wrapper + * @param voClass 返回的VO对象的Class对象 + * @param 返回的VO对象的类型 + * @return 查询到的单个VO对象,经过类型转换为指定的VO类后返回 */ default C selectVoOne(Wrapper wrapper, Class voClass) { return selectVoOne(wrapper, voClass, true); } /** - * 根据 entity 条件,查询一条记录 + * 根据条件查询单个实体对象,并将其转换为指定的VO对象 + * + * @param wrapper 查询条件Wrapper + * @param voClass 要转换的VO类的Class对象 + * @param throwEx 是否抛出异常的标志 + * @param VO类的类型 + * @return 查询到的单个VO对象,经过转换为指定的VO类后返回 */ default C selectVoOne(Wrapper wrapper, Class voClass, boolean throwEx) { T obj = this.selectOne(wrapper, throwEx); @@ -171,16 +265,32 @@ public interface BaseMapperPlus extends BaseMapper { return MapstructUtils.convert(obj, voClass); } + /** + * 查询所有VO对象列表 + * + * @return 查询到的VO对象列表 + */ default List selectVoList() { return selectVoList(new QueryWrapper<>(), this.currentVoClass()); } + /** + * 根据条件查询VO对象列表 + * + * @param wrapper 查询条件Wrapper + * @return 查询到的VO对象列表 + */ default List selectVoList(Wrapper wrapper) { return selectVoList(wrapper, this.currentVoClass()); } /** - * 根据 entity 条件,查询全部记录 + * 根据条件查询实体对象列表,并将其转换为指定的VO对象列表 + * + * @param wrapper 查询条件Wrapper + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @return 查询到的VO对象列表,经过转换为指定的VO类后返回 */ default List selectVoList(Wrapper wrapper, Class voClass) { List list = this.selectList(wrapper); @@ -190,15 +300,31 @@ public interface BaseMapperPlus extends BaseMapper { return MapstructUtils.convert(list, voClass); } + /** + * 根据条件分页查询VO对象列表 + * + * @param page 分页信息 + * @param wrapper 查询条件Wrapper + * @return 查询到的VO对象分页列表 + */ default

> P selectVoPage(IPage page, Wrapper wrapper) { return selectVoPage(page, wrapper, this.currentVoClass()); } /** - * 分页查询VO + * 根据条件分页查询实体对象列表,并将其转换为指定的VO对象分页列表 + * + * @param page 分页信息 + * @param wrapper 查询条件Wrapper + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @param

VO对象分页列表的类型 + * @return 查询到的VO对象分页列表,经过转换为指定的VO类后返回 */ default > P selectVoPage(IPage page, Wrapper wrapper, Class voClass) { + // 根据条件分页查询实体对象列表 List list = this.selectList(page, wrapper); + // 创建一个新的VO对象分页列表,并设置分页信息 IPage voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal()); if (CollUtil.isEmpty(list)) { return (P) voPage; @@ -207,8 +333,16 @@ public interface BaseMapperPlus extends BaseMapper { return (P) voPage; } + /** + * 根据条件查询符合条件的对象,并将其转换为指定类型的对象列表 + * + * @param wrapper 查询条件Wrapper + * @param mapper 转换函数,用于将查询到的对象转换为指定类型的对象 + * @param 要转换的对象的类型 + * @return 查询到的符合条件的对象列表,经过转换为指定类型的对象后返回 + */ default List selectObjs(Wrapper wrapper, Function mapper) { - return this.selectObjs(wrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList()); + return StreamUtils.toList(this.selectObjs(wrapper), mapper); } } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java index 40b7530..6ca9b27 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java @@ -4,10 +4,10 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.metadata.OrderItem; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.Data; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.sql.SqlUtil; -import lombok.Data; import java.io.Serial; import java.io.Serializable; @@ -19,7 +19,6 @@ import java.util.List; * * @author Lion Li */ - @Data public class PageQuery implements Serializable { @@ -56,6 +55,9 @@ public class PageQuery implements Serializable { */ public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE; + /** + * 构建分页对象 + */ public Page build() { Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM); Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE); diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java index a4b6799..8ecfb54 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java @@ -14,7 +14,6 @@ import java.util.List; * * @author Lion Li */ - @Data @NoArgsConstructor public class TableDataInfo implements Serializable { @@ -53,6 +52,9 @@ public class TableDataInfo implements Serializable { this.total = total; } + /** + * 根据分页对象构建表格分页数据对象 + */ public static TableDataInfo build(IPage page) { TableDataInfo rspData = new TableDataInfo<>(); rspData.setCode(HttpStatus.HTTP_OK); @@ -62,6 +64,9 @@ public class TableDataInfo implements Serializable { return rspData; } + /** + * 根据数据列表构建表格分页数据对象 + */ public static TableDataInfo build(List list) { TableDataInfo rspData = new TableDataInfo<>(); rspData.setCode(HttpStatus.HTTP_OK); @@ -71,6 +76,9 @@ public class TableDataInfo implements Serializable { return rspData; } + /** + * 构建表格分页数据对象 + */ public static TableDataInfo build() { TableDataInfo rspData = new TableDataInfo<>(); rspData.setCode(HttpStatus.HTTP_OK); diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/enums/DataBaseType.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/enums/DataBaseType.java index 93487e9..5084424 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/enums/DataBaseType.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/enums/DataBaseType.java @@ -1,8 +1,8 @@ package org.dromara.common.mybatis.enums; -import org.dromara.common.core.utils.StringUtils; import lombok.AllArgsConstructor; import lombok.Getter; +import org.dromara.common.core.utils.StringUtils; /** * 数据库类型 @@ -33,8 +33,17 @@ public enum DataBaseType { */ SQL_SERVER("Microsoft SQL Server"); + /** + * 数据库类型 + */ private final String type; + /** + * 根据数据库产品名称查找对应的数据库类型 + * + * @param databaseProductName 数据库产品名称 + * @return 对应的数据库类型枚举值,如果未找到则返回 null + */ public static DataBaseType find(String databaseProductName) { if (StringUtils.isBlank(databaseProductName)) { return null; diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/enums/DataScopeType.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/enums/DataScopeType.java index 9ea66b0..981bd42 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/enums/DataScopeType.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/enums/DataScopeType.java @@ -1,19 +1,22 @@ package org.dromara.common.mybatis.enums; -import org.dromara.common.core.utils.StringUtils; import lombok.AllArgsConstructor; import lombok.Getter; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.helper.DataPermissionHelper; /** - * 数据权限类型 - *

- * 语法支持 spel 模板表达式 + * 数据权限类型枚举 *

- * 内置数据 user 当前用户 内容参考 LoginUser - * 如需扩展数据 可使用 {@link DataPermissionHelper} 操作 - * 内置服务 sdss 系统数据权限服务 内容参考 SysDataScopeService - * 如需扩展更多自定义服务 可以参考 sdss 自行编写 + * 支持使用 SpEL 模板表达式定义 SQL 查询条件 + * 内置数据: + * - {@code user}: 当前登录用户信息,参考 {@link LoginUser} + * 内置服务: + * - {@code sdss}: 系统数据权限服务,参考 ISysDataScopeService + * 如需扩展数据,可以通过 {@link DataPermissionHelper} 进行操作 + * 如需扩展服务,可以通过 ISysDataScopeService 自行编写 + *

* * @author Lion Li * @version 3.5.0 @@ -50,15 +53,21 @@ public enum DataScopeType { private final String code; /** - * 语法 采用 spel 模板表达式 + * SpEL 模板表达式,用于构建 SQL 查询条件 */ private final String sqlTemplate; /** - * 不满足 sqlTemplate 则填充 + * 如果不满足 {@code sqlTemplate} 的条件,则使用此默认 SQL 表达式 */ private final String elseSql; + /** + * 根据枚举代码查找对应的枚举值 + * + * @param code 枚举代码 + * @return 对应的枚举值,如果未找到则返回 null + */ public static DataScopeType findCode(String code) { if (StringUtils.isBlank(code)) { return null; diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java index a66908f..7d44d26 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java @@ -3,12 +3,12 @@ package org.dromara.common.mybatis.handler; import cn.hutool.core.util.ObjectUtil; import cn.hutool.http.HttpStatus; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.reflection.MetaObject; import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.mybatis.core.domain.BaseEntity; import org.dromara.common.satoken.utils.LoginHelper; -import lombok.extern.slf4j.Slf4j; -import org.apache.ibatis.reflection.MetaObject; import java.util.Date; @@ -21,45 +21,63 @@ import java.util.Date; @Slf4j public class InjectionMetaObjectHandler implements MetaObjectHandler { + /** + * 插入填充方法,用于在插入数据时自动填充实体对象中的创建时间、更新时间、创建人、更新人等信息 + * + * @param metaObject 元对象,用于获取原始对象并进行填充 + */ @Override public void insertFill(MetaObject metaObject) { try { if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) { + // 获取当前时间作为创建时间和更新时间,如果创建时间不为空,则使用创建时间,否则使用当前时间 Date current = ObjectUtil.isNotNull(baseEntity.getCreateTime()) ? baseEntity.getCreateTime() : new Date(); baseEntity.setCreateTime(current); baseEntity.setUpdateTime(current); + + // 如果创建人为空,则填充当前登录用户的信息 if (ObjectUtil.isNull(baseEntity.getCreateBy())) { LoginUser loginUser = getLoginUser(); if (ObjectUtil.isNotNull(loginUser)) { Long userId = loginUser.getUserId(); - // 当前已登录 且 创建人为空 则填充 + // 填充创建人、更新人和创建部门信息 baseEntity.setCreateBy(userId); - // 当前已登录 且 更新人为空 则填充 baseEntity.setUpdateBy(userId); baseEntity.setCreateDept(ObjectUtil.isNotNull(baseEntity.getCreateDept()) ? baseEntity.getCreateDept() : loginUser.getDeptId()); } } + } else { + Date date = new Date(); + this.strictInsertFill(metaObject, "createTime", Date.class, date); + this.strictInsertFill(metaObject, "updateTime", Date.class, date); } } catch (Exception e) { throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); } } + /** + * 更新填充方法,用于在更新数据时自动填充实体对象中的更新时间和更新人信息 + * + * @param metaObject 元对象,用于获取原始对象并进行填充 + */ @Override public void updateFill(MetaObject metaObject) { try { if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) { + // 获取当前时间作为更新时间,无论原始对象中的更新时间是否为空都填充 Date current = new Date(); - // 更新时间填充(不管为不为空) baseEntity.setUpdateTime(current); - // 当前已登录 更新人填充(不管为不为空) + + // 获取当前登录用户的ID,并填充更新人信息 Long userId = LoginHelper.getUserId(); if (ObjectUtil.isNotNull(userId)) { baseEntity.setUpdateBy(userId); } - + } else { + this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date()); } } catch (Exception e) { throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); @@ -67,7 +85,9 @@ public class InjectionMetaObjectHandler implements MetaObjectHandler { } /** - * 获取登录用户名 + * 获取当前登录用户信息 + * + * @return 当前登录用户的信息,如果用户未登录则返回 null */ private LoginUser getLoginUser() { LoginUser loginUser; diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java index ec3ee0d..518d52d 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java @@ -1,15 +1,14 @@ package org.dromara.common.mybatis.handler; -import org.dromara.common.core.domain.R; +import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.R; import org.dromara.common.core.utils.StringUtils; import org.mybatis.spring.MyBatisSystemException; import org.springframework.dao.DuplicateKeyException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; -import jakarta.servlet.http.HttpServletRequest; - /** * Mybatis异常处理器 * diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java index 7d7fd84..5ac74c3 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java @@ -68,13 +68,27 @@ public class PlusDataPermissionHandler { */ private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory()); + /** + * 构造方法,扫描指定包下的 Mapper 类并初始化缓存 + * + * @param mapperPackage Mapper 类所在的包路径 + */ public PlusDataPermissionHandler(String mapperPackage) { scanMapperClasses(mapperPackage); } - + /** + * 获取数据过滤条件的 SQL 片段 + * + * @param where 原始的查询条件表达式 + * @param mappedStatementId Mapper 方法的 ID + * @param isSelect 是否为查询语句 + * @return 数据过滤条件的 SQL 片段 + */ public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) { + // 获取数据权限配置 DataPermission dataPermission = getDataPermission(mappedStatementId); + // 获取当前登录用户信息 LoginUser currentUser = DataPermissionHelper.getVariable("user"); if (ObjectUtil.isNull(currentUser)) { currentUser = LoginHelper.getLoginUser(); @@ -84,7 +98,8 @@ public class PlusDataPermissionHandler { if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) { return where; } - String dataFilterSql = buildDataFilter(dataPermission.value(), isSelect); + // 构造数据过滤条件的 SQL 片段 + String dataFilterSql = buildDataFilter(dataPermission, isSelect); if (StringUtils.isBlank(dataFilterSql)) { return where; } @@ -103,11 +118,19 @@ public class PlusDataPermissionHandler { } /** - * 构造数据过滤sql + * 构建数据过滤条件的 SQL 语句 + * + * @param dataPermission 数据权限注解 + * @param isSelect 标志当前操作是否为查询操作,查询操作和更新或删除操作在处理过滤条件时会有不同的处理方式 + * @return 构建的数据过滤条件的 SQL 语句 + * @throws ServiceException 如果角色的数据范围异常或者 key 与 value 的长度不匹配,则抛出 ServiceException 异常 */ - private String buildDataFilter(DataColumn[] dataColumns, boolean isSelect) { + private String buildDataFilter(DataPermission dataPermission, boolean isSelect) { // 更新或删除需满足所有条件 String joinStr = isSelect ? " OR " : " AND "; + if (StringUtils.isNotBlank(dataPermission.joinStr())) { + joinStr = " " + dataPermission.joinStr() + " "; + } LoginUser user = DataPermissionHelper.getVariable("user"); StandardEvaluationContext context = new StandardEvaluationContext(); context.setBeanResolver(beanResolver); @@ -125,7 +148,7 @@ public class PlusDataPermissionHandler { return ""; } boolean isSuccess = false; - for (DataColumn dataColumn : dataColumns) { + for (DataColumn dataColumn : dataPermission.value()) { if (dataColumn.key().length != dataColumn.value().length) { throw new ServiceException("角色数据范围异常 => key与value长度不匹配"); } @@ -135,6 +158,13 @@ public class PlusDataPermissionHandler { )) { continue; } + // 包含权限标识符 这直接跳过 + if (StringUtils.isNotBlank(dataColumn.permission()) && + CollUtil.contains(user.getMenuPermission(), dataColumn.permission()) + ) { + isSuccess = true; + continue; + } // 设置注解变量 key 为表达式变量 value 为变量值 for (int i = 0; i < dataColumn.key().length; i++) { context.setVariable(dataColumn.key()[i], dataColumn.value()[i]); @@ -159,20 +189,29 @@ public class PlusDataPermissionHandler { } /** - * 通过 mapperPackage 设置的扫描包 扫描缓存有注解的方法与类 + * 扫描指定包下的 Mapper 类,并查找其中带有特定注解的方法或类 + * + * @param mapperPackage Mapper 类所在的包路径 */ private void scanMapperClasses(String mapperPackage) { + // 创建资源解析器和元数据读取工厂 PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); + // 将 Mapper 包路径按分隔符拆分为数组 String[] packagePatternArray = StringUtils.splitPreserveAllTokens(mapperPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX; try { for (String packagePattern : packagePatternArray) { + // 将包路径转换为资源路径 String path = ClassUtils.convertClassNameToResourcePath(packagePattern); + // 获取指定路径下的所有 .class 文件资源 Resource[] resources = resolver.getResources(classpath + path + "/*.class"); for (Resource resource : resources) { + // 获取资源的类元数据 ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata(); + // 获取资源对应的类对象 Class clazz = Resources.classForName(classMetadata.getClassName()); + // 查找类中的特定注解 findAnnotation(clazz); } } @@ -181,9 +220,13 @@ public class PlusDataPermissionHandler { } } + /** + * 在指定的类中查找特定的注解 DataPermission,并将带有这个注解的方法或类存储到 dataPermissionCacheMap 中 + * + * @param clazz 要查找的类 + */ private void findAnnotation(Class clazz) { DataPermission dataPermission; - // 获取方法注解 for (Method method : clazz.getMethods()) { if (method.isDefault() || method.isVarArgs()) { continue; @@ -194,17 +237,24 @@ public class PlusDataPermissionHandler { dataPermissionCacheMap.put(mappedStatementId, dataPermission); } } - // 获取类注解 if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) { dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class); dataPermissionCacheMap.put(clazz.getName(), dataPermission); } } + /** + * 根据映射语句 ID 或类名获取对应的 DataPermission 注解对象 + * + * @param mapperId 映射语句 ID + * @return DataPermission 注解对象,如果不存在则返回 null + */ public DataPermission getDataPermission(String mapperId) { + // 检查缓存中是否包含映射语句 ID 对应的 DataPermission 注解对象 if (dataPermissionCacheMap.containsKey(mapperId)) { return dataPermissionCacheMap.get(mapperId); } + // 如果缓存中不包含映射语句 ID 对应的 DataPermission 注解对象,则尝试使用类名作为键查找 String clazzName = mapperId.substring(0, mapperId.lastIndexOf(".")); if (dataPermissionCacheMap.containsKey(clazzName)) { return dataPermissionCacheMap.get(clazzName); @@ -213,7 +263,10 @@ public class PlusDataPermissionHandler { } /** - * 是否无效 + * 检查给定的映射语句 ID 是否有效,即是否能够找到对应的 DataPermission 注解对象 + * + * @param mapperId 映射语句 ID + * @return 如果找到对应的 DataPermission 注解对象,则返回 false;否则返回 true */ public boolean invalid(String mapperId) { return getDataPermission(mapperId) == null; diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java index bb20f4b..a7cfee5 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java @@ -2,11 +2,11 @@ package org.dromara.common.mybatis.helper; import cn.hutool.core.convert.Convert; import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.mybatis.enums.DataBaseType; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; import javax.sql.DataSource; import java.sql.Connection; @@ -14,7 +14,6 @@ import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; -import java.util.Set; /** * 数据库助手 diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java index 7f6ab1f..932f173 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java @@ -2,14 +2,17 @@ package org.dromara.common.mybatis.helper; import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.context.model.SaStorage; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy; import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; import lombok.AccessLevel; import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.reflect.ReflectUtils; import java.util.HashMap; import java.util.Map; +import java.util.Stack; import java.util.function.Supplier; /** @@ -24,17 +27,37 @@ public class DataPermissionHelper { private static final String DATA_PERMISSION_KEY = "data:permission"; + private static final ThreadLocal> REENTRANT_IGNORE = ThreadLocal.withInitial(Stack::new); + + /** + * 从上下文中获取指定键的变量值,并将其转换为指定的类型 + * + * @param key 变量的键 + * @param 变量值的类型 + * @return 指定键的变量值,如果不存在则返回 null + */ public static T getVariable(String key) { Map context = getContext(); return (T) context.get(key); } - + /** + * 向上下文中设置指定键的变量值 + * + * @param key 要设置的变量的键 + * @param value 要设置的变量值 + */ public static void setVariable(String key, Object value) { Map context = getContext(); context.put(key, value); } + /** + * 获取数据权限上下文 + * + * @return 存储在SaStorage中的Map对象,用于存储数据权限相关的上下文信息 + * @throws NullPointerException 如果数据权限上下文类型异常,则抛出NullPointerException + */ public static Map getContext() { SaStorage saStorage = SaHolder.getStorage(); Object attribute = saStorage.get(DATA_PERMISSION_KEY); @@ -48,18 +71,50 @@ public class DataPermissionHelper { throw new NullPointerException("data permission context type exception"); } + private static IgnoreStrategy getIgnoreStrategy() { + Object ignoreStrategyLocal = ReflectUtils.getStaticFieldValue(ReflectUtils.getField(InterceptorIgnoreHelper.class, "IGNORE_STRATEGY_LOCAL")); + if (ignoreStrategyLocal instanceof ThreadLocal IGNORE_STRATEGY_LOCAL) { + if (IGNORE_STRATEGY_LOCAL.get() instanceof IgnoreStrategy ignoreStrategy) { + return ignoreStrategy; + } + } + return null; + } + /** * 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭) */ public static void enableIgnore() { - InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build()); + IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); + if (ObjectUtil.isNull(ignoreStrategy)) { + InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build()); + } else { + ignoreStrategy.setDataPermission(true); + } + Stack reentrantStack = REENTRANT_IGNORE.get(); + reentrantStack.push(reentrantStack.size() + 1); } /** * 关闭忽略数据权限 */ public static void disableIgnore() { - InterceptorIgnoreHelper.clearIgnoreStrategy(); + IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); + if (ObjectUtil.isNotNull(ignoreStrategy)) { + boolean noOtherIgnoreStrategy = !Boolean.TRUE.equals(ignoreStrategy.getDynamicTableName()) + && !Boolean.TRUE.equals(ignoreStrategy.getBlockAttack()) + && !Boolean.TRUE.equals(ignoreStrategy.getIllegalSql()) + && !Boolean.TRUE.equals(ignoreStrategy.getTenantLine()) + && CollectionUtil.isEmpty(ignoreStrategy.getOthers()); + Stack reentrantStack = REENTRANT_IGNORE.get(); + boolean empty = reentrantStack.isEmpty() || reentrantStack.pop() == 1; + if (noOtherIgnoreStrategy && empty) { + InterceptorIgnoreHelper.clearIgnoreStrategy(); + } else if (empty) { + ignoreStrategy.setDataPermission(false); + } + + } } /** diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java index 6eed8f7..85a4d0a 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java @@ -37,17 +37,33 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto private final PlusDataPermissionHandler dataPermissionHandler; + /** + * 构造函数,初始化 PlusDataPermissionHandler 实例 + * + * @param mapperPackage 扫描的映射器包 + */ public PlusDataPermissionInterceptor(String mapperPackage) { this.dataPermissionHandler = new PlusDataPermissionHandler(mapperPackage); } + /** + * 在执行查询之前,检查并处理数据权限相关逻辑 + * + * @param executor MyBatis 执行器对象 + * @param ms 映射语句对象 + * @param parameter 方法参数 + * @param rowBounds 分页对象 + * @param resultHandler 结果处理器 + * @param boundSql 绑定的 SQL 对象 + * @throws SQLException 如果发生 SQL 异常 + */ @Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { - // 检查忽略注解 + // 检查是否需要忽略数据权限处理 if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { return; } - // 检查是否无效 无数据权限注解 + // 检查是否缺少有效的数据权限注解 if (dataPermissionHandler.invalid(ms.getId())) { return; } @@ -56,16 +72,26 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto mpBs.sql(parserSingle(mpBs.sql(), ms.getId())); } + /** + * 在准备 SQL 语句之前,检查并处理更新和删除操作的数据权限相关逻辑 + * + * @param sh MyBatis StatementHandler 对象 + * @param connection 数据库连接对象 + * @param transactionTimeout 事务超时时间 + */ @Override public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh); MappedStatement ms = mpSh.mappedStatement(); + // 获取 SQL 命令类型(增、删、改、查) SqlCommandType sct = ms.getSqlCommandType(); + + // 只处理更新和删除操作的 SQL 语句 if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) { if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { return; } - // 检查是否无效 无数据权限注解 + // 检查是否缺少有效的数据权限注解 if (dataPermissionHandler.invalid(ms.getId())) { return; } @@ -74,6 +100,14 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto } } + /** + * 处理 SELECT 查询语句中的 WHERE 条件 + * + * @param select SELECT 查询对象 + * @param index 查询语句的索引 + * @param sql 查询语句 + * @param obj WHERE 条件参数 + */ @Override protected void processSelect(Select select, int index, String sql, Object obj) { if (select instanceof PlainSelect) { @@ -84,6 +118,14 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto } } + /** + * 处理 UPDATE 语句中的 WHERE 条件 + * + * @param update UPDATE 查询对象 + * @param index 查询语句的索引 + * @param sql 查询语句 + * @param obj WHERE 条件参数 + */ @Override protected void processUpdate(Update update, int index, String sql, Object obj) { Expression sqlSegment = dataPermissionHandler.getSqlSegment(update.getWhere(), (String) obj, false); @@ -92,6 +134,14 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto } } + /** + * 处理 DELETE 语句中的 WHERE 条件 + * + * @param delete DELETE 查询对象 + * @param index 查询语句的索引 + * @param sql 查询语句 + * @param obj WHERE 条件参数 + */ @Override protected void processDelete(Delete delete, int index, String sql, Object obj) { Expression sqlSegment = dataPermissionHandler.getSqlSegment(delete.getWhere(), (String) obj, false); @@ -101,10 +151,10 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto } /** - * 设置 where 条件 + * 设置 SELECT 语句的 WHERE 条件 * - * @param plainSelect 查询对象 - * @param mappedStatementId 执行方法id + * @param plainSelect SELECT 查询对象 + * @param mappedStatementId 映射语句的 ID */ protected void setWhere(PlainSelect plainSelect, String mappedStatementId) { Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), mappedStatementId, true); @@ -113,6 +163,14 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto } } + /** + * 构建表达式,用于处理表的数据权限 + * + * @param table 表对象 + * @param where WHERE 条件表达式 + * @param whereSegment WHERE 条件片段 + * @return 构建的表达式 + */ @Override public Expression buildTableExpression(Table table, Expression where, String whereSegment) { // 只有新版数据权限处理器才会执行到这里 diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/resources/spy.properties b/ruoyi-common/ruoyi-common-mybatis/src/main/resources/spy.properties index e9d10f3..f3ed7d8 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/resources/spy.properties +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/resources/spy.properties @@ -17,4 +17,4 @@ databaseDialectTimestampFormat=yyyy-MM-dd HH:mm:ss # 是否过滤 Log filter=true # 过滤 Log 时所排除的 sql 关键字,以逗号分隔 -exclude=SELECT 1 +exclude= diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java index 86668aa..5e300da 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java @@ -178,6 +178,7 @@ public class OssClient { .key(key) .contentMD5(StringUtils.isNotEmpty(md5Digest) ? md5Digest : null) .contentType(contentType) + .acl(getAccessPolicy().getObjectCannedACL()) .build()) .addTransferListener(LoggingTransferListener.create()) .source(filePath).build()); @@ -223,6 +224,7 @@ public class OssClient { y -> y.bucket(properties.getBucketName()) .key(key) .contentType(contentType) + .acl(getAccessPolicy().getObjectCannedACL()) .build()) .build()); diff --git a/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java b/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java index 02735b0..1f4904a 100644 --- a/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java +++ b/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java @@ -80,11 +80,11 @@ public class RateLimiterAspect { private String getCombineKey(RateLimiter rateLimiter, JoinPoint point) { String key = rateLimiter.key(); - if (StringUtils.isNotBlank(key)) { + // 判断 key 不为空 和 不是表达式 + if (StringUtils.isNotBlank(key) && StringUtils.containsAny(key, "#")) { MethodSignature signature = (MethodSignature) point.getSignature(); Method targetMethod = signature.getMethod(); Object[] args = point.getArgs(); - //noinspection DataFlowIssue MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(null, targetMethod, args, pnd); context.setBeanResolver(new BeanFactoryResolver(SpringUtils.getBeanFactory())); diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java index ee1d405..793e21f 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java @@ -15,15 +15,17 @@ public class CaffeineCacheDecorator implements Cache { private static final com.github.benmanes.caffeine.cache.Cache CAFFEINE = SpringUtils.getBean("caffeine"); + private final String name; private final Cache cache; - public CaffeineCacheDecorator(Cache cache) { + public CaffeineCacheDecorator(String name, Cache cache) { + this.name = name; this.cache = cache; } @Override public String getName() { - return cache.getName(); + return name; } @Override @@ -32,7 +34,7 @@ public class CaffeineCacheDecorator implements Cache { } public String getUniqueKey(Object key) { - return cache.getName() + ":" + key; + return name + ":" + key; } @Override diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java index a48cb14..740e2a1 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java @@ -156,7 +156,7 @@ public class PlusSpringCacheManager implements CacheManager { private Cache createMap(String name, CacheConfig config) { RMap map = RedisUtils.getClient().getMap(name); - Cache cache = new CaffeineCacheDecorator(new RedissonCache(map, allowNullValues)); + Cache cache = new CaffeineCacheDecorator(name, new RedissonCache(map, allowNullValues)); if (transactionAware) { cache = new TransactionAwareCacheDecorator(cache); } @@ -170,7 +170,7 @@ public class PlusSpringCacheManager implements CacheManager { private Cache createMapCache(String name, CacheConfig config) { RMapCache map = RedisUtils.getClient().getMapCache(name); - Cache cache = new CaffeineCacheDecorator(new RedissonCache(map, config, allowNullValues)); + Cache cache = new CaffeineCacheDecorator(name, new RedissonCache(map, config, allowNullValues)); if (transactionAware) { cache = new TransactionAwareCacheDecorator(cache); } diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/QueueUtils.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/QueueUtils.java index 4587e64..e436a46 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/QueueUtils.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/QueueUtils.java @@ -1,12 +1,13 @@ package org.dromara.common.redis.utils; -import org.dromara.common.core.utils.SpringUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.SpringUtils; import org.redisson.api.*; +import java.util.concurrent.CompletionStage; import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; +import java.util.function.Function; /** * 分布式队列工具 @@ -224,7 +225,7 @@ public class QueueUtils { /** * 订阅阻塞队列(可订阅所有实现类 例如: 延迟 优先 有界 等) */ - public static void subscribeBlockingQueue(String queueName, Consumer consumer, boolean isDelayed) { + public static void subscribeBlockingQueue(String queueName, Function> consumer, boolean isDelayed) { RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); if (isDelayed) { // 订阅延迟队列 diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java index 1e0d8a7..4ab7d4e 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java @@ -3,6 +3,7 @@ package org.dromara.common.satoken.utils; import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; import lombok.AccessLevel; @@ -87,6 +88,13 @@ public class LoginHelper { return Convert.toLong(getExtra(USER_KEY)); } + /** + * 获取用户账户 + */ + public static String getUsername() { + return Convert.toStr(getExtra(USER_NAME_KEY)); + } + /** * 获取租户ID */ @@ -129,13 +137,6 @@ public class LoginHelper { } } - /** - * 获取用户账户 - */ - public static String getUsername() { - return getLoginUser().getUsername(); - } - /** * 获取用户类型 */ @@ -170,6 +171,9 @@ public class LoginHelper { * @return 结果 */ public static boolean isTenantAdmin(Set rolePermission) { + if (CollUtil.isEmpty(rolePermission)) { + return false; + } return rolePermission.contains(TenantConstants.TENANT_ADMIN_ROLE_KEY); } @@ -188,7 +192,11 @@ public class LoginHelper { * @return 结果 */ public static boolean isLogin() { - return getLoginUser() != null; + try { + return getLoginUser() != null; + } catch (Exception e) { + return false; + } } } diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java b/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java index b9283e0..5fd49d1 100644 --- a/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java +++ b/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java @@ -1,11 +1,15 @@ package org.dromara.common.security.config; import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.filter.SaServletFilter; +import cn.dev33.satoken.httpauth.basic.SaHttpBasicUtil; import cn.dev33.satoken.interceptor.SaInterceptor; import cn.dev33.satoken.router.SaRouter; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StringUtils; @@ -14,6 +18,7 @@ import org.dromara.common.security.config.properties.SecurityProperties; import org.dromara.common.security.handler.AllUrlHandler; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -71,4 +76,19 @@ public class SecurityConfig implements WebMvcConfigurer { .excludePathPatterns(securityProperties.getExcludes()); } + /** + * 对 actuator 健康检查接口 做账号密码鉴权 + */ + @Bean + public SaServletFilter getSaServletFilter() { + String username = SpringUtils.getProperty("spring.boot.admin.client.username"); + String password = SpringUtils.getProperty("spring.boot.admin.client.password"); + return new SaServletFilter() + .addInclude("/actuator", "/actuator/**") + .setAuth(obj -> { + SaHttpBasicUtil.check(username + ":" + password); + }) + .setError(e -> SaResult.error(e.getMessage()).setCode(HttpStatus.UNAUTHORIZED)); + } + } diff --git a/ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/core/SensitiveStrategy.java b/ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/core/SensitiveStrategy.java index 9d1978a..995dcbd 100644 --- a/ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/core/SensitiveStrategy.java +++ b/ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/core/SensitiveStrategy.java @@ -37,7 +37,57 @@ public enum SensitiveStrategy { /** * 银行卡 */ - BANK_CARD(DesensitizedUtil::bankCard); + BANK_CARD(DesensitizedUtil::bankCard), + + /** + * 中文名 + */ + CHINESE_NAME(DesensitizedUtil::chineseName), + + /** + * 固定电话 + */ + FIXED_PHONE(DesensitizedUtil::fixedPhone), + + /** + * 用户ID + */ + USER_ID(s -> String.valueOf(DesensitizedUtil.userId())), + + /** + * 密码 + */ + PASSWORD(DesensitizedUtil::password), + + /** + * ipv4 + */ + IPV4(DesensitizedUtil::ipv4), + + /** + * ipv6 + */ + IPV6(DesensitizedUtil::ipv6), + + /** + * 中国大陆车牌,包含普通车辆、新能源车辆 + */ + CAR_LICENSE(DesensitizedUtil::carLicense), + + /** + * 只显示第一个字符 + */ + FIRST_MASK(DesensitizedUtil::firstMask), + + /** + * 清空为null + */ + CLEAR(s -> DesensitizedUtil.clear()), + + /** + * 清空为"" + */ + CLEAR_TO_NULL(s -> DesensitizedUtil.clearToNull()); //可自行添加其他脱敏策略 diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java index 04f6214..9191fca 100644 --- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java @@ -58,9 +58,9 @@ public class SocialUtils { case "linkedin" -> new AuthLinkedinRequest(builder.build(), STATE_CACHE); case "microsoft" -> new AuthMicrosoftRequest(builder.build(), STATE_CACHE); case "renren" -> new AuthRenrenRequest(builder.build(), STATE_CACHE); - case "stack_overflow" -> new AuthStackOverflowRequest(builder.stackOverflowKey("").build(), STATE_CACHE); + case "stack_overflow" -> new AuthStackOverflowRequest(builder.build(), STATE_CACHE); case "huawei" -> new AuthHuaweiRequest(builder.build(), STATE_CACHE); - case "wechat_enterprise" -> new AuthWeChatEnterpriseQrcodeRequest(builder.agentId("").build(), STATE_CACHE); + case "wechat_enterprise" -> new AuthWeChatEnterpriseQrcodeRequest(builder.build(), STATE_CACHE); case "gitlab" -> new AuthGitlabRequest(builder.build(), STATE_CACHE); case "wechat_mp" -> new AuthWeChatMpRequest(builder.build(), STATE_CACHE); case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE); diff --git a/ruoyi-common/ruoyi-common-sse/pom.xml b/ruoyi-common/ruoyi-common-sse/pom.xml new file mode 100644 index 0000000..ae44c98 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sse/pom.xml @@ -0,0 +1,36 @@ + + + + org.dromara + ruoyi-common + ${revision} + + 4.0.0 + + ruoyi-common-sse + + + ruoyi-common-sse 模块 + + + + + org.dromara + ruoyi-common-core + + + org.dromara + ruoyi-common-redis + + + org.dromara + ruoyi-common-satoken + + + org.dromara + ruoyi-common-json + + + diff --git a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/config/SseAutoConfiguration.java b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/config/SseAutoConfiguration.java new file mode 100644 index 0000000..0cf8054 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/config/SseAutoConfiguration.java @@ -0,0 +1,36 @@ +package org.dromara.common.sse.config; + +import org.dromara.common.sse.controller.SseController; +import org.dromara.common.sse.core.SseEmitterManager; +import org.dromara.common.sse.listener.SseTopicListener; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** + * SSE 自动装配 + * + * @author Lion Li + */ +@AutoConfiguration +@ConditionalOnProperty(value = "sse.enabled", havingValue = "true") +@EnableConfigurationProperties(SseProperties.class) +public class SseAutoConfiguration { + + @Bean + public SseEmitterManager sseEmitterManager() { + return new SseEmitterManager(); + } + + @Bean + public SseTopicListener sseTopicListener() { + return new SseTopicListener(); + } + + @Bean + public SseController sseController(SseEmitterManager sseEmitterManager) { + return new SseController(sseEmitterManager); + } + +} diff --git a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/config/SseProperties.java b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/config/SseProperties.java new file mode 100644 index 0000000..ce4e173 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/config/SseProperties.java @@ -0,0 +1,21 @@ +package org.dromara.common.sse.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * SSE 配置项 + * + * @author Lion Li + */ +@Data +@ConfigurationProperties("sse") +public class SseProperties { + + private Boolean enabled; + + /** + * 路径 + */ + private String path; +} diff --git a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/controller/SseController.java b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/controller/SseController.java new file mode 100644 index 0000000..e5331e4 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/controller/SseController.java @@ -0,0 +1,87 @@ +package org.dromara.common.sse.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.dev33.satoken.stp.StpUtil; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.sse.core.SseEmitterManager; +import org.dromara.common.sse.dto.SseMessageDto; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.util.List; + +/** + * SSE 控制器 + * + * @author Lion Li + */ +@RestController +@ConditionalOnProperty(value = "sse.enabled", havingValue = "true") +@RequiredArgsConstructor +public class SseController implements DisposableBean { + + private final SseEmitterManager sseEmitterManager; + + /** + * 建立 SSE 连接 + */ + @GetMapping(value = "${sse.path}", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public SseEmitter connect() { + String tokenValue = StpUtil.getTokenValue(); + Long userId = LoginHelper.getUserId(); + return sseEmitterManager.connect(userId, tokenValue); + } + + /** + * 关闭 SSE 连接 + */ + @SaIgnore + @GetMapping(value = "${sse.path}/close") + public R close() { + String tokenValue = StpUtil.getTokenValue(); + Long userId = LoginHelper.getUserId(); + sseEmitterManager.disconnect(userId, tokenValue); + return R.ok(); + } + + /** + * 向特定用户发送消息 + * + * @param userId 目标用户的 ID + * @param msg 要发送的消息内容 + */ + @GetMapping(value = "${sse.path}/send") + public R send(Long userId, String msg) { + SseMessageDto dto = new SseMessageDto(); + dto.setUserIds(List.of(userId)); + dto.setMessage(msg); + sseEmitterManager.publishMessage(dto); + return R.ok(); + } + + /** + * 向所有用户发送消息 + * + * @param msg 要发送的消息内容 + */ + @GetMapping(value = "${sse.path}/sendAll") + public R send(String msg) { + sseEmitterManager.publishAll(msg); + return R.ok(); + } + + /** + * 清理资源。此方法目前不执行任何操作,但避免因未实现而导致错误 + */ + @Override + public void destroy() throws Exception { + // 销毁时不需要做什么 此方法避免无用操作报错 + } + +} diff --git a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java new file mode 100644 index 0000000..1d37a27 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java @@ -0,0 +1,160 @@ +package org.dromara.common.sse.core; + +import cn.hutool.core.collection.CollUtil; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.redis.utils.RedisUtils; +import org.dromara.common.sse.dto.SseMessageDto; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; + +/** + * 管理 Server-Sent Events (SSE) 连接 + * + * @author Lion Li + */ +@Slf4j +public class SseEmitterManager { + + /** + * 订阅的频道 + */ + private final static String SSE_TOPIC = "global:sse"; + + private final static Map> USER_TOKEN_EMITTERS = new ConcurrentHashMap<>(); + + /** + * 建立与指定用户的 SSE 连接 + * + * @param userId 用户的唯一标识符,用于区分不同用户的连接 + * @param token 用户的唯一令牌,用于识别具体的连接 + * @return 返回一个 SseEmitter 实例,客户端可以通过该实例接收 SSE 事件 + */ + public SseEmitter connect(Long userId, String token) { + // 从 USER_TOKEN_EMITTERS 中获取或创建当前用户的 SseEmitter 映射表(ConcurrentHashMap) + // 每个用户可以有多个 SSE 连接,通过 token 进行区分 + Map emitters = USER_TOKEN_EMITTERS.computeIfAbsent(userId, k -> new ConcurrentHashMap<>()); + + // 创建一个新的 SseEmitter 实例,超时时间设置为 0 表示无限制 + SseEmitter emitter = new SseEmitter(0L); + + emitters.put(token, emitter); + + // 当 emitter 完成、超时或发生错误时,从映射表中移除对应的 token + emitter.onCompletion(() -> emitters.remove(token)); + emitter.onTimeout(() -> emitters.remove(token)); + emitter.onError((e) -> emitters.remove(token)); + + try { + // 向客户端发送一条连接成功的事件 + emitter.send(SseEmitter.event().comment("connected")); + } catch (IOException e) { + // 如果发送消息失败,则从映射表中移除 emitter + emitters.remove(token); + } + return emitter; + } + + /** + * 断开指定用户的 SSE 连接 + * + * @param userId 用户的唯一标识符,用于区分不同用户的连接 + * @param token 用户的唯一令牌,用于识别具体的连接 + */ + public void disconnect(Long userId, String token) { + Map emitters = USER_TOKEN_EMITTERS.get(userId); + if (emitters != null) { + try { + emitters.get(token).send(SseEmitter.event().comment("disconnected")); + } catch (Exception ignore) { + } + emitters.remove(token); + } + } + + /** + * 订阅SSE消息主题,并提供一个消费者函数来处理接收到的消息 + * + * @param consumer 处理SSE消息的消费者函数 + */ + public void subscribeMessage(Consumer consumer) { + RedisUtils.subscribe(SSE_TOPIC, SseMessageDto.class, consumer); + } + + /** + * 向指定的用户会话发送消息 + * + * @param userId 要发送消息的用户id + * @param message 要发送的消息内容 + */ + public void sendMessage(Long userId, String message) { + Map emitters = USER_TOKEN_EMITTERS.get(userId); + if (emitters != null) { + for (Map.Entry entry : emitters.entrySet()) { + try { + entry.getValue().send(SseEmitter.event() + .name("message") + .data(message)); + } catch (Exception e) { + emitters.remove(entry.getKey()); + } + } + } + } + + /** + * 本机全用户会话发送消息 + * + * @param message 要发送的消息内容 + */ + public void sendMessage(String message) { + for (Long userId : USER_TOKEN_EMITTERS.keySet()) { + sendMessage(userId, message); + } + } + + /** + * 发布SSE订阅消息 + * + * @param sseMessageDto 要发布的SSE消息对象 + */ + public void publishMessage(SseMessageDto sseMessageDto) { + List unsentUserIds = new ArrayList<>(); + // 当前服务内用户,直接发送消息 + for (Long userId : sseMessageDto.getUserIds()) { + if (USER_TOKEN_EMITTERS.containsKey(userId)) { + sendMessage(userId, sseMessageDto.getMessage()); + continue; + } + unsentUserIds.add(userId); + } + // 不在当前服务内用户,发布订阅消息 + if (CollUtil.isNotEmpty(unsentUserIds)) { + SseMessageDto broadcastMessage = new SseMessageDto(); + broadcastMessage.setMessage(sseMessageDto.getMessage()); + broadcastMessage.setUserIds(unsentUserIds); + RedisUtils.publish(SSE_TOPIC, broadcastMessage, consumer -> { + log.info("SSE发送主题订阅消息topic:{} session keys:{} message:{}", + SSE_TOPIC, unsentUserIds, sseMessageDto.getMessage()); + }); + } + } + + /** + * 向所有的用户发布订阅的消息(群发) + * + * @param message 要发布的消息内容 + */ + public void publishAll(String message) { + SseMessageDto broadcastMessage = new SseMessageDto(); + broadcastMessage.setMessage(message); + RedisUtils.publish(SSE_TOPIC, broadcastMessage, consumer -> { + log.info("SSE发送主题订阅消息topic:{} message:{}", SSE_TOPIC, message); + }); + } +} diff --git a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/dto/SseMessageDto.java b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/dto/SseMessageDto.java new file mode 100644 index 0000000..a2e1210 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/dto/SseMessageDto.java @@ -0,0 +1,29 @@ +package org.dromara.common.sse.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 消息的dto + * + * @author zendwang + */ +@Data +public class SseMessageDto implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 需要推送到的session key 列表 + */ + private List userIds; + + /** + * 需要发送的消息 + */ + private String message; +} diff --git a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/listener/SseTopicListener.java b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/listener/SseTopicListener.java new file mode 100644 index 0000000..7a4dff1 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/listener/SseTopicListener.java @@ -0,0 +1,48 @@ +package org.dromara.common.sse.listener; + +import cn.hutool.core.collection.CollUtil; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.sse.core.SseEmitterManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.Ordered; + +/** + * SSE 主题订阅监听器 + * + * @author Lion Li + */ +@Slf4j +public class SseTopicListener implements ApplicationRunner, Ordered { + + @Autowired + private SseEmitterManager sseEmitterManager; + + /** + * 在Spring Boot应用程序启动时初始化SSE主题订阅监听器 + * + * @param args 应用程序参数 + * @throws Exception 初始化过程中可能抛出的异常 + */ + @Override + public void run(ApplicationArguments args) throws Exception { + sseEmitterManager.subscribeMessage((message) -> { + log.info("SSE主题订阅收到消息session keys={} message={}", message.getUserIds(), message.getMessage()); + // 如果key不为空就按照key发消息 如果为空就群发 + if (CollUtil.isNotEmpty(message.getUserIds())) { + message.getUserIds().forEach(key -> { + sseEmitterManager.sendMessage(key, message.getMessage()); + }); + } else { + sseEmitterManager.sendMessage(message.getMessage()); + } + }); + log.info("初始化SSE主题订阅监听器成功"); + } + + @Override + public int getOrder() { + return -1; + } +} diff --git a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/utils/SseMessageUtils.java b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/utils/SseMessageUtils.java new file mode 100644 index 0000000..c6abdc8 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/utils/SseMessageUtils.java @@ -0,0 +1,58 @@ +package org.dromara.common.sse.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.sse.core.SseEmitterManager; +import org.dromara.common.sse.dto.SseMessageDto; + +/** + * SSE工具类 + * + * @author Lion Li + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SseMessageUtils { + + private final static SseEmitterManager MANAGER = SpringUtils.getBean(SseEmitterManager.class); + + /** + * 向指定的WebSocket会话发送消息 + * + * @param userId 要发送消息的用户id + * @param message 要发送的消息内容 + */ + public static void sendMessage(Long userId, String message) { + MANAGER.sendMessage(userId, message); + } + + /** + * 本机全用户会话发送消息 + * + * @param message 要发送的消息内容 + */ + public static void sendMessage(String message) { + MANAGER.sendMessage(message); + } + + /** + * 发布SSE订阅消息 + * + * @param sseMessageDto 要发布的SSE消息对象 + */ + public static void publishMessage(SseMessageDto sseMessageDto) { + MANAGER.publishMessage(sseMessageDto); + } + + /** + * 向所有的用户发布订阅的消息(群发) + * + * @param message 要发布的消息内容 + */ + public static void publishAll(String message) { + MANAGER.publishAll(message); + } + +} diff --git a/ruoyi-common/ruoyi-common-sse/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-sse/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..b809713 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sse/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.dromara.common.sse.config.SseAutoConfiguration diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/helper/TenantHelper.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/helper/TenantHelper.java index 9d087e1..b185612 100644 --- a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/helper/TenantHelper.java +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/helper/TenantHelper.java @@ -1,8 +1,9 @@ package org.dromara.common.tenant.helper; import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.convert.Convert; -import com.alibaba.ttl.TransmittableThreadLocal; +import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy; import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; import lombok.AccessLevel; @@ -11,9 +12,11 @@ import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.satoken.utils.LoginHelper; +import java.util.Stack; import java.util.function.Supplier; /** @@ -27,7 +30,9 @@ public class TenantHelper { private static final String DYNAMIC_TENANT_KEY = GlobalConstants.GLOBAL_REDIS_KEY + "dynamicTenant"; - private static final ThreadLocal TEMP_DYNAMIC_TENANT = new TransmittableThreadLocal<>(); + private static final ThreadLocal TEMP_DYNAMIC_TENANT = new ThreadLocal<>(); + + private static final ThreadLocal> REENTRANT_IGNORE = ThreadLocal.withInitial(Stack::new); /** * 租户功能是否启用 @@ -36,18 +41,49 @@ public class TenantHelper { return Convert.toBool(SpringUtils.getProperty("tenant.enable"), false); } + private static IgnoreStrategy getIgnoreStrategy() { + Object ignoreStrategyLocal = ReflectUtils.getStaticFieldValue(ReflectUtils.getField(InterceptorIgnoreHelper.class, "IGNORE_STRATEGY_LOCAL")); + if (ignoreStrategyLocal instanceof ThreadLocal IGNORE_STRATEGY_LOCAL) { + if (IGNORE_STRATEGY_LOCAL.get() instanceof IgnoreStrategy ignoreStrategy) { + return ignoreStrategy; + } + } + return null; + } + /** * 开启忽略租户(开启后需手动调用 {@link #disableIgnore()} 关闭) */ public static void enableIgnore() { - InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build()); + IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); + if (ObjectUtil.isNull(ignoreStrategy)) { + InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build()); + } else { + ignoreStrategy.setTenantLine(true); + } + Stack reentrantStack = REENTRANT_IGNORE.get(); + reentrantStack.push(reentrantStack.size() + 1); } /** * 关闭忽略租户 */ public static void disableIgnore() { - InterceptorIgnoreHelper.clearIgnoreStrategy(); + IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); + if (ObjectUtil.isNotNull(ignoreStrategy)) { + boolean noOtherIgnoreStrategy = !Boolean.TRUE.equals(ignoreStrategy.getDynamicTableName()) + && !Boolean.TRUE.equals(ignoreStrategy.getBlockAttack()) + && !Boolean.TRUE.equals(ignoreStrategy.getIllegalSql()) + && !Boolean.TRUE.equals(ignoreStrategy.getDataPermission()) + && CollectionUtil.isEmpty(ignoreStrategy.getOthers()); + Stack reentrantStack = REENTRANT_IGNORE.get(); + boolean empty = reentrantStack.isEmpty() || reentrantStack.pop() == 1; + if (noOtherIgnoreStrategy && empty) { + InterceptorIgnoreHelper.clearIgnoreStrategy(); + } else if (empty) { + ignoreStrategy.setTenantLine(false); + } + } } /** diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/manager/TenantSpringCacheManager.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/manager/TenantSpringCacheManager.java index d230afc..346e36f 100644 --- a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/manager/TenantSpringCacheManager.java +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/manager/TenantSpringCacheManager.java @@ -1,5 +1,7 @@ package org.dromara.common.tenant.manager; +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; +import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.redis.manager.PlusSpringCacheManager; @@ -11,6 +13,7 @@ import org.springframework.cache.Cache; * * @author Lion Li */ +@Slf4j public class TenantSpringCacheManager extends PlusSpringCacheManager { public TenantSpringCacheManager() { @@ -18,10 +21,16 @@ public class TenantSpringCacheManager extends PlusSpringCacheManager { @Override public Cache getCache(String name) { + if (InterceptorIgnoreHelper.willIgnoreTenantLine("")) { + return super.getCache(name); + } if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) { return super.getCache(name); } String tenantId = TenantHelper.getTenantId(); + if (StringUtils.isBlank(tenantId)) { + log.error("无法获取有效的租户id -> Null"); + } if (StringUtils.startsWith(name, tenantId)) { // 如果存在则直接返回 return super.getCache(name); diff --git a/ruoyi-common/ruoyi-common-web/pom.xml b/ruoyi-common/ruoyi-common-web/pom.xml index b250fa9..5e366bc 100644 --- a/ruoyi-common/ruoyi-common-web/pom.xml +++ b/ruoyi-common/ruoyi-common-web/pom.xml @@ -43,6 +43,19 @@ spring-boot-starter-undertow + + io.undertow + undertow-core + + + io.undertow + undertow-servlet + + + io.undertow + undertow-websockets-jsr + + org.springframework.boot spring-boot-starter-actuator diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java index 0f78928..ac50dd2 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java @@ -16,15 +16,11 @@ import org.springframework.core.task.VirtualThreadTaskExecutor; @AutoConfiguration public class UndertowConfig implements WebServerFactoryCustomizer { - /** - * 设置 Undertow 的 websocket 缓冲池 - */ @Override public void customize(UndertowServletWebServerFactory factory) { - // 默认不直接分配内存 如果项目中使用了 websocket 建议直接分配 factory.addDeploymentInfoCustomizers(deploymentInfo -> { WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo(); - webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 512)); + webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(true, 1024)); deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo); // 使用虚拟线程 if (SpringUtils.isVirtual()) { diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java index bd47c18..061d3aa 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java @@ -16,10 +16,13 @@ import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingPathVariableException; import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.servlet.NoHandlerFoundException; +import java.io.IOException; + /** * 全局异常处理器 * @@ -89,6 +92,20 @@ public class GlobalExceptionHandler { return R.fail(HttpStatus.HTTP_NOT_FOUND, e.getMessage()); } + /** + * 拦截未知的运行时异常 + */ + @ResponseStatus(org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR) + @ExceptionHandler(IOException.class) + public void handleRuntimeException(IOException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + if (requestURI.contains("sse")) { + // sse 经常性连接中断 例如关闭浏览器 直接屏蔽 + return; + } + log.error("请求地址'{}',连接中断", requestURI, e); + } + /** * 拦截未知的运行时异常 */ diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java index 12c8086..614a559 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java @@ -6,7 +6,6 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.time.StopWatch; -import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.web.filter.RepeatedlyRequestWrapper; @@ -19,7 +18,6 @@ import java.util.Map; /** * web的调用时间统计拦截器 - * dev环境有效 * * @author Lion Li * @since 3.3.0 @@ -27,37 +25,34 @@ import java.util.Map; @Slf4j public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor { - private final String prodProfile = "prod"; - private final static ThreadLocal KEY_CACHE = new ThreadLocal<>(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - if (!prodProfile.equals(SpringUtils.getActiveProfile())) { - String url = request.getMethod() + " " + request.getRequestURI(); + String url = request.getMethod() + " " + request.getRequestURI(); - // 打印请求参数 - if (isJsonRequest(request)) { - String jsonParam = ""; - if (request instanceof RepeatedlyRequestWrapper) { - BufferedReader reader = request.getReader(); - jsonParam = IoUtil.read(reader); - } - log.info("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam); + // 打印请求参数 + if (isJsonRequest(request)) { + String jsonParam = ""; + if (request instanceof RepeatedlyRequestWrapper) { + BufferedReader reader = request.getReader(); + jsonParam = IoUtil.read(reader); + } + log.info("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam); + } else { + Map parameterMap = request.getParameterMap(); + if (MapUtil.isNotEmpty(parameterMap)) { + String parameters = JsonUtils.toJsonString(parameterMap); + log.info("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters); } else { - Map parameterMap = request.getParameterMap(); - if (MapUtil.isNotEmpty(parameterMap)) { - String parameters = JsonUtils.toJsonString(parameterMap); - log.info("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters); - } else { - log.info("[PLUS]开始请求 => URL[{}],无参数", url); - } + log.info("[PLUS]开始请求 => URL[{}],无参数", url); } - - StopWatch stopWatch = new StopWatch(); - KEY_CACHE.set(stopWatch); - stopWatch.start(); } + + StopWatch stopWatch = new StopWatch(); + KEY_CACHE.set(stopWatch); + stopWatch.start(); + return true; } @@ -68,12 +63,10 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor { @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { - if (!prodProfile.equals(SpringUtils.getActiveProfile())) { - StopWatch stopWatch = KEY_CACHE.get(); - stopWatch.stop(); - log.info("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime()); - KEY_CACHE.remove(); - } + StopWatch stopWatch = KEY_CACHE.get(); + stopWatch.stop(); + log.info("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime()); + KEY_CACHE.remove(); } /** diff --git a/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/interceptor/PlusWebSocketInterceptor.java b/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/interceptor/PlusWebSocketInterceptor.java index 1ad8ad6..c70e377 100644 --- a/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/interceptor/PlusWebSocketInterceptor.java +++ b/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/interceptor/PlusWebSocketInterceptor.java @@ -35,23 +35,28 @@ public class PlusWebSocketInterceptor implements HandshakeInterceptor { */ @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) { - // 检查是否登录 是否有token - LoginUser loginUser = LoginHelper.getLoginUser(); + try { + // 检查是否登录 是否有token + LoginUser loginUser = LoginHelper.getLoginUser(); - // 解决 ws 不走 mvc 拦截器问题(cloud 版本不受影响) - // 检查 header 与 param 里的 clientid 与 token 里的是否一致 - String headerCid = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY); - String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY); - String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString(); - if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) { - // token 无效 - throw NotLoginException.newInstance(StpUtil.getLoginType(), - "-100", "客户端ID与Token不匹配", - StpUtil.getTokenValue()); - } + // 解决 ws 不走 mvc 拦截器问题(cloud 版本不受影响) + // 检查 header 与 param 里的 clientid 与 token 里的是否一致 + String headerCid = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY); + String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY); + String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString(); + if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) { + // token 无效 + throw NotLoginException.newInstance(StpUtil.getLoginType(), + "-100", "客户端ID与Token不匹配", + StpUtil.getTokenValue()); + } - attributes.put(LOGIN_USER_KEY, loginUser); - return true; + attributes.put(LOGIN_USER_KEY, loginUser); + return true; + } catch (NotLoginException e) { + log.error("WebSocket 认证失败'{}',无法访问系统资源", e.getMessage()); + return false; + } } /** diff --git a/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/utils/WebSocketUtils.java b/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/utils/WebSocketUtils.java index afe76e0..8c4170a 100644 --- a/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/utils/WebSocketUtils.java +++ b/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/utils/WebSocketUtils.java @@ -113,7 +113,7 @@ public class WebSocketUtils { * @param session WebSocket会话 * @param message 要发送的WebSocket消息对象 */ - private static void sendMessage(WebSocketSession session, WebSocketMessage message) { + private synchronized static void sendMessage(WebSocketSession session, WebSocketMessage message) { if (session == null || !session.isOpen()) { log.warn("[send] session会话已经关闭"); } else { diff --git a/ruoyi-extend/ruoyi-monitor-admin/Dockerfile b/ruoyi-extend/ruoyi-monitor-admin/Dockerfile index a54f816..99aaa74 100644 --- a/ruoyi-extend/ruoyi-monitor-admin/Dockerfile +++ b/ruoyi-extend/ruoyi-monitor-admin/Dockerfile @@ -1,7 +1,9 @@ +# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/ +FROM bellsoft/liberica-openjdk-debian:17.0.11-cds +#FROM bellsoft/liberica-openjdk-debian:21.0.3-cds #FROM findepi/graalvm:java17-native -FROM openjdk:17.0.2-oraclelinux8 -MAINTAINER Lion Li +LABEL maintainer="Lion Li" RUN mkdir -p /ruoyi/monitor/logs diff --git a/ruoyi-extend/ruoyi-monitor-admin/pom.xml b/ruoyi-extend/ruoyi-monitor-admin/pom.xml index 91194c6..77c9eb7 100644 --- a/ruoyi-extend/ruoyi-monitor-admin/pom.xml +++ b/ruoyi-extend/ruoyi-monitor-admin/pom.xml @@ -12,10 +12,21 @@ ruoyi-monitor-admin - + org.springframework.boot spring-boot-starter-web + + + spring-boot-starter-tomcat + org.springframework.boot + + + + + + org.springframework.boot + spring-boot-starter-undertow diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/SecurityConfig.java b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/SecurityConfig.java index 3f5dec8..3458cc9 100644 --- a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/SecurityConfig.java +++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/SecurityConfig.java @@ -39,9 +39,7 @@ public class SecurityConfig { .authorizeHttpRequests((authorize) -> authorize.requestMatchers( new AntPathRequestMatcher(adminContextPath + "/assets/**"), - new AntPathRequestMatcher(adminContextPath + "/login"), - new AntPathRequestMatcher("/actuator"), - new AntPathRequestMatcher("/actuator/**") + new AntPathRequestMatcher(adminContextPath + "/login") ).permitAll() .anyRequest().authenticated()) .formLogin((formLogin) -> diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/notifier/CustomNotifier.java b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/notifier/CustomNotifier.java index 477a598..838eefc 100644 --- a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/notifier/CustomNotifier.java +++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/notifier/CustomNotifier.java @@ -9,6 +9,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; +import static de.codecentric.boot.admin.server.domain.values.StatusInfo.*; + /** * 自定义事件通知处理 * @@ -28,13 +30,26 @@ public class CustomNotifier extends AbstractEventNotifier { return Mono.fromRunnable(() -> { // 实例状态改变事件 if (event instanceof InstanceStatusChangedEvent) { + // 获取实例注册名称 String registName = instance.getRegistration().getName(); + // 获取实例ID String instanceId = event.getInstance().getValue(); + // 获取实例状态 String status = ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(); - log.info("Instance Status Change: [{}],[{}],[{}]", registName, instanceId, status); + // 获取服务URL + String serviceUrl = instance.getRegistration().getServiceUrl(); + String statusName = switch (status) { + case STATUS_UP -> "服务上线"; // 实例成功启动并可以正常处理请求 + case STATUS_OFFLINE -> "服务离线"; //实例被手动或自动地从服务中移除 + case STATUS_RESTRICTED -> "服务受限"; //表示实例在某些方面受限,可能无法完全提供所有服务 + case STATUS_OUT_OF_SERVICE -> "停止服务状态"; //表示实例已被标记为停止提供服务,可能是计划内维护或测试 + case STATUS_DOWN -> "服务下线"; //实例因崩溃、错误或其他原因停止运行 + case STATUS_UNKNOWN -> "服务未知异常"; //监控系统无法确定实例的当前状态 + default -> "未知状态"; //没有匹配的状态 + }; + log.info("Instance Status Change: 状态名称【{}】, 注册名称【{}】, 实例ID【{}】, 状态【{}】, 服务URL【{}】", + statusName, registName, instanceId, status, serviceUrl); } - }); } - } diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml b/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml index 1b729ef..beee587 100644 --- a/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml +++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml @@ -41,5 +41,8 @@ spring.boot.admin.client: url: http://localhost:9090/admin instance: service-host-type: IP + metadata: + username: ${spring.boot.admin.client.username} + userpassword: ${spring.boot.admin.client.password} username: ruoyi password: 123456 diff --git a/ruoyi-extend/ruoyi-snailjob-server/Dockerfile b/ruoyi-extend/ruoyi-snailjob-server/Dockerfile index 1fda8db..a66ca66 100644 --- a/ruoyi-extend/ruoyi-snailjob-server/Dockerfile +++ b/ruoyi-extend/ruoyi-snailjob-server/Dockerfile @@ -1,7 +1,9 @@ +# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/ +FROM bellsoft/liberica-openjdk-debian:17.0.11-cds +#FROM bellsoft/liberica-openjdk-debian:21.0.3-cds #FROM findepi/graalvm:java17-native -FROM openjdk:17.0.2-oraclelinux8 -MAINTAINER Lion Li +LABEL maintainer="Lion Li" RUN mkdir -p /ruoyi/snailjob/logs @@ -10,7 +12,7 @@ WORKDIR /ruoyi/snailjob ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="-Xms512m -Xmx1024m" EXPOSE 8800 -EXPOSE 1788 +EXPOSE 17888 ADD ./target/ruoyi-snailjob-server.jar ./app.jar diff --git a/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/ActuatorAuthFilter.java b/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/ActuatorAuthFilter.java new file mode 100644 index 0000000..e3a6892 --- /dev/null +++ b/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/ActuatorAuthFilter.java @@ -0,0 +1,64 @@ +package com.aizuda.snailjob.server.starter.filter; + +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +public class ActuatorAuthFilter implements Filter { + + private final String username; + private final String password; + + public ActuatorAuthFilter(String username, String password) { + this.username = username; + this.password = password; + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + HttpServletResponse response = (HttpServletResponse) servletResponse; + + // 获取 Authorization 头 + String authHeader = request.getHeader("Authorization"); + + if (authHeader == null || !authHeader.startsWith("Basic ")) { + // 如果没有提供 Authorization 或者格式不对,则返回 401 + response.setHeader("WWW-Authenticate", "Basic realm=\"realm\""); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); + return; + } + + // 解码 Base64 编码的用户名和密码 + String base64Credentials = authHeader.substring("Basic ".length()); + byte[] credDecoded = Base64.getDecoder().decode(base64Credentials); + String credentials = new String(credDecoded, StandardCharsets.UTF_8); + String[] split = credentials.split(":"); + if (split.length != 2) { + response.setHeader("WWW-Authenticate", "Basic realm=\"realm\""); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); + return; + } + // 验证用户名和密码 + if (!username.equals(split[0]) && password.equals(split[1])) { + response.setHeader("WWW-Authenticate", "Basic realm=\"realm\""); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); + return; + } + // 如果认证成功,继续处理请求 + filterChain.doFilter(request, response); + } + + @Override + public void init(FilterConfig filterConfig) { + } + + @Override + public void destroy() { + } + +} diff --git a/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/SecurityConfig.java b/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/SecurityConfig.java new file mode 100644 index 0000000..3cae8f5 --- /dev/null +++ b/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/SecurityConfig.java @@ -0,0 +1,29 @@ +package com.aizuda.snailjob.server.starter.filter; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 权限安全配置 + * + * @author Lion Li + */ +@Configuration +public class SecurityConfig { + + @Value("${spring.boot.admin.client.username}") + private String username; + @Value("${spring.boot.admin.client.password}") + private String password; + + @Bean + public FilterRegistrationBean actuatorFilterRegistrationBean() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new ActuatorAuthFilter(username, password)); + registrationBean.addUrlPatterns("/actuator", "/actuator/**"); + return registrationBean; + } + +} diff --git a/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-dev.yml b/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-dev.yml index be0b9b4..cbe40be 100644 --- a/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-dev.yml +++ b/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-dev.yml @@ -21,7 +21,7 @@ snail-job: # 拉取重试数据的每批次的大小 job-pull-page-size: 1000 # 服务端netty端口 - netty-port: 1788 + netty-port: 17888 # 一个客户端每秒最多接收的重试数量指令 limiter: 1000 # 号段模式下步长配置 @@ -43,5 +43,8 @@ spring.boot.admin.client: url: http://localhost:9090/admin instance: service-host-type: IP + metadata: + username: ${spring.boot.admin.client.username} + userpassword: ${spring.boot.admin.client.password} username: ruoyi password: 123456 diff --git a/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod.yml b/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod.yml index 4c69ee7..3ba983c 100644 --- a/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod.yml +++ b/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod.yml @@ -21,7 +21,7 @@ snail-job: # 拉取重试数据的每批次的大小 job-pull-page-size: 1000 # 服务端 netty 端口 - netty-port: 1788 + netty-port: 17888 # 一个客户端每秒最多接收的重试数量指令 limiter: 1000 # 号段模式下步长配置 @@ -43,5 +43,8 @@ spring.boot.admin.client: url: http://localhost:9090/admin instance: service-host-type: IP + metadata: + username: ${spring.boot.admin.client.username} + userpassword: ${spring.boot.admin.client.password} username: ruoyi password: 123456 diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/DelayedQueueController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/DelayedQueueController.java index fb2aade..b6e51d3 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/DelayedQueueController.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/DelayedQueueController.java @@ -1,14 +1,15 @@ package org.dromara.demo.controller.queue; import cn.dev33.satoken.annotation.SaIgnore; -import org.dromara.common.core.domain.R; -import org.dromara.common.redis.utils.QueueUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.R; +import org.dromara.common.redis.utils.QueueUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; /** @@ -42,6 +43,10 @@ public class DelayedQueueController { QueueUtils.subscribeBlockingQueue(queueName, (String orderNum) -> { // 观察接收时间 log.info("通道: {}, 收到数据: {}", queueName, orderNum); + return CompletableFuture.runAsync(() -> { + // 异步处理数据逻辑 不要在上方处理业务逻辑 + log.info("数据处理: {}", orderNum); + }); }, true); return R.ok("操作成功"); } diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestDemoMapper.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestDemoMapper.java index ddf68f2..6aeeb50 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestDemoMapper.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestDemoMapper.java @@ -4,14 +4,14 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import org.apache.poi.ss.formula.functions.T; +import org.apache.ibatis.annotations.Param; import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.demo.domain.TestDemo; import org.dromara.demo.domain.vo.TestDemoVo; -import org.apache.ibatis.annotations.Param; +import java.io.Serializable; import java.util.Collection; import java.util.List; @@ -44,16 +44,17 @@ public interface TestDemoMapper extends BaseMapperPlus { List selectList(@Param(Constants.WRAPPER) Wrapper queryWrapper); @Override - @DataPermission({ + @DataPermission(value = { @DataColumn(key = "deptName", value = "dept_id"), @DataColumn(key = "userName", value = "user_id") - }) - int updateById(@Param(Constants.ENTITY) TestDemo entity); + }, joinStr = "AND") + List selectBatchIds(@Param(Constants.COLL) Collection idList); @Override @DataPermission({ @DataColumn(key = "deptName", value = "dept_id"), @DataColumn(key = "userName", value = "user_id") }) - int deleteBatchIds(@Param(Constants.COLL) Collection idList); + int updateById(@Param(Constants.ENTITY) TestDemo entity); + } diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java index b3c6ec2..3cfde3a 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java @@ -3,6 +3,8 @@ package org.dromara.demo.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; @@ -12,7 +14,6 @@ import org.dromara.demo.domain.bo.TestDemoBo; import org.dromara.demo.domain.vo.TestDemoVo; import org.dromara.demo.mapper.TestDemoMapper; import org.dromara.demo.service.ITestDemoService; -import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.Collection; @@ -99,9 +100,13 @@ public class TestDemoServiceImpl implements ITestDemoService { @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if (isValid) { - //TODO 做一些业务上的校验,判断是否需要校验 + // 做一些业务上的校验,判断是否需要校验 + List list = baseMapper.selectBatchIds(ids); + if (list.size() != ids.size()) { + throw new ServiceException("您没有删除权限!"); + } } - return baseMapper.deleteBatchIds(ids) > 0; + return baseMapper.deleteByIds(ids) > 0; } @Override diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestTreeServiceImpl.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestTreeServiceImpl.java index 463934c..e4e548b 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestTreeServiceImpl.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestTreeServiceImpl.java @@ -83,6 +83,6 @@ public class TestTreeServiceImpl implements ITestTreeService { if (isValid) { //TODO 做一些业务上的校验,判断是否需要校验 } - return baseMapper.deleteBatchIds(ids) > 0; + return baseMapper.deleteByIds(ids) > 0; } } diff --git a/ruoyi-modules/ruoyi-generator/pom.xml b/ruoyi-modules/ruoyi-generator/pom.xml index de34f69..b7fd94f 100644 --- a/ruoyi-modules/ruoyi-generator/pom.xml +++ b/ruoyi-modules/ruoyi-generator/pom.xml @@ -47,6 +47,38 @@ org.apache.velocity velocity-engine-core + + + org.anyline + anyline-environment-spring-data-jdbc + ${anyline.version} + + + + org.anyline + anyline-data-jdbc-mysql + ${anyline.version} + + + + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/config/MyBatisDataSourceMonitor.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/config/MyBatisDataSourceMonitor.java new file mode 100644 index 0000000..8c0f352 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/config/MyBatisDataSourceMonitor.java @@ -0,0 +1,105 @@ +package org.dromara.generator.config; + +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; +import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder; +import lombok.extern.slf4j.Slf4j; +import org.anyline.data.datasource.DataSourceMonitor; +import org.anyline.data.runtime.DataRuntime; +import org.anyline.util.ConfigTable; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceUtils; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.util.HashMap; +import java.util.Map; + +/** + * anyline 适配 动态数据源改造 + * + * @author Lion Li + */ +@Slf4j +@Component +public class MyBatisDataSourceMonitor implements DataSourceMonitor { + + public MyBatisDataSourceMonitor() { + // 调整执行模式为自定义 + ConfigTable.KEEP_ADAPTER = 2; + // 禁用缓存 + ConfigTable.METADATA_CACHE_SCOPE = 0; + } + + private final Map features = new HashMap<>(); + + /** + * 数据源特征 用来定准 adapter 包含数据库或JDBC协议关键字
+ * 一般会通过 产品名_url 合成 如果返回null 上层方法会通过driver_产品名_url合成 + * + * @param datasource 数据源 + * @return String 返回null由上层自动提取 + */ + @Override + public String feature(DataRuntime runtime, Object datasource) { + String feature = null; + if (datasource instanceof JdbcTemplate jdbc) { + DataSource ds = jdbc.getDataSource(); + if (ds instanceof DynamicRoutingDataSource) { + String key = DynamicDataSourceContextHolder.peek(); + feature = features.get(key); + if (null == feature) { + Connection con = null; + try { + con = DataSourceUtils.getConnection(ds); + DatabaseMetaData meta = con.getMetaData(); + String url = meta.getURL(); + feature = meta.getDatabaseProductName().toLowerCase().replace(" ", "") + "_" + url; + features.put(key, feature); + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + if (null != con && !DataSourceUtils.isConnectionTransactional(con, ds)) { + DataSourceUtils.releaseConnection(con, ds); + } + } + } + } + } + return feature; + } + + /** + * 数据源唯一标识 如果不实现则默认feature + * @param datasource 数据源 + * @return String 返回null由上层自动提取 + */ + @Override + public String key(DataRuntime runtime, Object datasource) { + if(datasource instanceof JdbcTemplate jdbc){ + DataSource ds = jdbc.getDataSource(); + if(ds instanceof DynamicRoutingDataSource){ + return DynamicDataSourceContextHolder.peek(); + } + } + return runtime.getKey(); + } + + /** + * ConfigTable.KEEP_ADAPTER=2 : 根据当前接口判断是否保持同一个数据源绑定同一个adapter
+ * DynamicRoutingDataSource类型的返回false,因为同一个DynamicRoutingDataSource可能对应多类数据库, 如果项目中只有一种数据库 应该直接返回true + * + * @param datasource 数据源 + * @return boolean + */ + @Override + public boolean keepAdapter(DataRuntime runtime, Object datasource) { + if (datasource instanceof JdbcTemplate jdbc) { + DataSource ds = jdbc.getDataSource(); + return !(ds instanceof DynamicRoutingDataSource); + } + return true; + } + +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java index ebdb993..e1560b4 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java +++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java @@ -17,7 +17,6 @@ import jakarta.validation.constraints.NotBlank; * * @author Lion Li */ - @Data @EqualsAndHashCode(callSuper = true) @TableName("gen_table_column") diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableColumnMapper.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableColumnMapper.java index f38d39c..ed8ed20 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableColumnMapper.java +++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableColumnMapper.java @@ -1,13 +1,9 @@ package org.dromara.generator.mapper; -import com.baomidou.dynamic.datasource.annotation.DS; import com.baomidou.mybatisplus.annotation.InterceptorIgnore; -import org.apache.ibatis.annotations.Param; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.generator.domain.GenTableColumn; -import java.util.List; - /** * 业务字段 数据层 * @@ -15,14 +11,5 @@ import java.util.List; */ @InterceptorIgnore(dataPermission = "true", tenantLine = "true") public interface GenTableColumnMapper extends BaseMapperPlus { - /** - * 根据表名称查询列信息 - * - * @param tableName 表名称 - * @param dataName 数据源名称 - * @return 列信息 - */ - @DS("#dataName") - List selectDbTableColumnsByName(@Param("tableName") String tableName, String dataName); } diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableMapper.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableMapper.java index c2ff7b6..63f4c15 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableMapper.java +++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableMapper.java @@ -17,22 +17,6 @@ import java.util.List; @InterceptorIgnore(dataPermission = "true", tenantLine = "true") public interface GenTableMapper extends BaseMapperPlus { - /** - * 查询据库列表 - * - * @param genTable 查询条件 - * @return 数据库表集合 - */ - Page selectPageDbTableList(@Param("page") Page page, @Param("genTable") GenTable genTable); - - /** - * 查询据库列表 - * - * @param tableNames 表名称组 - * @return 数据库表集合 - */ - List selectDbTableListByNames(String[] tableNames); - /** * 查询所有表信息 * diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java index d418815..99935f7 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java +++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java @@ -9,15 +9,20 @@ import com.baomidou.dynamic.datasource.annotation.DSTransactional; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; +import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.anyline.metadata.Column; +import org.anyline.metadata.Table; +import org.anyline.proxy.ServiceProxy; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.Velocity; import org.dromara.common.core.constant.Constants; import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.file.FileUtils; @@ -41,11 +46,7 @@ import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -63,6 +64,8 @@ public class GenTableServiceImpl implements IGenTableService { private final GenTableColumnMapper genTableColumnMapper; private final IdentifierGenerator identifierGenerator; + private static final String[] TABLE_IGNORE = new String[]{"sj_", "act_", "flw_", "gen_"}; + /** * 查询业务字段列表 * @@ -99,7 +102,7 @@ public class GenTableServiceImpl implements IGenTableService { Map params = genTable.getParams(); QueryWrapper wrapper = Wrappers.query(); wrapper - .eq(StringUtils.isNotEmpty(genTable.getDataName()),"data_name", genTable.getDataName()) + .eq(StringUtils.isNotEmpty(genTable.getDataName()), "data_name", genTable.getDataName()) .like(StringUtils.isNotBlank(genTable.getTableName()), "lower(table_name)", StringUtils.lowerCase(genTable.getTableName())) .like(StringUtils.isNotBlank(genTable.getTableComment()), "lower(table_comment)", StringUtils.lowerCase(genTable.getTableComment())) .between(params.get("beginTime") != null && params.get("endTime") != null, @@ -107,11 +110,67 @@ public class GenTableServiceImpl implements IGenTableService { return wrapper; } + /** + * 查询数据库列表 + * + * @param genTable 包含查询条件的GenTable对象 + * @param pageQuery 包含分页信息的PageQuery对象 + * @return 包含分页结果的TableDataInfo对象 + */ @DS("#genTable.dataName") @Override public TableDataInfo selectPageDbTableList(GenTable genTable, PageQuery pageQuery) { - genTable.getParams().put("genTableNames",baseMapper.selectTableNameList(genTable.getDataName())); - Page page = baseMapper.selectPageDbTableList(pageQuery.build(), genTable); + // 获取查询条件 + String tableName = genTable.getTableName(); + String tableComment = genTable.getTableComment(); + + LinkedHashMap> tablesMap = ServiceProxy.metadata().tables(); + if (CollUtil.isEmpty(tablesMap)) { + return TableDataInfo.build(); + } + List tableNames = baseMapper.selectTableNameList(genTable.getDataName()); + String[] tableArrays; + if (CollUtil.isNotEmpty(tableNames)) { + tableArrays = tableNames.toArray(new String[0]); + } else { + tableArrays = new String[0]; + } + // 过滤并转换表格数据 + List tables = tablesMap.values().stream() + .filter(x -> !StringUtils.containsAnyIgnoreCase(x.getName(), TABLE_IGNORE)) + .filter(x -> { + if (CollUtil.isEmpty(tableNames)) { + return true; + } + return !StringUtils.equalsAnyIgnoreCase(x.getName(), tableArrays); + }) + .filter(x -> { + boolean nameMatches = true; + boolean commentMatches = true; + // 进行表名称的模糊查询 + if (StringUtils.isNotBlank(tableName)) { + nameMatches = StringUtils.containsIgnoreCase(x.getName(), tableName); + } + // 进行表描述的模糊查询 + if (StringUtils.isNotBlank(tableComment)) { + commentMatches = StringUtils.containsIgnoreCase(x.getComment(), tableComment); + } + // 同时匹配名称和描述 + return nameMatches && commentMatches; + }) + .map(x -> { + GenTable gen = new GenTable(); + gen.setTableName(x.getName()); + gen.setTableComment(x.getComment()); + gen.setCreateTime(x.getCreateTime()); + gen.setUpdateTime(x.getUpdateTime()); + return gen; + }).toList(); + + IPage page = pageQuery.build(); + page.setTotal(tables.size()); + // 手动分页 set数据 + page.setRecords(CollUtil.page((int) page.getCurrent() - 1, (int) page.getSize(), tables)); return TableDataInfo.build(page); } @@ -125,7 +184,29 @@ public class GenTableServiceImpl implements IGenTableService { @DS("#dataName") @Override public List selectDbTableListByNames(String[] tableNames, String dataName) { - return baseMapper.selectDbTableListByNames(tableNames); + Set tableNameSet = new HashSet<>(List.of(tableNames)); + LinkedHashMap> tablesMap = ServiceProxy.metadata().tables(); + + if (CollUtil.isEmpty(tablesMap)) { + return new ArrayList<>(); + } + + List> tableList = tablesMap.values().stream() + .filter(x -> !StringUtils.containsAnyIgnoreCase(x.getName(), TABLE_IGNORE)) + .filter(x -> tableNameSet.contains(x.getName())).toList(); + + if (CollUtil.isEmpty(tableList)) { + return new ArrayList<>(); + } + return tableList.stream().map(x -> { + GenTable gen = new GenTable(); + gen.setDataName(dataName); + gen.setTableName(x.getName()); + gen.setTableComment(x.getComment()); + gen.setCreateTime(x.getCreateTime()); + gen.setUpdateTime(x.getUpdateTime()); + return gen; + }).toList(); } /** @@ -165,7 +246,7 @@ public class GenTableServiceImpl implements IGenTableService { @Override public void deleteGenTableByIds(Long[] tableIds) { List ids = Arrays.asList(tableIds); - baseMapper.deleteBatchIds(ids); + baseMapper.deleteByIds(ids); genTableColumnMapper.delete(new LambdaQueryWrapper().in(GenTableColumn::getTableId, ids)); } @@ -187,7 +268,7 @@ public class GenTableServiceImpl implements IGenTableService { int row = baseMapper.insert(table); if (row > 0) { // 保存列信息 - List genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName, dataName); + List genTableColumns = SpringUtils.getAopProxy(this).selectDbTableColumnsByName(tableName, dataName); List saveColumns = new ArrayList<>(); for (GenTableColumn column : genTableColumns) { GenUtils.initColumnField(column, table); @@ -203,6 +284,32 @@ public class GenTableServiceImpl implements IGenTableService { } } + /** + * 根据表名称查询列信息 + * + * @param tableName 表名称 + * @param dataName 数据源名称 + * @return 列信息 + */ + @DS("#dataName") + @Override + public List selectDbTableColumnsByName(String tableName, String dataName) { + LinkedHashMap columns = ServiceProxy.metadata().columns(tableName); + List tableColumns = new ArrayList<>(); + columns.forEach((columnName, column) -> { + GenTableColumn tableColumn = new GenTableColumn(); + tableColumn.setIsPk(String.valueOf(column.isPrimaryKey())); + tableColumn.setColumnName(column.getName()); + tableColumn.setColumnComment(column.getComment()); + tableColumn.setColumnType(column.getTypeName().toLowerCase()); + tableColumn.setSort(column.getPosition()); + tableColumn.setIsRequired(column.isNullable() == 0 ? "1" : "0"); + tableColumn.setIsIncrement(column.isAutoIncrement() == -1 ? "0" : "1"); + tableColumns.add(tableColumn); + }); + return tableColumns; + } + /** * 预览代码 * @@ -298,7 +405,7 @@ public class GenTableServiceImpl implements IGenTableService { List tableColumns = table.getColumns(); Map tableColumnMap = StreamUtils.toIdentityMap(tableColumns, GenTableColumn::getColumnName); - List dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(table.getTableName(), table.getDataName()); + List dbTableColumns = SpringUtils.getAopProxy(this).selectDbTableColumnsByName(table.getTableName(), table.getDataName()); if (CollUtil.isEmpty(dbTableColumns)) { throw new ServiceException("同步数据失败,原表结构不存在"); } @@ -332,7 +439,7 @@ public class GenTableServiceImpl implements IGenTableService { if (CollUtil.isNotEmpty(delColumns)) { List ids = StreamUtils.toList(delColumns, GenTableColumn::getColumnId); if (CollUtil.isNotEmpty(ids)) { - genTableColumnMapper.deleteBatchIds(ids); + genTableColumnMapper.deleteByIds(ids); } } } diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/IGenTableService.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/IGenTableService.java index 2a2fb82..b2c20c5 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/IGenTableService.java +++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/IGenTableService.java @@ -85,6 +85,15 @@ public interface IGenTableService { */ void importGenTable(List tableList, String dataName); + /** + * 根据表名称查询列信息 + * + * @param tableName 表名称 + * @param dataName 数据源名称 + * @return 列信息 + */ + List selectDbTableColumnsByName(String tableName, String dataName); + /** * 预览代码 * diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/VelocityUtils.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/VelocityUtils.java index f5db391..6e111e3 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/VelocityUtils.java +++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/VelocityUtils.java @@ -215,6 +215,9 @@ public class VelocityUtils { importList.add("com.fasterxml.jackson.annotation.JsonFormat"); } else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) { importList.add("java.math.BigDecimal"); + } else if (!column.isSuperColumn() && "imageUpload".equals(column.getHtmlType())) { + importList.add("org.dromara.common.translation.annotation.Translation"); + importList.add("org.dromara.common.translation.constant.TransConstant"); } } return importList; diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml index 123d51a..fc1c610 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml @@ -7,87 +7,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - - diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml b/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml index 901bbf0..78aa852 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml @@ -14,239 +14,25 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - - - - - - - + where t.table_id = #{tableId} order by c.sort diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm index e7236fd..67690ca 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -149,6 +149,6 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } - return baseMapper.deleteBatchIds(ids) > 0; + return baseMapper.deleteByIds(ids) > 0; } } diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm index f99a2ed..c896afb 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm @@ -53,6 +53,13 @@ public class ${ClassName}Vo implements Serializable { #end private $column.javaType $column.javaField; +#if($column.htmlType == "imageUpload") + /** + * ${column.columnComment}Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "${column.javaField}") + private String ${column.javaField}Url"; +#end #end #end diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/ts/types.ts.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/ts/types.ts.vm index c3f6ed1..35a468e 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/ts/types.ts.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/ts/types.ts.vm @@ -9,6 +9,12 @@ export interface ${BusinessName}VO { #elseif($column.javaType == 'Boolean') boolean; #else string; #end +#if($column.htmlType == "imageUpload") + /** + * ${column.columnComment}Url + */ + ${column.javaField}Url: string; +#end #end #end #if ($table.tree) diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm index d13ef2f..caf3472 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm @@ -99,9 +99,9 @@ #elseif($column.list && $column.htmlType == "imageUpload") - + #elseif($column.list && $column.dictType && "" != $column.dictType) diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm index 886f4ab..a92d19a 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -101,9 +101,9 @@ #elseif($column.list && $column.htmlType == "imageUpload") - + #elseif($column.list && $column.dictType && "" != $column.dictType) diff --git a/ruoyi-modules/ruoyi-system/pom.xml b/ruoyi-modules/ruoyi-system/pom.xml index acf33ce..0fc6d55 100644 --- a/ruoyi-modules/ruoyi-system/pom.xml +++ b/ruoyi-modules/ruoyi-system/pom.xml @@ -95,6 +95,11 @@ ruoyi-common-websocket + + org.dromara + ruoyi-common-sse + + diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLogininforController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLogininforController.java index 18e32d8..98ac2d5 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLogininforController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLogininforController.java @@ -1,7 +1,9 @@ package org.dromara.system.controller.monitor; import cn.dev33.satoken.annotation.SaCheckPermission; -import org.dromara.common.core.constant.GlobalConstants; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.constant.CacheConstants; import org.dromara.common.core.domain.R; import org.dromara.common.excel.utils.ExcelUtil; import org.dromara.common.log.annotation.Log; @@ -13,8 +15,6 @@ import org.dromara.common.web.core.BaseController; import org.dromara.system.domain.bo.SysLogininforBo; import org.dromara.system.domain.vo.SysLogininforVo; import org.dromara.system.service.ISysLogininforService; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -79,7 +79,7 @@ public class SysLogininforController extends BaseController { @Log(title = "账户解锁", businessType = BusinessType.OTHER) @GetMapping("/unlock/{userName}") public R unlock(@PathVariable("userName") String userName) { - String loginName = GlobalConstants.PWD_ERR_CNT_KEY + userName; + String loginName = CacheConstants.PWD_ERR_CNT_KEY + userName; if (RedisUtils.hasKey(loginName)) { RedisUtils.deleteObject(loginName); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java index 98b76e4..968bbe9 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java @@ -12,6 +12,7 @@ import org.dromara.common.web.core.BaseController; import org.dromara.system.domain.bo.SysDeptBo; import org.dromara.system.domain.vo.SysDeptVo; import org.dromara.system.service.ISysDeptService; +import org.dromara.system.service.ISysPostService; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -29,6 +30,7 @@ import java.util.List; public class SysDeptController extends BaseController { private final ISysDeptService deptService; + private final ISysPostService postService; /** * 获取部门列表 @@ -75,8 +77,6 @@ public class SysDeptController extends BaseController { public R add(@Validated @RequestBody SysDeptBo dept) { if (!deptService.checkDeptNameUnique(dept)) { return R.fail("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); - } else if (StringUtils.isNotBlank(dept.getDeptCategory()) && !deptService.checkDeptCategoryUnique(dept)) { - return R.fail("新增部门'" + dept.getDeptName() + "'失败,部门类别编码已存在"); } return toAjax(deptService.insertDept(dept)); } @@ -92,8 +92,6 @@ public class SysDeptController extends BaseController { deptService.checkDeptDataScope(deptId); if (!deptService.checkDeptNameUnique(dept)) { return R.fail("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); - } else if (StringUtils.isNotBlank(dept.getDeptCategory()) && !deptService.checkDeptCategoryUnique(dept)) { - return R.fail("修改部门'" + dept.getDeptName() + "'失败,部门类别编码已存在"); } else if (dept.getParentId().equals(deptId)) { return R.fail("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); } else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())) { @@ -121,6 +119,9 @@ public class SysDeptController extends BaseController { if (deptService.checkDeptExistUser(deptId)) { return R.warn("部门存在用户,不允许删除"); } + if (postService.countPostByDeptId(deptId) > 0) { + return R.warn("部门存在岗位,不允许删除"); + } deptService.checkDeptDataScope(deptId); return toAjax(deptService.deleteDeptById(deptId)); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysNoticeController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysNoticeController.java index a0aa26e..5d65137 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysNoticeController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysNoticeController.java @@ -8,8 +8,8 @@ import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.sse.utils.SseMessageUtils; import org.dromara.common.web.core.BaseController; -import org.dromara.common.websocket.utils.WebSocketUtils; import org.dromara.system.domain.bo.SysNoticeBo; import org.dromara.system.domain.vo.SysNoticeVo; import org.dromara.system.service.ISysNoticeService; @@ -62,7 +62,7 @@ public class SysNoticeController extends BaseController { return R.fail(); } String type = dictService.getDictLabel("sys_notice_type", notice.getNoticeType()); - WebSocketUtils.publishAll("[" + type + "] " + notice.getNoticeTitle()); + SseMessageUtils.publishAll("[" + type + "] " + notice.getNoticeTitle()); return R.ok(); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java index 559e1d5..893b381 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java @@ -11,6 +11,7 @@ import org.dromara.common.encrypt.annotation.ApiEncrypt; import org.dromara.common.idempotent.annotation.RepeatSubmit; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.helper.DataPermissionHelper; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.web.core.BaseController; import org.dromara.system.domain.bo.SysUserBo; @@ -72,7 +73,8 @@ public class SysProfileController extends BaseController { if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) { return R.fail("修改用户'" + username + "'失败,邮箱账号已存在"); } - if (userService.updateUserProfile(user) > 0) { + int rows = DataPermissionHelper.ignore(() -> userService.updateUserProfile(user)); + if (rows > 0) { return R.ok(); } return R.fail("修改个人信息异常,请联系管理员"); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantController.java index 60be68a..bad240c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantController.java @@ -24,6 +24,7 @@ import org.dromara.common.web.core.BaseController; import org.dromara.system.domain.bo.SysTenantBo; import org.dromara.system.domain.vo.SysTenantVo; import org.dromara.system.service.ISysTenantService; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -38,6 +39,7 @@ import java.util.List; @RequiredArgsConstructor @RestController @RequestMapping("/system/tenant") +@ConditionalOnProperty(value = "tenant.enable", havingValue = "true") public class SysTenantController extends BaseController { private final ISysTenantService tenantService; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantPackageController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantPackageController.java index 7d99916..4bfe597 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantPackageController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantPackageController.java @@ -20,6 +20,7 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -34,6 +35,7 @@ import java.util.List; @RequiredArgsConstructor @RestController @RequestMapping("/system/tenant/package") +@ConditionalOnProperty(value = "tenant.enable", havingValue = "true") public class SysTenantPackageController extends BaseController { private final ISysTenantPackageService tenantPackageService; @@ -92,6 +94,9 @@ public class SysTenantPackageController extends BaseController { @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody SysTenantPackageBo bo) { + if (!tenantPackageService.checkPackageNameUnique(bo)) { + return R.fail("新增套餐'" + bo.getPackageName() + "'失败,套餐名称已存在"); + } return toAjax(tenantPackageService.insertByBo(bo)); } @@ -104,6 +109,9 @@ public class SysTenantPackageController extends BaseController { @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody SysTenantPackageBo bo) { + if (!tenantPackageService.checkPackageNameUnique(bo)) { + return R.fail("修改套餐'" + bo.getPackageName() + "'失败,套餐名称已存在"); + } return toAjax(tenantPackageService.updateByBo(bo)); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java index d1f4059..86249d2 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java @@ -61,13 +61,13 @@ public class SysUserVo implements Serializable { /** * 用户邮箱 */ - @Sensitive(strategy = SensitiveStrategy.EMAIL) + @Sensitive(strategy = SensitiveStrategy.EMAIL, perms = "system:user:edit") private String email; /** * 手机号码 */ - @Sensitive(strategy = SensitiveStrategy.PHONE) + @Sensitive(strategy = SensitiveStrategy.PHONE, perms = "system:user:edit") private String phonenumber; /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMenuMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMenuMapper.java index b2be0e9..ac646c0 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMenuMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMenuMapper.java @@ -18,13 +18,6 @@ import java.util.List; */ public interface SysMenuMapper extends BaseMapperPlus { - /** - * 根据用户所有权限 - * - * @return 权限列表 - */ - List selectMenuPerms(); - /** * 根据用户查询系统菜单列表 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java index cd984d8..bf16642 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java @@ -92,14 +92,6 @@ public interface ISysDeptService { */ boolean checkDeptNameUnique(SysDeptBo dept); - /** - * 校验部门类别编码是否唯一 - * - * @param dept 部门信息 - * @return 结果 - */ - boolean checkDeptCategoryUnique(SysDeptBo dept); - /** * 校验部门是否有数据权限 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java index c43f039..3751b23 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java @@ -80,6 +80,14 @@ public interface ISysPostService { */ long countUserPostById(Long postId); + /** + * 通过部门ID查询岗位使用数量 + * + * @param deptId 部门id + * @return 结果 + */ + long countPostByDeptId(Long deptId); + /** * 删除岗位信息 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTenantPackageService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTenantPackageService.java index cdb887c..d060b68 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTenantPackageService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTenantPackageService.java @@ -1,9 +1,9 @@ package org.dromara.system.service; -import org.dromara.system.domain.vo.SysTenantPackageVo; -import org.dromara.system.domain.bo.SysTenantPackageBo; -import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.system.domain.bo.SysTenantPackageBo; +import org.dromara.system.domain.vo.SysTenantPackageVo; import java.util.Collection; import java.util.List; @@ -45,6 +45,11 @@ public interface ISysTenantPackageService { */ Boolean updateByBo(SysTenantPackageBo bo); + /** + * 校验套餐名称是否唯一 + */ + boolean checkPackageNameUnique(SysTenantPackageBo bo); + /** * 修改套餐状态 */ diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java index 26bc491..1c69243 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java @@ -146,6 +146,6 @@ public class SysClientServiceImpl implements ISysClientService { if (isValid) { //TODO 做一些业务上的校验,判断是否需要校验 } - return baseMapper.deleteBatchIds(ids) > 0; + return baseMapper.deleteByIds(ids) > 0; } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java index bebfdad..46526bd 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java @@ -178,7 +178,7 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService { } CacheUtils.evict(CacheNames.SYS_CONFIG, config.getConfigKey()); } - baseMapper.deleteBatchIds(Arrays.asList(configIds)); + baseMapper.deleteByIds(Arrays.asList(configIds)); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java index d7ba934..018f9a0 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java @@ -2,15 +2,16 @@ package org.dromara.system.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import org.dromara.system.domain.SysDept; -import org.dromara.common.mybatis.helper.DataBaseHelper; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.mybatis.helper.DataBaseHelper; +import org.dromara.system.domain.SysDept; import org.dromara.system.domain.SysRoleDept; import org.dromara.system.mapper.SysDeptMapper; import org.dromara.system.mapper.SysRoleDeptMapper; import org.dromara.system.service.ISysDataScopeService; -import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.List; @@ -30,8 +31,17 @@ public class SysDataScopeServiceImpl implements ISysDataScopeService { private final SysRoleDeptMapper roleDeptMapper; private final SysDeptMapper deptMapper; + /** + * 获取角色自定义权限 + * + * @param roleId 角色Id + * @return 部门Id组 + */ @Override public String getRoleCustom(Long roleId) { + if (ObjectUtil.isNull(roleId)) { + return "-1"; + } List list = roleDeptMapper.selectList( new LambdaQueryWrapper() .select(SysRoleDept::getDeptId) @@ -39,11 +49,20 @@ public class SysDataScopeServiceImpl implements ISysDataScopeService { if (CollUtil.isNotEmpty(list)) { return StreamUtils.join(list, rd -> Convert.toStr(rd.getDeptId())); } - return null; + return "-1"; } + /** + * 获取部门及以下权限 + * + * @param deptId 部门Id + * @return 部门Id组 + */ @Override public String getDeptAndChild(Long deptId) { + if (ObjectUtil.isNull(deptId)) { + return "-1"; + } List deptList = deptMapper.selectList(new LambdaQueryWrapper() .select(SysDept::getDeptId) .apply(DataBaseHelper.findInSet(deptId, "ancestors"))); @@ -52,7 +71,7 @@ public class SysDataScopeServiceImpl implements ISysDataScopeService { if (CollUtil.isNotEmpty(ids)) { return StreamUtils.join(ids, Convert::toStr); } - return null; + return "-1"; } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java index cb3f340..160238d 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java @@ -78,7 +78,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { private LambdaQueryWrapper buildQueryWrapper(SysDeptBo bo) { LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); - lqw.eq(SysDept::getDelFlag, "0"); + lqw.eq(SysDept::getDelFlag, UserConstants.DEL_FLAG_NORMAL); lqw.eq(ObjectUtil.isNotNull(bo.getDeptId()), SysDept::getDeptId, bo.getDeptId()); lqw.eq(ObjectUtil.isNotNull(bo.getParentId()), SysDept::getParentId, bo.getParentId()); lqw.like(StringUtils.isNotBlank(bo.getDeptName()), SysDept::getDeptName, bo.getDeptName()); @@ -218,20 +218,6 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { return !exist; } - /** - * 校验部门类别编码是否唯一 - * - * @param dept 部门信息 - * @return 结果 - */ - @Override - public boolean checkDeptCategoryUnique(SysDeptBo dept) { - boolean exist = baseMapper.exists(new LambdaQueryWrapper() - .eq(SysDept::getDeptCategory, dept.getDeptCategory()) - .ne(ObjectUtil.isNotNull(dept.getDeptId()), SysDept::getDeptId, dept.getDeptId())); - return !exist; - } - /** * 校验部门是否有数据权限 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java index bc6663f..1be0b7f 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java @@ -141,7 +141,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService } CacheUtils.evict(CacheNames.SYS_DICT, dictType.getDictType()); } - baseMapper.deleteBatchIds(Arrays.asList(dictIds)); + baseMapper.deleteByIds(Arrays.asList(dictIds)); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java index 9c930a0..b95baf4 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java @@ -163,7 +163,7 @@ public class SysLogininforServiceImpl implements ISysLogininforService { */ @Override public int deleteLogininforByIds(Long[] infoIds) { - return baseMapper.deleteBatchIds(Arrays.asList(infoIds)); + return baseMapper.deleteByIds(Arrays.asList(infoIds)); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java index 6e1765b..af844c9 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java @@ -75,7 +75,7 @@ public class SysMenuServiceImpl implements ISysMenuService { .orderByAsc(SysMenu::getOrderNum)); } else { QueryWrapper wrapper = Wrappers.query(); - wrapper.eq("sur.user_id", userId) + wrapper.inSql("r.role_id", "select role_id from sys_user_role where user_id = " + userId) .like(StringUtils.isNotBlank(menu.getMenuName()), "m.menu_name", menu.getMenuName()) .eq(StringUtils.isNotBlank(menu.getVisible()), "m.visible", menu.getVisible()) .eq(StringUtils.isNotBlank(menu.getStatus()), "m.status", menu.getStatus()) @@ -178,6 +178,7 @@ public class SysMenuServiceImpl implements ISysMenuService { /** * 构建前端路由所需要的菜单 + * 路由name命名规则 path首字母转大写 + id * * @param menus 菜单列表 * @return 路由列表 @@ -186,9 +187,10 @@ public class SysMenuServiceImpl implements ISysMenuService { public List buildMenus(List menus) { List routers = new LinkedList<>(); for (SysMenu menu : menus) { + String name = menu.getRouteName() + menu.getMenuId(); RouterVo router = new RouterVo(); router.setHidden("1".equals(menu.getVisible())); - router.setName(menu.getRouteName()); + router.setName(name); router.setPath(menu.getRouterPath()); router.setComponent(menu.getComponentInfo()); router.setQuery(menu.getQueryParam()); @@ -199,12 +201,13 @@ public class SysMenuServiceImpl implements ISysMenuService { router.setRedirect("noRedirect"); router.setChildren(buildMenus(cMenus)); } else if (menu.isMenuFrame()) { + String frameName = StringUtils.capitalize(menu.getPath()) + menu.getMenuId(); router.setMeta(null); List childrenList = new ArrayList<>(); RouterVo children = new RouterVo(); children.setPath(menu.getPath()); children.setComponent(menu.getComponent()); - children.setName(StringUtils.capitalize(menu.getPath())); + children.setName(frameName); children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); children.setQuery(menu.getQueryParam()); childrenList.add(children); @@ -215,9 +218,10 @@ public class SysMenuServiceImpl implements ISysMenuService { List childrenList = new ArrayList<>(); RouterVo children = new RouterVo(); String routerPath = SysMenu.innerLinkReplaceEach(menu.getPath()); + String innerLinkName = StringUtils.capitalize(routerPath) + menu.getMenuId(); children.setPath(routerPath); children.setComponent(UserConstants.INNER_LINK); - children.setName(StringUtils.capitalize(routerPath)); + children.setName(innerLinkName); children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath())); childrenList.add(children); router.setChildren(childrenList); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java index 18b7a08..db63e61 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java @@ -119,6 +119,6 @@ public class SysNoticeServiceImpl implements ISysNoticeService { */ @Override public int deleteNoticeByIds(Long[] noticeIds) { - return baseMapper.deleteBatchIds(Arrays.asList(noticeIds)); + return baseMapper.deleteByIds(Arrays.asList(noticeIds)); } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java index 2a76b82..b78b9dc 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java @@ -122,7 +122,7 @@ public class SysOperLogServiceImpl implements ISysOperLogService { */ @Override public int deleteOperLogByIds(Long[] operIds) { - return baseMapper.deleteBatchIds(Arrays.asList(operIds)); + return baseMapper.deleteByIds(Arrays.asList(operIds)); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java index 2ecd592..a1eefce 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java @@ -135,7 +135,7 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { SysOssConfig config = baseMapper.selectById(configId); list.add(config); } - boolean flag = baseMapper.deleteBatchIds(ids) > 0; + boolean flag = baseMapper.deleteByIds(ids) > 0; if (flag) { list.forEach(sysOssConfig -> CacheUtils.evict(CacheNames.SYS_OSS_CONFIG, sysOssConfig.getConfigKey())); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java index aa094b2..1866531 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java @@ -77,8 +77,9 @@ public class SysOssServiceImpl implements ISysOssService, OssService { @Override public List listByIds(Collection ossIds) { List list = new ArrayList<>(); + SysOssServiceImpl ossService = SpringUtils.getAopProxy(this); for (Long id : ossIds) { - SysOssVo vo = SpringUtils.getAopProxy(this).getById(id); + SysOssVo vo = ossService.getById(id); if (ObjectUtil.isNotNull(vo)) { try { list.add(this.matchingUrl(vo)); @@ -100,8 +101,9 @@ public class SysOssServiceImpl implements ISysOssService, OssService { @Override public String selectUrlByIds(String ossIds) { List list = new ArrayList<>(); + SysOssServiceImpl ossService = SpringUtils.getAopProxy(this); for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) { - SysOssVo vo = SpringUtils.getAopProxy(this).getById(id); + SysOssVo vo = ossService.getById(id); if (ObjectUtil.isNotNull(vo)) { try { list.add(this.matchingUrl(vo).getUrl()); @@ -247,7 +249,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService { OssClient storage = OssFactory.instance(sysOss.getService()); storage.delete(sysOss.getUrl()); } - return baseMapper.deleteBatchIds(ids) > 0; + return baseMapper.deleteByIds(ids) > 0; } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java index 17cd5ca..2c38129 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java @@ -177,6 +177,17 @@ public class SysPostServiceImpl implements ISysPostService { return userPostMapper.selectCount(new LambdaQueryWrapper().eq(SysUserPost::getPostId, postId)); } + /** + * 通过部门ID查询岗位使用数量 + * + * @param deptId 部门id + * @return 结果 + */ + @Override + public long countPostByDeptId(Long deptId) { + return baseMapper.selectCount(new LambdaQueryWrapper().eq(SysPost::getDeptId, deptId)); + } + /** * 删除岗位信息 * @@ -202,7 +213,7 @@ public class SysPostServiceImpl implements ISysPostService { throw new ServiceException(String.format("%1$s已分配,不能删除!", post.getPostName())); } } - return baseMapper.deleteBatchIds(Arrays.asList(postIds)); + return baseMapper.deleteByIds(Arrays.asList(postIds)); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java index abd294f..9b8b0ec 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java @@ -102,14 +102,13 @@ public class SysRoleServiceImpl implements ISysRoleService { */ @Override public List selectRolesAuthByUserId(Long userId) { - List userRoles = baseMapper.selectRolePermissionByUserId(userId); + List userRoles = baseMapper.selectRolesByUserId(userId); List roles = selectRoleAll(); + // 使用HashSet提高查找效率 + Set userRoleIds = StreamUtils.toSet(userRoles, SysRoleVo::getRoleId); for (SysRoleVo role : roles) { - for (SysRoleVo userRole : userRoles) { - if (role.getRoleId().longValue() == userRole.getRoleId().longValue()) { - role.setFlag(true); - break; - } + if (userRoleIds.contains(role.getRoleId())) { + role.setFlag(true); } } return roles; @@ -123,7 +122,7 @@ public class SysRoleServiceImpl implements ISysRoleService { */ @Override public Set selectRolePermissionByUserId(Long userId) { - List perms = baseMapper.selectRolePermissionByUserId(userId); + List perms = baseMapper.selectRolesByUserId(userId); Set permsSet = new HashSet<>(); for (SysRoleVo perm : perms) { if (ObjectUtil.isNotNull(perm)) { @@ -294,6 +293,10 @@ public class SysRoleServiceImpl implements ISysRoleService { @Transactional(rollbackFor = Exception.class) public int updateRole(SysRoleBo bo) { SysRole role = MapstructUtils.convert(bo, SysRole.class); + + if (UserConstants.ROLE_DISABLE.equals(role.getStatus()) && this.countUserRoleByRoleId(role.getRoleId()) > 0) { + throw new ServiceException("角色已分配,不能禁用!"); + } // 修改角色信息 baseMapper.updateById(role); // 删除角色与菜单关联 @@ -417,7 +420,7 @@ public class SysRoleServiceImpl implements ISysRoleService { roleMenuMapper.delete(new LambdaQueryWrapper().in(SysRoleMenu::getRoleId, ids)); // 删除角色与部门关联 roleDeptMapper.delete(new LambdaQueryWrapper().in(SysRoleDept::getRoleId, ids)); - return baseMapper.deleteBatchIds(ids); + return baseMapper.deleteByIds(ids); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantPackageServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantPackageServiceImpl.java index f7f8d46..d2a72f6 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantPackageServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantPackageServiceImpl.java @@ -1,6 +1,7 @@ package org.dromara.system.service.impl; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -116,6 +117,17 @@ public class SysTenantPackageServiceImpl implements ISysTenantPackageService { return baseMapper.updateById(update) > 0; } + /** + * 校验套餐名称是否唯一 + */ + @Override + public boolean checkPackageNameUnique(SysTenantPackageBo bo) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysTenantPackage::getPackageName, bo.getPackageName()) + .ne(ObjectUtil.isNotNull(bo.getPackageId()), SysTenantPackage::getPackageId, bo.getPackageId())); + return !exist; + } + /** * 修改套餐状态 * @@ -140,6 +152,6 @@ public class SysTenantPackageServiceImpl implements ISysTenantPackageService { throw new ServiceException("租户套餐已被使用"); } } - return baseMapper.deleteBatchIds(ids) > 0; + return baseMapper.deleteByIds(ids) > 0; } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java index 516149f..d30750d 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java @@ -294,7 +294,7 @@ public class SysTenantServiceImpl implements ISysTenantService { throw new ServiceException("超管租户不能删除"); } } - return baseMapper.deleteBatchIds(ids) > 0; + return baseMapper.deleteByIds(ids) > 0; } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java index d8f481d..2540606 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java @@ -26,10 +26,7 @@ import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.helper.DataBaseHelper; import org.dromara.common.satoken.utils.LoginHelper; -import org.dromara.system.domain.SysDept; -import org.dromara.system.domain.SysUser; -import org.dromara.system.domain.SysUserPost; -import org.dromara.system.domain.SysUserRole; +import org.dromara.system.domain.*; import org.dromara.system.domain.bo.SysUserBo; import org.dromara.system.domain.vo.SysPostVo; import org.dromara.system.domain.vo.SysRoleVo; @@ -99,7 +96,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { w.in("u.dept_id", ids); }).orderByAsc("u.user_id"); if (StringUtils.isNotBlank(user.getExcludeUserIds())) { - wrapper.notIn("u.user_id", StringUtils.splitList(user.getExcludeUserIds())); + wrapper.notIn("u.user_id", StringUtils.splitTo(user.getExcludeUserIds(), Convert::toLong)); } return wrapper; } @@ -473,17 +470,14 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ private void insertUserRole(Long userId, Long[] roleIds, boolean clear) { if (ArrayUtil.isNotEmpty(roleIds)) { - // 判断是否具有此角色的操作权限 - List roles = roleMapper.selectRoleList(new LambdaQueryWrapper<>()); - if (CollUtil.isEmpty(roles)) { - throw new ServiceException("没有权限访问角色的数据"); - } - List roleList = StreamUtils.toList(roles, SysRoleVo::getRoleId); + List roleList = new ArrayList<>(List.of(roleIds)); if (!LoginHelper.isSuperAdmin(userId)) { roleList.remove(UserConstants.SUPER_ADMIN_ID); } - List canDoRoleList = StreamUtils.filter(List.of(roleIds), roleList::contains); - if (CollUtil.isEmpty(canDoRoleList)) { + // 判断是否具有此角色的操作权限 + List roles = roleMapper.selectRoleList( + new QueryWrapper().in("r.role_id", roleList)); + if (CollUtil.isEmpty(roles)) { throw new ServiceException("没有权限访问角色的数据"); } if (clear) { @@ -491,7 +485,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { userRoleMapper.delete(new LambdaQueryWrapper().eq(SysUserRole::getUserId, userId)); } // 新增用户与角色管理 - List list = StreamUtils.toList(canDoRoleList, roleId -> { + List list = StreamUtils.toList(roleList, roleId -> { SysUserRole ur = new SysUserRole(); ur.setUserId(userId); ur.setRoleId(roleId); @@ -541,7 +535,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { // 删除用户与岗位表 userPostMapper.delete(new LambdaQueryWrapper().in(SysUserPost::getUserId, ids)); // 防止更新失败导致的数据删除 - int flag = baseMapper.deleteBatchIds(ids); + int flag = baseMapper.deleteByIds(ids); if (flag < 1) { throw new ServiceException("删除用户失败!"); } @@ -640,7 +634,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { return List.of(); } List list = baseMapper.selectVoList(new LambdaQueryWrapper() - .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName) + .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName, SysUser::getEmail, SysUser::getPhonenumber) .eq(SysUser::getStatus, UserConstants.USER_NORMAL) .in(CollUtil.isNotEmpty(userIds), SysUser::getUserId, userIds)); return BeanUtil.copyToList(list, UserDTO.class); @@ -653,4 +647,26 @@ public class SysUserServiceImpl implements ISysUserService, UserService { return StreamUtils.toList(userRoles, SysUserRole::getUserId); } + @Override + public List selectUsersByRoleIds(List roleIds) { + if (CollUtil.isEmpty(roleIds)) { + return List.of(); + } + List userRoles = userRoleMapper.selectList( + new LambdaQueryWrapper().in(SysUserRole::getRoleId, roleIds)); + List userIds = StreamUtils.toList(userRoles, SysUserRole::getUserId); + return selectListByIds(userIds); + } + + @Override + public List selectUsersByDeptIds(List deptIds) { + if (CollUtil.isEmpty(deptIds)) { + return List.of(); + } + List list = baseMapper.selectVoList(new LambdaQueryWrapper() + .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName, SysUser::getEmail, SysUser::getPhonenumber) + .eq(SysUser::getStatus, UserConstants.USER_NORMAL) + .in(CollUtil.isNotEmpty(deptIds), SysUser::getDeptId, deptIds)); + return BeanUtil.copyToList(list, UserDTO.class); + } } diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml index fad1812..9dd3f2e 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -11,9 +11,8 @@ select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query_param, m.visible, m.status, m.perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time from sys_menu m - left join sys_role_menu rm on m.menu_id = rm.menu_id - left join sys_user_role sur on rm.role_id = sur.role_id - left join sys_role ro on sur.role_id = ro.role_id + left join sys_role_menu rm on m.menu_id = rm.menu_id + left join sys_role r on rm.role_id = r.role_id ${ew.getCustomSqlSegment} @@ -34,14 +33,10 @@ m.order_num, m.create_time from sys_menu m - left join sys_role_menu rm on m.menu_id = rm.menu_id - left join sys_user_role sur on rm.role_id = sur.role_id - left join sys_role ro on sur.role_id = ro.role_id - left join sys_user u on sur.user_id = u.user_id - where u.user_id = #{userId} - and m.menu_type in ('M', 'C') - and m.status = '0' - and ro.status = '0' + left join sys_role_menu rm on m.menu_id = rm.menu_id and m.status = '0' + left join sys_role r on rm.role_id = r.role_id and r.status = '0' + where m.menu_type in ('M', 'C') + and r.role_id in (select role_id from sys_user_role where user_id = #{userId}) order by m.parent_id, m.order_num @@ -57,22 +52,12 @@ order by m.parent_id, m.order_num - -