文章目录
- 前言
- 一、版本
- 二、实现
- 1、回退至上一节点
- 2、回退至开始节点
- 3、测试方法
前言
Camunda驳回至上一节点,网上大多都是回退至开始节点,这样逻辑比较简单清晰。但实际使用中,往往需要驳回至上一节点,甚至需要连续驳回多次。
流程驳回关键的一步就是获取到要回退到的节点。其中,需要理解的是(假设流程节点是1,2,3,4,5这样顺序的流程),如果当前已办历史节点是{1,2,3},如果4节点执行了驳回操作,那么历史节点会变成{1,2,3,4},而当前待办节点是3, 节点4执行了驳回操作,4同样会出现在历史执行节点中。
- 思路一(实现代码见getLastNode):
分两种情况:
1、当前节点不在历史节点里
2、当前节点在历史节点里
假设,已办历史节点 resultList={1,2,3}
(1)当前节点是4,表示3是完成节点,4驳回需要回退到3
(2)当前节点是2,表示3是驳回节点,3驳回到当前2节点,2驳回需要回退到1
其他驳回过的情况也都包含在情况2中。
- 思路二:
假设,已办历史节点 resultList={1,2,3}
无论当前节点在不在历史节点里,一律将当前节点追加到已办历史节点列表中,再调用currentNodeInHis方法获取上一节点。
(1)当前节点是4,追加后 resultList={1,2,3,4}
(2)当前节点是2,追加后 resultList={1,2,3,2}
一、版本
camunda : 7.15.0
spring-boot : 2.4.3
spring-cloud :2020.0.1
二、实现
1、回退至上一节点
代码如下:
引入的jar包
import io.swagger.annotations.ApiOperation;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.history.HistoricActivityInstance;
import org.camunda.bpm.engine.runtime.ActivityInstance;
import org.camunda.bpm.engine.task.Task;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.*;
实现方法
其中RejectBean 实体类有三个属性:processInstanceId, currentUserId, rejectComment
public Result rejectToLastNode(@RequestBody RejectBean rejectBean) {//获取当前taskTask task = taskService.createTaskQuery().taskAssignee(rejectBean.getCurrentUserId()) //当前登录用户的id.processInstanceId(rejectBean.getProcessInstanceId()).singleResult();ActivityInstance tree = runtimeService.getActivityInstance(rejectBean.getProcessInstanceId());//获取所有已办用户任务节点List<HistoricActivityInstance> resultList = historyService.createHistoricActivityInstanceQuery().processInstanceId(rejectBean.getProcessInstanceId()).activityType("userTask").finished().orderByHistoricActivityInstanceEndTime().asc().list();if(null == resultList || resultList.size()<2){return ResultFactory.buildFailResult("第一个用户节点无法驳回!");}//得到上一个任务节点的ActivityId和待办人Map<String,String> lastNode = getLastNode(resultList,task.getTaskDefinitionKey());if(null == lastNode){return ResultFactory.buildFailResult("回退节点异常!");}String toActId = lastNode.get("toActId");String assignee = lastNode.get("assignee");//设置流程中的可变参数Map<String, Object> taskVariable = new HashMap<>(2);taskVariable.put("user", assignee);//taskVariable.put("formName", "流程驳回");taskService.createComment(task.getId(), rejectBean.getProcessInstanceId(), "驳回原因:" + rejectBean.getRejectComment());runtimeService.createProcessInstanceModification(rejectBean.getProcessInstanceId()).cancelActivityInstance(getInstanceIdForActivity(tree, task.getTaskDefinitionKey()))//关闭相关任务.setAnnotation("进行了驳回到上一个任务节点操作").startBeforeActivity(toActId)//启动目标活动节点.setVariables(taskVariable)//流程的可变参数赋值.execute();return ResultFactory.buildSuccessResult(null);}private String getInstanceIdForActivity(ActivityInstance activityInstance, String activityId) {ActivityInstance instance = getChildInstanceForActivity(activityInstance, activityId);if (instance != null) {return instance.getId();}return null;}private ActivityInstance getChildInstanceForActivity(ActivityInstance activityInstance, String activityId) {if (activityId.equals(activityInstance.getActivityId())) {return activityInstance;}for (ActivityInstance childInstance : activityInstance.getChildActivityInstances()) {ActivityInstance instance = getChildInstanceForActivity(childInstance, activityId);if (instance != null) {return instance;}}return null;}/*** 获取上一节点信息* 分两种情况:* 1、当前节点不在历史节点里* 2、当前节点在历史节点里* 比如,resultList={1,2,3}* (1)当前节点是4,表示3是完成节点,4驳回需要回退到3* (2)当前节点是2,表示3是驳回节点,3驳回到当前2节点,2驳回需要回退到1* 其他驳回过的情况也都包含在情况2中。** @param resultList 历史节点列表* @param currentActivityId 当前待办节点ActivityId* @return 返回值:上一节点的ActivityId和待办人(toActId, assignee)*/private static Map<String,String> getLastNode(List<HistoricActivityInstance> resultList, String currentActivityId){Map<String,String> backNode = new HashMap<>();//新建一个有序不重复集合LinkedHashMap<String,String> linkedHashMap = new LinkedHashMap();for(HistoricActivityInstance hai : resultList){linkedHashMap.put(hai.getActivityId(),hai.getAssignee());}//分两种情况:当前节点在不在历史节点里面,当前节点在历史节点里//情况1、当前节点不在历史节点里int originSize = resultList.size();int duplicateRemovalSize = linkedHashMap.size();//判断历史节点中是否有重复节点if(originSize == duplicateRemovalSize){boolean flag = false;for(Map.Entry entry: linkedHashMap.entrySet()){if(currentActivityId.equals(entry.getKey())){flag = true;break;}}
// if(flag){
// //当前节点在历史节点里:最后一个节点是回退节点
// return currentNodeInHis(linkedHashMap, currentActivityId);
// }if(!flag) {//当前节点不在历史节点里:最后一个节点是完成节点HistoricActivityInstance historicActivityInstance = resultList.get(originSize - 1);backNode.put("toActId", historicActivityInstance.getActivityId());backNode.put("assignee", historicActivityInstance.getAssignee());return backNode;}}//情况2、当前节点在历史节点里(已回退过的)return currentNodeInHis(linkedHashMap, currentActivityId);}private static Map<String,String> currentNodeInHis(LinkedHashMap<String,String> linkedHashMap,String currentActivityId){//情况2、当前节点在历史节点里(已回退过的)Map<String,String> backNode = new HashMap<>();ListIterator<Map.Entry<String,String>> li = new ArrayList<>(linkedHashMap.entrySet()).listIterator();//System.out.println("已回退过的");while (li.hasNext()){Map.Entry<String,String> entry = li.next();if(currentActivityId.equals(entry.getKey())){li.previous();Map.Entry<String,String> previousEntry = li.previous();backNode.put("toActId",previousEntry.getKey());backNode.put("assignee",previousEntry.getValue());return backNode;}}return null;}
2、回退至开始节点
public Result rejectToFirstNode(@RequestBody RejectBean rejectBean) {//String rejectMessage="项目的金额款项结算不正确";Task task = taskService.createTaskQuery().taskAssignee(rejectBean.getCurrentUserId()) //当前登录用户的id.processInstanceId(rejectBean.getProcessInstanceId()).singleResult();ActivityInstance tree = runtimeService.getActivityInstance(rejectBean.getProcessInstanceId());List<HistoricActivityInstance> resultList = historyService.createHistoricActivityInstanceQuery().processInstanceId(rejectBean.getProcessInstanceId()).activityType("userTask").finished().orderByHistoricActivityInstanceEndTime().asc().list();if(null == resultList || resultList.size()<2){return ResultFactory.buildFailResult("第一个用户节点无法驳回!");}//得到第一个任务节点的idHistoricActivityInstance historicActivityInstance = resultList.get(0);String toActId = historicActivityInstance.getActivityId();String assignee = historicActivityInstance.getAssignee();//设置流程中的可变参数Map<String, Object> taskVariable = new HashMap<>(2);taskVariable.put("user", assignee);//taskVariable.put("formName", "流程驳回");taskService.createComment(task.getId(), rejectBean.getProcessInstanceId(), "驳回原因:" + rejectBean.getRejectComment());runtimeService.createProcessInstanceModification(rejectBean.getProcessInstanceId()).cancelActivityInstance(getInstanceIdForActivity(tree, task.getTaskDefinitionKey()))//关闭相关任务.setAnnotation("进行了驳回到第一个任务节点操作").startBeforeActivity(toActId)//启动目标活动节点.setVariables(taskVariable)//流程的可变参数赋值.execute();return ResultFactory.buildSuccessResult(null);}
3、测试方法
测试时只需给getAcitvityId和getAssingee返回值赋值即可,本身也就只需要这两个属性
public static void main(String[] args) {HistoricActivityInstance hai1 = new HistoricActivityInstance() {@Overridepublic String getId() {return "act-1";}@Overridepublic String getParentActivityInstanceId() {return null;}@Overridepublic String getActivityId() {return "act-1";}@Overridepublic String getActivityName() {return null;}@Overridepublic String getActivityType() {return null;}@Overridepublic String getProcessDefinitionKey() {return null;}@Overridepublic String getProcessDefinitionId() {return null;}@Overridepublic String getRootProcessInstanceId() {return null;}@Overridepublic String getProcessInstanceId() {return null;}@Overridepublic String getExecutionId() {return null;}@Overridepublic String getTaskId() {return null;}@Overridepublic String getCalledProcessInstanceId() {return null;}@Overridepublic String getCalledCaseInstanceId() {return null;}@Overridepublic String getAssignee() {return "user-1";}@Overridepublic Date getStartTime() {return null;}@Overridepublic Date getEndTime() {return null;}@Overridepublic Long getDurationInMillis() {return null;}@Overridepublic boolean isCompleteScope() {return false;}@Overridepublic boolean isCanceled() {return false;}@Overridepublic String getTenantId() {return null;}@Overridepublic Date getRemovalTime() {return null;}};HistoricActivityInstance hai2 = new HistoricActivityInstance() {@Overridepublic String getId() {return "act-2";}@Overridepublic String getParentActivityInstanceId() {return null;}@Overridepublic String getActivityId() {return "act-2";}@Overridepublic String getActivityName() {return null;}@Overridepublic String getActivityType() {return null;}@Overridepublic String getProcessDefinitionKey() {return null;}@Overridepublic String getProcessDefinitionId() {return null;}@Overridepublic String getRootProcessInstanceId() {return null;}@Overridepublic String getProcessInstanceId() {return null;}@Overridepublic String getExecutionId() {return null;}@Overridepublic String getTaskId() {return null;}@Overridepublic String getCalledProcessInstanceId() {return null;}@Overridepublic String getCalledCaseInstanceId() {return null;}@Overridepublic String getAssignee() {return "user-2";}@Overridepublic Date getStartTime() {return null;}@Overridepublic Date getEndTime() {return null;}@Overridepublic Long getDurationInMillis() {return null;}@Overridepublic boolean isCompleteScope() {return false;}@Overridepublic boolean isCanceled() {return false;}@Overridepublic String getTenantId() {return null;}@Overridepublic Date getRemovalTime() {return null;}};HistoricActivityInstance hai3 = new HistoricActivityInstance() {@Overridepublic String getId() {return "act-3";}@Overridepublic String getParentActivityInstanceId() {return null;}@Overridepublic String getActivityId() {return "act-3";}@Overridepublic String getActivityName() {return null;}@Overridepublic String getActivityType() {return null;}@Overridepublic String getProcessDefinitionKey() {return null;}@Overridepublic String getProcessDefinitionId() {return null;}@Overridepublic String getRootProcessInstanceId() {return null;}@Overridepublic String getProcessInstanceId() {return null;}@Overridepublic String getExecutionId() {return null;}@Overridepublic String getTaskId() {return null;}@Overridepublic String getCalledProcessInstanceId() {return null;}@Overridepublic String getCalledCaseInstanceId() {return null;}@Overridepublic String getAssignee() {return "user-3";}@Overridepublic Date getStartTime() {return null;}@Overridepublic Date getEndTime() {return null;}@Overridepublic Long getDurationInMillis() {return null;}@Overridepublic boolean isCompleteScope() {return false;}@Overridepublic boolean isCanceled() {return false;}@Overridepublic String getTenantId() {return null;}@Overridepublic Date getRemovalTime() {return null;}};HistoricActivityInstance hai4 = new HistoricActivityInstance() {@Overridepublic String getId() {return "act-4";}@Overridepublic String getParentActivityInstanceId() {return null;}@Overridepublic String getActivityId() {return "act-4";}@Overridepublic String getActivityName() {return null;}@Overridepublic String getActivityType() {return null;}@Overridepublic String getProcessDefinitionKey() {return null;}@Overridepublic String getProcessDefinitionId() {return null;}@Overridepublic String getRootProcessInstanceId() {return null;}@Overridepublic String getProcessInstanceId() {return null;}@Overridepublic String getExecutionId() {return null;}@Overridepublic String getTaskId() {return null;}@Overridepublic String getCalledProcessInstanceId() {return null;}@Overridepublic String getCalledCaseInstanceId() {return null;}@Overridepublic String getAssignee() {return "user-4";}@Overridepublic Date getStartTime() {return null;}@Overridepublic Date getEndTime() {return null;}@Overridepublic Long getDurationInMillis() {return null;}@Overridepublic boolean isCompleteScope() {return false;}@Overridepublic boolean isCanceled() {return false;}@Overridepublic String getTenantId() {return null;}@Overridepublic Date getRemovalTime() {return null;}};List<HistoricActivityInstance> resultList = new ArrayList<>();resultList.add(hai1);resultList.add(hai2);resultList.add(hai3);
// resultList.add(hai4);
// resultList.add(hai3);Map<String,String> map = getLastNode(resultList,"act-2");System.out.println("toActId ==> "+map.get("toActId"));System.out.println("assignee ==> "+map.get("assignee"));}