Kaynağa Gözat

!88 工作流新增 Flowable 实现
Merge pull request !88 from 芋道源码/feature/flowable

芋道源码 3 yıl önce
ebeveyn
işleme
69b93ca75a
100 değiştirilmiş dosya ile 4066 ekleme ve 189 silme
  1. 4 4
      README.md
  2. 0 0
      sql/bpm-activiti.sql
  3. 65 0
      sql/bpm-flowable.sql
  4. 5 0
      yudao-dependencies/pom.xml
  5. 2 1
      yudao-framework/pom.xml
  6. 2 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/WebFilterOrderEnum.java
  7. 37 0
      yudao-framework/yudao-spring-boot-starter-flowable/pom.xml
  8. 22 0
      yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/config/YudaoFlowableConfiguration.java
  9. 1 0
      yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/package-info.java
  10. 62 0
      yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/util/FlowableUtils.java
  11. 35 0
      yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/web/FlowableWebFilter.java
  12. 1 0
      yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/package-info.java
  13. 2 0
      yudao-framework/yudao-spring-boot-starter-flowable/src/main/resources/META-INF/spring.factories
  14. 1 1
      yudao-framework/yudao-spring-boot-starter-web/pom.xml
  15. 4 0
      yudao-module-bpm/yudao-module-bpm-base/pom.xml
  16. 8 0
      yudao-module-bpm/yudao-module-bpm-base/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmFormService.java
  17. 30 4
      yudao-module-bpm/yudao-module-bpm-base/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmFormServiceImpl.java
  18. 1 1
      yudao-module-bpm/yudao-module-bpm-base/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java
  19. 0 0
      yudao-module-bpm/yudao-module-bpm-base/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java
  20. 1 1
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java
  21. 0 1
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java
  22. 2 2
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmTaskAssignRuleController.java
  23. 1 1
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
  24. 2 2
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmModelConvert.java
  25. 1 1
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java
  26. 1 1
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmActivityBehaviorFactory.java
  27. 2 2
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmUserTaskActivityBehavior.java
  28. 2 0
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java
  29. 2 0
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java
  30. 9 10
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
  31. 30 51
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
  32. 37 37
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java
  33. 1 1
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
  34. 8 0
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleService.java
  35. 15 0
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java
  36. 2 2
      yudao-module-bpm/yudao-module-bpm-impl-activiti/src/test/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmUserTaskActivityBehaviorTest.java
  37. 13 4
      yudao-module-bpm/yudao-module-bpm-impl-flowable/pom.xml
  38. 4 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/api/package-info.java
  39. 28 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java
  40. 97 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java
  41. 59 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java
  42. 58 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmTaskAssignRuleController.java
  43. 39 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmActivityController.java
  44. 58 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
  45. 79 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
  46. 4 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/app/package-info.java
  47. 6 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/package-info.java
  48. 140 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmModelConvert.java
  49. 82 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java
  50. 40 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmTaskAssignRuleConvert.java
  51. 6 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/package-info.java
  52. 29 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmActivityConvert.java
  53. 116 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
  54. 147 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
  55. 1 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md
  56. 62 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java
  57. 55 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java
  58. 195 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java
  59. 34 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/BpmTaskAssignScript.java
  60. 71 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.java
  61. 29 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java
  62. 30 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java
  63. 41 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignStartUserScript.java
  64. 53 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java
  65. 52 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java
  66. 6 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/package-info.java
  67. 12 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/package-info.java
  68. 77 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
  69. 286 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
  70. 159 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java
  71. 284 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
  72. 87 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleService.java
  73. 208 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java
  74. 22 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmActivityService.java
  75. 49 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmActivityServiceImpl.java
  76. 149 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
  77. 300 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
  78. 117 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
  79. 270 0
      yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
  80. 4 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/UserController.java
  81. 0 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/common/SexEnum.java
  82. 0 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/dept/DeptIdEnum.java
  83. 0 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/errorcode/ErrorCodeTypeEnum.java
  84. 0 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/notice/NoticeTypeEnum.java
  85. 1 1
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/DataScopeEnum.java
  86. 0 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/MenuIdEnum.java
  87. 0 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/MenuTypeEnum.java
  88. 0 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleCodeEnum.java
  89. 0 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleTypeEnum.java
  90. 0 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsReceiveStatusEnum.java
  91. 0 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSendStatusEnum.java
  92. 0 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsTemplateTypeEnum.java
  93. 4 0
      yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/app/dict/AppDictDataController.java
  94. 1 1
      yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleDO.java
  95. 0 29
      yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginLogTypeEnum.java
  96. 0 27
      yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginResultEnum.java
  97. 1 1
      yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java
  98. 1 1
      yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java
  99. 1 1
      yudao-module-system/yudao-module-system-impl/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java
  100. 1 1
      yudao-module-system/yudao-module-system-impl/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceTest.java

+ 4 - 4
README.md

@@ -21,10 +21,10 @@
 * 集成微信小程序、微信公众号、企业微信、钉钉等三方登陆,集成支付宝、微信等支付与退款。
 * 集成阿里云、腾讯云、云片等短信渠道,集成阿里云、腾讯云、七牛云等云存储服务。
 
-|  项目名 | 说明   | 传说门  |
-|  ----  | ----  | ----  |
-| `ruoyi-vue-pro` | Spring Boot 版本  | **[Gitee](https://gitee.com/zhijiantianya/ruoyi-vue-pro)**     [Github](https://github.com/YunaiV/ruoyi-vue-pro) |
-| `ruoyi-vue-cloud` | Spring Cloud 版本 | **[Gitee](https://gitee.com/zhijiantianya/ruoyi-vue-cloud)**     [Github](https://github.com/YunaiV/onemall) |
+|  项目名 | 说明                     | 传说门  |
+|  ----  |------------------------| ----  |
+| `ruoyi-vue-pro` | Spring Boot 多模块        | **[Gitee](https://gitee.com/zhijiantianya/ruoyi-vue-pro)**     [Github](https://github.com/YunaiV/ruoyi-vue-pro) |
+| `ruoyi-vue-cloud` | Spring Cloud 微服务       | **[Gitee](https://gitee.com/zhijiantianya/ruoyi-vue-cloud)**     [Github](https://github.com/YunaiV/onemall) |
 | `Spring-Boot-Labs` | Spring Boot & Cloud 入门 | **[Gitee](https://gitee.com/zhijiantianya/SpringBoot-Labs)**     [Github](https://github.com/YunaiV/SpringBoot-Labs) |
 
 ## 🐶 在线体验

+ 0 - 0
sql/activiti.sql → sql/bpm-activiti.sql


Dosya farkı çok büyük olduğundan ihmal edildi
+ 65 - 0
sql/bpm-flowable.sql


+ 5 - 0
yudao-dependencies/pom.xml

@@ -399,6 +399,11 @@
                 <version>${revision}</version>
             </dependency>
             <!-- 工作流相关 flowable -->
+            <dependency>
+                <groupId>cn.iocoder.boot</groupId>
+                <artifactId>yudao-spring-boot-starter-flowable</artifactId>
+                <version>${revision}</version>
+            </dependency>
             <dependency>
                 <groupId>org.flowable</groupId>
                 <artifactId>flowable-spring-boot-starter-basic</artifactId>

+ 2 - 1
yudao-framework/pom.xml

@@ -35,12 +35,13 @@
         <module>yudao-spring-boot-starter-biz-social</module>
         <module>yudao-spring-boot-starter-biz-tenant</module>
         <module>yudao-spring-boot-starter-biz-data-permission</module>
+        <module>yudao-spring-boot-starter-flowable</module>
     </modules>
 
     <artifactId>yudao-framework</artifactId>
     <description>
         该包是技术组件,每个子包,代表一个组件。每个组件包括两部分:
-            1. core 包:是该组件的核心
+            1. core 包:是该组件的核心
             2. config 包:是该组件基于 Spring 的配置
 
         技术组件,也分成两类:

+ 2 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/WebFilterOrderEnum.java

@@ -29,6 +29,8 @@ public interface WebFilterOrderEnum {
 
     int ACTIVITI_FILTER = -98; // 需要保证在 Spring Security 过滤后面
 
+    int FLOWABLE_FILTER = -98; // 需要保证在 Spring Security 过滤后面
+
     int DEMO_FILTER = Integer.MAX_VALUE;
 
 }

+ 37 - 0
yudao-framework/yudao-spring-boot-starter-flowable/pom.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>yudao-framework</artifactId>
+        <groupId>cn.iocoder.boot</groupId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>yudao-spring-boot-starter-flowable</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-common</artifactId>
+        </dependency>
+
+        <!-- Web 相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-security</artifactId>
+        </dependency>
+
+        <!-- flowable 工作流相关 -->
+        <dependency>
+            <groupId>org.flowable</groupId>
+            <artifactId>flowable-spring-boot-starter-basic</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.flowable</groupId>
+            <artifactId>flowable-spring-boot-starter-actuator</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 22 - 0
yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/config/YudaoFlowableConfiguration.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.framework.flowable.config;
+
+import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
+import cn.iocoder.yudao.framework.flowable.core.web.FlowableWebFilter;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class YudaoFlowableConfiguration {
+
+    /**
+     * 配置 flowable Web 过滤器
+     */
+    @Bean
+    public FilterRegistrationBean<FlowableWebFilter> flowableWebFilter() {
+        FilterRegistrationBean<FlowableWebFilter> registrationBean = new FilterRegistrationBean<>();
+        registrationBean.setFilter(new FlowableWebFilter());
+        registrationBean.setOrder(WebFilterOrderEnum.FLOWABLE_FILTER);
+        return registrationBean;
+    }
+}

+ 1 - 0
yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/package-info.java

@@ -0,0 +1 @@
+package cn.iocoder.yudao.framework.flowable.core;

+ 62 - 0
yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/util/FlowableUtils.java

@@ -0,0 +1,62 @@
+package cn.iocoder.yudao.framework.flowable.core.util;
+
+import org.flowable.bpmn.converter.BpmnXMLConverter;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.FlowElement;
+import org.flowable.common.engine.impl.identity.Authentication;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class FlowableUtils {
+
+    public static void setAuthenticatedUserId(Long userId) {
+        Authentication.setAuthenticatedUserId(String.valueOf(userId));
+    }
+
+    public static void clearAuthenticatedUserId() {
+        Authentication.setAuthenticatedUserId(null);
+    }
+
+    /**
+     * 获得 BPMN 流程中,指定的元素们
+     *
+     * @param model
+     * @param clazz 指定元素。例如说,{@link org.flowable.bpmn.model.UserTask}、{@link org.flowable.bpmn.model.Gateway} 等等
+     * @return 元素们
+     */
+    public static <T extends FlowElement> List<T> getBpmnModelElements(BpmnModel model, Class<T> clazz) {
+        List<T> result = new ArrayList<>();
+        model.getProcesses().forEach(process -> {
+            process.getFlowElements().forEach(flowElement -> {
+                if (flowElement.getClass().isAssignableFrom(clazz)) {
+                    result.add((T) flowElement);
+                }
+            });
+        });
+        return result;
+    }
+
+    /**
+     * 比较 两个bpmnModel 是否相同
+     * @param oldModel  老的bpmn model
+     * @param newModel 新的bpmn model
+     */
+    public static boolean equals(BpmnModel oldModel, BpmnModel newModel) {
+        // 由于 BpmnModel 未提供 equals 方法,所以只能转成字节数组,进行比较
+        return Arrays.equals(getBpmnBytes(oldModel), getBpmnBytes(newModel));
+    }
+
+    /**
+     * 把 bpmnModel 转换成 byte[]
+     * @param model  bpmnModel
+     */
+    public  static byte[] getBpmnBytes(BpmnModel model) {
+        if (model == null) {
+            return new byte[0];
+        }
+        BpmnXMLConverter converter = new BpmnXMLConverter();
+        return converter.convertToXML(model);
+    }
+}

+ 35 - 0
yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/web/FlowableWebFilter.java

@@ -0,0 +1,35 @@
+package cn.iocoder.yudao.framework.flowable.core.web;
+
+import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
+import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+/**
+ * flowable Web 过滤器,将 userId 设置到 {@link org.flowable.common.engine.impl.identity.Authentication} 中
+ *
+ * @author jason
+ */
+public class FlowableWebFilter extends OncePerRequestFilter {
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
+            throws ServletException, IOException {
+        try {
+            // 设置工作流的用户
+            Long userId = SecurityFrameworkUtils.getLoginUserId();
+            if (userId != null) {
+                FlowableUtils.setAuthenticatedUserId(userId);
+            }
+            // 过滤
+            chain.doFilter(request, response);
+        } finally {
+            // 清理
+            FlowableUtils.clearAuthenticatedUserId();
+        }
+    }
+}

+ 1 - 0
yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/package-info.java

@@ -0,0 +1 @@
+package cn.iocoder.yudao.framework.flowable;

+ 2 - 0
yudao-framework/yudao-spring-boot-starter-flowable/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  cn.iocoder.yudao.framework.flowable.config.YudaoFlowableConfiguration

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-web/pom.xml

@@ -12,7 +12,7 @@
     <packaging>jar</packaging>
 
     <name>${project.artifactId}</name>
-    <description>用户的认证、权限的校验</description>
+    <description>Web 框架,全局异常、API 日志等</description>
     <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
 
     <dependencies>

+ 4 - 0
yudao-module-bpm/yudao-module-bpm-base/pom.xml

@@ -32,6 +32,10 @@
             <groupId>cn.iocoder.boot</groupId>
             <artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
         </dependency>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-biz-data-permission</artifactId>
+        </dependency>
 
         <!-- Web 相关 -->
         <dependency>

+ 8 - 0
yudao-module-bpm/yudao-module-bpm-base/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmFormService.java

@@ -88,4 +88,12 @@ public interface BpmFormService {
      */
     PageResult<BpmFormDO> getFormPage(BpmFormPageReqVO pageReqVO);
 
+    /**
+     * 校验流程表单已配置
+     *
+     * @param configStr  configStr 字段
+     * @return 流程表单
+     */
+    BpmFormDO checkFormConfig(String  configStr);
+
 }

+ 30 - 4
yudao-module-bpm/yudao-module-bpm-base/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmFormServiceImpl.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.bpm.service.definition;
 
 import cn.hutool.core.lang.Assert;
+import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormCreateReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormUpdateReqVO;
@@ -8,18 +9,20 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmFormConvert;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
 import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmFormMapper;
 import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants;
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmFormFieldRespDTO;
 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
 
 /**
  * 动态表单 Service 实现类
@@ -87,6 +90,29 @@ public class BpmFormServiceImpl implements BpmFormService {
         return formMapper.selectPage(pageReqVO);
     }
 
+
+    @Override
+    public BpmFormDO checkFormConfig(String configStr) {
+        BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(configStr, BpmModelMetaInfoRespDTO.class);
+        if (metaInfo == null || metaInfo.getFormType() == null) {
+            throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
+        }
+        // 校验表单存在
+        if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) {
+            BpmFormDO form = getForm(metaInfo.getFormId());
+            if (form == null) {
+                throw exception(FORM_NOT_EXISTS);
+            }
+            return form;
+        }
+        return null;
+    }
+
+    private void checkKeyNCName(String key) {
+        if (!ValidationUtils.isXmlNCName(key)) {
+            throw exception(MODEL_KEY_VALID);
+        }
+    }
     /**
      * 校验 Field,避免 field 重复
      *

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java → yudao-module-bpm/yudao-module-bpm-base/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java

@@ -5,7 +5,7 @@ import lombok.Data;
 
 /**
  * BPM 流程 MetaInfo Response DTO
- * 主要用于 {@link org.activiti.engine.repository.Model#setMetaInfo(String)} 的存储
+ * 主要用于 { Model#setMetaInfo(String)} 的存储
  *
  * @author 芋道源码
  */

+ 0 - 0
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java → yudao-module-bpm/yudao-module-bpm-base/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java


+ 1 - 1
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java

@@ -8,7 +8,7 @@ import org.springframework.validation.annotation.Validated;
 import javax.annotation.Resource;
 
 /**
- * 流程实例 Api 实现类
+ * Activiti 流程实例 Api 实现类
  *
  * @author 芋道源码
  */

+ 0 - 1
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java

@@ -93,5 +93,4 @@ public class BpmModelController {
         bpmModelService.updateModelState(reqVO.getId(), reqVO.getState());
         return success(true);
     }
-
 }

+ 2 - 2
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmTaskAssignRuleController.java

@@ -1,10 +1,10 @@
 package cn.iocoder.yudao.module.bpm.controller.admin.definition;
 
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiImplicitParams;
@@ -32,7 +32,7 @@ public class BpmTaskAssignRuleController {
     @ApiOperation(value = "获得任务分配规则列表")
     @ApiImplicitParams({
             @ApiImplicitParam(name = "modelId", value = "模型编号", example = "1024", dataTypeClass = String.class),
-            @ApiImplicitParam(name = "processDefinitionId", value = "刘晨定义的编号", example = "2048", dataTypeClass = String.class)
+            @ApiImplicitParam(name = "processDefinitionId", value = "流程定义的编号", example = "2048", dataTypeClass = String.class)
     })
     @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:query')")
     public CommonResult<List<BpmTaskAssignRuleRespVO>> getTaskAssignRuleList(

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java

@@ -37,7 +37,7 @@ public class BpmTaskController {
     @GetMapping("done-page")
     @ApiOperation("获取 Done 已办任务分页")
     @PreAuthorize("@ss.hasPermission('bpm:task:query')")
-    public CommonResult<PageResult<BpmTaskDonePageItemRespVO>> getTodoTaskPage(@Valid BpmTaskDonePageReqVO pageVO) {
+    public CommonResult<PageResult<BpmTaskDonePageItemRespVO>> getDoneTaskPage(@Valid BpmTaskDonePageReqVO pageVO) {
         return success(taskService.getDoneTaskPage(getLoginUserId(), pageVO));
     }
 

+ 2 - 2
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmModelConvert.java

@@ -2,11 +2,11 @@ package cn.iocoder.yudao.module.bpm.convert.definition;
 
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
-import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
-import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
+import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
+import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
 import org.activiti.engine.impl.persistence.entity.SuspensionState;
 import org.activiti.engine.repository.Deployment;
 import org.activiti.engine.repository.Model;

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java

@@ -1,11 +1,11 @@
 package cn.iocoder.yudao.module.bpm.convert.definition;
 
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageItemRespVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
-import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import org.activiti.engine.impl.persistence.entity.SuspensionState;
 import org.activiti.engine.repository.Deployment;
 import org.activiti.engine.repository.ProcessDefinition;

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmActivityBehaviorFactory.java

@@ -44,7 +44,7 @@ public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory {
 
     @Override
     public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) {
-        BpmUserTaskActivitiBehavior userTaskActivityBehavior = new BpmUserTaskActivitiBehavior(userTask);
+        BpmUserTaskActivityBehavior userTaskActivityBehavior = new BpmUserTaskActivityBehavior(userTask);
         userTaskActivityBehavior.setBpmTaskRuleService(bpmTaskRuleService);
         userTaskActivityBehavior.setPermissionApi(permissionApi);
         userTaskActivityBehavior.setDeptApi(deptApi);

+ 2 - 2
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehavior.java → yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmUserTaskActivityBehavior.java

@@ -44,7 +44,7 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_CREATE_F
  * @author 芋道源码
  */
 @Slf4j
-public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
+public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
 
     @Setter
     private BpmTaskAssignRuleService bpmTaskRuleService;
@@ -64,7 +64,7 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
      */
     private Map<Long, BpmTaskAssignScript> scriptMap = Collections.emptyMap();
 
-    public BpmUserTaskActivitiBehavior(UserTask userTask) {
+    public BpmUserTaskActivityBehavior(UserTask userTask) {
         super(userTask);
     }
 

+ 2 - 0
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.bpm.framework.activiti.core.behavior.script.impl;
 
+import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
 import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
 import org.activiti.engine.impl.persistence.entity.TaskEntity;
 import org.springframework.stereotype.Component;
@@ -15,6 +16,7 @@ import java.util.Set;
 public class BpmTaskAssignLeaderX1Script extends BpmTaskAssignLeaderAbstractScript {
 
     @Override
+    @DataPermission(enable = false) // 不需要处理数据权限, 不然会有问题,查询不到数据
     public Set<Long> calculateTaskCandidateUsers(TaskEntity task) {
         return calculateTaskCandidateUsers(task, 1);
     }

+ 2 - 0
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.bpm.framework.activiti.core.behavior.script.impl;
 
+import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
 import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
 import org.activiti.engine.impl.persistence.entity.TaskEntity;
 import org.springframework.stereotype.Component;
@@ -15,6 +16,7 @@ import java.util.Set;
 public class BpmTaskAssignLeaderX2Script extends BpmTaskAssignLeaderAbstractScript {
 
     @Override
+    @DataPermission(enable = false) // 不需要处理数据权限, 不然会有问题,查询不到数据
     public Set<Long> calculateTaskCandidateUsers(TaskEntity task) {
         return calculateTaskCandidateUsers(task, 2);
     }

+ 9 - 10
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java

@@ -12,7 +12,6 @@ import javax.validation.Valid;
  * @author yunlongn
  */
 public interface BpmModelService {
-
     /**
      * 获得流程模型分页
      *
@@ -21,14 +20,6 @@ public interface BpmModelService {
      */
     PageResult<BpmModelPageItemRespVO> getModelPage(BpmModelPageReqVO pageVO);
 
-    /**
-     * 获得流程模块
-     *
-     * @param id 编号
-     * @return 流程模型
-     */
-    BpmModelRespVO getModel(String id);
-
     /**
      * 创建流程模型
      *
@@ -38,6 +29,14 @@ public interface BpmModelService {
      */
     String createModel(@Valid BpmModelCreateReqVO modelVO, String bpmnXml);
 
+    /**
+     * 获得流程模块
+     *
+     * @param id 编号
+     * @return 流程模型
+     */
+    BpmModelRespVO getModel(String id);
+
     /**
      * 修改流程模型
      *
@@ -63,7 +62,7 @@ public interface BpmModelService {
      * 修改模型的状态,实际更新的部署的流程定义的状态
      *
      * @param id 编号
-     * @param state 状态 {@link org.activiti.engine.impl.persistence.entity.SuspensionState}
+     * @param state 状态
      */
     void updateModelState(String id, Integer state);
 

+ 30 - 51
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java

@@ -3,19 +3,19 @@ package cn.iocoder.yudao.module.bpm.service.definition;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
 import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
-import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
 import cn.iocoder.yudao.framework.activiti.core.util.ActivitiUtils;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
-import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
+import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
 import lombok.extern.slf4j.Slf4j;
 import org.activiti.bpmn.model.BpmnModel;
 import org.activiti.engine.RepositoryService;
@@ -47,16 +47,15 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
 @Service
 @Validated
 @Slf4j
-public class BpmModelServiceImpl implements BpmModelService {
+public class BpmModelServiceImpl  implements BpmModelService {
 
     @Resource
     private RepositoryService repositoryService;
     @Resource
-    private BpmFormService bpmFormService;
-    @Resource
     private BpmProcessDefinitionService processDefinitionService;
     @Resource
-    @Lazy // 解决循环依赖
+    private BpmFormService bpmFormService;
+    @Resource
     private BpmTaskAssignRuleService taskAssignRuleService;
 
     @Override
@@ -167,9 +166,9 @@ public class BpmModelServiceImpl implements BpmModelService {
         }
         // TODO 芋艿:校验流程图的有效性;例如说,是否有开始的元素,是否有结束的元素;
         // 校验表单已配
-        BpmFormDO form = checkFormConfig(model);
+        BpmFormDO form = checkFormConfig(model.getMetaInfo());
         // 校验任务分配规则已配置
-        checkTaskAssignRuleAllConfig(id);
+        taskAssignRuleService.checkTaskAssignRuleAllConfig(id);
 
         // 校验模型是否发生修改。如果未修改,则不允许创建
         BpmProcessDefinitionCreateReqDTO definitionCreateReqDTO = BpmModelConvert.INSTANCE.convert2(model, form).setBpmnBytes(bpmnBytes);
@@ -194,48 +193,6 @@ public class BpmModelServiceImpl implements BpmModelService {
         taskAssignRuleService.copyTaskAssignRules(id, definition.getId());
     }
 
-    /**
-     * 校验流程模型的任务分配规则全部都配置了
-     * 目的:如果有规则未配置,会导致流程任务找不到负责人,进而流程无法进行下去!
-     *
-     * @param id 流程模型编号
-     */
-    private void checkTaskAssignRuleAllConfig(String id) {
-        // 一个用户任务都没配置,所以无需配置规则
-        List<BpmTaskAssignRuleRespVO> taskAssignRules = taskAssignRuleService.getTaskAssignRuleList(id, null);
-        if (CollUtil.isEmpty(taskAssignRules)) {
-            return;
-        }
-        // 校验未配置规则的任务
-        taskAssignRules.forEach(rule -> {
-            if (CollUtil.isEmpty(rule.getOptions())) {
-                throw exception(MODEL_DEPLOY_FAIL_TASK_ASSIGN_RULE_NOT_CONFIG, rule.getTaskDefinitionName());
-            }
-        });
-    }
-
-    /**
-     * 校验流程表单已配置
-     *
-     * @param model 流程模型
-     * @return 流程表单
-     */
-    private BpmFormDO checkFormConfig(Model model) {
-        BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
-        if (metaInfo == null || metaInfo.getFormType() == null) {
-            throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
-        }
-        // 校验表单存在
-        if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) {
-            BpmFormDO form = bpmFormService.getForm(metaInfo.getFormId());
-            if (form == null) {
-                throw exception(FORM_NOT_EXISTS);
-            }
-            return form;
-        }
-        return null;
-    }
-
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void deleteModel(String id) {
@@ -297,4 +254,26 @@ public class BpmModelServiceImpl implements BpmModelService {
         }
     }
 
+    /**
+     * 校验流程表单已配置
+     *
+     * @param metaInfoStr 流程模型 metaInfo 字段
+     * @return 流程表单
+     */
+    private BpmFormDO checkFormConfig(String  metaInfoStr) {
+        BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(metaInfoStr, BpmModelMetaInfoRespDTO.class);
+        if (metaInfo == null || metaInfo.getFormType() == null) {
+            throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
+        }
+        // 校验表单存在
+        if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) {
+            BpmFormDO form = bpmFormService.getForm(metaInfo.getFormId());
+            if (form == null) {
+                throw exception(FORM_NOT_EXISTS);
+            }
+            return form;
+        }
+        return null;
+    }
+
 }

+ 37 - 37
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java

@@ -5,9 +5,9 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmPro
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
-import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
 import org.activiti.bpmn.model.BpmnModel;
 import org.activiti.engine.repository.Deployment;
 import org.activiti.engine.repository.ProcessDefinition;
@@ -35,12 +35,20 @@ public interface BpmProcessDefinitionService {
     PageResult<BpmProcessDefinitionPageItemRespVO> getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageReqVO);
 
     /**
-     * 获得流程定义列表
+     * 创建流程定义
      *
-     * @param listReqVO 列表入参
-     * @return 流程定义列表
+     * @param createReqDTO 创建信息
+     * @return 流程编号
      */
-    List<BpmProcessDefinitionRespVO> getProcessDefinitionList(BpmProcessDefinitionListReqVO listReqVO);
+    String createProcessDefinition(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO);
+
+    /**
+     * 更新流程定义状态
+     *
+     * @param id 流程定义的编号
+     * @param state 状态
+     */
+    void updateProcessDefinitionState(String id, Integer state);
 
     /**
      * 获得流程定义对应的 BPMN XML
@@ -50,6 +58,30 @@ public interface BpmProcessDefinitionService {
      */
     String getProcessDefinitionBpmnXML(String id);
 
+    /**
+     * 获得需要创建的流程定义,是否和当前激活的流程定义相等
+     *
+     * @param createReqDTO 创建信息
+     * @return 是否相等
+     */
+    boolean isProcessDefinitionEquals(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO);
+
+    /**
+     * 获得编号对应的 BpmProcessDefinitionExtDO
+     *
+     * @param id 编号
+     * @return 流程定义拓展
+     */
+    BpmProcessDefinitionExtDO getProcessDefinitionExt(String id);
+
+    /**
+     * 获得流程定义列表
+     *
+     * @param listReqVO 列表入参
+     * @return 流程定义列表
+     */
+    List<BpmProcessDefinitionRespVO> getProcessDefinitionList(BpmProcessDefinitionListReqVO listReqVO);
+
     /**
      * 获得 Bpmn 模型
      *
@@ -84,14 +116,6 @@ public interface BpmProcessDefinitionService {
      */
     ProcessDefinition getActiveProcessDefinition(String key);
 
-    /**
-     * 获得编号对应的 BpmProcessDefinitionExtDO
-     *
-     * @param id 编号
-     * @return 流程定义拓展
-     */
-    BpmProcessDefinitionExtDO getProcessDefinitionExt(String id);
-
     /**
      * 获得 id 对应的 Deployment
      *
@@ -134,28 +158,4 @@ public interface BpmProcessDefinitionService {
      */
     List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> deploymentIds);
 
-    /**
-     * 获得需要创建的流程定义,是否和当前激活的流程定义相等
-     *
-     * @param createReqDTO 创建信息
-     * @return 是否相等
-     */
-    boolean isProcessDefinitionEquals(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO);
-
-    /**
-     * 创建流程定义
-     *
-     * @param createReqDTO 创建信息
-     * @return 流程编号
-     */
-    String createProcessDefinition(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO);
-
-    /**
-     * 更新流程定义的挂起状态
-     *
-     * @param id 流程定义的编号
-     * @param state 挂起状态 {@link org.activiti.engine.impl.persistence.entity.SuspensionState}
-     */
-    void updateProcessDefinitionState(String id, Integer state);
-
 }

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java

@@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConvert;
-import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
 import cn.iocoder.yudao.framework.activiti.core.util.ActivitiUtils;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
@@ -15,6 +14,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmPro
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
 import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessDefinitionExtMapper;
+import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
 import lombok.extern.slf4j.Slf4j;
 import org.activiti.bpmn.model.BpmnModel;
 import org.activiti.engine.RepositoryService;

+ 8 - 0
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleService.java

@@ -76,4 +76,12 @@ public interface BpmTaskAssignRuleService {
      */
     void copyTaskAssignRules(String fromModelId, String toProcessDefinitionId);
 
+    /**
+     * 校验流程模型的任务分配规则全部都配置了
+     * 目的:如果有规则未配置,会导致流程任务找不到负责人,进而流程无法进行下去!
+     *
+     * @param id 流程模型编号
+     */
+    void checkTaskAssignRuleAllConfig(String id);
+
 }

+ 15 - 0
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java

@@ -173,6 +173,21 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
         taskRuleMapper.insertBatch(newRules);
     }
 
+    @Override
+    public void checkTaskAssignRuleAllConfig(String id) {
+        // 一个用户任务都没配置,所以无需配置规则
+        List<BpmTaskAssignRuleRespVO> taskAssignRules = getTaskAssignRuleList(id, null);
+        if (CollUtil.isEmpty(taskAssignRules)) {
+            return;
+        }
+        // 校验未配置规则的任务
+        taskAssignRules.forEach(rule -> {
+            if (CollUtil.isEmpty(rule.getOptions())) {
+                throw exception(MODEL_DEPLOY_FAIL_TASK_ASSIGN_RULE_NOT_CONFIG, rule.getTaskDefinitionName());
+            }
+        });
+    }
+
     private void validTaskAssignRuleOptions(Integer type, Set<Long> options) {
         if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.ROLE.getType())) {
             roleApi.validRoles(options);

+ 2 - 2
yudao-module-bpm/yudao-module-bpm-impl-activiti/src/test/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehaviorTest.java → yudao-module-bpm/yudao-module-bpm-impl-activiti/src/test/java/cn/iocoder/yudao/module/bpm/framework/activiti/core/behavior/BpmUserTaskActivityBehaviorTest.java

@@ -33,10 +33,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 
-public class BpmUserTaskActivitiBehaviorTest extends BaseMockitoUnitTest {
+public class BpmUserTaskActivityBehaviorTest extends BaseMockitoUnitTest {
 
     @InjectMocks
-    private BpmUserTaskActivitiBehavior behavior;
+    private BpmUserTaskActivityBehavior behavior;
     @Mock
     private BpmTaskAssignRuleService bpmTaskRuleService;
     @Mock

+ 13 - 4
yudao-module-bpm/yudao-module-bpm-impl-flowable/pom.xml

@@ -16,14 +16,23 @@
     </description>
 
     <dependencies>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-bpm-base</artifactId>
+            <version>${revision}</version>
+        </dependency>
+
         <!-- 工作流相关 -->
         <dependency>
-            <groupId>org.flowable</groupId>
-            <artifactId>flowable-spring-boot-starter-basic</artifactId>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-flowable</artifactId>
         </dependency>
+
+        <!-- Test 测试相关 -->
         <dependency>
-            <groupId>org.flowable</groupId>
-            <artifactId>flowable-spring-boot-starter-actuator</artifactId>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-test</artifactId>
+            <scope>test</scope>
         </dependency>
     </dependencies>
 

+ 4 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/api/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * bpm API 实现类,定义暴露给其它模块的 API
+ */
+package cn.iocoder.yudao.module.bpm.api;

+ 28 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.bpm.api.task;
+
+import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
+import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+
+/**
+ * Flowable 流程实例 Api 实现类
+ *
+ * @author 芋道源码
+ * @author jason
+ */
+@Service
+@Validated
+public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi {
+
+    @Resource
+    private BpmProcessInstanceService processInstanceService;
+
+    @Override
+    public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO) {
+        return processInstanceService.createProcessInstance(userId, reqDTO);
+    }
+}

+ 97 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java

@@ -0,0 +1,97 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.definition;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.io.IoUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
+import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+
+import java.io.IOException;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Api(tags = "管理后台 - 流程模型")
+@RestController
+@RequestMapping("/bpm/model")
+@Validated
+public class BpmModelController {
+
+    @Resource
+    private BpmModelService modelService;
+
+    @GetMapping("/page")
+    @ApiOperation(value = "获得模型分页")
+    public CommonResult<PageResult<BpmModelPageItemRespVO>> getModelPage(BpmModelPageReqVO pageVO) {
+        return success(modelService.getModelPage(pageVO));
+    }
+
+    @GetMapping("/get")
+    @ApiOperation("获得模型")
+    @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = String.class)
+    @PreAuthorize("@ss.hasPermission('bpm:model:query')")
+    public CommonResult<BpmModelRespVO> getModel(@RequestParam("id") String id) {
+        BpmModelRespVO model = modelService.getModel(id);
+        return success(model);
+    }
+
+    @PostMapping("/create")
+    @ApiOperation(value = "新建模型")
+    @PreAuthorize("@ss.hasPermission('bpm:model:create')")
+    public CommonResult<String> createModel(@Valid @RequestBody BpmModelCreateReqVO createRetVO) {
+        return success(modelService.createModel(createRetVO, null));
+    }
+
+    @PutMapping("/update")
+    @ApiOperation(value = "修改模型")
+    @PreAuthorize("@ss.hasPermission('bpm:model:update')")
+    public CommonResult<Boolean> updateModel(@Valid @RequestBody BpmModelUpdateReqVO modelVO) {
+        modelService.updateModel(modelVO);
+        return success(true);
+    }
+
+    @PostMapping("/import")
+    @ApiOperation(value = "导入模型")
+    @PreAuthorize("@ss.hasPermission('bpm:model:import')")
+    public CommonResult<String> importModel(@Valid BpmModeImportReqVO importReqVO) throws IOException {
+        BpmModelCreateReqVO createReqVO = BpmModelConvert.INSTANCE.convert(importReqVO);
+        // 读取文件
+        String bpmnXml = IoUtils.readUtf8(importReqVO.getBpmnFile().getInputStream(), false);
+        return success(modelService.createModel(createReqVO, bpmnXml));
+    }
+
+    @PostMapping("/deploy")
+    @ApiOperation(value = "部署模型")
+    @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = String.class)
+    @PreAuthorize("@ss.hasPermission('bpm:model:deploy')")
+    public CommonResult<Boolean> deployModel(@RequestParam("id") String id) {
+        modelService.deployModel(id);
+        return success(true);
+    }
+
+    @PutMapping("/update-state")
+    @ApiOperation(value = "修改模型的状态", notes = "实际更新的部署的流程定义的状态")
+    @PreAuthorize("@ss.hasPermission('bpm:model:update')")
+    public CommonResult<Boolean> updateModelState(@Valid @RequestBody BpmModelUpdateStateReqVO reqVO) {
+        modelService.updateModelState(reqVO.getId(), reqVO.getState());
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @ApiOperation("删除模型")
+    @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = String.class)
+    @PreAuthorize("@ss.hasPermission('bpm:model:delete')")
+    public CommonResult<Boolean> deleteModel(@RequestParam("id") String id) {
+        modelService.deleteModel(id);
+        return success(true);
+    }
+}

+ 59 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java

@@ -0,0 +1,59 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.definition;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionListReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageItemRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Api(tags = "管理后台 - 流程定义")
+@RestController
+@RequestMapping("/bpm/process-definition")
+@Validated
+public class BpmProcessDefinitionController {
+
+    @Resource
+    private BpmProcessDefinitionService bpmDefinitionService;
+
+    @GetMapping("/page")
+    @ApiOperation(value = "获得流程定义分页")
+    @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
+    public CommonResult<PageResult<BpmProcessDefinitionPageItemRespVO>> getProcessDefinitionPage(
+            BpmProcessDefinitionPageReqVO pageReqVO) {
+        return success(bpmDefinitionService.getProcessDefinitionPage(pageReqVO));
+    }
+
+    @GetMapping ("/list")
+    @ApiOperation(value = "获得流程定义列表")
+    @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
+    public CommonResult<List<BpmProcessDefinitionRespVO>> getProcessDefinitionList(
+            BpmProcessDefinitionListReqVO listReqVO) {
+        return success(bpmDefinitionService.getProcessDefinitionList(listReqVO));
+    }
+
+    @GetMapping ("/get-bpmn-xml")
+    @ApiOperation(value = "获得流程定义的 BPMN XML")
+    @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = String.class)
+    @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
+    public CommonResult<String> getProcessDefinitionBpmnXML(@RequestParam("id") String id) {
+        String bpmnXML = bpmDefinitionService.getProcessDefinitionBpmnXML(id);
+        return success(bpmnXML);
+    }
+}

+ 58 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmTaskAssignRuleController.java

@@ -0,0 +1,58 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.definition;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Api(tags = "管理后台 - 任务分配规则")
+@RestController
+@RequestMapping("/bpm/task-assign-rule")
+@Validated
+public class BpmTaskAssignRuleController {
+
+    @Resource
+    private BpmTaskAssignRuleService taskAssignRuleService;
+
+    @GetMapping("/list")
+    @ApiOperation(value = "获得任务分配规则列表")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "modelId", value = "模型编号", example = "1024", dataTypeClass = String.class),
+            @ApiImplicitParam(name = "processDefinitionId", value = "流程定义的编号", example = "2048", dataTypeClass = String.class)
+    })
+    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:query')")
+    public CommonResult<List<BpmTaskAssignRuleRespVO>> getTaskAssignRuleList(
+            @RequestParam(value = "modelId", required = false) String modelId,
+            @RequestParam(value = "processDefinitionId", required = false) String processDefinitionId) {
+        return success(taskAssignRuleService.getTaskAssignRuleList(modelId, processDefinitionId));
+    }
+
+    @PostMapping("/create")
+    @ApiOperation(value = "创建任务分配规则")
+    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:create')")
+    public CommonResult<Long> createTaskAssignRule(@Valid @RequestBody BpmTaskAssignRuleCreateReqVO reqVO) {
+        return success(taskAssignRuleService.createTaskAssignRule(reqVO));
+    }
+
+    @PutMapping("/update")
+    @ApiOperation(value = "更新任务分配规则")
+    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:update')")
+    public CommonResult<Boolean> updateTaskAssignRule(@Valid @RequestBody BpmTaskAssignRuleUpdateReqVO reqVO) {
+        taskAssignRuleService.updateTaskAssignRule(reqVO);
+        return success(true);
+    }
+}

+ 39 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmActivityController.java

@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.task;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity.BpmActivityRespVO;
+import cn.iocoder.yudao.module.bpm.service.task.BpmActivityService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Api(tags = "管理后台 - 流程活动实例")
+@RestController
+@RequestMapping("/bpm/activity")
+@Validated
+public class BpmActivityController {
+
+    @Resource
+    private BpmActivityService activityService;
+
+    @GetMapping("/list")
+    @ApiOperation(value = "生成指定流程实例的高亮流程图",
+            notes = "只高亮进行中的任务。不过要注意,该接口暂时没用,通过前端的 ProcessViewer.vue 界面的 highlightDiagram 方法生成")
+    @ApiImplicitParam(name = "processInstanceId", value = "流程实例的编号", required = true, dataTypeClass = String.class)
+    @PreAuthorize("@ss.hasPermission('bpm:task:query')")
+    public CommonResult<List<BpmActivityRespVO>> getActivityList(
+            @RequestParam("processInstanceId") String processInstanceId) {
+        return success(activityService.getActivityListByProcessInstanceId(processInstanceId));
+    }
+}

+ 58 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java

@@ -0,0 +1,58 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.task;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
+import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Api(tags = "管理后台 - 流程实例") // 流程实例,通过流程定义创建的一次“申请”
+@RestController
+@RequestMapping("/bpm/process-instance")
+@Validated
+public class BpmProcessInstanceController {
+    @Resource
+    private BpmProcessInstanceService processInstanceService;
+
+    @GetMapping("/my-page")
+    @ApiOperation(value = "获得我的实例分页列表", notes = "在【我的流程】菜单中,进行调用")
+    @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
+    public CommonResult<PageResult<BpmProcessInstancePageItemRespVO>> getMyProcessInstancePage(
+            @Valid BpmProcessInstanceMyPageReqVO pageReqVO) {
+        return success(processInstanceService.getMyProcessInstancePage(getLoginUserId(), pageReqVO));
+    }
+
+    @PostMapping("/create")
+    @ApiOperation("新建流程实例")
+    @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
+    public CommonResult<String> createProcessInstance(@Valid @RequestBody BpmProcessInstanceCreateReqVO createReqVO) {
+        return success(processInstanceService.createProcessInstance(getLoginUserId(), createReqVO));
+    }
+
+    @GetMapping("/get")
+    @ApiOperation(value = "获得指定流程实例", notes = "在【流程详细】界面中,进行调用")
+    @ApiImplicitParam(name = "id", value = "流程实例的编号", required = true, dataTypeClass = String.class)
+    @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
+    public CommonResult<BpmProcessInstanceRespVO> getProcessInstance(@RequestParam("id") String id) {
+        return success(processInstanceService.getProcessInstanceVO(id));
+    }
+
+    @DeleteMapping("/cancel")
+    @ApiOperation(value = "取消流程实例", notes = "撤回发起的流程")
+    @PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel')")
+    public CommonResult<Boolean> cancelProcessInstance(@Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) {
+        processInstanceService.cancelProcessInstance(getLoginUserId(), cancelReqVO);
+        return success(true);
+    }
+}

+ 79 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java

@@ -0,0 +1,79 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.task;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
+import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.engine.TaskService;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
+
+@Api(tags = "管理后台 - 流程任务实例")
+@RestController
+@RequestMapping("/bpm/task")
+@Validated
+public class BpmTaskController {
+
+    @Resource
+    private BpmTaskService taskService;
+
+    @GetMapping("todo-page")
+    @ApiOperation("获取 Todo 待办任务分页")
+    @PreAuthorize("@ss.hasPermission('bpm:task:query')")
+    public CommonResult<PageResult<BpmTaskTodoPageItemRespVO>> getTodoTaskPage(@Valid BpmTaskTodoPageReqVO pageVO) {
+        return success(taskService.getTodoTaskPage(getLoginUserId(), pageVO));
+    }
+
+    @GetMapping("done-page")
+    @ApiOperation("获取 Done 已办任务分页")
+    @PreAuthorize("@ss.hasPermission('bpm:task:query')")
+    public CommonResult<PageResult<BpmTaskDonePageItemRespVO>> getDoneTaskPage(@Valid BpmTaskDonePageReqVO pageVO) {
+        return success(taskService.getDoneTaskPage(getLoginUserId(), pageVO));
+    }
+
+    @GetMapping("/list-by-process-instance-id")
+    @ApiOperation(value = "获得指定流程实例的任务列表", notes = "包括完成的、未完成的")
+    @ApiImplicitParam(name = "processInstanceId", value = "流程实例的编号", required = true, dataTypeClass = String.class)
+    @PreAuthorize("@ss.hasPermission('bpm:task:query')")
+    public CommonResult<List<BpmTaskRespVO>> getTaskListByProcessInstanceId(
+            @RequestParam("processInstanceId") String processInstanceId) {
+        return success(taskService.getTaskListByProcessInstanceId(processInstanceId));
+    }
+
+    @PutMapping("/approve")
+    @ApiOperation("通过任务")
+    @PreAuthorize("@ss.hasPermission('bpm:task:update')")
+    public CommonResult<Boolean> approveTask(@Valid @RequestBody BpmTaskApproveReqVO reqVO) {
+        taskService.approveTask(getLoginUserId(), reqVO);
+        return success(true);
+    }
+
+    @PutMapping("/reject")
+    @ApiOperation("不通过任务")
+    @PreAuthorize("@ss.hasPermission('bpm:task:update')")
+    public CommonResult<Boolean> rejectTask(@Valid @RequestBody BpmTaskRejectReqVO reqVO) {
+        taskService.rejectTask(getLoginUserId(), reqVO);
+        return success(true);
+    }
+
+    @PutMapping("/update-assignee")
+    @ApiOperation(value = "更新任务的负责人", notes = "用于【流程详情】的【转派】按钮")
+    @PreAuthorize("@ss.hasPermission('bpm:task:update')")
+    public CommonResult<Boolean> updateTaskAssignee(@Valid @RequestBody BpmTaskUpdateAssigneeReqVO reqVO) {
+        taskService.updateTaskAssignee(getLoginUserId(), reqVO);
+        return success(true);
+    }
+}

+ 4 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/app/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * 占位
+ */
+package cn.iocoder.yudao.module.bpm.controller.app;

+ 6 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/package-info.java

@@ -0,0 +1,6 @@
+/**
+ * 提供 RESTful API 给前端:
+ * 1. admin 包:提供给管理后台 yudao-ui-admin 前端项目
+ * 2. app 包:提供给用户 APP yudao-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分
+ */
+package cn.iocoder.yudao.module.bpm.controller;

+ 140 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmModelConvert.java

@@ -0,0 +1,140 @@
+package cn.iocoder.yudao.module.bpm.convert.definition;
+
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
+import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
+import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
+import org.flowable.common.engine.impl.db.SuspensionState;
+import org.flowable.engine.repository.Deployment;
+import org.flowable.engine.repository.Model;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.mapstruct.Mapper;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * 流程模型 Convert
+ *
+ * @author yunlongn
+ */
+@Mapper
+public interface BpmModelConvert {
+
+    BpmModelConvert INSTANCE = Mappers.getMapper(BpmModelConvert.class);
+
+    default List<BpmModelPageItemRespVO> convertList(List<Model> list, Map<Long, BpmFormDO> formMap,
+                                                     Map<String, Deployment> deploymentMap,
+                                                     Map<String, ProcessDefinition> processDefinitionMap) {
+        return CollectionUtils.convertList(list, model -> {
+            BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
+            BpmFormDO form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null;
+            Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null;
+            ProcessDefinition processDefinition = model.getDeploymentId() != null ? processDefinitionMap.get(model.getDeploymentId()) : null;
+            return convert(model, form, deployment, processDefinition);
+        });
+    }
+
+    default BpmModelPageItemRespVO convert(Model model, BpmFormDO form, Deployment deployment, ProcessDefinition processDefinition) {
+        BpmModelPageItemRespVO modelRespVO = new BpmModelPageItemRespVO();
+        modelRespVO.setId(model.getId());
+        modelRespVO.setCreateTime(model.getCreateTime());
+        // 通用 copy
+        copyTo(model, modelRespVO);
+        // Form
+        if (form != null) {
+            modelRespVO.setFormId(form.getId());
+            modelRespVO.setFormName(form.getName());
+        }
+        // ProcessDefinition
+        modelRespVO.setProcessDefinition(this.convert(processDefinition));
+        if (modelRespVO.getProcessDefinition() != null) {
+            modelRespVO.getProcessDefinition().setSuspensionState(processDefinition.isSuspended() ?
+                    SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
+            modelRespVO.getProcessDefinition().setDeploymentTime(deployment.getDeploymentTime());
+        }
+        return modelRespVO;
+    }
+
+    default BpmModelRespVO convert(Model model) {
+        BpmModelRespVO modelRespVO = new BpmModelRespVO();
+        modelRespVO.setId(model.getId());
+        modelRespVO.setCreateTime(model.getCreateTime());
+        // 通用 copy
+        copyTo(model, modelRespVO);
+        return modelRespVO;
+    }
+
+    default void copyTo(Model model, BpmModelBaseVO to) {
+        to.setName(model.getName());
+        to.setKey(model.getKey());
+        to.setCategory(model.getCategory());
+        // metaInfo
+        BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
+        copyTo(metaInfo, to);
+    }
+
+    BpmModelCreateReqVO convert(BpmModeImportReqVO bean);
+
+    default BpmProcessDefinitionCreateReqDTO convert2(Model model, BpmFormDO form) {
+        BpmProcessDefinitionCreateReqDTO createReqDTO = new BpmProcessDefinitionCreateReqDTO();
+        createReqDTO.setModelId(model.getId());
+        createReqDTO.setName(model.getName());
+        createReqDTO.setKey(model.getKey());
+        createReqDTO.setCategory(model.getCategory());
+        BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
+        // metaInfo
+        copyTo(metaInfo, createReqDTO);
+        // form
+        if (form != null) {
+            createReqDTO.setFormConf(form.getConf());
+            createReqDTO.setFormFields(form.getFields());
+        }
+        return createReqDTO;
+    }
+
+    void copyTo(BpmModelMetaInfoRespDTO from, @MappingTarget BpmProcessDefinitionCreateReqDTO to);
+
+    void copyTo(BpmModelMetaInfoRespDTO from, @MappingTarget BpmModelBaseVO to);
+
+    BpmModelPageItemRespVO.ProcessDefinition convert(ProcessDefinition bean);
+
+    default void copy(Model model, BpmModelCreateReqVO bean) {
+        model.setName(bean.getName());
+        model.setKey(bean.getKey());
+        model.setMetaInfo(buildMetaInfoStr(null, bean.getDescription(), null, null,
+                null, null));
+    }
+
+    default void copy(Model model, BpmModelUpdateReqVO bean) {
+        model.setName(bean.getName());
+        model.setCategory(bean.getCategory());
+        model.setMetaInfo(buildMetaInfoStr(JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class),
+                bean.getDescription(), bean.getFormType(), bean.getFormId(),
+                bean.getFormCustomCreatePath(), bean.getFormCustomViewPath()));
+    }
+
+    default String buildMetaInfoStr(BpmModelMetaInfoRespDTO metaInfo, String description, Integer formType,
+                                    Long formId, String formCustomCreatePath, String formCustomViewPath) {
+        if (metaInfo == null) {
+            metaInfo = new BpmModelMetaInfoRespDTO();
+        }
+        // 只有非空,才进行设置,避免更新时的覆盖
+        if (StrUtil.isNotEmpty(description)) {
+            metaInfo.setDescription(description);
+        }
+        if (Objects.nonNull(formType)) {
+            metaInfo.setFormType(formType);
+            metaInfo.setFormId(formId);
+            metaInfo.setFormCustomCreatePath(formCustomCreatePath);
+            metaInfo.setFormCustomViewPath(formCustomViewPath);
+        }
+        return JsonUtils.toJsonString(metaInfo);
+    }
+}

+ 82 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java

@@ -0,0 +1,82 @@
+package cn.iocoder.yudao.module.bpm.convert.definition;
+
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageItemRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
+import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
+import org.flowable.common.engine.impl.db.SuspensionState;
+import org.flowable.engine.repository.Deployment;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.Named;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Bpm 流程定义的 Convert
+ *
+ * @author yunlong.li
+ */
+@Mapper
+public interface BpmProcessDefinitionConvert {
+
+    BpmProcessDefinitionConvert INSTANCE = Mappers.getMapper(BpmProcessDefinitionConvert.class);
+
+    BpmProcessDefinitionPageItemRespVO convert(ProcessDefinition bean);
+
+    BpmProcessDefinitionExtDO convert2(BpmProcessDefinitionCreateReqDTO bean);
+
+    default List<BpmProcessDefinitionPageItemRespVO> convertList(List<ProcessDefinition> list, Map<String, Deployment> deploymentMap,
+                                                                 Map<String, BpmProcessDefinitionExtDO> processDefinitionDOMap, Map<Long, BpmFormDO> formMap) {
+        return CollectionUtils.convertList(list, definition -> {
+            Deployment deployment = definition.getDeploymentId() != null ? deploymentMap.get(definition.getDeploymentId()) : null;
+            BpmProcessDefinitionExtDO definitionDO = processDefinitionDOMap.get(definition.getId());
+            BpmFormDO form = definitionDO != null ? formMap.get(definitionDO.getFormId()) : null;
+            return convert(definition, deployment, definitionDO, form);
+        });
+    }
+
+    default List<BpmProcessDefinitionRespVO> convertList3(List<ProcessDefinition> list,
+                                                          Map<String, BpmProcessDefinitionExtDO> processDefinitionDOMap) {
+        return CollectionUtils.convertList(list, processDefinition -> {
+            BpmProcessDefinitionRespVO respVO = convert3(processDefinition);
+            BpmProcessDefinitionExtDO processDefinitionExtDO = processDefinitionDOMap.get(processDefinition.getId());
+            // 复制通用属性
+            copyTo(processDefinitionExtDO, respVO);
+            return respVO;
+        });
+    }
+
+    @Mapping(source = "suspended", target = "suspensionState", qualifiedByName = "convertSuspendedToSuspensionState")
+    BpmProcessDefinitionRespVO convert3(ProcessDefinition bean);
+
+    @Named("convertSuspendedToSuspensionState")
+    default Integer convertSuspendedToSuspensionState(boolean suspended) {
+        return suspended ? SuspensionState.SUSPENDED.getStateCode() :
+                SuspensionState.ACTIVE.getStateCode();
+    }
+
+    default BpmProcessDefinitionPageItemRespVO convert(ProcessDefinition bean, Deployment deployment,
+                                                       BpmProcessDefinitionExtDO processDefinitionExtDO, BpmFormDO form) {
+        BpmProcessDefinitionPageItemRespVO respVO = convert(bean);
+        respVO.setSuspensionState(bean.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
+        if (deployment != null) {
+            respVO.setDeploymentTime(deployment.getDeploymentTime());
+        }
+        if (form != null) {
+            respVO.setFormName(form.getName());
+        }
+        // 复制通用属性
+        copyTo(processDefinitionExtDO, respVO);
+        return respVO;
+    }
+
+    @Mapping(source = "from.id", target = "to.id", ignore = true)
+    void copyTo(BpmProcessDefinitionExtDO from, @MappingTarget BpmProcessDefinitionRespVO to);
+}

+ 40 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmTaskAssignRuleConvert.java

@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.module.bpm.convert.definition;
+
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
+import org.flowable.bpmn.model.UserTask;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+import java.util.Map;
+
+@Mapper
+public interface BpmTaskAssignRuleConvert {
+    BpmTaskAssignRuleConvert INSTANCE = Mappers.getMapper(BpmTaskAssignRuleConvert.class);
+
+    default List<BpmTaskAssignRuleRespVO> convertList(List<UserTask> tasks, List<BpmTaskAssignRuleDO> rules) {
+        Map<String, BpmTaskAssignRuleDO> ruleMap = CollectionUtils.convertMap(rules, BpmTaskAssignRuleDO::getTaskDefinitionKey);
+        // 以 UserTask 为主维度,原因是:流程图编辑后,一些规则实际就没用了。
+        return CollectionUtils.convertList(tasks, task -> {
+            BpmTaskAssignRuleRespVO respVO = convert(ruleMap.get(task.getId()));
+            if (respVO == null) {
+                respVO = new BpmTaskAssignRuleRespVO();
+                respVO.setTaskDefinitionKey(task.getId());
+            }
+            respVO.setTaskDefinitionName(task.getName());
+            return respVO;
+        });
+    }
+
+    BpmTaskAssignRuleRespVO convert(BpmTaskAssignRuleDO bean);
+
+    BpmTaskAssignRuleDO convert(BpmTaskAssignRuleCreateReqVO bean);
+
+    BpmTaskAssignRuleDO convert(BpmTaskAssignRuleUpdateReqVO bean);
+
+    List<BpmTaskAssignRuleDO> convertList2(List<BpmTaskAssignRuleRespVO> list);
+}

+ 6 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/package-info.java

@@ -0,0 +1,6 @@
+/**
+ * 提供 POJO 类的实体转换
+ *
+ * 目前使用 MapStruct 框架
+ */
+package cn.iocoder.yudao.module.bpm.convert;

+ 29 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmActivityConvert.java

@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.bpm.convert.task;
+
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity.BpmActivityRespVO;
+import org.flowable.engine.history.HistoricActivityInstance;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+ * BPM 活动 Convert
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface BpmActivityConvert {
+
+    BpmActivityConvert INSTANCE = Mappers.getMapper(BpmActivityConvert.class);
+
+    List<BpmActivityRespVO> convertList(List<HistoricActivityInstance> list);
+
+    @Mappings({
+            @Mapping(source = "activityId", target = "key"),
+            @Mapping(source = "activityType", target = "type")
+    })
+    BpmActivityRespVO convert(HistoricActivityInstance bean);
+}

+ 116 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java

@@ -0,0 +1,116 @@
+package cn.iocoder.yudao.module.bpm.convert.task;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageItemRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
+import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEvent;
+import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
+import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.task.api.Task;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.Mappings;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * 流程实例 Convert
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface BpmProcessInstanceConvert {
+
+    BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class);
+
+    default PageResult<BpmProcessInstancePageItemRespVO> convertPage(PageResult<BpmProcessInstanceExtDO> page,
+                                                                     Map<String, List<Task>> taskMap) {
+        List<BpmProcessInstancePageItemRespVO> list = convertList(page.getList());
+        list.forEach(respVO -> respVO.setTasks(convertList2(taskMap.get(respVO.getId()))));
+        return new PageResult<>(list, page.getTotal());
+    }
+
+    List<BpmProcessInstancePageItemRespVO> convertList(List<BpmProcessInstanceExtDO> list);
+
+    @Mapping(source = "processInstanceId", target = "id")
+    BpmProcessInstancePageItemRespVO convert(BpmProcessInstanceExtDO bean);
+
+    List<BpmProcessInstancePageItemRespVO.Task> convertList2(List<Task> tasks);
+
+    default BpmProcessInstanceRespVO convert2(HistoricProcessInstance processInstance, BpmProcessInstanceExtDO processInstanceExt,
+                                              ProcessDefinition processDefinition, BpmProcessDefinitionExtDO processDefinitionExt,
+                                              String bpmnXml, AdminUserRespDTO startUser, DeptRespDTO dept) {
+        BpmProcessInstanceRespVO respVO = convert2(processInstance);
+        copyTo(processInstanceExt, respVO);
+        // definition
+        respVO.setProcessDefinition(convert2(processDefinition));
+        copyTo(processDefinitionExt, respVO.getProcessDefinition());
+        respVO.getProcessDefinition().setBpmnXml(bpmnXml);
+        // user
+        if (startUser != null) {
+            respVO.setStartUser(convert2(startUser));
+            if (dept != null) {
+                respVO.getStartUser().setDeptName(dept.getName());
+            }
+        }
+        return respVO;
+    }
+
+    BpmProcessInstanceRespVO convert2(HistoricProcessInstance bean);
+
+    @Mapping(source = "from.id", target = "to.id", ignore = true)
+    void copyTo(BpmProcessInstanceExtDO from, @MappingTarget BpmProcessInstanceRespVO to);
+
+    BpmProcessInstanceRespVO.ProcessDefinition convert2(ProcessDefinition bean);
+
+    @Mapping(source = "from.id", target = "to.id", ignore = true)
+    void copyTo(BpmProcessDefinitionExtDO from, @MappingTarget BpmProcessInstanceRespVO.ProcessDefinition to);
+
+    BpmProcessInstanceRespVO.User convert2(AdminUserRespDTO bean);
+
+    default BpmProcessInstanceResultEvent convert(Object source, HistoricProcessInstance instance, Integer result) {
+        BpmProcessInstanceResultEvent event = new BpmProcessInstanceResultEvent(source);
+        event.setId(instance.getId());
+        event.setProcessDefinitionKey(instance.getProcessDefinitionKey());
+        event.setBusinessKey(instance.getBusinessKey());
+        event.setResult(result);
+        return event;
+    }
+
+    default BpmProcessInstanceResultEvent convert(Object source, ProcessInstance instance, Integer result) {
+        BpmProcessInstanceResultEvent event = new BpmProcessInstanceResultEvent(source);
+        event.setId(instance.getId());
+        event.setProcessDefinitionKey(instance.getProcessDefinitionKey());
+        event.setBusinessKey(instance.getBusinessKey());
+        event.setResult(result);
+        return event;
+    }
+
+    default BpmMessageSendWhenProcessInstanceApproveReqDTO convert2ApprovedReq(ProcessInstance instance){
+        return  new BpmMessageSendWhenProcessInstanceApproveReqDTO()
+                .setStartUserId(NumberUtils.parseLong(instance.getStartUserId()))
+                .setProcessInstanceId(instance.getId())
+                .setProcessInstanceName(instance.getName());
+    }
+
+    default BpmMessageSendWhenProcessInstanceRejectReqDTO convert2RejectReq(ProcessInstance instance, String comment) {
+        return new BpmMessageSendWhenProcessInstanceRejectReqDTO()
+            .setProcessInstanceName(instance.getName())
+            .setProcessInstanceId(instance.getId())
+            .setComment(comment)
+            .setStartUserId(NumberUtils.parseLong(instance.getStartUserId()));
+    }
+
+}

+ 147 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java

@@ -0,0 +1,147 @@
+package cn.iocoder.yudao.module.bpm.convert.task;
+
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskDonePageItemRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
+import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+
+import org.flowable.common.engine.impl.db.SuspensionState;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.task.api.Task;
+import org.flowable.task.api.history.HistoricTaskInstance;
+import org.mapstruct.*;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Bpm 任务 Convert
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface BpmTaskConvert {
+
+    BpmTaskConvert INSTANCE = Mappers.getMapper(BpmTaskConvert.class);
+
+    default List<BpmTaskTodoPageItemRespVO> convertList1(List<Task> tasks, Map<String, ProcessInstance> processInstanceMap,
+                                                         Map<Long, AdminUserRespDTO> userMap) {
+        return CollectionUtils.convertList(tasks, task -> {
+            BpmTaskTodoPageItemRespVO respVO = convert1(task);
+            ProcessInstance processInstance = processInstanceMap.get(task.getProcessInstanceId());
+            if (processInstance != null) {
+                AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
+                respVO.setProcessInstance(convert(processInstance, startUser));
+            }
+            return respVO;
+        });
+    }
+
+    @Mapping(source = "suspended", target = "suspensionState", qualifiedByName = "convertSuspendedToSuspensionState")
+    BpmTaskTodoPageItemRespVO convert1(Task bean);
+
+    @Named("convertSuspendedToSuspensionState")
+    default Integer convertSuspendedToSuspensionState(boolean suspended) {
+        return suspended ? SuspensionState.SUSPENDED.getStateCode() :
+                SuspensionState.ACTIVE.getStateCode();
+    }
+
+    default List<BpmTaskDonePageItemRespVO> convertList2(List<HistoricTaskInstance> tasks, Map<String, BpmTaskExtDO> bpmTaskExtDOMap,
+                                                         Map<String, HistoricProcessInstance> historicProcessInstanceMap,
+                                                         Map<Long, AdminUserRespDTO> userMap) {
+        return CollectionUtils.convertList(tasks, task -> {
+            BpmTaskDonePageItemRespVO respVO = convert2(task);
+            BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId());
+            copyTo(taskExtDO, respVO);
+            HistoricProcessInstance processInstance = historicProcessInstanceMap.get(task.getProcessInstanceId());
+            if (processInstance != null) {
+                AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
+                respVO.setProcessInstance(convert(processInstance, startUser));
+            }
+            return respVO;
+        });
+    }
+
+    BpmTaskDonePageItemRespVO convert2(HistoricTaskInstance bean);
+
+    @Mappings({
+            @Mapping(source = "processInstance.id", target = "id"),
+            @Mapping(source = "processInstance.name", target = "name"),
+            @Mapping(source = "processInstance.startUserId", target = "startUserId"),
+            @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"),
+            @Mapping(source = "startUser.nickname", target = "startUserNickname")
+    })
+    BpmTaskTodoPageItemRespVO.ProcessInstance convert(ProcessInstance processInstance, AdminUserRespDTO startUser);
+
+    default List<BpmTaskRespVO> convertList3(List<HistoricTaskInstance> tasks, Map<String, BpmTaskExtDO> bpmTaskExtDOMap,
+                                             HistoricProcessInstance processInstance, Map<Long, AdminUserRespDTO> userMap,
+                                             Map<Long, DeptRespDTO> deptMap) {
+        return CollectionUtils.convertList(tasks, task -> {
+            BpmTaskRespVO respVO = convert3(task);
+            BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId());
+            copyTo(taskExtDO, respVO);
+            if (processInstance != null) {
+                AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
+                respVO.setProcessInstance(convert(processInstance, startUser));
+            }
+            AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));
+            if (assignUser != null) {
+                respVO.setAssigneeUser(convert3(assignUser));
+                DeptRespDTO dept = deptMap.get(assignUser.getDeptId());
+                if (dept != null) {
+                    respVO.getAssigneeUser().setDeptName(dept.getName());
+                }
+            }
+            return respVO;
+        });
+    }
+
+    @Mapping(source = "taskDefinitionKey", target = "definitionKey")
+    BpmTaskRespVO convert3(HistoricTaskInstance bean);
+
+    BpmTaskRespVO.User convert3(AdminUserRespDTO bean);
+
+    @Mapping(target = "id", ignore = true)
+    void copyTo(BpmTaskExtDO from, @MappingTarget BpmTaskDonePageItemRespVO to);
+
+    @Mappings({
+            @Mapping(source = "processInstance.id", target = "id"),
+            @Mapping(source = "processInstance.name", target = "name"),
+            @Mapping(source = "processInstance.startUserId", target = "startUserId"),
+            @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"),
+            @Mapping(source = "startUser.nickname", target = "startUserNickname")
+    })
+    BpmTaskTodoPageItemRespVO.ProcessInstance convert(HistoricProcessInstance processInstance, AdminUserRespDTO startUser);
+
+    default BpmTaskExtDO convert2TaskExt(Task task){
+        BpmTaskExtDO taskExtDO = new BpmTaskExtDO()
+                .setTaskId(task.getId())
+                .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee()))
+                .setName(task.getName())
+                .setProcessDefinitionId(task.getProcessDefinitionId())
+                .setProcessInstanceId(task.getProcessInstanceId());
+        taskExtDO.setCreateTime(task.getCreateTime());
+        return taskExtDO;
+    }
+
+    default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, AdminUserRespDTO startUser, Task task) {
+        BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO();
+        reqDTO.setProcessInstanceId(processInstance.getProcessInstanceId())
+                .setProcessInstanceName(processInstance.getName())
+                .setStartUserId(startUser.getId())
+                .setStartUserNickname(startUser.getNickname())
+                .setTaskId(task.getId())
+                .setTaskName(task.getName())
+                .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee()));
+        return reqDTO;
+    }
+}
+
+

+ 1 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md

@@ -0,0 +1 @@
+<http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao>

+ 62 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java

@@ -0,0 +1,62 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.config;
+
+import cn.hutool.core.collection.ListUtil;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.BpmActivityBehaviorFactory;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
+import org.flowable.spring.SpringProcessEngineConfiguration;
+import org.flowable.spring.boot.EngineConfigurationConfigurer;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+
+/**
+ * BPM 模块的 Flowable 配置类
+ *
+ * @author jason
+ */
+@Configuration
+public class BpmFlowableConfiguration {
+
+    /**
+     * BPM 模块的 ProcessEngineConfigurationConfigurer 实现类:
+     *
+     * 1. 设置各种监听器
+     * 2. 设置自定义的 ActivityBehaviorFactory 实现
+     */
+    @Bean
+    public EngineConfigurationConfigurer<SpringProcessEngineConfiguration> bpmProcessEngineConfigurationConfigurer(
+            ObjectProvider<FlowableEventListener> listeners,
+            BpmActivityBehaviorFactory bpmActivityBehaviorFactory) {
+        return configuration -> {
+            // 注册监听器,例如说 BpmActivitiEventListener
+            configuration.setEventListeners(ListUtil.toList(listeners.iterator()));
+            // 设置 ActivityBehaviorFactory 实现类,用于流程任务的审核人的自定义
+            configuration.setActivityBehaviorFactory(bpmActivityBehaviorFactory);
+        };
+    }
+
+    @Bean
+    public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskAssignRuleService taskRuleService,
+                                                                 BpmUserGroupService userGroupService,
+                                                                 PermissionApi permissionApi,
+                                                                 DeptApi deptApi,
+                                                                 AdminUserApi adminUserApi,
+                                                                 List<BpmTaskAssignScript> scripts) {
+        BpmActivityBehaviorFactory bpmActivityBehaviorFactory = new BpmActivityBehaviorFactory();
+        bpmActivityBehaviorFactory.setBpmTaskRuleService(taskRuleService);
+        bpmActivityBehaviorFactory.setUserGroupService(userGroupService);
+        bpmActivityBehaviorFactory.setAdminUserApi(adminUserApi);
+        bpmActivityBehaviorFactory.setPermissionApi(permissionApi);
+        bpmActivityBehaviorFactory.setDeptApi(deptApi);
+        bpmActivityBehaviorFactory.setScripts(scripts);
+        return bpmActivityBehaviorFactory;
+    }
+}

