Camunda流程驳回至上一节点

news/2024/4/20 0:42:51/文章来源:https://blog.csdn.net/innerpeaceScorpio/article/details/127632275

文章目录

  • 前言
  • 一、版本
  • 二、实现
    • 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"));}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_410439.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实战经验:如何根据系统的业务场景需求定制自己的线程池?

线程池有那么多的参数和类型&#xff0c;在实际的开发中&#xff0c;我们应该如何设置呢&#xff1f;是直接使用Executors提供的线程池实现&#xff0c;还是自定义线程池&#xff1f;这都是我们本篇文章要回答的问题&#xff0c;那么就请跟随笔者一起分析一下在实战中如何根据系…

uniapp开发微信小程序-用户授权登录和获取手机号码

小程序开放文档 uniapp开发的小程序配置&#xff0c;找到manifest.json&#xff0c;填入正确的小程序appId&#xff1b; hbuilderx>运行>运行到小程序模拟器(安装开发者工具)&#xff0c;编译完成之后会直接在微信开发者工具内打开&#xff1b; 登录流程解析&#xff1…

【SpringBoot】一文了解SpringBoot热部署

文章目录前言手动启动热部署热部署种类手动进行热部署自动启动热部署热部署范围配置热部署的关闭总结&#x1f315;博客x主页&#xff1a;己不由心王道长&#x1f315;! &#x1f30e;文章说明&#xff1a;一文彻底搞懂SpringBoot热部署&#x1f30e; ✅系列专栏&#xff1a;Sp…

程序人生:去了字节跳动,才知道年薪40W的测试有这么多?

今年大环境不好&#xff0c;内卷的厉害&#xff0c;薪资待遇好的工作机会更是难得。最近脉脉职言区有一条讨论火了&#xff1a; 哪家互联网公司薪资最‘厉害’&#xff1f; 下面的评论多为字节跳动&#xff0c;还炸出了很多年薪40W的测试工程师 我只想问一句&#xff0c;现在的…

【C#】async和await

大概理解 查了一个小时的资料&#xff1a;async和await 发现这个大神的解释一针见血&#xff0c;深得我心&#xff01;以最简单的例子&#xff0c;解释了async和await。妙~~~ 大多情况下&#xff0c;分开才能体现async和await的价值&#xff01; 但&#xff0c;await 并没有…

C#中的弃元

从C#7.0开始,推出了一种新的特性:弃元,这种思想可能来源于Golang。弃元,就是不想要了的元素变量,用单下划线(_)表示,弃元在编译时起作用,就是搞编译器:这个变量我不要,你可以优化处理。我们经常在下面几个过程中使用弃元:1、元组解构赋值在使用元组解构赋值时,我们…

Linux——进程间通信——管道(文件)通信

目录 前言 一、有名管道 1、用法 2、管道分类 3、有名管道的创建 4、思考&#xff1a;如何进程a要将键盘获取的数据传递给另一个进程b&#xff1f; 5、有名管道实现进程间通信 二、无名管道 1、无名管道的创建 2、管道操作分为以下步骤 3、无名管道实现进程间通信 前言…

string类详解

文章目录1:构造string类1.1:方法1.2:测试2:size和length2.1:用途2.2:测试3:capacity3.1:用途3.2:测试4:clear4.1:用途4.2:测试5:empty5.1:用途5.2:测试6:reserve6.1:用途6.2:测试7:resize7.1:用途7.2:测试8:string的三种遍历8.1:方法一 for循环和[]重载8.2:方法二 迭代器8.2.1:…

基于CNTK/C#实现逻辑回归【附源码】

文章目录前言一、VS2022CNTK环境搭建二、逻辑回归代码构建1.逻辑回归构建2.训练数据的生成3.模型训练三、效果展示前言 本文基于CNTK实现逻辑回归二分类&#xff0c;并以之前的不同&#xff0c;本次使用C#实现&#xff0c;不适用python&#xff0c;python版的CNTK比较简单&…

Java多线程-ThreadPool线程池(三)

开完一趟车完整的过程是启动、行驶和停车,但老司机都知道,真正费油的不是行驶,而是长时间的怠速、频繁地踩刹车等动作。因为在速度切换的过程中,发送机要多做一些工作,当然就要多费一些油。 而一个Java线程完整的生命周期就包括:1、T1:创建(启动) 2、T2:运行(行驶)…

苹果IOS应用上架AppStore的流程与教程

快打包生成的苹果APP上架到苹果官方appstore商店的详细流程与教程第一步:创建app发布证书以及配置文件1、打开苹果开发者中心网站:https://developer.apple.com,点击右上角 Account 使用开发者账号登录,如下图所示:​编辑切换为居中添加图片注释,不超过 140 字(可选)2、…

基于IoT全链路实时质量-魔洛哥

简介: 通过基于IoT的全链路实时质量,业务使用狄仁杰进行全链路埋点后,可一键接入魔洛哥平台,实现终端问题的实时感知和链路分析,以及智能终端系统业务场景的全链路实时质量。整体方案接入成本低(分钟级别接入),可实现全链路的实时质量分析,以及精准的终端预警能力。帮…

JavaScript 51 JavaScript 严格模式

JavaScript 文章目录JavaScript51 JavaScript 严格模式51.1 "use strict" 指令51.2 声明严格模式51.3 "use strict"; 语法51.4 为什么使用严格模式&#xff1f;51.5 严格模式中不允许的事项51.6 对未来的保障51.7 警告51 JavaScript 严格模式 “use stric…

IDEA下载与安装,保姆级教程

这里写自定义目录标题1.搜索idea2.选择官方网站3.官网进入下载页面4.版本选择问题5. Ultimate和Community对比6.下载7.安装1.搜索idea 2.选择官方网站 以前idea的官网后面有官网俩字&#xff0c;现在没有了&#xff0c;你可以看他的具体网址&#xff0c;因为idea是Jetbrains公…

猿创征文|计算机学生必须掌握的学习工具

&#x1f353;个人主页&#xff1a;bit.. &#x1f352;系列专栏&#xff1a;Linux(Ubuntu)入门必看 C语言刷题 数据结构与算法 目录 一.c/c使用的软件 二.GitHub和gitee的使用 三.学会如何去调试代码 修改bug 四.学习Linux上面的基本操作 五.java使用的软件 六.p…

【案例源码公开】国产AD+全志T3开发案例,为能源电力行业排忧解难!8/16通道

前 言 本文主要介绍基于全志科技T3(ARM Cortex-A7)国产处理器的8/16通道AD采集开发案例,使用核芯互联CL1606/CL1616国产AD芯片,亦适用于ADI AD7606/AD7616。CL1606/CL1616与AD7606/AD7616软硬件兼容。 备注: (1)创龙科技TL7606I模块使用AD芯片为核芯互联CL1606或ADI AD…

Softing连接解决方案——将FANUC数控机床数据集成到西门子工业边缘

2022年10月10日&#xff08;哈尔&#xff09;&#xff0c;Softing发布了edgePlug FANUC CNC&#xff0c;其丰富了edgePlug产品系列。该产品系列基于Linux的Docker容器应用并为西门子工业边缘应用提供了控制器数据。 &#xff08;Softing的edgePlug Docker容器产品为西门子工业边…

《Python+Kivy(App开发)从入门到实践》自学笔记:简单UX部件——Label标签

章节知识点总揽 4.2 Label标签 在Kivy中&#xff0c;Label小部件用于呈现文本&#xff0c;它仅支持ASCII和Unicode编码的字符串&#xff08;不支持中文&#xff09;&#xff0c;在Label中&#xff0c;可以设置文本内容、字体、大小、颜色、对齐方式、换行、引用以及标记文字等…

【PCBA方案设计】快速体温计方案

一、电子体温计方案介绍 电子体温计由温度传感器&#xff0c;液晶显示器&#xff0c;纽扣电池&#xff0c;专用集成电路及其他电子元器件组成。能快速准确地测量人体体温&#xff0c;与传统的水银玻璃体温计相比&#xff0c;具有读数方便&#xff0c;测量时间短&#xff0c;测量…

DM数据库安装、登录和创建用户

DM数据库安装、登录和创建用户子安拉取镜像 wget -O dm8_docker.tar -c https://download.dameng.com/eco/dm8/dm8_20220822_rev166351_x86_rh6_64_ctm.tar docker load -i dm8_docker.tar docker images编写docker-compose.yml version: 3 services:dm8:image: dm8_single:v8.…