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

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

PS:

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

需求:

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

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

思路:

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

aaarticlea/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动">

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

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

  1. 启动后查询流程轨迹

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

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

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

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

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

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

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

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

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

好了下面上代码~~~~

代码:

每一个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 之自由驳回任务实现(亲测)的更多相关文章

  1. Navicat Premium 12.0.24安装与激活(亲测已成功激活)

    另请参见:Navicat Premium 12.0.18 / 12.0.24安装与激活 另请参见:Navicat Premium 12安装与激活(亲测已成功激活) 说明: 本主亲自验证过,可以激活! ...

  2. activiti explorer5.22.0源代码解读

    请求通过ExplorerApplicationServlet(AbstractApplicationServlet.service()方法)进入web系统中. Activiti Explorer的应用 ...

  3. Hadoop 2.7.0模拟分布式实验环境搭建[亲测]

    实验目的: 本实验通过在PC电脑上同时运行3个虚拟机,一个为master节点,两个slave节点.    搭建环境: 主机:mac os 10.10   OS:CenOS 6.5 虚拟机:VMware ...

  4. Linux下安装Harbor 1.8.0 仓库的安装和使用(亲测)

    根据Harbor官方描述: Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器,通过添加一些企业必需的功能特性,例如安全.标识和管理等,扩展了开源Docker Distri ...

  5. Activiti工作流学习之SpringBoot整合Activiti5.22.0实现在线设计器(二)

    一.概述 网上有很多关于Eclipse.IDEA等IDE插件通过拖拽的方式来画工作流程图,个人觉得还是不够好,所以花点时间研究了一下Activiti在线设计器,并与SpringBoot整合. 二.实现 ...

  6. 说一下集成 diagram-viewer 的心路历程 5.22.0

    1. 下载部署包文件地址:https://github.com/Activiti/Activiti/releases/download/activiti-5.22.0/activiti-5.22.0. ...

  7. Activiti 5.22 spring

    <!-- activiti依赖 --> <dependency> <groupId>org.activiti</groupId> <artifac ...

  8. 教你把UltraEdit如何注册激活教程及UltraEdit 22.0.0.48 官方中文版下载

    UltraEdit 22.0.0.48 官方中文版下载:链接: http://pan.baidu.com/s/1i3f7mZV 密码: r23v2015-5-30号更新 第一.关闭网络连接(或者直接拔 ...

  9. 搭建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 ...

随机推荐

  1. NPOI:处理xls文件中的合并行

    /// <summary> /// NPOI根据路径获取文件转换成DataTable /// </summary> /// <param name="FileP ...

  2. 浅析JavaScript的字符串查找函数:indexOf和search

    语法 ①indexOf:方法可返回某个指定的字符串值在长字符串中首次出现的位置.如果被查找字符串没有找到,返回-1. indexOf 说明:该方法将从头到尾地检索字符串 stringObject,看它 ...

  3. javascript中的异步 macrotask 和 microtask 简介

    javascript中的异步 macrotask 和 microtask 简介 什么是macrotask?什么是microtask?在理解什么是macrotask?什么是microtask之前,我们先 ...

  4. Python环境以及编辑器

    1.Python环境搭建 http://blog.csdn.net/qq_29663071/article/details/70158204 备注:a.个人感觉 安装路径为自己选择的 位置和特性 b. ...

  5. while,until

    while ] 注释:中括号写的条件判断式中不能用<.=.>这类符号,要用-lt.-eq.-gt这类符号,且变量前要用$来取值 do sum=$(($sum+$i)) i=$(($i+)) ...

  6. IOS学习2——Xcode快捷键大全

    转载自:图文解释XCode常用快捷键的使用 刚开始用Xcode是不是发现以前熟悉的开发环境的快捷键都不能用了?怎么快捷运行,停止,编辑等等.都不一样了.快速的掌握这些快捷键,能提供开发的效率. 其实快 ...

  7. C# VS2010结合SQL Server 2008数据库编程实现方法

    SQL Server 数据库在C#编程中经常用到,如何实现在具体项目中数据库和具体应用的结合是我们经常遇到的问题,我们这次主要针对如何使用SQL Server 数据库展开,下面是具体的操作以及简单的代 ...

  8. bzoj 2427: [HAOI2010]软件安装

    Description 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和 ...

  9. 基于telegraf+influxdb+grafana进行postgresql数据库监控

    前言 随着公司postgresql数据库被广泛应用,尤其是最近多个项目在做性能测试的时候都是基于postgresql的数据库,为了确定性能瓶颈是否会出现在数据库中,数据库监控也被我推上了日程.在网上找 ...

  10. Android开发之漫漫长途 IX——彻底掌握Binder

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...