+ 55 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java

@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;
+
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.Setter;
+import lombok.ToString;
+import org.flowable.bpmn.model.UserTask;
+import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
+import org.flowable.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory;
+
+import java.util.List;
+
+/**
+ * 自定义的 ActivityBehaviorFactory 实现类,目的如下:
+ * 1. 自定义 {@link #createUserTaskActivityBehavior(UserTask)}:实现自定义的流程任务的 assignee 负责人的分配
+ *
+ * @author 芋道源码
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory {
+
+    @Setter
+    private BpmTaskAssignRuleService bpmTaskRuleService;
+    @Setter
+    private BpmUserGroupService userGroupService;
+
+    @Setter
+    private PermissionApi permissionApi;
+    @Setter
+    private DeptApi deptApi;
+    @Setter
+    private AdminUserApi adminUserApi;
+    @Setter
+    private List<BpmTaskAssignScript> scripts;
+
+    @Override
+    public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) {
+        BpmUserTaskActivityBehavior userTaskActivityBehavior = new BpmUserTaskActivityBehavior(userTask);
+        userTaskActivityBehavior.setBpmTaskRuleService(bpmTaskRuleService);
+        userTaskActivityBehavior.setPermissionApi(permissionApi);
+        userTaskActivityBehavior.setDeptApi(deptApi);
+        userTaskActivityBehavior.setUserGroupService(userGroupService);
+        userTaskActivityBehavior.setAdminUserApi(adminUserApi);
+        userTaskActivityBehavior.setScripts(scripts);
+        return userTaskActivityBehavior;
+    }
+}

+ 195 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java

@@ -0,0 +1,195 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
+import cn.iocoder.yudao.framework.datapermission.core.dept.rule.DeptDataPermissionRule;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import com.google.common.annotations.VisibleForTesting;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.bpmn.model.UserTask;
+import org.flowable.common.engine.api.FlowableException;
+import org.flowable.common.engine.impl.el.ExpressionManager;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
+import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.flowable.engine.impl.util.TaskHelper;
+import org.flowable.task.service.TaskService;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
+
+import java.util.*;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_ASSIGN_SCRIPT_NOT_EXISTS;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_CREATE_FAIL_NO_CANDIDATE_USER;
+
+/**
+ * 自定义的流程任务的 assignee 负责人的分配
+ * 第一步,获得对应的分配规则;
+ * 第二步,根据分配规则,计算出分配任务的候选人。如果找不到,则直接报业务异常,不继续执行后续的流程;
+ * 第三步,随机选择一个候选人,则选择作为 assignee 负责人。
+ *
+ * @author 芋道源码
+ */
+@Slf4j
+public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
+
+    @Setter
+    private BpmTaskAssignRuleService bpmTaskRuleService;
+    @Setter
+    private BpmUserGroupService userGroupService;
+    @Setter
+    private DeptApi deptApi;
+    @Setter
+    private AdminUserApi adminUserApi;
+    @Setter
+    private PermissionApi permissionApi;
+
+    /**
+     * 任务分配脚本
+     */
+    private Map<Long, BpmTaskAssignScript> scriptMap = Collections.emptyMap();
+
+    public BpmUserTaskActivityBehavior(UserTask userTask) {
+        super(userTask);
+    }
+    public void setScripts(List<BpmTaskAssignScript> scripts) {
+        this.scriptMap = convertMap(scripts, script -> script.getEnum().getId());
+    }
+
+    @Override
+    protected void handleAssignments(TaskService taskService, String assignee, String owner, List<String> candidateUsers, List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager, DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) {
+        // 第一步,获得任务的规则
+        BpmTaskAssignRuleDO rule = getTaskRule(task);
+        // 第二步,获得任务的候选用户们
+        Set<Long> candidateUserIds = calculateTaskCandidateUsers(task, rule);
+        // 第三步,设置一个作为负责人
+        Long assigneeUserId = chooseTaskAssignee(candidateUserIds);
+        TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId));
+    }
+
+    private BpmTaskAssignRuleDO getTaskRule(TaskEntity task) {
+        List<BpmTaskAssignRuleDO> taskRules = bpmTaskRuleService.getTaskAssignRuleListByProcessDefinitionId(task.getProcessDefinitionId(),
+                task.getTaskDefinitionKey());
+        if (CollUtil.isEmpty(taskRules)) {
+            throw new FlowableException(StrUtil.format("流程任务({}/{}/{}) 找不到符合的任务规则",
+                    task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey()));
+        }
+        if (taskRules.size() > 1) {
+            throw new FlowableException(StrUtil.format("流程任务({}/{}/{}) 找到过多任务规则({})",
+                    task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey(), taskRules.size()));
+        }
+        return taskRules.get(0);
+    }
+
+    @VisibleForTesting
+    Set<Long> calculateTaskCandidateUsers(TaskEntity task, BpmTaskAssignRuleDO rule) {
+        Set<Long> assigneeUserIds = null;
+        if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByRole(task, rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByDeptMember(task, rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByDeptLeader(task, rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.POST.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByPost(task, rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByUserGroup(task, rule);
+        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.SCRIPT.getType(), rule.getType())) {
+            assigneeUserIds = calculateTaskCandidateUsersByScript(task, rule);
+        }
+
+        // 移除被禁用的用户
+        removeDisableUsers(assigneeUserIds);
+        // 如果候选人为空,抛出异常 TODO 芋艿:没候选人的策略选择。1 - 挂起;2 - 直接结束;3 - 强制一个兜底人
+        if (CollUtil.isEmpty(assigneeUserIds)) {
+            log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]",
+                    task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey(), toJsonString(rule));
+            throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER);
+        }
+        return assigneeUserIds;
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByRole(TaskEntity task, BpmTaskAssignRuleDO rule) {
+        return permissionApi.getUserRoleIdListByRoleIds(rule.getOptions());
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByDeptMember(TaskEntity task, BpmTaskAssignRuleDO rule) {
+        List<AdminUserRespDTO> users = adminUserApi.getUsersByDeptIds(rule.getOptions());
+        return convertSet(users, AdminUserRespDTO::getId);
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByDeptLeader(TaskEntity task, BpmTaskAssignRuleDO rule) {
+        List<DeptRespDTO> depts = deptApi.getDepts(rule.getOptions());
+        return convertSet(depts, DeptRespDTO::getLeaderUserId);
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByPost(TaskEntity task, BpmTaskAssignRuleDO rule) {
+        List<AdminUserRespDTO> users = adminUserApi.getUsersByPostIds(rule.getOptions());
+        return convertSet(users, AdminUserRespDTO::getId);
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByUser(TaskEntity task, BpmTaskAssignRuleDO rule) {
+        return rule.getOptions();
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByUserGroup(TaskEntity task, BpmTaskAssignRuleDO rule) {
+        List<BpmUserGroupDO> userGroups = userGroupService.getUserGroupList(rule.getOptions());
+        Set<Long> userIds = new HashSet<>();
+        userGroups.forEach(group -> userIds.addAll(group.getMemberUserIds()));
+        return userIds;
+    }
+
+    private Set<Long> calculateTaskCandidateUsersByScript(TaskEntity task, BpmTaskAssignRuleDO rule) {
+        // 获得对应的脚本
+        List<BpmTaskAssignScript> scripts = new ArrayList<>(rule.getOptions().size());
+        rule.getOptions().forEach(id -> {
+            BpmTaskAssignScript script = scriptMap.get(id);
+            if (script == null) {
+                throw exception(TASK_ASSIGN_SCRIPT_NOT_EXISTS, id);
+            }
+            scripts.add(script);
+        });
+        // 逐个计算任务
+        Set<Long> userIds = new HashSet<>();
+        scripts.forEach(script -> CollUtil.addAll(userIds, script.calculateTaskCandidateUsers(task)));
+        return userIds;
+    }
+
+    private Long chooseTaskAssignee(Set<Long> candidateUserIds) {
+        // TODO 芋艿:未来可以优化下,改成轮询的策略
+        int index = RandomUtil.randomInt(candidateUserIds.size());
+        return CollUtil.get(candidateUserIds, index);
+    }
+
+    @VisibleForTesting
+    void removeDisableUsers(Set<Long> assigneeUserIds) {
+        if (CollUtil.isEmpty(assigneeUserIds)) {
+            return;
+        }
+        //TODO 芋艿 这里有数据权限的问题。默认会加上数据权限 dept_id IN (deptId). 导致查询不到数据
+        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds);
+        assigneeUserIds.removeIf(id -> {
+            AdminUserRespDTO user = userMap.get(id);
+            return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus());
+        });
+    }
+}

+ 34 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/BpmTaskAssignScript.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script;
+
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
+
+import java.util.Set;
+
+/**
+ * Bpm 任务分配的自定义 Script 脚本
+ * 使用场景:
+ * 1. 设置审批人为发起人
+ * 2. 设置审批人为发起人的 Leader
+ * 3. 甚至审批人为发起人的 Leader 的 Leader
+ *
+ * @author 芋道源码
+ */
+public interface BpmTaskAssignScript {
+
+    /**
+     * 基于流程任务,获得任务的候选用户们
+     *
+     * @param task 任务
+     * @return 候选人用户的编号数组
+     */
+    Set<Long> calculateTaskCandidateUsers(TaskEntity task);
+
+    /**
+     * 获得枚举值
+     *
+     * @return 枚举值
+     */
+    BpmTaskRuleScriptEnum getEnum();
+}
+

+ 71 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.java

@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl;
+
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
+import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.util.Assert;
+
+import javax.annotation.Resource;
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
+import static java.util.Collections.emptySet;
+
+/**
+ * 分配给发起人的 Leader 审批的 Script 实现类
+ * 目前 Leader 的定义是,
+ *
+ * @author 芋道源码
+ */
+public abstract class BpmTaskAssignLeaderAbstractScript implements BpmTaskAssignScript {
+
+    @Resource
+    private AdminUserApi adminUserApi;
+    @Resource
+    private DeptApi deptApi;
+    @Resource
+    @Lazy // 解决循环依赖
+    private BpmProcessInstanceService bpmProcessInstanceService;
+
+    protected Set<Long> calculateTaskCandidateUsers(TaskEntity task, int level) {
+        Assert.isTrue(level > 0, "level 必须大于 0");
+        // 获得发起人
+        ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(task.getProcessInstanceId());
+        Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
+        // 获得对应 leve 的部门
+        DeptRespDTO dept = null;
+        for (int i = 0; i < level; i++) {
+            // 获得 level 对应的部门
+            if (dept == null) {
+                dept = getStartUserDept(startUserId);
+                if (dept == null) { // 找不到发起人的部门,所以无法使用该规则
+                    return emptySet();
+                }
+            } else {
+                DeptRespDTO parentDept = deptApi.getDept(dept.getParentId());
+                if (parentDept == null) { // 找不到父级部门,所以只好结束寻找。原因是:例如说,级别比较高的人,所在部门层级比较少
+                    break;
+                }
+                dept = parentDept;
+            }
+        }
+        return dept.getLeaderUserId() != null ? asSet(dept.getLeaderUserId()) : emptySet();
+    }
+
+    private DeptRespDTO getStartUserDept(Long startUserId) {
+        AdminUserRespDTO startUser = adminUserApi.getUser(startUserId);
+        if (startUser.getDeptId() == null) { // 找不到部门,所以无法使用该规则
+            return null;
+        }
+        return deptApi.getDept(startUser.getDeptId());
+    }
+
+}

+ 29 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java

@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl;
+
+import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+/**
+ * 分配给发起人的一级 Leader 审批的 Script 实现类
+ *
+ * @author 芋道源码
+ */
+@Component
+public class BpmTaskAssignLeaderX1Script extends BpmTaskAssignLeaderAbstractScript {
+
+    @Override
+    @DataPermission(enable = false) // 不需要处理数据权限, 不然会有问题,查询不到数据
+    public Set<Long> calculateTaskCandidateUsers(TaskEntity task) {
+        return calculateTaskCandidateUsers(task, 1);
+    }
+
+    @Override
+    public BpmTaskRuleScriptEnum getEnum() {
+        return BpmTaskRuleScriptEnum.LEADER_X1;
+    }
+
+}

+ 30 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java

@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl;
+
+import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+/**
+ * 分配给发起人的二级 Leader 审批的 Script 实现类
+ *
+ * @author 芋道源码
+ */
+@Component
+public class BpmTaskAssignLeaderX2Script extends BpmTaskAssignLeaderAbstractScript {
+
+
+    @Override
+    @DataPermission(enable = false) // 不需要处理数据权限, 不然会有问题,查询不到数据
+    public Set<Long> calculateTaskCandidateUsers(TaskEntity task) {
+        return calculateTaskCandidateUsers(task, 2);
+    }
+
+    @Override
+    public BpmTaskRuleScriptEnum getEnum() {
+        return BpmTaskRuleScriptEnum.LEADER_X2;
+    }
+
+}

+ 41 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignStartUserScript.java

@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl;
+
+import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
+
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
+import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.Set;
+
+/**
+ * 分配给发起人审批的 Script 实现类
+ *
+ * @author 芋道源码
+ */
+@Component
+public class BpmTaskAssignStartUserScript implements BpmTaskAssignScript {
+
+    @Resource
+    @Lazy // 解决循环依赖
+    private BpmProcessInstanceService bpmProcessInstanceService;
+
+    @Override
+    public Set<Long> calculateTaskCandidateUsers(TaskEntity task) {
+        ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(task.getProcessInstanceId());
+        Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
+        return SetUtils.asSet(startUserId);
+    }
+
+    @Override
+    public BpmTaskRuleScriptEnum getEnum() {
+        return BpmTaskRuleScriptEnum.START_USER;
+    }
+
+}

+ 53 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java

@@ -0,0 +1,53 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;
+
+import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
+import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
+import com.google.common.collect.ImmutableSet;
+import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
+import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
+import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
+import org.flowable.engine.delegate.event.FlowableCancelledEvent;
+import org.flowable.engine.delegate.event.FlowableProcessStartedEvent;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.Set;
+/**
+ * 监听 {@link ProcessInstance} 的开始与完成,创建与更新对应的 {@link BpmProcessInstanceExtDO} 记录
+ *
+ * @author jason
+ */
+@Component
+public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEventListener {
+
+    @Resource
+    @Lazy
+    private BpmProcessInstanceService processInstanceService;
+
+    public static final Set<FlowableEngineEventType> PROCESS_INSTANCE_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
+                     .add(FlowableEngineEventType.PROCESS_CREATED)
+                     .add(FlowableEngineEventType.PROCESS_CANCELLED)
+                     .add(FlowableEngineEventType.PROCESS_COMPLETED)
+                     .build();
+
+    public BpmProcessInstanceEventListener(){
+        super(PROCESS_INSTANCE_EVENTS);
+    }
+
+    @Override
+    protected void processCreated(FlowableEngineEntityEvent event) {
+        processInstanceService.createProcessInstanceExt((ProcessInstance)event.getEntity());
+    }
+
+    @Override
+    protected void processCancelled(FlowableCancelledEvent event) {
+        processInstanceService.updateProcessInstanceExtCancel(event);
+    }
+
+    @Override
+    protected void processCompleted(FlowableEngineEntityEvent event) {
+        processInstanceService.updateProcessInstanceExtComplete((ProcessInstance)event.getEntity());
+    }
+}

+ 52 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java

@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;
+
+import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
+import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
+import com.google.common.collect.ImmutableSet;
+import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
+import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
+import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
+import org.flowable.task.api.Task;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.Set;
+
+/**
+ * 监听 {@link org.flowable.task.api.Task} 的开始与完成,创建与更新对应的 {@link BpmTaskExtDO} 记录
+ *
+ * @author jason
+ */
+@Component
+public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
+
+    @Resource
+    @Lazy // 解决循环依赖
+    private BpmTaskService taskService;
+
+    public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
+            .add(FlowableEngineEventType.TASK_CREATED)
+            .add(FlowableEngineEventType.TASK_ASSIGNED)
+            .add(FlowableEngineEventType.TASK_COMPLETED)
+            .build();
+
+    public BpmTaskEventListener(){
+        super(TASK_EVENTS);
+    }
+
+    @Override
+    protected void taskCreated(FlowableEngineEntityEvent event) {
+        taskService.createTaskExt((Task) event.getEntity());
+    }
+
+    @Override
+    protected void taskCompleted(FlowableEngineEntityEvent event) {
+        taskService.updateTaskExtComplete((Task)event.getEntity());
+    }
+
+    @Override
+    protected void taskAssigned(FlowableEngineEntityEvent event) {
+        taskService.updateTaskExtAssign((Task)event.getEntity());
+    }
+}

+ 6 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/package-info.java

@@ -0,0 +1,6 @@
+/**
+ * 属于 bpm 模块的 framework 封装
+ *
+ * @author 芋道源码
+ */
+package cn.iocoder.yudao.module.bpm.framework;

+ 12 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/package-info.java

@@ -0,0 +1,12 @@
+/**
+ * bpm 包下,业务流程管理(Business Process Management),我们放工作流的功能,基于 activiti 7 版本实现。
+ * 例如说:流程定义、表单配置、审核中心(我的申请、我的待办、我的已办)等等
+ *
+ * bpm 解释:https://baike.baidu.com/item/BPM/1933
+ *
+ * 1. Controller URL:以 /bpm/ 开头,避免和其它 Module 冲突
+ * 2. DataObject 表名:以 bpm_ 开头,方便在数据库中区分
+ *
+ * 注意,由于 Bpm 模块下,容易和其它模块重名,所以类名都加载 Pay 的前缀~
+ */
+package cn.iocoder.yudao.module.bpm;

+ 77 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java

@@ -0,0 +1,77 @@
+package cn.iocoder.yudao.module.bpm.service.definition;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
+import org.flowable.bpmn.model.BpmnModel;
+
+import javax.validation.Valid;
+
+/**
+ * Flowable流程模型接口
+ *
+ * @author yunlongn
+ */
+public interface BpmModelService {
+    /**
+     * 获得流程模型分页
+     *
+     * @param pageVO 分页查询
+     * @return 流程模型分页
+     */
+    PageResult<BpmModelPageItemRespVO> getModelPage(BpmModelPageReqVO pageVO);
+
+    /**
+     * 创建流程模型
+     *
+     * @param modelVO 创建信息
+     * @param bpmnXml BPMN XML
+     * @return 创建的流程模型的编号
+     */
+    String createModel(@Valid BpmModelCreateReqVO modelVO, String bpmnXml);
+
+    /**
+     * 获得流程模块
+     *
+     * @param id 编号
+     * @return 流程模型
+     */
+    BpmModelRespVO getModel(String id);
+
+    /**
+     * 修改流程模型
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateModel(@Valid BpmModelUpdateReqVO updateReqVO);
+
+    /**
+     * 将流程模型,部署成一个流程定义
+     *
+     * @param id 编号
+     */
+    void deployModel(String id);
+
+    /**
+     * 删除模型
+     *
+     * @param id 编号
+     */
+    void deleteModel(String id);
+
+    /**
+     * 修改模型的状态,实际更新的部署的流程定义的状态
+     *
+     * @param id 编号
+     * @param state 状态
+     */
+    void updateModelState(String id, Integer state);
+
+    /**
+     * 获得流程模型编号对应的 BPMN Model
+     *
+     * @param id 流程模型编号
+     * @return BPMN Model
+     */
+    BpmnModel getBpmnModel(String id);
+
+}

+ 286 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java

@@ -0,0 +1,286 @@
+package cn.iocoder.yudao.module.bpm.service.definition;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import cn.iocoder.yudao.framework.common.util.object.PageUtils;
+import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
+import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
+import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
+import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.bpmn.converter.BpmnXMLConverter;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.common.engine.impl.db.SuspensionState;
+import org.flowable.common.engine.impl.util.io.BytesStreamSource;
+import org.flowable.engine.RepositoryService;
+import org.flowable.engine.repository.Deployment;
+import org.flowable.engine.repository.Model;
+import org.flowable.engine.repository.ModelQuery;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.ObjectUtils;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.*;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
+
+/**
+ * Flowable流程模型实现
+ * 主要进行 Flowable {@link Model} 的维护
+ *
+ * @author yunlongn
+ * @author 芋道源码
+ * @author jason
+ */
+@Service
+@Validated
+@Slf4j
+public class BpmModelServiceImpl implements BpmModelService {
+
+    @Resource
+    private RepositoryService repositoryService;
+    @Resource
+    private BpmProcessDefinitionService processDefinitionService;
+    @Resource
+    private BpmFormService bpmFormService;
+    @Resource
+    private BpmTaskAssignRuleService taskAssignRuleService;
+
+    @Override
+    public PageResult<BpmModelPageItemRespVO> getModelPage(BpmModelPageReqVO pageVO) {
+        ModelQuery modelQuery = repositoryService.createModelQuery();
+        if (StrUtil.isNotBlank(pageVO.getKey())) {
+            modelQuery.modelKey(pageVO.getKey());
+        }
+        if (StrUtil.isNotBlank(pageVO.getName())) {
+            modelQuery.modelNameLike("%" + pageVO.getName() + "%"); // 模糊匹配
+        }
+        if (StrUtil.isNotBlank(pageVO.getCategory())) {
+            modelQuery.modelCategory(pageVO.getCategory());
+        }
+        // 执行查询
+        List<Model> models = modelQuery.orderByCreateTime().desc()
+                .listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
+
+        // 获得 Form Map
+        Set<Long> formIds = CollectionUtils.convertSet(models, model -> {
+            BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
+            return metaInfo != null ? metaInfo.getFormId() : null;
+        });
+        Map<Long, BpmFormDO> formMap = bpmFormService.getFormMap(formIds);
+
+        // 获得 Deployment Map
+        Set<String> deploymentIds = new HashSet<>();
+        models.forEach(model -> CollectionUtils.addIfNotNull(deploymentIds, model.getDeploymentId()));
+        Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap(deploymentIds);
+        // 获得 ProcessDefinition Map
+        List<ProcessDefinition> processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds(deploymentIds);
+        Map<String, ProcessDefinition> processDefinitionMap = convertMap(processDefinitions, ProcessDefinition::getDeploymentId);
+
+        // 拼接结果
+        long modelCount = modelQuery.count();
+        return new PageResult<>(BpmModelConvert.INSTANCE.convertList(models, formMap, deploymentMap, processDefinitionMap), modelCount);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public String createModel(@Valid BpmModelCreateReqVO createReqVO, String bpmnXml) {
+        checkKeyNCName(createReqVO.getKey());
+        // 校验流程标识已经存在
+        Model keyModel = this.getModelByKey(createReqVO.getKey());
+        if (keyModel != null) {
+            throw exception(MODEL_KEY_EXISTS, createReqVO.getKey());
+        }
+
+        // 创建流程定义
+        Model model = repositoryService.newModel();
+        BpmModelConvert.INSTANCE.copy(model, createReqVO);
+        // 保存流程定义
+        repositoryService.saveModel(model);
+        // 保存 BPMN XML
+        saveModelBpmnXml(model, bpmnXml);
+        return model.getId();
+    }
+
+    private Model getModelByKey(String key) {
+        return repositoryService.createModelQuery().modelKey(key).singleResult();
+    }
+
+    @Override
+    public BpmModelRespVO getModel(String id) {
+        Model model = repositoryService.getModel(id);
+        if (model == null) {
+            return null;
+        }
+        BpmModelRespVO modelRespVO = BpmModelConvert.INSTANCE.convert(model);
+        // 拼接 bpmn XML
+        byte[] bpmnBytes = repositoryService.getModelEditorSource(id);
+        modelRespVO.setBpmnXml(StrUtil.utf8Str(bpmnBytes));
+        return modelRespVO;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务
+    public void updateModel(@Valid BpmModelUpdateReqVO updateReqVO) {
+        // 校验流程模型存在
+        Model model = repositoryService.getModel(updateReqVO.getId());
+        if (model == null) {
+            throw exception(MODEL_NOT_EXISTS);
+        }
+
+        // 修改流程定义
+        BpmModelConvert.INSTANCE.copy(model, updateReqVO);
+        // 更新模型
+        repositoryService.saveModel(model);
+        // 更新 BPMN XML
+        saveModelBpmnXml(model, updateReqVO.getBpmnXml());
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务
+    public void deployModel(String id) {
+        // 校验流程模型存在
+        Model model = repositoryService.getModel(id);
+        if (ObjectUtils.isEmpty(model)) {
+            throw exception(MODEL_NOT_EXISTS);
+        }
+        // 校验流程图
+        byte[] bpmnBytes = repositoryService.getModelEditorSource(model.getId());
+        if (bpmnBytes == null) {
+            throw exception(MODEL_NOT_EXISTS);
+        }
+        // TODO 芋艿:校验流程图的有效性;例如说,是否有开始的元素,是否有结束的元素;
+        // 校验表单已配
+        BpmFormDO form = checkFormConfig(model.getMetaInfo());
+        //校验任务分配规则已配置
+        taskAssignRuleService.checkTaskAssignRuleAllConfig(id);
+
+        BpmProcessDefinitionCreateReqDTO definitionCreateReqDTO = BpmModelConvert.INSTANCE.convert2(model, form).setBpmnBytes(bpmnBytes);
+        //校验模型是否发生修改。如果未修改,则不允许创建
+        if (processDefinitionService.isProcessDefinitionEquals(definitionCreateReqDTO)) { // 流程定义的信息相等
+            ProcessDefinition oldProcessInstance = processDefinitionService.getProcessDefinitionByDeploymentId(model.getDeploymentId());
+            if (oldProcessInstance != null && taskAssignRuleService.isTaskAssignRulesEquals(model.getId(), oldProcessInstance.getId())) {
+                throw exception(MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS);
+            }
+        }
+        // 创建流程定义
+        String definitionId = processDefinitionService.createProcessDefinition(definitionCreateReqDTO);
+
+        // 将老的流程定义进行挂起。也就是说,只有最新部署的流程定义,才可以发起任务。
+        updateProcessDefinitionSuspended(model.getDeploymentId());
+
+        // 更新 model 的 deploymentId,进行关联
+        ProcessDefinition definition = processDefinitionService.getProcessDefinition(definitionId);
+        model.setDeploymentId(definition.getDeploymentId());
+        repositoryService.saveModel(model);
+
+        //复制任务分配规则
+        taskAssignRuleService.copyTaskAssignRules(id, definition.getId());
+    }
+
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteModel(String id) {
+        // 校验流程模型存在
+        Model model = repositoryService.getModel(id);
+        if (model == null) {
+            throw exception(MODEL_NOT_EXISTS);
+        }
+        // 执行删除
+        repositoryService.deleteModel(id);
+        // 禁用流程实例
+        updateProcessDefinitionSuspended(model.getDeploymentId());
+    }
+
+    @Override
+    public void updateModelState(String id, Integer state) {
+        // 校验流程模型存在
+        Model model = repositoryService.getModel(id);
+        if (model == null) {
+            throw exception(MODEL_NOT_EXISTS);
+        }
+        // 校验流程定义存在
+        ProcessDefinition definition = processDefinitionService.getProcessDefinitionByDeploymentId(model.getDeploymentId());
+        if (definition == null) {
+            throw exception(PROCESS_DEFINITION_NOT_EXISTS);
+        }
+
+        // 更新状态
+        processDefinitionService.updateProcessDefinitionState(definition.getId(), state);
+    }
+
+    @Override
+    public BpmnModel getBpmnModel(String id) {
+        byte[] bpmnBytes = repositoryService.getModelEditorSource(id);
+        if (ArrayUtil.isEmpty(bpmnBytes)) {
+            return null;
+        }
+        BpmnXMLConverter converter = new BpmnXMLConverter();
+        return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), true, true);
+    }
+
+    private void checkKeyNCName(String key) {
+        if (!ValidationUtils.isXmlNCName(key)) {
+            throw exception(MODEL_KEY_VALID);
+        }
+    }
+
+    /**
+     * 校验流程表单已配置
+     *
+     * @param metaInfoStr 流程模型 metaInfo 字段
+     * @return 流程表单
+     */
+    private BpmFormDO checkFormConfig(String  metaInfoStr) {
+        BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(metaInfoStr, BpmModelMetaInfoRespDTO.class);
+        if (metaInfo == null || metaInfo.getFormType() == null) {
+            throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
+        }
+        // 校验表单存在
+        if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) {
+            BpmFormDO form = bpmFormService.getForm(metaInfo.getFormId());
+            if (form == null) {
+                throw exception(FORM_NOT_EXISTS);
+            }
+            return form;
+        }
+        return null;
+    }
+
+    private void saveModelBpmnXml(Model model, String bpmnXml) {
+        if (StrUtil.isEmpty(bpmnXml)) {
+            return;
+        }
+        repositoryService.addModelEditorSource(model.getId(), StrUtil.utf8Bytes(bpmnXml));
+    }
+
+    /**
+     * 挂起 deploymentId 对应的流程定义。 这里一个deploymentId 只关联一个流程定义
+     * @param deploymentId 流程发布Id.
+     */
+    private void updateProcessDefinitionSuspended(String deploymentId) {
+        if (StrUtil.isEmpty(deploymentId)) {
+            return;
+        }
+        ProcessDefinition oldDefinition = processDefinitionService.getProcessDefinitionByDeploymentId(deploymentId);
+        if (oldDefinition == null) {
+            return;
+        }
+        processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), SuspensionState.SUSPENDED.getStateCode());
+    }
+
+
+}

+ 159 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java

@@ -0,0 +1,159 @@
+package cn.iocoder.yudao.module.bpm.service.definition;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionListReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageItemRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
+import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.engine.repository.Deployment;
+import org.flowable.engine.repository.ProcessDefinition;
+
+import javax.validation.Valid;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+/**
+ * Flowable流程定义接口
+ *
+ * @author yunlong.li
+ * @author ZJQ
+ * @author 芋道源码
+ */
+public interface BpmProcessDefinitionService {
+
+    /**
+     * 获得流程定义分页
+     *
+     * @param pageReqVO 分页入参
+     * @return 流程定义 Page
+     */
+    PageResult<BpmProcessDefinitionPageItemRespVO> getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageReqVO);
+
+    /**
+     * 获得流程定义列表
+     *
+     * @param listReqVO 列表入参
+     * @return 流程定义列表
+     */
+    List<BpmProcessDefinitionRespVO> getProcessDefinitionList(BpmProcessDefinitionListReqVO listReqVO);
+
+    /**
+     * 创建流程定义
+     *
+     * @param createReqDTO 创建信息
+     * @return 流程编号
+     */
+    String createProcessDefinition(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO);
+
+    /**
+     * 更新流程定义状态
+     *
+     * @param id 流程定义的编号
+     * @param state 状态
+     */
+    void updateProcessDefinitionState(String id, Integer state);
+
+    /**
+     * 获得流程定义对应的 BPMN XML
+     *
+     * @param id 流程定义编号
+     * @return BPMN XML
+     */
+    String getProcessDefinitionBpmnXML(String id);
+
+    /**
+     * 获得需要创建的流程定义,是否和当前激活的流程定义相等
+     *
+     * @param createReqDTO 创建信息
+     * @return 是否相等
+     */
+    boolean isProcessDefinitionEquals(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO);
+
+    /**
+     * 获得编号对应的 BpmProcessDefinitionExtDO
+     *
+     * @param id 编号
+     * @return 流程定义拓展
+     */
+    BpmProcessDefinitionExtDO getProcessDefinitionExt(String id);
+
+    /**
+     * 获得编号对应的 ProcessDefinition
+     *
+     * @param id 编号
+     * @return 流程定义
+     */
+    ProcessDefinition getProcessDefinition(String id);
+
+    /**
+     * 获得编号对应的 ProcessDefinition
+     *
+     * 相比 {@link #getProcessDefinition(String)} 方法,category 的取值是正确
+     *
+     * @param id 编号
+     * @return 流程定义
+     */
+    ProcessDefinition getProcessDefinition2(String id);
+
+    /**
+     * 获得 deploymentId 对应的 ProcessDefinition
+     *
+     * @param deploymentId 部署编号
+     * @return 流程定义
+     */
+    ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId);
+
+    /**
+     * 获得 deploymentIds 对应的 ProcessDefinition 数组
+     *
+     * @param deploymentIds 部署编号的数组
+     * @return 流程定义的数组
+     */
+    List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> deploymentIds);
+
+    /**
+     * 获得流程定义标识对应的激活的流程定义
+     *
+     * @param key 流程定义的标识
+     * @return 流程定义
+     */
+    ProcessDefinition getActiveProcessDefinition(String key);
+
+    /**
+     * 获得 ids 对应的 Deployment Map
+     *
+     * @param ids 部署编号的数组
+     * @return 流程部署 Map
+     */
+    default Map<String, Deployment> getDeploymentMap(Set<String> ids) {
+        return CollectionUtils.convertMap(getDeployments(ids), Deployment::getId);
+    }
+
+    /**
+     * 获得 ids 对应的 Deployment 数组
+     *
+     * @param ids 部署编号的数组
+     * @return 流程部署的数组
+     */
+    List<Deployment> getDeployments(Set<String> ids);
+
+    /**
+     * 获得 id 对应的 Deployment
+     *
+     * @param id 部署编号
+     * @return 流程部署
+     */
+    Deployment getDeployment(String id);
+
+    /**
+     * 获得 Bpmn 模型
+     *
+     * @param processDefinitionId 流程定义的编号
+     * @return Bpmn 模型
+     */
+    BpmnModel getBpmnModel(String processDefinitionId);
+}

