diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/handle/WithDrawHandle.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/handle/WithDrawHandle.java index 9f50b6d..17e6b3d 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/handle/WithDrawHandle.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/handle/WithDrawHandle.java @@ -111,10 +111,12 @@ public class WithDrawHandle { return Boolean.FALSE; } currentProgressNode = children.get(children.size() - 1); + //把当前和上一个节点和 会签或签的情况 都check出来 beforeProgressNode = checkBeforeNodeAndOr(children,currentProgressNode,thisAndOr,beforeAndOr); } else { currentProgressNode = currentProgressInfo.get(currentProgressInfo.size() - 1); + // 把当前和上一个节点和 会签或签的情况 都check出来 beforeProgressNode = checkBeforeNodeAndOr(currentProgressInfo,currentProgressNode,thisAndOr,beforeAndOr); } @@ -127,12 +129,16 @@ public class WithDrawHandle { Boolean isAndOr = Boolean.FALSE; + //判断当前子流程是否结束 如果结束了 必不能撤回 + Boolean thisSubNodeOver = Boolean.FALSE; //当前节点是 会签|或签的情况 if(CollUtil.isNotEmpty(thisAndOr)){ + Integer finishNodes = 0; for(ProgressNode n : thisAndOr){ if(Objects.nonNull(n.getFinishTime())){ //当前会签 有审批过的 那么上个会签|或签 无论如何 就不可能可以撤回了 beforeAndOr = Collections.emptyList(); + finishNodes ++; } //找到了 当前的操作人 在当前的 会签|或签 中 并且已经审批了 if(n.getUserId().equals(user.getEmployeeCode()) && @@ -142,6 +148,16 @@ public class WithDrawHandle { isAndOr = Boolean.TRUE; } } + //如果都审批过了 结束了 那说明此子流程已经结束了 不能再撤回了 + if(finishNodes.equals(thisAndOr.size())){ + return Boolean.FALSE; + } + }else{ + //如果当前不是会签|或签 就判断 当前最后一个节点 结束了没 + //如果结束了 也同样 直接不能撤回 + if(Objects.nonNull(currentProgressNode.getFinishTime())){ + return Boolean.FALSE; + } } //如果上个会签没取到 还有种情况是 会签 或签 并且在上个节点 diff --git a/pmapi/src/test/java/com/ningdatech/pmapi/todocenter/FlowableTest.java b/pmapi/src/test/java/com/ningdatech/pmapi/todocenter/FlowableTest.java new file mode 100644 index 0000000..efeec00 --- /dev/null +++ b/pmapi/src/test/java/com/ningdatech/pmapi/todocenter/FlowableTest.java @@ -0,0 +1,252 @@ +package com.ningdatech.pmapi.todocenter; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ningdatech.basic.exception.BizException; +import com.ningdatech.pmapi.AppTests; +import com.wflow.bean.entity.WflowCcTasks; +import com.wflow.mapper.WflowCcTasksMapper; +import com.wflow.utils.CodeUtil; +import com.wflow.workflow.bean.dto.ReqProcessHandlerDTO; +import com.wflow.workflow.enums.ProcessHandlerEnum; +import com.wflow.workflow.mapper.CustomHisActInstMapper; +import com.wflow.workflow.utils.ProcessTaskUtils; +import lombok.extern.slf4j.Slf4j; +import org.assertj.core.util.Lists; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.common.engine.impl.cmd.CustomSqlExecution; +import org.flowable.engine.*; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.engine.impl.cmd.AbstractCustomSqlExecution; +import org.flowable.engine.runtime.Execution; +import org.flowable.task.api.Task; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @Classname FlowableTest + * @Description + * @Date 2023/5/14 9:12 + * @Author PoffyZhang + */ +@Slf4j +public class FlowableTest extends AppTests { + + @Autowired + private RuntimeService runtimeService; + + @Autowired + private HistoryService historyService; + + @Autowired + private RepositoryService repositoryService; + + @Autowired + private TaskService taskService; + + @Autowired + private ManagementService managementService; + + @Autowired + private WflowCcTasksMapper ccTasksMapper; + + @Test + public void withdrawTest(){ + String taskId = "cc3d7536-f1f3-11ed-b9e0-02426daa406d"; + + // 不是流程发起人撤回 + HistoricTaskInstance handledTaskInstance = historyService.createHistoricTaskInstanceQuery() + .taskId(taskId) + .singleResult(); + if (Objects.isNull(handledTaskInstance)){ + throw new BizException("获取任务实例失败!"); + } + + String processInstanceId = handledTaskInstance.getProcessInstanceId(); + + // 获取已处理历史活跃实例节点ID + String handledNodeId = handledTaskInstance.getTaskDefinitionKey(); + + BpmnModel bpmnModel = repositoryService.getBpmnModel(handledTaskInstance.getProcessDefinitionId()); + + //先得到 其所在子流程 + Map subNodeMap = ProcessTaskUtils.getSubNodeMap(bpmnModel); + String subProcessId = subNodeMap.get(handledNodeId); + //然后得到 子流程下的所有 nodeId + Map> allSubNodeMap = ProcessTaskUtils.loadSubNodeMap(bpmnModel); + List nodeIds = allSubNodeMap.get(subProcessId); + + // 获取待处理历史活跃实例节点ID(会签/或签会有多个) + // 获取当前流程实例待审核任务信息 + List taskList = taskService.createTaskQuery() + .processInstanceId(processInstanceId) + .taskDefinitionKeys(nodeIds) + .list(); + //保证是此子流程下的任务 那第一个就是当前操作节点了 + Task currentTask = taskList.get(0); + String currentNodeId = currentTask.getTaskDefinitionKey(); + + // 传节点定义key 获取撤回操作人在流程配置中所在的节点 + FlowElement beforeFlowElement = bpmnModel.getFlowElement(handledNodeId); + // 获取执行撤回操作的节点所在子流程所有节点ID + List actIdList = Lists.newArrayList(handledNodeId,currentNodeId); + + List historicActivityInstanceList = historyService + .createHistoricActivityInstanceQuery() + .processInstanceId(processInstanceId) + .list(); + + // 标记执行撤回操作节点及当前待处理节点(userTask类型)为被撤回(使用DELETE_REASON_标志) + List activityInstances = historicActivityInstanceList.stream() + .filter(a -> actIdList.contains(a.getActivityId())) + .collect(Collectors.toList()); + + // 先查出当前撤回节点所在子流程节点的节点ID + List subProcessActIds = historicActivityInstanceList.stream() + .filter(a -> "subProcess".equals(a.getActivityType())) + .map(HistoricActivityInstance::getActivityId) + .collect(Collectors.toList()); + List executions = runtimeService.createExecutionQuery() + .processInstanceId(processInstanceId).onlyChildExecutions().list(); + List actIds = executions.stream().map(Execution::getActivityId).collect(Collectors.toList()); + // 去除子流程节点ID + actIds.removeAll(subProcessActIds); + // 强制流程指向前一个审核人节点 + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(processInstanceId) + .moveActivityIdsToSingleActivityId(actIds,handledNodeId) + .changeState(); + + // 如果是从会签节点撤回,执行会签节点的节点ID和当前待处理节点ID相同,只需要标记当前会签节点为被撤回 + if (handledNodeId.equals(currentNodeId)){ + activityInstances = activityInstances.stream().filter(a -> currentNodeId.equals(a.getActivityId())).collect(Collectors.toList()); + } + for (HistoricActivityInstance activityInstance : activityInstances) { + String executionId = activityInstance.getExecutionId(); + // 备注子流程已处理节点为被撤回 + CustomSqlExecution customSqlExecution = new AbstractCustomSqlExecution(CustomHisActInstMapper.class) { + @Override + public Integer execute(CustomHisActInstMapper customHisActInstMapper) { + // 更新历史活跃实例表中删除原因为被撤回 + return customHisActInstMapper.deleteHisActInst(processInstanceId, executionId); + } + }; + managementService.executeCustomSql(customSqlExecution); + taskService.deleteTask(activityInstance.getTaskId()); + historyService.deleteHistoricTaskInstance(activityInstance.getTaskId()); + } + } + + @Test + public void testWithDraw(){ + ReqProcessHandlerDTO param = new ReqProcessHandlerDTO(); + param.setProjectId(633L); + param.setTaskId("8e3e9329-f544-11ed-91fd-02426daa406d"); + param.setAction(ProcessHandlerEnum.WITHDRAW); + param.setInstanceId("93877c57-f542-11ed-91fd-02426daa406d"); + + // 不是流程发起人撤回 + HistoricTaskInstance handledTaskInstance = historyService.createHistoricTaskInstanceQuery() + .taskId(param.getTaskId()) + .singleResult(); + if (Objects.isNull(handledTaskInstance)){ + throw new BizException("获取任务实例失败!"); + } + + String processInstanceId = handledTaskInstance.getProcessInstanceId(); + + // 获取已处理历史活跃实例节点ID + String handledNodeId = handledTaskInstance.getTaskDefinitionKey(); + Date handleCreateTime = handledTaskInstance.getCreateTime(); + + BpmnModel bpmnModel = repositoryService.getBpmnModel(handledTaskInstance.getProcessDefinitionId()); + + //先得到 其所在子流程 + Map subNodeMap = ProcessTaskUtils.getSubNodeMap(bpmnModel); + String subProcessId = subNodeMap.get(handledNodeId); + //然后得到 子流程下的所有 nodeId + Map> allSubNodeMap = ProcessTaskUtils.loadSubNodeMap(bpmnModel); + List nodeIds = allSubNodeMap.get(subProcessId); + + // 获取待处理历史活跃实例节点ID(会签/或签会有多个) + // 获取当前流程实例待审核任务信息 + List taskList = taskService.createTaskQuery() + .processInstanceId(processInstanceId) + .taskDefinitionKeys(nodeIds) + .list(); + //保证是此子流程下的任务 那第一个就是当前操作节点了 + Task currentTask = taskList.get(0); + Date currentCreateTime = currentTask.getCreateTime(); + String currentNodeId = currentTask.getTaskDefinitionKey(); + + // 传节点定义key 获取撤回操作人在流程配置中所在的节点 + FlowElement beforeFlowElement = bpmnModel.getFlowElement(handledNodeId); + // 获取执行撤回操作的节点所在子流程所有节点ID + List actIdList = Lists.newArrayList(handledNodeId,currentNodeId); + + List historicActivityInstanceList = historyService + .createHistoricActivityInstanceQuery() + .processInstanceId(processInstanceId) + .list(); + + // 标记执行撤回操作节点及当前待处理节点(userTask类型)为被撤回(使用DELETE_REASON_标志) + List activityInstances = historicActivityInstanceList.stream() + .filter(a -> { + if(actIdList.contains(a.getActivityId())){ + if(a.getActivityId().equals(handledNodeId)){ + return (CodeUtil.computationTime(a.getStartTime(),handleCreateTime) == 0L); + }else if(a.getActivityId().equals(currentNodeId)){ + return (CodeUtil.computationTime(a.getStartTime(),currentCreateTime) == 0L); + } + } + return Boolean.FALSE; + }) + .collect(Collectors.toList()); + + // 先查出当前撤回节点所在子流程节点的节点ID + List subProcessActIds = historicActivityInstanceList.stream() + .filter(a -> "subProcess".equals(a.getActivityType())) + .map(HistoricActivityInstance::getActivityId) + .collect(Collectors.toList()); + List executions = runtimeService.createExecutionQuery() + .processInstanceId(processInstanceId).onlyChildExecutions().list(); + List actIds = executions.stream().map(Execution::getActivityId).collect(Collectors.toList()); + // 去除子流程节点ID + actIds.removeAll(subProcessActIds); + // 强制流程指向前一个审核人节点 + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(processInstanceId) +// .moveActivityIdTo(currentNodeId,handledNodeId) + .moveActivityIdsToSingleActivityId(actIds,handledNodeId) + .changeState(); + + // 如果是从会签节点撤回,执行会签节点的节点ID和当前待处理节点ID相同,只需要标记当前会签节点为被撤回 + if (handledNodeId.equals(currentNodeId)){ + activityInstances = activityInstances.stream().filter(a -> currentNodeId.equals(a.getActivityId())).collect(Collectors.toList()); + } + for (HistoricActivityInstance activityInstance : activityInstances) { + String executionId = activityInstance.getExecutionId(); + // 备注子流程已处理节点为被撤回 + CustomSqlExecution customSqlExecution = new AbstractCustomSqlExecution(CustomHisActInstMapper.class) { + @Override + public Integer execute(CustomHisActInstMapper customHisActInstMapper) { + // 更新历史活跃实例表中删除原因为被撤回 + return customHisActInstMapper.deleteHisActInst(processInstanceId, executionId); + } + }; + managementService.executeCustomSql(customSqlExecution); + taskService.deleteTask(activityInstance.getTaskId()); + historyService.deleteHistoricTaskInstance(activityInstance.getTaskId()); + } + // 如果当前执行撤回操作的节点后有抄送节点,撤回后,需将抄送任务节点删除 + String ccNodeId = param.getCcNodeId(); + ccTasksMapper.delete(Wrappers.lambdaQuery(WflowCcTasks.class).eq(WflowCcTasks::getNodeId,ccNodeId)); + } +}