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

时间:2022-01-02 14:03:35

最近公司做的一个项目要实现工作流程的收回,驳回等操作,而采用的工作流引擎并不支持驳回功能,这个项目恰好就我和一个实习生一块做,所以这个问题就落到我的头上来解决了。。。

客户提出的要求是驳回时要记录日志,但是审批意见之类的需要清除掉,收回时不记录日志,审批意见同样清除。

百度了一下,总结了5个解决方案:

第一种是清除当前任务的出口,实现回到上一步的功能。

根据任务ID获取当前的任务节点,进而获取流程实例,然后取到流程的定义,找到当前活动的节点与上一步活动的节点,然后清除当前活动的出口,将上一步活动的出口作为最新的活动节点。

第二种是直接将当前的activiti引擎更换为第三方的支持退会驳回的引擎。

第三种是自己编译activiti引擎,加入退回,驳回功能。

第四种是直接操作数据库,使用历史任务替换正在进行的任务。

第五种是直接实现Command接口,实现工作流程可以任意跳转。

因为时间紧急所以直接将3.编译activiti引擎给pass掉了,本来项目是接手的离职员工的项目,更换流程引擎可能会引起其他的问题,此计划暂放。

最后在1、4、5之间决定了采用第一种,代码如下

 public String rollBackWorkFlow(String taskId) {
try {
Map<String, Object> variables;
// 取得当前任务.当前任务节点
HistoricTaskInstance currTask = historyService
.createHistoricTaskInstanceQuery().taskId(taskId)
.singleResult();
// 取得流程实例,流程实例
ProcessInstance instance = runtimeService
.createProcessInstanceQuery()
.processInstanceId(currTask.getProcessInstanceId())
.singleResult();
if (instance == null) {
logger.info("流程结束");
logger.error("出错啦!流程已经结束");
return "ERROR";
}
variables = instance.getProcessVariables();
// 取得流程定义
ProcessDefinitionEntity definition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
.getDeployedProcessDefinition(currTask
.getProcessDefinitionId());
if (definition == null) {
logger.info("流程定义未找到");
logger.error("出错啦!流程定义未找到");
return "ERROR";
}
// 取得上一步活动
ActivityImpl currActivity = ((ProcessDefinitionImpl) definition)
.findActivity(currTask.getTaskDefinitionKey()); //也就是节点间的连线
List<PvmTransition> nextTransitionList = currActivity
.getIncomingTransitions();
// 清除当前活动的出口
List<PvmTransition> oriPvmTransitionList = new ArrayList<PvmTransition>();
//新建一个节点连线关系集合 List<PvmTransition> pvmTransitionList = currActivity
.getOutgoingTransitions();
//
for (PvmTransition pvmTransition : pvmTransitionList) {
oriPvmTransitionList.add(pvmTransition);
}
pvmTransitionList.clear(); // 建立新出口
List<TransitionImpl> newTransitions = new ArrayList<TransitionImpl>();
for (PvmTransition nextTransition : nextTransitionList) {
PvmActivity nextActivity = nextTransition.getSource();
ActivityImpl nextActivityImpl = ((ProcessDefinitionImpl) definition)
.findActivity(nextActivity.getId());
TransitionImpl newTransition = currActivity
.createOutgoingTransition();
newTransition.setDestination(nextActivityImpl);
newTransitions.add(newTransition);
}
// 完成任务
List<Task> tasks = taskService.createTaskQuery()
.processInstanceId(instance.getId())
.taskDefinitionKey(currTask.getTaskDefinitionKey()).list();
for (Task task : tasks) {
taskService.claim(task.getId(), task.getAssignee());
taskService.complete(task.getId(), variables);
historyService.deleteHistoricTaskInstance(task.getId());
}
// 恢复方向
for (TransitionImpl transitionImpl : newTransitions) {
currActivity.getOutgoingTransitions().remove(transitionImpl);
}
for (PvmTransition pvmTransition : oriPvmTransitionList) {
pvmTransitionList.add(pvmTransition);
}
logger.info("OK");
logger.info("流程结束");
return "SUCCESS";
} catch (Exception e) {
logger.error("失败",e);
return "ERROR";
}
}

原本一切按计划进行着,但不知为什么在taskService.complete(task.getId(), variables);完成任务这一步,一直报错误,debug了好久也没有找到错误,无奈之下,采用4、5两种方式结合先将流程跳转到上一个人,然后在操作数据库清除掉一些不必要的数据。代码如下

 /**
* 工作流*跳转
* @param taskId 要跳转到的节点名称
* @return
*/
public String taskRollback(String taskId){
//根据要跳转的任务ID获取其任务
HistoricTaskInstance hisTask = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
String taskAssignee = hisTask.getAssignee();
//进而获取流程实例
ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(hisTask.getProcessInstanceId()).singleResult();
//取得流程定义
ProcessDefinitionEntity definition = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(hisTask.getProcessDefinitionId());
//获取历史任务的Activity
ActivityImpl hisActivity = definition.findActivity(hisTask.getTaskDefinitionKey());
//实现跳转
managementService.executeCommand(new JumpCmd(instance.getId(), hisActivity.getId()));
return hisTask.getProcessInstanceId();
}

重写的Command接口

 public class JumpCmd implements Command<ExecutionEntity>{

     private String processInstanceId;
private String activityId;
public static final String REASION_DELETE = "deleted"; public JumpCmd(String processInstanceId, String activityId) {
this.processInstanceId = processInstanceId;
this.activityId = activityId;
} public ExecutionEntity execute(CommandContext commandContext) {
ExecutionEntity executionEntity = commandContext.getExecutionEntityManager().findExecutionById(processInstanceId);
executionEntity.destroyScope(REASION_DELETE);
ProcessDefinitionImpl processDefinition = executionEntity.getProcessDefinition();
ActivityImpl activity = processDefinition.findActivity(activityId);
executionEntity.executeActivity(activity);
return executionEntity;
} }

最后手动清除流程细节信息

 /**
* 跳转到指定节点,并隐藏处理信息
*
* @param lastTaskID
*/
public String Back(String lastTaskID, String runTtaskId, String back) {
String msg = "";
try {
HistoricTaskInstance hisTask = historyService.createHistoricTaskInstanceQuery().taskId(lastTaskID).singleResult();
// 判断是否会签
boolean flag = true;
ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(hisTask.getProcessDefinitionId());
List<ActivityImpl> activitiList = processDefinitionEntity.getActivities();
for (ActivityImpl activityImpl : activitiList) {
if (activityImpl.getId().equals(hisTask.getTaskDefinitionKey())) {// 找到对应任务
Object sign = activityImpl.getProperties().get("multiInstance");// 获取会签标识
if (sign != null) {// 会签
// 会签暂时不可以退回
System.out.println("会签");
msg = "会签不可以退回,收回";
flag = false;
break;
}
}
}
// 不是会签
if (flag) {
// if(back!=null && !back.equals("")){
// // //完成当前任务,设置处理信息
// HistoricTaskInstance runTask =
// historyService.createHistoricTaskInstanceQuery().taskId(runTtaskId).singleResult();
// systemService.findUniqueByProperty(TSBaseUser.class,
// "userName", runTask.getAssignee()).getRealName();
// Map variables = new HashMap();
// variables.put("back",
// systemService.findUniqueByProperty(TSBaseUser.class,
// "userName",
// runTask.getAssignee()).getRealName()+":退回到:"+systemService.findUniqueByProperty(TSBaseUser.class,
// "userName", hisTask.getAssignee()).getRealName());
// taskService.claim(runTask.getId(),
// runTask.getAssignee());//领取任务
// taskService.complete(runTask.getId(), variables);//完成当前任务
// }
// 跳转到上一个处理人
taskRollback(lastTaskID);
// 设置下一步处理人
setAssigneeDynamic(hisTask.getProcessInstanceId(), hisTask.getAssignee());
// 隐藏签字痕迹,opt=0
String sql = "UPDATE ACT_HI_DETAIL SET LONG_='0' where ACT_INST_ID_ = (select ACT_INST_ID_ from ACT_HI_DETAIL where NAME_='taskid' and TEXT_='" + lastTaskID + "') and NAME_='opt'";
int i = systemService.getSession().createSQLQuery(sql).executeUpdate();
if (i != 1) {
logger.error("流程退回/收回隐藏签字意见出现问题,如果没有填写意见或者签字请忽略,流程可能已经出错,请修改ACT_HI_DETAIL表,任务ID:" + lastTaskID);
}
// 修改业务中的下一步处理人
ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(hisTask.getProcessInstanceId()).singleResult(); String id = instance.getBusinessKey();
TSBaseBus base = systemService.getEntity(TSBaseBus.class, id);
try {
Method getCurrentSteps = base.getClass().getMethod("setCurrentSteps", String.class);
Method getCurrentUsers = base.getClass().getMethod("setCurrentUsers", String.class);
getCurrentSteps.invoke(base, hisTask.getTaskDefinitionKey());
getCurrentUsers.invoke(base, hisTask.getAssignee());
TSPrjstatus tSPrjstatus = systemService.get(TSPrjstatus.class, BaseDataConstants.WF_STATE_REBACK);// 修改状态为退回
base.setTSPrjstatus(tSPrjstatus);
} catch (Exception e) {
logger.error("退回/收回时修改业务表中下一步处理人出错", e);
}
systemService.save(base);
}
} catch (Exception e) {
logger.error("流程退回/收回失败,设置下一步处理人出错,任务ID:" + lastTaskID, e);
msg = null;
}
return msg;
}

中间一边写一边调试,终于可以实现驳回功能了。但是这样在实现方式上可能会出问题,因为自己操作数据代码,与activiti操作数据的代码,不在一个事物中,很可能出现流程退回去了,签字信息没有被擦除掉。

以后再使用工作流时尽量根据需求选用合适的流程引擎。

(在具体实现上参考了许多人的代码,思想)

Activiti工作流几种驳回方式的实现与比较的更多相关文章

  1. Activiti工作流的应用示例 (官方guide项目方式)

    转: Activiti工作流的应用示例 1.新建流程模型 模型管理->模型工作区 点击“创建”后会立即跳转到“流程在线设计器”页面,请参考下一节 2.在线流程设计器 模型管理->模型工作区 ...

  2. activiti工作流的web流程设计器整合视频教程 SSM和独立部署

    本视频为activiti工作流的web流程设计器整合视频教程 整合Acitiviti在线流程设计器(Activiti-Modeler 5.21.0 官方流程设计器) 本视频共讲了两种整合方式 1. 流 ...

  3. activiti工作流的web流程设计器整合视频教程 SSM 和 独立部署

    本视频为activiti工作流的web流程设计器整合视频教程 整合Acitiviti在线流程设计器(Activiti-Modeler 5.21.0 官方流程设计器) 本视频共讲了两种整合方式 1. 流 ...

  4. activiti工作流的web流程设计器整合视频教程

    本视频为activiti工作流的web流程设计器整合视频教程 整合Acitiviti在线流程设计器(Activiti-Modeler 5.21.0 官方流程设计器) 本视频共讲了两种整合方式 1. 流 ...

  5. 比较Activiti中三种不同的表单及其应用

    http://www.kafeitu.me/activiti/2012/08/05/diff-activiti-workflow-forms.html 开篇语 这个恐怕是初次接触工作流最多的话题之一了 ...

  6. Activiti工作流学习-----基于5&period;19&period;0版本&lpar;1&rpar;

    该版本的Activiti运行须知: 1.JDK 6+,Eclipse最好是Kepler以上版本. 2.试验功能都有EXPERIMENTAL标注,被标注的部分不应该视为稳定的. 有兴趣的同学可以去了解下 ...

  7. Activiti工作流&lpar;二&rpar;之常用操作

    前面介绍了Activiti工作流的基本操作,但是在实际应用过程中,往往不满足项目需求,因此还需要了解一些其他的功能比如:连线.排他网关.并行网管.流程变量.个人任务及组任务的三种发布方式. 下面将介绍 ...

  8. 深入了解Activiti工作流流程定义

    深入了解Activiti工作流流程定义 2016-03-27| 发布: | 浏览: 2363 |保存PDF 部署流程定义 部署流程定义的流程: 1. 先获取流程引擎对象:在创建时会自动加载 class ...

  9. Activiti工作流笔记(3)

    Activiti工作流的流程部署和删除流程部署 流程部署代码: /** * 部署流程 */ public class ActivitiTest { RepositoryService reposito ...

随机推荐

  1. EF里Guid类型数据的自增长、时间戳和复杂类型的用法

    通过前两章Lodging和Destination类的演示,大家肯定基本了解Code First是怎么玩的了,本章继续演示一些很实用的东西.文章的开头提示下:提供的demo为了后面演示效果,前面代码有些 ...

  2. JAVA时间格式转换大全

    import java.text.*; import java.util.Calendar; public class VeDate { /** * 获取现在时间 * * @return 返回时间类型 ...

  3. 如何通过CRM评估客户价值和提高客户忠诚度&quest;

    随着市场经济的日益繁荣,同行业之间企业的竞争越来越激烈,企业纷纷各出奇招吸引和挖掘客户,力求让自己的品牌成为更多客户的第一选择.那么,我们可以用什么方法来评估客户价值,提高客户忠诚度呢? 在互联网时代 ...

  4. POJ 2484

    A Funny Game Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 3861   Accepted: 2311 Desc ...

  5. codeforce447 D SGU 548 贪心&plus;优先队列

    codeforce447 D - DZY Loves Modification 题意:有一个n*m的矩阵,每次可以选择一行或者一列,可以得到这行或这列的所有元素sum的积分,然后使这一列/行的每一个元 ...

  6. LDAP索引及缓存优化

    一.设置索引 索引将查找信息和 Directory Server 条目关联起来. Directory Server支持以下几种索引: 1出现索引 (pres) - 列出了具有特定属性的条目,与属性的值 ...

  7. RedHat安装GCC问题-解决依赖问题

    RedHat Linux在安装gcc时需要cpp和cloog-ppl但是在安装cpp的时候需要这个依赖:libmpfr.so.1()(64bit) is needed by cpp-4.4.6-3.e ...

  8. 基于Office 365的随需应变业务应用平台

    作者:陈希章 发表于 2017年9月7日 这是我去年10月底在微软技术大会(Microsoft Ignite 2016) 上面的演讲主题,承蒙大家抬爱,也沾了前一场明星讲师的光,我记得会场几乎是满座. ...

  9. bootstrap中的dropdown组件扩展hover事件

    bootstrap的下拉组件,需要点击click时,方可展示下拉列表.因此对于喜欢简单少操作的大家来说,点击一下多少带来不便,因此,引入hover监听,鼠标经过自动展示下拉框.其实在bootstrap ...

  10. Consul集群搭建

    一.集群搭建 准备三台机器 需要开启的端口,8300, 8301, 8500, 8600 机器1: 172.16.106.201 ./consul agent -server -bootstrap-e ...