22. flowable 驳回 回退 并行网关驳回 多实例驳回 子流程驳回

本文深入探讨了Flowable工作流引擎的驳回机制,详细介绍了如何通过代码实现不同类型的节点驳回,包括普通节点、多实例节点、并行网关节点以及子流程节点的驳回操作。并通过实例演示了获取可驳回节点列表的方法,以及具体的驳回代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目地址:https://gitee.com/lwj/flowable.git 分支flowable-base
视频讲解地址 https://www.bilibili.com/video/av78779999/

驳回:当前处理人可以驳回历史走过的任何一个节点
1、驳回任意普通节点
2、驳回多实例节点
3、驳回并行网关节点
4、驳回子流程节点
5、子流程节点驳回主流程节点

实际情况中,为了获取可驳回的节点列表,我们做了一些规定,比方说并行网关节点,要求必须成对出现,也只能驳回到并行网关节点的 fork节点

一、演示

由于情况很多,截图反而不能重点讲述驳回的事情,这里只截一张图,如果想看详情请查看视频里面的讲解
在这里插入图片描述

二、代码分享

2.1 获取可驳回节点

public List<FlowNodeVo> getBackNodesByProcessInstanceId(String taskId,String processInstanceId) {
        List<FlowNodeVo> backNods = new ArrayList<>();
        TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();
        String currActId = taskEntity.getTaskDefinitionKey();
        //获取运行节点表中usertask
        String sql = "select t.* from act_ru_actinst t where t.ACT_TYPE_ = 'userTask' " +
                " and t.PROC_INST_ID_=#{processInstanceId} and t.END_TIME_ is not null ";
        List<ActivityInstance> activityInstances = runtimeService.createNativeActivityInstanceQuery().sql(sql)
                .parameter("processInstanceId", processInstanceId)
                .list();
        //获取运行节点表的parallelGateway节点并出重
        sql = "SELECT t.ID_, t.REV_,t.PROC_DEF_ID_,t.PROC_INST_ID_,t.EXECUTION_ID_,t.ACT_ID_, t.TASK_ID_, t.CALL_PROC_INST_ID_, t.ACT_NAME_, t.ACT_TYPE_, " +
                " t.ASSIGNEE_, t.START_TIME_, max(t.END_TIME_) as END_TIME_, t.DURATION_, t.DELETE_REASON_, t.TENANT_ID_" +
                " FROM  act_ru_actinst t WHERE t.ACT_TYPE_ = 'parallelGateway' AND t.PROC_INST_ID_ = #{processInstanceId} and t.END_TIME_ is not null" +
                " and t.ACT_ID_ <> #{actId} GROUP BY t.act_id_";
        List<ActivityInstance> parallelGatewaies = runtimeService.createNativeActivityInstanceQuery().sql(sql)
                .parameter("processInstanceId", processInstanceId)
                .parameter("actId", currActId)
                .list();
        //排序
        if (CollectionUtils.isNotEmpty(parallelGatewaies)) {
            activityInstances.addAll(parallelGatewaies);
            activityInstances.sort(Comparator.comparing(ActivityInstance::getEndTime));
        }
        //分组节点
        int count = 0;
        Map<ActivityInstance, List<ActivityInstance>> parallelGatewayUserTasks = new HashMap<>();
        List<ActivityInstance> userTasks = new ArrayList<>();
        ActivityInstance currActivityInstance = null;
        for (ActivityInstance activityInstance : activityInstances) {
            if (BpmnXMLConstants.ELEMENT_GATEWAY_PARALLEL.equals(activityInstance.getActivityType())) {
                count++;
                if (count % 2 != 0) {
                    List<ActivityInstance> datas = new ArrayList<>();
                    currActivityInstance = activityInstance;
                    parallelGatewayUserTasks.put(currActivityInstance, datas);
                }
            }
            if (BpmnXMLConstants.ELEMENT_TASK_USER.equals(activityInstance.getActivityType())) {
                if (count % 2 == 0) {
                    userTasks.add(activityInstance);
                } else {
                    if (parallelGatewayUserTasks.containsKey(currActivityInstance)) {
                        parallelGatewayUserTasks.get(currActivityInstance).add(activityInstance);
                    }
                }
            }
        }
        //组装人员名称
        List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId).finished().list();
        Map<String, List<HistoricTaskInstance>> taskInstanceMap = new HashMap<>();
        List<String> userCodes = new ArrayList<>();
        historicTaskInstances.forEach(historicTaskInstance -> {
            userCodes.add(historicTaskInstance.getAssignee());
            String taskDefinitionKey = historicTaskInstance.getTaskDefinitionKey();
            if (taskInstanceMap.containsKey(historicTaskInstance.getTaskDefinitionKey())) {
                taskInstanceMap.get(taskDefinitionKey).add(historicTaskInstance);
            } else {
                List<HistoricTaskInstance> tasks = new ArrayList<>();
                tasks.add(historicTaskInstance);
                taskInstanceMap.put(taskDefinitionKey, tasks);
            }
        });
        //组装usertask的数据
        List<User> userList = identityService.createUserQuery().userIds(userCodes).list();
        Map<String, String> activityIdUserNames = this.getApplyers(processInstanceId, userList, taskInstanceMap);
        if (CollectionUtils.isNotEmpty(userTasks)) {
            userTasks.forEach(activityInstance -> {
                FlowNodeVo node = new FlowNodeVo();
                node.setNodeId(activityInstance.getActivityId());
                node.setNodeName(activityInstance.getActivityName());
                node.setEndTime(activityInstance.getEndTime());
                node.setUserName(activityIdUserNames.get(activityInstance.getActivityId()));
                backNods.add(node);
            });
        }
        //组装会签节点数据
        if (MapUtils.isNotEmpty(taskInstanceMap)) {
            parallelGatewayUserTasks.forEach((activity, activities) -> {
                FlowNodeVo node = new FlowNodeVo();
                node.setNodeId(activity.getActivityId());
                node.setEndTime(activity.getEndTime());
                StringBuffer nodeNames = new StringBuffer("会签:");
                StringBuffer userNames = new StringBuffer("审批人员:");
                if (CollectionUtils.isNotEmpty(activities)){
                    activities.forEach(activityInstance -> {
                        nodeNames.append(activityInstance.getActivityName()).append(",");
                        userNames.append(activityIdUserNames.get(activityInstance.getActivityId())).append(",");
                    });
                    node.setNodeName(nodeNames.toString());
                    node.setUserName(userNames.toString());
                    backNods.add(node);
                }
            });
        }
        //去重合并
        List<FlowNodeVo> datas = backNods.stream().collect(
                Collectors.collectingAndThen(Collectors.toCollection(() ->
                        new TreeSet<>(Comparator.comparing(nodeVo -> nodeVo.getNodeId()))), ArrayList::new));

        //排序
        datas.sort(Comparator.comparing(FlowNodeVo::getEndTime));
        return datas;
    }

2.2、驳回代码分享

public ReturnVo<String> backToStepTask(BackTaskVo backTaskVo) {
        ReturnVo<String> returnVo = null;
        TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(backTaskVo.getTaskId()).singleResult();
        //1.把当前的节点设置为空
        if (taskEntity != null) {
            //2.设置审批人
            taskEntity.setAssignee(backTaskVo.getUserCode());
            taskService.saveTask(taskEntity);
            //3.添加驳回意见
            this.addComment(backTaskVo.getTaskId(), backTaskVo.getUserCode(), backTaskVo.getProcessInstanceId(),
                    CommentTypeEnum.BH.toString(), backTaskVo.getMessage());
            //4.处理提交人节点
            FlowNode distActivity = flowableBpmnModelService.findFlowNodeByActivityId(taskEntity.getProcessDefinitionId(), backTaskVo.getDistFlowElementId());
            if (distActivity != null) {
                if (FlowConstant.FLOW_SUBMITTER.equals(distActivity.getName())) {
                    ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(taskEntity.getProcessInstanceId()).singleResult();
                    runtimeService.setVariable(backTaskVo.getProcessInstanceId(), FlowConstant.FLOW_SUBMITTER_VAR, processInstance.getStartUserId());
                }
            }
            //5.删除节点
            this.deleteActivity(backTaskVo.getDistFlowElementId(), taskEntity.getProcessInstanceId());
            List<String> executionIds = new ArrayList<>();
            //6.判断节点是不是子流程内部的节点
            if (flowableBpmnModelService.checkActivitySubprocessByActivityId(taskEntity.getProcessDefinitionId(),
                    backTaskVo.getDistFlowElementId())
                    && flowableBpmnModelService.checkActivitySubprocessByActivityId(taskEntity.getProcessDefinitionId(),
                    taskEntity.getTaskDefinitionKey())){
                //6.1 子流程内部驳回
                Execution executionTask = runtimeService.createExecutionQuery().executionId(taskEntity.getExecutionId()).singleResult();
                String parentId = executionTask.getParentId();
                List<Execution> executions = runtimeService.createExecutionQuery().parentId(parentId).list();
                executions.forEach(execution -> executionIds.add(execution.getId()));
                this.moveExecutionsToSingleActivityId(executionIds,backTaskVo.getDistFlowElementId());
            }else {
                //6.2 普通驳回
                List<Execution> executions = runtimeService.createExecutionQuery().parentId(taskEntity.getProcessInstanceId()).list();
                executions.forEach(execution -> executionIds.add(execution.getId()));
                this.moveExecutionsToSingleActivityId(executionIds,backTaskVo.getDistFlowElementId());
            }
            returnVo = new ReturnVo<>(ReturnCode.SUCCESS, "驳回成功!");
        } else {
            returnVo = new ReturnVo<>(ReturnCode.FAIL, "不存在任务实例,请确认!");
        }
        return returnVo;
    }
<think>嗯,用户想知道在Flowable中如何实现并行网关多分支审批被拒绝后的回退机制。首先,我需要回忆一下Flowable的基本处理流程Flowable支持并行网关,但默认的回退功能可能不够,特别是并行流程的处理比较复杂。根据提供的引用,Flowable本身只能处理串行流程回退,而并行流程需要额外的逻辑。 用户提到的是多分支审批被拒绝后的回退,这可能涉及到并行网关中的多个分支同时或部分回退的情况。引用[2]中提到,他们通过增加leaf值来标记节点的层级,从而实现不同分支的回退控制。比如,当某个节点需要回退到上一级时,同级分支可能需要同步回退,而上级节点不受影响。例如,节点4退回到2时,节点6也要退回,但节点8保持不变。 接下来,我需要考虑如何具体实现这一点。可能需要通过任务监听器或执行监听器来捕获回退事件,然后动态调整流程的路径。此外,可能需要修改流程定义中的节点属性,比如添加扩展属性来记录节点的层级关系,这样在回退时可以根据这些属性判断哪些节点需要一起回退。 另外,引用[2]中的例显示,当任务从节点9退回到4时,只需要处理当前分支,不影响其他分支。这可能需要动态生成目标节点,并处理令牌的移动,可能需要使用Flowable的API如RuntimeService.createChangeActivityStateBuilder()来调整流程实例的状态。 不过,用户可能需要具体的步骤或代码示例。比如,如何添加leaf值,如何在回退时检查这些值,以及如何调用API来移动流程节点。同时,需要处理并行网关的同步问题,确保流程不会出现不一致的状态。 可能还需要考虑边界情况,比如部分分支已经完成审批,而其他分支被拒绝,这时候如何只回退被拒绝的分支。或者当回退到某个节点后,重新审批时如何合并流程继续执行。 总结下来,实现并行网关多分支回退的关键点包括:标记节点层级、监听回退事件、动态调整流程路径、处理同步问题。需要结合Flowable提供的API和自定义逻辑来实现这些功能。</think>在Flowable工作流引擎中实现并行网关多分支审批拒绝后的回退,需要结合自定义逻辑和引擎特性。以下是具体实现方案: ### 一、核心实现原理 1. **令牌(Token)控制**:通过动态调整流程实例的`execution`令牌位置实现回退[^2] 2. **层级标记**:使用自定义属性`leaf`标记节点层级关系 ```xml <userTask id="task4" flowable:leaf="2"/> <userTask id="task8" flowable:leaf="1"/> ``` 3. **路径追踪**:通过历史记录判断分支归属关系 ### 二、具体实现步骤 1. **拦截退回事件**:使用任务监听器捕获拒绝操作 ```java taskService.addTaskListener(TaskListener.EVENTNAME_COMPLETE, new RejectListener()); ``` 2. **层级关系判断**:根据目标节点的`leaf`值确定影响范围 ```java HistoricActivityInstance currentAct = historyService.createHistoricActivityInstanceQuery() .processInstanceId(procInstId) .unfinished() .singleResult(); ``` 3. **动态调整流程路径**:通过API修改执行流 ```java runtimeService.createChangeActivityStateBuilder() .processInstanceId(procInstId) .moveActivityIdTo(currentAct.getActivityId(), targetActId) .changeState(); ``` ### 三、典型场景处理逻辑 1. **分支内部回退**(如节点4→3): - 仅修改当前分支的令牌位置 - 其他分支保持当前状态 2. **跨层级回退**(如节点4→2): ```java if(targetLeaf < currentLeaf){ // 找到所有同级分支节点 List<Execution> parallelExecutions = runtimeService.createExecutionQuery() .parentId(parentExecutionId) .list(); } ``` 3. **完全回退**(如节点4→1): - 终止所有并行分支 - 重置流程并行网关入口 ```sql UPDATE ACT_RU_EXECUTION SET ACT_ID_='parallelGateway1' WHERE PROC_INST_ID_ = #{procInstId} ``` ### 四、数据一致性保障 1. **历史记录修复**: ```java managementService.executeCommand(new DeleteActivityInstanceCmd( procInstId, Arrays.asList("task4","task6") )); ``` 2. **异步补偿机制**: ```xml <serviceTask id="compensationTask" flowable:async="true" flowable:class="com.example.CompensationHandler"/> ```
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小学生05101

flowable

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值