Commit 559bab571ae6a1d80febdaee675bb24fa54e44b3

Authored by YunaiV
1 parent 29a0fbfc

BPM:移除 flowable starter 模块,融合到 bpm 模块中

Showing 25 changed files with 147 additions and 159 deletions
yudao-module-bpm/pom.xml
@@ -11,7 +11,6 @@ @@ -11,7 +11,6 @@
11 <modules> 11 <modules>
12 <module>yudao-module-bpm-api</module> 12 <module>yudao-module-bpm-api</module>
13 <module>yudao-module-bpm-biz</module> 13 <module>yudao-module-bpm-biz</module>
14 - <module>yudao-spring-boot-starter-flowable</module>  
15 </modules> 14 </modules>
16 <artifactId>yudao-module-bpm</artifactId> 15 <artifactId>yudao-module-bpm</artifactId>
17 <packaging>pom</packaging> 16 <packaging>pom</packaging>
yudao-module-bpm/yudao-module-bpm-biz/pom.xml
@@ -64,15 +64,19 @@ @@ -64,15 +64,19 @@
64 <artifactId>yudao-spring-boot-starter-test</artifactId> 64 <artifactId>yudao-spring-boot-starter-test</artifactId>
65 </dependency> 65 </dependency>
66 66
67 - <!-- 工作流相关 --> 67 + <!-- 工具类相关 -->
68 <dependency> 68 <dependency>
69 <groupId>cn.iocoder.boot</groupId> 69 <groupId>cn.iocoder.boot</groupId>
70 - <artifactId>yudao-spring-boot-starter-flowable</artifactId>  
71 - <version>${revision}</version> 70 + <artifactId>yudao-spring-boot-starter-excel</artifactId>
72 </dependency> 71 </dependency>
  72 + <!-- Flowable 工作流相关 -->
73 <dependency> 73 <dependency>
74 - <groupId>cn.iocoder.boot</groupId>  
75 - <artifactId>yudao-spring-boot-starter-excel</artifactId> 74 + <groupId>org.flowable</groupId>
  75 + <artifactId>flowable-spring-boot-starter-process</artifactId>
  76 + </dependency>
  77 + <dependency>
  78 + <groupId>org.flowable</groupId>
  79 + <artifactId>flowable-spring-boot-starter-actuator</artifactId>
76 </dependency> 80 </dependency>
77 </dependencies> 81 </dependencies>
78 </project> 82 </project>
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
@@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.common.util.collection.MapUtils; 4 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
5 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 5 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 -import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils; 7 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
8 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageItemRespVO; 8 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageItemRespVO;
9 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO; 9 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
10 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; 10 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
@@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 6 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 -import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils; 9 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
10 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO; 10 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
11 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; 11 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
12 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; 12 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
yudao-module-bpm/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/context/FlowableContextHolder.java renamed to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/FlowableContextHolder.java
1 -package cn.iocoder.yudao.framework.flowable.core.context; 1 +package cn.iocoder.yudao.module.bpm.framework;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import com.alibaba.ttl.TransmittableThreadLocal; 4 import com.alibaba.ttl.TransmittableThreadLocal;
@@ -10,6 +10,7 @@ import java.util.Map; @@ -10,6 +10,7 @@ import java.util.Map;
10 /** 10 /**
11 * 工作流--用户用到的上下文相关信息 11 * 工作流--用户用到的上下文相关信息
12 */ 12 */
  13 +@Deprecated // TODO 芋艿:找个方式,去掉这个上下文