+ 284 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java

@@ -0,0 +1,284 @@
+package cn.iocoder.yudao.module.bpm.service.definition;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.PageUtils;
+import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionListReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageItemRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
+import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConvert;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
+import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessDefinitionExtMapper;
+import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.bpmn.converter.BpmnXMLConverter;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.common.engine.impl.db.SuspensionState;
+import org.flowable.common.engine.impl.util.io.BytesStreamSource;
+import org.flowable.engine.RepositoryService;
+import org.flowable.engine.repository.Deployment;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.flowable.engine.repository.ProcessDefinitionQuery;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.*;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_KEY_NOT_MATCH;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_NAME_NOT_MATCH;
+import static java.util.Collections.emptyList;
+
+/**
+ * 流程定义实现
+ * 主要进行 Flowable {@link ProcessDefinition} 和 {@link Deployment} 的维护
+ *
+ * @author yunlongn
+ * @author ZJQ
+ * @author 芋道源码
+ */
+@Service
+@Validated
+@Slf4j
+public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionService {
+
+    private static final String BPMN_FILE_SUFFIX = ".bpmn";
+
+    @Resource
+    private RepositoryService repositoryService;
+
+    @Resource
+    private BpmProcessDefinitionExtMapper processDefinitionMapper;
+
+    @Resource
+    private BpmFormService formService;
+
+    @Override
+    public ProcessDefinition getProcessDefinition(String id) {
+        return repositoryService.getProcessDefinition(id);
+    }
+
+    @Override
+    public ProcessDefinition getProcessDefinition2(String id) {
+        return repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();
+    }
+
+    @Override
+    public ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId) {
+        if (StrUtil.isEmpty(deploymentId)) {
+            return null;
+        }
+        return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
+    }
+
+    @Override
+    public List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> deploymentIds) {
+        if (CollUtil.isEmpty(deploymentIds)) {
+            return emptyList();
+        }
+        return repositoryService.createProcessDefinitionQuery().deploymentIds(deploymentIds).list();
+    }
+
+    @Override
+    public ProcessDefinition getActiveProcessDefinition(String key) {
+        return repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).active().singleResult();
+    }
+
+    @Override
+    public List<Deployment> getDeployments(Set<String> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return emptyList();
+        }
+        List<Deployment> list = new ArrayList<>(ids.size());
+        for (String id : ids) {
+            addIfNotNull(list, getDeployment(id));
+        }
+        return list;
+    }
+
+    @Override
+    public Deployment getDeployment(String id) {
+        if (StrUtil.isEmpty(id)) {
+            return null;
+        }
+        return repositoryService.createDeploymentQuery().deploymentId(id).singleResult();
+    }
+
+    @Override
+    public BpmnModel getBpmnModel(String processDefinitionId) {
+        return repositoryService.getBpmnModel(processDefinitionId);
+    }
+
+    @Override
+    public String createProcessDefinition(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO) {
+        // 创建 Deployment 部署
+        Deployment deploy = repositoryService.createDeployment()
+                .key(createReqDTO.getKey()).name(createReqDTO.getName()).category(createReqDTO.getCategory())
+                .addBytes(createReqDTO.getKey() + BPMN_FILE_SUFFIX, createReqDTO.getBpmnBytes())
+                .deploy();
+
+        // 设置 ProcessDefinition 的 category 分类
+        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
+        repositoryService.setProcessDefinitionCategory(definition.getId(), createReqDTO.getCategory());
+        // 注意 1,ProcessDefinition 的 key 和 name 是通过 BPMN 中的 <bpmn2:process /> 的 id 和 name 决定
+        // 注意 2,目前该项目的设计上,需要保证 Model、Deployment、ProcessDefinition 使用相同的 key,保证关联性。
+        //          否则,会导致 ProcessDefinition 的分页无法查询到。
+        if (!Objects.equals(definition.getKey(), createReqDTO.getKey())) {
+            throw exception(PROCESS_DEFINITION_KEY_NOT_MATCH, createReqDTO.getKey(), definition.getKey());
+        }
+        if (!Objects.equals(definition.getName(), createReqDTO.getName())) {
+            throw exception(PROCESS_DEFINITION_NAME_NOT_MATCH, createReqDTO.getName(), definition.getName());
+        }
+
+        // 插入拓展表
+        BpmProcessDefinitionExtDO definitionDO = BpmProcessDefinitionConvert.INSTANCE.convert2(createReqDTO)
+                .setProcessDefinitionId(definition.getId());
+        processDefinitionMapper.insert(definitionDO);
+        return definition.getId();
+    }
+
+    @Override
+    public void updateProcessDefinitionState(String id, Integer state) {
+        // 激活
+        if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) {
+            repositoryService.activateProcessDefinitionById(id, false, null);
+            return;
+        }
+        // 挂起
+        if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) {
+            // suspendProcessInstances = false,进行中的任务,不进行挂起。
+            // 原因:只要新的流程不允许发起即可,老流程继续可以执行。
+            repositoryService.suspendProcessDefinitionById(id, false, null);
+            return;
+        }
+        log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state);
+    }
+
+    @Override
+    public String getProcessDefinitionBpmnXML(String id) {
+        BpmnModel bpmnModel = repositoryService.getBpmnModel(id);
+        if (bpmnModel == null) {
+            return null;
+        }
+        BpmnXMLConverter converter = new BpmnXMLConverter();
+        return StrUtil.utf8Str(converter.convertToXML(bpmnModel));
+    }
+
+    @Override
+    public boolean isProcessDefinitionEquals(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO) {
+        // 校验 name、description 是否更新
+        ProcessDefinition oldProcessDefinition = getActiveProcessDefinition(createReqDTO.getKey());
+        if (oldProcessDefinition == null) {
+            return false;
+        }
+        BpmProcessDefinitionExtDO oldProcessDefinitionExt = getProcessDefinitionExt(oldProcessDefinition.getId());
+        if (!StrUtil.equals(createReqDTO.getName(), oldProcessDefinition.getName())
+                || !StrUtil.equals(createReqDTO.getDescription(), oldProcessDefinitionExt.getDescription())
+                || !StrUtil.equals(createReqDTO.getCategory(), oldProcessDefinition.getCategory())) {
+            return false;
+        }
+        // 校验 form 信息是否更新
+        if (!ObjectUtil.equal(createReqDTO.getFormType(), oldProcessDefinitionExt.getFormType())
+                || !ObjectUtil.equal(createReqDTO.getFormId(), oldProcessDefinitionExt.getFormId())
+                || !ObjectUtil.equal(createReqDTO.getFormConf(), oldProcessDefinitionExt.getFormConf())
+                || !ObjectUtil.equal(createReqDTO.getFormFields(), oldProcessDefinitionExt.getFormFields())
+                || !ObjectUtil.equal(createReqDTO.getFormCustomCreatePath(), oldProcessDefinitionExt.getFormCustomCreatePath())
+                || !ObjectUtil.equal(createReqDTO.getFormCustomViewPath(), oldProcessDefinitionExt.getFormCustomViewPath())) {
+            return false;
+        }
+        // 校验 BPMN XML 信息
+        BpmnModel newModel = buildBpmnModel(createReqDTO.getBpmnBytes());
+        BpmnModel oldModel = getBpmnModel(oldProcessDefinition.getId());
+        //TODO  貌似 flowable 不修改这个也不同。需要看看。 sourceSystemId 不同
+        if (FlowableUtils.equals(oldModel, newModel)) {
+            return false;
+        }
+        // 最终发现都一致,则返回 true
+        return true;
+    }
+
+    /**
+     * 构建对应的 BPMN Model
+     *
+     * @param bpmnBytes 原始的 BPMN XML 字节数组
+     * @return BPMN Model
+     */
+    private  BpmnModel buildBpmnModel(byte[] bpmnBytes) {
+        // 转换成 BpmnModel 对象
+        BpmnXMLConverter converter = new BpmnXMLConverter();
+        return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), true, true);
+    }
+
+
+
+    @Override
+    public BpmProcessDefinitionExtDO getProcessDefinitionExt(String id) {
+        return processDefinitionMapper.selectByProcessDefinitionId(id);
+    }
+
+    @Override
+    public List<BpmProcessDefinitionRespVO> getProcessDefinitionList(BpmProcessDefinitionListReqVO listReqVO) {
+        // 拼接查询条件
+        ProcessDefinitionQuery definitionQuery = repositoryService.createProcessDefinitionQuery();
+        if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), listReqVO.getSuspensionState())) {
+            definitionQuery.suspended();
+        } else if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), listReqVO.getSuspensionState())) {
+            definitionQuery.active();
+        }
+        // 执行查询
+        List<ProcessDefinition> processDefinitions = definitionQuery.list();
+
+        // 获得 BpmProcessDefinitionDO Map
+        List<BpmProcessDefinitionExtDO> processDefinitionDOs = processDefinitionMapper.selectListByProcessDefinitionIds(
+                convertList(processDefinitions, ProcessDefinition::getId));
+        Map<String, BpmProcessDefinitionExtDO> processDefinitionDOMap = convertMap(processDefinitionDOs,
+                BpmProcessDefinitionExtDO::getProcessDefinitionId);
+        // 执行查询,并返回
+        return BpmProcessDefinitionConvert.INSTANCE.convertList3(processDefinitions, processDefinitionDOMap);
+    }
+
+    @Override
+    public PageResult<BpmProcessDefinitionPageItemRespVO> getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageVO) {
+        ProcessDefinitionQuery definitionQuery = repositoryService.createProcessDefinitionQuery();
+        if (StrUtil.isNotBlank(pageVO.getKey())) {
+            definitionQuery.processDefinitionKey(pageVO.getKey());
+        }
+
+        // 执行查询
+        List<ProcessDefinition> processDefinitions = definitionQuery.orderByProcessDefinitionVersion().desc()
+                .listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
+
+        if (CollUtil.isEmpty(processDefinitions)) {
+            return new PageResult<>(emptyList(), definitionQuery.count());
+        }
+        // 获得 Deployment Map
+        Set<String> deploymentIds = new HashSet<>();
+        processDefinitions.forEach(definition -> addIfNotNull(deploymentIds, definition.getDeploymentId()));
+        Map<String, Deployment> deploymentMap = getDeploymentMap(deploymentIds);
+
+        // 获得 BpmProcessDefinitionDO Map
+        List<BpmProcessDefinitionExtDO> processDefinitionDOs = processDefinitionMapper.selectListByProcessDefinitionIds(
+                convertList(processDefinitions, ProcessDefinition::getId));
+        Map<String, BpmProcessDefinitionExtDO> processDefinitionDOMap = convertMap(processDefinitionDOs,
+                BpmProcessDefinitionExtDO::getProcessDefinitionId);
+
+        // 获得 Form Map
+        Set<Long> formIds = convertSet(processDefinitionDOs, BpmProcessDefinitionExtDO::getFormId);
+        Map<Long, BpmFormDO> formMap = formService.getFormMap(formIds);
+
+        // 拼接结果
+        long definitionCount = definitionQuery.count();
+        return new PageResult<>(BpmProcessDefinitionConvert.INSTANCE.convertList(processDefinitions, deploymentMap,
+                processDefinitionDOMap, formMap), definitionCount);
+    }
+
+}

+ 87 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleService.java

@@ -0,0 +1,87 @@
+package cn.iocoder.yudao.module.bpm.service.definition;
+
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
+import org.springframework.lang.Nullable;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * BPM 任务分配规则 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface BpmTaskAssignRuleService {
+
+    /**
+     * 获得流程定义的任务分配规则数组
+     *
+     * @param processDefinitionId 流程定义的编号
+     * @param taskDefinitionKey 流程任务定义的 Key。允许空
+     * @return 任务规则数组
+     */
+    List<BpmTaskAssignRuleDO> getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId,
+                                                                         @Nullable String taskDefinitionKey);
+
+    /**
+     * 获得流程模型的任务规则数组
+     *
+     * @param modelId 流程模型的编号
+     * @return 任务规则数组
+     */
+    List<BpmTaskAssignRuleDO> getTaskAssignRuleListByModelId(String modelId);
+
+    /**
+     * 获得流程定义的任务分配规则数组
+     *
+     * @param modelId 流程模型的编号
+     * @param processDefinitionId 流程定义的编号
+     * @return 任务规则数组
+     */
+    List<BpmTaskAssignRuleRespVO> getTaskAssignRuleList(String modelId, String processDefinitionId);
+
+    /**
+     * 创建任务分配规则
+     *
+     * @param reqVO 创建信息
+     * @return 规则编号
+     */
+    Long createTaskAssignRule(@Valid BpmTaskAssignRuleCreateReqVO reqVO);
+
+    /**
+     * 更新任务分配规则
+     *
+     * @param reqVO 创建信息
+     */
+    void updateTaskAssignRule(@Valid BpmTaskAssignRuleUpdateReqVO reqVO);
+
+    /**
+     * 判断指定流程模型和流程定义的分配规则是否相等
+     *
+     * @param modelId 流程模型编号
+     * @param processDefinitionId 流程定义编号
+     * @return 是否相等
+     */
+    boolean isTaskAssignRulesEquals(String modelId, String processDefinitionId);
+
+    /**
+     * 将流程流程模型的任务分配规则,复制一份给流程定义
+     * 目的:每次流程模型部署时,都会生成一个新的流程定义,此时考虑到每次部署的流程不可变性,所以需要复制一份给该流程定义
+     *
+     * @param fromModelId 流程模型编号
+     * @param toProcessDefinitionId 流程定义编号
+     */
+    void copyTaskAssignRules(String fromModelId, String toProcessDefinitionId);
+
+    /**
+     * 校验流程模型的任务分配规则全部都配置了
+     * 目的:如果有规则未配置,会导致流程任务找不到负责人,进而流程无法进行下去!
+     *
+     * @param id 流程模型编号
+     */
+    void checkTaskAssignRuleAllConfig(String id);
+
+}

+ 208 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java

@@ -0,0 +1,208 @@
+package cn.iocoder.yudao.module.bpm.service.definition;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
+import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO;
+import cn.iocoder.yudao.module.bpm.convert.definition.BpmTaskAssignRuleConvert;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
+import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper;
+import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants;
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.dept.PostApi;
+import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
+import cn.iocoder.yudao.module.system.api.permission.RoleApi;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.FlowElement;
+import org.flowable.bpmn.model.UserTask;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.*;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
+
+/**
+ * BPM 任务分配规则 Service 实现类
+ */
+@Service
+@Validated
+@Slf4j
+public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService{
+
+    @Resource
+    private BpmTaskAssignRuleMapper taskRuleMapper;
+    @Resource
+    @Lazy // 解决循环依赖
+    private BpmModelService modelService;
+    @Resource
+    @Lazy // 解决循环依赖
+    private BpmProcessDefinitionService processDefinitionService;
+    @Resource
+    private BpmUserGroupService userGroupService;
+    @Resource
+    private RoleApi roleApi;
+    @Resource
+    private DeptApi deptApi;
+    @Resource
+    private PostApi postApi;
+    @Resource
+    private AdminUserApi adminUserApi;
+    @Resource
+    private DictDataApi dictDataApi;
+
+    @Override
+    public List<BpmTaskAssignRuleDO> getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId, String taskDefinitionKey) {
+        return taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, taskDefinitionKey);
+    }
+
+    @Override
+    public List<BpmTaskAssignRuleDO> getTaskAssignRuleListByModelId(String modelId) {
+        return taskRuleMapper.selectListByModelId(modelId);
+    }
+
+    @Override
+    public List<BpmTaskAssignRuleRespVO> getTaskAssignRuleList(String modelId, String processDefinitionId) {
+        // 获得规则
+        List<BpmTaskAssignRuleDO> rules = Collections.emptyList();
+        BpmnModel model = null;
+        if (StrUtil.isNotEmpty(modelId)) {
+            rules = getTaskAssignRuleListByModelId(modelId);
+            model = modelService.getBpmnModel(modelId);
+        } else if (StrUtil.isNotEmpty(processDefinitionId)) {
+            rules = getTaskAssignRuleListByProcessDefinitionId(processDefinitionId, null);
+            model = processDefinitionService.getBpmnModel(processDefinitionId);
+        }
+        if (model == null) {
+            return Collections.emptyList();
+        }
+        // 获得用户任务,只有用户任务才可以设置分配规则
+        List<UserTask> userTasks = FlowableUtils.getBpmnModelElements(model, UserTask.class);
+        if (CollUtil.isEmpty(userTasks)) {
+            return Collections.emptyList();
+        }
+        // 转换数据
+        return BpmTaskAssignRuleConvert.INSTANCE.convertList(userTasks, rules);
+    }
+
+    @Override
+    public Long createTaskAssignRule(@Valid BpmTaskAssignRuleCreateReqVO reqVO) {
+        // 校验参数
+        validTaskAssignRuleOptions(reqVO.getType(), reqVO.getOptions());
+        // 校验是否已经配置
+        BpmTaskAssignRuleDO existRule = taskRuleMapper.selectListByModelIdAndTaskDefinitionKey(
+                reqVO.getModelId(), reqVO.getTaskDefinitionKey());
+        if (existRule != null) {
+            throw exception(TASK_ASSIGN_RULE_EXISTS, reqVO.getModelId(), reqVO.getTaskDefinitionKey());
+        }
+
+        // 存储
+        BpmTaskAssignRuleDO rule = BpmTaskAssignRuleConvert.INSTANCE.convert(reqVO)
+                .setProcessDefinitionId(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL); // 只有流程模型,才允许新建
+        taskRuleMapper.insert(rule);
+        return rule.getId();
+    }
+
+    @Override
+    public void updateTaskAssignRule(@Valid BpmTaskAssignRuleUpdateReqVO reqVO) {
+        // 校验参数
+        validTaskAssignRuleOptions(reqVO.getType(), reqVO.getOptions());
+        // 校验是否存在
+        BpmTaskAssignRuleDO existRule = taskRuleMapper.selectById(reqVO.getId());
+        if (existRule == null) {
+            throw exception(TASK_ASSIGN_RULE_NOT_EXISTS);
+        }
+        // 只允许修改流程模型的规则
+        if (!Objects.equals(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL, existRule.getProcessDefinitionId())) {
+            throw exception(TASK_UPDATE_FAIL_NOT_MODEL);
+        }
+
+        // 执行更新
+        taskRuleMapper.updateById(BpmTaskAssignRuleConvert.INSTANCE.convert(reqVO));
+    }
+
+    @Override
+    public boolean isTaskAssignRulesEquals(String modelId, String processDefinitionId) {
+        // 调用 VO 接口的原因是,过滤掉流程模型不需要的规则,保持和 copyTaskAssignRules 方法的一致性
+        List<BpmTaskAssignRuleRespVO> modelRules = getTaskAssignRuleList(modelId, null);
+        List<BpmTaskAssignRuleRespVO> processInstanceRules = getTaskAssignRuleList(null, processDefinitionId);
+        if (modelRules.size() != processInstanceRules.size()) {
+            return false;
+        }
+
+        // 遍历,匹配对应的规则
+        Map<String, BpmTaskAssignRuleRespVO> processInstanceRuleMap = CollectionUtils.convertMap(processInstanceRules,
+                BpmTaskAssignRuleRespVO::getTaskDefinitionKey);
+        for (BpmTaskAssignRuleRespVO modelRule : modelRules) {
+            BpmTaskAssignRuleRespVO processInstanceRule = processInstanceRuleMap.get(modelRule.getTaskDefinitionKey());
+            if (processInstanceRule == null) {
+                return false;
+            }
+            if (!ObjectUtil.equals(modelRule.getType(), processInstanceRule.getType())
+                    || !ObjectUtil.equal(modelRule.getOptions(), processInstanceRule.getOptions())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void copyTaskAssignRules(String fromModelId, String toProcessDefinitionId) {
+        List<BpmTaskAssignRuleRespVO> rules = getTaskAssignRuleList(fromModelId, null);
+        if (CollUtil.isEmpty(rules)) {
+            return;
+        }
+        // 开始复制
+        List<BpmTaskAssignRuleDO> newRules = BpmTaskAssignRuleConvert.INSTANCE.convertList2(rules);
+        newRules.forEach(rule -> rule.setProcessDefinitionId(toProcessDefinitionId).setId(null)
+                .setCreateTime(null).setUpdateTime(null));
+        taskRuleMapper.insertBatch(newRules);
+    }
+
+    @Override
+    public void checkTaskAssignRuleAllConfig(String id) {
+        // 一个用户任务都没配置,所以无需配置规则
+        List<BpmTaskAssignRuleRespVO> taskAssignRules = getTaskAssignRuleList(id, null);
+        if (CollUtil.isEmpty(taskAssignRules)) {
+            return;
+        }
+        // 校验未配置规则的任务
+        taskAssignRules.forEach(rule -> {
+            if (CollUtil.isEmpty(rule.getOptions())) {
+                throw exception(MODEL_DEPLOY_FAIL_TASK_ASSIGN_RULE_NOT_CONFIG, rule.getTaskDefinitionName());
+            }
+        });
+    }
+
+    private void validTaskAssignRuleOptions(Integer type, Set<Long> options) {
+        if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.ROLE.getType())) {
+            roleApi.validRoles(options);
+        } else if (ObjectUtils.equalsAny(type, BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(),
+                BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType())) {
+            deptApi.validDepts(options);
+        } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.POST.getType())) {
+            postApi.validPosts(options);
+        } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER.getType())) {
+            adminUserApi.validUsers(options);
+        } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER_GROUP.getType())) {
+            userGroupService.validUserGroups(options);
+        } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.SCRIPT.getType())) {
+            dictDataApi.validDictDatas(DictTypeConstants.TASK_ASSIGN_SCRIPT,
+                    CollectionUtils.convertSet(options, String::valueOf));
+        } else {
+            throw new IllegalArgumentException(StrUtil.format("未知的规则类型({})", type));
+        }
+    }
+}

+ 22 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmActivityService.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.bpm.service.task;
+
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity.BpmActivityRespVO;
+
+import java.util.List;
+
+/**
+ * BPM 活动实例 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface BpmActivityService {
+
+    /**
+     * 获得指定流程实例的活动实例列表
+     *
+     * @param processInstanceId 流程实例的编号
+     * @return 活动实例列表
+     */
+    List<BpmActivityRespVO> getActivityListByProcessInstanceId(String processInstanceId);
+
+}

+ 49 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmActivityServiceImpl.java

@@ -0,0 +1,49 @@
+package cn.iocoder.yudao.module.bpm.service.task;
+
+import cn.hutool.core.io.IoUtil;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity.BpmActivityRespVO;
+import cn.iocoder.yudao.module.bpm.convert.task.BpmActivityConvert;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
+import lombok.extern.slf4j.Slf4j;
+
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.engine.HistoryService;
+import org.flowable.engine.history.HistoricActivityInstance;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.image.ProcessDiagramGenerator;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_BPMN_MODEL_NOT_EXISTS;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS;
+
+/**
+ * BPM 活动实例 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Slf4j
+@Validated
+public class BpmActivityServiceImpl implements BpmActivityService {
+
+    @Resource
+    private HistoryService historyService;
+
+    @Override
+    public List<BpmActivityRespVO> getActivityListByProcessInstanceId(String processInstanceId) {
+        List<HistoricActivityInstance> activityList = historyService.createHistoricActivityInstanceQuery()
+                .processInstanceId(processInstanceId).list();
+        return BpmActivityConvert.INSTANCE.convertList(activityList);
+    }
+
+
+
+}

+ 149 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java

@@ -0,0 +1,149 @@
+package cn.iocoder.yudao.module.bpm.service.task;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
+import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
+import org.flowable.engine.delegate.event.FlowableCancelledEvent;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.runtime.ProcessInstance;
+
+import javax.validation.Valid;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 流程实例 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface BpmProcessInstanceService {
+
+    /**
+     * 获得流程实例
+     *
+     * @param id 流程实例的编号
+     * @return 流程实例
+     */
+    ProcessInstance getProcessInstance(String id);
+
+    /**
+     * 获得流程实例列表
+     *
+     * @param ids 流程实例的编号集合
+     * @return 流程实例列表
+     */
+    List<ProcessInstance> getProcessInstances(Set<String> ids);
+
+    /**
+     * 获得流程实例 Map
+     *
+     * @param ids 流程实例的编号集合
+     * @return 流程实例列表 Map
+     */
+    default Map<String, ProcessInstance> getProcessInstanceMap(Set<String> ids) {
+        return CollectionUtils.convertMap(getProcessInstances(ids), ProcessInstance::getProcessInstanceId);
+    }
+
+    /**
+     * 获得流程实例的分页
+     *
+     * @param userId 用户编号
+     * @param pageReqVO 分页请求
+     * @return 流程实例的分页
+     */
+    PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId,
+                                                                          @Valid BpmProcessInstanceMyPageReqVO pageReqVO);
+    /**
+     * 创建流程实例(提供给前端)
+     *
+     * @param userId 用户编号
+     * @param createReqVO 创建信息
+     * @return 实例的编号
+     */
+    String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO);
+
+    /**
+     * 创建流程实例(提供给内部)
+     *
+     * @param userId 用户编号
+     * @param createReqDTO 创建信息
+     * @return 实例的编号
+     */
+    String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO);
+
+    /**
+     * 获得流程实例 VO 信息
+     *
+     * @param id 流程实例的编号
+     * @return 流程实例
+     */
+    BpmProcessInstanceRespVO getProcessInstanceVO(String id);
+
+    /**
+     * 取消流程实例
+     *
+     * @param userId 用户编号
+     * @param cancelReqVO 取消信息
+     */
+    void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO);
+
+    /**
+     * 获得历史的流程实例
+     *
+     * @param id 流程实例的编号
+     * @return 历史的流程实例
+     */
+    HistoricProcessInstance getHistoricProcessInstance(String id);
+
+    /**
+     * 获得历史的流程实例列表
+     *
+     * @param ids 流程实例的编号集合
+     * @return 历史的流程实例列表
+     */
+    List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids);
+
+    /**
+     * 获得历史的流程实例 Map
+     *
+     * @param ids 流程实例的编号集合
+     * @return 历史的流程实例列表 Map
+     */
+    default Map<String, HistoricProcessInstance> getHistoricProcessInstanceMap(Set<String> ids) {
+        return CollectionUtils.convertMap(getHistoricProcessInstances(ids), HistoricProcessInstance::getId);
+    }
+
+    /**
+     * 创建 ProcessInstance 拓展记录
+     *
+     * @param instance 流程任务
+     */
+    void createProcessInstanceExt(ProcessInstance instance);
+
+    /**
+     * 更新 ProcessInstance 拓展记录为取消
+     *
+     * @param event 流程取消事件
+     */
+    void updateProcessInstanceExtCancel(FlowableCancelledEvent event);
+
+    /**
+     * 更新 ProcessInstance 拓展记录为完成
+     *
+     * @param instance 流程任务
+     */
+    void updateProcessInstanceExtComplete(ProcessInstance instance);
+
+    /**
+     * 更新 ProcessInstance 拓展记录为不通过
+     *
+     * @param id 流程编号
+     * @param comment 理由。例如说,审批不通过时,需要传递该值
+     */
+    void updateProcessInstanceExtReject(String id, String comment);
+
+
+}

+ 300 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java

@@ -0,0 +1,300 @@
+package cn.iocoder.yudao.module.bpm.service.task;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
+import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
+import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
+import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
+import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
+import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.engine.HistoryService;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.delegate.event.FlowableCancelledEvent;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.task.api.Task;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.*;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF;
+
+/**
+ * 流程实例 Service 实现类
+ *
+ * ProcessDefinition & ProcessInstance & Execution & Task 的关系:
+ *     1. https://blog.csdn.net/bobozai86/article/details/105210414
+ *
+ * HistoricProcessInstance & ProcessInstance 的关系:
+ *     1.https://my.oschina.net/843294669/blog/719024
+ * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+@Slf4j
+public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService {
+
+    @Resource
+    private RuntimeService runtimeService;
+    @Resource
+    private BpmProcessInstanceExtMapper processInstanceExtMapper;
+    @Resource
+    @Lazy // 解决循环依赖
+    private BpmTaskService taskService;
+    @Resource
+    private BpmProcessDefinitionService processDefinitionService;
+    @Resource
+    private HistoryService historyService;
+    @Resource
+    private AdminUserApi adminUserApi;
+    @Resource
+    private DeptApi deptApi;
+    @Resource
+    private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher;
+    @Resource
+    private BpmMessageService messageService;
+    @Override
+    public ProcessInstance getProcessInstance(String id) {
+        return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult();
+    }
+
+    @Override
+    public List<ProcessInstance> getProcessInstances(Set<String> ids) {
+        return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
+    }
+
+    @Override
+    public PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId,
+                                                                                 BpmProcessInstanceMyPageReqVO pageReqVO) {
+        // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
+        PageResult<BpmProcessInstanceExtDO> pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO);
+        if (CollUtil.isEmpty(pageResult.getList())) {
+            return new PageResult<>(pageResult.getTotal());
+        }
+
+        // 获得流程 Task Map
+        List<String> processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
+        Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
+        // 转换返回
+        return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) {
+        // 获得流程定义
+        ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId());
+        // 发起流程
+        return createProcessInstance0(userId, definition, createReqVO.getVariables(), null);
+    }
+
+    @Override
+    public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {
+        // 获得流程定义
+        ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
+        // 发起流程
+        return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey());
+    }
+
+    @Override
+    public BpmProcessInstanceRespVO getProcessInstanceVO(String id) {
+        // 获得流程实例
+        HistoricProcessInstance processInstance = getHistoricProcessInstance(id);
+        if (processInstance == null) {
+            return null;
+        }
+        BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id);
+        Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id);
+
+        // 获得流程定义
+        ProcessDefinition processDefinition = processDefinitionService
+                                                    .getProcessDefinition(processInstance.getProcessDefinitionId());
+        Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId());
+        BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt(
+                processInstance.getProcessDefinitionId());
+        Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id);
+        String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId());
+
+        // 获得 User
+        AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId()));
+        DeptRespDTO dept = null;
+        if (startUser != null) {
+            dept = deptApi.getDept(startUser.getDeptId());
+        }
+
+        // 拼接结果
+        return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt,
+                processDefinition, processDefinitionExt, bpmnXml, startUser, dept);
+    }
+
+    @Override
+    public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) {
+        // 校验流程实例存在
+        ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
+        if (instance == null) {
+            throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
+        }
+        // 只能取消自己的
+        if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
+            throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
+        }
+
+        // 通过删除流程实例,实现流程实例的取消,
+        // 删除流程实例,正则执行任务ACT_RU_TASK. 任务会被删除。通过历史表查询
+        deleteProcessInstance(cancelReqVO.getId(),
+                BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason()));
+    }
+
+    /**
+     * 获得历史的流程实例
+     *
+     * @param id 流程实例的编号
+     * @return 历史的流程实例
+     */
+    @Override
+    public HistoricProcessInstance getHistoricProcessInstance(String id) {
+        return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult();
+    }
+
+    @Override
+    public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) {
+        return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list();
+    }
+
+    @Override
+    public void createProcessInstanceExt(ProcessInstance instance) {
+        // 获得流程定义
+        ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId());
+        // 插入 BpmProcessInstanceExtDO 对象
+        BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
+                .setProcessInstanceId(instance.getId())
+                .setProcessDefinitionId(definition.getId())
+                .setName(instance.getProcessDefinitionName())
+                .setStartUserId(Long.valueOf(instance.getStartUserId()))
+                .setCategory(definition.getCategory())
+                .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus())
+                .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
+
+        processInstanceExtMapper.insert(instanceExtDO);
+    }
+
+    @Override
+    public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) {
+        // 判断是否为 Reject 不通过。如果是,则不进行更新
+        if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String)event.getCause())) {
+            return;
+        }
+
+        // 需要主动查询,因为 instance 只有 id 属性
+        // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
+        HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId());
+        // 更新拓展表
+        BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
+                .setProcessInstanceId(event.getProcessInstanceId())
+                .setEndTime(new Date()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
+                .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
+                .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult());
+
+        processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
+
+        // 发送流程实例的状态事件
+        processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
+                BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
+    }
+
+    @Override
+    public void updateProcessInstanceExtComplete(ProcessInstance instance) {
+        // 需要主动查询,因为 instance 只有 id 属性
+        // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
+        HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId());
+        // 更新拓展表
+        BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
+                .setProcessInstanceId(instance.getProcessInstanceId())
+                .setEndTime(new Date()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
+                .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
+                .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过
+        processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
+
+        // 发送流程被通过的消息
+        messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance));
+
+        // 发送流程实例的状态事件
+        processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
+                BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public void updateProcessInstanceExtReject(String id, String comment) {
+        // 需要主动查询,因为 instance 只有 id 属性
+        ProcessInstance processInstance = getProcessInstance(id);
+        // 删除流程实例,以实现驳回任务时,取消整个审批流程
+        deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(comment)));
+
+        // 更新 status + result
+        // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法,
+        // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的
+        BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id)
+                .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
+                .setResult(BpmProcessInstanceResultEnum.REJECT.getResult());
+        processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
+
+        // 发送流程被不通过的消息
+        messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, comment));
+
+        // 发送流程实例的状态事件
+        processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
+                BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
+    }
+
+    private void deleteProcessInstance(String id, String reason) {
+        runtimeService.deleteProcessInstance(id, reason);
+    }
+
+    private String createProcessInstance0(Long userId, ProcessDefinition definition,
+                                          Map<String, Object> variables, String businessKey) {
+        // 校验流程定义
+        if (definition == null) {
+            throw exception(PROCESS_DEFINITION_NOT_EXISTS);
+        }
+        if (definition.isSuspended()) {
+            throw exception(PROCESS_DEFINITION_IS_SUSPENDED);
+        }
+
+        // 创建流程实例
+        ProcessInstance instance = runtimeService.startProcessInstanceById(definition.getId(), businessKey, variables);
+        // 设置流程名字
+        runtimeService.setProcessInstanceName(instance.getId(), definition.getName());
+
+        // 补全流程实例的拓展表
+        processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId())
+                .setFormVariables(variables));
+
+        return instance.getId();
+    }
+}

