activiti5.22.0之自由驳回任务实现(亲测)

author author     2022-10-10     106

关键词:

上篇博文,我们完成一个任务SKIP的实现,说好要给各位看官带来驳回实现的现在,就奉上具体实现和讲解。(其实我感觉我的注释写的已经非常清楚了,哈哈)

依旧是,先说我们的需求和思路。

PS:

从6.0.0降到5.22.0版本的原因因为项目中有一个版本冲突,导致的降级。后期还是以新版本为主。6.0版本的驳回有时间再来搞。

需求:

  1. 流程中的审批任务节点可以驳回到之前的任意任务节点
  2. 驳回到指定节点的任务之后的轨迹不需要显示

嗯,大致上就是这样的一个需求,根据这个需求,其实我走了很多弯路,但都离不开两点。

思路:

1. 将当前的任务节点的下一个任务节点指定为指定的驳回任务节点
2. 将指定任务(目标任务)节点之后的流程轨迹,清空。

点击并拖拽以移动

根据这个思路,我追了源码,看了各种Service,Manager等等。因为别人的驳回流程我拿下来发现是有错的,所以就自己研究了起来。现在就直接上代码吧。呸。先上图,没图谁会信你成功了呢?

  1. 启动报销流程 返回的是下个任务编号

1.启动报销流程

  1. 启动后查询流程轨迹

2.启动后查询流程轨迹

  1. 查询流程中历史任务节点信息

3.查询流程中历史任务节点信息

  1. 驳回任务到指定任务节点

4.驳回任务到指定节点

  1. 驳回后查询流程轨迹图

5.驳回后查询

  1. 查询驳回的历史任务信息

6.查询驳回的历史任务信息

  1. 启动一个新的流程实例

7启动一个新的流程实例

  1. 查询新的流程实例的轨迹

8.查询新的流程实例的轨迹

  1. 完成新的流程实例任务,模拟审批通过

9.完成新的流程实例任务,模拟审批通过

  1. 查询新流程实例对应完成任务后的轨迹

10.查询新流程实例对应完成任务后的轨迹

嗯 上面 就是一个测试过程,主要想表达一个意思:当前流程实例中的任务驳回之后,不影响别的流程实例。这里有一张之前研究时的错误图,可以给大家看看。不喷哈~~

研究过程中的错误

好了下面上代码~~~~

代码:

每一个region endregion是一个代码块。在IDEA中是可以折叠的。C#中的习惯吧算是 能让代码更好看些。。。。(个人认为)

/**
 * 驳回任务方封装
 *
 * @param destinationTaskID 驳回的任务ID 目标任务ID
 * @param messageContent  驳回的理由
 * @param currentTaskID  当前正要执行的任务ID
 * @return 驳回结果 携带下个任务编号
 */
public ResponseResult rejectTask(String destinationTaskID, String currentTaskID, String messageContent) {
        // region 目标任务实例 historicDestinationTaskInstance 带流程变量,任务变量
        HistoricTaskInstance historicDestinationTaskInstance = historyService
                            .createHistoricTaskInstanceQuery()
                            .taskId(destinationTaskID)
                            .includeProcessVariables()
                            .includeTaskLocalVariables()
                            .singleResult();
            // endregion
            // region 正在执行的任务实例 historicCurrentTaskInstance 带流程变量,任务变量
            HistoricTaskInstance historicCurrentTaskInstance = historyService
                            .createHistoricTaskInstanceQuery()
                            .taskId(currentTaskID)
                            .includeProcessVariables()
                            .includeTaskLocalVariables()
                            .singleResult();
            // endregion
            // 流程定义ID
            String processDefinitionId = historicCurrentTaskInstance.getProcessDefinitionId();
            // 流程实例ID
            String processInstanceId = historicCurrentTaskInstance.getProcessInstanceId();
            // 流程定义实体
            ProcessDefinitionEntity processDefinition =
                    (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);
            // region 根据任务创建时间正序排序获取历史任务实例集合 historicTaskInstanceList 含流程变量,任务变量
            List<HistoricTaskInstance> historicTaskInstanceList = historyService
                    .createHistoricTaskInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .includeProcessVariables()
                    .includeTaskLocalVariables()
                    .orderByTaskCreateTime()
                    .asc()
                    .list();
            // endregion
            // region 历史活动节点实例集合 historicActivityInstanceList
            List<HistoricActivityInstance> historicActivityInstanceList =
                    historyService
                            .createHistoricActivityInstanceQuery()
                            .processInstanceId(processInstanceId)
                            .orderByHistoricActivityInstanceStartTime()
                            .asc()
                            .list();
            // endregion
            // 获取目标任务的节点信息
            ActivityImpl destinationActivity = processDefinition
                    .findActivity(historicDestinationTaskInstance.getTaskDefinitionKey());
            // 定义一个历史任务集合,完成任务后任务删除此集合中的任务
            List<HistoricTaskInstance> deleteHistoricTaskInstanceList = new ArrayList<>();
            // 定义一个历史活动节点集合,完成任务后要添加的历史活动节点集合
            List<HistoricActivityInstanceEntity> insertHistoricTaskActivityInstanceList = new ArrayList<>();
            // 目标任务编号
            Integer destinationTaskInstanceId = Integer.valueOf(destinationTaskID);
            // 有序
            for (HistoricTaskInstance historicTaskInstance : historicTaskInstanceList) {
                Integer historicTaskInstanceId = Integer.valueOf(historicTaskInstance.getId());
                if (destinationTaskInstanceId <= historicTaskInstanceId) {
                    deleteHistoricTaskInstanceList.add(historicTaskInstance);
                }
            }
            // 有序
            for (int i = 0; i < historicActivityInstanceList.size() - 1; i++) {
                HistoricActivityInstance historicActivityInstance = historicActivityInstanceList.get(i);
                // 历史活动节点的任务编号
                Integer historicActivityInstanceTaskId;
                String taskId = historicActivityInstance.getTaskId();
                if (taskId != null) {
                    historicActivityInstanceTaskId = Integer.valueOf(taskId);
                    if (historicActivityInstanceTaskId <= destinationTaskInstanceId) {
                        insertHistoricTaskActivityInstanceList.add((HistoricActivityInstanceEntity) historicActivityInstance);
                    }
                } else {
                    if (historicActivityInstance.getActivityType().equals(ProcessConstant.START_EVENT)) {
                        insertHistoricTaskActivityInstanceList.add((HistoricActivityInstanceEntity) historicActivityInstance);
                    } else if (historicActivityInstance.getActivityType().equals(ProcessConstant.EXCLUSIVE_GATEWAY)) {
                        insertHistoricTaskActivityInstanceList.add((HistoricActivityInstanceEntity) historicActivityInstance);
                    }
                }
            }
            // 获取流程定义的节点信息
            List<ActivityImpl> processDefinitionActivities = processDefinition.getActivities();
            // 用于保存正在执行的任务节点信息
            ActivityImpl currentActivity = null;
            // 用于保存原来的任务节点的出口信息
            PvmTransition pvmTransition = null;
            // 保存原来的流程节点出口信息
            for (ActivityImpl activity : processDefinitionActivities) {
                if (historicCurrentTaskInstance.getTaskDefinitionKey().equals(activity.getId())) {
                    currentActivity = activity;
                    // 备份
                    pvmTransition = activity.getOutgoingTransitions().get(0);
                    // 清空当前任务节点的出口信息
                    activity.getOutgoingTransitions().clear();
                }
            }
            // 执行流程转向
            processEngine.getManagementService().executeCommand(
                    new RejectTaskCMD(historicDestinationTaskInstance, historicCurrentTaskInstance, destinationActivity));
            // 获取正在执行的任务的流程变量
            Map<String, Object> taskLocalVariables = historicCurrentTaskInstance.getTaskLocalVariables();
            // 获取目标任务的流程变量,修改任务不自动跳过,要求审批
            Map<String, Object> processVariables = historicDestinationTaskInstance.getProcessVariables();
            // 获取流程发起人编号
            Integer employeeId = (Integer) processVariables.get(ProcessConstant.PROCESS_START_PERSON);
            processVariables.put(ProcessConstant.SKIP_EXPRESSION, false);
            taskLocalVariables.put(ProcessConstant.SKIP_EXPRESSION, false);
            // 设置驳回原因
            taskLocalVariables.put(ProcessConstant.REJECT_REASON, messageContent);
            // region 流程变量转换
            // 修改下个任务的任务办理人
            processVariables.put(ProcessConstant.DEAL_PERSON_ID, processVariables.get(ProcessConstant.CURRENT_PERSON_ID));
            // 修改下个任务的任务办理人姓名
            processVariables.put(ProcessConstant.DEAL_PERSON_NAME, processVariables.get(ProcessConstant.CURRENT_PERSON_NAME));
            // 修改下个任务的任务办理人
            taskLocalVariables.put(ProcessConstant.DEAL_PERSON_ID, processVariables.get(ProcessConstant.CURRENT_PERSON_ID));
            // 修改下个任务的任务办理人姓名
            taskLocalVariables.put(ProcessConstant.DEAL_PERSON_NAME, processVariables.get(ProcessConstant.CURRENT_PERSON_NAME));
            // endregion
            // 完成当前任务,任务走向目标任务
            String nextTaskId = processService.completeTaskByTaskID(currentTaskID, processVariables, taskLocalVariables);
            if (currentActivity != null) {
                // 清空临时转向信息
                currentActivity.getOutgoingTransitions().clear();
            }
            if (currentActivity != null) {
                // 恢复原来的走向
                currentActivity.getOutgoingTransitions().add(pvmTransition);
            }
            // 删除历史任务
            for (HistoricTaskInstance historicTaskInstance : deleteHistoricTaskInstanceList) {
                historyService.deleteHistoricTaskInstance(historicTaskInstance.getId());
            }
            // 删除活动节点
            processEngine.getManagementService().executeCommand(
                    (Command<List<HistoricActivityInstanceEntity>>) commandContext -> {
                        HistoricActivityInstanceEntityManager historicActivityInstanceEntityManager =
                                commandContext.getHistoricActivityInstanceEntityManager();
                        // 删除所有的历史活动节点
                        historicActivityInstanceEntityManager
                                .deleteHistoricActivityInstancesByProcessInstanceId(processInstanceId);
                        // 提交到数据库
                        commandContext.getDbSqlSession().flush();
                        // 添加历史活动节点的
                        for (HistoricActivityInstanceEntity historicActivityInstance : insertHistoricTaskActivityInstanceList) {
                            historicActivityInstanceEntityManager.insertHistoricActivityInstance(historicActivityInstance);
                        }
                        // 提交到数据库
                        commandContext.getDbSqlSession().flush();
                        return null;
                    }
            );
        // 返回下个任务的任务ID
        return ResponseResultUtil.success(nextTaskId);
    }

我自己都知道有不好的地方,但是别的方法我没有实现成功,所以先这样做吧。过年的时候再好好看看改改。

下面是RejectTaskCMD这个类的代码:

package com.edu.hart.web.manage.process;

import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ExecutionEntityManager;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.TransitionImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;

/**
 * 任务驳回方法支持
 *
 * @author create by 叶云轩 at 2018/1/15 09:32
 */
public class RejectTaskCMD implements Command<Object>, Serializable {
    /**
 * RejectTaskCMD 日志控制器
 * Create by 叶云轩 at 2018/1/19 09:43
 * Concat at yCountJavaXuan@outlook.com
 */
    private static final Logger LOGGER = LoggerFactory.getLogger(RejectTaskCMD.class);
    /**
 * 历史信息中的当前任务实例
 */
    private HistoricTaskInstance currentTaskInstance;
    /**
 * 历史信息中的目标任务实例
 */
    private HistoricTaskInstance destinationTaskInstance;
    /**
 * 目标任务节点
 */
    private ActivityImpl destinationActivity;

    /**
 * 构造方法
 *
 * @param currentTaskInstance  当前任务实例
 * @param destinationTaskInstance 目标任务实例
 * @param destinationActivity  目标节点
 */
    public RejectTaskCMD(HistoricTaskInstance currentTaskInstance
            , HistoricTaskInstance destinationTaskInstance
            , ActivityImpl destinationActivity) {
        this.currentTaskInstance = currentTaskInstance;
        this.destinationTaskInstance = destinationTaskInstance;
        this.destinationActivity = destinationActivity;
    }

    @Override
    public Object execute(CommandContext commandContext) {
        // 流程实例ID
        String processInstanceId = destinationTaskInstance.getProcessInstanceId();
        // 执行管理器
        ExecutionEntityManager executionEntityManager =
                commandContext.getExecutionEntityManager();
        // select * from ACT_RU_EXECUTION where ID_ = ? 查询当前流程实例中正在执行的唯一任务 --追源码时发现这个方法的作用,就记录了下来,省的自己遗忘掉
        ExecutionEntity executionEntity = executionEntityManager.findExecutionById(processInstanceId);
        // 当前活跃的节点信息
        ActivityImpl currentActivity = executionEntity.getActivity();
        // 创建一个出口转向
        TransitionImpl outgoingTransition = currentActivity.createOutgoingTransition();
        // 封装目标节点到转向实体
        outgoingTransition.setDestination(destinationActivity);
        // 流程转向
        executionEntity.setTransition(outgoingTransition);
        return null;
    }
}

嗯,就是这样来完成任意节点驳回的。当前先这样实现了,6.0版本没有了Pvm这些类,还需要再研究研究~~

全网最新springboot2.5.1整合activiti5.22.0企业实战教程<流程挂起与激活篇>

文章目录前言一、单个流程实例挂起1.1部署流程1.2启动流程1.3挂起流程(suspendProcessInstanceById)1.4执行任务1.5激活流程(activateProcessInstanceById)二、全部流程实例挂起1.1激活1.2挂起1.3代码总结前言某些情况可能由于流程变更需要... 查看详情

activiti工作流几种驳回方式的实现与比较

最近公司做的一个项目要实现工作流程的收回,驳回等操作,而采用的工作流引擎并不支持驳回功能,这个项目恰好就我和一个实习生一块做,所以这个问题就落到我的头上来解决了。。。客户提出的要求是驳回时要记录日志,... 查看详情

flowable之驳回多实例驳回并行网关驳回普通节点驳回到多实例

flowable驳回欢迎大家学习交流,如有不对的地方,请大家多多指教,我接下来会把flowable的所有的中国式API*都写出来,也希望对大家有帮助,程序员只要静下心来,其实可以产生巨大的能量,靠任何人都没有用,唯有靠自己。学... 查看详情

工作流引擎驳回设计

1.1关于驳回驳回,在有的应用中叫“回退”。驳回是中国特色的一种方式,驳回在流程图上也没有迁移线的表达经常也是隐性的,比如申请经费可能由于资料不足被驳回来补充资料,像这样的例子有非常多,也很常见。驳... 查看详情

第十五篇camunda系列-任务回退驳回回退(代码片段)

Camunda任务回退/驳回专题  任务回退驳回撤销相关的操作在实际的开发中还是会经常遇到的,我们来看看Camunda中针对这些情况是如何处理的。1.串行的回退  我们先从最简单的串行流程来分析,案例如下  上面的流程就是... 查看详情

第十五篇camunda系列-任务回退驳回回退(代码片段)

Camunda任务回退/驳回专题  任务回退驳回撤销相关的操作在实际的开发中还是会经常遇到的,我们来看看Camunda中针对这些情况是如何处理的。1.串行的回退  我们先从最简单的串行流程来分析,案例如下  上面的流程就是... 查看详情

activiti6.0提交流程至某节点,可用于实现驳回操作(未测试)(代码片段)

/***@paramtask任务Id*@paramvariables...*@paramtargetActivityId节点ID*@throwsException...*@creator毛志涛*@date2018/2/25*@描述提交流程至某节点*/privatevoidcommitProcess(Tasktask,Map<String,Object>variables,Stringt 查看详情

activiti5.22整合modeler时出错typeerror:cannotreadproperty'split'ofundefined

activiti5.22.0整合modeler时,打开的流程页面不显示工具栏和左边的控件栏,产生如下的错误:TypeError:Cannotreadproperty‘split‘ofundefinedatObject.ORYX.Core.StencilSet.stencilSet(oryx.debug.js:8640)atoryx.debug.js:8619atprototype-1.5.1.js 查看详情

基于lvs的负载均衡实现之nat

...目在1998年5月由章文嵩博士成立,是中国国内最早出现的自由软件项目 查看详情

jenkins入门之执行定时任务

...,下面就讲一下如何使用Jenkins执行定时任务.我们新建一个自由式(FreeStyleProject)任务,命名为periodi 查看详情

商标驳回和不予受理区别是啥?

  1.拒绝的原因不同  商标驳回是因为商标不符合在先申请或者在先注册原则而被驳回注册的;但因申请人提交的商标申请文件或者违反禁止注册规定,才会不予受理。  2.审查的阶段和对象不同  商标不予受理仍处于... 查看详情

springmvc自动任务调度之task实现

<?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&# 查看详情

spring实现定时任务之执行时间设置规则

Spring实现定时任务之执行时间设置规则一个cronExpression表达式有至少6个(也可能是7个)由空格分隔的时间元素。从左至右,这些元素的定义如下:字段域秒分时日月星期(7为周六)年(可选)取值... 查看详情

celery学习---celery最佳实践之与django结合实现异步任务

django可以轻松跟celery结合实现异步任务,只需简单配置即可同步执行和异步执行注意:即使Celery的任务没有执行完成,但是已经创建了任务ID。可以利用前台的定时任务发送Ajax异步请求根据ID查询结果项目整合项目的目录结构:... 查看详情

优雅实现延时任务之zookeeper篇(代码片段)

前言在《优雅实现延时任务之Redis篇》一文中提到,实现延时任务的关键点,是要存储任务的描述和任务的执行时间,还要能根据任务执行时间进行排序,那么我们可不可以使用zookeeper来实现延时任务呢?答案... 查看详情

重学springboot系列之异步任务与定时任务(代码片段)

重学SpringBoot系列之异步任务与定时任务实现Async异步任务环境准备同步调用异步调用异步回调为异步任务规划线程池SpringBoot任务线程池自定义线程池优雅地关闭线程池通过@Scheduled实现定时任务开启定时任务方法不同定时方式... 查看详情

efcodefirst自由组卷功能的设计与实现

1.今日完成任务自由组卷功能的实现试卷添加试卷修改2.核心代码自由组卷是在线考试系统的核心功能,也是整个系统的关键部分。组卷的思路是,根据题库及其题目数量,动态生成试卷。于是,设计界面如下:试卷名称、时长... 查看详情

silverlight&blend动画设计系列十二:三角函数(trigonometry)动画之自由旋转(free-formrotation)

...amp;Blend动画设计系列十二:三角函数(Trigonometry)动画之自由旋转(Free-formrotation)  说到对象的旋转,或许就会联想到对象角度的概念。对象的旋转实现实际上就是利用对象的角度改变来实现的位置变换,在《Silverlight&Ble... 查看详情