From 52b72cffb3a6d4d310f9faa0565c072a825339da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E7=84=95=E8=B6=85?= <14155701+wu-huanchao@user.noreply.gitee.com> Date: Tue, 20 May 2025 10:26:31 +0800 Subject: [PATCH 1/4] =?UTF-8?q?fix(bpm):=E4=BF=AE=E5=A4=8D=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E9=A2=84=E6=B5=8B=E6=97=B6=E7=94=B1=E4=BA=8E=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E6=9C=AA=E5=BC=80=E5=A7=8B=E6=88=96=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E6=9C=AA=E7=9C=9F=E6=AD=A3=E6=8A=B5=E8=BE=BE=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E8=8A=82=E7=82=B9=EF=BC=8C=E6=97=A0=E6=B3=95=E8=8E=B7=E5=8F=96?= =?UTF-8?q?execution=E5=AF=BC=E8=87=B4=E7=9A=84Expression=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E8=A1=A8=E8=BE=BE=E5=BC=8F=E6=89=A7=E8=A1=8C=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BpmTaskAssignLeaderExpression.java | 30 +++++++++++++++++-- .../BpmTaskAssignStartUserExpression.java | 22 ++++++++++++++ .../core/enums/BpmnVariableConstants.java | 4 +++ .../flowable/core/util/FlowableUtils.java | 30 +++++++++++++++++++ .../task/BpmProcessInstanceServiceImpl.java | 8 ++++- 5 files changed, 90 insertions(+), 4 deletions(-) diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java index 7c1950f8ce..24f76b3aca 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Component; import org.springframework.util.Assert; @@ -41,11 +42,34 @@ public class BpmTaskAssignLeaderExpression { * @param level 指定级别 * @return 指定级别的领导 */ - public Set calculateUsers(DelegateExecution execution, int level) { - Assert.isTrue(level > 0, "level 必须大于 0"); + public Set calculateUsers(ExecutionEntityImpl execution, Long level) { // 获得发起人 ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); - Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); + return calculateUsers(NumberUtils.parseLong(processInstance.getStartUserId()), level); + } + + /** + * 计算审批的候选人 + * + * @param processInstanceId 流程实例id + * @param level 指定级别 + * @return 指定级别的领导 + */ + public Set calculateUsers(String processInstanceId, Long level) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); + return calculateUsers(NumberUtils.parseLong(processInstance.getStartUserId()), level); + } + + + /** + * 计算审批的候选人 + * + * @param startUserId 发起人 + * @param level 指定级别 + * @return 指定级别的领导 + */ + public Set calculateUsers(Long startUserId, Long level) { + Assert.isTrue(level > 0, "level 必须大于 0"); // 获得对应 leve 的部门 DeptRespDTO dept = null; for (int i = 0; i < level; i++) { diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java index ac243c0f43..fe6bff1916 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java @@ -33,4 +33,26 @@ public class BpmTaskAssignStartUserExpression { return SetUtils.asSet(startUserId); } + /** + * 计算审批的候选人 + * + * @param startUserId 发起人id + * @return 发起人 + */ + public Set calculateUsers(Long startUserId) { + return SetUtils.asSet(startUserId); + } + + /** + * 计算审批的候选人 + * + * @param processInstanceId 流程实例id + * @return 发起人 + */ + public Set calculateUsers(String processInstanceId) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); + Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); + return SetUtils.asSet(startUserId); + } + } diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java index 893c4d053c..f1d3088cfd 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java @@ -96,4 +96,8 @@ public class BpmnVariableConstants { */ public static final String TASK_SIGN_PIC_URL = "TASK_SIGN_PIC_URL"; + /** + * 流程实例的变量 - 流程实例id + */ + public static final String PROCESS_INSTANCE_ID = "PROCESS_INSTANCE_ID"; } diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java index 67c24bb9f4..98c3ef2803 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java @@ -5,6 +5,7 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormFieldVO; @@ -30,6 +31,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.Callable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @@ -355,6 +358,33 @@ public class FlowableUtils { } public static Object getExpressionValue(Map variable, String expressionString) { + // 替换方法参数中的 execution 为流程实例id或userId,因为流程预测时获取不到execution对象 + // 例如将 calculateUsers(execution, 1) 替换为 calculateUsers('xxx-xxx-xxx-xxx', 1) + Pattern pattern = Pattern.compile("\\(([^)]*)\\)"); + Matcher matcher = pattern.matcher(expressionString); + StringBuilder sb = new StringBuilder(); + while (matcher.find()) { + // 获取方法参数列表 + String args = matcher.group(1); + // 用逗号分隔参数并逐个替换精确匹配 execution 的部分 + String[] params = args.split(","); + for (int i = 0; i < params.length; i++) { + if ("execution".equals(params[i].trim())) { + // 流程已开始 + if(ObjectUtil.isNotEmpty(variable.get(BpmnVariableConstants.PROCESS_INSTANCE_ID))) { + String processInstanceId = variable.get(BpmnVariableConstants.PROCESS_INSTANCE_ID).toString(); + params[i] = "'" + processInstanceId + "'"; + } else { + //流程未开始 + params[i] = Objects.requireNonNull(SecurityFrameworkUtils.getLoginUserId()).toString(); + } + } + } + String newArgs = String.join(", ", params); + matcher.appendReplacement(sb, "(" + newArgs + ")"); + } + matcher.appendTail(sb); + expressionString = sb.toString(); VariableContainer variableContainer = new MapDelegateVariableContainer(variable, VariableContainer.empty()); return getExpressionValue(variableContainer, expressionString); } diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 319f7bd8a8..9d102b4821 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -46,6 +46,7 @@ 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.apache.commons.lang3.StringUtils; import org.flowable.bpmn.constants.BpmnXMLConstants; import org.flowable.bpmn.model.*; import org.flowable.engine.HistoryService; @@ -766,9 +767,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService .businessKey(businessKey) .variables(variables); // 3.1 创建流程 ID + String processInstanceId = null; BpmModelMetaInfoVO.ProcessIdRule processIdRule = processDefinitionInfo.getProcessIdRule(); if (processIdRule != null && Boolean.TRUE.equals(processIdRule.getEnable())) { - processInstanceBuilder.predefineProcessInstanceId(processIdRedisDAO.generate(processIdRule)); + processInstanceId = processIdRedisDAO.generate(processIdRule); + processInstanceBuilder.predefineProcessInstanceId(processInstanceId); } // 3.2 流程名称 BpmModelMetaInfoVO.TitleSetting titleSetting = processDefinitionInfo.getTitleSetting(); @@ -784,6 +787,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } // 3.3 发起流程实例 ProcessInstance instance = processInstanceBuilder.start(); + // 将流程实例id保存到流程变量里,用于无法获取execution时获取流程实例id + processInstanceId = StringUtils.defaultIfBlank(processInstanceId, instance.getId()); + runtimeService.setVariable(instance.getProcessInstanceId(),BpmnVariableConstants.PROCESS_INSTANCE_ID,processInstanceId); return instance.getId(); } From 0f50e722e216ba1c85718c1796575d579f582675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E7=84=95=E8=B6=85?= <14155701+wu-huanchao@user.noreply.gitee.com> Date: Fri, 23 May 2025 08:34:54 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix(bpm):=E8=A7=A3=E5=86=B3=E5=8C=85?= =?UTF-8?q?=E5=AE=B9=E7=BD=91=E5=85=B3=E5=AD=98=E5=9C=A8=E5=A4=9A=E4=B8=AA?= =?UTF-8?q?=E6=BF=80=E6=B4=BB=E5=88=86=E6=94=AF=E6=97=B6=EF=BC=8C=E5=88=86?= =?UTF-8?q?=E6=94=AF=E4=B8=8A=E8=8A=82=E7=82=B9=E8=BF=9B=E8=A1=8C=E9=80=80?= =?UTF-8?q?=E5=9B=9E=E5=88=B0=E5=8C=85=E5=AE=B9=E7=BD=91=E5=85=B3=E5=89=8D?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E6=93=8D=E4=BD=9C=E6=97=B6=E5=A4=9A=E4=B8=AA?= =?UTF-8?q?execution=E9=80=80=E5=9B=9E=E5=AF=BC=E8=87=B4=E7=9A=84=E5=9C=A8?= =?UTF-8?q?=E7=9B=AE=E6=A0=87=E8=8A=82=E7=82=B9=E5=88=9B=E5=BB=BA=E5=A4=9A?= =?UTF-8?q?=E4=B8=AA=E4=BB=BB=E5=8A=A1=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/listener/DeleteExecutionCommand.java | 33 +++++++++++++++++++ .../bpm/service/task/BpmTaskServiceImpl.java | 10 +++++- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/DeleteExecutionCommand.java diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/DeleteExecutionCommand.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/DeleteExecutionCommand.java new file mode 100644 index 0000000000..b26f24b75f --- /dev/null +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/DeleteExecutionCommand.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; + +/** + * @Author whc + * @Date 2025/5/21 11:30 + */ +import org.flowable.common.engine.impl.interceptor.Command; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.impl.persistence.entity.ExecutionEntity; +import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager; +import org.flowable.engine.impl.util.CommandContextUtil; + +public class DeleteExecutionCommand implements Command { + + private final String executionId; + private final String reason; + + public DeleteExecutionCommand(String executionId, String reason) { + this.executionId = executionId; + this.reason = reason; + } + + @Override + public Void execute(CommandContext commandContext) { + ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext); + ExecutionEntity execution = executionEntityManager.findById(executionId); + if (execution != null) { + executionEntityManager.deleteExecutionAndRelatedData(execution, reason, false); + } + return null; + } +} + diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 1164f4da72..a09f48e49c 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -24,6 +24,7 @@ import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.DeleteExecutionCommand; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; @@ -906,9 +907,16 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 4. 执行驳回 // 使用 moveExecutionsToSingleActivityId 替换 moveActivityIdsToSingleActivityId 原因: // 当多实例任务回退的时候有问题。相关 issue: https://github.com/flowable/flowable-engine/issues/3944 + // 选择一个主 execution + String mainExecutionId = runExecutionIds.get(0); + // 删除其他 execution 解决多个execution会在移动的目标节点创建出多个任务的问题 + for (int i = 1; i < runExecutionIds.size(); i++) { + String redundantId = runExecutionIds.get(i); + managementService.executeCommand(new DeleteExecutionCommand(redundantId, "退回清理冗余路径")); + } runtimeService.createChangeActivityStateBuilder() .processInstanceId(currentTask.getProcessInstanceId()) - .moveExecutionsToSingleActivityId(runExecutionIds, reqVO.getTargetTaskDefinitionKey()) + .moveExecutionToActivityId(mainExecutionId, reqVO.getTargetTaskDefinitionKey()) .changeState(); } From 72c8ac38170875a3c8b9aff6b784916d43673af0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E7=84=95=E8=B6=85?= <14155701+wu-huanchao@user.noreply.gitee.com> Date: Fri, 23 May 2025 08:41:08 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix(bpm):=E8=A7=A3=E5=86=B3=E5=A4=9A?= =?UTF-8?q?=E5=AE=9E=E4=BE=8B=E4=BB=BB=E5=8A=A1=E6=A0=B9=E6=8D=AE'?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA=E9=80=89'=E7=AD=96?= =?UTF-8?q?=E7=95=A5=E5=A4=9A=E6=AC=A1=E9=80=89=E6=8B=A9=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=A2=AB=E5=90=8E=E4=B8=80=E6=AC=A1?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E8=A6=86=E7=9B=96=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index a09f48e49c..a02c945fcc 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -647,6 +647,13 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (approveUserSelectAssignees == null) { approveUserSelectAssignees = new HashMap<>(); } + // 当多实例任务根据'审批人自选'策略多次选择时,避免审批人被后一次选择覆盖 + List nodeHadAssignees = approveUserSelectAssignees.get(nextFlowNode.getId()); + if (nodeHadAssignees != null){ + assignees = Stream.concat(nodeHadAssignees.stream(), assignees.stream()) + .distinct() + .toList(); + } approveUserSelectAssignees.put(nextFlowNode.getId(), assignees); Map> existingApproveUserSelectAssignees = (Map>) variables.get( BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); From 38607f0a3e91ef401f49d489a4289fee31477452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E7=84=95=E8=B6=85?= <14155701+wu-huanchao@user.noreply.gitee.com> Date: Fri, 23 May 2025 16:01:51 +0800 Subject: [PATCH 4/4] =?UTF-8?q?fix(bpm):=E8=A7=A3=E5=86=B3=E7=94=B1?= =?UTF-8?q?=E4=BA=8E=E9=80=80=E5=9B=9E=E7=9A=84=E4=BB=BB=E5=8A=A1=E4=B8=AD?= =?UTF-8?q?=E5=B7=B2=E6=9C=89=E8=87=AA=E9=80=89=E5=AE=A1=E6=89=B9=E4=BA=BA?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=95=B0=E6=8D=AE=EF=BC=8C=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E9=80=80=E5=9B=9E=E5=90=8E=E9=87=8D=E6=96=B0=E8=87=AA=E9=80=89?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA=E7=9B=B8=E5=85=B3=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E8=A2=AB=E6=97=A7=E6=95=B0=E6=8D=AE=E5=BD=B1=E5=93=8D=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BpmParallelMultiInstanceBehavior.java | 2 +- .../flowable/core/util/BpmnModelUtils.java | 42 +++++++++++++++++++ .../bpm/service/task/BpmTaskServiceImpl.java | 30 +++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java index 57f4d393f3..5ba75c0524 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java @@ -59,7 +59,7 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav // 第二步,获取任务的所有处理人 @SuppressWarnings("unchecked") - Set assigneeUserIds = (Set) execution.getVariable(super.collectionVariable, Set.class); + Set assigneeUserIds = (Set) execution.getVariableLocal(super.collectionVariable, Set.class); if (assigneeUserIds == null) { assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution); if (CollUtil.isEmpty(assigneeUserIds)) { diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 1cccf18f04..adc5741c50 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -755,6 +755,48 @@ public class BpmnModelUtils { return userTaskList; } + /** + * 获取当前元素后可能执行到的所有userTask + * + * @param startElement 起始元素 + * @param bpmnModel 流程模型 + * @return 当前元素后可能执行到的所有userTask,不包含自身 + */ + public static Set findReachableUserTasks(FlowElement startElement, BpmnModel bpmnModel) { + Set userTasks = new HashSet<>(); + Set visited = new HashSet<>(); + + if (startElement instanceof FlowNode) { + List outgoing = ((FlowNode) startElement).getOutgoingFlows(); + for (SequenceFlow flow : outgoing) { + FlowElement target = bpmnModel.getMainProcess().getFlowElement(flow.getTargetRef()); + dfs(target, bpmnModel, userTasks, visited); + } + } + return userTasks; + } + + private static void dfs(FlowElement current, BpmnModel model, Set userTasks, Set visited) { + if (current == null || visited.contains(current.getId())) { + return; + } + + visited.add(current.getId()); + + if (current instanceof UserTask) { + userTasks.add((UserTask) current); + } + + if (current instanceof FlowNode) { + List outgoingFlows = ((FlowNode) current).getOutgoingFlows(); + for (SequenceFlow flow : outgoingFlows) { + String targetRef = flow.getTargetRef(); + FlowElement targetElement = model.getMainProcess().getFlowElement(targetRef); + dfs(targetElement, model, userTasks, visited); + } + } + + } // ========== BPMN 流程预测相关的方法 ========== /** diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index a02c945fcc..163d10bfa2 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -925,6 +925,36 @@ public class BpmTaskServiceImpl implements BpmTaskService { .processInstanceId(currentTask.getProcessInstanceId()) .moveExecutionToActivityId(mainExecutionId, reqVO.getTargetTaskDefinitionKey()) .changeState(); + // 5. 清除'审批人自选'策略相关数据 + // 移除退回后不会自动通过的节点的审批人数据 + // 根据自动去重设置执行相关操作 + BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.getProcessDefinitionInfo(currentTask.getProcessDefinitionId()); + BpmnModel bpmnModel = bpmProcessDefinitionService.getProcessDefinitionBpmnModel(currentTask.getProcessDefinitionId()); + ProcessInstance processInstance = processInstanceService.getProcessInstance(currentTask.getProcessInstanceId()); + if (BpmAutoApproveTypeEnum.NONE.getType().equals(processDefinitionInfo.getAutoApprovalType())) { + // 不自动通过,清除 targetElement 后所有节点的审批人数据 + Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance); + if (ObjectUtil.isNotEmpty(approveUserSelectAssignees)) { + Set reachableUserTasks = findReachableUserTasks(targetElement, bpmnModel); + for (UserTask reachableUserTask : reachableUserTasks) { + approveUserSelectAssignees.remove(reachableUserTask.getId()); + } + runtimeService.setVariable(processInstance.getProcessInstanceId(),BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES,approveUserSelectAssignees); + } + } else if (BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getType().equals(processDefinitionInfo.getAutoApprovalType())) { + // TODO 优化:去重规则为连续审批的节点自动通过,看退回到目标节点后会自动通过走到哪里,将走到的位置之后的节点自选审批人数据清空 + // 暂且清除 targetElement 后所有节点的审批人数据 + // 自动通过时若候选人策略为'审批人自选',会因为缺失自选审批人报错,需要重新选择审批人手动审批 + Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance); + if (ObjectUtil.isNotEmpty(approveUserSelectAssignees)) { + Set reachableUserTasks = findReachableUserTasks(targetElement, bpmnModel); + for (UserTask reachableUserTask : reachableUserTasks) { + approveUserSelectAssignees.remove(reachableUserTask.getId()); + } + runtimeService.setVariable(processInstance.getProcessInstanceId(),BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES,approveUserSelectAssignees); + } + } + // 若为自动通过,还会走到当前节点,不清除节点自选审批人数据 } @Override