10. Task node / TaskController
1. Query all tasks
Query all task nodes, mainly calling the methods provided by the historyService API. See the notes for detailed logic.
@GetMapping(value = "/list") public Result list(TaskQueryVo taskQueryVo) { // Use the historyservice API to build query factories that can enter criteria HistoricTaskInstanceQuery query = createHistoricTaskInstanceQuery(taskQueryVo); //Execute API query and sorting FlowablePage page = this.pageList(taskQueryVo, query, TaskListWrapper.class, allowedSortProperties, HistoricTaskInstanceQueryProperty.START); return Result.ok(page); }
2. Completed tasks
My to-do list mainly calls the methods provided by the historyService API (as above) and adds query criteria. See the notes for detailed logic.
@GetMapping(value = "/listDone") public Result listDone(TaskQueryVo taskQueryVo) { //Create a query factory according to the historyService api HistoricTaskInstanceQuery query = createHistoricTaskInstanceQuery(taskQueryVo); // Add query criteria to assembly parameters. The criteria are completed / self submitted / self approved query.finished().or().taskAssignee(SecurityUtils.getUserId()) .taskOwner(SecurityUtils.getUserId()).endOr(); //Execute API query and sorting FlowablePage page = this.pageList(taskQueryVo, query, TaskListWrapper.class, allowedSortProperties, HistoricTaskInstanceQueryProperty.START); return Result.ok(page); }
3. My to-do list
My to-do list mainly calls the methods provided by the historyService API (as above) and adds query criteria. See the notes for detailed logic.
@GetMapping(value = "/listTodo") public Result listTodo(TaskQueryVo taskQueryVo) { String userId = SecurityUtils.getUserId(); // Create a query factory according to the historyService api TaskQuery query = createTaskQuery(taskQueryVo); // The spelling parameter assembly query condition is to do and the approver is himself query.taskCategory(FlowableConstant.CATEGORY_TODO); query.or().taskCandidateOrAssigned(userId).taskOwner(userId).endOr(); // Call API method query FlowablePage page = this.pageList(taskQueryVo, query, TaskTodoListWrapper.class, allowedSortProperties, TaskQueryProperty.CREATE_TIME); return Result.ok(page); }
4. Query task details
My to-do task list, query the task node information, assemble all the details and return. See the notes for the detailed logic.
@GetMapping(value = "/queryById") public Result queryById(@RequestParam String taskId) { //Call the service method TaskResponse task = flowableTaskService.getTask(taskId); return Result.ok(task); } @Override public TaskResponse getTask(String taskId) { String userId = SecurityUtils.getUserId(); //Query the current task information and verify whether you have permission to view it HistoricTaskInstance taskHis = permissionService.validateReadPermissionOnTask(taskId, userId, true, true); TaskResponse rep = null; ProcessDefinition processDefinition = null; String formKey = null; Object renderedTaskForm = null; HistoricTaskInstance parentTask = null; //Validation process definition if (StringUtils.isNotEmpty(taskHis.getProcessDefinitionId())) { //Query process definition information processDefinition = repositoryService.getProcessDefinition(taskHis.getProcessDefinitionId()); //Mounted form information formKey = formService.getTaskFormKey(processDefinition.getId(), taskHis.getTaskDefinitionKey()); if (taskHis.getEndTime() == null && formKey != null && formKey.length() > 0) { //Get form json renderedTaskForm = formService.getRenderedTaskForm(taskId); } } if (StringUtils.isNotEmpty(taskHis.getParentTaskId())) { parentTask = historyService.createHistoricTaskInstanceQuery().taskId(taskHis.getParentTaskId()).singleResult(); } rep = new TaskResponse(taskHis, processDefinition, parentTask, null); rep.setFormKey(formKey); rep.setRenderedTaskForm(renderedTaskForm); //Complete identity information fillPermissionInformation(rep, taskHis, userId); // Complete approver information populateAssignee(taskHis, rep); rep.setInvolvedPeople(getInvolvedUsers(taskId)); Task task = null; if (taskHis.getEndTime() == null) { task = taskService.createTaskQuery().taskId(taskId).singleResult(); rep.setSuspended(task.isSuspended()); rep.setDelegationState(task.getDelegationState()); } //Will use someone to join the entity rep.setOwnerName(this.getUserName(taskHis.getOwner())); //Add approver to entity rep.setAssigneeName(this.getUserName(taskHis.getAssignee())); return rep; }
5. Transfer tasks
The transfer business during approval is to add a process opinion to the current approval and reset the new approver. See notes for detailed logic.
@PutMapping(value = "/assign") public Result assign(@RequestBody TaskRequest taskRequest) { //Call the service method flowableTaskService.assignTask(taskRequest); return Result.ok(); } public void assignTask(TaskRequest taskRequest) { String taskId = taskRequest.getTaskId(); String assignee = taskRequest.getUserId(); String userId = SecurityUtils.getUserId(); //Get task information Task task = permissionService.validateAssignPermissionOnTask(taskId, userId, assignee); //Add task process comments this.addComment(taskId, task.getProcessInstanceId(), userId, CommentTypeEnum.ZB, taskRequest.getMessage()); //Reset approver taskService.setAssignee(task.getId(), assignee); }
6. Delegate tasks
The delegation business in the approval process is the same as above, except that the provided API needs to be called. See the notes for the detailed logic.
@PutMapping(value = "/delegate") public Result delegate(@RequestBody TaskRequest taskRequest) { //Call the service method flowableTaskService.delegateTask(taskRequest); return Result.ok(); } public void delegateTask(TaskRequest taskRequest) { String taskId = taskRequest.getTaskId(); String delegater = taskRequest.getUserId(); String userId =SecurityUtils.getUserId(); //Get task node information Task task = permissionService.validateDelegatePermissionOnTask(taskId, userId, delegater); //Add a process comment this.addComment(taskId, task.getProcessInstanceId(), userId, CommentTypeEnum.WP, taskRequest.getMessage()); //After calling API and delegating approval, the current person needs to approve again taskService.delegateTask(task.getId(), delegater); }
7. Claim task
For example, in the case of group approval, many people may be able to approve the task node in the next node. At this time, one of these people needs to claim the task. After claiming, others cannot find it in the to-do list. The API provides a claim method. See the notes for the detailed logic.
@PutMapping(value = "/claim") public Result claim(@RequestBody TaskRequest taskRequest) { // Call the service provider method flowableTaskService.claimTask(taskRequest); return Result.ok(); } public void claimTask(TaskRequest taskRequest) { String taskId = taskRequest.getTaskId(); String userId = SecurityUtils.getUserId(); //Get task node information TaskInfo task = permissionService.validateReadPermissionOnTask2(taskId, userId, false, false); if (task.getAssignee() != null && task.getAssignee().length() > 0) { throw new FlowableNoPermissionException("User does not have permission"); } //Add process comments this.addComment(taskId, task.getProcessInstanceId(), userId, CommentTypeEnum.RL, taskRequest.getMessage()); //The claim task (and the specified approver can only be the current one) is considered to be the specific user who sets the approver taskService.claim(taskId, userId); }
8. Cancellation of claim
Contrary to the above, go back to the initial state and mainly call API methods. See the notes for detailed logic.
@PutMapping(value = "/unclaim") public Result unclaim(@RequestBody TaskRequest taskRequest) { //Call the service method flowableTaskService.unclaimTask(taskRequest); return Result.ok(); } public void unclaimTask(TaskRequest taskRequest) { String taskId = taskRequest.getTaskId(); String userId = SecurityUtils.getUserId(); TaskInfo task = this.getTaskNotNull(taskId); if (!userId.equals(task.getAssignee())) { throw new FlowableNoPermissionException("User does not have permission"); } if (FlowableConstant.CATEGORY_TO_READ.equals(task.getCategory())) { throw new FlowableNoPermissionException("User cannot unclaim the read task"); } if (FlowableConstant.INITIATOR.equals(task.getTaskDefinitionKey())) { throw new FlowableNoPermissionException("Initiator cannot unclaim the task"); } //Add process comments this.addComment(taskId, task.getProcessInstanceId(), userId, CommentTypeEnum.QXRL, taskRequest.getMessage()); //Call API taskService.unclaim(taskId); // Determine whether to cancel the claim if (permissionService.isTaskPending((Task)task)) { //Call API taskService.resolveTask(taskId, null); } }
9. Complete the task
Submit the task, mainly to call the complete() method of the API, and put the parameters required by the splicing API on the method. See the notes for the detailed logic.
@PutMapping(value = "/complete") public Result complete(@RequestBody TaskRequest taskRequest) { //Call the service method flowableTaskService.completeTask(taskRequest); return Result.ok(); } public void completeTask(TaskRequest taskRequest) { String taskId = taskRequest.getTaskId(); String currUserId = SecurityUtils.getUserId() Task task = getTaskNotNull(taskId); //Judge whether the user has permission to approve if (!permissionService.isTaskOwnerOrAssignee(currUserId, task)) { if (StringUtils.isEmpty(task.getScopeType()) && !permissionService.validateIfUserIsInitiatorAndCanCompleteTask(currUserId, task)) { throw new FlowableNoPermissionException("User does not have permission"); } } Map<String, Object> completeVariables = null; //Verify whether the form at the time of approval is attached to the current node if (taskRequest.getValues() != null && !taskRequest.getValues().isEmpty()) { //Gets the input value of the mount form completeVariables = taskRequest.getValues(); // Allow task form to modify process form scenario begin // Agreement with the front end: the process form variable name is processInstanceFormData, and the value of the variable can be modified only when the process form startFormKey=taskFormKey, so as to prevent malicious nodes from modifying the content of the process form if (completeVariables.containsKey(FlowableConstant.PROCESS_INSTANCE_FORM_DATA)) { String startFormKey = formService.getStartFormKey(task.getProcessDefinitionId()); String taskFormKey = formService.getTaskFormKey(task.getProcessDefinitionId(), task.getTaskDefinitionKey()); boolean modifyProcessInstanceFormData = CommonFlowableUtil.isNotEmptyStr(startFormKey) && CommonFlowableUtil.isNotEmptyStr(taskFormKey) && startFormKey.equals(taskFormKey); if (!modifyProcessInstanceFormData) { throw new FlowableNoPermissionException("User does not have permission"); } } // Allow task forms to modify process form scenarios // Non countersign user node. Process variables are set by default__ taskDefinitionKey__=currUserId is used to store the executor of the node, and the latest Executor shall prevail UserTask userTask = (UserTask)FlowableUtils.getFlowElement(repositoryService, task.getProcessDefinitionId(), task.getTaskDefinitionKey()); if (userTask != null && !userTask.hasMultiInstanceLoopCharacteristics()) { completeVariables.put("__" + task.getTaskDefinitionKey() + "__", currUserId); } } else { // Non countersign user node. Process variables are set by default__ taskDefinitionKey__=currUserId is used to store the executor of the node, and the latest Executor shall prevail UserTask userTask = (UserTask)FlowableUtils.getFlowElement(repositoryService, task.getProcessDefinitionId(), task.getTaskDefinitionKey()); if (userTask != null && !userTask.hasMultiInstanceLoopCharacteristics()) { completeVariables = new HashMap<>(1); completeVariables.put("__" + task.getTaskDefinitionKey() + "__", currUserId); } } //Add process comments this.addComment(taskId, task.getProcessInstanceId(), currUserId, FlowableConstant.INITIATOR.equals(task.getTaskDefinitionKey()) ? CommentTypeEnum.CXTJ : CommentTypeEnum.WC, taskRequest.getMessage()); // Process CC if (CommonFlowableUtil.isNotEmptyObject(taskRequest.getCcToVos())) { managementService.executeCommand(new AddCcIdentityLinkCmd(task.getProcessInstanceId(), task.getId(), currUserId, taskRequest.getCcToVos())); } //Add approver if (task.getAssignee() == null || !task.getAssignee().equals(currUserId)) { taskService.setAssignee(taskId, currUserId); } // Judge whether it is completed in cooperation or normal circulation if (permissionService.isTaskPending(task)) { taskService.resolveTask(taskId, completeVariables); // If the current performer is the task owner, complete the task directly if (currUserId.equals(task.getOwner())) { //Call api taskService.complete(taskId, completeVariables); } } else { //Call api taskService.complete(taskId, completeVariables); } }
1. Processing CC
In the browsing process, you can see that there are CC people processing and calling custom classes. It mainly assembles the details of the CC and calls the provided API methods.
public Void execute(CommandContext commandContext) { // Get process information ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext); ExecutionEntity processInstance = executionEntityManager.findById(processInstanceId); if (processInstance == null) { throw new FlowableObjectNotFoundException("Cannot find process instance with id " + processInstanceId, ExecutionEntity.class); } // Obtain personnel specific information for (CcToVo ccTo : ccToVos) { IdentityLinkUtil.createProcessInstanceIdentityLink(processInstance, ccTo.getUserId(), null, FlowableConstant.CC); } this.createCcComment(commandContext); return null; } protected void createCcComment(CommandContext commandContext) { CommentEntityManager commentEntityManager = CommandContextUtil.getCommentEntityManager(commandContext); CommentEntity comment = (CommentEntity)commentEntityManager.create(); // Assembly CC information comment.setProcessInstanceId(processInstanceId); comment.setUserId(userId); comment.setType(FlowableConstant.CC); comment.setTime(CommandContextUtil.getProcessEngineConfiguration(commandContext).getClock().getCurrentTime()); comment.setTaskId(taskId); comment.setAction("AddCcTo"); String ccToStr = StringUtils.arrayToCommaDelimitedString((Object[])ccToVos); comment.setMessage(ccToStr); comment.setFullMessage(ccToStr); // Call api commentEntityManager.insert(comment); }
10. Termination process
Only the current person and the administrator can terminate the process (such as filling in the form incorrectly). After assembling the process information, directly call the API to change the process state. See notes for detailed logic.
@PutMapping(value = "/stopProcessInstance") public Result stopProcessInstance(@RequestBody TaskRequest taskRequest) { // Call the service provider method flowableTaskService.stopProcessInstance(taskRequest); return Result.ok(); } public void stopProcessInstance(TaskRequest taskRequest) { String taskId = taskRequest.getTaskId(); String userId = SecurityUtils.getUserId(); //Verify that you have permission to end the process ProcessInstance processInstance = permissionService.validateStopProcessInstancePermissionOnTask(taskId, userId); //Get the object of bpmn BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); if (bpmnModel != null) { Process process = bpmnModel.getMainProcess(); List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class, false); //Number of query end nodes if (endNodes != null && endNodes.size() > 0) { //Add process comments this.addComment(taskId, processInstance.getProcessInstanceId(), userId, CommentTypeEnum.ZZ, taskRequest.getMessage()); //Get the first end node id String endId = endNodes.get(0).getId(); //Get process definition information List<Execution> executions = runtimeService.createExecutionQuery().parentId(processInstance.getProcessInstanceId()).list(); List<String> executionIds = new ArrayList<>(); executions.forEach(execution -> executionIds.add(execution.getId())); //Call api to change status to inactive runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, endId) .changeState(); } } }
11. Initiation process - query form information
When initiating a process, render the form information to be filled in. It mainly refers to calling API. See notes for detailed logic.
@GetMapping(value = "/renderedTaskForm") public ResultrenderedTaskForm(@RequestParam String taskId) { // Authentication user permissionService.validateReadPermissionOnTask2(taskId, SecurityUtils.getUserId(), true, true); // Execute query call api Object renderedTaskForm = formService.getRenderedTaskForm(taskId); return Result.ok(renderedTaskForm); }
12. Execute approval pop-up page
The main output is the applicant form and the approval form information to be filled in. Use the API to query the information and assemble it back. See notes for detailed logic.
@GetMapping(value = "/executeTaskData") public Result<ExecuteTaskDataVo> executeTaskData(@RequestParam String taskId) { // Verify permissions Task task = permissionService.validateReadPermissionOnTask2(taskId, SecurityUtils.getUserId(), true, true); //Query bpmn information Process process = repositoryService.getBpmnModel(task.getProcessDefinitionId()).getMainProcess(); UserTask userTask = (UserTask)process.getFlowElement(task.getTaskDefinitionKey(), true); if (userTask == null) { throw new FlowableObjectNotFoundException("Can not find userTask by id " + task.getTaskDefinitionKey()); } //Assemble the approver form and the form required for the current approval String startFormKey = formService.getStartFormKey(task.getProcessDefinitionId()); String taskFormKey = formService.getTaskFormKey(task.getProcessDefinitionId(), task.getTaskDefinitionKey()); Object renderedStartForm = formService.getRenderedStartForm(task.getProcessDefinitionId()); Object renderedTaskForm = formService.getRenderedTaskForm(taskId); Map<String, Object> variables = runtimeService.getVariables(task.getProcessInstanceId()); //Get process instance information ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult(); Boolean showBusinessKey = isShowBusinessKey(task.getProcessDefinitionId()); //Assembly output parameters ExecuteTaskDataVo executeTaskDataVo = new ExecuteTaskDataVo(); executeTaskDataVo.setStartUserId(processInstance.getStartUserId()); executeTaskDataVo.setStartFormKey(startFormKey); executeTaskDataVo.setTaskFormKey(taskFormKey); executeTaskDataVo.setRenderedStartForm(renderedStartForm); executeTaskDataVo.setRenderedTaskForm(renderedTaskForm); executeTaskDataVo.setVariables(variables); executeTaskDataVo.setShowBusinessKey(showBusinessKey); // The current task is the initiator if (FlowableConstant.INITIATOR.equals(task.getTaskDefinitionKey())) { executeTaskDataVo.setInitiator(true); } String buttons = FlowableUtils.getFlowableAttributeValue(userTask, FlowableConstant.BUTTONS); if (buttons != null) { executeTaskDataVo.setButtons(buttons.split(",")); } historyService.createHistoricVariableInstanceQuery().processInstanceId("ss").variableNameLike("ss"); return Result.ok(executeTaskDataVo); }
13. Return tasks
During the approval process, you can return the task to a previous node.
1. Returnable task list
When returning, you can specify specific nodes. You need to query the previous node list and assemble it into an entity to return.
@GetMapping(value = "/backNodes") public Result backNodes(@RequestParam String taskId) { Resultresult = new Result<>(); // Call the service method List<FlowNodeResponse> datas = flowableTaskService.getBackNodes(taskId); return Result.ok(datas); } public void stopProcessInstance(TaskRequest taskRequest) { String taskId = taskRequest.getTaskId(); String userId = SecurityUtils.getUserId(); // Verify that you have permission to end the process ProcessInstance processInstance = permissionService.validateStopProcessInstancePermissionOnTask(taskId, userId); // Get the object of bpmn BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); if (bpmnModel != null) { Process process = bpmnModel.getMainProcess(); List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class, false); // Number of query end nodes if (endNodes != null && endNodes.size() > 0) { // Add process comments this.addComment(taskId, processInstance.getProcessInstanceId(), userId, CommentTypeEnum.ZZ, taskRequest.getMessage()); // Get the first end node id String endId = endNodes.get(0).getId(); // Get process definition information List<Execution> executions = runtimeService.createExecutionQuery().parentId(processInstance.getProcessInstanceId()).list(); List<String> executionIds = new ArrayList<>(); executions.forEach(execution -> executionIds.add(execution.getId())); // Call api to change status to inactive runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, endId) .changeState(); } } }
2. Execute fallback
Use the provided api to change the task status. Before, in addition to many verification judgments, see the notes for detailed logic.
@PutMapping(value = "/back") public Result back(@RequestBody TaskRequest taskRequest) { // Call the service method flowableTaskService.backTask(taskRequest); return Result.ok(); } public void backTask(TaskRequest taskRequest) { String taskId = taskRequest.getTaskId(); String userId = SecurityUtils.getUserId(); Task task = permissionService.validateExcutePermissionOnTask(taskId, userId); // Verify that you have permission to fallback permissionService.validateTaskHasButtonPermission(task, ButtonsEnum.BACK); String backSysMessage = "Back to" + taskRequest.getActivityName() + ". "; // Add process comments this.addComment(taskId, task.getProcessInstanceId(), userId, CommentTypeEnum.TH, backSysMessage + taskRequest.getMessage()); // Execute BackUserTaskCmd method String targetRealActivityId = managementService .executeCommand(new BackUserTaskCmd(runtimeService, taskRequest.getTaskId(), taskRequest.getActivityId())); // Return to the initiator. Return to the initiator. The task executor is set as the initiator by default if (FlowableConstant.INITIATOR.equals(targetRealActivityId)) { //Get the username of the initiator String initiator = runtimeService.createProcessInstanceQuery() .processInstanceId(task.getProcessInstanceId()).singleResult().getStartUserId(); //Get start node information List<Task> newTasks = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); for (Task newTask : newTasks) { // Agreement: the initiator node is__ initiator__ if (FlowableConstant.INITIATOR.equals(newTask.getTaskDefinitionKey())) { if (CommonFlowableUtil.isEmpty(newTask.getAssignee())) { //Set approver information taskService.setAssignee(newTask.getId(), initiator); } } } } } public class BackUserTaskCmd implements Command<String>, Serializable { public String execute(CommandContext commandContext) { if (targetActivityId == null || targetActivityId.length() == 0) { throw new FlowableException("TargetActivityId cannot be empty"); } /// v6.5.1.28 TaskEntity task = CommandContextUtil.getProcessEngineConfiguration().getTaskServiceConfiguration() .getTaskService().getTask(taskId); if (task == null) { throw new FlowableObjectNotFoundException("task " + taskId + " doesn't exist", Task.class); } // Get task specific information String sourceActivityId = task.getTaskDefinitionKey(); String processInstanceId = task.getProcessInstanceId(); String processDefinitionId = task.getProcessDefinitionId(); Process process = ProcessDefinitionUtil.getProcess(processDefinitionId); FlowNode sourceFlowElement = (FlowNode)process.getFlowElement(sourceActivityId, true); // Only return from user task is supported if (!(sourceFlowElement instanceof UserTask)) { throw new FlowableException("Task with id:" + taskId + " is not a UserTask"); } FlowNode targetFlowElement = (FlowNode)process.getFlowElement(targetActivityId, true); // Returning the node to the current node is unreachable and cannot be returned if (!FlowableUtils.isReachable(process, targetFlowElement, sourceFlowElement)) { throw new FlowableException("Cannot back to [" + targetActivityId + "]"); } // ps: if the target node is inside the sub process relative to the current node, it cannot be returned directly. At present, it can only be returned to the sub process start node String[] sourceAndTargetRealActivityId = FlowableUtils.getSourceAndTargetRealActivityId(sourceFlowElement, targetFlowElement); // The current node ID of the actual operation String sourceRealActivityId = sourceAndTargetRealActivityId[0]; // Target node ID of the actual operation String targetRealActivityId = sourceAndTargetRealActivityId[1]; Map<String, Set<String>> specialGatewayNodes = FlowableUtils.getSpecialGatewayElements(process); // list of parallel gateways in which the current node is located List<String> sourceInSpecialGatewayList = new ArrayList<>(); // list of parallel gateways where the target node is located List<String> targetInSpecialGatewayList = new ArrayList<>(); setSpecialGatewayList(sourceRealActivityId, targetRealActivityId, specialGatewayNodes, sourceInSpecialGatewayList, targetInSpecialGatewayList); // Node ID that should actually be filtered Set<String> sourceRealAcitivtyIds = null; // If the returned target node is in the parallel gateway relative to the current node, find the parallel gateway closest to the current node for subsequent special processing String targetRealSpecialGateway = null; // 1. Neither the target node nor the current node is in the parallel gateway if (targetInSpecialGatewayList.isEmpty() && sourceInSpecialGatewayList.isEmpty()) { sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId); } // 2. The target node is not in the parallel gateway, and the current node is in the parallel gateway else if (targetInSpecialGatewayList.isEmpty() && !sourceInSpecialGatewayList.isEmpty()) { sourceRealAcitivtyIds = specialGatewayNodes.get(sourceInSpecialGatewayList.get(0)); } // 3. The target node is in the parallel gateway and the current node is not in the parallel gateway else if (!targetInSpecialGatewayList.isEmpty() && sourceInSpecialGatewayList.isEmpty()) { sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId); targetRealSpecialGateway = targetInSpecialGatewayList.get(0); } // 4. Both the target node and the current node are in the parallel gateway else { int diffSpecialGatewayLevel = FlowableUtils.getDiffLevel(sourceInSpecialGatewayList, targetInSpecialGatewayList); // On the same layer and branch of the parallel gateway if (diffSpecialGatewayLevel == -1) { sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId); } else { // The innermost parallel gateway of the current node is not included by the innermost parallel gateway of the target node // Or it is understood that the current node is outside the parallel gateway relative to the target node // Filter only the execution of the current node if (sourceInSpecialGatewayList.size() == diffSpecialGatewayLevel) { sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId); } // The current node is in the parallel gateway relative to the target node, and the execution of all nodes in the parallel gateway closest to the target node shall be filtered else { sourceRealAcitivtyIds = specialGatewayNodes.get(sourceInSpecialGatewayList.get(diffSpecialGatewayLevel)); } // The innermost parallel gateway of the target node contains the innermost parallel gateway of the current node // Or it is understood that the target node is outside the parallel gateway relative to the current node // No treatment if (targetInSpecialGatewayList.size() == diffSpecialGatewayLevel) { } // The target node is in the parallel gateway relative to the current node else { targetRealSpecialGateway = targetInSpecialGatewayList.get(diffSpecialGatewayLevel); } } } // Filter the execution to be processed List<ExecutionEntity> realExecutions = this.getRealExecutions(commandContext, processInstanceId, task.getExecutionId(), sourceRealActivityId, sourceRealAcitivtyIds); // Execute the return and directly jump to the actual targetRealActivityId List<String> realExecutionIds = realExecutions.stream().map(ExecutionEntity::getId).collect(Collectors.toList()); //Execution API runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId) .moveExecutionsToSingleActivityId(realExecutionIds, targetRealActivityId).changeState(); // The target node is in the parallel gateway relative to the current node, which requires special processing. It is necessary to manually generate the execution data of the parallel gateway sink node (_end) if (targetRealSpecialGateway != null) { createTargetInSpecialGatewayEndExecutions(commandContext, realExecutions, process, targetInSpecialGatewayList, targetRealSpecialGateway); } return targetRealActivityId; } }
11. Task authorization / TaskIdentityLinkController
Task authorization is not a required business process and is omitted.