Activiti 5.22.0 之自由驳回任务实现(亲测)
上篇博文,我们完成一个任务SKIP的实现,说好要给各位看官带来驳回实现的现在,就奉上具体实现和讲解。(其实我感觉我的注释写的已经非常清楚了,哈哈)
依旧是,先说我们的需求和思路。
PS:
从6.0.0降到5.22.0版本的原因因为项目中有一个版本冲突,导致的降级。后期还是以新版本为主。6.0版本的驳回有时间再来搞。
需求:
- 流程中的审批任务节点可以驳回到之前的任意任务节点
 - 驳回到指定节点的任务之后的轨迹不需要显示
 
嗯,大致上就是这样的一个需求,根据这个需求,其实我走了很多弯路,但都离不开两点。
思路:
1. 将当前的任务节点的下一个任务节点指定为指定的驳回任务节点
2. 将指定任务(目标任务)节点之后的流程轨迹,清空。
aaarticlea/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动">
根据这个思路,我追了源码,看了各种Service,Manager等等。因为别人的驳回流程我拿下来发现是有错的,所以就自己研究了起来。现在就直接上代码吧。呸。先上图,没图谁会信你成功了呢?
- 启动报销流程 返回的是下个任务编号
 

- 启动后查询流程轨迹
 

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

- 驳回任务到指定任务节点
 

- 驳回后查询流程轨迹图
 

- 查询驳回的历史任务信息
 

- 启动一个新的流程实例
 

- 查询新的流程实例的轨迹
 

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

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

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

好了下面上代码~~~~
代码:
每一个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这些类,还需要再研究研究~~
Activiti 5.22.0 之自由驳回任务实现(亲测)的更多相关文章
- Navicat Premium 12.0.24安装与激活(亲测已成功激活)
		
另请参见:Navicat Premium 12.0.18 / 12.0.24安装与激活 另请参见:Navicat Premium 12安装与激活(亲测已成功激活) 说明: 本主亲自验证过,可以激活! ...
 - activiti explorer5.22.0源代码解读
		
请求通过ExplorerApplicationServlet(AbstractApplicationServlet.service()方法)进入web系统中. Activiti Explorer的应用 ...
 - Hadoop 2.7.0模拟分布式实验环境搭建[亲测]
		
实验目的: 本实验通过在PC电脑上同时运行3个虚拟机,一个为master节点,两个slave节点. 搭建环境: 主机:mac os 10.10 OS:CenOS 6.5 虚拟机:VMware ...
 - Linux下安装Harbor 1.8.0 仓库的安装和使用(亲测)
		
根据Harbor官方描述: Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器,通过添加一些企业必需的功能特性,例如安全.标识和管理等,扩展了开源Docker Distri ...
 - Activiti工作流学习之SpringBoot整合Activiti5.22.0实现在线设计器(二)
		
一.概述 网上有很多关于Eclipse.IDEA等IDE插件通过拖拽的方式来画工作流程图,个人觉得还是不够好,所以花点时间研究了一下Activiti在线设计器,并与SpringBoot整合. 二.实现 ...
 - 说一下集成 diagram-viewer 的心路历程 5.22.0
		
1. 下载部署包文件地址:https://github.com/Activiti/Activiti/releases/download/activiti-5.22.0/activiti-5.22.0. ...
 - Activiti 5.22 spring
		
<!-- activiti依赖 --> <dependency> <groupId>org.activiti</groupId> <artifac ...
 - 教你把UltraEdit如何注册激活教程及UltraEdit 22.0.0.48 官方中文版下载
		
UltraEdit 22.0.0.48 官方中文版下载:链接: http://pan.baidu.com/s/1i3f7mZV 密码: r23v2015-5-30号更新 第一.关闭网络连接(或者直接拔 ...
 - 搭建Android开发环境之——Android4.0.3, 4.1, 4.2, 4.3, 4.x,及升级 ADT(22.0.5)和SDK(22.x)
		
1.首先要下载相关的软件 1). JDK 6 以上 2). eclipse( Version 3.6.2 or higher ) 点击下载 3). SDK(android-sdk_r18-windo ...
 
随机推荐
- thymeleaf模板的使用(转)
			
作者:纯洁的微笑 出处:http://www.ityouknow.com/ 在上篇文章springboot(二):web综合开发中简单介绍了一下thymeleaf,这篇文章将更加全面详细的介绍thym ...
 - 前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定
			
Vue.js 使用了基于 HTML 的模版语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据.所有 Vue.js 的模板都是合法的 HTML ,所以能被遵循规范的浏览器和 HTML 解 ...
 - linhaifeng
			
http://www.cnblogs.com/linhaifeng/p/7278389.html http://blog.51cto.com/egon09
 - 【CSS3】盒模型
			
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
 - DataBase MongoDB集群方案介绍
			
MongoDB集群方案介绍 一.什么是mongodb集群? 是指由多台mongodb服务器组成的服务器集群,称之为mongodb集群. 二.mongodb集群搭建的方式: 1.Replica Set ...
 - iOS知识点、面试题 之二
			
最近面试,与大家分享一下,分三文给大家: 当然Xcode新版本区别,以及iOS新特性 Xcode8 和iOS 10 在之前文章有发过,感兴趣的可以查阅: http://www.cnblogs.com/ ...
 - iOS 让图片变模糊
			
#import <Accelerate/Accelerate.h> 1.初始化图片 UIImageView *iv = [[UIImageView alloc]initWithFrame: ...
 - akka-stream与actor系统集成以及如何处理随之而来的背压问题
			
这几天上海快下了五天的雨☔️☔️☔️☔️,淅淅沥沥,郁郁沉沉. 一共存在四个api: Source.actorRef,返回actorRef,该actorRef接收到的消息,将被下游消费者所消费 ...
 - 一个两年java程序猿的2017个人总结
			
前言 又到了一年中最后的日子了,相信有不少公司要求员工写年度总结了,我也不例外.不过个人感觉在公司的写个年度总结来说,过于模板化了.其实很多没有必要.总之,本篇的个人总结,是按照个人的想法写的.简而言 ...
 - lua 实现tableToString
			
function tableToString(studentNum) local str = "{ " str = str.."\n" for k, v in ...