Merge remote-tracking branch 'origin/feature/REQ-2924' into feature/REQ-2924
This commit is contained in:
commit
d05c42b606
@ -38,6 +38,7 @@ public enum BpmnTaskRespCode implements IModuleRespCode {
|
||||
PROCESS_CANT_SET_ASSIGNEE("021", "当前审批状态不允许设置审批人"),
|
||||
ASSIGNER_NUMBER_EXCEEDS_NUMBER_LIMIT("022", String.format("人员数量超过限制,节点审批人限制数量为: %d!", APPROVAL_ASSIGNER_LIMIT_NUMBER)),
|
||||
BACK_TARGET_ACTIVITY_NOT_EXISTS("023", "回退到指定节点【{}】失败!"),
|
||||
BACK_NODE_CANNOT_REACHABLE("024", "退回节点【{}】不可达,不允许退回"),
|
||||
;
|
||||
|
||||
private final String code;
|
||||
|
||||
@ -0,0 +1,91 @@
|
||||
package cn.axzo.workflow.core.common.utils;
|
||||
|
||||
import org.flowable.bpmn.model.EventSubProcess;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.bpmn.model.FlowElementsContainer;
|
||||
import org.flowable.bpmn.model.FlowNode;
|
||||
import org.flowable.bpmn.model.SequenceFlow;
|
||||
import org.flowable.bpmn.model.StartEvent;
|
||||
import org.flowable.bpmn.model.SubProcess;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class BpmnModelUtils {
|
||||
|
||||
/**
|
||||
* 节点是否可达
|
||||
* @param process
|
||||
* @param sourceElement
|
||||
* @param targetElement
|
||||
* @return
|
||||
*/
|
||||
public static boolean isReachable(org.flowable.bpmn.model.Process process, FlowNode sourceElement, FlowNode targetElement) {
|
||||
return isReachable(process, sourceElement, targetElement, new HashSet<>());
|
||||
}
|
||||
|
||||
public static boolean isReachable(org.flowable.bpmn.model.Process process, FlowNode sourceElement, FlowNode targetElement, Set<String> visitedElements) {
|
||||
// Special case: start events in an event subprocess might exist as an execution and are most likely be able to
|
||||
// reach the target
|
||||
// when the target is in the event subprocess, but should be ignored as they are not 'real' runtime executions
|
||||
// (but rather waiting for trigger)
|
||||
if (sourceElement instanceof StartEvent && isInEventSubprocess(sourceElement)) {
|
||||
return false;
|
||||
}
|
||||
// No outgoing seq flow: could be the end of eg . the process or an embedded subprocess
|
||||
if (sourceElement.getOutgoingFlows().isEmpty()) {
|
||||
visitedElements.add(sourceElement.getId());
|
||||
FlowElementsContainer parentElement = process.findParent(sourceElement);
|
||||
if (parentElement instanceof SubProcess) {
|
||||
sourceElement = (SubProcess) parentElement;
|
||||
// 子流程的结束节点,若目标节点在该子流程中,说明无法到达,返回false
|
||||
if (((SubProcess) sourceElement).getFlowElement(targetElement.getId()) != null) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (sourceElement.getId().equals(targetElement.getId())) {
|
||||
return true;
|
||||
}
|
||||
// To avoid infinite looping, we must capture every node we visit
|
||||
// and check before going further in the graph if we have already
|
||||
// visited the node.
|
||||
visitedElements.add(sourceElement.getId());
|
||||
// 当前节点能够到达子流程,且目标节点在子流程中,说明可以到达,返回true
|
||||
if (sourceElement instanceof SubProcess && ((SubProcess) sourceElement).getFlowElement(targetElement.getId()) != null) {
|
||||
return true;
|
||||
}
|
||||
List<SequenceFlow> sequenceFlows = sourceElement.getOutgoingFlows();
|
||||
if (sequenceFlows != null && !sequenceFlows.isEmpty()) {
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
String targetRef = sequenceFlow.getTargetRef();
|
||||
FlowNode sequenceFlowTarget = (FlowNode) process.getFlowElement(targetRef, true);
|
||||
if (sequenceFlowTarget != null && !visitedElements.contains(sequenceFlowTarget.getId())) {
|
||||
boolean reachable = isReachable(process, sequenceFlowTarget, targetElement, visitedElements);
|
||||
if (reachable) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static boolean isInEventSubprocess(FlowNode flowNode) {
|
||||
FlowElementsContainer flowElementsContainer = flowNode.getParentContainer();
|
||||
while (flowElementsContainer != null) {
|
||||
if (flowElementsContainer instanceof EventSubProcess) {
|
||||
return true;
|
||||
}
|
||||
if (flowElementsContainer instanceof FlowElement) {
|
||||
flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer();
|
||||
} else {
|
||||
flowElementsContainer = null;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1,34 +1,39 @@
|
||||
package cn.axzo.workflow.core.engine.cmd;
|
||||
|
||||
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskBackAuditDTO;
|
||||
import cn.axzo.workflow.core.engine.job.AsyncApproveTaskJobHandler;
|
||||
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
|
||||
import cn.axzo.workflow.core.common.utils.BpmnModelUtils;
|
||||
import cn.axzo.workflow.core.engine.job.AsyncBackTaskJobHandler;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.bpmn.model.FlowNode;
|
||||
import org.flowable.bpmn.model.Process;
|
||||
import org.flowable.common.engine.impl.interceptor.CommandContext;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
|
||||
import org.flowable.engine.impl.util.CommandContextUtil;
|
||||
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
|
||||
import org.flowable.job.service.JobService;
|
||||
import org.flowable.job.service.impl.persistence.entity.JobEntity;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.flowable.task.api.history.HistoricTaskInstance;
|
||||
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
|
||||
import org.flowable.task.service.TaskService;
|
||||
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.BACK_NODE_CANNOT_REACHABLE;
|
||||
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.BACK_TARGET_ACTIVITY_NOT_EXISTS;
|
||||
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask;
|
||||
|
||||
@Slf4j
|
||||
public class CustomBackTaskAsyncCmd extends AbstractCommand<String> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1773108485033787095L;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CustomBackTaskAsyncCmd.class);
|
||||
|
||||
private final BpmnTaskBackAuditDTO dto;
|
||||
|
||||
public CustomBackTaskAsyncCmd(BpmnTaskBackAuditDTO dto) {
|
||||
@ -41,17 +46,25 @@ public class CustomBackTaskAsyncCmd extends AbstractCommand<String> implements S
|
||||
CommandContextUtil.getProcessEngineConfiguration(commandContext);
|
||||
HistoricTaskInstanceQuery taskQuery =
|
||||
processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery();
|
||||
TaskService taskService = processEngineConfiguration.getTaskService();
|
||||
TaskService taskService = processEngineConfiguration.getTaskServiceConfiguration().getTaskService();
|
||||
|
||||
HistoricTaskInstance historicTaskInstance = taskQuery.taskId(dto.getTaskId()).singleResult();
|
||||
|
||||
Task task = taskService.createTaskQuery().taskId(dto.getTaskId()).singleResult();
|
||||
if (Objects.isNull(task)) {
|
||||
log.info("任务不存在: {}", dto.getTaskId());
|
||||
}
|
||||
validTask(historicTaskInstance, (TaskEntity) task, dto.getApprover(), null);
|
||||
TaskEntity taskEntity = taskService.getTask(dto.getTaskId());
|
||||
validTask(historicTaskInstance, taskEntity, dto.getApprover(), dto.getNodeTypes());
|
||||
|
||||
return startAsync(processEngineConfiguration, task);
|
||||
Process process = ProcessDefinitionUtil.getProcess(taskEntity.getProcessDefinitionId());
|
||||
FlowElement targetFlowElement = process.getFlowElement(dto.getToActivityId(), true);
|
||||
if (Objects.isNull(targetFlowElement)) {
|
||||
throw new WorkflowEngineException(BACK_TARGET_ACTIVITY_NOT_EXISTS, dto.getToActivityId());
|
||||
}
|
||||
FlowElement sourceFlowElement = process.getFlowElement(taskEntity.getTaskDefinitionKey(), true);
|
||||
// 退回节点到当前节点不可达到,不允许退回
|
||||
if (!BpmnModelUtils.isReachable(process, (FlowNode) sourceFlowElement, (FlowNode) targetFlowElement)) {
|
||||
throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, dto.getToActivityId());
|
||||
}
|
||||
|
||||
return startAsync(processEngineConfiguration, taskEntity);
|
||||
}
|
||||
|
||||
private String startAsync(ProcessEngineConfigurationImpl processEngineConfiguration, Task task) {
|
||||
|
||||
@ -2,18 +2,21 @@ package cn.axzo.workflow.core.engine.cmd;
|
||||
|
||||
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskBackAuditDTO;
|
||||
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
|
||||
import cn.axzo.workflow.core.common.utils.BpmnModelUtils;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.bpmn.model.FlowNode;
|
||||
import org.flowable.bpmn.model.Process;
|
||||
import org.flowable.common.engine.impl.identity.Authentication;
|
||||
import org.flowable.common.engine.impl.interceptor.CommandContext;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
|
||||
import org.flowable.engine.impl.util.CommandContextUtil;
|
||||
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
|
||||
import org.flowable.task.api.history.HistoricTaskInstance;
|
||||
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
|
||||
import org.flowable.task.service.TaskService;
|
||||
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
|
||||
|
||||
import java.io.Serializable;
|
||||
@ -26,6 +29,7 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE
|
||||
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
|
||||
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
|
||||
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.BACKED;
|
||||
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.BACK_NODE_CANNOT_REACHABLE;
|
||||
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.BACK_TARGET_ACTIVITY_NOT_EXISTS;
|
||||
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment;
|
||||
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.batchAddAttachment;
|
||||
@ -40,14 +44,14 @@ public class CustomBackTaskCmd extends AbstractCommand<Void> implements Serializ
|
||||
|
||||
private final BpmnTaskBackAuditDTO dto;
|
||||
|
||||
private static String operationDesc = "回退至";
|
||||
private static final String OPERATION_DESC = "回退至";
|
||||
|
||||
@Override
|
||||
public String paramToJsonString() {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("taskId", dto.getTaskId());
|
||||
params.put("advice", dto.getAdvice());
|
||||
params.put("operationDesc", operationDesc);
|
||||
params.put("operationDesc", OPERATION_DESC);
|
||||
params.put("attachmentList", JSON.toJSONString(dto.getAttachmentList()));
|
||||
params.put("approver", JSON.toJSONString(dto.getApprover()));
|
||||
params.put("nodeTypes", JSON.toJSONString(dto.getNodeTypes()));
|
||||
@ -64,24 +68,28 @@ public class CustomBackTaskCmd extends AbstractCommand<Void> implements Serializ
|
||||
CommandContextUtil.getProcessEngineConfiguration(commandContext);
|
||||
HistoricTaskInstanceQuery taskQuery =
|
||||
processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery();
|
||||
TaskService taskService = processEngineConfiguration.getTaskService();
|
||||
TaskService taskService = processEngineConfiguration.getTaskServiceConfiguration().getTaskService();
|
||||
|
||||
HistoricTaskInstance historicTaskInstance = taskQuery.taskId(dto.getTaskId()).singleResult();
|
||||
|
||||
TaskEntity task = (TaskEntity) taskService.createTaskQuery().taskId(dto.getTaskId()).singleResult();
|
||||
TaskEntity task = taskService.getTask(dto.getTaskId());
|
||||
validTask(historicTaskInstance, task, dto.getApprover(), dto.getNodeTypes());
|
||||
|
||||
BpmnModel bpmnModel = processEngineConfiguration.getRepositoryService().getBpmnModel(historicTaskInstance.getProcessDefinitionId());
|
||||
FlowElement flowElement = bpmnModel.getFlowElement(dto.getToActivityId());
|
||||
if (Objects.isNull(flowElement)) {
|
||||
Process process = ProcessDefinitionUtil.getProcess(task.getProcessDefinitionId());
|
||||
FlowElement targetFlowElement = process.getFlowElement(dto.getToActivityId(), true);
|
||||
if (Objects.isNull(targetFlowElement)) {
|
||||
throw new WorkflowEngineException(BACK_TARGET_ACTIVITY_NOT_EXISTS, dto.getToActivityId());
|
||||
}
|
||||
|
||||
FlowElement sourceFlowElement = process.getFlowElement(task.getTaskDefinitionKey(), true);
|
||||
// 退回节点到当前节点不可达到,不允许退回
|
||||
if (!BpmnModelUtils.isReachable(process, (FlowNode) sourceFlowElement, (FlowNode) targetFlowElement)) {
|
||||
throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, dto.getToActivityId());
|
||||
}
|
||||
batchAddAttachment(commandContext, task.getProcessInstanceId(), dto.getTaskId(), dto.getAttachmentList(), dto.getApprover());
|
||||
|
||||
Authentication.setAuthenticatedUserId(dto.getApprover().buildAssigneeId());
|
||||
addComment(commandContext, task, COMMENT_TYPE_ADVICE, dto.getAdvice());
|
||||
addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, operationDesc + flowElement.getName());
|
||||
addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, OPERATION_DESC + targetFlowElement.getName());
|
||||
Authentication.setAuthenticatedUserId(null);
|
||||
|
||||
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
|
||||
|
||||
@ -63,6 +63,7 @@ import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ASSIGNER_NUMBER
|
||||
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF;
|
||||
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_COMPLETE_FAIL_NOT_EXISTS;
|
||||
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_HAS_BEEN_COMPLETE;
|
||||
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_TYPE_MISMATCH;
|
||||
import static org.flowable.task.api.Task.DEFAULT_PRIORITY;
|
||||
|
||||
/**
|
||||
@ -154,7 +155,7 @@ public class CustomTaskHelper {
|
||||
//不包含对应的任务
|
||||
if (!nodeTypes.contains(nodeType)) {
|
||||
// log.warn(TASK_TYPE_MISMATCH.getMessage(), nodeType.getDesc(), nodeTypes.stream().map(BpmnFlowNodeType::getDesc).collect(Collectors.joining(",")));
|
||||
throw new WorkflowEngineException(ASSIGNER_NUMBER_EXCEEDS_NUMBER_LIMIT, nodeType.getDesc(), nodeTypes.stream().map(BpmnFlowNodeType::getDesc).collect(Collectors.joining(",")));
|
||||
throw new WorkflowEngineException(TASK_TYPE_MISMATCH, nodeType.getDesc(), nodeTypes.stream().map(BpmnFlowNodeType::getDesc).collect(Collectors.joining(",")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user