Sfoglia il codice sorgente

!839 发布 5.6.0 版本 新年第一版

Merge pull request !839 from 疯狂的狮子Li/dev
疯狂的狮子Li 3 mesi fa
parent
commit
b629c82d0f
72 ha cambiato i file con 1419 aggiunte e 229 eliminazioni
  1. 1 1
      .run/ruoyi-monitor-admin.run.xml
  2. 1 1
      .run/ruoyi-server.run.xml
  3. 1 1
      .run/ruoyi-snailjob-server.run.xml
  4. 4 4
      README.md
  5. 6 7
      pom.xml
  6. 1 1
      ruoyi-admin/src/main/resources/application.yml
  7. BIN
      ruoyi-admin/src/main/resources/ip2region_v4.xdb
  8. 1 1
      ruoyi-common/ruoyi-common-bom/pom.xml
  9. 1 1
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java
  10. 2 2
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/FlowCopyDTO.java
  11. 2 1
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/StartProcessDTO.java
  12. 1 1
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/UserOnlineDTO.java
  13. 7 7
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java
  14. 4 4
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java
  15. 13 2
      ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java
  16. 175 0
      ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/core/model/SaTokenSecurityMetadata.java
  17. 163 0
      ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/core/resolver/AbstractMetadataJavadocResolver.java
  18. 51 0
      ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/core/resolver/JavadocResolver.java
  19. 164 0
      ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/core/resolver/SaTokenAnnotationMetadataJavadocResolver.java
  20. 25 1
      ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/handler/OpenApiHandler.java
  21. 1 1
      ruoyi-common/ruoyi-common-encrypt/pom.xml
  22. 8 11
      ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java
  23. 18 25
      ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeHandler.java
  24. 1 1
      ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java
  25. 1 1
      ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java
  26. 2 2
      ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java
  27. 19 7
      ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/handler/SensitiveHandler.java
  28. 1 1
      ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/constant/TransConstant.java
  29. 13 3
      ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/handler/TranslationHandler.java
  30. 1 1
      ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java
  31. 11 0
      ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java
  32. 198 0
      ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SaTokenTestController.java
  33. 1 1
      ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestDemoController.java
  34. 2 2
      ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/ExportDemoVo.java
  35. 4 1
      ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoImportVo.java
  36. 3 0
      ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java
  37. 1 0
      ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapJobAnnotation.java
  38. 1 0
      ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapReduceAnnotation1.java
  39. 4 4
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java
  40. 1 1
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java
  41. 2 2
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java
  42. 2 2
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java
  43. 1 1
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java
  44. 15 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java
  45. 1 1
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java
  46. 13 8
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java
  47. 0 20
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java
  48. 53 0
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskOperationEnum.java
  49. 14 0
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwDefinitionController.java
  50. 14 0
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwInstanceController.java
  51. 2 1
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java
  52. 2 2
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowCopyBo.java
  53. 2 1
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowNextNodeBo.java
  54. 2 1
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java
  55. 5 0
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskOperationBo.java
  56. 2 2
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCopyVo.java
  57. 4 2
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java
  58. 12 1
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java
  59. 47 31
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java
  60. 1 1
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java
  61. 11 2
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwSpelServiceImpl.java
  62. 1 1
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java
  63. 87 32
      ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java
  64. 6 6
      script/docker/docker-compose.yml
  65. 24 4
      script/sql/oracle/oracle_ry_workflow.sql
  66. 24 4
      script/sql/postgres/postgres_ry_workflow.sql
  67. 24 4
      script/sql/ry_workflow.sql
  68. 19 3
      script/sql/sqlserver/sqlserver_ry_workflow.sql
  69. 24 0
      script/sql/update/oracle/update_5.5.3_5.6.0.sql
  70. 24 0
      script/sql/update/postgres/update_5.5.3_5.6.0.sql
  71. 43 0
      script/sql/update/sqlserver/update_5.5.3_5.6.0.sql
  72. 24 0
      script/sql/update/update_5.5.3_5.6.0.sql

+ 1 - 1
.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.5.3" />
+        <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.6.0" />
         <option name="buildOnly" value="true" />
         <option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
       </settings>

+ 1 - 1
.run/ruoyi-server.run.xml

@@ -2,7 +2,7 @@
   <configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
     <deployment type="dockerfile">
       <settings>
-        <option name="imageTag" value="ruoyi/ruoyi-server:5.5.3" />
+        <option name="imageTag" value="ruoyi/ruoyi-server:5.6.0" />
         <option name="buildOnly" value="true" />
         <option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
       </settings>

+ 1 - 1
.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.5.3" />
+        <option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.6.0" />
         <option name="buildOnly" value="true" />
         <option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" />
       </settings>

+ 4 - 4
README.md

@@ -5,11 +5,11 @@
 ## 平台简介
 
 [![码云Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/dromara/RuoYi-Vue-Plus)
-[![GitHub](https://img.shields.io/github/stars/dromara/RuoYi-Vue-Plus.svg?style=social&label=Stars)](https://github.com/dromara/RuoYi-Vue-Plus)
+[![GitHub](https://img.shields.io/github/stars/dromara/RuoYi-Vue-Plus.svg?label=Github%20Stars)](https://github.com/dromara/RuoYi-Vue-Plus)
 [![Star](https://gitcode.com/dromara/RuoYi-Vue-Plus/star/badge.svg)](https://gitcode.com/dromara/RuoYi-Vue-Plus)
 [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE)
 <br>
-[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.5.3-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
+[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.6.0-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
 [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.5-blue.svg)]()
 [![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]()
 [![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]()
@@ -22,7 +22,7 @@
 > 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system)
 
 > 官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/JavaLionLi/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)<br>
-> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5)<br>
+> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://github.com/imdap/ruoyi-plus-vben5)<br>
 > 成员前端项目地址: 基于soybean [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean)<br>
 > 成员项目地址: 删除多租户与工作流 [RuoYi-Vue-Plus-Single](https://gitee.com/ColorDreams/RuoYi-Vue-Plus-Single)<br>
 
@@ -53,7 +53,7 @@ Topiam IAM/IDaaS身份管理平台 - https://www.topiam.cn/ <br>
 | 权限注解        | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic校验、忽略校验<br/>角色与权限校验支持多种条件 如 `AND` `OR` 或 `权限 OR 角色` 等复杂表达式        | 只支持是否存在匹配                                                                          |
 | 三方鉴权        | 采用 JustAuth 第三方登录组件 支持微信、钉钉等数十种三方认证                                                                               | 无                                                                                  |
 | 关系数据库支持     | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer<br/>可同时使用异构切换(支持其他 mybatis-plus 支持的所有数据库 只需要增加jdbc依赖即可使用 达梦金仓等均有成功案例)      | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换                                                    |
-| 缓存数据库       | 支持 Redis 5-7 支持大部分新功能特性 如 分布式限流、分布式队列                                                                             | Redis 简单 get set 支持                                                                |
+| 缓存数据库       | 支持 Redis >= 6 支持大部分新功能特性 如 分布式限流、分布式队列                                                                            | Redis 简单 get set 支持                                                                |
 | Redis客户端    | 采用 Redisson Redis官方推荐 基于Netty的客户端工具<br/>支持Redis 90%以上的命令 底层优化规避很多不正确的用法 例如: keys被转换为scan<br/>支持单机、哨兵、单主集群、多主集群等模式 | Lettuce + RedisTemplate 支持模式少 工具使用繁琐<br/>连接池采用 common-pool Bug多经常性出问题              |
 | 缓存注解        | 采用 Spring-Cache 注解 对其扩展了实现支持了更多功能<br/>例如 过期时间 最大空闲时间 组最大长度等 只需一个注解即可完成数据自动缓存                                      | 需手动编写Redis代码逻辑                                                                     |
 | ORM框架       | 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多<br/>例如多租户插件 分页插件 乐观锁插件等等                                             | 采用 Mybatis 基于XML需要手写SQL                                                            |

+ 6 - 7
pom.xml

@@ -13,8 +13,8 @@
     <description>Dromara RuoYi-Vue-Plus多租户管理系统</description>
 
     <properties>
-        <revision>5.5.3</revision>
-        <spring-boot.version>3.5.9</spring-boot.version>
+        <revision>5.6.0</revision>
+        <spring-boot.version>3.5.12</spring-boot.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
         <java.version>17</java.version>
@@ -35,10 +35,10 @@
         <mapstruct-plus.version>1.5.0</mapstruct-plus.version>
         <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
         <lombok.version>1.18.42</lombok.version>
-        <bouncycastle.version>1.80</bouncycastle.version>
+        <bouncycastle.version>1.83</bouncycastle.version>
         <justauth.version>1.16.7</justauth.version>
         <!-- 离线IP地址定位库 -->
-        <ip2region.version>3.3.2</ip2region.version>
+        <ip2region.version>3.3.4</ip2region.version>
         <!-- OSS 配置 -->
         <aws.sdk.version>2.28.22</aws.sdk.version>
         <!-- SMS 配置 -->
@@ -284,7 +284,7 @@
             <!-- 加密包引入 -->
             <dependency>
                 <groupId>org.bouncycastle</groupId>
-                <artifactId>bcprov-jdk15to18</artifactId>
+                <artifactId>bcpkix-jdk18on</artifactId>
                 <version>${bouncycastle.version}</version>
             </dependency>
 
@@ -375,8 +375,7 @@
                 <artifactId>maven-compiler-plugin</artifactId>
                 <version>${maven-compiler-plugin.version}</version>
                 <configuration>
-                    <source>${java.version}</source>
-                    <target>${java.version}</target>
+                    <release>${java.version}</release>
                     <encoding>${project.build.sourceEncoding}</encoding>
                     <annotationProcessorPaths>
                         <path>

+ 1 - 1
ruoyi-admin/src/main/resources/application.yml

@@ -8,7 +8,7 @@ server:
   # undertow 配置
   undertow:
     # HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的
-    max-http-post-size: -1
+    max-http-post-size: 1GB
     # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
     # 每块buffer的空间大小,越小的空间被利用越充分
     buffer-size: 512

BIN
ruoyi-admin/src/main/resources/ip2region_v4.xdb


+ 1 - 1
ruoyi-common/ruoyi-common-bom/pom.xml

@@ -14,7 +14,7 @@
     </description>
 
     <properties>
-        <revision>5.5.3</revision>
+        <revision>5.6.0</revision>
     </properties>
 
     <dependencyManagement>

+ 1 - 1
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java

@@ -52,7 +52,7 @@ public interface CacheNames {
     String SYS_USER_NAME = "sys_user_name#30d";
 
     /**
-     * 用户
+     * 用户
      */
     String SYS_NICKNAME = "sys_nickname#30d";
 

+ 2 - 2
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/FlowCopyDTO.java

@@ -23,8 +23,8 @@ public class FlowCopyDTO implements Serializable {
     private Long userId;
 
     /**
-     * 用户
+     * 用户
      */
-    private String userName;
+    private String nickName;
 
 }

+ 2 - 1
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/StartProcessDTO.java

@@ -48,7 +48,8 @@ public class StartProcessDTO implements Serializable {
 
     public Map<String, Object> getVariables() {
         if (variables == null) {
-            return new HashMap<>(16);
+            variables = new HashMap<>(16);
+            return variables;
         }
         variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
         return variables;

+ 1 - 1
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/UserOnlineDTO.java

@@ -30,7 +30,7 @@ public class UserOnlineDTO implements Serializable {
     private String deptName;
 
     /**
-     * 用户名称
+     * 用户账号
      */
     private String userName;
 

+ 7 - 7
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java

@@ -21,18 +21,18 @@ public interface UserService {
     String selectUserNameById(Long userId);
 
     /**
-     * 通过用户ID查询用户账户
+     * 通过用户ID查询用户昵称
      *
      * @param userId 用户ID
-     * @return 用户
+     * @return 用户
      */
     String selectNicknameById(Long userId);
 
     /**
-     * 通过用户ID查询用户账户
+     * 通过用户ID查询用户昵称
      *
      * @param userIds 用户ID 多个用逗号隔开
-     * @return 用户
+     * @return 用户
      */
     String selectNicknameByIds(String userIds);
 
@@ -93,11 +93,11 @@ public interface UserService {
     List<UserDTO> selectUsersByPostIds(List<Long> postIds);
 
     /**
-     * 根据用户 ID 列表查询用户称映射关系
+     * 根据用户 ID 列表查询用户称映射关系
      *
      * @param userIds 用户 ID 列表
-     * @return Map,其中 key 为用户 ID,value 为对应的用户
+     * @return Map,其中 key 为用户 ID,value 为对应的用户
      */
-    Map<Long, String> selectUserNamesByIds(List<Long> userIds);
+    Map<Long, String> selectUserNicksByIds(List<Long> userIds);
 
 }

+ 4 - 4
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java

@@ -90,9 +90,9 @@ public class RegionUtils {
         try {
             String region = ip2Region.search(ipString);
             if (StringUtils.isBlank(region)) {
-                region = UNKNOWN_ADDRESS;
+                return UNKNOWN_ADDRESS;
             }
-            return region;
+            return StringUtils.replace(region, "0", UNKNOWN_ADDRESS);
         } catch (Exception e) {
             log.error("IP地址离线获取城市异常 {}", ipString);
             return UNKNOWN_ADDRESS;
@@ -109,9 +109,9 @@ public class RegionUtils {
         try {
             String region = ip2Region.search(ipBytes);
             if (StringUtils.isBlank(region)) {
-                region = UNKNOWN_ADDRESS;
+                return UNKNOWN_ADDRESS;
             }
-            return region;
+            return StringUtils.replace(region, "0", UNKNOWN_ADDRESS);
         } catch (Exception e) {
             log.error("IP地址离线获取城市异常 {}", Util.ipToString(ipBytes));
             return UNKNOWN_ADDRESS;

+ 13 - 2
ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java

@@ -7,6 +7,8 @@ import io.swagger.v3.oas.models.security.SecurityRequirement;
 import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.doc.config.properties.SpringDocProperties;
+import org.dromara.common.doc.core.resolver.JavadocResolver;
+import org.dromara.common.doc.core.resolver.SaTokenAnnotationMetadataJavadocResolver;
 import org.dromara.common.doc.handler.OpenApiHandler;
 import org.springdoc.core.configuration.SpringDocConfiguration;
 import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
@@ -84,8 +86,9 @@ public class SpringDocConfig {
                                          SecurityService securityParser,
                                          SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
                                          Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers,
-                                         Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers, Optional<JavadocProvider> javadocProvider) {
-        return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider);
+                                         Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers, Optional<JavadocProvider> javadocProvider,
+                                         List<JavadocResolver> javadocResolvers) {
+        return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider, javadocResolvers);
     }
 
     /**
@@ -112,6 +115,14 @@ public class SpringDocConfig {
         };
     }
 
+    /**
+     * 注册SaToken JavaDoc权限注解解析器
+     */
+    @Bean
+    public JavadocResolver saTokenAnnotationJavadocResolver() {
+        return new SaTokenAnnotationMetadataJavadocResolver();
+    }
+
     /**
      * 单独使用一个类便于判断 解决springdoc路径拼接重复问题
      *

+ 175 - 0
ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/core/model/SaTokenSecurityMetadata.java

@@ -0,0 +1,175 @@
+package org.dromara.common.doc.core.model;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 存储权限框架注解解析后的权限和角色信息
+ *
+ * @author AprilWind
+ */
+@Data
+@JsonInclude(Include.NON_EMPTY)
+public class SaTokenSecurityMetadata {
+
+    /**
+     * 权限校验信息列表(对应 @SaCheckPermission 注解)
+     */
+    private List<AuthInfo> permissions = new ArrayList<>();
+
+    /**
+     * 角色校验信息列表(对应 @SaCheckRole 注解)
+     */
+    private List<AuthInfo> roles = new ArrayList<>();
+
+    /**
+     * 是否忽略校验(对应 @SaIgnore 注解)
+     */
+    private boolean ignore = false;
+
+    /**
+     * 添加权限信息
+     *
+     * @param values  权限值数组
+     * @param mode    校验模式(AND/OR)
+     * @param type    权限类型
+     * @param orRoles 或角色数组
+     */
+    public void addPermission(String[] values, String mode, String type, String[] orRoles) {
+        if (values != null && values.length > 0) {
+            AuthInfo authInfo = new AuthInfo();
+            authInfo.setValues(values);
+            authInfo.setMode(mode);
+            authInfo.setType(type);
+            if (orRoles != null && orRoles.length > 0) {
+                authInfo.setOrValues(orRoles);
+                authInfo.setOrType("role");
+            }
+            this.permissions.add(authInfo);
+        }
+    }
+
+    /**
+     * 添加角色信息
+     *
+     * @param values 角色值数组
+     * @param mode   校验模式(AND/OR)
+     * @param type   角色类型
+     */
+    public void addRole(String[] values, String mode, String type) {
+        if (values != null && values.length > 0) {
+            AuthInfo authInfo = new AuthInfo();
+            authInfo.setValues(values);
+            authInfo.setMode(mode);
+            authInfo.setType(type);
+            this.roles.add(authInfo);
+        }
+    }
+
+    /**
+     * 生成 Markdown 结构的权限说明
+     *
+     * @return Markdown 文本
+     */
+    public String toMarkdownString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("<br><h3>访问权限</h3><br>");
+
+        if (ignore) {
+            sb.append("> **权限策略**:忽略权限检查<br>");
+            return sb.toString();
+        }
+
+        if (!ignore && permissions.isEmpty() && roles.isEmpty()){
+            sb.append("> **权限策略**:需要登录<br><br>");
+            return sb.toString();
+        }
+
+        if (!permissions.isEmpty()) {
+            sb.append("**权限校验:**<br><br>");
+
+            permissions.forEach(p -> {
+                String permTags = Arrays.stream(p.getValues())
+                    .map(v -> "`" + v + "`")
+                    .collect(Collectors.joining(p.getModeSymbol()));
+
+                sb.append("- ").append(permTags).append("<br>");
+
+                if (p.getOrValues() != null && p.getOrValues().length > 0) {
+                    String orTags = Arrays.stream(p.getOrValues())
+                        .map(v -> "`" + v + "`")
+                        .collect(Collectors.joining(p.getModeSymbol()));
+                    sb.append("  - 或角色:").append(orTags).append("<br>");
+                }
+            });
+
+            sb.append("<br>");
+        }
+
+        if (!roles.isEmpty()) {
+            sb.append("**角色校验:**<br><br>");
+
+            roles.forEach(r -> {
+
+                String roleTags = Arrays.stream(r.getValues())
+                    .map(v -> "`" + v + "`")
+                    .collect(Collectors.joining(r.getModeSymbol()));
+
+                sb.append("- ").append(roleTags).append("<br>");
+            });
+        }
+
+        return sb.toString().trim();
+    }
+
+    /**
+     * 认证信息
+     */
+    @Data
+    @JsonInclude(Include.NON_EMPTY)
+    public static class AuthInfo {
+
+        /**
+         * 权限或角色值数组
+         */
+        private String[] values;
+
+        /**
+         * 校验模式(AND/OR)
+         */
+        private String mode;
+
+        /**
+         * 类型说明
+         */
+        private String type;
+
+        /**
+         * 或权限/角色值数组(用于权限校验时的或角色校验)
+         */
+        private String[] orValues;
+
+        /**
+         * 或值的类型(role/permission)
+         */
+        private String orType;
+
+        /**
+         * 重写mode的获取方法,返回符号而非文字
+         * @return AND→&,OR→|,默认→&
+         */
+        public String getModeSymbol() {
+            if (mode == null) {
+                return " & "; // 默认AND,返回&
+            }
+            return "AND".equalsIgnoreCase(mode) ? " & " : " | ";
+        }
+
+    }
+}

+ 163 - 0
ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/core/resolver/AbstractMetadataJavadocResolver.java

@@ -0,0 +1,163 @@
+package org.dromara.common.doc.core.resolver;
+
+import cn.hutool.core.annotation.AnnotationUtil;
+import cn.hutool.core.util.ClassLoaderUtil;
+import io.swagger.v3.oas.models.Operation;
+import org.springframework.web.method.HandlerMethod;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.util.Map;
+import java.util.function.Supplier;
+
+/**
+ * 抽象元数据 Javadoc 解析器
+ *
+ * @param <M> 元数据类型
+ * @author 秋辞未寒
+ */
+public abstract class AbstractMetadataJavadocResolver<M> implements JavadocResolver {
+
+    public static final int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
+    public static final int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
+
+    private final Supplier<M> metadataProvider;
+
+    private final int order;
+
+    public AbstractMetadataJavadocResolver(Supplier<M> metadataProvider) {
+        this(metadataProvider, LOWEST_PRECEDENCE);
+    }
+
+    public AbstractMetadataJavadocResolver(Supplier<M> metadataProvider, int order) {
+        this.metadataProvider = metadataProvider;
+        this.order = order;
+    }
+
+    @Override
+    public int getOrder() {
+        return order;
+    }
+
+    @Override
+    public String resolve(HandlerMethod handlerMethod, Operation operation) {
+        return resolve(handlerMethod, operation, metadataProvider.get());
+    }
+
+    /**
+     * 执行解析并返回解析到的 Javadoc 内容
+     * @param handlerMethod 处理器方法
+     * @param operation Swagger Operation实例
+     * @param metadata 元信息
+     * @return 解析到的 Javadoc 内容
+     */
+    public abstract String resolve(HandlerMethod handlerMethod, Operation operation, M metadata);
+
+    /**
+     * 检查处理器方法所属的类上是否存在注解
+     * @param handlerMethod 处理器方法
+     * @param annotationClass 注解类
+     * @return 是否存在注解
+     */
+    public boolean hasClassAnnotation(HandlerMethod handlerMethod,Class<? extends Annotation> annotationClass){
+        return AnnotationUtil.hasAnnotation(handlerMethod.getBeanType(), annotationClass);
+    }
+
+    /**
+     * 检查处理器方法所属的类上是否存在注解
+     * @param handlerMethod 处理器方法
+     * @param annotationTypeName 注解类名称
+     * @return 是否存在注解
+     */
+    public boolean hasClassAnnotation(HandlerMethod handlerMethod, String annotationTypeName){
+        return AnnotationUtil.hasAnnotation(handlerMethod.getBeanType(), annotationTypeName);
+    }
+
+    /**
+     * 检查处理器方法上是否存在注解
+     * @param handlerMethod 处理器方法
+     * @param annotationClass 注解类
+     * @return 是否存在注解
+     */
+    public boolean hasMethodAnnotation(HandlerMethod handlerMethod,Class<? extends Annotation> annotationClass){
+        return AnnotationUtil.hasAnnotation(handlerMethod.getMethod(), annotationClass);
+    }
+
+    /**
+     * 检查处理器方法上是否存在注解
+     * @param handlerMethod 处理器方法
+     * @param annotationTypeName 注解类名称
+     * @return 是否存在注解
+     */
+    public boolean hasMethodAnnotation(HandlerMethod handlerMethod, String annotationTypeName){
+        return AnnotationUtil.hasAnnotation(handlerMethod.getMethod(), annotationTypeName);
+    }
+
+    /**
+     * 检查处理器方法上是否存在注解
+     * @param handlerMethod 处理器方法
+     * @param annotationClass 注解类
+     * @return 是否存在注解
+     */
+    public boolean hasAnnotation(HandlerMethod handlerMethod,Class<? extends Annotation> annotationClass){
+        return this.hasClassAnnotation(handlerMethod, annotationClass) || this.hasMethodAnnotation(handlerMethod, annotationClass);
+    }
+
+    /**
+     * 检查处理器方法上是否存在注解
+     * @param handlerMethod 处理器方法
+     * @param annotationTypeName 注解类名称
+     * @return 是否存在注解
+     */
+    public boolean hasAnnotation(HandlerMethod handlerMethod, String annotationTypeName){
+        return this.hasClassAnnotation(handlerMethod, annotationTypeName) || this.hasMethodAnnotation(handlerMethod, annotationTypeName);
+    }
+
+    /**
+     * 获取处理器方法所属类上的注解的值
+     * @param handlerMethod 处理器方法
+     * @param annotationClass 注解类
+     * @return 注解的值
+     */
+    public Map<String, Object> getClassAnnotationValueMap(HandlerMethod handlerMethod, Class<? extends Annotation> annotationClass) {
+        return AnnotationUtil.getAnnotationValueMap(handlerMethod.getBeanType(), annotationClass);
+    }
+
+    /**
+     * 获取处理器方法所属类上的注解的值
+     * @param handlerMethod 处理器方法
+     * @param annotationClassName 注解类名称
+     * @return 注解的值
+     */
+    @SuppressWarnings("unchecked")
+    public Map<String, Object> getClassAnnotationValueMap(HandlerMethod handlerMethod, String annotationClassName) {
+        Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(annotationClassName, false);
+        return AnnotationUtil.getAnnotationValueMap(handlerMethod.getBeanType(), annotationClass);
+    }
+
+    /**
+     * 获取处理器方法上的注解的值
+     * @param handlerMethod 处理器方法
+     * @param annotationClass 注解类
+     * @return 注解的值
+     */
+    public Map<String, Object> getMethodAnnotationValueMap(HandlerMethod handlerMethod, Class<? extends Annotation> annotationClass) {
+        return AnnotationUtil.getAnnotationValueMap(handlerMethod.getMethod(), annotationClass);
+    }
+
+    /**
+     * 获取处理器方法所属类上的注解的值
+     * @param handlerMethod 处理器方法
+     * @param annotationClassName 注解类名称
+     * @return 注解的值
+     */
+    @SuppressWarnings("unchecked")
+    public Map<String, Object> getMethodAnnotationValueMap(HandlerMethod handlerMethod, String annotationClassName) {
+        Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(annotationClassName, false);
+        return AnnotationUtil.getAnnotationValueMap(handlerMethod.getMethod(), annotationClass);
+    }
+
+    private Map<String, Object> getAnnotationValueMap(AnnotatedElement annotatedElement, Class<? extends Annotation> annotationClass) {
+        return AnnotationUtil.getAnnotationValueMap(annotatedElement, annotationClass);
+    }
+}

+ 51 - 0
ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/core/resolver/JavadocResolver.java

@@ -0,0 +1,51 @@
+package org.dromara.common.doc.core.resolver;
+
+import io.swagger.v3.oas.models.Operation;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.core.Ordered;
+import org.springframework.web.method.HandlerMethod;
+
+/**
+ * Javadoc解析器接口
+ *
+ * @author echo
+ * @author 秋辞未寒
+ */
+public interface JavadocResolver extends Comparable<JavadocResolver>, Ordered {
+
+    /**
+     * 检查解析器是否支持解析 HandlerMethod
+     * @param handlerMethod 处理器方法
+     * @return 是否支持解析
+     */
+    boolean supports(HandlerMethod handlerMethod);
+
+    /**
+     * 执行解析并返回解析到的 Javadoc 内容
+     * @param handlerMethod 处理器方法
+     * @param operation Swagger Operation实例
+     * @return 解析到的 Javadoc 内容
+     */
+    String resolve(HandlerMethod handlerMethod, Operation operation);
+
+    /**
+     * 获取解析器优先级
+     */
+    default int getOrder() {
+        return Ordered.LOWEST_PRECEDENCE;
+    }
+
+    /**
+     * 获取解析器的名称
+     *
+     * @return 解析器名称
+     */
+    default String getName() {
+        return this.getClass().getSimpleName();
+    }
+
+    @Override
+    default int compareTo(@NotNull JavadocResolver o) {
+        return Integer.compare(getOrder(), o.getOrder());
+    }
+}

+ 164 - 0
ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/core/resolver/SaTokenAnnotationMetadataJavadocResolver.java

@@ -0,0 +1,164 @@
+package org.dromara.common.doc.core.resolver;
+
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.ClassLoaderUtil;
+import io.swagger.v3.oas.models.Operation;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.doc.core.model.SaTokenSecurityMetadata;
+import org.springframework.web.method.HandlerMethod;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
+
+/**
+ * 基于JavaDoc的SaToken权限解析器
+ *
+ * @author echo
+ * @author 秋辞未寒
+ */
+@SuppressWarnings("unchecked")
+@Slf4j
+public class SaTokenAnnotationMetadataJavadocResolver extends AbstractMetadataJavadocResolver<SaTokenSecurityMetadata> {
+
+    /**
+     * 默认元数据提供者,每次解析都会创建一个新的元数据对象
+     */
+    public static final Supplier<SaTokenSecurityMetadata> DEFAULT_METADATA_PROVIDER = SaTokenSecurityMetadata::new;
+
+    private static final String BASE_CLASS_NAME = "cn.dev33.satoken.annotation";
+    private static final String SA_CHECK_ROLE_CLASS_NAME = BASE_CLASS_NAME + ".SaCheckRole";
+    private static final String SA_CHECK_PERMISSION_CLASS_NAME = BASE_CLASS_NAME + ".SaCheckPermission";
+    private static final String SA_IGNORE_CLASS_NAME = BASE_CLASS_NAME + ".SaIgnore";
+    private static final String SA_CHECK_LOGIN_NAME = BASE_CLASS_NAME + ".SaCheckLogin";
+
+    private static final Class<? extends Annotation> SA_CHECK_ROLE_CLASS;
+    private static final Class<? extends Annotation> SA_CHECK_PERMISSION_CLASS;
+    private static final Class<? extends Annotation> SA_IGNORE_CLASS;
+    private static final Class<? extends Annotation> SA_CHECK_LOGIN_CLASS;
+
+
+    static {
+        // 通过类加载器去加载注解类Class实例
+        SA_CHECK_ROLE_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_CHECK_ROLE_CLASS_NAME, false);
+        SA_CHECK_PERMISSION_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_CHECK_PERMISSION_CLASS_NAME, false);
+        SA_IGNORE_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_IGNORE_CLASS_NAME, false);
+        SA_CHECK_LOGIN_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_CHECK_LOGIN_NAME, false);
+        if (log.isDebugEnabled()) {
+            log.debug("SaTokenAnnotationJavadocResolver init success, load annotation class: {}", List.of(SA_CHECK_ROLE_CLASS, SA_CHECK_PERMISSION_CLASS, SA_IGNORE_CLASS, SA_CHECK_LOGIN_CLASS));
+        }
+    }
+
+    public SaTokenAnnotationMetadataJavadocResolver() {
+        this(DEFAULT_METADATA_PROVIDER);
+    }
+
+    public SaTokenAnnotationMetadataJavadocResolver(Supplier<SaTokenSecurityMetadata> metadataProvider) {
+        super(metadataProvider);
+    }
+
+    public SaTokenAnnotationMetadataJavadocResolver(int order) {
+        this(DEFAULT_METADATA_PROVIDER,order);
+    }
+
+    public SaTokenAnnotationMetadataJavadocResolver(Supplier<SaTokenSecurityMetadata> metadataProvider, int order) {
+        super(metadataProvider,order);
+    }
+
+    @Override
+    public boolean supports(HandlerMethod handlerMethod) {
+        return hasAnnotation(handlerMethod, SA_CHECK_ROLE_CLASS) || hasAnnotation(handlerMethod, SA_CHECK_PERMISSION_CLASS) || hasAnnotation(handlerMethod, SA_IGNORE_CLASS);
+    }
+
+    @Override
+    public String resolve(HandlerMethod handlerMethod, Operation operation, SaTokenSecurityMetadata metadata) {
+        // 检查是否忽略校验
+        if(hasAnnotation(handlerMethod, SA_IGNORE_CLASS_NAME)){
+            metadata.setIgnore(true);
+            return metadata.toMarkdownString();
+        }
+
+        // 解析权限校验
+        resolvePermissionCheck(handlerMethod, metadata);
+
+        // 解析角色校验
+        resolveRoleCheck(handlerMethod, metadata);
+        return metadata.toMarkdownString();
+    }
+
+    /**
+     * 解析权限校验
+     */
+    private void resolvePermissionCheck(HandlerMethod handlerMethod, SaTokenSecurityMetadata metadata) {
+        // 解析获取方法上的注解角色信息
+        if (hasMethodAnnotation(handlerMethod, SA_CHECK_PERMISSION_CLASS_NAME)) {
+            Map<String, Object> annotationValueMap = getMethodAnnotationValueMap(handlerMethod, SA_CHECK_PERMISSION_CLASS);
+            resolvePermissionAnnotation(metadata, annotationValueMap);
+        }
+        // 解析获取类上的注解角色信息
+        if (hasClassAnnotation(handlerMethod, SA_CHECK_PERMISSION_CLASS_NAME)) {
+            Map<String, Object> annotationValueMap = getClassAnnotationValueMap(handlerMethod, SA_CHECK_PERMISSION_CLASS);
+            resolvePermissionAnnotation(metadata, annotationValueMap);
+        }
+    }
+
+    /**
+     * 解析权限注解
+     */
+    private void resolvePermissionAnnotation(SaTokenSecurityMetadata metadata, Map<String, Object> annotationValueMap) {
+        try {
+            // 反射获取注解属性
+            Object value = annotationValueMap.get( "value");
+            Object mode = annotationValueMap.get( "mode");
+            Object type = annotationValueMap.get( "type");
+            Object orRole = annotationValueMap.get( "orRole");
+
+            String[] values = Convert.toStrArray(value);
+            String modeStr = mode != null ? mode.toString() : "AND";
+            String typeStr = type != null ? type.toString() : "";
+            String[] orRoles = Convert.toStrArray(orRole);
+
+            metadata.addPermission(values, modeStr, typeStr, orRoles);
+        } catch (Exception ignore) {
+            // 忽略解析错误
+        }
+    }
+
+    /**
+     * 解析角色校验
+     */
+    private void resolveRoleCheck(HandlerMethod handlerMethod, SaTokenSecurityMetadata metadata) {
+        // 解析获取方法上的注解角色信息
+        if (hasMethodAnnotation(handlerMethod, SA_CHECK_ROLE_CLASS_NAME)) {
+            Map<String, Object> annotationValueMap = getMethodAnnotationValueMap(handlerMethod, SA_CHECK_ROLE_CLASS);
+            resolveRoleAnnotation(metadata, annotationValueMap);
+        }
+        // 解析获取类上的注解角色信息
+        if (hasClassAnnotation(handlerMethod, SA_CHECK_ROLE_CLASS_NAME)) {
+            Map<String, Object> annotationValueMap = getClassAnnotationValueMap(handlerMethod, SA_CHECK_ROLE_CLASS);
+            resolveRoleAnnotation(metadata, annotationValueMap);
+        }
+    }
+
+    /**
+     * 解析角色注解
+     */
+    private void resolveRoleAnnotation(SaTokenSecurityMetadata metadata, Map<String, Object> annotationValueMap) {
+        try {
+            // 反射获取注解属性
+            Object value = annotationValueMap.get("value");
+            Object mode = annotationValueMap.get("mode");
+            Object type = annotationValueMap.get("type");
+
+            String[] values = Convert.toStrArray(value);
+            String modeStr = mode != null ? mode.toString() : "AND";
+            String typeStr = type != null ? type.toString() : "";
+
+            metadata.addRole(values, modeStr, typeStr);
+        } catch (Exception ignore) {
+            // 忽略解析错误
+        }
+    }
+
+}

+ 25 - 1
ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/handler/OpenApiHandler.java

@@ -12,6 +12,7 @@ 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.dromara.common.doc.core.resolver.JavadocResolver;
 import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
 import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
 import org.springdoc.core.properties.SpringDocConfigProperties;
@@ -83,6 +84,11 @@ public class OpenApiHandler extends OpenAPIService {
      */
     private final PropertyResolverUtils propertyResolverUtils;
 
+    /**
+     * Javadoc解析器接口
+     */
+    private final List<JavadocResolver> javadocResolvers;
+
     /**
      * The javadoc provider.
      */
@@ -123,7 +129,8 @@ public class OpenApiHandler extends OpenAPIService {
                           SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
                           Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
                           Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
-                          Optional<JavadocProvider> javadocProvider) {
+                          Optional<JavadocProvider> javadocProvider,
+                          List<JavadocResolver> javadocResolvers) {
         super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
         if (openAPI.isPresent()) {
             this.openAPI = openAPI.get();
@@ -140,6 +147,7 @@ public class OpenApiHandler extends OpenAPIService {
         this.openApiBuilderCustomisers = openApiBuilderCustomizers;
         this.serverBaseUrlCustomizers = serverBaseUrlCustomizers;
         this.javadocProvider = javadocProvider;
+        this.javadocResolvers = javadocResolvers == null ? new ArrayList<>() : javadocResolvers;
         if (springDocConfigProperties.isUseFqn())
             TypeNameResolver.std.setUseFqn(true);
     }
@@ -220,6 +228,22 @@ public class OpenApiHandler extends OpenAPIService {
                 securityParser.buildSecurityRequirement(securityRequirements, operation);
         }
 
+        if (javadocProvider.isPresent()) {
+            String description = javadocProvider.get().getMethodJavadocDescription(handlerMethod.getMethod());
+            String summary = javadocProvider.get().getFirstSentence(description);
+            if (StringUtils.isNotBlank(description)){
+                operation.setSummary(summary);
+            }
+            // 调用解析器提取JavaDoc中的权限信息
+            if (javadocResolvers != null && !javadocResolvers.isEmpty()) {
+                for (JavadocResolver resolver : javadocResolvers) {
+                    String desc = resolver.resolve(handlerMethod, operation);
+                    description = description + desc;
+                }
+                operation.setDescription(description);
+            }
+        }
+
         return operation;
     }
 

+ 1 - 1
ruoyi-common/ruoyi-common-encrypt/pom.xml

@@ -24,7 +24,7 @@
 
         <dependency>
             <groupId>org.bouncycastle</groupId>
-            <artifactId>bcprov-jdk15to18</artifactId>
+            <artifactId>bcpkix-jdk18on</artifactId>
         </dependency>
 
         <dependency>

+ 8 - 11
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java

@@ -6,10 +6,7 @@ import cn.hutool.core.util.ObjectUtil;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.ibatis.executor.parameter.ParameterHandler;
-import org.apache.ibatis.plugin.Interceptor;
-import org.apache.ibatis.plugin.Intercepts;
-import org.apache.ibatis.plugin.Invocation;
-import org.apache.ibatis.plugin.Signature;
+import org.apache.ibatis.plugin.*;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.encrypt.annotation.EncryptField;
 import org.dromara.common.encrypt.core.EncryptContext;
@@ -42,19 +39,19 @@ public class MybatisEncryptInterceptor implements Interceptor {
 
     @Override
     public Object intercept(Invocation invocation) throws Throwable {
-        return invocation;
-    }
-
-    @Override
-    public Object plugin(Object target) {
+        Object target = invocation.getTarget();
         if (target instanceof ParameterHandler parameterHandler) {
-            // 进行加密操作
             Object parameterObject = parameterHandler.getParameterObject();
             if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) {
                 this.encryptHandler(parameterObject);
             }
         }
-        return target;
+        return invocation.proceed();
+    }
+
+    @Override
+    public Object plugin(Object target) {
+        return Plugin.wrap(target, this);
     }
 
     /**

+ 18 - 25
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeHandler.java

@@ -64,8 +64,10 @@ public class CellMergeHandler {
                 // 当前行数据字段值
                 Object currentRowObjFieldVal = ReflectUtils.invokeGetter(currentRowObj, field.getName());
 
-                // 空值跳过不处理
-                if (currentRowObjFieldVal == null || "".equals(currentRowObjFieldVal)) {
+                // 空值视为合并中断,需要先收口上一段合并区间
+                if (isBlankCell(currentRowObjFieldVal)) {
+                    appendMergeResult(result, rowRepeatCellMap.get(field), i - 1, colNum);
+                    rowRepeatCellMap.remove(field);
                     continue;
                 }
 
@@ -78,7 +80,6 @@ public class CellMergeHandler {
                 // 获取 单元格合并Map 中字段值
                 RepeatCell repeatCell = rowRepeatCellMap.get(field);
                 Object cellValue = repeatCell.value();
-                int current = repeatCell.current();
 
                 // 检查是否满足合并条件
                 // currentRowObj 当前行数据
@@ -86,33 +87,14 @@ public class CellMergeHandler {
                 // cellMerge 当前行字段合并注解
                 boolean merge = isMerge(currentRowObj, rows.get(i - 1), cellMerge);
 
-                // 是否添加到结果集
-                boolean isAddResult = false;
-                // 最新行
-                int lastRow = i + rowIndex - 1;
-
                 // 如果当前行字段值和缓存中的字段值不相等,或不满足合并条件,则替换
                 if (!currentRowObjFieldVal.equals(cellValue) || !merge) {
+                    appendMergeResult(result, repeatCell, i - 1, colNum);
                     rowRepeatCellMap.put(field, RepeatCell.of(currentRowObjFieldVal, i));
-                    isAddResult = true;
-                }
-
-                // 如果最后一行不能合并,检查之前的数据是否需要合并;如果最后一行可以合并,则直接合并到最后
-                if (i == rows.size() - 1) {
-                    isAddResult = true;
-                    if (i > current) {
-                        lastRow = i + rowIndex;
-                    }
-                }
-
-                if (isAddResult && i > current) {
-                    //如果是同一行,则跳过合并
-                    if (current + rowIndex == lastRow) {
-                        continue;
-                    }
-                    result.add(new CellRangeAddress(current + rowIndex, lastRow, colNum, colNum));
                 }
             }
+            appendMergeResult(result, rowRepeatCellMap.get(field), rows.size() - 1, colNum);
+            rowRepeatCellMap.remove(field);
         }
         return result;
     }
@@ -167,6 +149,17 @@ public class CellMergeHandler {
         return true;
     }
 
+    private boolean isBlankCell(Object value) {
+        return value == null || StrUtil.isBlankIfStr(value);
+    }
+
+    private void appendMergeResult(List<CellRangeAddress> result, RepeatCell repeatCell, int endIndex, int colNum) {
+        if (repeatCell == null || endIndex <= repeatCell.current()) {
+            return;
+        }
+        result.add(new CellRangeAddress(repeatCell.current() + rowIndex, endIndex + rowIndex, colNum, colNum));
+    }
+
     /**
      * 单元格合并
      */

+ 1 - 1
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java

@@ -23,7 +23,7 @@ import java.util.function.Supplier;
  * @version 3.5.0
  */
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
-@SuppressWarnings("unchecked cast")
+@SuppressWarnings("unchecked")
 public class DataPermissionHelper {
 
     private static final String DATA_PERMISSION_KEY = "data:permission";

+ 1 - 1
ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java

@@ -113,7 +113,7 @@ public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject {
      * @param key 键名称
      * @return object
      */
-    @SuppressWarnings("unchecked cast")
+    @SuppressWarnings("unchecked")
     @Override
     public <T> T getObject(String key, Class<T> classType) {
         Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key));

+ 2 - 2
ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java

@@ -63,7 +63,7 @@ public class LoginHelper {
     /**
      * 获取用户(多级缓存)
      */
-    @SuppressWarnings("unchecked cast")
+    @SuppressWarnings("unchecked")
     public static <T extends LoginUser> T getLoginUser() {
         SaSession session = StpUtil.getTokenSession();
         if (ObjectUtil.isNull(session)) {
@@ -75,7 +75,7 @@ public class LoginHelper {
     /**
      * 获取用户基于token
      */
-    @SuppressWarnings("unchecked cast")
+    @SuppressWarnings("unchecked")
     public static <T extends LoginUser> T getLoginUser(String token) {
         SaSession session = StpUtil.getTokenSessionByToken(token);
         if (ObjectUtil.isNull(session)) {

+ 19 - 7
ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/handler/SensitiveHandler.java

@@ -25,9 +25,24 @@ import java.util.Objects;
 @Slf4j
 public class SensitiveHandler extends JsonSerializer<String> implements ContextualSerializer {
 
-    private SensitiveStrategy strategy;
-    private String[] roleKey;
-    private String[] perms;
+    private final SensitiveStrategy strategy;
+    private final String[] roleKey;
+    private final String[] perms;
+
+    /**
+     * 提供给 jackson 创建上下文序列化器时使用 不然会报错
+     */
+    public SensitiveHandler() {
+        this.strategy = null;
+        this.roleKey = null;
+        this.perms = null;
+    }
+
+    public SensitiveHandler(SensitiveStrategy strategy, String[] strings, String[] perms) {
+        this.strategy = strategy;
+        this.roleKey = strings;
+        this.perms = perms;
+    }
 
     @Override
     public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
@@ -48,10 +63,7 @@ public class SensitiveHandler extends JsonSerializer<String> implements Contextu
     public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
         Sensitive annotation = property.getAnnotation(Sensitive.class);
         if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) {
-            this.strategy = annotation.strategy();
-            this.roleKey = annotation.roleKey();
-            this.perms = annotation.perms();
-            return this;
+            return new SensitiveHandler(annotation.strategy(), annotation.roleKey(), annotation.perms());
         }
         return prov.findValueSerializer(property.getType(), property);
     }

+ 1 - 1
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/constant/TransConstant.java

@@ -13,7 +13,7 @@ public interface TransConstant {
     String USER_ID_TO_NAME = "user_id_to_name";
 
     /**
-     * 用户id转用户
+     * 用户id转用户
      */
     String USER_ID_TO_NICKNAME = "user_id_to_nickname";
 

+ 13 - 3
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/handler/TranslationHandler.java

@@ -31,7 +31,18 @@ public class TranslationHandler extends JsonSerializer<Object> implements Contex
      */
     public static final Map<String, TranslationInterface<?>> TRANSLATION_MAPPER = new ConcurrentHashMap<>();
 
-    private Translation translation;
+    private final Translation translation;
+
+    /**
+     * 提供给 jackson 创建上下文序列化器时使用 不然会报错
+     */
+    public TranslationHandler() {
+        this.translation = null;
+    }
+
+    public TranslationHandler(Translation translation) {
+        this.translation = translation;
+    }
 
     @Override
     public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
@@ -63,8 +74,7 @@ public class TranslationHandler extends JsonSerializer<Object> implements Contex
     public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
         Translation translation = property.getAnnotation(Translation.class);
         if (Objects.nonNull(translation)) {
-            this.translation = translation;
-            return this;
+            return new TranslationHandler(translation);
         }
         return prov.findValueSerializer(property.getType(), property);
     }

+ 1 - 1
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java

@@ -7,7 +7,7 @@ import org.dromara.common.translation.constant.TransConstant;
 import org.dromara.common.translation.core.TranslationInterface;
 
 /**
- * 用户称翻译实现
+ * 用户称翻译实现
  *
  * @author may
  */

+ 11 - 0
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java

@@ -1,11 +1,14 @@
 package org.dromara.common.web.config;
 
+import io.undertow.UndertowOptions;
 import io.undertow.server.DefaultByteBufferPool;
 import io.undertow.server.handlers.DisallowedMethodsHandler;
 import io.undertow.util.HttpString;
 import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
 import org.dromara.common.core.utils.SpringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
 import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
 import org.springframework.boot.web.server.WebServerFactoryCustomizer;
 import org.springframework.core.task.VirtualThreadTaskExecutor;
@@ -18,6 +21,9 @@ import org.springframework.core.task.VirtualThreadTaskExecutor;
 @AutoConfiguration
 public class UndertowConfig implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
 
+    @Autowired
+    private ServerProperties serverProperties;
+
     /**
      * 自定义 Undertow 配置
      * <p>
@@ -31,6 +37,11 @@ public class UndertowConfig implements WebServerFactoryCustomizer<UndertowServle
      */
     @Override
     public void customize(UndertowServletWebServerFactory factory) {
+        long bytes = serverProperties.getUndertow().getMaxHttpPostSize().toBytes();
+        factory.addBuilderCustomizers(builder -> {
+            builder.setServerOption(UndertowOptions.MULTIPART_MAX_ENTITY_SIZE, bytes);
+        });
+
         factory.addDeploymentInfoCustomizers(deploymentInfo -> {
             // 配置 WebSocket 部署信息,设置 WebSocket 使用的缓冲区池
             WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();

+ 198 - 0
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SaTokenTestController.java

@@ -0,0 +1,198 @@
+package org.dromara.demo.controller;
+
+import cn.dev33.satoken.annotation.*;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.domain.R;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * SaToken 权限测试 接口文档输出测试
+ *
+ * @author AprilWind
+ */
+@Slf4j
+@RestController
+@RequestMapping("/demo/saTokenDoc")
+public class SaTokenTestController {
+
+    // ====================== 基础场景:单一校验规则 ======================
+
+    /**
+     * 场景1:仅登录校验(无角色/权限限制,只需登录态)
+     */
+    @SaCheckLogin
+    @GetMapping("/basic/loginOnly")
+    public R<Void> loginOnly() {
+        log.info("【场景1】仅登录校验通过");
+        return R.ok("仅登录校验通过,无需角色/权限");
+    }
+
+    /**
+     * 场景2:单一角色校验(AND模式,默认)
+     */
+    @SaCheckRole("admin")
+    @GetMapping("/basic/singleRole")
+    public R<Void> singleRole() {
+        log.info("【场景2】单一角色(admin)校验通过");
+        return R.ok("拥有admin角色,校验通过");
+    }
+
+    /**
+     * 场景3:单一权限校验(AND模式,默认)
+     */
+    @SaCheckPermission("system:user:view")
+    @GetMapping("/basic/singlePermission")
+    public R<Void> singlePermission() {
+        log.info("【场景3】单一权限(system:user:view)校验通过");
+        return R.ok("拥有system:user:view权限,校验通过");
+    }
+
+    /**
+     * 场景4:忽略所有权限校验(SaIgnore优先级最高)
+     */
+    @SaIgnore
+    @SaCheckRole("none_exist") // 该注解会被忽略
+    @GetMapping("/basic/ignoreAll")
+    public R<Void> ignoreAll() {
+        log.info("【场景4】SaIgnore忽略所有权限校验");
+        return R.ok("SaIgnore生效,所有权限校验被忽略");
+    }
+
+    // ====================== 进阶场景:多条件组合(AND/OR) ======================
+
+    /**
+     * 场景5:多角色AND模式(必须同时拥有所有角色)
+     */
+    @SaCheckRole(value = {"admin", "operator"}, mode = SaMode.AND)
+    @GetMapping("/advance/multiRoleAnd")
+    public R<Void> multiRoleAnd() {
+        log.info("【场景5】多角色AND模式(admin+operator)校验通过");
+        return R.ok("同时拥有admin和operator角色,校验通过");
+    }
+
+    /**
+     * 场景6:多角色OR模式(拥有任一角色即可)
+     */
+    @SaCheckRole(value = {"admin", "test"}, mode = SaMode.OR)
+    @GetMapping("/advance/multiRoleOr")
+    public R<Void> multiRoleOr() {
+        log.info("【场景6】多角色OR模式(admin|test)校验通过");
+        return R.ok("拥有admin或test角色,校验通过");
+    }
+
+    /**
+     * 场景7:多权限AND模式(必须同时拥有所有权限)
+     */
+    @SaCheckPermission(value = {"system:user:edit", "system:log:view"}, mode = SaMode.AND)
+    @GetMapping("/advance/multiPermAnd")
+    public R<Void> multiPermAnd() {
+        log.info("【场景7】多权限AND模式(system:user:edit+system:log:view)校验通过");
+        return R.ok("同时拥有system:user:edit和system:log:view权限,校验通过");
+    }
+
+    /**
+     * 场景8:多权限OR模式(拥有任一权限即可)
+     */
+    @SaCheckPermission(value = {"system:user:add", "system:user:delete"}, mode = SaMode.OR)
+    @GetMapping("/advance/multiPermOr")
+    public R<Void> multiPermOr() {
+        log.info("【场景8】多权限OR模式(system:user:add|system:user:delete)校验通过");
+        return R.ok("拥有system:user:add或system:user:delete权限,校验通过");
+    }
+
+    // ====================== 高级场景:通配符/混合组合 ======================
+
+    /**
+     * 场景9:权限通配符匹配(前缀匹配)
+     * 拥有system:user:* 即可匹配所有用户模块权限
+     */
+    @SaCheckPermission("system:user:*")
+    @GetMapping("/advanced/permWildcardPrefix")
+    public R<Void> permWildcardPrefix() {
+        log.info("【场景9】权限通配符(system:user:*)校验通过");
+        return R.ok("拥有system:user:*前缀权限,校验通过");
+    }
+
+    /**
+     * 场景10:角色通配符匹配(前缀匹配)
+     * 拥有admin_* 即可匹配所有admin开头的角色
+     */
+    @SaCheckRole("admin_*")
+    @GetMapping("/advanced/roleWildcardPrefix")
+    public R<Void> roleWildcardPrefix() {
+        log.info("【场景10】角色通配符(admin_*)校验通过");
+        return R.ok("拥有admin_*前缀角色,校验通过");
+    }
+
+    /**
+     * 场景11:权限+角色混合AND模式(所有条件必须满足)
+     * 需同时满足:拥有admin角色 + 拥有system:user:all权限
+     */
+    @SaCheckRole("admin")
+    @SaCheckPermission("system:user:all")
+    @GetMapping("/advanced/mixRolePermAnd")
+    public R<Void> mixRolePermAnd() {
+        log.info("【场景11】角色+权限混合AND(admin+system:user:all)校验通过");
+        return R.ok("拥有admin角色且拥有system:user:all权限,校验通过");
+    }
+
+    /**
+     * 场景12:权限+角色混合OR模式(任一条件满足即可)
+     * 满足任一:拥有super_admin角色 | 拥有system:manage权限
+     */
+    @SaCheckRole(value = {"super_admin"}, mode = SaMode.OR)
+    @SaCheckPermission(value = {"system:manage"}, mode = SaMode.OR)
+    @GetMapping("/advanced/mixRolePermOr")
+    public R<Void> mixRolePermOr() {
+        log.info("【场景12】角色+权限混合OR(super_admin|system:manage)校验通过");
+        return R.ok("拥有super_admin角色或system:manage权限,校验通过");
+    }
+
+    /**
+     * 场景13:orRole参数(权限校验失败时,兜底角色校验)
+     * 核心逻辑:无system:user:export权限时,检查是否有admin/operator角色
+     */
+    @SaCheckPermission(value = "system:user:export", orRole = {"admin", "operator"})
+    @GetMapping("/advanced/permWithOrRole")
+    public R<Void> permWithOrRole() {
+        log.info("【场景13】权限+orRole兜底校验通过");
+        return R.ok("拥有system:user:export权限,或拥有admin/operator角色,校验通过");
+    }
+
+    // ====================== 特殊场景:临时权限/注解覆盖 ======================
+
+    /**
+     * 场景14:SaIgnore局部覆盖(方法注解覆盖类注解,若有)
+     * 假设类上有@SaCheckLogin,方法上@SaIgnore会覆盖
+     */
+    @SaIgnore
+    @GetMapping("/special/ignoreOverride")
+    public R<Void> ignoreOverride() {
+        log.info("【场景14】SaIgnore覆盖类级别权限注解");
+        return R.ok("方法级SaIgnore覆盖类级别权限校验");
+    }
+
+    /**
+     * 场景15:临时权限校验(SaCheckPermission逻辑:临时权限>永久权限)
+     * 注:临时权限需通过SaToken API手动设置,如 SaHolder.getStpLogic().setTempPermission("system:temp:test")
+     */
+    @SaCheckPermission("system:temp:test")
+    @GetMapping("/special/tempPermission")
+    public R<Void> tempPermission() {
+        log.info("【场景15】临时权限(system:temp:test)校验通过");
+        return R.ok("临时权限校验通过(需先通过API设置临时权限)");
+    }
+
+    /**
+     * 场景16:登录类型指定(多端登录场景,如PC/APP/小程序)
+     * 注:需配合SaToken多账号体系配置
+     */
+    @SaCheckLogin(type = "PC") // 仅校验PC端的登录态
+    @GetMapping("/special/loginTypeSpecify")
+    public R<Void> loginTypeSpecify() {
+        log.info("【场景16】指定登录类型(PC)校验通过");
+        return R.ok("仅PC端登录态校验通过");
+    }
+}

+ 1 - 1
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestDemoController.java

@@ -17,7 +17,7 @@ import org.dromara.common.log.annotation.Log;
 import org.dromara.common.log.enums.BusinessType;
 import org.dromara.demo.domain.TestDemo;
 import org.dromara.demo.domain.bo.TestDemoBo;
-import org.dromara.demo.domain.bo.TestDemoImportVo;
+import org.dromara.demo.domain.vo.TestDemoImportVo;
 import org.dromara.demo.domain.vo.TestDemoVo;
 import org.dromara.demo.service.ITestDemoService;
 import lombok.RequiredArgsConstructor;

+ 2 - 2
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/ExportDemoVo.java

@@ -35,8 +35,8 @@ public class ExportDemoVo implements Serializable {
     /**
      * 用户昵称
      */
-    @ExcelProperty(value = "用户", index = 0)
-    @NotEmpty(message = "用户不能为空", groups = AddGroup.class)
+    @ExcelProperty(value = "用户昵称", index = 0)
+    @NotEmpty(message = "用户昵称不能为空", groups = AddGroup.class)
     private String nickName;
 
     /**

+ 4 - 1
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestDemoImportVo.java → ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoImportVo.java

@@ -1,10 +1,12 @@
-package org.dromara.demo.domain.bo;
+package org.dromara.demo.domain.vo;
 
 import cn.idev.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
 import lombok.Data;
 
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import org.dromara.demo.domain.TestDemo;
 
 /**
  * 测试单表业务对象 test_demo
@@ -13,6 +15,7 @@ import jakarta.validation.constraints.NotNull;
  * @date 2021-07-26
  */
 @Data
+@AutoMapper(target = TestDemo.class)
 public class TestDemoImportVo {
 
     /**

+ 3 - 0
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java

@@ -523,6 +523,9 @@ public class GenTableServiceImpl implements IGenTableService {
      * @param table 业务表信息
      */
     public void setPkColumn(GenTable table) {
+        if (CollUtil.isEmpty(table.getColumns())) {
+            throw new ServiceException("表【" + table.getTableName() + "】字段为空,请检查表结构");
+        }
         for (GenTableColumn column : table.getColumns()) {
             if (column.isPk()) {
                 table.setPkColumn(column);

+ 1 - 0
ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapJobAnnotation.java

@@ -21,6 +21,7 @@ import java.util.stream.IntStream;
  *
  * @author 老马
  */
+@SuppressWarnings({"unchecked", "rawtypes"})
 @Component
 @JobExecutor(name = "testMapJobAnnotation")
 public class TestMapJobAnnotation {

+ 1 - 0
ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapReduceAnnotation1.java

@@ -23,6 +23,7 @@ import java.util.stream.IntStream;
  *
  * @author 老马
  */
+@SuppressWarnings({"unchecked", "rawtypes"})
 @Component
 @JobExecutor(name = "testMapReduceAnnotation1")
 public class TestMapReduceAnnotation1 {

+ 4 - 4
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java

@@ -83,7 +83,7 @@ public class SysDictDataController extends BaseController {
     }
 
     /**
-     * 新增字典类型
+     * 新增字典数据
      */
     @SaCheckPermission("system:dict:add")
     @Log(title = "字典数据", businessType = BusinessType.INSERT)
@@ -98,7 +98,7 @@ public class SysDictDataController extends BaseController {
     }
 
     /**
-     * 修改保存字典类型
+     * 修改保存字典数据
      */
     @SaCheckPermission("system:dict:edit")
     @Log(title = "字典数据", businessType = BusinessType.UPDATE)
@@ -113,12 +113,12 @@ public class SysDictDataController extends BaseController {
     }
 
     /**
-     * 删除字典类型
+     * 删除字典数据
      *
      * @param dictCodes 字典code串
      */
     @SaCheckPermission("system:dict:remove")
-    @Log(title = "字典类型", businessType = BusinessType.DELETE)
+    @Log(title = "字典数据", businessType = BusinessType.DELETE)
     @DeleteMapping("/{dictCodes}")
     public R<Void> remove(@PathVariable Long[] dictCodes) {
         dictDataService.deleteDictDataByIds(Arrays.asList(dictCodes));

+ 1 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java

@@ -21,7 +21,7 @@ public class SysUserOnline {
     private String deptName;
 
     /**
-     * 用户名称
+     * 用户账号
      */
     private String userName;
 

+ 2 - 2
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java

@@ -34,13 +34,13 @@ public class SysUserExportVo implements Serializable {
     /**
      * 用户账号
      */
-    @ExcelProperty(value = "登录名称")
+    @ExcelProperty(value = "用户账号")
     private String userName;
 
     /**
      * 用户昵称
      */
-    @ExcelProperty(value = "用户称")
+    @ExcelProperty(value = "用户称")
     private String nickName;
 
     /**

+ 2 - 2
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java

@@ -38,13 +38,13 @@ public class SysUserImportVo implements Serializable {
     /**
      * 用户账号
      */
-    @ExcelProperty(value = "登录名称")
+    @ExcelProperty(value = "用户账号")
     private String userName;
 
     /**
      * 用户昵称
      */
-    @ExcelProperty(value = "用户称")
+    @ExcelProperty(value = "用户称")
     private String nickName;
 
     /**

+ 1 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java

@@ -101,7 +101,7 @@ public interface ISysUserService {
     String selectUserPostGroup(Long userId);
 
     /**
-     * 校验用户名称是否唯一
+     * 校验用户账号是否唯一
      *
      * @param user 用户信息
      * @return 结果

+ 15 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java

@@ -229,6 +229,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
     @Override
     public String getDictLabel(String dictType, String dictValue, String separator) {
         List<SysDictDataVo> datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
+        if (CollUtil.isEmpty(datas)) {
+            return StringUtils.EMPTY;
+        }
         Map<String, String> map = StreamUtils.toMap(datas, SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel);
         if (StringUtils.containsAny(dictValue, separator)) {
             return Arrays.stream(dictValue.split(separator))
@@ -250,6 +253,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
     @Override
     public String getDictValue(String dictType, String dictLabel, String separator) {
         List<SysDictDataVo> datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
+        if (CollUtil.isEmpty(datas)) {
+            return StringUtils.EMPTY;
+        }
         Map<String, String> map = StreamUtils.toMap(datas, SysDictDataVo::getDictLabel, SysDictDataVo::getDictValue);
         if (StringUtils.containsAny(dictLabel, separator)) {
             return Arrays.stream(dictLabel.split(separator))
@@ -269,6 +275,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
     @Override
     public Map<String, String> getAllDictByDictType(String dictType) {
         List<SysDictDataVo> list = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
+        if (CollUtil.isEmpty(list)) {
+            return new HashMap<>();
+        }
         // 保证顺序
         LinkedHashMap<String, String> map = new LinkedHashMap<>();
         for (SysDictDataVo vo : list) {
@@ -286,6 +295,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
     @Override
     public DictTypeDTO getDictType(String dictType) {
         SysDictTypeVo vo = SpringUtils.getAopProxy(this).selectDictTypeByType(dictType);
+        if (ObjectUtil.isNull(vo)) {
+            return null;
+        }
         return BeanUtil.toBean(vo, DictTypeDTO.class);
     }
 
@@ -298,6 +310,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
     @Override
     public List<DictDataDTO> getDictData(String dictType) {
         List<SysDictDataVo> list = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
+        if (CollUtil.isEmpty(list)) {
+            return new ArrayList<>();
+        }
         return BeanUtil.copyToList(list, DictDataDTO.class);
     }
 

+ 1 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java

@@ -112,7 +112,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
     /**
      * 根据用户ID查询菜单
      *
-     * @param userId 用户名称
+     * @param userId 用户ID
      * @return 菜单列表
      */
     @Override

+ 13 - 8
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java

@@ -232,7 +232,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
     }
 
     /**
-     * 校验用户名称是否唯一
+     * 校验用户账号是否唯一
      *
      * @param user 用户信息
      * @return 结果
@@ -497,6 +497,11 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
             roleList.remove(SystemConstants.SUPER_ADMIN_ID);
         }
 
+        // 移除超管角色后若无剩余角色,说明仅选了超管角色且不允许分配,显式报错
+        if (roleList.isEmpty()) {
+            throw new ServiceException("不允许为普通用户分配超级管理员角色,请至少选择一个其他角色");
+        }
+
         // 校验是否有权限访问这些角色(含数据权限控制)
         if (roleMapper.selectRoleCount(roleList) != roleList.size()) {
             throw new ServiceException("没有权限访问角色的数据");
@@ -594,10 +599,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
     }
 
     /**
-     * 通过用户ID查询用户账户
+     * 通过用户ID查询用户昵称
      *
      * @param userId 用户ID
-     * @return 用户账户
+     * @return 用户昵称
      */
     @Override
     @Cacheable(cacheNames = CacheNames.SYS_NICKNAME, key = "#userId")
@@ -608,10 +613,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
     }
 
     /**
-     * 通过用户ID查询用户账户
+     * 通过用户ID查询用户昵称
      *
      * @param userIds 用户ID 多个用逗号隔开
-     * @return 用户账户
+     * @return 用户昵称
      */
     @Override
     public String selectNicknameByIds(String userIds) {
@@ -751,13 +756,13 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
     }
 
     /**
-     * 根据用户 ID 列表查询用户称映射关系
+     * 根据用户 ID 列表查询用户称映射关系
      *
      * @param userIds 用户 ID 列表
-     * @return Map,其中 key 为用户 ID,value 为对应的用户
+     * @return Map,其中 key 为用户 ID,value 为对应的用户
      */
     @Override
-    public Map<Long, String> selectUserNamesByIds(List<Long> userIds) {
+    public Map<Long, String> selectUserNicksByIds(List<Long> userIds) {
         if (CollUtil.isEmpty(userIds)) {
             return Collections.emptyMap();
         }

+ 0 - 20
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java

@@ -23,26 +23,6 @@ public interface FlowConstant {
      */
     String INITIATOR_DEPT_ID = "initiatorDeptId";
 
-    /**
-     * 委托
-     */
-    String DELEGATE_TASK = "delegateTask";
-
-    /**
-     * 转办
-     */
-    String TRANSFER_TASK = "transferTask";
-
-    /**
-     * 加签
-     */
-    String ADD_SIGNATURE = "addSignature";
-
-    /**
-     * 减签
-     */
-    String REDUCTION_SIGNATURE = "reductionSignature";
-
     /**
      * 流程分类Id转名称
      */

+ 53 - 0
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskOperationEnum.java

@@ -0,0 +1,53 @@
+package org.dromara.workflow.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 任务操作类型枚举
+ *
+ * @author may
+ */
+@Getter
+@AllArgsConstructor
+public enum TaskOperationEnum {
+
+    /**
+     * 委派
+     */
+    DELEGATE_TASK("delegateTask", "委派"),
+
+    /**
+     * 转办
+     */
+    TRANSFER_TASK("transferTask", "转办"),
+
+    /**
+     * 加签
+     */
+    ADD_SIGNATURE("addSignature", "加签"),
+
+    /**
+     * 减签
+     */
+    REDUCTION_SIGNATURE("reductionSignature", "减签");
+
+    private final String code;
+    private final String desc;
+
+    private static final Map<String, TaskOperationEnum> CODE_MAP = Arrays.stream(values())
+        .collect(Collectors.toConcurrentMap(TaskOperationEnum::getCode, Function.identity()));
+
+    /**
+     * 根据 code 获取枚举
+     */
+    public static TaskOperationEnum getByCode(String code) {
+        return CODE_MAP.get(code);
+    }
+
+}

+ 14 - 0
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwDefinitionController.java

@@ -1,5 +1,6 @@
 package org.dromara.workflow.controller;
 
+import cn.dev33.satoken.annotation.SaCheckPermission;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.domain.R;
@@ -45,6 +46,7 @@ public class FlwDefinitionController extends BaseController {
      * @param pageQuery      分页
      */
     @GetMapping("/list")
+    @SaCheckPermission("workflow:definition:list")
     public TableDataInfo<FlowDefinitionVo> list(FlowDefinition flowDefinition, PageQuery pageQuery) {
         return flwDefinitionService.queryList(flowDefinition, pageQuery);
     }
@@ -56,6 +58,7 @@ public class FlwDefinitionController extends BaseController {
      * @param pageQuery      分页
      */
     @GetMapping("/unPublishList")
+    @SaCheckPermission("workflow:definition:list")
     public TableDataInfo<FlowDefinitionVo> unPublishList(FlowDefinition flowDefinition, PageQuery pageQuery) {
         return flwDefinitionService.unPublishList(flowDefinition, pageQuery);
     }
@@ -66,6 +69,7 @@ public class FlwDefinitionController extends BaseController {
      * @param id 流程定义id
      */
     @GetMapping(value = "/{id}")
+    @SaCheckPermission("workflow:definition:query")
     public R<Definition> getInfo(@PathVariable Long id) {
         return R.ok(defService.getById(id));
     }
@@ -79,6 +83,7 @@ public class FlwDefinitionController extends BaseController {
     @PostMapping
     @RepeatSubmit()
     @Transactional(rollbackFor = Exception.class)
+    @SaCheckPermission("workflow:definition:add")
     public R<Boolean> add(@RequestBody FlowDefinition flowDefinition) {
         return R.ok(defService.checkAndSave(flowDefinition));
     }
@@ -92,6 +97,7 @@ public class FlwDefinitionController extends BaseController {
     @PutMapping
     @RepeatSubmit()
     @Transactional(rollbackFor = Exception.class)
+    @SaCheckPermission("workflow:definition:edit")
     public R<Boolean> edit(@RequestBody FlowDefinition flowDefinition) {
         return R.ok(defService.updateById(flowDefinition));
     }
@@ -104,6 +110,7 @@ public class FlwDefinitionController extends BaseController {
     @Log(title = "流程定义", businessType = BusinessType.INSERT)
     @PutMapping("/publish/{id}")
     @RepeatSubmit()
+    @SaCheckPermission("workflow:definition:publish")
     public R<Boolean> publish(@PathVariable Long id) {
         return R.ok(flwDefinitionService.publish(id));
     }
@@ -117,6 +124,7 @@ public class FlwDefinitionController extends BaseController {
     @PutMapping("/unPublish/{id}")
     @RepeatSubmit()
     @Transactional(rollbackFor = Exception.class)
+    @SaCheckPermission("workflow:definition:publish")
     public R<Boolean> unPublish(@PathVariable Long id) {
         return R.ok(defService.unPublish(id));
     }
@@ -126,6 +134,7 @@ public class FlwDefinitionController extends BaseController {
      */
     @Log(title = "流程定义", businessType = BusinessType.DELETE)
     @DeleteMapping("/{ids}")
+    @SaCheckPermission("workflow:definition:remove")
     public R<Void> remove(@PathVariable List<Long> ids) {
         return toAjax(flwDefinitionService.removeDef(ids));
     }
@@ -139,6 +148,7 @@ public class FlwDefinitionController extends BaseController {
     @PostMapping("/copy/{id}")
     @RepeatSubmit()
     @Transactional(rollbackFor = Exception.class)
+    @SaCheckPermission("workflow:definition:copy")
     public R<Boolean> copy(@PathVariable Long id) {
         return R.ok(defService.copyDef(id));
     }
@@ -151,6 +161,7 @@ public class FlwDefinitionController extends BaseController {
      */
     @Log(title = "流程定义", businessType = BusinessType.IMPORT)
     @PostMapping("/importDef")
+    @SaCheckPermission("workflow:definition:import")
     public R<Boolean> importDef(MultipartFile file, String category) {
         return R.ok(flwDefinitionService.importJson(file, category));
     }
@@ -164,6 +175,7 @@ public class FlwDefinitionController extends BaseController {
      */
     @Log(title = "流程定义", businessType = BusinessType.EXPORT)
     @PostMapping("/exportDef/{id}")
+    @SaCheckPermission("workflow:definition:export")
     public void exportDef(@PathVariable Long id, HttpServletResponse response) throws IOException {
         flwDefinitionService.exportDef(id, response);
     }
@@ -174,6 +186,7 @@ public class FlwDefinitionController extends BaseController {
      * @param id 流程定义id
      */
     @GetMapping("/xmlString/{id}")
+    @SaCheckPermission("workflow:definition:query")
     public R<String> xmlString(@PathVariable Long id) {
         return R.ok("操作成功", defService.exportJson(id));
     }
@@ -188,6 +201,7 @@ public class FlwDefinitionController extends BaseController {
     @PutMapping("/active/{id}")
     @Transactional(rollbackFor = Exception.class)
     @Log(title = "流程定义", businessType = BusinessType.UPDATE)
+    @SaCheckPermission("workflow:definition:active")
     public R<Boolean> active(@PathVariable Long id, @RequestParam boolean active) {
         return R.ok(active ? defService.active(id) : defService.unActive(id));
     }

+ 14 - 0
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwInstanceController.java

@@ -1,5 +1,6 @@
 package org.dromara.workflow.controller;
 
+import cn.dev33.satoken.annotation.SaCheckPermission;
 import cn.hutool.core.convert.Convert;
 import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.domain.R;
@@ -46,6 +47,7 @@ public class FlwInstanceController extends BaseController {
      * @param pageQuery      分页
      */
     @GetMapping("/pageByRunning")
+    @SaCheckPermission("workflow:instance:list")
     public TableDataInfo<FlowInstanceVo> selectRunningInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) {
         return flwInstanceService.selectRunningInstanceList(flowInstanceBo, pageQuery);
     }
@@ -57,6 +59,7 @@ public class FlwInstanceController extends BaseController {
      * @param pageQuery      分页
      */
     @GetMapping("/pageByFinish")
+    @SaCheckPermission("workflow:instance:list")
     public TableDataInfo<FlowInstanceVo> selectFinishInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) {
         return flwInstanceService.selectFinishInstanceList(flowInstanceBo, pageQuery);
     }
@@ -67,6 +70,7 @@ public class FlwInstanceController extends BaseController {
      * @param businessId 业务id
      */
     @GetMapping("/getInfo/{businessId}")
+    @SaCheckPermission("workflow:instance:query")
     public R<FlowInstanceVo> getInfo(@PathVariable Long businessId) {
         return R.ok(flwInstanceService.queryByBusinessId(businessId));
     }
@@ -78,6 +82,7 @@ public class FlwInstanceController extends BaseController {
      */
     @DeleteMapping("/deleteByBusinessIds/{businessIds}")
     @Log(title = "流程实例管理", businessType = BusinessType.DELETE)
+    @SaCheckPermission("workflow:instance:remove")
     public R<Void> deleteByBusinessIds(@PathVariable List<Long> businessIds) {
         return toAjax(flwInstanceService.deleteByBusinessIds(StreamUtils.toList(businessIds, Convert::toStr)));
     }
@@ -89,6 +94,7 @@ public class FlwInstanceController extends BaseController {
      */
     @DeleteMapping("/deleteByInstanceIds/{instanceIds}")
     @Log(title = "流程实例管理", businessType = BusinessType.DELETE)
+    @SaCheckPermission("workflow:instance:remove")
     public R<Void> deleteByInstanceIds(@PathVariable List<Long> instanceIds) {
         return toAjax(flwInstanceService.deleteByInstanceIds(instanceIds));
     }
@@ -100,6 +106,7 @@ public class FlwInstanceController extends BaseController {
      */
     @DeleteMapping("/deleteHisByInstanceIds/{instanceIds}")
     @Log(title = "流程实例管理", businessType = BusinessType.DELETE)
+    @SaCheckPermission("workflow:instance:remove")
     public R<Void> deleteHisByInstanceIds(@PathVariable List<Long> instanceIds) {
         return toAjax(flwInstanceService.deleteHisByInstanceIds(instanceIds));
     }
@@ -112,6 +119,7 @@ public class FlwInstanceController extends BaseController {
     @RepeatSubmit()
     @PutMapping("/cancelProcessApply")
     @Log(title = "流程实例管理", businessType = BusinessType.UPDATE)
+    @SaCheckPermission("workflow:instance:cancel")
     public R<Void> cancelProcessApply(@RequestBody FlowCancelBo bo) {
         return toAjax(flwInstanceService.cancelProcessApply(bo));
     }
@@ -125,6 +133,7 @@ public class FlwInstanceController extends BaseController {
     @RepeatSubmit()
     @PutMapping("/active/{id}")
     @Log(title = "流程实例管理", businessType = BusinessType.UPDATE)
+    @SaCheckPermission("workflow:instance:active")
     public R<Boolean> active(@PathVariable Long id, @RequestParam boolean active) {
         return R.ok(active ? insService.active(id) : insService.unActive(id));
     }
@@ -136,6 +145,7 @@ public class FlwInstanceController extends BaseController {
      * @param pageQuery      分页
      */
     @GetMapping("/pageByCurrent")
+    @SaCheckPermission("workflow:instance:currentList")
     public TableDataInfo<FlowInstanceVo> selectCurrentInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) {
         return flwInstanceService.selectCurrentInstanceList(flowInstanceBo, pageQuery);
     }
@@ -146,6 +156,7 @@ public class FlwInstanceController extends BaseController {
      * @param businessId 业务id
      */
     @GetMapping("/flowHisTaskList/{businessId}")
+    @SaCheckPermission("workflow:instance:query")
     public R<Map<String, Object>> flowHisTaskList(@PathVariable String businessId) {
         return R.ok(flwInstanceService.flowHisTaskList(businessId));
     }
@@ -156,6 +167,7 @@ public class FlwInstanceController extends BaseController {
      * @param instanceId 流程实例id
      */
     @GetMapping("/instanceVariable/{instanceId}")
+    @SaCheckPermission("workflow:instance:variableQuery")
     public R<Map<String, Object>> instanceVariable(@PathVariable Long instanceId) {
         return R.ok(flwInstanceService.instanceVariable(instanceId));
     }
@@ -168,6 +180,7 @@ public class FlwInstanceController extends BaseController {
     @RepeatSubmit()
     @PutMapping("/updateVariable")
     @Log(title = "流程实例管理", businessType = BusinessType.UPDATE)
+    @SaCheckPermission("workflow:instance:variable")
     public R<Void> updateVariable(@Validated @RequestBody FlowVariableBo bo) {
         return toAjax(flwInstanceService.updateVariable(bo));
     }
@@ -180,6 +193,7 @@ public class FlwInstanceController extends BaseController {
     @Log(title = "流程实例管理", businessType = BusinessType.INSERT)
     @RepeatSubmit()
     @PostMapping("/invalid")
+    @SaCheckPermission("workflow:instance:invalid")
     public R<Boolean> invalid(@Validated @RequestBody FlowInvalidBo bo) {
         return R.ok(flwInstanceService.processInvalid(bo));
     }

+ 2 - 1
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java

@@ -61,7 +61,8 @@ public class BackProcessBo implements Serializable {
 
     public Map<String, Object> getVariables() {
         if (variables == null) {
-            return new HashMap<>(16);
+            variables = new HashMap<>(16);
+            return variables;
         }
         variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
         return variables;

+ 2 - 2
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowCopyBo.java

@@ -23,8 +23,8 @@ public class FlowCopyBo implements Serializable {
     private Long userId;
 
     /**
-     * 用户
+     * 用户
      */
-    private String userName;
+    private String nickName;
 
 }

+ 2 - 1
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowNextNodeBo.java

@@ -30,7 +30,8 @@ public class FlowNextNodeBo implements Serializable {
 
     public Map<String, Object> getVariables() {
         if (variables == null) {
-            return new HashMap<>(16);
+            variables = new HashMap<>(16);
+            return variables;
         }
         variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
         return variables;

+ 2 - 1
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java

@@ -53,7 +53,8 @@ public class StartProcessBo implements Serializable {
 
     public Map<String, Object> getVariables() {
         if (variables == null) {
-            return new HashMap<>(16);
+            variables = new HashMap<>(16);
+            return variables;
         }
         variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
         return variables;

+ 5 - 0
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskOperationBo.java

@@ -40,6 +40,11 @@ public class TaskOperationBo implements Serializable {
     @NotNull(message = "任务id不能为空")
     private Long taskId;
 
+    /**
+     * 消息类型
+     */
+    private List<String> messageType;
+
     /**
      * 意见或备注信息(可选)
      */

+ 2 - 2
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCopyVo.java

@@ -24,10 +24,10 @@ public class FlowCopyVo implements Serializable {
     private Long userId;
 
     /**
-     * 用户
+     * 用户
      */
     @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "userId")
-    private String userName;
+    private String nickName;
 
     public FlowCopyVo(Long userId) {
         this.userId = userId;

+ 4 - 2
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java

@@ -80,11 +80,13 @@ public class WorkflowGlobalListener implements GlobalListener {
             NodeExtVo nodeExt = nodeExtService.parseNodeExt(ext, variable);
             Set<String> copyList = nodeExt.getCopySettings();
             if (CollUtil.isNotEmpty(copyList)) {
+                List<Long> userIds = StreamUtils.toList(copyList, Convert::toLong);
+                Map<Long, String> nickNameMap = userService.selectUserNicksByIds(userIds);
                 List<FlowCopyBo> list = StreamUtils.toList(copyList, x -> {
                     FlowCopyBo bo = new FlowCopyBo();
                     Long id = Convert.toLong(x);
                     bo.setUserId(id);
-                    bo.setUserName(userService.selectUserNameById(id));
+                    bo.setNickName(nickNameMap.getOrDefault(id, StringUtils.EMPTY));
                     return bo;
                 });
                 variable.put(FlowConstant.FLOW_COPY_LIST, list);
@@ -159,7 +161,7 @@ public class WorkflowGlobalListener implements GlobalListener {
             flowTask.setPermissionList(List.of(userIdArray));
             // 移除已处理的状态变量
             variable.remove(nodeKey);
-            FlowEngine.insService().removeVariables(flowTask.getInstanceId(),nodeKey);
+            FlowEngine.insService().removeVariables(flowTask.getInstanceId(), nodeKey);
         }
     }
 

+ 12 - 1
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java

@@ -5,6 +5,7 @@ import cn.hutool.core.util.ObjectUtil;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.dromara.common.core.domain.dto.UserDTO;
+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;
@@ -98,7 +99,14 @@ public class FlwCommonServiceImpl implements IFlwCommonService {
                     }
                     case EMAIL_MESSAGE -> MailUtils.sendText(emails, subject, message);
                     case SMS_MESSAGE -> {
-                        // TODO: 补充短信发送逻辑
+//                        LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
+//                        // 根据具体短信服务商参数用法传参
+//                        map.put("code", "1234");
+//                        // 自动获取一个短信服务商
+//                        SmsBlend smsBlend = SmsFactory.getSmsBlend();
+//                        // 指定获取一个短信服务商 configKey
+//                        SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
+//                        SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map);
                         log.info("【短信发送 - TODO】用户数量={} 内容={}", userList.size(), message);
                     }
                     default -> log.warn("【消息发送】未处理的消息类型:{}", messageTypeEnum);
@@ -119,6 +127,9 @@ public class FlwCommonServiceImpl implements IFlwCommonService {
     @Override
     public String applyNodeCode(Long definitionId) {
         List<Node> firstBetweenNode = FlowEngine.nodeService().getFirstBetweenNode(definitionId, new HashMap<>());
+        if (CollUtil.isEmpty(firstBetweenNode)) {
+            throw new ServiceException("流程定义缺少申请人节点,请检查流程定义配置");
+        }
         return firstBetweenNode.get(0).getNodeCode();
     }
 }

+ 47 - 31
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java

@@ -111,8 +111,14 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
     @Override
     public FlowInstanceVo queryByBusinessId(Long businessId) {
         FlowInstance instance = this.selectInstByBusinessId(Convert.toStr(businessId));
+        if (ObjectUtil.isNull(instance)) {
+            throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE);
+        }
         FlowInstanceVo instanceVo = BeanUtil.toBean(instance, FlowInstanceVo.class);
         Definition definition = defService.getById(instanceVo.getDefinitionId());
+        if (ObjectUtil.isNull(definition)) {
+            throw new ServiceException(ExceptionCons.NOT_FOUNT_DEF);
+        }
         instanceVo.setFlowName(definition.getFlowName());
         instanceVo.setFlowCode(definition.getFlowCode());
         instanceVo.setVersion(definition.getVersion());
@@ -187,6 +193,8 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
             log.warn("未找到对应的流程实例信息,无法执行删除操作。");
             return false;
         }
+        // 发送事件
+        processDeleteHandler(flowInstances);
         return insService.remove(StreamUtils.toList(flowInstances, FlowInstance::getId));
     }
 
@@ -199,27 +207,13 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
     @Transactional(rollbackFor = Exception.class)
     public boolean deleteByInstanceIds(List<Long> instanceIds) {
         // 获取实例信息
-        List<Instance> instances = insService.getByIds(instanceIds);
-        if (CollUtil.isEmpty(instances)) {
+        List<FlowInstance> flowInstances = flowInstanceMapper.selectByIds(instanceIds);
+        if (CollUtil.isEmpty(flowInstances)) {
             log.warn("未找到对应的流程实例信息,无法执行删除操作。");
             return false;
         }
-        // 获取定义信息
-        Map<Long, Definition> definitionMap = StreamUtils.toMap(
-            defService.getByIds(StreamUtils.toList(instances, Instance::getDefinitionId)),
-            Definition::getId,
-            Function.identity()
-        );
-
-        // 逐一触发删除事件
-        instances.forEach(instance -> {
-            Definition definition = definitionMap.get(instance.getDefinitionId());
-            if (ObjectUtil.isNull(definition)) {
-                log.warn("实例 ID: {} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId());
-                return;
-            }
-            flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId());
-        });
+        // 发送事件
+        processDeleteHandler(flowInstances);
         // 删除实例
         return insService.remove(instanceIds);
     }
@@ -233,19 +227,43 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
     @Transactional(rollbackFor = Exception.class)
     public boolean deleteHisByInstanceIds(List<Long> instanceIds) {
         // 获取实例信息
-        List<Instance> instances = insService.getByIds(instanceIds);
-        if (CollUtil.isEmpty(instances)) {
+        List<FlowInstance> flowInstances = flowInstanceMapper.selectByIds(instanceIds);
+        if (CollUtil.isEmpty(flowInstances)) {
             log.warn("未找到对应的流程实例信息,无法执行删除操作。");
             return false;
         }
+        // 发送事件
+        processDeleteHandler(flowInstances);
+        List<FlowTask> flowTaskList = flwTaskService.selectByInstIds(instanceIds);
+        if (CollUtil.isNotEmpty(flowTaskList)) {
+            FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTaskList, FlowTask::getId));
+        }
+        FlowEngine.taskService().deleteByInsIds(instanceIds);
+        FlowEngine.hisTaskService().deleteByInsIds(instanceIds);
+        FlowEngine.insService().removeByIds(instanceIds);
+        return true;
+    }
+
+
+    private void processDeleteHandler(List<FlowInstance> flowInstances) {
+
+        String userId = LoginHelper.getUserIdStr();
+        for (FlowInstance flowInstance : flowInstances) {
+            //如果创建人与当前登陆人一致或者当前登陆人为管理员才能删除
+            if (LoginHelper.isSuperAdmin() || flowInstance.getCreateBy().equals(userId)) {
+                continue;
+            }
+            throw new ServiceException("权限不足,无法删除流程实例信息!");
+        }
         // 获取定义信息
         Map<Long, Definition> definitionMap = StreamUtils.toMap(
-            defService.getByIds(StreamUtils.toList(instances, Instance::getDefinitionId)),
+            defService.getByIds(StreamUtils.toList(flowInstances, Instance::getDefinitionId)),
             Definition::getId,
             Function.identity()
         );
+
         // 逐一触发删除事件
-        instances.forEach(instance -> {
+        flowInstances.forEach(instance -> {
             Definition definition = definitionMap.get(instance.getDefinitionId());
             if (ObjectUtil.isNull(definition)) {
                 log.warn("实例 ID: {} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId());
@@ -253,14 +271,6 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
             }
             flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId());
         });
-        List<FlowTask> flowTaskList = flwTaskService.selectByInstIds(instanceIds);
-        if (CollUtil.isNotEmpty(flowTaskList)) {
-            FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTaskList, FlowTask::getId));
-        }
-        FlowEngine.taskService().deleteByInsIds(instanceIds);
-        FlowEngine.hisTaskService().deleteByInsIds(instanceIds);
-        FlowEngine.insService().removeByIds(instanceIds);
-        return true;
     }
 
     /**
@@ -279,8 +289,11 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
         if (definition == null) {
             throw new ServiceException(ExceptionCons.NOT_FOUNT_DEF);
         }
-        String message = bo.getMessage();
         String userIdStr = LoginHelper.getUserIdStr();
+        if (!LoginHelper.isSuperAdmin() && !instance.getCreateBy().equals(userIdStr)) {
+            throw new ServiceException("权限不足,无法撤销流程!");
+        }
+        String message = bo.getMessage();
         BusinessStatusEnum.checkCancelStatus(instance.getFlowStatus());
         FlowParams flowParams = FlowParams.build()
             .message(message)
@@ -383,6 +396,9 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
     @Override
     public Map<String, Object> instanceVariable(Long instanceId) {
         FlowInstance flowInstance = flowInstanceMapper.selectById(instanceId);
+        if (ObjectUtil.isNull(flowInstance)) {
+            throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE);
+        }
         Map<String, Object> variableMap = Optional.ofNullable(flowInstance.getVariableMap()).orElse(Collections.emptyMap());
         List<Map<String, Object>> variableList = variableMap.entrySet().stream()
             .map(entry -> Map.of("key", entry.getKey(), "value", entry.getValue()))

+ 1 - 1
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java

@@ -102,7 +102,7 @@ public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService
      * @param sources 数据来源(枚举类或字典类型)
      * @return 构建的 `NodeExt` 对象
      */
-    @SuppressWarnings("unchecked cast")
+    @SuppressWarnings("unchecked")
     private NodeExt buildNodeExt(String code, String name, int type, List<Object> sources) {
         NodeExt nodeExt = new NodeExt();
         nodeExt.setCode(code);

+ 11 - 2
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwSpelServiceImpl.java

@@ -1,6 +1,7 @@
 package org.dromara.workflow.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;
@@ -9,6 +10,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.dromara.common.core.constant.SystemConstants;
 import org.dromara.common.core.domain.dto.TaskAssigneeDTO;
 import org.dromara.common.core.domain.model.TaskAssigneeBody;
+import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.core.utils.StreamUtils;
 import org.dromara.common.core.utils.StringUtils;
@@ -125,7 +127,14 @@ public class FlwSpelServiceImpl implements IFlwSpelService {
      * 保存前的数据校验
      */
     private void validEntityBeforeSave(FlowSpel entity){
-        //TODO 做一些数据校验,如唯一约束
+        if (StringUtils.isNotBlank(entity.getViewSpel())) {
+            boolean exists = baseMapper.exists(new LambdaQueryWrapper<FlowSpel>()
+                .eq(FlowSpel::getViewSpel, entity.getViewSpel())
+                .ne(ObjectUtil.isNotNull(entity.getId()), FlowSpel::getId, entity.getId()));
+            if (exists) {
+                throw new ServiceException("SpEL表达式已存在,请勿重复添加");
+            }
+        }
     }
 
     /**
@@ -137,7 +146,7 @@ public class FlwSpelServiceImpl implements IFlwSpelService {
      */
     @Override
     public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
-        if(isValid){
+        if (isValid){
             //TODO 做一些业务上的校验,判断是否需要校验
         }
         return baseMapper.deleteByIds(ids) > 0;

+ 1 - 1
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java

@@ -244,7 +244,7 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand
 
         List<Long> longIds = StreamUtils.toList(ids, Convert::toLong);
         Map<Long, String> rawMap = switch (type) {
-            case USER -> userService.selectUserNamesByIds(longIds);
+            case USER -> userService.selectUserNicksByIds(longIds);
             case ROLE -> roleService.selectRoleNamesByIds(longIds);
             case DEPT -> deptService.selectDeptNamesByIds(longIds);
             case POST -> postService.selectPostNamesByIds(longIds);

+ 87 - 32
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java

@@ -44,8 +44,8 @@ import org.dromara.warm.flow.orm.mapper.FlowInstanceMapper;
 import org.dromara.warm.flow.orm.mapper.FlowNodeMapper;
 import org.dromara.warm.flow.orm.mapper.FlowTaskMapper;
 import org.dromara.workflow.common.ConditionalOnEnable;
-import org.dromara.workflow.common.constant.FlowConstant;
 import org.dromara.workflow.common.enums.TaskAssigneeType;
+import org.dromara.workflow.common.enums.TaskOperationEnum;
 import org.dromara.workflow.common.enums.TaskStatusEnum;
 import org.dromara.workflow.domain.FlowInstanceBizExt;
 import org.dromara.workflow.domain.bo.*;
@@ -127,6 +127,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
             // 已存在流程
             BusinessStatusEnum.checkStartStatus(flowInstance.getFlowStatus());
             List<Task> taskList = taskService.list(new FlowTask().setInstanceId(flowInstance.getId()));
+            if (CollUtil.isEmpty(taskList)) {
+                throw new ServiceException("流程实例缺少任务,请检查流程定义配置");
+            }
             taskService.mergeVariable(flowInstance, variables);
             insService.updateById(flowInstance);
             StartProcessReturnDTO dto = new StartProcessReturnDTO();
@@ -143,9 +146,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
             throw new ServiceException("流程【" + startProcessBo.getFlowCode() + "】未发布,请先在流程设计器中发布流程定义");
         }
         Dict dict = JsonUtils.parseMap(definition.getExt());
-        boolean autoPass = !ObjectUtil.isNull(dict) && dict.getBool(FlowConstant.AUTO_PASS);
-        variables.put(FlowConstant.AUTO_PASS, autoPass);
-        variables.put(FlowConstant.BUSINESS_CODE, this.generateBusinessCode(bizExt));
+        boolean autoPass = !ObjectUtil.isNull(dict) && dict.getBool(AUTO_PASS);
+        variables.put(AUTO_PASS, autoPass);
+        variables.put(BUSINESS_CODE, this.generateBusinessCode(bizExt));
         FlowParams flowParams = FlowParams.build()
             .handler(startProcessBo.getHandler())
             .flowCode(startProcessBo.getFlowCode())
@@ -156,6 +159,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
         this.buildFlowInstanceBizExt(instance, bizExt);
         // 申请人执行流程
         List<Task> taskList = taskService.list(new FlowTask().setInstanceId(instance.getId()));
+        if (CollUtil.isEmpty(taskList)) {
+            throw new ServiceException("流程启动失败,未生成任务");
+        }
         if (taskList.size() > 1) {
             throw new ServiceException("请检查流程第一个环节是否为申请人!");
         }
@@ -207,11 +213,11 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
         List<FlowCopyBo> flowCopyList = completeTaskBo.getFlowCopyList();
         // 设置抄送人
         Map<String, Object> variables = completeTaskBo.getVariables();
-        variables.put(FlowConstant.FLOW_COPY_LIST, flowCopyList);
+        variables.put(FLOW_COPY_LIST, flowCopyList);
         // 消息类型
-        variables.put(FlowConstant.MESSAGE_TYPE, messageType);
+        variables.put(MESSAGE_TYPE, messageType);
         // 消息通知
-        variables.put(FlowConstant.MESSAGE_NOTICE, notice);
+        variables.put(MESSAGE_NOTICE, notice);
 
 
         FlowTask flowTask = flowTaskMapper.selectById(taskId);
@@ -219,9 +225,12 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
             throw new ServiceException("流程任务不存在或任务已审批!");
         }
         Instance ins = insService.getById(flowTask.getInstanceId());
+        if (ObjectUtil.isNull(ins)) {
+            throw new ServiceException("流程实例不存在");
+        }
         // 检查流程状态是否为草稿、已撤销或已退回状态,若是则执行流程提交监听
         if (BusinessStatusEnum.isDraftOrCancelOrBack(ins.getFlowStatus())) {
-            variables.put(FlowConstant.SUBMIT, true);
+            variables.put(SUBMIT, true);
         }
         // 设置弹窗处理人
         Map<String, Object> assigneeMap = setPopAssigneeMap(completeTaskBo.getAssigneeMap(), ins.getVariableMap());
@@ -274,9 +283,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
                 flowParams.
                     message("流程引擎自动审批!").
                     variable(Map.of(
-                        FlowConstant.SUBMIT, false,
-                        FlowConstant.FLOW_COPY_LIST, Collections.emptyList(),
-                        FlowConstant.MESSAGE_NOTICE, StringUtils.EMPTY));
+                        SUBMIT, false,
+                        FLOW_COPY_LIST, Collections.emptyList(),
+                        MESSAGE_NOTICE, StringUtils.EMPTY));
                 skipTask(task.getId(), flowParams, instanceId, true);
             }
         }
@@ -341,7 +350,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
         FlowParams flowParams = FlowParams.build()
             .skipType(SkipType.NONE.getKey())
             .hisStatus(TaskStatusEnum.COPY.getStatus())
-            .message("【抄送给】" + StreamUtils.join(flowCopyList, FlowCopyBo::getUserName));
+            .message("【抄送给】" + StreamUtils.join(flowCopyList, FlowCopyBo::getNickName));
         HisTask hisTask = hisTaskService.setSkipHisTask(task, flowNode, flowParams);
         hisTask.setCreateTime(updateTime);
         hisTask.setUpdateTime(updateTime);
@@ -482,15 +491,18 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
             throw new ServiceException("任务不存在!");
         }
         Instance inst = insService.getById(task.getInstanceId());
+        if (ObjectUtil.isNull(inst)) {
+            throw new ServiceException("流程实例不存在");
+        }
         BusinessStatusEnum.checkBackStatus(inst.getFlowStatus());
         Long definitionId = task.getDefinitionId();
         String applyNodeCode = flwCommonService.applyNodeCode(definitionId);
 
         Map<String, Object> variable = new HashMap<>();
         // 消息类型
-        variable.put(FlowConstant.MESSAGE_TYPE, messageType);
+        variable.put(MESSAGE_TYPE, messageType);
         // 消息通知
-        variable.put(FlowConstant.MESSAGE_NOTICE, notice);
+        variable.put(MESSAGE_NOTICE, notice);
 
         FlowParams flowParams = FlowParams.build()
             .nodeCode(bo.getNodeCode())
@@ -513,6 +525,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
     @Override
     public List<Node> getBackTaskNode(Long taskId, String nowNodeCode) {
         FlowTask task = flowTaskMapper.selectById(taskId);
+        if (ObjectUtil.isNull(task)) {
+            throw new ServiceException("任务不存在!");
+        }
         List<Node> nodeCodes = nodeService.getByNodeCodes(Collections.singletonList(nowNodeCode), task.getDefinitionId());
         if (!CollUtil.isNotEmpty(nodeCodes)) {
             return nodeCodes;
@@ -597,7 +612,13 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
         }
         FlowTaskVo flowTaskVo = BeanUtil.toBean(task, FlowTaskVo.class);
         Instance instance = insService.getById(task.getInstanceId());
+        if (ObjectUtil.isNull(instance)) {
+            throw new ServiceException("流程实例不存在");
+        }
         Definition definition = defService.getById(task.getDefinitionId());
+        if (ObjectUtil.isNull(definition)) {
+            throw new ServiceException("流程定义不存在");
+        }
         flowTaskVo.setFlowStatus(instance.getFlowStatus());
         flowTaskVo.setVersion(definition.getVersion());
         flowTaskVo.setFlowCode(definition.getFlowCode());
@@ -640,11 +661,23 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
         Long taskId = bo.getTaskId();
         Map<String, Object> variables = bo.getVariables();
         Task task = taskService.getById(taskId);
+        if (ObjectUtil.isNull(task)) {
+            throw new ServiceException("任务不存在!");
+        }
         Instance instance = insService.getById(task.getInstanceId());
+        if (ObjectUtil.isNull(instance)) {
+            throw new ServiceException("流程实例不存在");
+        }
         Definition definition = defService.getById(task.getDefinitionId());
+        if (ObjectUtil.isNull(definition)) {
+            throw new ServiceException("流程定义不存在");
+        }
         Map<String, Object> mergeVariable = MapUtil.mergeAll(instance.getVariableMap(), variables);
         // 获取下一节点列表
         List<Node> nextNodeList = nodeService.getNextNodeList(task.getDefinitionId(), task.getNodeCode(), null, SkipType.PASS.getKey(), mergeVariable);
+        if (CollUtil.isEmpty(nextNodeList)) {
+            return new ArrayList<>();
+        }
         List<FlowNode> nextFlowNodes = BeanUtil.copyToList(nextNodeList, FlowNode.class);
         // 只获取中间节点
         nextFlowNodes = StreamUtils.filter(nextFlowNodes, node -> NodeType.BETWEEN.getKey().equals(node.getNodeType()));
@@ -719,13 +752,19 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
     @Override
     @Transactional(rollbackFor = Exception.class)
     public boolean taskOperation(TaskOperationBo bo, String taskOperation) {
+        TaskOperationEnum op = TaskOperationEnum.getByCode(taskOperation);
+        if (op == null) {
+            log.error("Invalid operation type:{} ", taskOperation);
+            throw new ServiceException("Invalid operation type " + taskOperation);
+        }
+
         FlowParams flowParams = FlowParams.build().message(bo.getMessage());
         if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) {
             flowParams.ignore(true);
         }
 
         // 根据操作类型构建 FlowParams
-        switch (taskOperation) {
+        switch (op) {
             case DELEGATE_TASK, TRANSFER_TASK -> {
                 ValidatorUtils.validate(bo, AddGroup.class);
                 flowParams.addHandlers(Collections.singletonList(bo.getUserId()));
@@ -738,47 +777,63 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
                 ValidatorUtils.validate(bo, EditGroup.class);
                 flowParams.reductionHandlers(bo.getUserIds());
             }
-            default -> {
-                log.error("Invalid operation type:{} ", taskOperation);
-                throw new ServiceException("Invalid operation type " + taskOperation);
-            }
         }
 
         Long taskId = bo.getTaskId();
         Task task = taskService.getById(taskId);
+        if (ObjectUtil.isNull(task)) {
+            throw new ServiceException("任务不存在!");
+        }
         FlowNode flowNode = getByNodeCode(task.getNodeCode(), task.getDefinitionId());
-        if (ADD_SIGNATURE.equals(taskOperation) || REDUCTION_SIGNATURE.equals(taskOperation)) {
+        if (ObjectUtil.isNull(flowNode)) {
+            throw new ServiceException("流程节点不存在");
+        }
+        if (op == TaskOperationEnum.ADD_SIGNATURE || op == TaskOperationEnum.REDUCTION_SIGNATURE) {
             if (CooperateType.isOrSign(flowNode.getNodeRatio())) {
                 throw new ServiceException(task.getNodeName() + "不是会签或票签节点!");
             }
         }
+
         // 设置任务状态并执行对应的任务操作
-        switch (taskOperation) {
-            //委派任务
+        boolean result = false;
+        switch (op) {
             case DELEGATE_TASK -> {
                 flowParams.hisStatus(TaskStatusEnum.DEPUTE.getStatus());
-                return taskService.depute(taskId, flowParams);
+                result = taskService.depute(taskId, flowParams);
             }
-            //转办任务
             case TRANSFER_TASK -> {
                 flowParams.hisStatus(TaskStatusEnum.TRANSFER.getStatus());
-                return taskService.transfer(taskId, flowParams);
+                result = taskService.transfer(taskId, flowParams);
             }
-            //加签,增加办理人
             case ADD_SIGNATURE -> {
                 flowParams.hisStatus(TaskStatusEnum.SIGN.getStatus());
-                return taskService.addSignature(taskId, flowParams);
+                result = taskService.addSignature(taskId, flowParams);
             }
-            //减签,减少办理人
             case REDUCTION_SIGNATURE -> {
                 flowParams.hisStatus(TaskStatusEnum.SIGN_OFF.getStatus());
-                return taskService.reductionSignature(taskId, flowParams);
+                result = taskService.reductionSignature(taskId, flowParams);
+            }
+        }
+
+        // 操作执行成功后再发送消息
+        if (result && CollUtil.isNotEmpty(bo.getMessageType())) {
+            List<Long> userIdList = new ArrayList<>();
+            if (StrUtil.isNotBlank(bo.getUserId())) {
+                userIdList.add(Convert.toLong(bo.getUserId()));
+            }
+            if (CollUtil.isNotEmpty(bo.getUserIds())) {
+                userIdList.addAll(StreamUtils.toList(bo.getUserIds(), Convert::toLong));
             }
-            default -> {
-                log.error("Invalid operation type:{} ", taskOperation);
-                throw new ServiceException("Invalid operation type " + taskOperation);
+            if (CollUtil.isNotEmpty(userIdList)) {
+                flwCommonService.sendMessage(
+                    bo.getMessageType(),
+                    StringUtils.isNotBlank(bo.getMessage()) ? bo.getMessage() : "单据「" + op.getDesc() + "」通知",
+                    "单据「" + op.getDesc() + "」提醒",
+                    userService.selectListByIds(userIdList)
+                );
             }
         }
+        return result;
     }
 
     /**

+ 6 - 6
script/docker/docker-compose.yml

@@ -65,8 +65,8 @@ services:
     network_mode: "host"
 
   minio:
-    # minio 最后一个未阉割版本 不能再进行升级 在往上的版本功能被阉割
-    image: minio/minio:RELEASE.2025-04-22T22-12-26Z
+    # pgsty 开源社区 fork 重新维护的最新版 minio
+    image: pgsty/minio:RELEASE.2026-02-14T12-00-00Z
     container_name: minio
     ports:
       # api 端口
@@ -99,7 +99,7 @@ services:
     network_mode: "host"
 
   ruoyi-server1:
-    image: ruoyi/ruoyi-server:5.5.3
+    image: ruoyi/ruoyi-server:5.6.0
     container_name: ruoyi-server1
     environment:
       # 时区上海
@@ -115,7 +115,7 @@ services:
     network_mode: "host"
 
   ruoyi-server2:
-    image: ruoyi/ruoyi-server:5.5.3
+    image: ruoyi/ruoyi-server:5.6.0
     container_name: ruoyi-server2
     environment:
       # 时区上海
@@ -131,7 +131,7 @@ services:
     network_mode: "host"
 
   ruoyi-monitor-admin:
-    image: ruoyi/ruoyi-monitor-admin:5.5.3
+    image: ruoyi/ruoyi-monitor-admin:5.6.0
     container_name: ruoyi-monitor-admin
     environment:
       # 时区上海
@@ -143,7 +143,7 @@ services:
     network_mode: "host"
 
   ruoyi-snailjob-server:
-    image: ruoyi/ruoyi-snailjob-server:5.5.3
+    image: ruoyi/ruoyi-snailjob-server:5.6.0
     container_name: ruoyi-snailjob-server
     environment:
       # 时区上海

+ 24 - 4
script/sql/oracle/oracle_ry_workflow.sql

@@ -473,11 +473,11 @@ INSERT INTO sys_menu VALUES ('11618', '我的任务', '0', '7', 'task', '', '',
 INSERT INTO sys_menu VALUES ('11619', '我的待办', '11618', '2', 'taskWaiting', 'workflow/task/taskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, SYSDATE, NULL, NULL, '');
 INSERT INTO sys_menu VALUES ('11632', '我的已办', '11618', '3', 'taskFinish', 'workflow/task/taskFinish', '', '1', '1', 'C', '0', '0', '', 'finish', 103, 1, SYSDATE, NULL, NULL, '');
 INSERT INTO sys_menu VALUES ('11633', '我的抄送', '11618', '4', 'taskCopyList', 'workflow/task/taskCopyList', '', '1', '1', 'C', '0', '0', '', 'my-copy', 103, 1, SYSDATE, NULL, NULL, '');
-INSERT INTO sys_menu VALUES ('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '', 'process-definition', 103, 1, SYSDATE, NULL, NULL, '');
-INSERT INTO sys_menu VALUES ('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', '', 'tree-table', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', 'workflow:definition:list', 'process-definition', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', 'workflow:instance:list', 'tree-table', 103, 1, SYSDATE, NULL, NULL, '');
 INSERT INTO sys_menu VALUES ('11622', '流程分类', '11616', '1', 'category', 'workflow/category/index', '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'category', 103, 1, SYSDATE, NULL, NULL, '');
-INSERT INTO sys_menu VALUES ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, SYSDATE, NULL, NULL, '');
-INSERT INTO sys_menu VALUES ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', 'workflow:instance:currentList', 'guide', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11630', '流程监控', '11616', '4', 'processMonitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, SYSDATE, NULL, NULL, '');
 INSERT INTO sys_menu VALUES ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, SYSDATE, NULL, NULL, '');
 INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index',   'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, '/workflow/processDefinition');
 INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, '');
@@ -488,6 +488,26 @@ INSERT INTO sys_menu VALUES ('11625', '流程分类修改', '11622', '3', '#', '
 INSERT INTO sys_menu VALUES ('11626', '流程分类删除', '11622', '4', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:remove', '#', 103, 1, SYSDATE, NULL, NULL, '');
 INSERT INTO sys_menu VALUES ('11627', '流程分类导出', '11622', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:export', '#', 103, 1, SYSDATE, NULL, NULL, '');
 
+-- 流程实例管理相关按钮
+INSERT INTO sys_menu VALUES ('11653', '流程实例查询', '11621', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:query', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11654', '流程变量查询', '11621', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:variableQuery', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11655', '流程变量修改', '11621', '3', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:variable', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11656', '流程实例激活/挂起', '11621', '4', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:active', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11657', '流程实例删除', '11621', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:remove', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11658', '流程实例作废', '11621', '6', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:invalid', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11659', '流程实例撤销', '11621', '7', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:cancel', '#', 103, 1, SYSDATE, NULL, NULL, '');
+
+-- 流程定义管理相关按钮
+INSERT INTO sys_menu VALUES ('11644', '流程定义查询', '11620', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:query', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11645', '流程定义新增', '11620', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:add', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11646', '流程定义修改', '11620', '3', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:edit', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11647', '流程定义删除', '11620', '4', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:remove', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11648', '流程定义导出', '11620', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:export', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11649', '流程定义导入', '11620', '6', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:import', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11650', '流程定义发布/取消发布', '11620', '7', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:publish', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11651', '流程定义复制', '11620', '8', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:copy', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11652', '流程定义激活/挂起', '11620', '9', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:active', '#', 103, 1, SYSDATE, NULL, NULL, '');
+
 INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', 2, 'spel', 'workflow/spel/index', '', 1, 0, 'C', '0', '0', 'workflow:spel:list', 'input', 103, 1, SYSDATE, 1, SYSDATE, '流程达式定义菜单');
 INSERT INTO sys_menu VALUES ('11802', '流程spel表达式定义查询', '11801', 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:query', '#', 103, 1, SYSDATE, NULL, NULL, '');
 INSERT INTO sys_menu VALUES ('11803', '流程spel表达式定义新增', '11801', 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:add', '#', 103, 1, SYSDATE, NULL, NULL, '');

+ 24 - 4
script/sql/postgres/postgres_ry_workflow.sql

@@ -451,11 +451,11 @@ INSERT INTO sys_menu VALUES ('11618', '我的任务', '0', '7', 'task', '', '',
 INSERT INTO sys_menu VALUES ('11619', '我的待办', '11618', '2', 'taskWaiting', 'workflow/task/taskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, now(), NULL, NULL, '');
 INSERT INTO sys_menu VALUES ('11632', '我的已办', '11618', '3', 'taskFinish', 'workflow/task/taskFinish', '', '1', '1', 'C', '0', '0', '', 'finish', 103, 1, now(), NULL, NULL, '');
 INSERT INTO sys_menu VALUES ('11633', '我的抄送', '11618', '4', 'taskCopyList', 'workflow/task/taskCopyList', '', '1', '1', 'C', '0', '0', '', 'my-copy', 103, 1, now(), NULL, NULL, '');
-INSERT INTO sys_menu VALUES ('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '', 'process-definition', 103, 1, now(), NULL, NULL, '');
-INSERT INTO sys_menu VALUES ('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', '', 'tree-table', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', 'workflow:definition:list', 'process-definition', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', 'workflow:instance:list', 'tree-table', 103, 1, now(), NULL, NULL, '');
 INSERT INTO sys_menu VALUES ('11622', '流程分类', '11616', '1', 'category', 'workflow/category/index', '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'category', 103, 1, now(), NULL, NULL, '');
-INSERT INTO sys_menu VALUES ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, now(), NULL, NULL, '');
-INSERT INTO sys_menu VALUES ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', 'workflow:instance:currentList', 'guide', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11630', '流程监控', '11616', '4', 'processMonitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, now(), NULL, NULL, '');
 INSERT INTO sys_menu VALUES ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, now(), NULL, NULL, '');
 INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index',   'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, '/workflow/processDefinition');
 INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, '');
@@ -466,6 +466,26 @@ INSERT INTO sys_menu VALUES ('11625', '流程分类修改', '11622', '3', '#', '
 INSERT INTO sys_menu VALUES ('11626', '流程分类删除', '11622', '4', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:remove', '#', 103, 1, now(), NULL, NULL, '');
 INSERT INTO sys_menu VALUES ('11627', '流程分类导出', '11622', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:export', '#', 103, 1, now(), NULL, NULL, '');
 
+-- 流程实例管理相关按钮
+INSERT INTO sys_menu VALUES ('11653', '流程实例查询', '11621', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:query', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11654', '流程变量查询', '11621', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:variableQuery', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11655', '流程变量修改', '11621', '3', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:variable', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11656', '流程实例激活/挂起', '11621', '4', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:active', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11657', '流程实例删除', '11621', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:remove', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11658', '流程实例作废', '11621', '6', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:invalid', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11659', '流程实例撤销', '11621', '7', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:instance:cancel', '#', 103, 1, now(), NULL, NULL, '');
+
+-- 流程定义管理相关按钮
+INSERT INTO sys_menu VALUES ('11644', '流程定义查询', '11620', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:query', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11645', '流程定义新增', '11620', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:add', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11646', '流程定义修改', '11620', '3', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:edit', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11647', '流程定义删除', '11620', '4', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:remove', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11648', '流程定义导出', '11620', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:export', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11649', '流程定义导入', '11620', '6', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:import', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11650', '流程定义发布/取消发布', '11620', '7', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:publish', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11651', '流程定义复制', '11620', '8', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:copy', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11652', '流程定义激活/挂起', '11620', '9', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:definition:active', '#', 103, 1, now(), NULL, NULL, '');
+
 INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', 2, 'spel', 'workflow/spel/index', '', 1, 0, 'C', '0', '0', 'workflow:spel:list', 'input', 103, 1, now(), 1, now(), '流程达式定义菜单');
 INSERT INTO sys_menu VALUES ('11802', '流程spel表达式定义查询', '11801', 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:query', '#', 103, 1, now(), NULL, NULL, '');
 INSERT INTO sys_menu VALUES ('11803', '流程spel表达式定义新增', '11801', 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:add', '#', 103, 1, now(), NULL, NULL, '');

+ 24 - 4
script/sql/ry_workflow.sql

@@ -263,12 +263,12 @@ insert into sys_menu values ('11618', '我的任务', '0', '7', 'task', '', '',
 insert into sys_menu values ('11619', '我的待办', '11618', '2', 'taskWaiting', 'workflow/task/taskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, sysdate(), NULL, NULL, '');
 insert into sys_menu values ('11632', '我的已办', '11618', '3', 'taskFinish', 'workflow/task/taskFinish', '', '1', '1', 'C', '0', '0', '', 'finish', 103, 1, sysdate(), NULL, NULL, '');
 insert into sys_menu values ('11633', '我的抄送', '11618', '4', 'taskCopyList', 'workflow/task/taskCopyList', '', '1', '1', 'C', '0', '0', '', 'my-copy', 103, 1, sysdate(), NULL, NULL, '');
-insert into sys_menu values ('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '', 'process-definition', 103, 1, sysdate(), NULL, NULL, '');
-insert into sys_menu values ('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', '', 'tree-table', 103, 1, sysdate(), NULL, NULL, '');
+insert into sys_menu values ('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', 'workflow:definition:list', 'process-definition', 103, 1, sysdate(), NULL, NULL, '');
+insert into sys_menu values ('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', 'workflow:instance:list', 'tree-table', 103, 1, sysdate(), NULL, NULL, '');
 insert into sys_menu values ('11622', '流程分类', '11616', '1', 'category', 'workflow/category/index', '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'category', 103, 1, sysdate(), NULL, NULL, '');
 INSERT INTO sys_menu VALUES ('11801', '流程表达式', '11616', '2', 'spel',    'workflow/spel/index', '', 1, 0, 'C', '0', '0', 'workflow:spel:list', 'input', 103, 1, sysdate(), 1, sysdate(), '流程达式定义菜单');
-insert into sys_menu values ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, sysdate(), NULL, NULL, '');
-insert into sys_menu values ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, sysdate(), NULL, NULL, '');
+insert into sys_menu values ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', 'workflow:instance:currentList', 'guide', 103, 1, sysdate(), NULL, NULL, '');
+insert into sys_menu values ('11630', '流程监控', '11616', '4', 'processMonitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, sysdate(), NULL, NULL, '');
 insert into sys_menu values ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, sysdate(), NULL, NULL, '');
 insert into sys_menu values ('11700', '流程设计', '11616', '5', 'design/index',   'workflow/processDefinition/design', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, '/workflow/processDefinition');
 insert into sys_menu values ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, '');
@@ -278,6 +278,26 @@ insert into sys_menu values ('11624', '流程分类新增', '11622', '2', '#', '
 insert into sys_menu values ('11625', '流程分类修改', '11622', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:edit', '#', 103, 1,sysdate(), null, null, '');
 insert into sys_menu values ('11626', '流程分类删除', '11622', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:remove', '#', 103,1, sysdate(), null, null, '');
 insert into sys_menu values ('11627', '流程分类导出', '11622', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:export', '#', 103,1, sysdate(), null, null, '');
+
+-- 流程实例管理相关按钮
+insert into sys_menu values ('11653', '流程实例查询', '11621', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:query', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11654', '流程变量查询', '11621', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:variableQuery', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11655', '流程变量修改', '11621', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:variable', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11656', '流程实例激活/挂起', '11621', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:active', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11657', '流程实例删除', '11621', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:remove', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11658', '流程实例作废', '11621', '6', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:invalid', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11659', '流程实例撤销', '11621', '7', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:instance:cancel', '#', 103, 1, sysdate(), null, null, '');
+
+-- 流程定义管理相关按钮
+insert into sys_menu values ('11644', '流程定义查询', '11620', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:query', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11645', '流程定义新增', '11620', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:add', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11646', '流程定义修改', '11620', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:edit', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11647', '流程定义删除', '11620', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:remove', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11648', '流程定义导出', '11620', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:export', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11649', '流程定义导入', '11620', '6', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:import', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11650', '流程定义发布/取消发布', '11620', '7', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:publish', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11651', '流程定义复制', '11620', '8', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:copy', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11652', '流程定义激活/挂起', '11620', '9', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:definition:active', '#', 103, 1, sysdate(), null, null, '');
 -- 流程表达式管理相关按钮
 INSERT INTO sys_menu VALUES ('11802', '流程达式定义查询', '11801', 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:query', '#', 103, 1, sysdate(), NULL, NULL, '');
 INSERT INTO sys_menu VALUES ('11803', '流程达式定义新增', '11801', 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'workflow:spel:add', '#', 103, 1, sysdate(), NULL, NULL, '');

+ 19 - 3
script/sql/sqlserver/sqlserver_ry_workflow.sql

@@ -1573,13 +1573,13 @@ INSERT sys_menu VALUES (11633, N'我的抄送', 11618, 4, N'taskCopyList', N'wor
 GO
 INSERT sys_menu VALUES (11620, N'流程定义', 11616, 3, N'processDefinition', N'workflow/processDefinition/index', N'', 1, 1, N'C', N'0', N'0', N'', N'process-definition', 103, 1, GETDATE(), NULL, NULL, N'');
 GO
-INSERT sys_menu VALUES (11621, N'流程实例', 11630, 1, N'processInstance', N'workflow/processInstance/index', N'', 1, 1, N'C', N'0', N'0', N'', N'tree-table', 103, 1, GETDATE(), NULL, NULL, N'');
+INSERT sys_menu VALUES (11621, N'流程实例', 11630, 1, N'processInstance', N'workflow/processInstance/index', N'', 1, 1, N'C', N'0', N'0', N'workflow:instance:list', N'tree-table', 103, 1, GETDATE(), NULL, NULL, N'');
 GO
 INSERT sys_menu VALUES (11622, N'流程分类', 11616, 1, N'category', N'workflow/category/index', N'', 1, 0, N'C', N'0', N'0', N'workflow:category:list', N'category', 103, 1, GETDATE(), NULL, NULL, N'');
 GO
-INSERT sys_menu VALUES (11629, N'我发起的', 11618, 1, N'myDocument', N'workflow/task/myDocument', N'', 1, 1, N'C', N'0', N'0', N'', N'guide', 103, 1, GETDATE(), NULL, NULL, N'');
+INSERT sys_menu VALUES (11629, N'我发起的', 11618, 1, N'myDocument', N'workflow/task/myDocument', N'', 1, 1, N'C', N'0', N'0', N'workflow:instance:currentList', N'guide', 103, 1, GETDATE(), NULL, NULL, N'');
 GO
-INSERT sys_menu VALUES (11630, N'流程监控', 11616, 4, N'monitor', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'monitor', 103, 1, GETDATE(), NULL, NULL, N'');
+INSERT sys_menu VALUES (11630, N'流程监控', 11616, 4, N'processMonitor', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'monitor', 103, 1, GETDATE(), NULL, NULL, N'');
 GO
 INSERT sys_menu VALUES (11631, N'待办任务', 11630, 2, N'allTaskWaiting', N'workflow/task/allTaskWaiting', N'', 1, 1, N'C', N'0', N'0', N'', N'waiting', 103, 1, GETDATE(), NULL, NULL, N'');
 GO
@@ -1600,6 +1600,22 @@ GO
 INSERT sys_menu VALUES (11627, N'流程分类导出', 11622, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:category:export', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
 GO
 
+-- 流程实例管理相关按钮
+INSERT sys_menu VALUES (11653, N'流程实例查询', 11621, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:query', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11654, N'流程变量查询', 11621, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:variableQuery', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11655, N'流程变量修改', 11621, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:variable', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11656, N'流程实例激活/挂起', 11621, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:active', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11657, N'流程实例删除', 11621, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:remove', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11658, N'流程实例作废', 11621, 6, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:invalid', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11659, N'流程实例撤销', 11621, 7, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:instance:cancel', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+
 INSERT sys_menu VALUES (11801, N'流程表达式', N'11616', 2, N'spel', N'workflow/spel/index', N'', 1, 0, N'C', N'0', N'0', N'workflow:spel:list', N'input', 103, 1, GETDATE(), 1, GETDATE(), N'流程达式定义菜单');
 GO
 INSERT sys_menu VALUES (11802, N'流程spel表达式定义查询', N'11801', 1, N'#', N'', NULL, 1, 0, N'F', N'0', N'0', N'workflow:spel:query', N'#', 103, 1, GETDATE(), NULL, NULL, N'');

+ 24 - 0
script/sql/update/oracle/update_5.5.3_5.6.0.sql

@@ -0,0 +1,24 @@
+-- 修正已有菜单权限标识
+update sys_menu set perms = 'workflow:definition:list' where menu_id = '11620';
+update sys_menu set perms = 'workflow:instance:list' where menu_id = '11621';
+update sys_menu set perms = 'workflow:instance:currentList' where menu_id = '11629';
+
+-- 流程定义管理相关按钮
+INSERT INTO sys_menu VALUES ('11644', '流程定义查询', '11620', '1', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:query', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11645', '流程定义新增', '11620', '2', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:add', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11646', '流程定义修改', '11620', '3', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:edit', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11647', '流程定义删除', '11620', '4', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:remove', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11648', '流程定义导出', '11620', '5', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:export', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11649', '流程定义导入', '11620', '6', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:import', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11650', '流程定义发布/取消发布', '11620', '7', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:publish', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11651', '流程定义复制', '11620', '8', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:copy', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11652', '流程定义激活/挂起', '11620', '9', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:active', '#', 103, 1, SYSDATE, NULL, NULL, '');
+
+-- 流程实例管理相关按钮
+INSERT INTO sys_menu VALUES ('11653', '流程实例查询', '11621', '1', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:query', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11654', '流程变量查询', '11621', '2', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:variableQuery', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11655', '流程变量修改', '11621', '3', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:variable', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11656', '流程实例激活/挂起', '11621', '4', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:active', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11657', '流程实例删除', '11621', '5', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:remove', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11658', '流程实例作废', '11621', '6', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:invalid', '#', 103, 1, SYSDATE, NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11659', '流程实例撤销', '11621', '7', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:cancel', '#', 103, 1, SYSDATE, NULL, NULL, '');

+ 24 - 0
script/sql/update/postgres/update_5.5.3_5.6.0.sql

@@ -0,0 +1,24 @@
+-- 修正已有菜单权限标识
+update sys_menu set perms = 'workflow:definition:list' where menu_id = '11620';
+update sys_menu set perms = 'workflow:instance:list' where menu_id = '11621';
+update sys_menu set perms = 'workflow:instance:currentList' where menu_id = '11629';
+
+-- 流程定义管理相关按钮
+INSERT INTO sys_menu VALUES ('11644', '流程定义查询', '11620', '1', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:query', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11645', '流程定义新增', '11620', '2', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:add', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11646', '流程定义修改', '11620', '3', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:edit', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11647', '流程定义删除', '11620', '4', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:remove', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11648', '流程定义导出', '11620', '5', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:export', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11649', '流程定义导入', '11620', '6', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:import', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11650', '流程定义发布/取消发布', '11620', '7', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:publish', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11651', '流程定义复制', '11620', '8', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:copy', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11652', '流程定义激活/挂起', '11620', '9', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:active', '#', 103, 1, now(), NULL, NULL, '');
+
+-- 流程实例管理相关按钮
+INSERT INTO sys_menu VALUES ('11653', '流程实例查询', '11621', '1', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:query', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11654', '流程变量查询', '11621', '2', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:variableQuery', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11655', '流程变量修改', '11621', '3', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:variable', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11656', '流程实例激活/挂起', '11621', '4', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:active', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11657', '流程实例删除', '11621', '5', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:remove', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11658', '流程实例作废', '11621', '6', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:invalid', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES ('11659', '流程实例撤销', '11621', '7', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:cancel', '#', 103, 1, now(), NULL, NULL, '');

+ 43 - 0
script/sql/update/sqlserver/update_5.5.3_5.6.0.sql

@@ -0,0 +1,43 @@
+-- 修正已有菜单权限标识
+update sys_menu set perms = N'workflow:definition:list' where menu_id = 11620;
+GO
+update sys_menu set perms = N'workflow:instance:list' where menu_id = 11621;
+GO
+update sys_menu set perms = N'workflow:instance:currentList' where menu_id = 11629;
+GO
+
+-- 流程定义管理相关按钮
+INSERT sys_menu VALUES (11644, N'流程定义查询', N'11620', 1, N'#', N'', N'', N'N', N'Y', N'F', N'0', N'0', N'workflow:definition:query', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11645, N'流程定义新增', N'11620', 2, N'#', N'', N'', N'N', N'Y', N'F', N'0', N'0', N'workflow:definition:add', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11646, N'流程定义修改', N'11620', 3, N'#', N'', N'', N'N', N'Y', N'F', N'0', N'0', N'workflow:definition:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11647, N'流程定义删除', N'11620', 4, N'#', N'', N'', N'N', N'Y', N'F', N'0', N'0', N'workflow:definition:remove', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11648, N'流程定义导出', N'11620', 5, N'#', N'', N'', N'N', N'Y', N'F', N'0', N'0', N'workflow:definition:export', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11649, N'流程定义导入', N'11620', 6, N'#', N'', N'', N'N', N'Y', N'F', N'0', N'0', N'workflow:definition:import', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11650, N'流程定义发布/取消发布', N'11620', 7, N'#', N'', N'', N'N', N'Y', N'F', N'0', N'0', N'workflow:definition:publish', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11651, N'流程定义复制', N'11620', 8, N'#', N'', N'', N'N', N'Y', N'F', N'0', N'0', N'workflow:definition:copy', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11652, N'流程定义激活/挂起', N'11620', 9, N'#', N'', N'', N'N', N'Y', N'F', N'0', N'0', N'workflow:definition:active', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+
+-- 流程实例管理相关按钮
+INSERT sys_menu VALUES (11653, N'流程实例查询', N'11621', 1, N'#', N'', N'', N'N', N'Y', N'F', N'0', N'0', N'workflow:instance:query', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11654, N'流程变量查询', N'11621', 2, N'#', N'', N'', N'N', N'Y', N'F', N'0', N'0', N'workflow:instance:variableQuery', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11655, N'流程变量修改', N'11621', 3, N'#', N'', N'', N'N', N'Y', N'F', N'0', N'0', N'workflow:instance:variable', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11656, N'流程实例激活/挂起', N'11621', 4, N'#', N'', N'', N'N', N'Y', N'F', N'0', N'0', N'workflow:instance:active', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11657, N'流程实例删除', N'11621', 5, N'#', N'', N'', N'N', N'Y', N'F', N'0', N'0', N'workflow:instance:remove', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11658, N'流程实例作废', N'11621', 6, N'#', N'', N'', N'N', N'Y', N'F', N'0', N'0', N'workflow:instance:invalid', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (11659, N'流程实例撤销', N'11621', 7, N'#', N'', N'', N'N', N'Y', N'F', N'0', N'0', N'workflow:instance:cancel', N'#', 103, 1, GETDATE(), NULL, NULL, N'');
+GO

+ 24 - 0
script/sql/update/update_5.5.3_5.6.0.sql

@@ -0,0 +1,24 @@
+-- 修正已有菜单权限标识
+update sys_menu set perms = 'workflow:definition:list' where menu_id = '11620';
+update sys_menu set perms = 'workflow:instance:list' where menu_id = '11621';
+update sys_menu set perms = 'workflow:instance:currentList' where menu_id = '11629';
+
+-- 流程定义管理相关按钮
+INSERT INTO sys_menu VALUES ('11644', '流程定义查询', '11620', '1', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:query', '#', 103, 1, sysdate(), null, null, '');
+INSERT INTO sys_menu VALUES ('11645', '流程定义新增', '11620', '2', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:add', '#', 103, 1, sysdate(), null, null, '');
+INSERT INTO sys_menu VALUES ('11646', '流程定义修改', '11620', '3', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:edit', '#', 103, 1, sysdate(), null, null, '');
+INSERT INTO sys_menu VALUES ('11647', '流程定义删除', '11620', '4', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:remove', '#', 103, 1, sysdate(), null, null, '');
+INSERT INTO sys_menu VALUES ('11648', '流程定义导出', '11620', '5', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:export', '#', 103, 1, sysdate(), null, null, '');
+INSERT INTO sys_menu VALUES ('11649', '流程定义导入', '11620', '6', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:import', '#', 103, 1, sysdate(), null, null, '');
+INSERT INTO sys_menu VALUES ('11650', '流程定义发布/取消发布', '11620', '7', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:publish', '#', 103, 1, sysdate(), null, null, '');
+INSERT INTO sys_menu VALUES ('11651', '流程定义复制', '11620', '8', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:copy', '#', 103, 1, sysdate(), null, null, '');
+INSERT INTO sys_menu VALUES ('11652', '流程定义激活/挂起', '11620', '9', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:definition:active', '#', 103, 1, sysdate(), null, null, '');
+
+-- 流程实例管理相关按钮
+INSERT INTO sys_menu VALUES ('11653', '流程实例查询', '11621', '1', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:query', '#', 103, 1, sysdate(), null, null, '');
+INSERT INTO sys_menu VALUES ('11654', '流程变量查询', '11621', '2', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:variableQuery', '#', 103, 1, sysdate(), null, null, '');
+INSERT INTO sys_menu VALUES ('11655', '流程变量修改', '11621', '3', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:variable', '#', 103, 1, sysdate(), null, null, '');
+INSERT INTO sys_menu VALUES ('11656', '流程实例激活/挂起', '11621', '4', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:active', '#', 103, 1, sysdate(), null, null, '');
+INSERT INTO sys_menu VALUES ('11657', '流程实例删除', '11621', '5', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:remove', '#', 103, 1, sysdate(), null, null, '');
+INSERT INTO sys_menu VALUES ('11658', '流程实例作废', '11621', '6', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:invalid', '#', 103, 1, sysdate(), null, null, '');
+INSERT INTO sys_menu VALUES ('11659', '流程实例撤销', '11621', '7', '#', '', '', 'N', 'Y', 'F', '0', '0', 'workflow:instance:cancel', '#', 103, 1, sysdate(), null, null, '');