+ 117 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java

@@ -0,0 +1,117 @@
+package cn.iocoder.yudao.module.bpm.service.task;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
+import org.flowable.task.api.Task;
+
+
+import javax.validation.Valid;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 流程任务实例 Service 接口
+ *
+ * @author jason
+ * @author 芋道源码
+ */
+public interface BpmTaskService {
+    /**
+     * 获得待办的流程任务分页
+     *
+     * @param userId 用户编号
+     * @param pageReqVO 分页请求
+     * @return 流程任务分页
+     */
+    PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageReqVO);
+    /**
+     * 获得已办的流程任务分页
+     *
+     * @param userId 用户编号
+     * @param pageReqVO 分页请求
+     * @return 流程任务分页
+     */
+    PageResult<BpmTaskDonePageItemRespVO> getDoneTaskPage(Long userId, BpmTaskDonePageReqVO pageReqVO);
+
+    /**
+     * 获得流程任务 Map
+     *
+     * @param processInstanceIds 流程实例的编号数组
+     * @return 流程任务 Map
+     */
+    default Map<String, List<Task>> getTaskMapByProcessInstanceIds(List<String> processInstanceIds) {
+        return CollectionUtils.convertMultiMap(getTasksByProcessInstanceIds(processInstanceIds),
+                Task::getProcessInstanceId);
+    }
+
+    /**
+     * 获得流程任务列表
+     *
+     * @param processInstanceIds 流程实例的编号数组
+     * @return 流程任务列表
+     */
+    List<Task> getTasksByProcessInstanceIds(List<String> processInstanceIds);
+
+    /**
+     * 获得指令流程实例的流程任务列表,包括所有状态的
+     *
+     * @param processInstanceId 流程实例的编号
+     * @return 流程任务列表
+     */
+    List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId);
+
+    /**
+     * 通过任务
+     *
+     * @param userId 用户编号
+     * @param reqVO 通过请求
+     */
+    void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO);
+
+    /**
+     * 不通过任务
+     *
+     * @param userId 用户编号
+     * @param reqVO 不通过请求
+     */
+    void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO);
+
+    /**
+     * 将流程任务分配给指定用户
+     *
+     * @param userId 用户编号
+     * @param reqVO 分配请求
+     */
+    void updateTaskAssignee(Long userId, BpmTaskUpdateAssigneeReqVO reqVO);
+
+    /**
+     * 将流程任务分配给指定用户
+     *
+     * @param id 流程任务编号
+     * @param userId 用户编号
+     */
+    void updateTaskAssignee(String id, Long userId);
+
+    /**
+     * 创建 Task 拓展记录
+     *
+     * @param task 任务实体
+     */
+    void createTaskExt(Task task);
+
+    /**
+     * 更新 Task 拓展记录为完成
+     *
+     * @param task 任务实体
+     */
+    void updateTaskExtComplete(Task task);
+
+    /**
+     * 更新 Task 拓展记录,并发送通知
+     *
+     * @param task 任务实体
+     */
+    void updateTaskExtAssign(Task task);
+
+}

+ 270 - 0
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java

@@ -0,0 +1,270 @@
+package cn.iocoder.yudao.module.bpm.service.task;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
+import cn.iocoder.yudao.framework.common.util.object.PageUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
+import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
+import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
+import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.engine.HistoryService;
+import org.flowable.engine.TaskService;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.task.api.Task;
+import org.flowable.task.api.TaskQuery;
+import org.flowable.task.api.history.HistoricTaskInstance;
+import org.flowable.task.api.history.HistoricTaskInstanceQuery;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.support.TransactionSynchronization;
+import org.springframework.transaction.support.TransactionSynchronizationManager;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.*;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
+
+/**
+ * 流程任务实例 Service 实现类
+ *
+ * @author 芋道源码
+ * @author jason
+ */
+@Slf4j
+@Service
+public class BpmTaskServiceImpl implements BpmTaskService{
+
+    @Resource
+    private TaskService taskService;
+    @Resource
+    private HistoryService historyService;
+
+    @Resource
+    private BpmProcessInstanceService processInstanceService;
+    @Resource
+    private AdminUserApi adminUserApi;
+    @Resource
+    private DeptApi deptApi;
+    @Resource
+    private BpmTaskExtMapper taskExtMapper;
+    @Resource
+    private BpmMessageService messageService;
+
+    @Override
+    public PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) {
+        // 查询待办任务
+        TaskQuery taskQuery = taskService.createTaskQuery()
+                .taskAssignee(String.valueOf(userId)) // 分配给自己
+                .orderByTaskCreateTime().desc(); // 创建时间倒序
+        if (StrUtil.isNotBlank(pageVO.getName())) {
+            taskQuery.taskNameLike("%" + pageVO.getName() + "%");
+        }
+        if (pageVO.getBeginCreateTime() != null) {
+            taskQuery.taskCreatedAfter(pageVO.getBeginCreateTime());
+        }
+        if (pageVO.getEndCreateTime() != null) {
+            taskQuery.taskCreatedBefore(pageVO.getEndCreateTime());
+        }
+        // 执行查询
+        List<Task> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
+        if (CollUtil.isEmpty(tasks)) {
+            return PageResult.empty(taskQuery.count());
+        }
+
+        // 获得 ProcessInstance Map
+        Map<String, ProcessInstance> processInstanceMap = processInstanceService.getProcessInstanceMap(
+                convertSet(tasks, Task::getProcessInstanceId));
+        // 获得 User Map
+        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
+                convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
+        // 拼接结果
+        return new PageResult<>(BpmTaskConvert.INSTANCE.convertList1(tasks, processInstanceMap, userMap),
+                taskQuery.count());
+    }
+
+    @Override
+    public PageResult<BpmTaskDonePageItemRespVO> getDoneTaskPage(Long userId, BpmTaskDonePageReqVO pageVO) {
+        // 查询已办任务
+        HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery()
+                .finished() // 已完成
+                .taskAssignee(String.valueOf(userId)) // 分配给自己
+                .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序
+        if (StrUtil.isNotBlank(pageVO.getName())) {
+            taskQuery.taskNameLike("%" + pageVO.getName() + "%");
+        }
+        if (pageVO.getBeginCreateTime() != null) {
+            taskQuery.taskCreatedAfter(pageVO.getBeginCreateTime());
+        }
+        if (pageVO.getEndCreateTime() != null) {
+            taskQuery.taskCreatedBefore(pageVO.getEndCreateTime());
+        }
+        // 执行查询
+        List<HistoricTaskInstance> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
+        if (CollUtil.isEmpty(tasks)) {
+            return PageResult.empty(taskQuery.count());
+        }
+
+        // 获得 TaskExtDO Map
+        List<BpmTaskExtDO> bpmTaskExtDOs = taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId));
+        Map<String, BpmTaskExtDO> bpmTaskExtDOMap = convertMap(bpmTaskExtDOs, BpmTaskExtDO::getTaskId);
+        // 获得 ProcessInstance Map
+        Map<String, HistoricProcessInstance> historicProcessInstanceMap = processInstanceService.getHistoricProcessInstanceMap(
+                convertSet(tasks, HistoricTaskInstance::getProcessInstanceId));
+        // 获得 User Map
+        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
+                convertSet(historicProcessInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
+        // 拼接结果
+        return new PageResult<>(BpmTaskConvert.INSTANCE.convertList2(tasks, bpmTaskExtDOMap, historicProcessInstanceMap, userMap),
+                taskQuery.count());
+    }
+
+    @Override
+    public List<Task> getTasksByProcessInstanceIds(List<String> processInstanceIds) {
+        if (CollUtil.isEmpty(processInstanceIds)) {
+            return Collections.emptyList();
+        }
+        return taskService.createTaskQuery().processInstanceIdIn(processInstanceIds).list();
+    }
+
+    @Override
+    public List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId) {
+        // 获得任务列表
+        List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery()
+                .processInstanceId(processInstanceId)
+                .orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序
+                .list();
+        if (CollUtil.isEmpty(tasks)) {
+            return Collections.emptyList();
+        }
+
+        // 获得 TaskExtDO Map
+        List<BpmTaskExtDO> bpmTaskExtDOs = taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId));
+        Map<String, BpmTaskExtDO> bpmTaskExtDOMap = convertMap(bpmTaskExtDOs, BpmTaskExtDO::getTaskId);
+        // 获得 ProcessInstance Map
+        HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(processInstanceId);
+        // 获得 User Map
+        Set<Long> userIds = convertSet(tasks, task -> NumberUtils.parseLong(task.getAssignee()));
+        userIds.add(NumberUtils.parseLong(processInstance.getStartUserId()));
+        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
+        // 获得 Dept Map
+        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
+
+        // 拼接数据
+        return BpmTaskConvert.INSTANCE.convertList3(tasks, bpmTaskExtDOMap, processInstance, userMap, deptMap);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO) {
+        // 校验任务存在
+        Task task = checkTask(userId, reqVO.getId());
+        // 校验流程实例存在
+        ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
+        if (instance == null) {
+            throw exception(PROCESS_INSTANCE_NOT_EXISTS);
+        }
+
+        // 完成任务,审批通过
+        taskService.complete(task.getId(), instance.getProcessVariables());
+        // 更新任务拓展表为通过
+        taskExtMapper.updateByTaskId(new BpmTaskExtDO().setTaskId(task.getId())
+                .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()).setComment(reqVO.getComment()));
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO) {
+        Task task = checkTask(userId, reqVO.getId());
+        // 校验流程实例存在
+        ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
+        if (instance == null) {
+            throw exception(PROCESS_INSTANCE_NOT_EXISTS);
+        }
+
+        // 更新流程实例为不通过
+        processInstanceService.updateProcessInstanceExtReject(instance.getProcessInstanceId(), reqVO.getComment());
+
+        // 更新任务拓展表为不通过
+        taskExtMapper.updateByTaskId(new BpmTaskExtDO().setTaskId(task.getId())
+                .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()).setComment(reqVO.getComment()));
+    }
+
+    @Override
+    public void updateTaskAssignee(Long userId, BpmTaskUpdateAssigneeReqVO reqVO) {
+        // 校验任务存在
+        Task task = checkTask(userId, reqVO.getId());
+        // 更新负责人
+        updateTaskAssignee(task.getId(), reqVO.getAssigneeUserId());
+    }
+
+    @Override
+    public void updateTaskAssignee(String id, Long userId) {
+        taskService.setAssignee(id, String.valueOf(userId));
+    }
+
+
+    @Override
+    public void createTaskExt(Task task) {
+        BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert2TaskExt(task)
+                .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
+        taskExtMapper.insert(taskExtDO);
+    }
+
+    @Override
+    public void updateTaskExtComplete(Task task) {
+        BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert2TaskExt(task)
+                .setEndTime(new Date());
+        taskExtMapper.updateByTaskId(taskExtDO);
+    }
+
+    @Override
+    public void updateTaskExtAssign(Task task) {
+        BpmTaskExtDO taskExtDO = new BpmTaskExtDO()
+                .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee()))
+                .setTaskId(task.getId());
+        taskExtMapper.updateByTaskId(taskExtDO);
+        // 发送通知。在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。
+        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
+            @Override
+            public void afterCommit() {
+                ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
+                AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
+                messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
+            }
+        });
+    }
+
+    /**
+     * 校验任务是否存在, 并且是否是分配给自己的任务
+     * @param userId 用户 id
+     * @param taskId task id
+     */
+    private Task checkTask(Long userId, String taskId) {
+        Task task = getTask(taskId);
+        if (task == null) {
+            throw exception(TASK_COMPLETE_FAIL_NOT_EXISTS);
+        }
+        if (!Objects.equals(userId, NumberUtils.parseLong(task.getAssignee()))) {
+            throw exception(TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF);
+        }
+        return task;
+    }
+
+    private Task getTask(String id) {
+        return taskService.createTaskQuery().taskId(id).singleResult();
+    }
+}

+ 4 - 0
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/UserController.java

@@ -0,0 +1,4 @@
+package cn.iocoder.yudao.module.member.controller.admin.user;
+
+public class UserController {
+}

+ 0 - 0
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/enums/common/SexEnum.java → yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/common/SexEnum.java


+ 0 - 0
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/enums/dept/DeptIdEnum.java → yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/dept/DeptIdEnum.java


+ 0 - 0
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/enums/errorcode/ErrorCodeTypeEnum.java → yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/errorcode/ErrorCodeTypeEnum.java


+ 0 - 0
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/enums/notice/NoticeTypeEnum.java → yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/notice/NoticeTypeEnum.java


+ 1 - 1
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/enums/DataScopeEnum.java → yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/DataScopeEnum.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.framework.security.core.enums;
+package cn.iocoder.yudao.module.system.enums.permission;
 
 import lombok.AllArgsConstructor;
 import lombok.Getter;

+ 0 - 0
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/enums/permission/MenuIdEnum.java → yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/MenuIdEnum.java


+ 0 - 0
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/enums/permission/MenuTypeEnum.java → yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/MenuTypeEnum.java


+ 0 - 0
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleCodeEnum.java → yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleCodeEnum.java


+ 0 - 0
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleTypeEnum.java → yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleTypeEnum.java


+ 0 - 0
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsReceiveStatusEnum.java → yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsReceiveStatusEnum.java


+ 0 - 0
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSendStatusEnum.java → yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSendStatusEnum.java


+ 0 - 0
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsTemplateTypeEnum.java → yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsTemplateTypeEnum.java


+ 4 - 0
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/app/dict/AppDictDataController.java

@@ -0,0 +1,4 @@
+package cn.iocoder.yudao.module.system.controller.app.dict;
+
+public class AppDictDataController {
+}

+ 1 - 1
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleDO.java

@@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.system.dal.dataobject.permission;
 
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.mybatis.core.type.JsonLongSetTypeHandler;
-import cn.iocoder.yudao.framework.security.core.enums.DataScopeEnum;
+import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
 import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
 import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
 import com.baomidou.mybatisplus.annotation.TableField;

+ 0 - 29
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginLogTypeEnum.java

@@ -1,29 +0,0 @@
-package cn.iocoder.yudao.module.system.enums.logger;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * 登录日志的类型枚举
- */
-@Getter
-@AllArgsConstructor
-public enum LoginLogTypeEnum {
-
-    LOGIN_USERNAME(100), // 使用账号登录
-    LOGIN_SOCIAL(101), // 使用社交登录
-    LOGIN_MOCK(102), // 使用 Mock 登录
-    LOGIN_MOBILE(103), // 使用手机登陆
-    LOGIN_SMS(104), // 使用短信登陆
-
-    LOGOUT_SELF(200),  // 自己主动登出
-    LOGOUT_TIMEOUT(201), // 超时登出
-    LOGOUT_DELETE(202), // 强制退出
-    ;
-
-    /**
-     * 日志类型
-     */
-    private final Integer type;
-
-}

+ 0 - 27
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginResultEnum.java

@@ -1,27 +0,0 @@
-package cn.iocoder.yudao.module.system.enums.logger;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * 登录结果的枚举类
- */
-@Getter
-@AllArgsConstructor
-public enum LoginResultEnum {
-
-    SUCCESS(0), // 成功
-    BAD_CREDENTIALS(10), // 账号或密码不正确
-    USER_DISABLED(20), // 用户被禁用
-    CAPTCHA_NOT_FOUND(30), // 图片验证码不存在
-    CAPTCHA_CODE_ERROR(31), // 图片验证码不正确
-
-    UNKNOWN_ERROR(100), // 未知异常
-    ;
-
-    /**
-     * 结果
-     */
-    private final Integer result;
-
-}

+ 1 - 1
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java

@@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO;
 import cn.iocoder.yudao.framework.security.core.LoginUser;
-import cn.iocoder.yudao.framework.security.core.enums.DataScopeEnum;
+import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;

+ 1 - 1
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java

@@ -6,7 +6,7 @@ import cn.hutool.core.util.ObjectUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
-import cn.iocoder.yudao.framework.security.core.enums.DataScopeEnum;
+import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleCreateReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleExportReqVO;

+ 1 - 1
yudao-module-system/yudao-module-system-impl/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java

@@ -11,7 +11,7 @@ import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;
 import cn.iocoder.yudao.module.system.service.dept.DeptService;
 import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO;
 import cn.iocoder.yudao.framework.security.core.LoginUser;
-import cn.iocoder.yudao.framework.security.core.enums.DataScopeEnum;
+import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
 import cn.iocoder.yudao.module.system.test.BaseDbUnitTest;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.mock.mockito.MockBean;

+ 1 - 1
yudao-module-system/yudao-module-system-impl/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceTest.java

@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.system.service.permission;
 import cn.hutool.core.util.RandomUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.security.core.enums.DataScopeEnum;
+import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleCreateReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleUpdateReqVO;

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor