feat:审批通过时,校验节点是否为下一个执行节点
This commit is contained in:
parent
093e563b80
commit
deef88f56f
|
@ -828,6 +828,125 @@ public class BpmnModelUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据当前节点,获取下一个节点
|
||||||
|
*
|
||||||
|
* @param currentElement 当前节点
|
||||||
|
* @param bpmnModel BPMN模型
|
||||||
|
* @param variables 流程变量
|
||||||
|
*/
|
||||||
|
public static List<FlowNode> getNextFlowNodes(FlowElement currentElement, BpmnModel bpmnModel,
|
||||||
|
Map<String, Object> variables){
|
||||||
|
// 下一个执行的流程节点集合
|
||||||
|
List<FlowNode> nextFlowNodes = new ArrayList<>();
|
||||||
|
// 当前执行节点的基本属性
|
||||||
|
FlowNode currentNode = (FlowNode) currentElement;
|
||||||
|
// 获取当前节点的关联节点
|
||||||
|
List<SequenceFlow> outgoingFlows = currentNode.getOutgoingFlows();
|
||||||
|
if (CollUtil.isEmpty(outgoingFlows)){
|
||||||
|
log.warn("[getNextFlowNodes][当前节点({}) 的 outgoingFlows 为空]", currentNode.getId());
|
||||||
|
return nextFlowNodes;
|
||||||
|
}
|
||||||
|
// 遍历每个出口流
|
||||||
|
for (SequenceFlow outgoingFlow : outgoingFlows) {
|
||||||
|
// 获取目标节点的基本属性
|
||||||
|
FlowElement targetElement = bpmnModel.getFlowElement(outgoingFlow.getTargetRef());
|
||||||
|
if (targetElement == null){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (targetElement instanceof Gateway gateway) {
|
||||||
|
// 处理不同类型的网关
|
||||||
|
if (gateway instanceof ExclusiveGateway) {
|
||||||
|
handleExclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes);
|
||||||
|
} else if (gateway instanceof InclusiveGateway) {
|
||||||
|
handleInclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes);
|
||||||
|
} else if (gateway instanceof ParallelGateway) {
|
||||||
|
handleParallelGateway(gateway, bpmnModel, variables, nextFlowNodes);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果不是网关,直接添加到下一个节点列表
|
||||||
|
nextFlowNodes.add((FlowNode) targetElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nextFlowNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理排他网关
|
||||||
|
*
|
||||||
|
* @param gateway 排他网关
|
||||||
|
* @param bpmnModel BPMN模型
|
||||||
|
* @param variables 流程变量
|
||||||
|
* @param nextFlowNodes 下一个执行的流程节点集合
|
||||||
|
*/
|
||||||
|
private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map<String, Object> variables, List<FlowNode> nextFlowNodes) {
|
||||||
|
// TODO @小北: 这里findOne和simulateNextFlowElements中有重复代码,需要优化,@芋道:是否重构??
|
||||||
|
SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
|
||||||
|
flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())
|
||||||
|
&& (evalConditionExpress(variables, flow.getConditionExpression())));
|
||||||
|
if (matchSequenceFlow == null) {
|
||||||
|
matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
|
||||||
|
flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId()));
|
||||||
|
// 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的
|
||||||
|
if (matchSequenceFlow == null && gateway.getOutgoingFlows().size() == 1) {
|
||||||
|
matchSequenceFlow = gateway.getOutgoingFlows().get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 遍历满足条件的 SequenceFlow 路径
|
||||||
|
if (matchSequenceFlow != null) {
|
||||||
|
FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef());
|
||||||
|
if (targetElement instanceof FlowNode) {
|
||||||
|
nextFlowNodes.add((FlowNode) targetElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理包容网关
|
||||||
|
*
|
||||||
|
* @param gateway 排他网关
|
||||||
|
* @param bpmnModel BPMN模型
|
||||||
|
* @param variables 流程变量
|
||||||
|
* @param nextFlowNodes 下一个执行的流程节点集合
|
||||||
|
*/
|
||||||
|
private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map<String, Object> variables, List<FlowNode> nextFlowNodes) {
|
||||||
|
Collection<SequenceFlow> matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(),
|
||||||
|
flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())
|
||||||
|
&& evalConditionExpress(variables, flow.getConditionExpression()));
|
||||||
|
if (CollUtil.isEmpty(matchSequenceFlows)) {
|
||||||
|
matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(),
|
||||||
|
flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId()));
|
||||||
|
// 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的
|
||||||
|
if (CollUtil.isEmpty(matchSequenceFlows) && gateway.getOutgoingFlows().size() == 1) {
|
||||||
|
matchSequenceFlows = gateway.getOutgoingFlows();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 遍历满足条件的 SequenceFlow 路径,获取目标节点
|
||||||
|
matchSequenceFlows.forEach(flow -> {
|
||||||
|
FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef());
|
||||||
|
if (targetElement instanceof FlowNode) {
|
||||||
|
nextFlowNodes.add((FlowNode) targetElement);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 处理并行网关
|
||||||
|
*
|
||||||
|
* @param gateway 排他网关
|
||||||
|
* @param bpmnModel BPMN模型
|
||||||
|
* @param variables 流程变量
|
||||||
|
* @param nextFlowNodes 下一个执行的流程节点集合
|
||||||
|
*/
|
||||||
|
private static void handleParallelGateway(Gateway gateway, BpmnModel bpmnModel, Map<String, Object> variables, List<FlowNode> nextFlowNodes) {
|
||||||
|
// 并行网关,遍历所有出口路径,获取目标节点
|
||||||
|
gateway.getOutgoingFlows().forEach(flow -> {
|
||||||
|
FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef());
|
||||||
|
if (targetElement instanceof FlowNode) {
|
||||||
|
nextFlowNodes.add((FlowNode) targetElement);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算条件表达式是否为 true 满足条件
|
* 计算条件表达式是否为 true 满足条件
|
||||||
*
|
*
|
||||||
|
|
|
@ -23,6 +23,7 @@ import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum;
|
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.BpmnModelConstants;
|
||||||
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.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;
|
||||||
|
@ -522,14 +523,10 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason()));
|
BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason()));
|
||||||
// 2.3 调用 BPM complete 去完成任务
|
// 2.3 调用 BPM complete 去完成任务
|
||||||
// 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用
|
// 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用
|
||||||
if (CollUtil.isNotEmpty(reqVO.getVariables())) {
|
// if (CollUtil.isNotEmpty(reqVO.getVariables())) {
|
||||||
Map<String, Object> variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables());
|
Map<String, Object> variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables());
|
||||||
// 校验传递的参数中是否存在不是下一个执行的节点
|
// 校验传递的参数中是否存在不是下一个执行的节点
|
||||||
// 当前执行的流程节点,需根据该节点寻找下一个节点
|
validateNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance);
|
||||||
String taskDefinitionKey = task.getTaskDefinitionKey();
|
|
||||||
List<FlowNode> nextFlowNodes = getNextFlowNodes(taskDefinitionKey, bpmnModel, variables);
|
|
||||||
System.out.println(nextFlowNodes);
|
|
||||||
// validateNextAssignees(userId, reqVO.getVariables(), task.getProcessInstanceId(), reqVO.getNextAssignees());
|
|
||||||
// 下个节点审批人如果不存在,则由前端传递
|
// 下个节点审批人如果不存在,则由前端传递
|
||||||
if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) {
|
if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) {
|
||||||
// 获取实例中的全部节点数据,避免后续节点的审批人被覆盖
|
// 获取实例中的全部节点数据,避免后续节点的审批人被覆盖
|
||||||
|
@ -539,104 +536,58 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||||
}
|
}
|
||||||
runtimeService.setVariables(task.getProcessInstanceId(), variables);
|
runtimeService.setVariables(task.getProcessInstanceId(), variables);
|
||||||
taskService.complete(task.getId(), variables, true);
|
taskService.complete(task.getId(), variables, true);
|
||||||
} else {
|
// } else {
|
||||||
taskService.complete(task.getId());
|
// taskService.complete(task.getId());
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 【加签专属】处理加签任务
|
// 【加签专属】处理加签任务
|
||||||
handleParentTaskIfSign(task.getParentTaskId());
|
handleParentTaskIfSign(task.getParentTaskId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据当前节点 ID 获取下一个执行的 FlowNode 列表
|
|
||||||
* @param taskDefinitionKey 当前节点 ID
|
|
||||||
* @param bpmnModel BPMN 模型
|
|
||||||
* @param variables 流程变量,用于条件判断
|
|
||||||
* @return 下一个执行的 FlowNode 列表
|
|
||||||
*/
|
|
||||||
public List<FlowNode> getNextFlowNodes(String taskDefinitionKey, BpmnModel bpmnModel, Map<String, Object> variables) {
|
|
||||||
if (taskDefinitionKey == null || bpmnModel == null) {
|
|
||||||
throw new IllegalArgumentException("taskDefinitionKey and bpmnModel cannot be null");
|
|
||||||
}
|
|
||||||
FlowNode currentNode = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey);
|
|
||||||
if (currentNode == null) {
|
|
||||||
throw new IllegalArgumentException("FlowElement with given taskDefinitionKey not found in BpmnModel");
|
|
||||||
}
|
|
||||||
List<FlowNode> nextFlowNodes = new ArrayList<>();
|
|
||||||
resolveNextNodes(currentNode, bpmnModel, variables, nextFlowNodes);
|
|
||||||
return nextFlowNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 递归解析下一个执行节点
|
|
||||||
* @param currentNode 当前节点
|
|
||||||
* @param bpmnModel BPMN 模型
|
|
||||||
* @param variables 流程变量,用于条件判断
|
|
||||||
* @param nextFlowNodes 存储下一个执行节点的列表
|
|
||||||
*/
|
|
||||||
private void resolveNextNodes(FlowNode currentNode, BpmnModel bpmnModel, Map<String, Object> variables, List<FlowNode> nextFlowNodes) {
|
|
||||||
List<SequenceFlow> outgoingFlows = currentNode.getOutgoingFlows();
|
|
||||||
for (SequenceFlow sequenceFlow : outgoingFlows) {
|
|
||||||
if (!shouldFollowSequenceFlow(currentNode, sequenceFlow, variables)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
FlowElement targetElement = bpmnModel.getFlowElement(sequenceFlow.getTargetRef());
|
|
||||||
if (targetElement instanceof FlowNode targetNode) {
|
|
||||||
if (targetNode instanceof Gateway) {
|
|
||||||
// 如果目标节点是网关,递归处理
|
|
||||||
resolveNextNodes(targetNode, bpmnModel, variables, nextFlowNodes);
|
|
||||||
}else {
|
|
||||||
nextFlowNodes.add(targetNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否应该遵循当前序列流
|
|
||||||
* @param currentNode 当前节点
|
|
||||||
* @param sequenceFlow 序列流
|
|
||||||
* @param variables 流程变量,用于条件判断
|
|
||||||
* @return 是否应该遵循该序列流
|
|
||||||
*/
|
|
||||||
private boolean shouldFollowSequenceFlow(FlowNode currentNode, SequenceFlow sequenceFlow, Map<String, Object> variables) {
|
|
||||||
if (currentNode instanceof ExclusiveGateway) {
|
|
||||||
String conditionExpression = sequenceFlow.getConditionExpression();
|
|
||||||
return conditionExpression == null || BpmnModelUtils.evalConditionExpress(variables, conditionExpression);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验传递的参数中是否存在不是下一个执行的节点
|
* 校验传递的参数中是否存在不是下一个执行的节点
|
||||||
*
|
*
|
||||||
* @param loginUserId 流程发起人
|
* @param taskDefinitionKey 当前任务节点id
|
||||||
* @param processInstanceId 流程实例id
|
* @param variables 流程变量
|
||||||
* @param nextActivityNodes 下一个执行节点信息 {节点id : [审批人id,审批人id]}
|
* @param bpmnModel 流程模型
|
||||||
|
* @param nextActivityNodes 下一个节点审批人集合(参数)
|
||||||
*/
|
*/
|
||||||
private void validateNextAssignees(Long loginUserId, Map<String, Object> variables,String processInstanceId,
|
private void validateNextAssignees(String taskDefinitionKey, Map<String, Object> variables, BpmnModel bpmnModel,
|
||||||
Map<String, List<Long>> nextActivityNodes){
|
Map<String, List<Long>> nextActivityNodes,ProcessInstance processInstance){
|
||||||
// 1、查询流程【预测】的全部信息
|
|
||||||
BpmApprovalDetailRespVO approvalDetail = processInstanceService.getApprovalDetail(loginUserId,
|
// 1、获取当前任务节点的信息
|
||||||
new BpmApprovalDetailReqVO().setProcessVariables(variables).setProcessInstanceId(processInstanceId));
|
FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey);
|
||||||
// 2、获取预测节点的信息
|
// 2、获取下一个应该执行的节点集合
|
||||||
List<BpmApprovalDetailRespVO.ActivityNode> activityNodes = approvalDetail.getActivityNodes();
|
List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables);
|
||||||
if (CollUtil.isNotEmpty(activityNodes)) {
|
// 3、比较前端传递的节点和预测的下一个节点是否匹配,匹配则将该节点设置上审批人
|
||||||
// 2.1、获取节点中的审批人策略为【发起人自选】且状态为【未执行】的节点
|
for (FlowNode nextFlowNode : nextFlowNodes) {
|
||||||
// TODO 获取下一个执行节点
|
// 获取下一个执行节点的属性 是否为 发起人自选
|
||||||
List<BpmApprovalDetailRespVO.ActivityNode> notStartActivityNodes = activityNodes.stream().filter(node ->
|
Map<String, List<ExtensionElement>> extensionElements = nextFlowNode.getExtensionElements();
|
||||||
BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())
|
List<ExtensionElement> elements = extensionElements.get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY);
|
||||||
&& BpmTaskStatusEnum.NOT_START.getStatus().equals(node.getStatus())
|
if (CollUtil.isEmpty(elements)){
|
||||||
&& CollUtil.isEmpty(node.getCandidateUsers())).toList();
|
continue;
|
||||||
// 3、校验传递的参数中是否存在不是下一个节点的信息
|
}
|
||||||
for (Map.Entry<String, List<Long>> nextActivityNode : nextActivityNodes.entrySet()) {
|
// 获取节点中的审批人策略
|
||||||
if (notStartActivityNodes.stream().noneMatch(taskNode -> taskNode.getId().equals(nextActivityNode.getKey()))) {
|
Integer candidateStrategy = Integer.valueOf(elements.get(0).getElementText());
|
||||||
log.error("[checkNextActivityNodes][ ({}) 不是下一个执行的流程节点!]", nextActivityNode.getKey());
|
// 获取流程实例中的发起人自选审批人
|
||||||
throw exception(TASK_START_USER_SELECT_NODE_NOT_EXISTS, nextActivityNode.getKey());
|
Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables());
|
||||||
|
List<Long> startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId());
|
||||||
|
if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) {
|
||||||
|
// 先判断节点是否存在
|
||||||
|
if (!nextActivityNodes.containsKey(nextFlowNode.getId())){
|
||||||
|
throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName());
|
||||||
|
}
|
||||||
|
// 如果节点存在,则判断节点中的审批人策略是否为 发起人自选
|
||||||
|
List<Long> nextAssignees = nextActivityNodes.get(nextFlowNode.getId());
|
||||||
|
// 3.1、如果前端传递的节点为空,则抛出异常
|
||||||
|
if (CollUtil.isEmpty(nextAssignees)) {
|
||||||
|
throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue