package com.dl.workflow.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.dl.common.core.service.UserService; import com.dl.common.exception.ServiceException; import com.dl.common.helper.LoginHelper; import com.dl.common.utils.StringUtils; import com.dl.flowable.common.constant.ProcessConstants; import com.dl.flowable.common.constant.TaskConstants; import com.dl.flowable.common.enums.FlowComment; import com.dl.flowable.common.enums.ProcessStatus; import com.dl.flowable.factory.FlowServiceFactory; import com.dl.flowable.flow.CustomProcessDiagramGenerator; import com.dl.flowable.flow.FlowableUtils; import com.dl.flowable.utils.ModelUtils; import com.dl.flowable.utils.TaskUtils; import com.dl.workflow.domain.bo.WfTaskBo; import com.dl.workflow.service.IWfCopyService; import com.dl.workflow.service.IWfTaskService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.flowable.bpmn.constants.BpmnXMLConstants; import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.*; import org.flowable.common.engine.api.FlowableException; import org.flowable.common.engine.api.FlowableObjectNotFoundException; import org.flowable.common.engine.impl.identity.Authentication; import org.flowable.engine.ProcessEngineConfiguration; import org.flowable.engine.history.HistoricActivityInstance; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.Execution; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.image.ProcessDiagramGenerator; import org.flowable.task.api.DelegationState; import org.flowable.task.api.Task; import org.flowable.task.api.history.HistoricTaskInstance; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.InputStream; import java.util.*; import java.util.stream.Collectors; /** * @author KonBAI * @createTime 2022/3/10 00:12 */ @RequiredArgsConstructor @Service @Slf4j public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskService { private final UserService sysUserService; private final IWfCopyService copyService; /** * 完成任务 * * @param taskBo 请求实体参数 */ @Transactional(rollbackFor = Exception.class) @Override public void complete(WfTaskBo taskBo) { Task task = taskService.createTaskQuery().taskId(taskBo.getTaskId()).singleResult(); if (Objects.isNull(task)) { throw new ServiceException("任务不存在"); } // 获取 bpmn 模型 BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId()); identityService.setAuthenticatedUserId(TaskUtils.getUserId()); if (DelegationState.PENDING.equals(task.getDelegationState())) { taskService.addComment(taskBo.getTaskId(), taskBo.getProcInsId(), FlowComment.DELEGATE.getType(), taskBo.getComment()); taskService.resolveTask(taskBo.getTaskId()); } else { taskService.addComment(taskBo.getTaskId(), taskBo.getProcInsId(), FlowComment.NORMAL.getType(), taskBo.getComment()); taskService.setAssignee(taskBo.getTaskId(), TaskUtils.getUserId()); if (ObjectUtil.isNotEmpty(taskBo.getVariables())) { // 获取模型信息 String localScopeValue = ModelUtils.getUserTaskAttributeValue(bpmnModel, task.getTaskDefinitionKey(), ProcessConstants.PROCESS_FORM_LOCAL_SCOPE); boolean localScope = Convert.toBool(localScopeValue, false); taskService.complete(taskBo.getTaskId(), taskBo.getVariables(), localScope); } else { taskService.complete(taskBo.getTaskId()); } } // 设置任务节点名称 taskBo.setTaskName(task.getName()); // 处理下一级审批人 if (StringUtils.isNotBlank(taskBo.getNextUserIds())) { this.assignNextUsers(bpmnModel, taskBo.getProcInsId(), taskBo.getNextUserIds()); } // 处理抄送用户 if (!copyService.makeCopy(taskBo)) { throw new RuntimeException("抄送任务失败"); } } /** * 拒绝任务 * * @param taskBo */ @Override @Transactional(rollbackFor = Exception.class) public void taskReject(WfTaskBo taskBo) { // 当前任务 task Task task = taskService.createTaskQuery().taskId(taskBo.getTaskId()).singleResult(); if (ObjectUtil.isNull(task)) { throw new RuntimeException("获取任务信息异常!"); } if (task.isSuspended()) { throw new RuntimeException("任务处于挂起状态"); } // 获取流程实例 ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() .processInstanceId(taskBo.getProcInsId()) .singleResult(); if (processInstance == null) { throw new RuntimeException("流程实例不存在,请确认!"); } // 获取流程定义信息 ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() .processDefinitionId(task.getProcessDefinitionId()) .singleResult(); identityService.setAuthenticatedUserId(TaskUtils.getUserId()); // 添加审批意见 taskService.addComment(taskBo.getTaskId(), taskBo.getProcInsId(), FlowComment.REJECT.getType(), taskBo.getComment()); // 设置流程状态为已终结 runtimeService.setVariable(processInstance.getId(), ProcessConstants.PROCESS_STATUS_KEY, ProcessStatus.TERMINATED.getStatus()); // 获取所有节点信息 BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); EndEvent endEvent = ModelUtils.getEndEvent(bpmnModel); // 终止流程 List executions = runtimeService.createExecutionQuery().parentId(task.getProcessInstanceId()).list(); List executionIds = executions.stream().map(Execution::getId).collect(Collectors.toList()); runtimeService.createChangeActivityStateBuilder() .processInstanceId(task.getProcessInstanceId()) .moveExecutionsToSingleActivityId(executionIds, endEvent.getId()) .changeState(); // 处理抄送用户 if (!copyService.makeCopy(taskBo)) { throw new RuntimeException("抄送任务失败"); } } /** * 退回任务 * * @param bo 请求实体参数 */ @Transactional(rollbackFor = Exception.class) @Override public void taskReturn(WfTaskBo bo) { // 当前任务 task Task task = taskService.createTaskQuery().taskId(bo.getTaskId()).singleResult(); if (ObjectUtil.isNull(task)) { throw new RuntimeException("获取任务信息异常!"); } if (task.isSuspended()) { throw new RuntimeException("任务处于挂起状态"); } // 获取流程定义信息 ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); // 获取流程模型信息 BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); // 获取当前任务节点元素 FlowElement source = ModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); // 获取跳转的节点元素 FlowElement target = ModelUtils.getFlowElementById(bpmnModel, bo.getTargetKey()); // 从当前节点向前扫描,判断当前节点与目标节点是否属于串行,若目标节点是在并行网关上或非同一路线上,不可跳转 boolean isSequential = ModelUtils.isSequentialReachable(source, target, new HashSet<>()); if (!isSequential) { throw new RuntimeException("当前节点相对于目标节点,不属于串行关系,无法回退"); } // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); List runTaskKeyList = new ArrayList<>(); runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey())); // 需退回任务列表 List currentIds = new ArrayList<>(); // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务 List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(target, runTaskKeyList, null, null); currentUserTaskList.forEach(item -> currentIds.add(item.getId())); // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因 List currentTaskIds = new ArrayList<>(); currentIds.forEach(currentId -> runTaskList.forEach(runTask -> { if (currentId.equals(runTask.getTaskDefinitionKey())) { currentTaskIds.add(runTask.getId()); } })); identityService.setAuthenticatedUserId(TaskUtils.getUserId()); // 设置回退意见 for (String currentTaskId : currentTaskIds) { taskService.addComment(currentTaskId, task.getProcessInstanceId(), FlowComment.REBACK.getType(), bo.getComment()); } try { // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetKey 跳转到的节点(1) runtimeService.createChangeActivityStateBuilder() .processInstanceId(task.getProcessInstanceId()) .moveActivityIdsToSingleActivityId(currentIds, bo.getTargetKey()).changeState(); } catch (FlowableObjectNotFoundException e) { throw new RuntimeException("未找到流程实例,流程可能已发生变化"); } catch (FlowableException e) { throw new RuntimeException("无法取消或开始活动"); } // 设置任务节点名称 bo.setTaskName(task.getName()); // 处理抄送用户 if (!copyService.makeCopy(bo)) { throw new RuntimeException("抄送任务失败"); } } /** * 获取所有可回退的节点 * * @param bo * @return */ @Override public List findReturnTaskList(WfTaskBo bo) { // 当前任务 task Task task = taskService.createTaskQuery().taskId(bo.getTaskId()).singleResult(); // 获取流程定义信息 ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); // 获取流程模型信息 BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); // 查询历史节点实例 List activityInstanceList = historyService.createHistoricActivityInstanceQuery() .processInstanceId(task.getProcessInstanceId()) .activityType(BpmnXMLConstants.ELEMENT_TASK_USER) .finished() .orderByHistoricActivityInstanceEndTime().asc() .list(); List activityIdList = activityInstanceList.stream() .map(HistoricActivityInstance::getActivityId) .filter(activityId -> !StringUtils.equals(activityId, task.getTaskDefinitionKey())) .distinct() .collect(Collectors.toList()); // 获取当前任务节点元素 FlowElement source = ModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); List elementList = new ArrayList<>(); for (String activityId : activityIdList) { FlowElement target = ModelUtils.getFlowElementById(bpmnModel, activityId); boolean isSequential = ModelUtils.isSequentialReachable(source, target, new HashSet<>()); if (isSequential) { elementList.add(target); } } return elementList; } /** * 删除任务 * * @param bo 请求实体参数 */ @Override public void deleteTask(WfTaskBo bo) { // todo 待确认删除任务是物理删除任务 还是逻辑删除,让这个任务直接通过? identityService.setAuthenticatedUserId(TaskUtils.getUserId()); taskService.deleteTask(bo.getTaskId(), bo.getComment()); } /** * 认领/签收任务 * * @param taskBo 请求实体参数 */ @Override @Transactional(rollbackFor = Exception.class) public void claim(WfTaskBo taskBo) { Task task = taskService.createTaskQuery().taskId(taskBo.getTaskId()).singleResult(); if (Objects.isNull(task)) { throw new ServiceException("任务不存在"); } taskService.claim(taskBo.getTaskId(), TaskUtils.getUserId()); } /** * 取消认领/签收任务 * * @param bo 请求实体参数 */ @Override @Transactional(rollbackFor = Exception.class) public void unClaim(WfTaskBo bo) { taskService.unclaim(bo.getTaskId()); } /** * 委派任务 * * @param bo 请求实体参数 */ @Override @Transactional(rollbackFor = Exception.class) public void delegateTask(WfTaskBo bo) { // 当前任务 task Task task = taskService.createTaskQuery().taskId(bo.getTaskId()).singleResult(); if (ObjectUtil.isEmpty(task)) { throw new ServiceException("获取任务失败!"); } StringBuilder commentBuilder = new StringBuilder(LoginHelper.getNickName()) .append("->"); String nickName = sysUserService.selectNickNameById(bo.getUserId()); if (StringUtils.isNotBlank(nickName)) { commentBuilder.append(nickName); } else { commentBuilder.append(bo.getUserId()); } if (StringUtils.isNotBlank(bo.getComment())) { commentBuilder.append(": ").append(bo.getComment()); } identityService.setAuthenticatedUserId(TaskUtils.getUserId()); // 添加审批意见 taskService.addComment(bo.getTaskId(), task.getProcessInstanceId(), FlowComment.DELEGATE.getType(), commentBuilder.toString()); // 设置办理人为当前登录人 taskService.setOwner(bo.getTaskId(), TaskUtils.getUserId()); // 执行委派 taskService.delegateTask(bo.getTaskId(), bo.getUserId()); // 设置任务节点名称 bo.setTaskName(task.getName()); // 处理抄送用户 if (!copyService.makeCopy(bo)) { throw new RuntimeException("抄送任务失败"); } } /** * 转办任务 * * @param bo 请求实体参数 */ @Override @Transactional(rollbackFor = Exception.class) public void transferTask(WfTaskBo bo) { // 当前任务 task Task task = taskService.createTaskQuery().taskId(bo.getTaskId()).singleResult(); if (ObjectUtil.isEmpty(task)) { throw new ServiceException("获取任务失败!"); } StringBuilder commentBuilder = new StringBuilder(LoginHelper.getNickName()) .append("->"); String nickName = sysUserService.selectNickNameById(bo.getUserId()); if (StringUtils.isNotBlank(nickName)) { commentBuilder.append(nickName); } else { commentBuilder.append(bo.getUserId()); } if (StringUtils.isNotBlank(bo.getComment())) { commentBuilder.append(": ").append(bo.getComment()); } identityService.setAuthenticatedUserId(TaskUtils.getUserId()); // 添加审批意见 taskService.addComment(bo.getTaskId(), task.getProcessInstanceId(), FlowComment.TRANSFER.getType(), commentBuilder.toString()); // 设置拥有者为当前登录人 taskService.setOwner(bo.getTaskId(), TaskUtils.getUserId()); // 转办任务 taskService.setAssignee(bo.getTaskId(), bo.getUserId()); // 设置任务节点名称 bo.setTaskName(task.getName()); // 处理抄送用户 if (!copyService.makeCopy(bo)) { throw new RuntimeException("抄送任务失败"); } } /** * 取消申请 * * @param bo * @return */ @Override public void stopProcess(WfTaskBo bo) { List taskList = taskService.createTaskQuery().processInstanceId(bo.getProcInsId()).list(); if (CollectionUtils.isEmpty(taskList)) { throw new RuntimeException("流程未启动或已执行完成,取消申请失败"); } ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() .processInstanceId(bo.getProcInsId()).singleResult(); BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); if (Objects.nonNull(bpmnModel)) { Process process = bpmnModel.getMainProcess(); List endNodes = process.findFlowElementsOfType(EndEvent.class, false); if (CollectionUtils.isNotEmpty(endNodes)) { Authentication.setAuthenticatedUserId(TaskUtils.getUserId()); runtimeService.setVariable(processInstance.getId(), ProcessConstants.PROCESS_STATUS_KEY, ProcessStatus.CANCELED.getStatus()); for (Task task : taskList) { taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.STOP.getType(), "取消流程"); } // 获取当前流程最后一个节点 String endId = endNodes.get(0).getId(); List executions = runtimeService.createExecutionQuery() .parentId(processInstance.getProcessInstanceId()).list(); List executionIds = new ArrayList<>(); executions.forEach(execution -> executionIds.add(execution.getId())); // 变更流程为已结束状态 runtimeService.createChangeActivityStateBuilder() .moveExecutionsToSingleActivityId(executionIds, endId).changeState(); } } } /** * 撤回流程 * * @param taskBo 请求实体参数 */ @Override @Transactional(rollbackFor = Exception.class) public void revokeProcess(WfTaskBo taskBo) { String procInsId = taskBo.getProcInsId(); String taskId = taskBo.getTaskId(); // 校验流程是否结束 ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() .processInstanceId(procInsId) .active() .singleResult(); if(ObjectUtil.isNull(processInstance)) { throw new RuntimeException("流程已结束或已挂起,无法执行撤回操作"); } // 获取待撤回任务实例 HistoricTaskInstance currTaskIns = historyService.createHistoricTaskInstanceQuery() .taskId(taskId) .taskAssignee(TaskUtils.getUserId()) .singleResult(); if (ObjectUtil.isNull(currTaskIns)) { throw new RuntimeException("当前任务不存在,无法执行撤回操作"); } // 获取 bpmn 模型 BpmnModel bpmnModel = repositoryService.getBpmnModel(currTaskIns.getProcessDefinitionId()); UserTask currUserTask = ModelUtils.getUserTaskByKey(bpmnModel, currTaskIns.getTaskDefinitionKey()); // 查找下一级用户任务列表 List nextUserTaskList = ModelUtils.findNextUserTasks(currUserTask); List nextUserTaskKeys = nextUserTaskList.stream().map(UserTask::getId).collect(Collectors.toList()); // 获取当前节点之后已完成的流程历史节点 List finishedTaskInsList = historyService.createHistoricTaskInstanceQuery() .processInstanceId(procInsId) .taskCreatedAfter(currTaskIns.getEndTime()) .finished() .list(); for (HistoricTaskInstance finishedTaskInstance : finishedTaskInsList) { // 检查已完成流程历史节点是否存在下一级中 if (CollUtil.contains(nextUserTaskKeys, finishedTaskInstance.getTaskDefinitionKey())) { throw new RuntimeException("下一流程已处理,无法执行撤回操作"); } } // 获取所有激活的任务节点,找到需要撤回的任务 List activateTaskList = taskService.createTaskQuery().processInstanceId(procInsId).list(); List revokeExecutionIds = new ArrayList<>(); identityService.setAuthenticatedUserId(TaskUtils.getUserId()); for (Task task : activateTaskList) { // 检查激活的任务节点是否存在下一级中,如果存在,则加入到需要撤回的节点 if (CollUtil.contains(nextUserTaskKeys, task.getTaskDefinitionKey())) { // 添加撤回审批信息 taskService.setAssignee(task.getId(), TaskUtils.getUserId()); taskService.addComment(task.getId(), task.getProcessInstanceId(), FlowComment.REVOKE.getType(), LoginHelper.getNickName() + "撤回流程审批"); revokeExecutionIds.add(task.getExecutionId()); } } try { runtimeService.createChangeActivityStateBuilder() .processInstanceId(procInsId) .moveExecutionsToSingleActivityId(revokeExecutionIds, currTaskIns.getTaskDefinitionKey()).changeState(); } catch (FlowableObjectNotFoundException e) { throw new RuntimeException("未找到流程实例,流程可能已发生变化"); } catch (FlowableException e) { throw new RuntimeException("执行撤回操作失败"); } } /** * 获取流程过程图 * * @param processId * @return */ @Override public InputStream diagram(String processId) { String processDefinitionId; // 获取当前的流程实例 ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult(); // 如果流程已经结束,则得到结束节点 if (Objects.isNull(processInstance)) { HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId).singleResult(); processDefinitionId = pi.getProcessDefinitionId(); } else {// 如果流程没有结束,则取当前活动节点 // 根据流程实例ID获得当前处于活动状态的ActivityId合集 ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult(); processDefinitionId = pi.getProcessDefinitionId(); } // 获得活动的节点 List highLightedFlowList = historyService.createHistoricActivityInstanceQuery() .processInstanceId(processId).orderByHistoricActivityInstanceStartTime().asc().list(); List highLightedFlows = new ArrayList<>(); List highLightedNodes = new ArrayList<>(); //高亮线 for (HistoricActivityInstance tempActivity : highLightedFlowList) { if ("sequenceFlow".equals(tempActivity.getActivityType())) { //高亮线 highLightedFlows.add(tempActivity.getActivityId()); } else { //高亮节点 highLightedNodes.add(tempActivity.getActivityId()); } } //获取流程图 BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId); ProcessEngineConfiguration configuration = processEngine.getProcessEngineConfiguration(); //获取自定义图片生成器 ProcessDiagramGenerator diagramGenerator = new CustomProcessDiagramGenerator(); return diagramGenerator.generateDiagram(bpmnModel, "png", highLightedNodes, highLightedFlows, configuration.getActivityFontName(), configuration.getLabelFontName(), configuration.getAnnotationFontName(), configuration.getClassLoader(), 1.0, true); } /** * 获取流程变量 * * @param taskId 任务ID * @return 流程变量 */ @Override public Map getProcessVariables(String taskId) { HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery() .includeProcessVariables() .finished() .taskId(taskId) .singleResult(); if (Objects.nonNull(historicTaskInstance)) { return historicTaskInstance.getProcessVariables(); } return taskService.getVariables(taskId); } /** * 启动第一个任务 * @param processInstance 流程实例 * @param variables 流程参数 */ @Override public void startFirstTask(ProcessInstance processInstance, Map variables) { // 若第一个用户任务为发起人,则自动完成任务 List tasks = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).list(); if (CollUtil.isNotEmpty(tasks)) { String userIdStr = (String) variables.get(TaskConstants.PROCESS_INITIATOR); identityService.setAuthenticatedUserId(TaskUtils.getUserId()); for (Task task : tasks) { if (StrUtil.equals(task.getAssignee(), userIdStr)) { taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.NORMAL.getType(), LoginHelper.getNickName() + "发起流程申请"); taskService.complete(task.getId(), variables); } } } } /** * 指派下一任务审批人 * @param bpmnModel bpmn模型 * @param processInsId 流程实例id * @param userIds 用户ids */ private void assignNextUsers(BpmnModel bpmnModel, String processInsId, String userIds) { // 获取所有节点信息 List list = taskService.createTaskQuery() .processInstanceId(processInsId) .list(); if (list.size() == 0) { return; } Queue assignIds = CollUtil.newLinkedList(userIds.split(",")); if (list.size() == assignIds.size()) { for (Task task : list) { taskService.setAssignee(task.getId(), assignIds.poll()); } return; } // 优先处理非多实例任务 Iterator iterator = list.iterator(); while (iterator.hasNext()) { Task task = iterator.next(); if (!ModelUtils.isMultiInstance(bpmnModel, task.getTaskDefinitionKey())) { if (!assignIds.isEmpty()) { taskService.setAssignee(task.getId(), assignIds.poll()); } iterator.remove(); } } // 若存在多实例任务,则进行动态加减签 if (CollUtil.isNotEmpty(list)) { if (assignIds.isEmpty()) { // 动态减签 for (Task task : list) { runtimeService.deleteMultiInstanceExecution(task.getExecutionId(), true); } } else { // 动态加签 for (String assignId : assignIds) { Map assignVariables = Collections.singletonMap(BpmnXMLConstants.ATTRIBUTE_TASK_USER_ASSIGNEE, assignId); runtimeService.addMultiInstanceExecution(list.get(0).getTaskDefinitionKey(), list.get(0).getProcessInstanceId(), assignVariables); } } } } }