Pre Merge pull request !1356 from whc/fix/bpm
This commit is contained in:
commit
01efd78753
|
@ -59,7 +59,7 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav
|
||||||
|
|
||||||
// 第二步,获取任务的所有处理人
|
// 第二步,获取任务的所有处理人
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Set<Long> assigneeUserIds = (Set<Long>) execution.getVariable(super.collectionVariable, Set.class);
|
Set<Long> assigneeUserIds = (Set<Long>) execution.getVariableLocal(super.collectionVariable, Set.class);
|
||||||
if (assigneeUserIds == null) {
|
if (assigneeUserIds == null) {
|
||||||
assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);
|
assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);
|
||||||
if (CollUtil.isEmpty(assigneeUserIds)) {
|
if (CollUtil.isEmpty(assigneeUserIds)) {
|
||||||
|
|
|
@ -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<Void> {
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -755,6 +755,48 @@ public class BpmnModelUtils {
|
||||||
return userTaskList;
|
return userTaskList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前元素后可能执行到的所有userTask
|
||||||
|
*
|
||||||
|
* @param startElement 起始元素
|
||||||
|
* @param bpmnModel 流程模型
|
||||||
|
* @return 当前元素后可能执行到的所有userTask,不包含自身
|
||||||
|
*/
|
||||||
|
public static Set<UserTask> findReachableUserTasks(FlowElement startElement, BpmnModel bpmnModel) {
|
||||||
|
Set<UserTask> userTasks = new HashSet<>();
|
||||||
|
Set<String> visited = new HashSet<>();
|
||||||
|
|
||||||
|
if (startElement instanceof FlowNode) {
|
||||||
|
List<SequenceFlow> 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<UserTask> userTasks, Set<String> 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<SequenceFlow> outgoingFlows = ((FlowNode) current).getOutgoingFlows();
|
||||||
|
for (SequenceFlow flow : outgoingFlows) {
|
||||||
|
String targetRef = flow.getTargetRef();
|
||||||
|
FlowElement targetElement = model.getMainProcess().getFlowElement(targetRef);
|
||||||
|
dfs(targetElement, model, userTasks, visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
// ========== BPMN 流程预测相关的方法 ==========
|
// ========== BPMN 流程预测相关的方法 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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.enums.task.BpmTaskStatusEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
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.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.BpmHttpRequestUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
||||||
|
@ -646,6 +647,13 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
if (approveUserSelectAssignees == null) {
|
if (approveUserSelectAssignees == null) {
|
||||||
approveUserSelectAssignees = new HashMap<>();
|
approveUserSelectAssignees = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
// 当多实例任务根据'审批人自选'策略多次选择时,避免审批人被后一次选择覆盖
|
||||||
|
List<Long> nodeHadAssignees = approveUserSelectAssignees.get(nextFlowNode.getId());
|
||||||
|
if (nodeHadAssignees != null){
|
||||||
|
assignees = Stream.concat(nodeHadAssignees.stream(), assignees.stream())
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
approveUserSelectAssignees.put(nextFlowNode.getId(), assignees);
|
approveUserSelectAssignees.put(nextFlowNode.getId(), assignees);
|
||||||
Map<String,List<Long>> existingApproveUserSelectAssignees = (Map<String,List<Long>>) variables.get(
|
Map<String,List<Long>> existingApproveUserSelectAssignees = (Map<String,List<Long>>) variables.get(
|
||||||
BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES);
|
BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES);
|
||||||
|
@ -906,10 +914,47 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
// 4. 执行驳回
|
// 4. 执行驳回
|
||||||
// 使用 moveExecutionsToSingleActivityId 替换 moveActivityIdsToSingleActivityId 原因:
|
// 使用 moveExecutionsToSingleActivityId 替换 moveActivityIdsToSingleActivityId 原因:
|
||||||
// 当多实例任务回退的时候有问题。相关 issue: https://github.com/flowable/flowable-engine/issues/3944
|
// 当多实例任务回退的时候有问题。相关 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()
|
runtimeService.createChangeActivityStateBuilder()
|
||||||
.processInstanceId(currentTask.getProcessInstanceId())
|
.processInstanceId(currentTask.getProcessInstanceId())
|
||||||
.moveExecutionsToSingleActivityId(runExecutionIds, reqVO.getTargetTaskDefinitionKey())
|
.moveExecutionToActivityId(mainExecutionId, reqVO.getTargetTaskDefinitionKey())
|
||||||
.changeState();
|
.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<String, List<Long>> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance);
|
||||||
|
if (ObjectUtil.isNotEmpty(approveUserSelectAssignees)) {
|
||||||
|
Set<UserTask> 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<String, List<Long>> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance);
|
||||||
|
if (ObjectUtil.isNotEmpty(approveUserSelectAssignees)) {
|
||||||
|
Set<UserTask> 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
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue