Commit 942f51dd594cb8c399bb871e1f5b7181f6c57e99

Authored by YunaiV
2 parents 8c6dc8fc 4347f133

Merge branch 'develop' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17

Showing 19 changed files with 248 additions and 122 deletions
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java
@@ -73,7 +73,7 @@ public class BpmApprovalDetailRespVO { @@ -73,7 +73,7 @@ public class BpmApprovalDetailRespVO {
73 private List<UserSimpleBaseVO> candidateUsers; // 只包含未生成 ApprovalTaskInfo 的用户列表 73 private List<UserSimpleBaseVO> candidateUsers; // 只包含未生成 ApprovalTaskInfo 的用户列表
74 74
75 @Schema(description = "流程编号", example = "8761d8e0-0922-11f0-bd37-00ff1db677bf") 75 @Schema(description = "流程编号", example = "8761d8e0-0922-11f0-bd37-00ff1db677bf")
76 - private String processInstanceId; // 当且仅当,该节点是子流程节点时,才会有值(CallActivity 的 processInstanceId 字段) 76 + private String processInstanceId; // 当且仅当,该节点是子流程节点时,才会有值(CallActivity 的 calledProcessInstanceId 字段)
77 77
78 } 78 }
79 79
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
@@ -74,6 +74,7 @@ public interface ErrorCodeConstants { @@ -74,6 +74,7 @@ public interface ErrorCodeConstants {
74 ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1_009_012_000, "流程分类不存在"); 74 ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1_009_012_000, "流程分类不存在");
75 ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(1_009_012_001, "流程分类名字【{}】重复"); 75 ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(1_009_012_001, "流程分类名字【{}】重复");
76 ErrorCode CATEGORY_CODE_DUPLICATE = new ErrorCode(1_009_012_002, "流程分类编码【{}】重复"); 76 ErrorCode CATEGORY_CODE_DUPLICATE = new ErrorCode(1_009_012_002, "流程分类编码【{}】重复");
  77 + ErrorCode CATEGORY_DELETE_FAIL_MODEL_USED = new ErrorCode(1_009_012_003, "删除失败,流程分类【{}】已被流程模型使用,请先删除对应的流程模型");