13 public class FlowableContextHolder { 14 public class FlowableContextHolder {
14 15
15 private static final ThreadLocal<Map<String, List<Long>>> ASSIGNEE = new TransmittableThreadLocal<>(); 16 private static final ThreadLocal<Map<String, List<Long>>> ASSIGNEE = new TransmittableThreadLocal<>();
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/config/BpmCommonConfiguration.java deleted
1 -package cn.iocoder.yudao.module.bpm.framework.bpm.config;  
2 -  
3 -import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher;  
4 -import org.springframework.context.ApplicationEventPublisher;  
5 -import org.springframework.context.annotation.Bean;  
6 -import org.springframework.context.annotation.Configuration;  
7 -  
8 -/**  
9 - * BPM 通用的 Configuration 配置类,提供给 Activiti 和 Flowable  
10 - */  
11 -@Configuration(proxyBeanMethods = false)  
12 -public class BpmCommonConfiguration {  
13 -  
14 - @Bean  
15 - public BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher(ApplicationEventPublisher publisher) {  
16 - return new BpmProcessInstanceResultEventPublisher(publisher);  
17 - }  
18 -  
19 -}  
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/core/event/package-info.java deleted
1 -/**  
2 - * 自定义 Event 实现,提供方便业务接入的 Listener!  
3 - *  
4 - * @author 芋道源码  
5 - */  
6 -package cn.iocoder.yudao.module.bpm.framework.bpm.core.event;  
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/core/package-info.java deleted
1 -/**  
2 - * 占位  
3 - */  
4 -package cn.iocoder.yudao.module.bpm.framework.bpm.core;  
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/package-info.java deleted
1 -/**  
2 - * 提供给 Activiti 和 Flowable 的通用封装  
3 - *  
4 - * @author 芋道源码  
5 - */  
6 -package cn.iocoder.yudao.module.bpm.framework.bpm;  
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java
@@ -4,13 +4,18 @@ import cn.hutool.core.collection.ListUtil; @@ -4,13 +4,18 @@ import cn.hutool.core.collection.ListUtil;
4 import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.BpmActivityBehaviorFactory; 4 import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.BpmActivityBehaviorFactory;
5 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; 5 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
6 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; 6 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
  7 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceResultEventPublisher;
7 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 8 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
8 import org.flowable.common.engine.api.delegate.event.FlowableEventListener; 9 import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
9 import org.flowable.spring.SpringProcessEngineConfiguration; 10 import org.flowable.spring.SpringProcessEngineConfiguration;
10 import org.flowable.spring.boot.EngineConfigurationConfigurer; 11 import org.flowable.spring.boot.EngineConfigurationConfigurer;
11 import org.springframework.beans.factory.ObjectProvider; 12 import org.springframework.beans.factory.ObjectProvider;
  13 +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
  14 +import org.springframework.context.ApplicationEventPublisher;
12 import org.springframework.context.annotation.Bean; 15 import org.springframework.context.annotation.Bean;
13 import org.springframework.context.annotation.Configuration; 16 import org.springframework.context.annotation.Configuration;
  17 +import org.springframework.core.task.AsyncListenableTaskExecutor;
  18 +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
14 19
15 import java.util.List; 20 import java.util.List;
16 21
@@ -23,6 +28,26 @@ import java.util.List; @@ -23,6 +28,26 @@ import java.util.List;
23 public class BpmFlowableConfiguration { 28 public class BpmFlowableConfiguration {
24 29
25 /** 30 /**
  31 + * 参考 {@link org.flowable.spring.boot.FlowableJobConfiguration} 类,创建对应的 AsyncListenableTaskExecutor Bean
  32 + *
  33 + * 如果不创建,会导致项目启动时,Flowable 报错的问题
  34 + */
  35 + @Bean(name = "applicationTaskExecutor")
  36 + @ConditionalOnMissingBean(name = "applicationTaskExecutor")
  37 + public AsyncListenableTaskExecutor taskExecutor() {
  38 + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  39 + executor.setCorePoolSize(8);
  40 + executor.setMaxPoolSize(8);
  41 + executor.setQueueCapacity(100);
  42 + executor.setThreadNamePrefix("flowable-task-Executor-");
  43 + executor.setAwaitTerminationSeconds(30);
  44 + executor.setWaitForTasksToCompleteOnShutdown(true);
  45 + executor.setAllowCoreThreadTimeOut(true);
  46 + executor.initialize();
  47 + return executor;
  48 + }
  49 +
  50 + /**
26 * BPM 模块的 ProcessEngineConfigurationConfigurer 实现类: 51 * BPM 模块的 ProcessEngineConfigurationConfigurer 实现类:
27 * 52 *
28 * 1. 设置各种监听器 53 * 1. 设置各种监听器
@@ -56,4 +81,11 @@ public class BpmFlowableConfiguration { @@ -56,4 +81,11 @@ public class BpmFlowableConfiguration {
56 return new BpmTaskCandidateInvoker(strategyList, adminUserApi); 81 return new BpmTaskCandidateInvoker(strategyList, adminUserApi);
57 } 82 }
58 83
  84 + // =========== 自己拓展的 Bean ==========
  85 +
  86 + @Bean
  87 + public BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher(ApplicationEventPublisher publisher) {
  88 + return new BpmProcessInstanceResultEventPublisher(publisher);
  89 + }
  90 +
59 } 91 }
60 \ No newline at end of file 92 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java
1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;
2 2
3 -import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils; 3 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
4 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; 4 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
5 import lombok.Setter; 5 import lombok.Setter;
6 import org.flowable.bpmn.model.Activity; 6 import org.flowable.bpmn.model.Activity;
@@ -43,9 +43,9 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav @@ -43,9 +43,9 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav
43 // 第一步,设置 collectionVariable 和 CollectionVariable 43 // 第一步,设置 collectionVariable 和 CollectionVariable
44 // 从 execution.getVariable() 读取所有任务处理人的 key 44 // 从 execution.getVariable() 读取所有任务处理人的 key
45 super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的 45 super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的
46 - super.collectionVariable = FlowableUtils.formatCollectionVariable(execution.getCurrentActivityId()); 46 + super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId());
47 // 从 execution.getVariable() 读取当前所有任务处理的人的 key 47 // 从 execution.getVariable() 读取当前所有任务处理的人的 key
48 - super.collectionElementVariable = FlowableUtils.formatCollectionElementVariable(execution.getCurrentActivityId()); 48 + super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
49 49
50 // 第二步,获取任务的所有处理人 50 // 第二步,获取任务的所有处理人
51 Set<Long> assigneeUserIds = taskCandidateInvoker.calculateUsers(execution); 51 Set<Long> assigneeUserIds = taskCandidateInvoker.calculateUsers(execution);
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java
1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;
2 2
3 -import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils; 3 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
4 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; 4 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
5 import lombok.Setter; 5 import lombok.Setter;
6 import org.flowable.bpmn.model.Activity; 6 import org.flowable.bpmn.model.Activity;
@@ -37,9 +37,9 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB @@ -37,9 +37,9 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB
37 // 第一步,设置 collectionVariable 和 CollectionVariable 37 // 第一步,设置 collectionVariable 和 CollectionVariable
38 // 从 execution.getVariable() 读取所有任务处理人的 key 38 // 从 execution.getVariable() 读取所有任务处理人的 key
39 super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的 39 super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的
40 - super.collectionVariable = FlowableUtils.formatCollectionVariable(execution.getCurrentActivityId()); 40 + super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId());
41 // 从 execution.getVariable() 读取当前所有任务处理的人的 key 41 // 从 execution.getVariable() 读取当前所有任务处理的人的 key
42 - super.collectionElementVariable = FlowableUtils.formatCollectionElementVariable(execution.getCurrentActivityId()); 42 + super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
43 43
44 // 第二步,获取任务的所有处理人 44 // 第二步,获取任务的所有处理人
45 Set<Long> assigneeUserIds = new LinkedHashSet<>(taskCandidateInvoker.calculateUsers(execution)); // 保证有序!!! 45 Set<Long> assigneeUserIds = new LinkedHashSet<>(taskCandidateInvoker.calculateUsers(execution)); // 保证有序!!!
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java
@@ -5,7 +5,7 @@ import cn.hutool.core.lang.Assert; @@ -5,7 +5,7 @@ import cn.hutool.core.lang.Assert;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
8 -import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils; 8 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
9 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; 9 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
10 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; 10 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
11 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 11 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateExpressionStrategy.java
1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
2 2
3 -import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils; 3 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
4 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; 4 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
5 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; 5 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
6 import org.dromara.hutool.core.convert.Convert; 6 import org.dromara.hutool.core.convert.Convert;
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/core/event/BpmProcessInstanceResultEventPublisher.java renamed to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/event/BpmProcessInstanceResultEventPublisher.java
1 -package cn.iocoder.yudao.module.bpm.framework.bpm.core.event; 1 +package cn.iocoder.yudao.module.bpm.framework.flowable.core.event;
2 2
3 import cn.iocoder.yudao.module.bpm.event.BpmProcessInstanceResultEvent; 3 import cn.iocoder.yudao.module.bpm.event.BpmProcessInstanceResultEvent;
4 import lombok.AllArgsConstructor; 4 import lombok.AllArgsConstructor;
yudao-module-bpm/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/util/BpmnModelUtils.java renamed to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
1 -package cn.iocoder.yudao.framework.flowable.core.util; 1 +package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ArrayUtil; 4 import cn.hutool.core.util.ArrayUtil;
yudao-module-bpm/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/util/FlowableUtils.java renamed to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java
1 -package cn.iocoder.yudao.framework.flowable.core.util; 1 +package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
2 2
  3 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants;
3 import org.flowable.common.engine.api.delegate.Expression; 4 import org.flowable.common.engine.api.delegate.Expression;
4 import org.flowable.common.engine.api.variable.VariableContainer; 5 import org.flowable.common.engine.api.variable.VariableContainer;
5 import org.flowable.common.engine.impl.el.ExpressionManager; 6 import org.flowable.common.engine.impl.el.ExpressionManager;
@@ -32,11 +33,23 @@ public class FlowableUtils { @@ -32,11 +33,23 @@ public class FlowableUtils {
32 33
33 // ========== Execution 相关的工具方法 ========== 34 // ========== Execution 相关的工具方法 ==========
34 35
35 - public static String formatCollectionVariable(String activityId) { 36 + /**
  37 + * 格式化多实例(并签、或签)的 collectionVariable 变量(多实例对应的多审批人列表)
  38 + *
  39 + * @param activityId 活动编号
  40 + * @return collectionVariable 变量
  41 + */
  42 + public static String formatExecutionCollectionVariable(String activityId) {
36 return activityId + "_assignees"; 43 return activityId + "_assignees";
37 } 44 }
38 45
39 - public static String formatCollectionElementVariable(String activityId) { 46 + /**
  47 + * 格式化多实例(并签、或签)的 collectionElementVariable 变量(当前实例对应的一个审批人)
  48 + *
  49 + * @param activityId 活动编号
  50 + * @return collectionElementVariable 变量
  51 + */
  52 + public static String formatExecutionCollectionElementVariable(String activityId) {
40 return activityId + "_assignee"; 53 return activityId + "_assignee";
41 } 54 }
42 55
@@ -50,43 +63,86 @@ public class FlowableUtils { @@ -50,43 +63,86 @@ public class FlowableUtils {
50 return getProcessInstanceStatus(processInstance.getProcessVariables()); 63 return getProcessInstanceStatus(processInstance.getProcessVariables());
51 } 64 }
52 65
53 - // TODO 芋艿:需要再搞搞 66 + /**
  67 + * 获得流程实例的状态
  68 + *
  69 + * @param processVariables 流程实例的 variables
  70 + * @return 状态
  71 + */
54 private static Integer getProcessInstanceStatus(Map<String, Object> processVariables) { 72 private static Integer getProcessInstanceStatus(Map<String, Object> processVariables) {
55 - return (Integer) processVariables.get("PROCESS_STATUS"); 73 + return (Integer) processVariables.get(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
56 } 74 }
57 75
  76 + /**
  77 + * 获得流程实例的表单
  78 + *
  79 + * @param processInstance 流程实例
  80 + * @return 表单
  81 + */
58 public static Map<String, Object> getProcessInstanceFormVariable(ProcessInstance processInstance) { 82 public static Map<String, Object> getProcessInstanceFormVariable(ProcessInstance processInstance) {
59 Map<String, Object> formVariables = new HashMap<>(processInstance.getProcessVariables()); 83 Map<String, Object> formVariables = new HashMap<>(processInstance.getProcessVariables());
60 filterProcessInstanceFormVariable(formVariables); 84 filterProcessInstanceFormVariable(formVariables);
61 return formVariables; 85 return formVariables;
62 } 86 }
63 87
  88 + /**
  89 + * 过滤流程实例的表单
  90 + *
  91 + * 为什么要过滤?目前使用 processVariables 存储所有流程实例的拓展字段,需要过滤掉一部分的系统字段,从而实现表单的展示
  92 + *
  93 + * @param processVariables 流程实例的 variables
  94 + * @return 过滤后的表单
  95 + */
64 public static Map<String, Object> filterProcessInstanceFormVariable(Map<String, Object> processVariables) { 96 public static Map<String, Object> filterProcessInstanceFormVariable(Map<String, Object> processVariables) {
65 - processVariables.remove("PROCESS_STATUS"); 97 + processVariables.remove(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
66 return processVariables; 98 return processVariables;
67 } 99 }
68 100
69 // ========== Task 相关的工具方法 ========== 101 // ========== Task 相关的工具方法 ==========
70 102
71 - // TODO 芋艿:需要再搞搞  
72 - 103 + /**
  104 + * 获得任务的状态
  105 + *
  106 + * @param task 任务
  107 + * @return 状态
  108 + */
73 public static Integer getTaskStatus(TaskInfo task) { 109 public static Integer getTaskStatus(TaskInfo task) {
74 - return (Integer) task.getTaskLocalVariables().get("TASK_STATUS"); 110 + return (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS);
75 } 111 }
76 112
  113 + /**
  114 + * 获得任务的审批原因
  115 + *
  116 + * @param task 任务
  117 + * @return 审批原因
  118 + */
77 public static String getTaskReason(TaskInfo task) { 119 public static String getTaskReason(TaskInfo task) {
78 - return (String) task.getTaskLocalVariables().get("TASK_REASON"); 120 + return (String) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_REASON);
79 } 121 }
80 122
  123 + /**
  124 + * 获得任务的表单
  125 + *
  126 + * @param task 任务
  127 + * @return 表单
  128 + */
81 public static Map<String, Object> getTaskFormVariable(TaskInfo task) { 129 public static Map<String, Object> getTaskFormVariable(TaskInfo task) {
82 Map<String, Object> formVariables = new HashMap<>(task.getTaskLocalVariables()); 130 Map<String, Object> formVariables = new HashMap<>(task.getTaskLocalVariables());
83 filterTaskFormVariable(formVariables); 131 filterTaskFormVariable(formVariables);
84 return formVariables; 132 return formVariables;
85 } 133 }
86 134
  135 + /**
  136 + * 过滤任务的表单
  137 + *
  138 + * 为什么要过滤?目前使用 taskLocalVariables 存储所有任务的拓展字段,需要过滤掉一部分的系统字段,从而实现表单的展示
  139 + *
  140 + * @param taskLocalVariables 任务的 taskLocalVariables
  141 + * @return 过滤后的表单
  142 + */
87 public static Map<String, Object> filterTaskFormVariable(Map<String, Object> taskLocalVariables) { 143 public static Map<String, Object> filterTaskFormVariable(Map<String, Object> taskLocalVariables) {
88 - taskLocalVariables.remove("TASK_STATUS");  
89 - taskLocalVariables.remove("TASK_REASON"); 144 + taskLocalVariables.remove(BpmConstants.TASK_VARIABLE_STATUS);
  145 + taskLocalVariables.remove(BpmConstants.TASK_VARIABLE_REASON);
90 return taskLocalVariables; 146 return taskLocalVariables;
91 } 147 }
92 148
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/web/config/BpmWebConfiguration.java
1 package cn.iocoder.yudao.module.bpm.framework.web.config; 1 package cn.iocoder.yudao.module.bpm.framework.web.config;
2 2
  3 +import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
3 import cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration; 4 import cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration;
  5 +import cn.iocoder.yudao.module.bpm.framework.web.core.FlowableWebFilter;
4 import org.springdoc.core.models.GroupedOpenApi; 6 import org.springdoc.core.models.GroupedOpenApi;
  7 +import org.springframework.boot.web.servlet.FilterRegistrationBean;
5 import org.springframework.context.annotation.Bean; 8 import org.springframework.context.annotation.Bean;
6 import org.springframework.context.annotation.Configuration; 9 import org.springframework.context.annotation.Configuration;
7 10
@@ -21,4 +24,15 @@ public class BpmWebConfiguration { @@ -21,4 +24,15 @@ public class BpmWebConfiguration {
21 return YudaoSwaggerAutoConfiguration.buildGroupedOpenApi("bpm"); 24 return YudaoSwaggerAutoConfiguration.buildGroupedOpenApi("bpm");
22 } 25 }
23 26
  27 + /**
  28 + * 配置 Flowable Web 过滤器
  29 + */
  30 + @Bean
  31 + public FilterRegistrationBean<FlowableWebFilter> flowableWebFilter() {
  32 + FilterRegistrationBean<FlowableWebFilter> registrationBean = new FilterRegistrationBean<>();
  33 + registrationBean.setFilter(new FlowableWebFilter());
  34 + registrationBean.setOrder(WebFilterOrderEnum.FLOWABLE_FILTER);
  35 + return registrationBean;
  36 + }
  37 +
24 } 38 }
yudao-module-bpm/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/web/FlowableWebFilter.java renamed to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/web/core/FlowableWebFilter.java
1 -package cn.iocoder.yudao.framework.flowable.core.web; 1 +package cn.iocoder.yudao.module.bpm.framework.web.core;
2 2
3 -import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;  
4 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; 3 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
5 -import org.springframework.web.filter.OncePerRequestFilter;  
6 - 4 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
7 import jakarta.servlet.FilterChain; 5 import jakarta.servlet.FilterChain;
8 import jakarta.servlet.ServletException; 6 import jakarta.servlet.ServletException;
9 import jakarta.servlet.http.HttpServletRequest; 7 import jakarta.servlet.http.HttpServletRequest;
10 import jakarta.servlet.http.HttpServletResponse; 8 import jakarta.servlet.http.HttpServletResponse;
  9 +import org.springframework.web.filter.OncePerRequestFilter;
  10 +
11 import java.io.IOException; 11 import java.io.IOException;
  12 +
12 /** 13 /**
13 - * flowable Web 过滤器,将 userId 设置到 {@link org.flowable.common.engine.impl.identity.Authentication} 中 14 + * Flowable Web 过滤器,将 userId 设置到 {@link org.flowable.common.engine.impl.identity.Authentication} 中
14 * 15 *
15 * @author jason 16 * @author jason
16 */ 17 */
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
1 -package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.framework.flowable.core.context.FlowableContextHolder; import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceMyPageReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum; 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.framework.flowable.core.enums.BpmConstants; 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 jakarta.annotation.Resource; import jakarta.validation.Valid; 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.history.HistoricProcessInstanceQuery; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private HistoryService historyService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private BpmMessageService messageService; @Resource private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery() .includeProcessVariables() .processInstanceId(id) .singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<HistoricProcessInstance> getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery().includeProcessVariables() .startedBy(String.valueOf(userId)) .orderByProcessInstanceStartTime().desc(); if (StrUtil.isNotEmpty(pageReqVO.getName())) { processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionId())) { processInstanceQuery.processDefinitionId("%" + pageReqVO.getProcessDefinitionId() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getCategory())) { processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory()); } if (pageReqVO.getStatus() != null) { processInstanceQuery.variableValueEquals(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus()); } if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) { processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0])); processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1])); } // 查询数量 long processInstanceCount = processInstanceQuery.count(); if (processInstanceCount == 0) { return PageResult.empty(processInstanceCount); } // 查询列表 List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize()); return new PageResult<>(processInstanceList, processInstanceCount); } @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, createReqVO.getAssignee()); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), createReqDTO.getAssignee()); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey, Map<String, List<Long>> assignee) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 设置上下文信息 // TODO @hai:要不往 variables 存到一个全局固定 key 里,减少对上下文的依赖 FlowableContextHolder.setAssignee(assignee); // 创建流程实例 FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用 variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); return instance.getId(); } @Override public BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionInfoDO processDefinitionExt = processDefinitionService.getProcessDefinitionInfo( 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, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @Override public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 2. 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询 deleteProcessInstance(cancelReqVO.getId(), BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE.format(cancelReqVO.getReason())); // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 } @Override public void updateProcessInstanceWhenCancel(FlowableCancelledEvent event) { // 1. 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceReject 方法(审批不通过),已经进行更新了 if (BpmDeleteReasonEnum.isRejectReason((String) event.getCause())) { return; } // 2. 更新流程实例 status runtimeService.setVariable(event.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.CANCEL.getStatus()); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, BpmProcessInstanceStatusEnum.CANCEL.getStatus())); } @Override public void updateProcessInstanceWhenApprove(ProcessInstance instance) { // 1. 更新流程实例 status runtimeService.setVariable(instance.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.APPROVE.getStatus()); // 2. 发送流程被【通过】的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance)); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, BpmProcessInstanceStatusEnum.APPROVE.getStatus())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceReject(String id, String reason) { // 1. 更新流程实例 status runtimeService.setVariable(id, BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus()); // 2. 删除流程实例,以实现驳回任务时,取消整个审批流程 ProcessInstance processInstance = getProcessInstance(id); deleteProcessInstance(id, StrUtil.format(BpmDeleteReasonEnum.REJECT_TASK.format(reason))); // 3. 发送流程被【不通过】的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason)); // 4. 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, BpmProcessInstanceStatusEnum.REJECT.getStatus())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } @Override public List<Long> getAssigneeByProcessInstanceIdAndTaskDefinitionKey(String processInstanceId, String taskDefinitionKey) { // 1. 先从上下文获取,首次提交数据库中查询不到 List<Long> result = FlowableContextHolder.getAssigneeByTaskDefinitionKey(taskDefinitionKey); if (CollUtil.isNotEmpty(result)) { return result; } // 2. 从数据库中获取 // TODO @芋艿:指定审批人,这里的存储方案有问题,后续优化下 // BpmProcessInstanceExtDO instance = processInstanceExtMapper.selectByProcessInstanceId(processInstanceId); // if (instance == null) { // throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); // } // if (CollUtil.isNotEmpty(instance.getAssignee())) { // return instance.getAssignee().get(taskDefinitionKey); // } return Collections.emptyList(); } }  
2 \ No newline at end of file 1 \ No newline at end of file
  2 +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.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.framework.FlowableContextHolder; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceMyPageReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceResultEventPublisher; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; 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 jakarta.annotation.Resource; import jakarta.validation.Valid; 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.history.HistoricProcessInstanceQuery; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private HistoryService historyService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private BpmMessageService messageService; @Resource private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery() .includeProcessVariables() .processInstanceId(id) .singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<HistoricProcessInstance> getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery().includeProcessVariables() .startedBy(String.valueOf(userId)) .orderByProcessInstanceStartTime().desc(); if (StrUtil.isNotEmpty(pageReqVO.getName())) { processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionId())) { processInstanceQuery.processDefinitionId("%" + pageReqVO.getProcessDefinitionId() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getCategory())) { processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory()); } if (pageReqVO.getStatus() != null) { processInstanceQuery.variableValueEquals(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus()); } if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) { processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0])); processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1])); } // 查询数量 long processInstanceCount = processInstanceQuery.count(); if (processInstanceCount == 0) { return PageResult.empty(processInstanceCount); } // 查询列表 List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize()); return new PageResult<>(processInstanceList, processInstanceCount); } @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, createReqVO.getAssignee()); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), createReqDTO.getAssignee()); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey, Map<String, List<Long>> assignee) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 设置上下文信息 // TODO @hai:要不往 variables 存到一个全局固定 key 里,减少对上下文的依赖 FlowableContextHolder.setAssignee(assignee); // 创建流程实例 FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用 variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); return instance.getId(); } @Override public BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionInfoDO processDefinitionExt = processDefinitionService.getProcessDefinitionInfo( 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, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @Override public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 2. 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询 deleteProcessInstance(cancelReqVO.getId(), BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE.format(cancelReqVO.getReason())); // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 } @Override public void updateProcessInstanceWhenCancel(FlowableCancelledEvent event) { // 1. 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceReject 方法(审批不通过),已经进行更新了 if (BpmDeleteReasonEnum.isRejectReason((String) event.getCause())) { return; } // 2. 更新流程实例 status runtimeService.setVariable(event.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.CANCEL.getStatus()); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, BpmProcessInstanceStatusEnum.CANCEL.getStatus())); } @Override public void updateProcessInstanceWhenApprove(ProcessInstance instance) { // 1. 更新流程实例 status runtimeService.setVariable(instance.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.APPROVE.getStatus()); // 2. 发送流程被【通过】的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance)); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, BpmProcessInstanceStatusEnum.APPROVE.getStatus())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceReject(String id, String reason) { // 1. 更新流程实例 status runtimeService.setVariable(id, BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus()); // 2. 删除流程实例,以实现驳回任务时,取消整个审批流程 ProcessInstance processInstance = getProcessInstance(id); deleteProcessInstance(id, StrUtil.format(BpmDeleteReasonEnum.REJECT_TASK.format(reason))); // 3. 发送流程被【不通过】的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason)); // 4. 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, BpmProcessInstanceStatusEnum.REJECT.getStatus())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } @Override public List<Long> getAssigneeByProcessInstanceIdAndTaskDefinitionKey(String processInstanceId, String taskDefinitionKey) { // 1. 先从上下文获取,首次提交数据库中查询不到 List<Long> result = FlowableContextHolder.getAssigneeByTaskDefinitionKey(taskDefinitionKey); if (CollUtil.isNotEmpty(result)) { return result; } // 2. 从数据库中获取 // TODO @芋艿:指定审批人,这里的存储方案有问题,后续优化下 // BpmProcessInstanceExtDO instance = processInstanceExtMapper.selectByProcessInstanceId(processInstanceId); // if (instance == null) { // throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); // } // if (CollUtil.isNotEmpty(instance.getAssignee())) { // return instance.getAssignee().get(taskDefinitionKey); // } return Collections.emptyList(); } }
3 \ No newline at end of file 3 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
@@ -9,8 +9,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -9,8 +9,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 import cn.iocoder.yudao.framework.common.util.date.DateUtils; 9 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
10 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 10 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
11 import cn.iocoder.yudao.framework.common.util.object.PageUtils; 11 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
12 -import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;  
13 -import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils; 12 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
  13 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
14 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; 14 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
15 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; 15 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
16 import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; 16 import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateExpressionStrategyTest.java
1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
2 2
3 -import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils; 3 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
4 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; 4 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
5 import org.flowable.engine.delegate.DelegateExecution; 5 import org.flowable.engine.delegate.DelegateExecution;
6 import org.junit.jupiter.api.Test; 6 import org.junit.jupiter.api.Test;
yudao-module-bpm/yudao-spring-boot-starter-flowable/pom.xml deleted
1 -<?xml version="1.0" encoding="UTF-8"?>  
2 -<project xmlns="http://maven.apache.org/POM/4.0.0"  
3 - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
4 - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
5 - <parent>  
6 - <groupId>cn.iocoder.boot</groupId>  
7 - <artifactId>yudao-module-bpm</artifactId>  
8 - <version>${revision}</version>  
9 - </parent>  
10 - <modelVersion>4.0.0</modelVersion>  
11 -  
12 - <artifactId>yudao-spring-boot-starter-flowable</artifactId>  
13 -  
14 - <dependencies>  
15 - <dependency>  
16 - <groupId>cn.iocoder.boot</groupId>  
17 - <artifactId>yudao-common</artifactId>  
18 - </dependency>  
19 -  
20 - <!-- Web 相关 -->  
21 - <dependency>  
22 - <groupId>cn.iocoder.boot</groupId>  
23 - <artifactId>yudao-spring-boot-starter-security</artifactId>  
24 - </dependency>  
25 -  
26 - <!-- flowable 工作流相关 -->  
27 - <dependency>  
28 - <groupId>org.flowable</groupId>  
29 - <artifactId>flowable-spring-boot-starter-process</artifactId>  
30 - </dependency>  
31 - <dependency>  
32 - <groupId>org.flowable</groupId>  
33 - <artifactId>flowable-spring-boot-starter-actuator</artifactId>  
34 - </dependency>  
35 - </dependencies>  
36 -  
37 -</project>  
yudao-module-bpm/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/config/YudaoFlowableConfiguration.java deleted
1 -package cn.iocoder.yudao.framework.flowable.config;  
2 -  
3 -import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;  
4 -import cn.iocoder.yudao.framework.flowable.core.web.FlowableWebFilter;  
5 -import org.springframework.boot.autoconfigure.AutoConfiguration;  
6 -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;  
7 -import org.springframework.boot.web.servlet.FilterRegistrationBean;  
8 -import org.springframework.context.annotation.Bean;  
9 -import org.springframework.core.task.AsyncListenableTaskExecutor;  
10 -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;  
11 -  
12 -@AutoConfiguration  
13 -public class YudaoFlowableConfiguration {  
14 -  
15 - /**  
16 - * 参考 {@link org.flowable.spring.boot.FlowableJobConfiguration} 类,创建对应的 AsyncListenableTaskExecutor Bean  
17 - *  
18 - * 如果不创建,会导致项目启动时,Flowable 报错的问题  
19 - */  
20 - @Bean(name = "applicationTaskExecutor")  
21 - @ConditionalOnMissingBean(name = "applicationTaskExecutor")  
22 - public AsyncListenableTaskExecutor taskExecutor() {  
23 - ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();  
24 - executor.setCorePoolSize(8);  
25 - executor.setMaxPoolSize(8);  
26 - executor.setQueueCapacity(100);  
27 - executor.setThreadNamePrefix("flowable-task-Executor-");  
28 - executor.setAwaitTerminationSeconds(30);  
29 - executor.setWaitForTasksToCompleteOnShutdown(true);  
30 - executor.setAllowCoreThreadTimeOut(true);  
31 - executor.initialize();  
32 - return executor;  
33 - }  
34 -  
35 - /**  
36 - * 配置 flowable Web 过滤器  
37 - */  
38 - @Bean  
39 - public FilterRegistrationBean<FlowableWebFilter> flowableWebFilter() {  
40 - FilterRegistrationBean<FlowableWebFilter> registrationBean = new FilterRegistrationBean<>();  
41 - registrationBean.setFilter(new FlowableWebFilter());  
42 - registrationBean.setOrder(WebFilterOrderEnum.FLOWABLE_FILTER);  
43 - return registrationBean;  
44 - }  
45 -  
46 -}  
yudao-module-bpm/yudao-spring-boot-starter-flowable/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted
1 -cn.iocoder.yudao.framework.flowable.config.YudaoFlowableConfiguration  
2 \ No newline at end of file 0 \ No newline at end of file