疯狂的狮子Li
2024-10-25 015b4060011ed00262369d49fc36d420f2f040a4
!591 发布 5.2.3 正式版
Merge pull request !591 from 疯狂的狮子Li/dev
已修改62个文件
已添加1个文件
已重命名8个文件
897 ■■■■■ 文件已修改
.run/ruoyi-monitor-admin.run.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
.run/ruoyi-server.run.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.run/ruoyi-snailjob-server.run.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-dev.yml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-prod.yml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application.yml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-bom/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/SseException.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/TreeBuildUtils.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/sql/SqlUtil.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/pom.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/CacheUtils.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/helper/TenantHelper.java 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/pom.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/FilterConfig.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/XssProperties.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/XssFilter.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/XssHttpServletRequestWrapper.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-snailjob-server/pom.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-dev.yml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod.yml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestDemoMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/pom.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/constant/GenConstants.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTable.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableMapper.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantController.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/SysUserImportListener.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTenantService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java 55 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfDefinitionConfigServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfFormManageServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActTaskMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
script/docker/docker-compose.yml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
script/sql/oracle/oracle_ry_job.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
script/sql/oracle/oracle_ry_workflow.sql 补丁 | 查看 | 原始文档 | blame | 历史
script/sql/postgres/postgres_ry_job.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
script/sql/postgres/postgres_ry_vue_5.X.sql 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
script/sql/postgres/postgres_ry_workflow.sql 补丁 | 查看 | 原始文档 | blame | 历史
script/sql/ry_job.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
script/sql/ry_workflow.sql 补丁 | 查看 | 原始文档 | blame | 历史
script/sql/sqlserver/sqlserver_ry_job.sql 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
script/sql/sqlserver/sqlserver_ry_workflow.sql 补丁 | 查看 | 原始文档 | blame | 历史
.run/ruoyi-monitor-admin.run.xml
@@ -2,7 +2,7 @@
  <configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
    <deployment type="dockerfile">
      <settings>
        <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.2.2" />
        <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.2.3" />
        <option name="buildOnly" value="true" />
        <option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
      </settings>
.run/ruoyi-server.run.xml
@@ -2,11 +2,11 @@
  <configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="演示机">
    <deployment type="dockerfile">
      <settings>
        <option name="imageTag" value="ruoyi/ruoyi-server:5.2.2" />
        <option name="imageTag" value="ruoyi/ruoyi-server:5.2.3" />
        <option name="buildOnly" value="true" />
        <option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
      </settings>
    </deployment>
    <method v="2" />
  </configuration>
</component>
</component>
.run/ruoyi-snailjob-server.run.xml
@@ -2,7 +2,7 @@
  <configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
    <deployment type="dockerfile">
      <settings>
        <option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.2.2" />
        <option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.2.3" />
        <option name="buildOnly" value="true" />
        <option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" />
      </settings>
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)
<br>
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.2.2-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.2.3-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)]()
pom.xml
@@ -13,44 +13,42 @@
    <description>RuoYi-Vue-Plus多租户管理系统</description>
    <properties>
        <revision>5.2.2</revision>
        <spring-boot.version>3.2.9</spring-boot.version>
        <revision>5.2.3</revision>
        <spring-boot.version>3.2.11</spring-boot.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>17</java.version>
        <mybatis.version>3.5.16</mybatis.version>
        <springdoc.version>2.6.0</springdoc.version>
        <therapi-javadoc.version>0.15.0</therapi-javadoc.version>
        <easyexcel.version>4.0.2</easyexcel.version>
        <easyexcel.version>4.0.3</easyexcel.version>
        <velocity.version>2.3</velocity.version>
        <satoken.version>1.38.0</satoken.version>
        <mybatis-plus.version>3.5.7</mybatis-plus.version>
        <satoken.version>1.39.0</satoken.version>
        <mybatis-plus.version>3.5.8</mybatis-plus.version>
        <p6spy.version>3.9.1</p6spy.version>
        <hutool.version>5.8.31</hutool.version>
        <okhttp.version>4.10.0</okhttp.version>
        <spring-boot-admin.version>3.2.3</spring-boot-admin.version>
        <redisson.version>3.34.1</redisson.version>
        <redisson.version>3.37.0</redisson.version>
        <lock4j.version>2.2.7</lock4j.version>
        <dynamic-ds.version>4.3.1</dynamic-ds.version>
        <snailjob.version>1.1.2</snailjob.version>
        <mapstruct-plus.version>1.4.4</mapstruct-plus.version>
        <mapstruct-plus.version>1.4.5</mapstruct-plus.version>
        <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
        <lombok.version>1.18.34</lombok.version>
        <bouncycastle.version>1.76</bouncycastle.version>
        <justauth.version>1.16.6</justauth.version>
        <!-- ç¦»çº¿IP地址定位库 -->
        <ip2region.version>2.7.0</ip2region.version>
        <undertow.version>2.3.15.Final</undertow.version>
        <!-- OSS é…ç½® -->
        <aws.sdk.version>2.25.15</aws.sdk.version>
        <aws.crt.version>0.29.13</aws.crt.version>
        <aws.sdk.version>2.28.22</aws.sdk.version>
        <aws.crt.version>0.31.3</aws.crt.version>
        <!-- SMS é…ç½® -->
        <sms4j.version>3.3.2</sms4j.version>
        <sms4j.version>3.3.3</sms4j.version>
        <!-- é™åˆ¶æ¡†æž¶ä¸­çš„fastjson版本 -->
        <fastjson.version>1.2.83</fastjson.version>
        <!-- é¢å‘运行时的D-ORM依赖 -->
        <anyline.version>8.7.2-20240808</anyline.version>
        <anyline.version>8.7.2-20241022</anyline.version>
        <!--工作流配置-->
        <flowable.version>7.0.1</flowable.version>
@@ -69,6 +67,8 @@
                <!-- çŽ¯å¢ƒæ ‡è¯†ï¼Œéœ€è¦ä¸Žé…ç½®æ–‡ä»¶çš„åç§°ç›¸å¯¹åº” -->
                <profiles.active>local</profiles.active>
                <logging.level>info</logging.level>
                <monitor.username>ruoyi</monitor.username>
                <monitor.password>123456</monitor.password>
            </properties>
        </profile>
        <profile>
@@ -77,6 +77,8 @@
                <!-- çŽ¯å¢ƒæ ‡è¯†ï¼Œéœ€è¦ä¸Žé…ç½®æ–‡ä»¶çš„åç§°ç›¸å¯¹åº” -->
                <profiles.active>dev</profiles.active>
                <logging.level>info</logging.level>
                <monitor.username>ruoyi</monitor.username>
                <monitor.password>123456</monitor.password>
            </properties>
            <activation>
                <!-- é»˜è®¤çŽ¯å¢ƒ -->
@@ -88,6 +90,8 @@
            <properties>
                <profiles.active>prod</profiles.active>
                <logging.level>warn</logging.level>
                <monitor.username>ruoyi</monitor.username>
                <monitor.password>123456</monitor.password>
            </properties>
        </profile>
    </profiles>
@@ -225,12 +229,6 @@
                <version>${p6spy.version}</version>
            </dependency>
            <dependency>
                <groupId>com.squareup.okhttp3</groupId>
                <artifactId>okhttp</artifactId>
                <version>${okhttp.version}</version>
            </dependency>
            <!--  AWS SDK for Java 2.x  -->
            <dependency>
                <groupId>software.amazon.awssdk</groupId>
@@ -313,25 +311,9 @@
            </dependency>
            <dependency>
                <groupId>io.undertow</groupId>
                <artifactId>undertow-core</artifactId>
                <version>${undertow.version}</version>
            </dependency>
            <dependency>
                <groupId>io.undertow</groupId>
                <artifactId>undertow-servlet</artifactId>
                <version>${undertow.version}</version>
            </dependency>
            <dependency>
                <groupId>io.undertow</groupId>
                <artifactId>undertow-websockets-jsr</artifactId>
                <version>${undertow.version}</version>
            </dependency>
            <dependency>
                <artifactId>commons-compress</artifactId>
                <groupId>org.apache.commons</groupId>
                <version>1.26.2</version>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>2.15.0</version>
            </dependency>
            <dependency>
ruoyi-admin/src/main/resources/application-dev.yml
@@ -8,8 +8,8 @@
    metadata:
      username: ${spring.boot.admin.client.username}
      userpassword: ${spring.boot.admin.client.password}
  username: ruoyi
  password: 123456
  username: @monitor.username@
  password: @monitor.password@
--- # snail-job é…ç½®
snail-job:
@@ -25,6 +25,8 @@
  namespace: ${spring.profiles.active}
  # éšä¸»åº”用端口飘逸
  port: 2${server.port}
  # å®¢æˆ·ç«¯ip指定
  host:
--- # æ•°æ®æºé…ç½®
spring:
ruoyi-admin/src/main/resources/application-prod.yml
@@ -11,8 +11,8 @@
    metadata:
      username: ${spring.boot.admin.client.username}
      userpassword: ${spring.boot.admin.client.password}
  username: ruoyi
  password: 123456
  username: @monitor.username@
  password: @monitor.password@
--- # snail-job é…ç½®
snail-job:
@@ -28,6 +28,8 @@
  namespace: ${spring.profiles.active}
  # éšä¸»åº”用端口飘逸
  port: 2${server.port}
  # å®¢æˆ·ç«¯ip指定
  host:
--- # æ•°æ®æºé…ç½®
spring:
ruoyi-admin/src/main/resources/application.yml
@@ -223,9 +223,10 @@
  # è¿‡æ»¤å¼€å…³
  enabled: true
  # æŽ’除链接(多个用逗号分隔)
  excludes: /system/notice
  # åŒ¹é…é“¾æŽ¥
  urlPatterns: /system/*,/monitor/*,/tool/*
  excludeUrls:
    - /system/notice
    - /workflow/model/save
    - /workflow/model/editModelXml
# å…¨å±€çº¿ç¨‹æ± ç›¸å…³é…ç½®
# å¦‚使用JDK21请直接使用虚拟线程 ä¸è¦å¼€å¯æ­¤é…ç½®
ruoyi-common/ruoyi-common-bom/pom.xml
@@ -14,7 +14,7 @@
    </description>
    <properties>
        <revision>5.2.2</revision>
        <revision>5.2.3</revision>
    </properties>
    <dependencyManagement>
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/SseException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
package org.dromara.common.core.exception;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.io.Serial;
/**
 * sse ç‰¹åˆ¶å¼‚常
 *
 * @author LionLi
 */
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
public final class SseException extends RuntimeException {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * é”™è¯¯ç 
     */
    private Integer code;
    /**
     * é”™è¯¯æç¤º
     */
    private String message;
    /**
     * é”™è¯¯æ˜Žç»†ï¼Œå†…部调试错误
     */
    private String detailMessage;
    public SseException(String message) {
        this.message = message;
    }
    public SseException(String message, Integer code) {
        this.message = message;
        this.code = code;
    }
    @Override
    public String getMessage() {
        return message;
    }
    public SseException setMessage(String message) {
        this.message = message;
        return this;
    }
    public SseException setDetailMessage(String detailMessage) {
        this.detailMessage = detailMessage;
        return this;
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/TreeBuildUtils.java
@@ -5,11 +5,13 @@
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.lang.tree.parser.NodeParser;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
 * æ‰©å±• hutool TreeUtil å°è£…系统树构建
@@ -24,12 +26,54 @@
     */
    public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label");
    /**
     * æž„建树形结构
     *
     * @param <T>        è¾“入节点的类型
     * @param <K>        èŠ‚ç‚¹ID的类型
     * @param list       èŠ‚ç‚¹åˆ—è¡¨ï¼Œå…¶ä¸­åŒ…å«äº†è¦æž„å»ºæ ‘å½¢ç»“æž„çš„æ‰€æœ‰èŠ‚ç‚¹
     * @param nodeParser è§£æžå™¨ï¼Œç”¨äºŽå°†è¾“入节点转换为树节点
     * @return æž„建好的树形结构列表
     */
    public static <T, K> List<Tree<K>> build(List<T> list, NodeParser<T, K> nodeParser) {
        if (CollUtil.isEmpty(list)) {
            return null;
            return CollUtil.newArrayList();
        }
        K k = ReflectUtils.invokeGetter(list.get(0), "parentId");
        return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser);
    }
    /**
     * èŽ·å–èŠ‚ç‚¹åˆ—è¡¨ä¸­æ‰€æœ‰èŠ‚ç‚¹çš„å¶å­èŠ‚ç‚¹
     *
     * @param <K>   èŠ‚ç‚¹ID的类型
     * @param nodes èŠ‚ç‚¹åˆ—è¡¨
     * @return åŒ…含所有叶子节点的列表
     */
    public static <K> List<Tree<K>> getLeafNodes(List<Tree<K>> nodes) {
        if (CollUtil.isEmpty(nodes)) {
            return CollUtil.newArrayList();
        }
        return nodes.stream()
            .flatMap(TreeBuildUtils::extractLeafNodes)
            .collect(Collectors.toList());
    }
    /**
     * èŽ·å–æŒ‡å®šèŠ‚ç‚¹ä¸‹çš„æ‰€æœ‰å¶å­èŠ‚ç‚¹
     *
     * @param <K>  èŠ‚ç‚¹ID的类型
     * @param node è¦æŸ¥æ‰¾å¶å­èŠ‚ç‚¹çš„æ ¹èŠ‚ç‚¹
     * @return åŒ…含所有叶子节点的列表
     */
    private static <K> Stream<Tree<K>> extractLeafNodes(Tree<K> node) {
        if (!node.hasChild()) {
            return Stream.of(node);
        } else {
            // é€’归调用,获取所有子节点的叶子节点
            return node.getChildren().stream()
                .flatMap(TreeBuildUtils::extractLeafNodes);
        }
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java
@@ -21,7 +21,8 @@
     */
    public static String extractFromString(String input, String regex, String defaultInput) {
        try {
            return ReUtil.get(regex, input, 1);
            String str = ReUtil.get(regex, input, 1);
            return str == null ? defaultInput : str;
        } catch (Exception e) {
            return defaultInput;
        }
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/sql/SqlUtil.java
@@ -15,7 +15,7 @@
    /**
     * å®šä¹‰å¸¸ç”¨çš„ sql关键字
     */
    public static final String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare ";
    public static String SQL_REGEX = "and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
    /**
     * ä»…支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java
@@ -17,7 +17,10 @@
import org.springframework.util.ClassUtils;
import java.lang.reflect.Field;
import java.util.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@@ -34,7 +37,7 @@
    /**
     * ç¼“存加密器
     */
    Map<EncryptContext, IEncryptor> encryptorMap = new ConcurrentHashMap<>();
    Map<Integer, IEncryptor> encryptorMap = new ConcurrentHashMap<>();
    /**
     * ç±»åŠ å¯†å­—æ®µç¼“å­˜
@@ -67,11 +70,12 @@
     * @param encryptContext åŠ å¯†æ‰§è¡Œè€…éœ€è¦çš„ç›¸å…³é…ç½®å‚æ•°
     */
    public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) {
        if (encryptorMap.containsKey(encryptContext)) {
            return encryptorMap.get(encryptContext);
        int key = encryptContext.hashCode();
        if (encryptorMap.containsKey(key)) {
            return encryptorMap.get(key);
        }
        IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext);
        encryptorMap.put(encryptContext, encryptor);
        encryptorMap.put(key, encryptor);
        return encryptor;
    }
@@ -81,7 +85,7 @@
     * @param encryptContext åŠ å¯†æ‰§è¡Œè€…éœ€è¦çš„ç›¸å…³é…ç½®å‚æ•°
     */
    public void removeEncryptor(EncryptContext encryptContext) {
        this.encryptorMap.remove(encryptContext);
        this.encryptorMap.remove(encryptContext.hashCode());
    }
    /**
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java
@@ -99,7 +99,7 @@
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
            return null;
        }
        return null;
    }
ruoyi-common/ruoyi-common-excel/pom.xml
@@ -25,11 +25,6 @@
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
        </dependency>
        <dependency>
            <artifactId>commons-compress</artifactId>
            <groupId>org.apache.commons</groupId>
            <version>1.26.2</version>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java
@@ -159,8 +159,7 @@
    private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog, String[] excludeParamNames) throws Exception {
        Map<String, String> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
        String requestMethod = operLog.getRequestMethod();
        if (MapUtil.isEmpty(paramsMap)
                && HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
        if (MapUtil.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name())) {
            String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
            operLog.setOperParam(StringUtils.substring(params, 0, 2000));
        } else {
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java
@@ -18,9 +18,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
 * è‡ªå®šä¹‰ Mapper æŽ¥å£, å®žçް è‡ªå®šä¹‰æ‰©å±•
@@ -69,9 +67,7 @@
     * @return æ’入操作是否成功的布尔值
     */
    default boolean insertBatch(Collection<T> entityList) {
        Db.saveBatch(entityList);
        // ä¸´æ—¶è§£å†³ æ–°ç‰ˆæœ¬ mp æ’入状态判断错误问题
        return true;
        return Db.saveBatch(entityList);
    }
    /**
@@ -81,9 +77,7 @@
     * @return æ›´æ–°æ“ä½œæ˜¯å¦æˆåŠŸçš„å¸ƒå°”å€¼
     */
    default boolean updateBatchById(Collection<T> entityList) {
        Db.updateBatchById(entityList);
        // ä¸´æ—¶è§£å†³ æ–°ç‰ˆæœ¬ mp æ’入状态判断错误问题
        return true;
        return Db.updateBatchById(entityList);
    }
    /**
@@ -93,9 +87,7 @@
     * @return æ’入或更新操作是否成功的布尔值
     */
    default boolean insertOrUpdateBatch(Collection<T> entityList) {
        Db.saveOrUpdateBatch(entityList);
        // ä¸´æ—¶è§£å†³ æ–°ç‰ˆæœ¬ mp æ’入状态判断错误问题
        return true;
        return Db.saveOrUpdateBatch(entityList);
    }
    /**
@@ -106,9 +98,7 @@
     * @return æ’入操作是否成功的布尔值
     */
    default boolean insertBatch(Collection<T> entityList, int batchSize) {
        Db.saveBatch(entityList, batchSize);
        // ä¸´æ—¶è§£å†³ æ–°ç‰ˆæœ¬ mp æ’入状态判断错误问题
        return true;
        return Db.saveBatch(entityList, batchSize);
    }
    /**
@@ -119,9 +109,7 @@
     * @return æ›´æ–°æ“ä½œæ˜¯å¦æˆåŠŸçš„å¸ƒå°”å€¼
     */
    default boolean updateBatchById(Collection<T> entityList, int batchSize) {
        Db.updateBatchById(entityList, batchSize);
        // ä¸´æ—¶è§£å†³ æ–°ç‰ˆæœ¬ mp æ’入状态判断错误问题
        return true;
        return Db.updateBatchById(entityList, batchSize);
    }
    /**
@@ -132,9 +120,7 @@
     * @return æ’入或更新操作是否成功的布尔值
     */
    default boolean insertOrUpdateBatch(Collection<T> entityList, int batchSize) {
        Db.saveOrUpdateBatch(entityList, batchSize);
        // ä¸´æ—¶è§£å†³ æ–°ç‰ˆæœ¬ mp æ’入状态判断错误问题
        return true;
        return Db.saveOrUpdateBatch(entityList, batchSize);
    }
    /**
@@ -169,8 +155,8 @@
     * @param idList ä¸»é”®ID集合
     * @return æŸ¥è¯¢åˆ°çš„VO对象列表
     */
    default List<V> selectVoBatchIds(Collection<? extends Serializable> idList) {
        return selectVoBatchIds(idList, this.currentVoClass());
    default List<V> selectVoByIds(Collection<? extends Serializable> idList) {
        return selectVoByIds(idList, this.currentVoClass());
    }
    /**
@@ -181,8 +167,8 @@
     * @param <C>     VO类的类型
     * @return æŸ¥è¯¢åˆ°çš„VO对象列表,经过转换为指定的VO类后返回
     */
    default <C> List<C> selectVoBatchIds(Collection<? extends Serializable> idList, Class<C> voClass) {
        List<T> list = this.selectBatchIds(idList);
    default <C> List<C> selectVoByIds(Collection<? extends Serializable> idList, Class<C> voClass) {
        List<T> list = this.selectByIds(idList);
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java
@@ -6,8 +6,8 @@
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import org.apache.ibatis.io.Resources;
import org.dromara.common.core.domain.dto.RoleDTO;
@@ -106,7 +106,7 @@
        try {
            Expression expression = CCJSqlParserUtil.parseExpression(dataFilterSql);
            // æ•°æ®æƒé™ä½¿ç”¨å•独的括号 é˜²æ­¢ä¸Žå…¶ä»–条件冲突
            Parenthesis parenthesis = new Parenthesis(expression);
            ParenthesedExpressionList<Expression> parenthesis = new ParenthesedExpressionList<>(expression);
            if (ObjectUtil.isNotNull(where)) {
                return new AndExpression(where, parenthesis);
            } else {
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java
@@ -62,8 +62,8 @@
            // charindex(',100,' , ',0,100,101,') <> 0
            return "charindex(',%s,' , ','+%s+',') <> 0".formatted(var, var2);
        } else if (dataBasyType == DataBaseType.POSTGRE_SQL) {
            // (select position(',100,' in ',0,100,101,')) <> 0
            return "(select position(',%s,' in ','||%s||',')) <> 0".formatted(var, var2);
            // (select strpos(',0,100,101,' , ',100,')) <> 0
            return "(select strpos(','||%s||',' , ',%s,')) <> 0".formatted(var2, var);
        } else if (dataBasyType == DataBaseType.ORACLE) {
            // instr(',0,100,101,' , ',100,') <> 0
            return "instr(','||%s||',' , ',%s,') <> 0".formatted(var2, var);
ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java
@@ -15,12 +15,12 @@
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.ResponseInputStream;
import software.amazon.awssdk.core.async.AsyncRequestBody;
import software.amazon.awssdk.core.async.AsyncResponseTransformer;
import software.amazon.awssdk.core.async.BlockingInputStreamAsyncRequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.S3Configuration;
import software.amazon.awssdk.services.s3.crt.S3CrtHttpConfiguration;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.model.NoSuchBucketException;
import software.amazon.awssdk.services.s3.model.S3Exception;
@@ -83,8 +83,8 @@
            StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
                AwsBasicCredentials.create(properties.getAccessKey(), properties.getSecretKey()));
            //MinIO ä½¿ç”¨ HTTPS é™åˆ¶ä½¿ç”¨åŸŸåè®¿é—®ï¼Œç«™ç‚¹å¡«åŸŸåã€‚需要启用路径样式访问
            boolean isStyle = !StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE);
            //使用对象存储服务时要求明确配置访问样式(路径样式或虚拟托管样式)。需要启用路径样式访问
            boolean isStyle = true;
            //创建AWS基于 CRT çš„ S3 å®¢æˆ·ç«¯
            this.client = S3AsyncClient.crtBuilder()
@@ -95,6 +95,9 @@
                .minimumPartSizeInBytes(10 * 1025 * 1024L)
                .checksumValidationEnabled(false)
                .forcePathStyle(isStyle)
                .httpConfiguration(S3CrtHttpConfiguration.builder()
                    .connectionTimeout(Duration.ofSeconds(60)) // è®¾ç½®è¿žæŽ¥è¶…æ—¶
                    .build())
                .build();
            //AWS基于 CRT çš„ S3 AsyncClient å®žä¾‹ç”¨ä½œ S3 ä¼ è¾“管理器的底层客户端
@@ -178,7 +181,9 @@
                            .key(key)
                            .contentMD5(StringUtils.isNotEmpty(md5Digest) ? md5Digest : null)
                            .contentType(contentType)
                            .acl(getAccessPolicy().getObjectCannedACL())
                            // ç”¨äºŽè®¾ç½®å¯¹è±¡çš„访问控制列表(ACL)。不同云厂商对ACL的支持和实现方式有所不同,
                            // å› æ­¤æ ¹æ®å…·ä½“的云服务提供商,你可能需要进行不同的配置(自行开启,阿里云有acl权限配置,腾讯云没有acl权限配置)
                            //.acl(getAccessPolicy().getObjectCannedACL())
                            .build())
                    .addTransferListener(LoggingTransferListener.create())
                    .source(filePath).build());
@@ -215,7 +220,10 @@
        }
        try {
            // åˆ›å»ºå¼‚步请求体(length如果为空会报错)
            BlockingInputStreamAsyncRequestBody body = AsyncRequestBody.forBlockingInputStream(length);
            BlockingInputStreamAsyncRequestBody body = BlockingInputStreamAsyncRequestBody.builder()
                .contentLength(length)
                .subscribeTimeout(Duration.ofSeconds(30))
                .build();
            // ä½¿ç”¨ transferManager è¿›è¡Œä¸Šä¼ 
            Upload upload = transferManager.upload(
@@ -224,7 +232,9 @@
                        y -> y.bucket(properties.getBucketName())
                            .key(key)
                            .contentType(contentType)
                            .acl(getAccessPolicy().getObjectCannedACL())
                            // ç”¨äºŽè®¾ç½®å¯¹è±¡çš„访问控制列表(ACL)。不同云厂商对ACL的支持和实现方式有所不同,
                            // å› æ­¤æ ¹æ®å…·ä½“的云服务提供商,你可能需要进行不同的配置(自行开启,阿里云有acl权限配置,腾讯云没有acl权限配置)
                            //.acl(getAccessPolicy().getObjectCannedACL())
                            .build())
                    .build());
@@ -340,8 +350,8 @@
     * @return UploadResult åŒ…含上传后的文件信息
     * @throws OssException å¦‚果上传失败,抛出自定义异常
     */
    public UploadResult uploadSuffix(byte[] data, String suffix) {
        return upload(new ByteArrayInputStream(data), getPath(properties.getPrefix(), suffix), Long.valueOf(data.length), FileUtils.getMimeType(suffix));
    public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
        return upload(new ByteArrayInputStream(data), getPath(properties.getPrefix(), suffix), Long.valueOf(data.length), contentType);
    }
    /**
@@ -353,8 +363,8 @@
     * @return UploadResult åŒ…含上传后的文件信息
     * @throws OssException å¦‚果上传失败,抛出自定义异常
     */
    public UploadResult uploadSuffix(InputStream inputStream, String suffix, Long length) {
        return upload(inputStream, getPath(properties.getPrefix(), suffix), length, FileUtils.getMimeType(suffix));
    public UploadResult uploadSuffix(InputStream inputStream, String suffix, Long length, String contentType) {
        return upload(inputStream, getPath(properties.getPrefix(), suffix), length, contentType);
    }
    /**
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java
@@ -44,6 +44,7 @@
    }
    @SuppressWarnings("unchecked")
    @Override
    public <T> T get(Object key, Class<T> type) {
        Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, type));
        return (T) o;
@@ -55,6 +56,7 @@
        cache.put(key, value);
    }
    @Override
    public ValueWrapper putIfAbsent(Object key, Object value) {
        CAFFEINE.invalidate(getUniqueKey(key));
        return cache.putIfAbsent(key, value);
@@ -65,6 +67,7 @@
        evictIfPresent(key);
    }
    @Override
    public boolean evictIfPresent(Object key) {
        boolean b = cache.evictIfPresent(key);
        if (b) {
@@ -78,6 +81,7 @@
        cache.clear();
    }
    @Override
    public boolean invalidate() {
        return cache.invalidate();
    }
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/CacheUtils.java
@@ -1,35 +1,21 @@
package org.dromara.common.redis.utils;
import org.dromara.common.core.utils.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.redisson.api.RMap;
import org.dromara.common.core.utils.SpringUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import java.util.Set;
/**
 * ç¼“存操作工具类 {@link }
 * ç¼“存操作工具类
 *
 * @author Michelle.Chung
 * @date 2022/8/13
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings(value = {"unchecked"})
public class CacheUtils {
    private static final CacheManager CACHE_MANAGER = SpringUtils.getBean(CacheManager.class);
    /**
     * èŽ·å–ç¼“å­˜ç»„å†…æ‰€æœ‰çš„KEY
     *
     * @param cacheNames ç¼“存组名称
     */
    public static Set<Object> keys(String cacheNames) {
        RMap<Object, Object> rmap = (RMap<Object, Object>) CACHE_MANAGER.getCache(cacheNames).getNativeCache();
        return rmap.keySet();
    }
    /**
     * èŽ·å–ç¼“å­˜å€¼
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java
@@ -517,7 +517,7 @@
    }
    /**
     * èŽ·å¾—ç¼“å­˜çš„åŸºæœ¬å¯¹è±¡åˆ—è¡¨
     * èŽ·å¾—ç¼“å­˜çš„åŸºæœ¬å¯¹è±¡åˆ—è¡¨(全局匹配忽略租户 è‡ªè¡Œæ‹¼æŽ¥ç§Ÿæˆ·id)
     *
     * @param pattern å­—符串前缀
     * @return å¯¹è±¡åˆ—表
@@ -528,7 +528,7 @@
    }
    /**
     * åˆ é™¤ç¼“存的基本对象列表
     * åˆ é™¤ç¼“存的基本对象列表(全局匹配忽略租户 è‡ªè¡Œæ‹¼æŽ¥ç§Ÿæˆ·id)
     *
     * @param pattern å­—符串前缀
     */
ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java
@@ -7,9 +7,11 @@
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.SseException;
import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
@@ -50,11 +52,20 @@
                    .match(allUrlHandler.getUrls())
                    // å¯¹æœªæŽ’除的路径进行检查
                    .check(() -> {
                        HttpServletRequest request = ServletUtils.getRequest();
                        // æ£€æŸ¥æ˜¯å¦ç™»å½• æ˜¯å¦æœ‰token
                        StpUtil.checkLogin();
                        try {
                            StpUtil.checkLogin();
                        } catch (NotLoginException e) {
                            if (request.getRequestURI().contains("sse")) {
                                throw new SseException(e.getMessage(), e.getCode());
                            } else {
                                throw e;
                            }
                        }
                        // æ£€æŸ¥ header ä¸Ž param é‡Œçš„ clientid ä¸Ž token é‡Œçš„æ˜¯å¦ä¸€è‡´
                        String headerCid = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY);
                        String headerCid = request.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)) {
ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java
@@ -1,14 +1,11 @@
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;
@@ -124,25 +121,13 @@
     * @param sseMessageDto è¦å‘布的SSE消息对象
     */
    public void publishMessage(SseMessageDto sseMessageDto) {
        List<Long> 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());
            });
        }
        SseMessageDto broadcastMessage = new SseMessageDto();
        broadcastMessage.setMessage(sseMessageDto.getMessage());
        broadcastMessage.setUserIds(sseMessageDto.getUserIds());
        RedisUtils.publish(SSE_TOPIC, broadcastMessage, consumer -> {
            log.info("SSE发送主题订阅消息topic:{} session keys:{} message:{}",
                SSE_TOPIC, sseMessageDto.getUserIds(), sseMessageDto.getMessage());
        });
    }
    /**
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java
@@ -35,7 +35,8 @@
        }
        String tenantId = TenantHelper.getTenantId();
        if (StringUtils.isBlank(tenantId)) {
            log.error("无法获取有效的租户id -> Null");
            log.debug("无法获取有效的租户id -> Null");
            return super.map(name);
        }
        if (StringUtils.startsWith(name, tenantId + "")) {
            // å¦‚果存在则直接返回
@@ -61,7 +62,8 @@
        }
        String tenantId = TenantHelper.getTenantId();
        if (StringUtils.isBlank(tenantId)) {
            log.error("无法获取有效的租户id -> Null");
            log.debug("无法获取有效的租户id -> Null");
            return super.unmap(name);
        }
        if (StringUtils.startsWith(unmap, tenantId + "")) {
            // å¦‚果存在则删除
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/helper/TenantHelper.java
@@ -1,6 +1,5 @@
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 cn.hutool.core.util.ObjectUtil;
@@ -130,7 +129,7 @@
        if (!isEnable()) {
            return;
        }
        if (!isLogin() || !global) {
        if (!LoginHelper.isLogin() || !global) {
            TEMP_DYNAMIC_TENANT.set(tenantId);
            return;
        }
@@ -147,7 +146,7 @@
        if (!isEnable()) {
            return null;
        }
        if (!isLogin()) {
        if (!LoginHelper.isLogin()) {
            return TEMP_DYNAMIC_TENANT.get();
        }
        // å¦‚果线程内有值 ä¼˜å…ˆè¿”回
@@ -167,7 +166,7 @@
        if (!isEnable()) {
            return;
        }
        if (!isLogin()) {
        if (!LoginHelper.isLogin()) {
            TEMP_DYNAMIC_TENANT.remove();
            return;
        }
@@ -216,15 +215,6 @@
            tenantId = LoginHelper.getTenantId();
        }
        return tenantId;
    }
    private static boolean isLogin() {
        try {
            StpUtil.checkLogin();
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}
ruoyi-common/ruoyi-common-web/pom.xml
@@ -44,19 +44,6 @@
        </dependency>
        <dependency>
            <groupId>io.undertow</groupId>
            <artifactId>undertow-core</artifactId>
        </dependency>
        <dependency>
            <groupId>io.undertow</groupId>
            <artifactId>undertow-servlet</artifactId>
        </dependency>
        <dependency>
            <groupId>io.undertow</groupId>
            <artifactId>undertow-websockets-jsr</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/FilterConfig.java
@@ -1,18 +1,14 @@
package org.dromara.common.web.config;
import org.dromara.common.core.utils.StringUtils;
import jakarta.servlet.DispatcherType;
import org.dromara.common.web.config.properties.XssProperties;
import org.dromara.common.web.filter.RepeatableFilter;
import org.dromara.common.web.filter.XssFilter;
import jakarta.servlet.DispatcherType;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import java.util.HashMap;
import java.util.Map;
/**
 * Filter配置
@@ -23,26 +19,21 @@
@EnableConfigurationProperties(XssProperties.class)
public class FilterConfig {
    @SuppressWarnings({"rawtypes", "unchecked"})
    @Bean
    @ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
    public FilterRegistrationBean xssFilterRegistration(XssProperties xssProperties) {
        FilterRegistrationBean registration = new FilterRegistrationBean();
    public FilterRegistrationBean<XssFilter> xssFilterRegistration() {
        FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
        registration.setDispatcherTypes(DispatcherType.REQUEST);
        registration.setFilter(new XssFilter());
        registration.addUrlPatterns(StringUtils.split(xssProperties.getUrlPatterns(), StringUtils.SEPARATOR));
        registration.addUrlPatterns("/*");
        registration.setName("xssFilter");
        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
        Map<String, String> initParameters = new HashMap<>();
        initParameters.put("excludes", xssProperties.getExcludes());
        registration.setInitParameters(initParameters);
        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE + 1);
        return registration;
    }
    @SuppressWarnings({"rawtypes", "unchecked"})
    @Bean
    public FilterRegistrationBean someFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
    public FilterRegistrationBean<RepeatableFilter> someFilterRegistration() {
        FilterRegistrationBean<RepeatableFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new RepeatableFilter());
        registration.addUrlPatterns("/*");
        registration.setName("repeatableFilter");
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/XssProperties.java
@@ -3,6 +3,9 @@
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.List;
/**
 * xss过滤 é…ç½®å±žæ€§
 *
@@ -13,18 +16,13 @@
public class XssProperties {
    /**
     * è¿‡æ»¤å¼€å…³
     * Xss开关
     */
    private String enabled;
    private Boolean enabled;
    /**
     * æŽ’除链接(多个用逗号分隔)
     * æŽ’除路径
     */
    private String excludes;
    /**
     * åŒ¹é…é“¾æŽ¥
     */
    private String urlPatterns;
    private List<String> excludeUrls = new ArrayList<>();
}
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/XssFilter.java
@@ -1,6 +1,8 @@
package org.dromara.common.web.filter;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.web.config.properties.XssProperties;
import org.springframework.http.HttpMethod;
import jakarta.servlet.*;
@@ -23,13 +25,8 @@
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String tempExcludes = filterConfig.getInitParameter("excludes");
        if (StringUtils.isNotEmpty(tempExcludes)) {
            String[] url = tempExcludes.split(StringUtils.SEPARATOR);
            for (int i = 0; url != null && i < url.length; i++) {
                excludes.add(url[i]);
            }
        }
        XssProperties properties = SpringUtils.getBean(XssProperties.class);
        excludes.addAll(properties.getExcludeUrls());
    }
    @Override
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/XssHttpServletRequestWrapper.java
@@ -14,6 +14,7 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/**
 * XSS过滤处理
@@ -29,6 +30,33 @@
    }
    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        if (value != null) {
            return HtmlUtil.cleanHtmlTag(value).trim();
        }
        return value;
    }
    @Override
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> valueMap = super.getParameterMap();
        for (Map.Entry<String, String[]> entry : valueMap.entrySet()) {
            String[] values = entry.getValue();
            if (values != null) {
                int length = values.length;
                String[] escapseValues = new String[length];
                for (int i = 0; i < length; i++) {
                    // é˜²xss攻击和过滤前后空格
                    escapseValues[i] = HtmlUtil.cleanHtmlTag(values[i]).trim();
                }
                valueMap.put(entry.getKey(), escapseValues);
            }
        }
        return valueMap;
    }
    @Override
    public String[] getParameterValues(String name) {
        String[] values = super.getParameterValues(name);
        if (values != null) {
@@ -40,7 +68,7 @@
            }
            return escapseValues;
        }
        return super.getParameterValues(name);
        return values;
    }
    @Override
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java
@@ -2,14 +2,17 @@
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpStatus;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.exception.SseException;
import org.dromara.common.core.exception.base.BaseException;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
@@ -51,6 +54,27 @@
        log.error(e.getMessage());
        Integer code = e.getCode();
        return ObjectUtil.isNotNull(code) ? R.fail(code, e.getMessage()) : R.fail(e.getMessage());
    }
    /**
     * è®¤è¯å¤±è´¥
     */
    @ResponseStatus(org.springframework.http.HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(SseException.class)
    public String handleNotLoginException(SseException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage());
        return JsonUtils.toJsonString(R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源"));
    }
    /**
     * servlet异常
     */
    @ExceptionHandler(ServletException.class)
    public R<Void> handleServletException(ServletException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生未知异常.", requestURI, e);
        return R.fail(e.getMessage());
    }
    /**
@@ -152,7 +176,7 @@
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error(e.getMessage());
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        String message = StreamUtils.join(e.getBindingResult().getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ", ");
        return R.fail(message);
    }
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java
@@ -2,6 +2,7 @@
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
@@ -64,9 +65,11 @@
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        StopWatch stopWatch = KEY_CACHE.get();
        stopWatch.stop();
        log.info("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime());
        KEY_CACHE.remove();
        if (ObjectUtil.isNotNull(stopWatch)) {
            stopWatch.stop();
            log.info("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime());
            KEY_CACHE.remove();
        }
    }
    /**
ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml
@@ -13,8 +13,8 @@
spring:
  security:
    user:
      name: ruoyi
      password: 123456
      name: @monitor.username@
      password: @monitor.password@
  boot:
    admin:
      ui:
@@ -44,5 +44,5 @@
    metadata:
      username: ${spring.boot.admin.client.username}
      userpassword: ${spring.boot.admin.client.password}
  username: ruoyi
  password: 123456
  username: @monitor.username@
  password: @monitor.password@
ruoyi-extend/ruoyi-snailjob-server/pom.xml
@@ -16,6 +16,18 @@
            <groupId>com.aizuda</groupId>
            <artifactId>snail-job-server-starter</artifactId>
            <version>${snailjob.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.scala-lang</groupId>
                    <artifactId>scala-library</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>2.13.9</version>
        </dependency>
        <dependency>
ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-dev.yml
@@ -46,5 +46,5 @@
    metadata:
      username: ${spring.boot.admin.client.username}
      userpassword: ${spring.boot.admin.client.password}
  username: ruoyi
  password: 123456
  username: @monitor.username@
  password: @monitor.password@
ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod.yml
@@ -46,5 +46,5 @@
    metadata:
      username: ${spring.boot.admin.client.username}
      userpassword: ${spring.boot.admin.client.password}
  username: ruoyi
  password: 123456
  username: @monitor.username@
  password: @monitor.password@
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestDemoMapper.java
@@ -48,7 +48,7 @@
        @DataColumn(key = "deptName", value = "dept_id"),
        @DataColumn(key = "userName", value = "user_id")
    }, joinStr = "AND")
    List<TestDemo> selectBatchIds(@Param(Constants.COLL) Collection<? extends Serializable> idList);
    List<TestDemo> selectByIds(@Param(Constants.COLL) Collection<? extends Serializable> idList);
    @Override
    @DataPermission({
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java
@@ -101,7 +101,7 @@
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            // åšä¸€äº›ä¸šåŠ¡ä¸Šçš„æ ¡éªŒ,判断是否需要校验
            List<TestDemo> list = baseMapper.selectBatchIds(ids);
            List<TestDemo> list = baseMapper.selectByIds(ids);
            if (list.size() != ids.size()) {
                throw new ServiceException("您没有删除权限!");
            }
ruoyi-modules/ruoyi-generator/pom.xml
@@ -64,19 +64,19 @@
<!--        <dependency>-->
<!--            <groupId>org.anyline</groupId>-->
<!--            <artifactId>anyline-data-jdbc-oracle</artifactId>-->
<!--        <version>${anyline.version}</version>-->
<!--            <version>${anyline.version}</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.anyline</groupId>-->
<!--            <artifactId>anyline-data-jdbc-postgresql</artifactId>-->
<!--        <version>${anyline.version}</version>-->
<!--            <version>${anyline.version}</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.anyline</groupId>-->
<!--            <artifactId>anyline-data-jdbc-mssql</artifactId>-->
<!--        <version>${anyline.version}</version>-->
<!--            <version>${anyline.version}</version>-->
<!--        </dependency>-->
    </dependencies>
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/constant/GenConstants.java
@@ -56,13 +56,13 @@
     * æ•°æ®åº“时间类型
     */
    String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp", "year", "interval",
        "smalldatetime", "datetime2", "datetimeoffset"};
        "smalldatetime", "datetime2", "datetimeoffset", "timestamptz"};
    /**
     * æ•°æ®åº“数字类型
     */
    String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "number", "integer",
        "bit", "bigint", "float", "double", "decimal", "numeric", "real", "double precision",
    String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "int2", "int4", "int8", "number", "integer",
        "bit", "bigint", "float", "float4", "float8", "double", "decimal", "numeric", "real", "double precision",
        "smallserial", "serial", "bigserial", "money", "smallmoney"};
    /**
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTable.java
@@ -162,7 +162,7 @@
     * ä¸Šçº§èœå•ID字段
     */
    @TableField(exist = false)
    private String parentMenuId;
    private Long parentMenuId;
    /**
     * ä¸Šçº§èœå•名称字段
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableMapper.java
@@ -2,10 +2,8 @@
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.generator.domain.GenTable;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@@ -40,6 +38,14 @@
     */
    GenTable selectGenTableByName(String tableName);
    /**
     * æŸ¥è¯¢æŒ‡å®šæ•°æ®æºä¸‹çš„æ‰€æœ‰è¡¨ååˆ—表
     *
     * @param dataName æ•°æ®æºåç§°ï¼Œç”¨äºŽé€‰æ‹©ä¸åŒçš„æ•°æ®æº
     * @return å½“前数据库中的表名列表
     *
     * @DS("") ä½¿ç”¨é»˜è®¤æ•°æ®æºæ‰§è¡ŒæŸ¥è¯¢æ“ä½œ
     */
    @DS("")
    List<String> selectTableNameList(String dataName);
}
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java
@@ -137,7 +137,7 @@
        }
        // è¿‡æ»¤å¹¶è½¬æ¢è¡¨æ ¼æ•°æ®
        List<GenTable> tables = tablesMap.values().stream()
            .filter(x -> !StringUtils.containsAnyIgnoreCase(x.getName(), TABLE_IGNORE))
            .filter(x -> !startWithAnyIgnoreCase(x.getName(), TABLE_IGNORE))
            .filter(x -> {
                if (CollUtil.isEmpty(tableNames)) {
                    return true;
@@ -172,6 +172,16 @@
        // æ‰‹åŠ¨åˆ†é¡µ set数据
        page.setRecords(CollUtil.page((int) page.getCurrent() - 1, (int) page.getSize(), tables));
        return TableDataInfo.build(page);
    }
    public static boolean startWithAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) {
        // åˆ¤æ–­æ˜¯å¦æ˜¯ä»¥æŒ‡å®šå­—符串开头
        for (CharSequence searchCharSequence : searchCharSequences) {
            if (StringUtils.startsWithIgnoreCase(cs, searchCharSequence)) {
                return true;
            }
        }
        return false;
    }
    /**
@@ -548,7 +558,7 @@
            String treeCode = paramsObj.getStr(GenConstants.TREE_CODE);
            String treeParentCode = paramsObj.getStr(GenConstants.TREE_PARENT_CODE);
            String treeName = paramsObj.getStr(GenConstants.TREE_NAME);
            String parentMenuId = paramsObj.getStr(GenConstants.PARENT_MENU_ID);
            Long parentMenuId = paramsObj.getLong(GenConstants.PARENT_MENU_ID);
            String parentMenuName = paramsObj.getStr(GenConstants.PARENT_MENU_NAME);
            genTable.setTreeCode(treeCode);
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm
@@ -58,7 +58,7 @@
     * ${column.columnComment}Url
     */
    @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "${column.javaField}")
    private String ${column.javaField}Url";
    private String ${column.javaField}Url;
#end
#end
#end
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantController.java
@@ -176,4 +176,18 @@
        return toAjax(TenantHelper.ignore(() -> tenantService.syncTenantPackage(tenantId, packageId)));
    }
    /**
     * åŒæ­¥ç§Ÿæˆ·å­—å…¸
     */
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
    @Log(title = "同步租户字典", businessType = BusinessType.INSERT)
    @GetMapping("/syncTenantDict")
    public R<Void> syncTenantDict() {
        if (!TenantHelper.isEnable()) {
            return R.fail("当前未开启租户模式");
        }
        tenantService.syncTenantDict();
        return R.ok("同步租户字典成功");
    }
}
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/SysUserImportListener.java
@@ -3,10 +3,14 @@
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.digest.BCrypt;
import cn.hutool.http.HtmlUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
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.ValidatorUtils;
import org.dromara.common.excel.core.ExcelListener;
import org.dromara.common.excel.core.ExcelResult;
@@ -79,8 +83,12 @@
            }
        } catch (Exception e) {
            failureNum++;
            String msg = "<br/>" + failureNum + "、账号 " + userVo.getUserName() + " å¯¼å…¥å¤±è´¥ï¼š";
            failureMsg.append(msg).append(e.getMessage());
            String msg = "<br/>" + failureNum + "、账号 " + HtmlUtil.cleanHtmlTag(userVo.getUserName()) + " å¯¼å…¥å¤±è´¥ï¼š";
            String message = e.getMessage();
            if (e instanceof ConstraintViolationException cvException) {
                message = StreamUtils.join(cvException.getConstraintViolations(), ConstraintViolation::getMessage, ", ");
            }
            failureMsg.append(msg).append(message);
            log.error(msg, e);
        }
    }
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTenantService.java
@@ -79,4 +79,9 @@
     * åŒæ­¥ç§Ÿæˆ·å¥—餐
     */
    Boolean syncTenantPackage(String tenantId, Long packageId);
    /**
     * åŒæ­¥ç§Ÿæˆ·å­—å…¸
     */
    void syncTenantDict();
}
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java
@@ -275,6 +275,8 @@
                dept.setAncestors(newAncestors);
                updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors);
            }
        } else {
            dept.setAncestors(oldDept.getAncestors());
        }
        int result = baseMapper.updateById(dept);
        if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors())
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java
@@ -3,18 +3,18 @@
import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.ip.AddressUtils;
import org.dromara.common.log.event.OperLogEvent;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.system.domain.SysOperLog;
import org.dromara.system.domain.bo.SysOperLogBo;
import org.dromara.system.domain.vo.SysOperLogVo;
import org.dromara.system.mapper.SysOperLogMapper;
import org.dromara.system.service.ISysOperLogService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@@ -51,8 +51,18 @@
    @Override
    public TableDataInfo<SysOperLogVo> selectPageOperLogList(SysOperLogBo operLog, PageQuery pageQuery) {
        LambdaQueryWrapper<SysOperLog> lqw = buildQueryWrapper(operLog);
        if (StringUtils.isBlank(pageQuery.getOrderByColumn())) {
            pageQuery.setOrderByColumn("oper_id");
            pageQuery.setIsAsc("desc");
        }
        Page<SysOperLogVo> page = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(page);
    }
    private LambdaQueryWrapper<SysOperLog> buildQueryWrapper(SysOperLogBo operLog) {
        Map<String, Object> params = operLog.getParams();
        LambdaQueryWrapper<SysOperLog> lqw = new LambdaQueryWrapper<SysOperLog>()
        return new LambdaQueryWrapper<SysOperLog>()
            .like(StringUtils.isNotBlank(operLog.getOperIp()), SysOperLog::getOperIp, operLog.getOperIp())
            .like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle())
            .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0,
@@ -67,12 +77,6 @@
            .like(StringUtils.isNotBlank(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName())
            .between(params.get("beginTime") != null && params.get("endTime") != null,
                SysOperLog::getOperTime, params.get("beginTime"), params.get("endTime"));
        if (StringUtils.isBlank(pageQuery.getOrderByColumn())) {
            pageQuery.setOrderByColumn("oper_id");
            pageQuery.setIsAsc("desc");
        }
        Page<SysOperLogVo> page = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(page);
    }
    /**
@@ -95,23 +99,8 @@
     */
    @Override
    public List<SysOperLogVo> selectOperLogList(SysOperLogBo operLog) {
        Map<String, Object> params = operLog.getParams();
        return baseMapper.selectVoList(new LambdaQueryWrapper<SysOperLog>()
            .like(StringUtils.isNotBlank(operLog.getOperIp()), SysOperLog::getOperIp, operLog.getOperIp())
            .like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle())
            .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0,
                SysOperLog::getBusinessType, operLog.getBusinessType())
            .func(f -> {
                if (ArrayUtil.isNotEmpty(operLog.getBusinessTypes())) {
                    f.in(SysOperLog::getBusinessType, Arrays.asList(operLog.getBusinessTypes()));
                }
            })
            .eq(operLog.getStatus() != null && operLog.getStatus() > 0,
                SysOperLog::getStatus, operLog.getStatus())
            .like(StringUtils.isNotBlank(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName())
            .between(params.get("beginTime") != null && params.get("endTime") != null,
                SysOperLog::getOperTime, params.get("beginTime"), params.get("endTime"))
            .orderByDesc(SysOperLog::getOperId));
        LambdaQueryWrapper<SysOperLog> lqw = buildQueryWrapper(operLog);
        return baseMapper.selectVoList(lqw.orderByDesc(SysOperLog::getOperId));
    }
    /**
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java
@@ -195,7 +195,7 @@
        OssClient storage = OssFactory.instance();
        UploadResult uploadResult;
        try {
            uploadResult = storage.uploadSuffix(file.getBytes(), suffix);
            uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
        } catch (IOException e) {
            throw new ServiceException(e.getMessage());
        }
@@ -244,7 +244,7 @@
        if (isValid) {
            // åšä¸€äº›ä¸šåŠ¡ä¸Šçš„æ ¡éªŒ,判断是否需要校验
        }
        List<SysOss> list = baseMapper.selectBatchIds(ids);
        List<SysOss> list = baseMapper.selectByIds(ids);
        for (SysOss sysOss : list) {
            OssClient storage = OssFactory.instance(sysOss.getService());
            storage.delete(sysOss.getUrl());
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java
@@ -1,6 +1,8 @@
package org.dromara.system.service.impl;
import cn.dev33.satoken.secure.BCrypt;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
@@ -14,9 +16,13 @@
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
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.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.redis.utils.CacheUtils;
import org.dromara.common.tenant.core.TenantEntity;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.*;
import org.dromara.system.domain.bo.SysTenantBo;
import org.dromara.system.domain.vo.SysTenantVo;
@@ -27,10 +33,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.*;
/**
 * ç§Ÿæˆ·Service业务层处理
@@ -201,7 +204,7 @@
        String numbers = RandomUtil.randomNumbers(6);
        // åˆ¤æ–­æ˜¯å¦å­˜åœ¨ï¼Œå¦‚果存在则重新生成
        if (tenantIds.contains(numbers)) {
            generateTenantId(tenantIds);
            return generateTenantId(tenantIds);
        }
        return numbers;
    }
@@ -266,7 +269,9 @@
    @CacheEvict(cacheNames = CacheNames.SYS_TENANT, key = "#bo.tenantId")
    @Override
    public int updateTenantStatus(SysTenantBo bo) {
        SysTenant tenant = MapstructUtils.convert(bo, SysTenant.class);
        SysTenant tenant = new SysTenant();
        tenant.setId(bo.getId());
        tenant.setStatus(bo.getStatus());
        return baseMapper.updateById(tenant);
    }
@@ -369,4 +374,91 @@
        }
        return true;
    }
    /**
     * åŒæ­¥ç§Ÿæˆ·å­—å…¸
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void syncTenantDict() {
        // æŸ¥è¯¢è¶…管 æ‰€æœ‰å­—典数据
        List<SysDictType> dictTypeList = new ArrayList<>();
        List<SysDictData> dictDataList = new ArrayList<>();
        TenantHelper.ignore(() -> {
            dictTypeList.addAll(dictTypeMapper.selectList());
            dictDataList.addAll(dictDataMapper.selectList());
        });
        Map<String, List<SysDictType>> typeMap = StreamUtils.groupByKey(dictTypeList, TenantEntity::getTenantId);
        Map<String, Map<String, List<SysDictData>>> typeDataMap = StreamUtils.groupBy2Key(
            dictDataList, TenantEntity::getTenantId, SysDictData::getDictType);
        // ç®¡ç†ç§Ÿæˆ·å­—典数据
        List<SysDictType> defaultTypeMap = typeMap.get(TenantConstants.DEFAULT_TENANT_ID);
        Map<String, List<SysDictData>> defaultTypeDataMap = typeDataMap.get(TenantConstants.DEFAULT_TENANT_ID);
        // èŽ·å–æ‰€æœ‰ç§Ÿæˆ·ç¼–å·
        List<String> tenantIds = baseMapper.selectObjs(
            new LambdaQueryWrapper<SysTenant>().select(SysTenant::getTenantId)
                .eq(SysTenant::getStatus, TenantConstants.NORMAL), x -> {return Convert.toStr(x);});
        List<SysDictType> saveTypeList = new ArrayList<>();
        List<SysDictData> saveDataList = new ArrayList<>();
        Set<String> set = new HashSet<>();
        for (String tenantId : tenantIds) {
            if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
                continue;
            }
            for (SysDictType dictType : defaultTypeMap) {
                List<String> typeList = StreamUtils.toList(typeMap.get(tenantId), SysDictType::getDictType);
                List<SysDictData> dataList = defaultTypeDataMap.get(dictType.getDictType());
                if (typeList.contains(dictType.getDictType())) {
                    List<SysDictData> dataListTenant = typeDataMap.get(tenantId).get(dictType.getDictType());
                    Map<String, SysDictData> map = StreamUtils.toIdentityMap(dataListTenant, SysDictData::getDictValue);
                    for (SysDictData dictData : dataList) {
                        if (!map.containsKey(dictData.getDictValue())) {
                            SysDictData data = BeanUtil.toBean(dictData, SysDictData.class);
                            // è®¾ç½®å­—典编码为 null
                            data.setDictCode(null);
                            data.setTenantId(tenantId);
                            data.setCreateTime(null);
                            data.setUpdateTime(null);
                            set.add(tenantId);
                            saveDataList.add(data);
                        }
                    }
                } else {
                    SysDictType type = BeanUtil.toBean(dictType, SysDictType.class);
                    type.setDictId(null);
                    type.setTenantId(tenantId);
                    type.setCreateTime(null);
                    type.setUpdateTime(null);
                    set.add(tenantId);
                    saveTypeList.add(type);
                    if (CollUtil.isNotEmpty(dataList)) {
                        // ç­›é€‰å‡º dictType å¯¹åº”çš„ data
                        for (SysDictData dictData : dataList) {
                            SysDictData data = BeanUtil.toBean(dictData, SysDictData.class);
                            // è®¾ç½®å­—典编码为 null
                            data.setDictCode(null);
                            data.setTenantId(tenantId);
                            data.setCreateTime(null);
                            data.setUpdateTime(null);
                            set.add(tenantId);
                            saveDataList.add(data);
                        }
                    }
                }
            }
        }
        TenantHelper.ignore(() -> {
            if (CollUtil.isNotEmpty(saveTypeList)) {
                dictTypeMapper.insertBatch(saveTypeList);
            }
            if (CollUtil.isNotEmpty(saveDataList)) {
                dictDataMapper.insertBatch(saveDataList);
            }
        });
        for (String tenantId : set) {
            TenantHelper.dynamic(tenantId, () -> CacheUtils.clear(CacheNames.SYS_DICT));
        }
    }
}
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java
@@ -42,6 +42,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
 * ç”¨æˆ· ä¸šåŠ¡å±‚å¤„ç†
@@ -628,6 +629,12 @@
        return ObjectUtil.isNull(sysUser) ? null : sysUser.getEmail();
    }
    /**
     * é€šè¿‡ç”¨æˆ·ID查询用户列表
     *
     * @param userIds ç”¨æˆ·ids
     * @return ç”¨æˆ·åˆ—表
     */
    @Override
    public List<UserDTO> selectListByIds(List<Long> userIds) {
        if (CollUtil.isEmpty(userIds)) {
@@ -636,28 +643,54 @@
        List<SysUserVo> list = baseMapper.selectVoList(new LambdaQueryWrapper<SysUser>()
            .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName, SysUser::getEmail, SysUser::getPhonenumber)
            .eq(SysUser::getStatus, UserConstants.USER_NORMAL)
            .in(CollUtil.isNotEmpty(userIds), SysUser::getUserId, userIds));
            .in(SysUser::getUserId, userIds));
        return BeanUtil.copyToList(list, UserDTO.class);
    }
    /**
     * é€šè¿‡è§’色ID查询用户ID
     *
     * @param roleIds è§’色ids
     * @return ç”¨æˆ·ids
     */
    @Override
    public List<Long> selectUserIdsByRoleIds(List<Long> roleIds) {
        List<SysUserRole> userRoles = userRoleMapper.selectList(
            new LambdaQueryWrapper<SysUserRole>().in(SysUserRole::getRoleId, roleIds));
        return StreamUtils.toList(userRoles, SysUserRole::getUserId);
    }
    @Override
    public List<UserDTO> selectUsersByRoleIds(List<Long> roleIds) {
        if (CollUtil.isEmpty(roleIds)) {
            return List.of();
        }
        List<SysUserRole> userRoles = userRoleMapper.selectList(
            new LambdaQueryWrapper<SysUserRole>().in(SysUserRole::getRoleId, roleIds));
        List<Long> userIds = StreamUtils.toList(userRoles, SysUserRole::getUserId);
        return selectListByIds(userIds);
        return StreamUtils.toList(userRoles, SysUserRole::getUserId);
    }
    /**
     * é€šè¿‡è§’色ID查询用户
     *
     * @param roleIds è§’色ids
     * @return ç”¨æˆ·
     */
    @Override
    public List<UserDTO> selectUsersByRoleIds(List<Long> roleIds) {
        if (CollUtil.isEmpty(roleIds)) {
            return List.of();
        }
        // é€šè¿‡è§’色ID获取用户角色信息
        List<SysUserRole> userRoles = userRoleMapper.selectList(
            new LambdaQueryWrapper<SysUserRole>().in(SysUserRole::getRoleId, roleIds));
        // èŽ·å–ç”¨æˆ·ID列表
        Set<Long> userIds = StreamUtils.toSet(userRoles, SysUserRole::getUserId);
        return selectListByIds(new ArrayList<>(userIds));
    }
    /**
     * é€šè¿‡éƒ¨é—¨ID查询用户
     *
     * @param deptIds éƒ¨é—¨ids
     * @return ç”¨æˆ·
     */
    @Override
    public List<UserDTO> selectUsersByDeptIds(List<Long> deptIds) {
        if (CollUtil.isEmpty(deptIds)) {
@@ -666,7 +699,7 @@
        List<SysUserVo> list = baseMapper.selectVoList(new LambdaQueryWrapper<SysUser>()
            .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName, SysUser::getEmail, SysUser::getPhonenumber)
            .eq(SysUser::getStatus, UserConstants.USER_NORMAL)
            .in(CollUtil.isNotEmpty(deptIds), SysUser::getDeptId, deptIds));
            .in(SysUser::getDeptId, deptIds));
        return BeanUtil.copyToList(list, UserDTO.class);
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java
@@ -277,6 +277,7 @@
        if (StringUtils.isNotBlank(taskBo.getProcessDefinitionKey())) {
            queryWrapper.eq("t.processDefinitionKey", taskBo.getProcessDefinitionKey());
        }
        queryWrapper.orderByDesc("t.CREATE_TIME_");
        Page<TaskVo> page = actTaskMapper.getTaskWaitByPage(pageQuery.build(), queryWrapper);
        List<TaskVo> taskList = page.getRecords();
@@ -366,6 +367,7 @@
        queryWrapper.like(StringUtils.isNotBlank(taskBo.getProcessDefinitionName()), "t.processDefinitionName", taskBo.getProcessDefinitionName());
        queryWrapper.eq(StringUtils.isNotBlank(taskBo.getProcessDefinitionKey()), "t.processDefinitionKey", taskBo.getProcessDefinitionKey());
        queryWrapper.eq("t.assignee_", userId);
        queryWrapper.orderByDesc("t.START_TIME_");
        Page<TaskVo> page = actTaskMapper.getTaskFinishByPage(pageQuery.build(), queryWrapper);
        List<TaskVo> taskList = page.getRecords();
@@ -402,6 +404,7 @@
            queryWrapper.eq("t.processDefinitionKey", taskBo.getProcessDefinitionKey());
        }
        queryWrapper.eq("t.assignee_", userId);
        queryWrapper.orderByDesc("t.START_TIME_");
        Page<TaskVo> page = actTaskMapper.getTaskCopyByPage(pageQuery.build(), queryWrapper);
        List<TaskVo> taskList = page.getRecords();
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java
@@ -142,11 +142,16 @@
     *
     * @param processTaskEvent å‚æ•°
     */
    @EventListener(condition = "#processTaskEvent.key=='leave1' && #processTaskEvent.taskDefinitionKey=='Activity_14633hx'")
    @EventListener(condition = "#processTaskEvent.key.startsWith('leave')")
    public void processTaskHandler(ProcessTaskEvent processTaskEvent) {
        log.info("当前任务执行了{}", processTaskEvent.toString());
        TestLeave testLeave = baseMapper.selectById(Long.valueOf(processTaskEvent.getBusinessKey()));
        testLeave.setStatus(BusinessStatusEnum.WAITING.getStatus());
        baseMapper.updateById(testLeave);
        // æ‰€æœ‰demo案例的申请人节点id
        String[] ids = {"Activity_14633hx", "Activity_19b1i4j", "Activity_0uscrk3",
            "Activity_0uscrk3", "Activity_0x6b71j", "Activity_0zy3g6j", "Activity_06a55t0"};
        if (StringUtils.equalsAny(processTaskEvent.getTaskDefinitionKey(), ids)) {
            log.info("当前任务执行了{}", processTaskEvent.toString());
            TestLeave testLeave = baseMapper.selectById(Long.valueOf(processTaskEvent.getBusinessKey()));
            testLeave.setStatus(BusinessStatusEnum.WAITING.getStatus());
            baseMapper.updateById(testLeave);
        }
    }
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfDefinitionConfigServiceImpl.java
@@ -96,7 +96,7 @@
        baseMapper.delete(new LambdaQueryWrapper<WfDefinitionConfig>().eq(WfDefinitionConfig::getTableName, bo.getTableName()));
        add.setTableName(add.getTableName().toLowerCase());
        boolean flag = baseMapper.insertOrUpdate(add);
        if (baseMapper.insertOrUpdate(add)) {
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfFormManageServiceImpl.java
@@ -41,7 +41,7 @@
    @Override
    public List<WfFormManageVo> queryByIds(List<Long> ids) {
        return baseMapper.selectVoBatchIds(ids);
        return baseMapper.selectVoByIds(ids);
    }
    /**
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java
@@ -7,7 +7,7 @@
import org.dromara.workflow.service.IActHiProcinstService;
import org.dromara.workflow.service.IActProcessInstanceService;
import org.dromara.workflow.utils.WorkflowUtils;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -24,9 +24,9 @@
public class WorkflowServiceImpl implements WorkflowService {
    @Autowired(required = false)
    private RuntimeService runtimeService;
    private final IActProcessInstanceService iActProcessInstanceService;
    private final IActHiProcinstService iActHiProcinstService;
    private TaskService taskService;
    private final IActProcessInstanceService actProcessInstanceService;
    private final IActHiProcinstService actHiProcinstService;
    /**
     * è¿è¡Œä¸­çš„实例 åˆ é™¤ç¨‹å®žä¾‹ï¼Œåˆ é™¤åŽ†å²è®°å½•ï¼Œåˆ é™¤ä¸šåŠ¡ä¸Žæµç¨‹å…³è”ä¿¡æ¯
     *
@@ -35,7 +35,7 @@
     */
    @Override
    public boolean deleteRunAndHisInstance(List<String> businessKeys) {
        return iActProcessInstanceService.deleteRunAndHisInstance(businessKeys);
        return actProcessInstanceService.deleteRunAndHisInstance(businessKeys);
    }
    /**
@@ -67,7 +67,7 @@
     */
    @Override
    public void setVariable(String taskId, String variableName, Object value) {
        runtimeService.setVariable(taskId, variableName, value);
        taskService.setVariable(taskId, variableName, value);
    }
    /**
@@ -78,7 +78,7 @@
     */
    @Override
    public void setVariables(String taskId, Map<String, Object> variables) {
        runtimeService.setVariables(taskId, variables);
        taskService.setVariables(taskId, variables);
    }
    /**
@@ -90,7 +90,7 @@
     */
    @Override
    public void setVariableLocal(String taskId, String variableName, Object value) {
        runtimeService.setVariableLocal(taskId, variableName, value);
        taskService.setVariableLocal(taskId, variableName, value);
    }
    /**
@@ -101,7 +101,7 @@
     */
    @Override
    public void setVariablesLocal(String taskId, Map<String, Object> variables) {
        runtimeService.setVariablesLocal(taskId, variables);
        taskService.setVariablesLocal(taskId, variables);
    }
    /**
@@ -112,7 +112,7 @@
     */
    @Override
    public String getInstanceIdByBusinessKey(String businessKey) {
        ActHiProcinst actHiProcinst = iActHiProcinstService.selectByBusinessKey(businessKey);
        ActHiProcinst actHiProcinst = actHiProcinstService.selectByBusinessKey(businessKey);
        if (actHiProcinst == null) {
            return StrUtil.EMPTY;
        }
ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActTaskMapper.xml
@@ -41,8 +41,7 @@
              FROM ACT_RU_TASK RES
                       INNER JOIN ACT_HI_PROCINST AHP ON RES.PROC_INST_ID_ = AHP.PROC_INST_ID_
                       INNER JOIN ACT_RE_PROCDEF ARP ON ARP.ID_ = RES.PROC_DEF_ID_
              WHERE RES.PARENT_TASK_ID_ IS NULL
              ORDER BY RES.CREATE_TIME_ DESC) t ${ew.getCustomSqlSegment}
              WHERE RES.PARENT_TASK_ID_ IS NULL) t ${ew.getCustomSqlSegment}
    </select>
    <select id="getTaskFinishByPage" resultMap="TaskVoResult">
@@ -57,7 +56,7 @@
                       INNER JOIN ACT_HI_PROCINST AHP ON HTI.PROC_INST_ID_ = AHP.PROC_INST_ID_
                       INNER JOIN ACT_RE_PROCDEF ARP ON ARP.ID_ = HTI.PROC_DEF_ID_
              WHERE HTI.PARENT_TASK_ID_ IS NULL AND HTI.END_TIME_ IS NOT NULL
              ORDER BY HTI.START_TIME_ DESC) t ${ew.getCustomSqlSegment}
             ) t ${ew.getCustomSqlSegment}
    </select>
    <select id="getTaskCopyByPage" resultMap="TaskVoResult">
@@ -73,6 +72,6 @@
                       INNER JOIN ACT_RE_PROCDEF ARP ON ARP.ID_ = AHT.PROC_DEF_ID_
              WHERE AHT.PARENT_TASK_ID_ IS NOT NULL
                and AHT.scope_type_ = 'copy'
              ORDER BY AHT.START_TIME_ DESC) t ${ew.getCustomSqlSegment}
             ) t ${ew.getCustomSqlSegment}
    </select>
</mapper>
script/docker/docker-compose.yml
@@ -100,7 +100,7 @@
    network_mode: "host"
  ruoyi-server1:
    image: ruoyi/ruoyi-server:5.2.2
    image: ruoyi/ruoyi-server:5.2.3
    container_name: ruoyi-server1
    environment:
      # æ—¶åŒºä¸Šæµ·
@@ -115,7 +115,7 @@
    network_mode: "host"
  ruoyi-server2:
    image: ruoyi/ruoyi-server:5.2.2
    image: ruoyi/ruoyi-server:5.2.3
    container_name: ruoyi-server2
    environment:
      # æ—¶åŒºä¸Šæµ·
@@ -130,7 +130,7 @@
    network_mode: "host"
  ruoyi-monitor-admin:
    image: ruoyi/ruoyi-monitor-admin:5.2.2
    image: ruoyi/ruoyi-monitor-admin:5.2.3
    container_name: ruoyi-monitor-admin
    environment:
      # æ—¶åŒºä¸Šæµ·
@@ -142,7 +142,7 @@
    network_mode: "host"
  ruoyi-snailjob-server:
    image: ruoyi/ruoyi-snailjob-server:5.2.2
    image: ruoyi/ruoyi-snailjob-server:5.2.3
    container_name: ruoyi-snailjob-server
    environment:
      # æ—¶åŒºä¸Šæµ·
script/sql/oracle/oracle_ry_job.sql
ÎļþÃû´Ó script/sql/oracle/snail_job_oracle.sql ÐÞ¸Ä
@@ -74,6 +74,7 @@
COMMENT ON TABLE sj_group_config IS '组配置';
INSERT INTO sj_group_config (namespace_id, group_name, description, token, group_status, version, group_partition, id_generator_mode, init_scene, bucket_index, create_dt, update_dt) VALUES ('dev', 'ruoyi_group', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, 4, sysdate, sysdate);
INSERT INTO sj_group_config (namespace_id, group_name, description, token, group_status, version, group_partition, id_generator_mode, init_scene, bucket_index, create_dt, update_dt) VALUES ('prod', 'ruoyi_group', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, 4, sysdate, sysdate);
-- sj_notify_config
CREATE TABLE sj_notify_config
script/sql/oracle/oracle_ry_workflow.sql
script/sql/postgres/postgres_ry_job.sql
ÎļþÃû´Ó script/sql/postgres/snail_job_postgre.sql ÐÞ¸Ä
@@ -68,6 +68,7 @@
COMMENT ON TABLE sj_group_config IS '组配置';
INSERT INTO sj_group_config VALUES (1, 'dev', 'ruoyi_group', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, 4, now(), now());
INSERT INTO sj_group_config VALUES (2, 'prod', 'ruoyi_group', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, 4, now(), now());
-- sj_notify_config
CREATE TABLE sj_notify_config
script/sql/postgres/postgres_ry_vue_5.X.sql
@@ -427,7 +427,7 @@
-- ----------------------------
-- ä¸€çº§èœå•
insert into sys_menu values('1', '系统管理', '0', '1', 'system',           null, '', '1', '0', 'M', '0', '0', '', 'system',   103, 1, now(), null, null, '系统管理目录');
insert into sys_menu values('6', '系统管理', '0', '2', 'tenant',           null, '', '1', '0', 'M', '0', '0', '', 'chart',    103, 1, now(), null, null, '租户管理目录');
insert into sys_menu values('6', '租户管理', '0', '2', 'tenant',           null, '', '1', '0', 'M', '0', '0', '', 'chart',    103, 1, now(), null, null, '租户管理目录');
insert into sys_menu values('2', '系统监控', '0', '3', 'monitor',          null, '', '1', '0', 'M', '0', '0', '', 'monitor',  103, 1, now(), null, null, '系统监控目录');
insert into sys_menu values('3', '系统工具', '0', '4', 'tool',             null, '', '1', '0', 'M', '0', '0', '', 'tool',     103, 1, now(), null, null, '系统工具目录');
insert into sys_menu values('4', 'PLUS官网', '0', '5', 'https://gitee.com/dromara/RuoYi-Vue-Plus', null, '', '0', '0', 'M', '0', '0', '', 'guide',    103, 1, now(), null, null, 'RuoYi-Vue-Plus官网地址');
script/sql/postgres/postgres_ry_workflow.sql
script/sql/ry_job.sql
ÎļþÃû´Ó script/sql/snail_job.sql ÐÞ¸Ä
@@ -40,6 +40,7 @@
  DEFAULT CHARSET = utf8mb4 COMMENT ='组配置';
INSERT INTO `sj_group_config` VALUES (1, 'dev', 'ruoyi_group', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, 4, now(), now());
INSERT INTO `sj_group_config` VALUES (2, 'prod', 'ruoyi_group', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, 4, now(), now());
CREATE TABLE `sj_notify_config`
(
script/sql/ry_workflow.sql
script/sql/sqlserver/sqlserver_ry_job.sql
ÎļþÃû´Ó script/sql/sqlserver/snail_job_sqlserver.sql ÐÞ¸Ä
@@ -203,6 +203,8 @@
INSERT INTO sj_group_config(namespace_id, group_name, description, token, group_status, version, group_partition, id_generator_mode, init_scene, bucket_index, create_dt, update_dt) VALUES (N'dev', N'ruoyi_group', N'', N'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', N'1', N'1', N'0', N'1', N'1', N'4', getdate(), getdate())
GO
INSERT INTO sj_group_config(namespace_id, group_name, description, token, group_status, version, group_partition, id_generator_mode, init_scene, bucket_index, create_dt, update_dt) VALUES (N'prod', N'ruoyi_group', N'', N'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', N'1', N'1', N'0', N'1', N'1', N'4', getdate(), getdate())
GO
-- sj_notify_config
CREATE TABLE sj_notify_config
script/sql/sqlserver/sqlserver_ry_workflow.sql