77 78
78 // ========== BPM 流程监听器 1-009-013-000 ========== 79 // ========== BPM 流程监听器 1-009-013-000 ==========
79 ErrorCode PROCESS_LISTENER_NOT_EXISTS = new ErrorCode(1_009_013_000, "流程监听器不存在"); 80 ErrorCode PROCESS_LISTENER_NOT_EXISTS = new ErrorCode(1_009_013_000, "流程监听器不存在");
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java
@@ -35,7 +35,7 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable&lt;Integer&gt; { @@ -35,7 +35,7 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable&lt;Integer&gt; {
35 // 50 ~ 条件分支 35 // 50 ~ 条件分支
36 CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式 36 CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式
37 CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"), 37 CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"),
38 - PARALLEL_BRANCH_NODE(52, "并行分支", "parallelGateway"), 38 + PARALLEL_BRANCH_NODE(52, "并行分支", "inclusiveGateway"), // 并行分支使用包容网关实现,条件表达式结果设置为 true
39 INCLUSIVE_BRANCH_NODE(53, "包容分支", "inclusiveGateway"), 39 INCLUSIVE_BRANCH_NODE(53, "包容分支", "inclusiveGateway"),
40 ROUTER_BRANCH_NODE(54, "路由分支", "exclusiveGateway") 40 ROUTER_BRANCH_NODE(54, "路由分支", "exclusiveGateway")
41 ; 41 ;
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java
@@ -18,10 +18,7 @@ import cn.iocoder.yudao.module.system.api.user.AdminUserApi; @@ -18,10 +18,7 @@ import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
18 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 18 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
19 import com.google.common.annotations.VisibleForTesting; 19 import com.google.common.annotations.VisibleForTesting;
20 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
21 -import org.flowable.bpmn.model.BpmnModel;  
22 -import org.flowable.bpmn.model.CallActivity;  
23 -import org.flowable.bpmn.model.FlowElement;  
24 -import org.flowable.bpmn.model.UserTask; 21 +import org.flowable.bpmn.model.*;
25 import org.flowable.engine.delegate.DelegateExecution; 22 import org.flowable.engine.delegate.DelegateExecution;
26 import org.flowable.engine.runtime.ProcessInstance; 23 import org.flowable.engine.runtime.ProcessInstance;
27 24
@@ -132,7 +129,7 @@ public class BpmTaskCandidateInvoker { @@ -132,7 +129,7 @@ public class BpmTaskCandidateInvoker {
132 Long startUserId, String processDefinitionId, Map<String, Object> processVariables) { 129 Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
133 // 如果是 CallActivity 子流程,不进行计算候选人 130 // 如果是 CallActivity 子流程,不进行计算候选人
134 FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId); 131 FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId);
135 - if (flowElement instanceof CallActivity) { 132 + if (flowElement instanceof CallActivity || flowElement instanceof SubProcess) {
136 return new HashSet<>(); 133 return new HashSet<>();
137 } 134 }
138 // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过 135 // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java
@@ -24,6 +24,7 @@ import static java.util.Collections.emptySet; @@ -24,6 +24,7 @@ import static java.util.Collections.emptySet;
24 * @author 芋道源码 24 * @author 芋道源码
25 */ 25 */
26 @Component 26 @Component
  27 +@Deprecated // 仅仅是表达式的示例,建议使用 BpmTaskCandidateStartUserDeptLeaderStrategy 替代
27 public class BpmTaskAssignLeaderExpression { 28 public class BpmTaskAssignLeaderExpression {
28 29
29 @Resource 30 @Resource
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java
@@ -16,6 +16,7 @@ import java.util.Set; @@ -16,6 +16,7 @@ import java.util.Set;
16 * @author 芋道源码 16 * @author 芋道源码
17 */ 17 */
18 @Component 18 @Component
  19 +@Deprecated // 仅仅是表达式的示例,建议使用 BpmTaskCandidateStartUserStrategy 替代
19 public class BpmTaskAssignStartUserExpression { 20 public class BpmTaskAssignStartUserExpression {
20 21
21 @Resource 22 @Resource
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java
@@ -8,6 +8,7 @@ import com.google.common.collect.Sets; @@ -8,6 +8,7 @@ import com.google.common.collect.Sets;
8 import lombok.extern.slf4j.Slf4j; 8 import lombok.extern.slf4j.Slf4j;
9 import org.flowable.bpmn.model.BpmnModel; 9 import org.flowable.bpmn.model.BpmnModel;
10 import org.flowable.common.engine.api.FlowableException; 10 import org.flowable.common.engine.api.FlowableException;
  11 +import org.flowable.common.engine.impl.javax.el.PropertyNotFoundException;
11 import org.flowable.engine.delegate.DelegateExecution; 12 import org.flowable.engine.delegate.DelegateExecution;
12 import org.springframework.stereotype.Component; 13 import org.springframework.stereotype.Component;
13 14
@@ -48,10 +49,12 @@ public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrat @@ -48,10 +49,12 @@ public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrat
48 Object result = FlowableUtils.getExpressionValue(variables, param); 49 Object result = FlowableUtils.getExpressionValue(variables, param);
49 return CollectionUtils.toLinkedHashSet(Long.class, result); 50 return CollectionUtils.toLinkedHashSet(Long.class, result);
50 } catch (FlowableException ex) { 51 } catch (FlowableException ex) {
51 - // 预测未运行的节点时候,表达式如果包含 execution 或者不存在的流程变量会抛异常,  
52 - log.warn("[calculateUsersByActivity][表达式({}) 变量({}) 解析报错", param, variables, ex);  
53 - // 不能预测候选人,返回空列表, 避免流程无法进行  
54 - return Sets.newHashSet(); 52 + // 预测未运行的节点时候,表达式如果包含 execution 或者不存在的流程变量会抛异常,此时忽略该异常!相当于说,不做流程预测!!!
  53 + if (ex.getCause() != null && ex.getCause() instanceof PropertyNotFoundException) {
  54 + return Sets.newHashSet();
  55 + }
  56 + log.error("[calculateUsersByActivity][表达式({}) 变量({}) 解析报错", param, variables, ex);
  57 + throw ex;
55 } 58 }
56 } 59 }
57 60
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java
1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;
2 2
  3 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
3 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; 4 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
4 import com.google.common.collect.ImmutableSet; 5 import com.google.common.collect.ImmutableSet;
5 import jakarta.annotation.Resource; 6 import jakarta.annotation.Resource;
@@ -37,18 +38,26 @@ public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEvent @@ -37,18 +38,26 @@ public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEvent
37 38
38 @Override 39 @Override
39 protected void processCreated(FlowableEngineEntityEvent event) { 40 protected void processCreated(FlowableEngineEntityEvent event) {
40 - processInstanceService.processProcessInstanceCreated((ProcessInstance)event.getEntity()); 41 + ProcessInstance processInstance = (ProcessInstance) event.getEntity();
  42 + FlowableUtils.execute(processInstance.getTenantId(),
  43 + () -> processInstanceService.processProcessInstanceCreated(processInstance));
41 } 44 }
42 45
43 @Override 46 @Override
44 protected void processCompleted(FlowableEngineEntityEvent event) { 47 protected void processCompleted(FlowableEngineEntityEvent event) {
45 - processInstanceService.processProcessInstanceCompleted((ProcessInstance)event.getEntity()); 48 + ProcessInstance processInstance = (ProcessInstance) event.getEntity();
  49 + FlowableUtils.execute(processInstance.getTenantId(),
  50 + () -> processInstanceService.processProcessInstanceCompleted(processInstance));
46 } 51 }
47 52
48 - @Override // 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法 53 + @Override
49 protected void processCancelled(FlowableCancelledEvent event) { 54 protected void processCancelled(FlowableCancelledEvent event) {
  55 + // 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法
50 ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getProcessInstanceId()); 56 ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getProcessInstanceId());
51 - processInstanceService.processProcessInstanceCompleted(processInstance); 57 + if (processInstance != null) {
  58 + FlowableUtils.execute(processInstance.getTenantId(),
  59 + () -> processInstanceService.processProcessInstanceCompleted(processInstance));
  60 + }
52 } 61 }
53 62
54 } 63 }
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java
@@ -3,10 +3,12 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; @@ -3,10 +3,12 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjectUtil; 4 import cn.hutool.core.util.ObjectUtil;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
  6 +import cn.hutool.json.JSONObject;
6 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
7 import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventTypeEnum; 8 import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventTypeEnum;
8 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; 9 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
9 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; 10 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
  11 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
10 import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; 12 import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
11 import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; 13 import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
12 import com.google.common.collect.ImmutableSet; 14 import com.google.common.collect.ImmutableSet;
@@ -58,17 +60,20 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { @@ -58,17 +60,20 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
58 60
59 @Override 61 @Override
60 protected void taskCreated(FlowableEngineEntityEvent event) { 62 protected void taskCreated(FlowableEngineEntityEvent event) {
61 - taskService.processTaskCreated((Task) event.getEntity()); 63 + Task entity = (Task) event.getEntity();
  64 + FlowableUtils.execute(entity.getTenantId(), () -> taskService.processTaskCreated(entity));
62 } 65 }
63 66
64 @Override 67 @Override
65 protected void taskAssigned(FlowableEngineEntityEvent event) { 68 protected void taskAssigned(FlowableEngineEntityEvent event) {
66 - taskService.processTaskAssigned((Task) event.getEntity()); 69 + Task entity = (Task) event.getEntity();
  70 + FlowableUtils.execute(entity.getTenantId(), () -> taskService.processTaskAssigned(entity));
67 } 71 }
68 72
69 @Override 73 @Override
70 protected void taskCompleted(FlowableEngineEntityEvent event) { 74 protected void taskCompleted(FlowableEngineEntityEvent event) {
71 - taskService.processTaskCompleted((Task) event.getEntity()); 75 + Task entity = (Task) event.getEntity();
  76 + FlowableUtils.execute(entity.getTenantId(), () -> taskService.processTaskCompleted(entity));
72 } 77 }
73 78
74 @Override 79 @Override
@@ -94,6 +99,23 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { @@ -94,6 +99,23 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
94 String processDefinitionId = event.getProcessDefinitionId(); 99 String processDefinitionId = event.getProcessDefinitionId();
95 BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId); 100 BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId);
96 Job entity = (Job) event.getEntity(); 101 Job entity = (Job) event.getEntity();
  102 + // 特殊 from https://t.zsxq.com/h6oWr :当 elementId 为空时,尝试从 JobHandlerConfiguration 中解析 JSON 获取
  103 + String elementId = entity.getElementId();
  104 + if (elementId == null && entity.getJobHandlerConfiguration() != null) {
  105 + try {
  106 + String handlerConfig = entity.getJobHandlerConfiguration();
  107 + if (handlerConfig.startsWith("{") && handlerConfig.contains("activityId")) {
  108 + elementId = new JSONObject(handlerConfig).getStr("activityId");
  109 + }
  110 + } catch (Exception e) {
  111 + log.error("[timerFired][解析 entity({}) 失败]", entity, e);
  112 + return;
  113 + }
  114 + }
  115 + if (elementId == null) {
  116 + log.error("[timerFired][解析 entity({}) elementId 为空,跳过处理]", entity);
  117 + return;
  118 + }
97 FlowElement element = BpmnModelUtils.getFlowElementById(bpmnModel, entity.getElementId()); 119 FlowElement element = BpmnModelUtils.getFlowElementById(bpmnModel, entity.getElementId());
98 if (!(element instanceof BoundaryEvent)) { 120 if (!(element instanceof BoundaryEvent)) {
99 return; 121 return;
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java
@@ -108,7 +108,9 @@ public class BpmHttpRequestUtils { @@ -108,7 +108,9 @@ public class BpmHttpRequestUtils {
108 Map<String, Object> processVariables = processInstance.getProcessVariables(); 108 Map<String, Object> processVariables = processInstance.getProcessVariables();
109 MultiValueMap<String, String> body = new LinkedMultiValueMap<>(); 109 MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
110 addHttpRequestParam(body, bodySettings, processVariables); 110 addHttpRequestParam(body, bodySettings, processVariables);
111 - body.add("processInstanceId", processInstance.getId()); 111 + if (!body.containsKey("processInstanceId")) { // 避免重复添加
  112 + body.add("processInstanceId", processInstance.getId());
  113 + }
112 return body; 114 return body;
113 } 115 }
114 116
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
@@ -478,7 +478,11 @@ public class BpmnModelUtils { @@ -478,7 +478,11 @@ public class BpmnModelUtils {
478 */ 478 */
479 public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { 479 public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) {
480 Process process = model.getMainProcess(); 480 Process process = model.getMainProcess();
481 - return process.getFlowElement(flowElementId); 481 + FlowElement flowElement = process.getFlowElement(flowElementId);
  482 + if (flowElement != null) {
  483 + return flowElement;
  484 + }
  485 + return model.getFlowElement(flowElementId);
482 } 486 }
483 487
484 /** 488 /**
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
@@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil; @@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
5 import cn.hutool.core.map.MapUtil; 5 import cn.hutool.core.map.MapUtil;
6 import cn.hutool.core.util.*; 6 import cn.hutool.core.util.*;
7 -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;  
8 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 7 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
9 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; 8 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
10 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; 9 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups;
@@ -17,13 +16,14 @@ import cn.iocoder.yudao.module.bpm.service.task.listener.BpmCallActivityListener @@ -17,13 +16,14 @@ import cn.iocoder.yudao.module.bpm.service.task.listener.BpmCallActivityListener
17 import cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener; 16 import cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener;
18 import org.flowable.bpmn.BpmnAutoLayout; 17 import org.flowable.bpmn.BpmnAutoLayout;
19 import org.flowable.bpmn.constants.BpmnXMLConstants; 18 import org.flowable.bpmn.constants.BpmnXMLConstants;
20 -import org.flowable.bpmn.model.Process;  
21 import org.flowable.bpmn.model.*; 19 import org.flowable.bpmn.model.*;
  20 +import org.flowable.bpmn.model.Process;
22 import org.flowable.engine.delegate.ExecutionListener; 21 import org.flowable.engine.delegate.ExecutionListener;
23 import org.flowable.engine.delegate.TaskListener; 22 import org.flowable.engine.delegate.TaskListener;
24 23
25 import java.util.*; 24 import java.util.*;
26 25
  26 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
27 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; 27 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*;
28 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; 28 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*;
29 import static java.util.Arrays.asList; 29 import static java.util.Arrays.asList;
@@ -239,13 +239,13 @@ public class SimpleModelUtils { @@ -239,13 +239,13 @@ public class SimpleModelUtils {
239 // 3.1 分支有后续节点。即分支 1: A->B->C->D 的情况 239 // 3.1 分支有后续节点。即分支 1: A->B->C->D 的情况
240 if (isValidNode(conditionChildNode)) { 240 if (isValidNode(conditionChildNode)) {
241 // 3.1.1 建立与后续的节点的连线。例如说,建立 A->B 的连线 241 // 3.1.1 建立与后续的节点的连线。例如说,建立 A->B 的连线
242 - SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), conditionChildNode.getId(), item); 242 + SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), conditionChildNode.getId(), nodeType, item);
243 process.addFlowElement(sequenceFlow); 243 process.addFlowElement(sequenceFlow);
244 // 3.1.2 递归调用后续节点连线。例如说,建立 B->C->D 的连线 244 // 3.1.2 递归调用后续节点连线。例如说,建立 B->C->D 的连线
245 traverseNodeToBuildSequenceFlow(process, conditionChildNode, branchEndNodeId); 245 traverseNodeToBuildSequenceFlow(process, conditionChildNode, branchEndNodeId);
246 } else { 246 } else {
247 // 3.2 分支没有后续节点。例如说,建立 A->D 的连线 247 // 3.2 分支没有后续节点。例如说,建立 A->D 的连线
248 - SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), branchEndNodeId, item); 248 + SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), branchEndNodeId, nodeType, item);
249 process.addFlowElement(sequenceFlow); 249 process.addFlowElement(sequenceFlow);
250 } 250 }
251 } 251 }
@@ -591,17 +591,23 @@ public class SimpleModelUtils { @@ -591,17 +591,23 @@ public class SimpleModelUtils {
591 591
592 private static class ParallelBranchNodeConvert implements NodeConvert { 592 private static class ParallelBranchNodeConvert implements NodeConvert {
593 593
  594 + /**
  595 + * 并行分支使用包容网关。需要设置所有出口条件表达式的值为 true 。原因是,解决 https://t.zsxq.com/m6GXh 反馈问题
  596 + *
  597 + * @see {@link ConditionNodeConvert#buildSequenceFlow}
  598 + */
594 @Override 599 @Override
595 - public List<ParallelGateway> convertList(BpmSimpleModelNodeVO node) {  
596 - ParallelGateway parallelGateway = new ParallelGateway();  
597 - parallelGateway.setId(node.getId()); 600 + public List<InclusiveGateway> convertList(BpmSimpleModelNodeVO node) {
  601 +
  602 + InclusiveGateway inclusiveGateway = new InclusiveGateway();
  603 + inclusiveGateway.setId(node.getId());
598 // TODO @jason:setName 604 // TODO @jason:setName
599 605
600 - // 并行聚合网关由程序创建,前端不需要传入  
601 - ParallelGateway joinParallelGateway = new ParallelGateway(); 606 + // 合并网关 由程序创建,前端不需要传入
  607 + InclusiveGateway joinParallelGateway = new InclusiveGateway();
602 joinParallelGateway.setId(buildGatewayJoinId(node.getId())); 608 joinParallelGateway.setId(buildGatewayJoinId(node.getId()));
603 // TODO @jason:setName 609 // TODO @jason:setName
604 - return CollUtil.newArrayList(parallelGateway, joinParallelGateway); 610 + return CollUtil.newArrayList(inclusiveGateway, joinParallelGateway);
605 } 611 }
606 612
607 @Override 613 @Override
@@ -652,8 +658,14 @@ public class SimpleModelUtils { @@ -652,8 +658,14 @@ public class SimpleModelUtils {
652 } 658 }
653 659
654 public static SequenceFlow buildSequenceFlow(String sourceId, String targetId, 660 public static SequenceFlow buildSequenceFlow(String sourceId, String targetId,
655 - BpmSimpleModelNodeVO node) {  
656 - String conditionExpression = buildConditionExpression(node.getConditionSetting()); 661 + BpmSimpleModelNodeTypeEnum nodeType, BpmSimpleModelNodeVO node) {
  662 + String conditionExpression;
  663 + // 并行分支,使用包容网关实现,强制设置条件表达式为 true
  664 + if (BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE == nodeType) {
  665 + conditionExpression ="${true}";
  666 + } else {
  667 + conditionExpression = buildConditionExpression(node.getConditionSetting());
  668 + }
657 return buildBpmnSequenceFlow(sourceId, targetId, node.getId(), node.getName(), conditionExpression); 669 return buildBpmnSequenceFlow(sourceId, targetId, node.getId(), node.getName(), conditionExpression);
658 } 670 }
659 } 671 }
@@ -662,7 +674,6 @@ public class SimpleModelUtils { @@ -662,7 +674,6 @@ public class SimpleModelUtils {
662 * 构造条件表达式 674 * 构造条件表达式
663 */ 675 */
664 public static String buildConditionExpression(BpmSimpleModelNodeVO.ConditionSetting conditionSetting) { 676 public static String buildConditionExpression(BpmSimpleModelNodeVO.ConditionSetting conditionSetting) {
665 - // 并行网关不需要设置条件  
666 if (conditionSetting == null) { 677 if (conditionSetting == null) {
667 return null; 678 return null;
668 } 679 }
@@ -684,15 +695,18 @@ public class SimpleModelUtils { @@ -684,15 +695,18 @@ public class SimpleModelUtils {
684 if (conditionGroups == null || CollUtil.isEmpty(conditionGroups.getConditions())) { 695 if (conditionGroups == null || CollUtil.isEmpty(conditionGroups.getConditions())) {
685 return null; 696 return null;
686 } 697 }
687 - List<String> strConditionGroups = CollectionUtils.convertList(conditionGroups.getConditions(), item -> { 698 + List<String> strConditionGroups = convertList(conditionGroups.getConditions(), item -> {
688 if (CollUtil.isEmpty(item.getRules())) { 699 if (CollUtil.isEmpty(item.getRules())) {
689 return ""; 700 return "";
690 } 701 }
691 // 构造规则表达式 702 // 构造规则表达式
692 - List<String> list = CollectionUtils.convertList(item.getRules(), (rule) -> { 703 + List<String> list = convertList(item.getRules(), (rule) -> {
693 String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide() 704 String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide()
694 : "\"" + rule.getRightSide() + "\""; // 如果非数值类型加引号 705 : "\"" + rule.getRightSide() + "\""; // 如果非数值类型加引号
695 - return String.format(" %s %s var:convertByType(%s,%s)", rule.getLeftSide(), rule.getOpCode(), rule.getLeftSide(), rightSide); 706 + return String.format(" vars:getOrDefault(%s, null) %s var:convertByType(%s,%s) ",
  707 + rule.getLeftSide(), // 左侧:读取变量
  708 + rule.getOpCode(), // 中间:操作符,比较
  709 + rule.getLeftSide(), rightSide); // 右侧:转换变量,VariableConvertByTypeExpressionFunction
696 }); 710 });
697 // 构造条件组的表达式 711 // 构造条件组的表达式
698 Boolean and = item.getAnd(); 712 Boolean and = item.getAnd();
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmCategoryServiceImpl.java
@@ -34,6 +34,9 @@ public class BpmCategoryServiceImpl implements BpmCategoryService { @@ -34,6 +34,9 @@ public class BpmCategoryServiceImpl implements BpmCategoryService {
34 @Resource 34 @Resource
35 private BpmCategoryMapper bpmCategoryMapper; 35 private BpmCategoryMapper bpmCategoryMapper;
36 36
  37 + @Resource
  38 + private BpmModelService modelService;
  39 +
37 @Override 40 @Override
38 public Long createCategory(BpmCategorySaveReqVO createReqVO) { 41 public Long createCategory(BpmCategorySaveReqVO createReqVO) {
39 // 校验唯一 42 // 校验唯一
@@ -77,15 +80,22 @@ public class BpmCategoryServiceImpl implements BpmCategoryService { @@ -77,15 +80,22 @@ public class BpmCategoryServiceImpl implements BpmCategoryService {
77 @Override 80 @Override
78 public void deleteCategory(Long id) { 81 public void deleteCategory(Long id) {
79 // 校验存在 82 // 校验存在
80 - validateCategoryExists(id); 83 + BpmCategoryDO category = validateCategoryExists(id);
  84 + // 校验是否被流程模型使用
  85 + Long count = modelService.getModelCountByCategory(category.getCode());
  86 + if (count > 0) {
  87 + throw exception(CATEGORY_DELETE_FAIL_MODEL_USED, category.getName());
  88 + }
81 // 删除 89 // 删除
82 bpmCategoryMapper.deleteById(id); 90 bpmCategoryMapper.deleteById(id);
83 } 91 }
84 92
85 - private void validateCategoryExists(Long id) {  
86 - if (bpmCategoryMapper.selectById(id) == null) { 93 + private BpmCategoryDO validateCategoryExists(Long id) {
  94 + BpmCategoryDO category = bpmCategoryMapper.selectById(id);
  95 + if (category == null) {
87 throw exception(CATEGORY_NOT_EXISTS); 96 throw exception(CATEGORY_NOT_EXISTS);
88 } 97 }
  98 + return category;
89 } 99 }
90 100
91 @Override 101 @Override
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
@@ -25,6 +25,14 @@ public interface BpmModelService { @@ -25,6 +25,14 @@ public interface BpmModelService {
25 List<Model> getModelList(String name); 25 List<Model> getModelList(String name);
26 26
27 /** 27 /**
  28 + * 根据分类编码获得流程模型数量
  29 + *
  30 + * @param category 分类编码
  31 + * @return 流程模型数量
  32 + */
  33 + Long getModelCountByCategory(String category);
  34 +
  35 + /**
28 * 创建流程模型 36 * 创建流程模型
29 * 37 *
30 * @param modelVO 创建信息 38 * @param modelVO 创建信息
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
@@ -89,6 +89,14 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -89,6 +89,14 @@ public class BpmModelServiceImpl implements BpmModelService {
89 } 89 }
90 90
91 @Override 91 @Override
  92 + public Long getModelCountByCategory(String category) {
  93 + return repositoryService.createModelQuery()
  94 + .modelCategory(category)
  95 + .modelTenantId(FlowableUtils.getTenantId())
  96 + .count();
  97 + }
  98 +
  99 + @Override
92 @Transactional(rollbackFor = Exception.class) 100 @Transactional(rollbackFor = Exception.class)
93 public String createModel(@Valid BpmModelSaveReqVO createReqVO) { 101 public String createModel(@Valid BpmModelSaveReqVO createReqVO) {
94 if (!ValidationUtils.isXmlNCName(createReqVO.getKey())) { 102 if (!ValidationUtils.isXmlNCName(createReqVO.getKey())) {
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
@@ -4,11 +4,9 @@ import cn.hutool.core.collection.CollUtil; @@ -4,11 +4,9 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.ListUtil; 4 import cn.hutool.core.collection.ListUtil;
5 import cn.hutool.core.date.DateUtil; 5 import cn.hutool.core.date.DateUtil;
6 import cn.hutool.core.lang.Assert; 6 import cn.hutool.core.lang.Assert;
7 -import cn.hutool.core.util.ArrayUtil;  
8 -import cn.hutool.core.util.ObjUtil;  
9 -import cn.hutool.core.util.ObjectUtil;  
10 -import cn.hutool.core.util.StrUtil; 7 +import cn.hutool.core.util.*;
11 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  9 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
12 import cn.iocoder.yudao.framework.common.util.date.DateUtils; 10 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
13 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 11 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
14 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 12 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
@@ -61,12 +59,15 @@ import org.flowable.task.api.history.HistoricTaskInstance; @@ -61,12 +59,15 @@ import org.flowable.task.api.history.HistoricTaskInstance;
61 import org.springframework.context.annotation.Lazy; 59 import org.springframework.context.annotation.Lazy;
62 import org.springframework.stereotype.Service; 60 import org.springframework.stereotype.Service;
63 import org.springframework.transaction.annotation.Transactional; 61 import org.springframework.transaction.annotation.Transactional;
  62 +import org.springframework.transaction.support.TransactionSynchronization;
  63 +import org.springframework.transaction.support.TransactionSynchronizationManager;
64 import org.springframework.validation.annotation.Validated; 64 import org.springframework.validation.annotation.Validated;
65 65
66 import java.util.*; 66 import java.util.*;
67 67
68 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 68 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
69 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 69 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  70 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
70 import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode; 71 import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode;
71 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; 72 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
72 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; 73 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID;
@@ -264,7 +265,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -264,7 +265,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
264 // 3. 获取下一个将要执行的节点集合 265 // 3. 获取下一个将要执行的节点集合
265 FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); 266 FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());
266 List<FlowNode> nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); 267 List<FlowNode> nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables);
267 - List<ActivityNode> nextActivityNodes = convertList(nextFlowNodes, node -> new ActivityNode().setId(node.getId()) 268 + // 仅仅获取 UserTask 节点 TODO add from jason:如果网关节点和网关节点相连,获取下个 UserTask. 貌似有点不准。
  269 + List<FlowNode> nextUserTaskList = CollectionUtils.filterList(nextFlowNodes, node -> node instanceof UserTask);
  270 + List<ActivityNode> nextActivityNodes = convertList(nextUserTaskList, node -> new ActivityNode().setId(node.getId())
268 .setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) 271 .setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())
269 .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) 272 .setStatus(BpmTaskStatusEnum.RUNNING.getStatus())
270 .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) 273 .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node))
@@ -449,7 +452,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -449,7 +452,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
449 .setNodeType(BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType()).setStatus(processInstanceStatus) 452 .setNodeType(BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType()).setStatus(processInstanceStatus)
450 .setStartTime(DateUtils.of(activity.getStartTime())) 453 .setStartTime(DateUtils.of(activity.getStartTime()))
451 .setEndTime(DateUtils.of(activity.getEndTime())) 454 .setEndTime(DateUtils.of(activity.getEndTime()))
452 - .setProcessInstanceId(activity.getProcessInstanceId()); 455 + .setProcessInstanceId(activity.getCalledProcessInstanceId());
453 approvalNodes.add(callActivity); 456 approvalNodes.add(callActivity);
454 } 457 }
455 }); 458 });
@@ -521,7 +524,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -521,7 +524,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
521 activityNode.setCandidateUserIds(CollUtil.sub(candidateUserIds, index + 1, candidateUserIds.size())); 524 activityNode.setCandidateUserIds(CollUtil.sub(candidateUserIds, index + 1, candidateUserIds.size()));
522 } 525 }
523 if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(activityNode.getNodeType())) { 526 if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(activityNode.getNodeType())) {
524 - activityNode.setProcessInstanceId(firstActivity.getProcessInstanceId()); 527 + activityNode.setProcessInstanceId(firstActivity.getCalledProcessInstanceId());
525 } 528 }
526 return activityNode; 529 return activityNode;
527 }); 530 });
@@ -771,17 +774,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -771,17 +774,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
771 processInstanceBuilder.predefineProcessInstanceId(processIdRedisDAO.generate(processIdRule)); 774 processInstanceBuilder.predefineProcessInstanceId(processIdRedisDAO.generate(processIdRule));
772 } 775 }
773 // 3.2 流程名称 776 // 3.2 流程名称
774 - BpmModelMetaInfoVO.TitleSetting titleSetting = processDefinitionInfo.getTitleSetting();  
775 - if (titleSetting != null && Boolean.TRUE.equals(titleSetting.getEnable())) {  
776 - AdminUserRespDTO user = adminUserApi.getUser(userId);  
777 - Map<String, Object> cloneVariables = new HashMap<>(variables);  
778 - cloneVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, user.getNickname());  
779 - cloneVariables.put(BpmnVariableConstants.PROCESS_START_TIME, DateUtil.now());  
780 - cloneVariables.put(BpmnVariableConstants.PROCESS_DEFINITION_NAME, definition.getName().trim());  
781 - processInstanceBuilder.name(StrUtil.format(titleSetting.getTitle(), cloneVariables));  
782 - } else {  
783 - processInstanceBuilder.name(definition.getName().trim());  
784 - } 777 + processInstanceBuilder.name(generateProcessInstanceName(userId, definition, processDefinitionInfo, variables));
785 // 3.3 发起流程实例 778 // 3.3 发起流程实例
786 ProcessInstance instance = processInstanceBuilder.start(); 779 ProcessInstance instance = processInstanceBuilder.start();
787 return instance.getId(); 780 return instance.getId();
@@ -817,6 +810,25 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -817,6 +810,25 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
817 }); 810 });
818 } 811 }
819 812
  813 + private String generateProcessInstanceName(Long userId,
  814 + ProcessDefinition definition,
  815 + BpmProcessDefinitionInfoDO definitionInfo,
  816 + Map<String, Object> variables) {
  817 + if (definition == null || definitionInfo == null) {
  818 + return null;
  819 + }
  820 + BpmModelMetaInfoVO.TitleSetting titleSetting = definitionInfo.getTitleSetting();
  821 + if (titleSetting == null || !BooleanUtil.isTrue(titleSetting.getEnable())) {
  822 + return definition.getName();
  823 + }
  824 + AdminUserRespDTO user = adminUserApi.getUser(userId);
  825 + Map<String, Object> cloneVariables = new HashMap<>(variables);
  826 + cloneVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, user.getNickname());
  827 + cloneVariables.put(BpmnVariableConstants.PROCESS_START_TIME, DateUtil.now());
  828 + cloneVariables.put(BpmnVariableConstants.PROCESS_DEFINITION_NAME, definition.getName().trim());
  829 + return StrUtil.format(definitionInfo.getTitleSetting().getTitle(), cloneVariables);
  830 + }
  831 +
820 @Override 832 @Override
821 public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { 833 public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) {
822 // 1.1 校验流程实例存在 834 // 1.1 校验流程实例存在
@@ -833,7 +845,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -833,7 +845,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
833 .getProcessDefinitionInfo(instance.getProcessDefinitionId()); 845 .getProcessDefinitionInfo(instance.getProcessDefinitionId());
834 Assert.notNull(processDefinitionInfo, "流程定义({})不存在", processDefinitionInfo); 846 Assert.notNull(processDefinitionInfo, "流程定义({})不存在", processDefinitionInfo);
835 if (processDefinitionInfo.getAllowCancelRunningProcess() != null // 防止未配置 AllowCancelRunningProcess , 默认为可取消 847 if (processDefinitionInfo.getAllowCancelRunningProcess() != null // 防止未配置 AllowCancelRunningProcess , 默认为可取消
836 - && Boolean.FALSE.equals(processDefinitionInfo.getAllowCancelRunningProcess())) { 848 + && BooleanUtil.isFalse(processDefinitionInfo.getAllowCancelRunningProcess())) {
837 throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW); 849 throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW);
838 } 850 }
839 // 1.4 子流程不允许取消 851 // 1.4 子流程不允许取消
@@ -900,64 +912,77 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -900,64 +912,77 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
900 912
901 @Override 913 @Override
902 public void processProcessInstanceCompleted(ProcessInstance instance) { 914 public void processProcessInstanceCompleted(ProcessInstance instance) {
903 - // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号  
904 - FlowableUtils.execute(instance.getTenantId(), () -> {  
905 - // 1.1 获取当前状态  
906 - Integer status = (Integer) instance.getProcessVariables()  
907 - .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);  
908 - String reason = (String) instance.getProcessVariables()  
909 - .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON);  
910 - // 1.2 当流程状态还是审批状态中,说明审批通过了,则变更下它的状态  
911 - // 为什么这么处理?因为流程完成,并且完成了,说明审批通过了  
912 - if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) {  
913 - status = BpmProcessInstanceStatusEnum.APPROVE.getStatus();  
914 - runtimeService.setVariable(instance.getId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,  
915 - status);  
916 - } 915 + // 1.1 获取当前状态
  916 + Integer status = (Integer) instance.getProcessVariables()
  917 + .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
  918 + String reason = (String) instance.getProcessVariables()
  919 + .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON);
  920 + // 1.2 当流程状态还是审批状态中,说明审批通过了,则变更下它的状态
  921 + // 为什么这么处理?因为流程完成,并且完成了,说明审批通过了
  922 + if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) {
  923 + status = BpmProcessInstanceStatusEnum.APPROVE.getStatus();
  924 + runtimeService.setVariable(instance.getId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
  925 + status);
  926 + }
  927 +
  928 + // 2. 发送对应的消息通知
  929 + if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {
  930 + messageService.sendMessageWhenProcessInstanceApprove(
  931 + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance));
  932 + } else if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())) {
  933 + messageService.sendMessageWhenProcessInstanceReject(
  934 + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(instance, reason));
  935 + }
  936 +
  937 + // 3. 发送流程实例的状态事件
  938 + processInstanceEventPublisher.sendProcessInstanceResultEvent(
  939 + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status));
  940 +
  941 + // 4. 流程后置通知
  942 + if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {
  943 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.
  944 + getProcessDefinitionInfo(instance.getProcessDefinitionId());
  945 + if (ObjUtil.isNotNull(processDefinitionInfo) &&
  946 + ObjUtil.isNotNull(processDefinitionInfo.getProcessAfterTriggerSetting())) {
  947 + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessAfterTriggerSetting();
917 948
918 - // 2. 发送对应的消息通知  
919 - if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {  
920 - messageService.sendMessageWhenProcessInstanceApprove(  
921 - BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance));  
922 - } else if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())) {  
923 - messageService.sendMessageWhenProcessInstanceReject(  
924 - BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(instance, reason)); 949 + BpmHttpRequestUtils.executeBpmHttpRequest(instance,
  950 + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());
925 } 951 }
  952 + }
  953 + }
926 954
927 - // 3. 发送流程实例的状态事件  
928 - processInstanceEventPublisher.sendProcessInstanceResultEvent(  
929 - BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status)); 955 + @Override
  956 + public void processProcessInstanceCreated(ProcessInstance instance) {
  957 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.
  958 + getProcessDefinitionInfo(instance.getProcessDefinitionId());
  959 + ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(instance.getProcessDefinitionId());
  960 + if (processDefinition == null || processDefinitionInfo == null) {
  961 + return;
  962 + }
930 963
931 - // 4. 流程后置通知  
932 - if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {  
933 - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.  
934 - getProcessDefinitionInfo(instance.getProcessDefinitionId());  
935 - if (ObjUtil.isNotNull(processDefinitionInfo) &&  
936 - ObjUtil.isNotNull(processDefinitionInfo.getProcessAfterTriggerSetting())) {  
937 - BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessAfterTriggerSetting(); 964 + // 自定义标题。目的:主要处理子流程的标题无法处理
  965 + // 注意:必须使用 TransactionSynchronizationManager 事务提交后,否则不生效!!!
  966 + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
938 967
939 - BpmHttpRequestUtils.executeBpmHttpRequest(instance,  
940 - setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); 968 + @Override
  969 + public void afterCommit() {
  970 + String name = generateProcessInstanceName(Long.valueOf(instance.getStartUserId()),
  971 + processDefinition, processDefinitionInfo, instance.getProcessVariables());
  972 + if (ObjUtil.notEqual(instance.getName(), name)) {
  973 + runtimeService.setProcessInstanceName(instance.getProcessInstanceId(), name);
941 } 974 }
942 } 975 }
943 - });  
944 - }  
945 976
946 - @Override  
947 - public void processProcessInstanceCreated(ProcessInstance instance) {  
948 - // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号  
949 - FlowableUtils.execute(instance.getTenantId(), () -> {  
950 - // 流程前置通知  
951 - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.  
952 - getProcessDefinitionInfo(instance.getProcessDefinitionId());  
953 - if (ObjUtil.isNull(processDefinitionInfo) ||  
954 - ObjUtil.isNull(processDefinitionInfo.getProcessBeforeTriggerSetting())) {  
955 - return;  
956 - }  
957 - BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessBeforeTriggerSetting();  
958 - BpmHttpRequestUtils.executeBpmHttpRequest(instance,  
959 - setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());  
960 }); 977 });
  978 +
  979 + // 流程前置通知
  980 + if (ObjUtil.isNull(processDefinitionInfo.getProcessBeforeTriggerSetting())) {
  981 + return;
  982 + }
  983 + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessBeforeTriggerSetting();
  984 + BpmHttpRequestUtils.executeBpmHttpRequest(instance,
  985 + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());
961 } 986 }
962 987
963 } 988 }
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
@@ -230,10 +230,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -230,10 +230,10 @@ public class BpmTaskServiceImpl implements BpmTaskService {
230 if (StrUtil.isNotBlank(pageVO.getName())) { 230 if (StrUtil.isNotBlank(pageVO.getName())) {
231 taskQuery.taskNameLike("%" + pageVO.getName() + "%"); 231 taskQuery.taskNameLike("%" + pageVO.getName() + "%");
232 } 232 }
233 - if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {  
234 - taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0]));  
235 - taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1]));  
236 - } 233 +// if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {
  234 +// taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0]));
  235 +// taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1]));
  236 +// }
237 // 执行查询 237 // 执行查询
238 long count = taskQuery.count(); 238 long count = taskQuery.count();
239 if (count == 0) { 239 if (count == 0) {
@@ -244,6 +244,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -244,6 +244,12 @@ public class BpmTaskServiceImpl implements BpmTaskService {
244 // 特殊:强制移除自动完成的“发起人”节点 244 // 特殊:强制移除自动完成的“发起人”节点
245 // 补充说明:由于 taskQuery 无法方面的过滤,所以暂时通过内存过滤 245 // 补充说明:由于 taskQuery 无法方面的过滤,所以暂时通过内存过滤
246 tasks.removeIf(task -> task.getTaskDefinitionKey().equals(START_USER_NODE_ID)); 246 tasks.removeIf(task -> task.getTaskDefinitionKey().equals(START_USER_NODE_ID));
  247 + // TODO @芋艿:https://t.zsxq.com/MNzqp 【flowable bug】:taskCreatedAfter、taskCreatedBefore 拼接的是 OR
  248 + if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {
  249 + tasks.removeIf(task -> task.getCreateTime() == null
  250 + || task.getCreateTime().before(DateUtils.of(pageVO.getCreateTime()[0]))
  251 + || task.getCreateTime().after(DateUtils.of(pageVO.getCreateTime()[1])));
  252 + }
247 return new PageResult<>(tasks, count); 253 return new PageResult<>(tasks, count);
248 } 254 }
249 255
@@ -259,16 +265,22 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -259,16 +265,22 @@ public class BpmTaskServiceImpl implements BpmTaskService {
259 if (StrUtil.isNotEmpty(pageVO.getCategory())) { 265 if (StrUtil.isNotEmpty(pageVO.getCategory())) {
260 taskQuery.taskCategory(pageVO.getCategory()); 266 taskQuery.taskCategory(pageVO.getCategory());
261 } 267 }
262 - if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {  
263 - taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0]));  
264 - taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1]));  
265 - } 268 +// if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {
  269 +// taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0]));
  270 +// taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1]));
  271 +// }
266 // 执行查询 272 // 执行查询
267 long count = taskQuery.count(); 273 long count = taskQuery.count();
268 if (count == 0) { 274 if (count == 0) {
269 return PageResult.empty(); 275 return PageResult.empty();
270 } 276 }
271 List<HistoricTaskInstance> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); 277 List<HistoricTaskInstance> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
  278 + // TODO @芋艿:https://t.zsxq.com/MNzqp 【flowable bug】:taskCreatedAfter、taskCreatedBefore 拼接的是 OR
  279 + if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) {
  280 + tasks.removeIf(task -> task.getCreateTime() == null
  281 + || task.getCreateTime().before(DateUtils.of(pageVO.getCreateTime()[0]))
  282 + || task.getCreateTime().after(DateUtils.of(pageVO.getCreateTime()[1])));
  283 + }
272 return new PageResult<>(tasks, count); 284 return new PageResult<>(tasks, count);
273 } 285 }
274 286
@@ -886,7 +898,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -886,7 +898,9 @@ public class BpmTaskServiceImpl implements BpmTaskService {
886 if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) { 898 if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) {
887 return; 899 return;
888 } 900 }
889 - runExecutionIds.add(task.getExecutionId()); 901 + if (task.getExecutionId() != null) {
  902 + runExecutionIds.add(task.getExecutionId());
  903 + }
890 904
891 // 判断是否分配给自己任务,因为会签任务,一个节点会有多个任务 905 // 判断是否分配给自己任务,因为会签任务,一个节点会有多个任务
892 if (isAssignUserTask(userId, task)) { // 情况一:自己的任务,进行 RETURN 标记 906 if (isAssignUserTask(userId, task)) { // 情况一:自己的任务,进行 RETURN 标记
@@ -1367,7 +1381,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -1367,7 +1381,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
1367 PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); 1381 PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class));
1368 if (userTaskElement.getId().equals(START_USER_NODE_ID) 1382 if (userTaskElement.getId().equals(START_USER_NODE_ID)
1369 && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核 1383 && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核
1370 - || Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 1384 + || BooleanUtil.isTrue(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核
1371 && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { 1385 && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {
1372 getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) 1386 getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
1373 .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); 1387 .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason()));
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java
@@ -85,10 +85,15 @@ public class BpmCallActivityListener implements ExecutionListener { @@ -85,10 +85,15 @@ public class BpmCallActivityListener implements ExecutionListener {
85 // 2.2 使用表单值,并兜底字符串转 Long 失败时使用主流程发起人 85 // 2.2 使用表单值,并兜底字符串转 Long 失败时使用主流程发起人
86 try { 86 try {
87 FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue)); 87 FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue));
88 - } catch (Exception e) {  
89 - log.error("[notify][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]",  
90 - DELEGATE_EXPRESSION, formFieldValue);  
91 - FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId())); 88 + } catch (NumberFormatException ex) {
  89 + try {
  90 + List<Long> formFieldValues = JsonUtils.parseArray(formFieldValue, Long.class);
  91 + FlowableUtils.setAuthenticatedUserId(formFieldValues.get(0));
  92 + } catch (Exception e) {
  93 + log.error("[notify][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]",
  94 + DELEGATE_EXPRESSION, formFieldValue);
  95 + FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId()));
  96 + }
92 } 97 }
93 } 98 }
94 } 99 }
yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
@@ -303,7 +303,9 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { @@ -303,7 +303,9 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
303 throw exception(SECKILL_JOIN_ACTIVITY_TIME_ERROR); 303 throw exception(SECKILL_JOIN_ACTIVITY_TIME_ERROR);
304 } 304 }
305 SeckillConfigDO config = seckillConfigService.getCurrentSeckillConfig(); 305 SeckillConfigDO config = seckillConfigService.getCurrentSeckillConfig();
306 - if (config == null || !CollectionUtil.contains(activity.getConfigIds(), config.getId())) { 306 + if (config == null
  307 + || !CollectionUtil.contains(activity.getConfigIds(), config.getId())
  308 + || !LocalDateTimeUtils.isBetween(config.getStartTime(), config.getEndTime())) {
307 throw exception(SECKILL_JOIN_ACTIVITY_TIME_ERROR); 309 throw exception(SECKILL_JOIN_ACTIVITY_TIME_ERROR);
308 } 310 }
309 // 1.3 超过单次购买限制 311 // 1.3 超过单次购买限制