Camunda流程驳回至上一节点


前言

Camunda驳回至上一节点,网上大多都是回退至开始节点,这样逻辑比较简单清晰。但实际使用中,往往需要驳回至上一节点,甚至需要连续驳回多次。
流程驳回关键的一步就是获取到要回退到的节点。其中,需要理解的是(假设流程节点是1,2,3,4,5这样顺序的流程),如果当前已办历史节点是{1,2,3},如果4节点执行了驳回操作,那么历史节点会变成{1,2,3,4},而当前待办节点是3, 节点4执行了驳回操作,4同样会出现在历史执行节点中。
也就是说,当历史节点是{1,2,3,4}时,有两种可能性:一种是常规的顺序执行,节点3执行通过到达4节点,当前待办节点是4;另外一种是节点4执行了驳回操作,当前待办节点是3。


  • 思路一(实现代码见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) {
        //获取当前task
        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() == 0){
            return ResultFactory.buildFailResult("当前任务无法驳回!");
        }
        //得到第一个任务节点的id
        HistoricActivityInstance historicActivityInstance = resultList.get(0);
        String startActId = historicActivityInstance.getActivityId();
        if(startActId.equals(task.getTaskDefinitionKey())){
            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("第一个用户节点无法驳回!");
        }
        //得到第一个任务节点的id
        HistoricActivityInstance 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() {
            @Override
            public String getId() {
                return "act-1";
            }

            @Override
            public String getParentActivityInstanceId() {
                return null;
            }

            @Override
            public String getActivityId() {
                return "act-1";
            }

            @Override
            public String getActivityName() {
                return null;
            }

            @Override
            public String getActivityType() {
                return null;
            }

            @Override
            public String getProcessDefinitionKey() {
                return null;
            }

            @Override
            public String getProcessDefinitionId() {
                return null;
            }

            @Override
            public String getRootProcessInstanceId() {
                return null;
            }

            @Override
            public String getProcessInstanceId() {
                return null;
            }

            @Override
            public String getExecutionId() {
                return null;
            }

            @Override
            public String getTaskId() {
                return null;
            }

            @Override
            public String getCalledProcessInstanceId() {
                return null;
            }

            @Override
            public String getCalledCaseInstanceId() {
                return null;
            }

            @Override
            public String getAssignee() {
                return "user-1";
            }

            @Override
            public Date getStartTime() {
                return null;
            }

            @Override
            public Date getEndTime() {
                return null;
            }

            @Override
            public Long getDurationInMillis() {
                return null;
            }

            @Override
            public boolean isCompleteScope() {
                return false;
            }

            @Override
            public boolean isCanceled() {
                return false;
            }

            @Override
            public String getTenantId() {
                return null;
            }

            @Override
            public Date getRemovalTime() {
                return null;
            }
        };
        HistoricActivityInstance hai2 = new HistoricActivityInstance() {
            @Override
            public String getId() {
                return "act-2";
            }

            @Override
            public String getParentActivityInstanceId() {
                return null;
            }

            @Override
            public String getActivityId() {
                return "act-2";
            }

            @Override
            public String getActivityName() {
                return null;
            }

            @Override
            public String getActivityType() {
                return null;
            }

            @Override
            public String getProcessDefinitionKey() {
                return null;
            }

            @Override
            public String getProcessDefinitionId() {
                return null;
            }

            @Override
            public String getRootProcessInstanceId() {
                return null;
            }

            @Override
            public String getProcessInstanceId() {
                return null;
            }

            @Override
            public String getExecutionId() {
                return null;
            }

            @Override
            public String getTaskId() {
                return null;
            }

            @Override
            public String getCalledProcessInstanceId() {
                return null;
            }

            @Override
            public String getCalledCaseInstanceId() {
                return null;
            }

            @Override
            public String getAssignee() {
                return "user-2";
            }

            @Override
            public Date getStartTime() {
                return null;
            }

            @Override
            public Date getEndTime() {
                return null;
            }

            @Override
            public Long getDurationInMillis() {
                return null;
            }

            @Override
            public boolean isCompleteScope() {
                return false;
            }

            @Override
            public boolean isCanceled() {
                return false;
            }

            @Override
            public String getTenantId() {
                return null;
            }

            @Override
            public Date getRemovalTime() {
                return null;
            }
        };
        HistoricActivityInstance hai3 = new HistoricActivityInstance() {
            @Override
            public String getId() {
                return "act-3";
            }

            @Override
            public String getParentActivityInstanceId() {
                return null;
            }

            @Override
            public String getActivityId() {
                return "act-3";
            }

            @Override
            public String getActivityName() {
                return null;
            }

            @Override
            public String getActivityType() {
                return null;
            }

            @Override
            public String getProcessDefinitionKey() {
                return null;
            }

            @Override
            public String getProcessDefinitionId() {
                return null;
            }

            @Override
            public String getRootProcessInstanceId() {
                return null;
            }

            @Override
            public String getProcessInstanceId() {
                return null;
            }

            @Override
            public String getExecutionId() {
                return null;
            }

            @Override
            public String getTaskId() {
                return null;
            }

            @Override
            public String getCalledProcessInstanceId() {
                return null;
            }

            @Override
            public String getCalledCaseInstanceId() {
                return null;
            }

            @Override
            public String getAssignee() {
                return "user-3";
            }

            @Override
            public Date getStartTime() {
                return null;
            }

            @Override
            public Date getEndTime() {
                return null;
            }

            @Override
            public Long getDurationInMillis() {
                return null;
            }

            @Override
            public boolean isCompleteScope() {
                return false;
            }

            @Override
            public boolean isCanceled() {
                return false;
            }

            @Override
            public String getTenantId() {
                return null;
            }

            @Override
            public Date getRemovalTime() {
                return null;
            }
        };
        HistoricActivityInstance hai4 = new HistoricActivityInstance() {
            @Override
            public String getId() {
                return "act-4";
            }

            @Override
            public String getParentActivityInstanceId() {
                return null;
            }

            @Override
            public String getActivityId() {
                return "act-4";
            }

            @Override
            public String getActivityName() {
                return null;
            }

            @Override
            public String getActivityType() {
                return null;
            }

            @Override
            public String getProcessDefinitionKey() {
                return null;
            }

            @Override
            public String getProcessDefinitionId() {
                return null;
            }

            @Override
            public String getRootProcessInstanceId() {
                return null;
            }

            @Override
            public String getProcessInstanceId() {
                return null;
            }

            @Override
            public String getExecutionId() {
                return null;
            }

            @Override
            public String getTaskId() {
                return null;
            }

            @Override
            public String getCalledProcessInstanceId() {
                return null;
            }

            @Override
            public String getCalledCaseInstanceId() {
                return null;
            }

            @Override
            public String getAssignee() {
                return "user-4";
            }

            @Override
            public Date getStartTime() {
                return null;
            }

            @Override
            public Date getEndTime() {
                return null;
            }

            @Override
            public Long getDurationInMillis() {
                return null;
            }

            @Override
            public boolean isCompleteScope() {
                return false;
            }

            @Override
            public boolean isCanceled() {
                return false;
            }

            @Override
            public String getTenantId() {
                return null;
            }

            @Override
            public 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"));

    }
 
  • 6
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 31
    评论
评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值