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

Posted kongbin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Activiti工作流几种驳回方式的实现与比较相关的知识,希望对你有一定的参考价值。

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

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

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

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

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

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

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

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

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

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

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

技术分享
 1 public String rollBackWorkFlow(String taskId) {
 2         try {
 3             Map<String, Object> variables;
 4             // 取得当前任务.当前任务节点
 5             HistoricTaskInstance currTask = historyService
 6                     .createHistoricTaskInstanceQuery().taskId(taskId)
 7                     .singleResult();
 8             // 取得流程实例,流程实例
 9             ProcessInstance instance = runtimeService
10                     .createProcessInstanceQuery()
11                     .processInstanceId(currTask.getProcessInstanceId())
12                     .singleResult();
13             if (instance == null) {
14              logger.info("流程结束");
15              logger.error("出错啦!流程已经结束");
16                 return "ERROR";
17             }
18             variables = instance.getProcessVariables();
19             // 取得流程定义
20             ProcessDefinitionEntity definition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
21                     .getDeployedProcessDefinition(currTask
22                             .getProcessDefinitionId());
23             if (definition == null) {
24                 logger.info("流程定义未找到");
25                 logger.error("出错啦!流程定义未找到");
26                 return "ERROR";
27             }
28             // 取得上一步活动
29             ActivityImpl currActivity = ((ProcessDefinitionImpl) definition)
30                     .findActivity(currTask.getTaskDefinitionKey());
31             
32             //也就是节点间的连线
33             List<PvmTransition> nextTransitionList = currActivity
34                     .getIncomingTransitions();
35             // 清除当前活动的出口
36             List<PvmTransition> oriPvmTransitionList = new ArrayList<PvmTransition>();
37             //新建一个节点连线关系集合
38             
39             List<PvmTransition> pvmTransitionList = currActivity
40                     .getOutgoingTransitions();
41             //
42             for (PvmTransition pvmTransition : pvmTransitionList) {
43                 oriPvmTransitionList.add(pvmTransition);
44             }
45             pvmTransitionList.clear();
46  
47             // 建立新出口
48             List<TransitionImpl> newTransitions = new ArrayList<TransitionImpl>();
49             for (PvmTransition nextTransition : nextTransitionList) {
50                 PvmActivity nextActivity = nextTransition.getSource();
51                 ActivityImpl nextActivityImpl = ((ProcessDefinitionImpl) definition)
52                         .findActivity(nextActivity.getId());
53                 TransitionImpl newTransition = currActivity
54                         .createOutgoingTransition();
55                 newTransition.setDestination(nextActivityImpl);
56                 newTransitions.add(newTransition);
57             }
58             // 完成任务
59             List<Task> tasks = taskService.createTaskQuery()
60                     .processInstanceId(instance.getId())
61                     .taskDefinitionKey(currTask.getTaskDefinitionKey()).list();
62             for (Task task : tasks) {
63                 taskService.claim(task.getId(), task.getAssignee());
64                 taskService.complete(task.getId(), variables);
65                 historyService.deleteHistoricTaskInstance(task.getId());
66             }
67             // 恢复方向
68             for (TransitionImpl transitionImpl : newTransitions) {
69                 currActivity.getOutgoingTransitions().remove(transitionImpl);
70             }
71             for (PvmTransition pvmTransition : oriPvmTransitionList) {
72                 pvmTransitionList.add(pvmTransition);
73             }
74             logger.info("OK");
75             logger.info("流程结束");
76             return "SUCCESS";
77         } catch (Exception e) {
78             logger.error("失败",e);
79             return "ERROR";
80         }
81     }
View Code

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

技术分享
 1 /**
 2      * 工作流自由跳转
 3      * @param taskId 要跳转到的节点名称
 4      * @return
 5      */
 6     public String taskRollback(String taskId){
 7         //根据要跳转的任务ID获取其任务
 8         HistoricTaskInstance hisTask = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
 9         String taskAssignee = hisTask.getAssignee();
10         //进而获取流程实例
11         ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(hisTask.getProcessInstanceId()).singleResult();
12         //取得流程定义
13         ProcessDefinitionEntity definition = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(hisTask.getProcessDefinitionId());
14         //获取历史任务的Activity
15         ActivityImpl hisActivity = definition.findActivity(hisTask.getTaskDefinitionKey());
16         //实现跳转
17         managementService.executeCommand(new JumpCmd(instance.getId(), hisActivity.getId()));
18         return hisTask.getProcessInstanceId();
19     }
View Code

重写的Command接口

技术分享
 1 public class JumpCmd implements Command<ExecutionEntity>{
 2 
 3     private String processInstanceId;
 4     private String activityId;
 5     public static final String REASION_DELETE = "deleted";
 6 
 7     public JumpCmd(String processInstanceId, String activityId) {
 8         this.processInstanceId = processInstanceId;
 9         this.activityId = activityId;
10     }
11 
12     public ExecutionEntity execute(CommandContext commandContext) {
13         ExecutionEntity executionEntity = commandContext.getExecutionEntityManager().findExecutionById(processInstanceId);
14         executionEntity.destroyScope(REASION_DELETE);
15         ProcessDefinitionImpl processDefinition = executionEntity.getProcessDefinition();
16         ActivityImpl activity = processDefinition.findActivity(activityId);
17         executionEntity.executeActivity(activity);
18         return executionEntity;
19     }
20 
21 }
View Code

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

技术分享
 1 /**
 2      * 跳转到指定节点,并隐藏处理信息
 3      * 
 4      * @param lastTaskID
 5      */
 6     public String Back(String lastTaskID, String runTtaskId, String back) {
 7         String msg = "";
 8         try {
 9             HistoricTaskInstance hisTask = historyService.createHistoricTaskInstanceQuery().taskId(lastTaskID).singleResult();
10             // 判断是否会签
11             boolean flag = true;
12             ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(hisTask.getProcessDefinitionId());
13             List<ActivityImpl> activitiList = processDefinitionEntity.getActivities();
14             for (ActivityImpl activityImpl : activitiList) {
15                 if (activityImpl.getId().equals(hisTask.getTaskDefinitionKey())) {// 找到对应任务
16                     Object sign = activityImpl.getProperties().get("multiInstance");// 获取会签标识
17                     if (sign != null) {// 会签
18                         // 会签暂时不可以退回
19                         System.out.println("会签");
20                         msg = "会签不可以退回,收回";
21                         flag = false;
22                         break;
23                     }
24                 }
25             }
26             // 不是会签
27             if (flag) {
28                 // if(back!=null && !back.equals("")){
29                 // // //完成当前任务,设置处理信息
30                 // HistoricTaskInstance runTask =
31                 // historyService.createHistoricTaskInstanceQuery().taskId(runTtaskId).singleResult();
32                 // systemService.findUniqueByProperty(TSBaseUser.class,
33                 // "userName", runTask.getAssignee()).getRealName();
34                 // Map variables = new HashMap();
35                 // variables.put("back",
36                 // systemService.findUniqueByProperty(TSBaseUser.class,
37                 // "userName",
38                 // runTask.getAssignee()).getRealName()+":退回到:"+systemService.findUniqueByProperty(TSBaseUser.class,
39                 // "userName", hisTask.getAssignee()).getRealName());
40                 // taskService.claim(runTask.getId(),
41                 // runTask.getAssignee());//领取任务
42                 // taskService.complete(runTask.getId(), variables);//完成当前任务
43                 // }
44                 // 跳转到上一个处理人
45                 taskRollback(lastTaskID);
46                 // 设置下一步处理人
47                 setAssigneeDynamic(hisTask.getProcessInstanceId(), hisTask.getAssignee());
48                 // 隐藏签字痕迹,opt=0
49                 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‘";
50                 int i = systemService.getSession().createSQLQuery(sql).executeUpdate();
51                 if (i != 1) {
52                     logger.error("流程退回/收回隐藏签字意见出现问题,如果没有填写意见或者签字请忽略,流程可能已经出错,请修改ACT_HI_DETAIL表,任务ID:" + lastTaskID);
53                 }
54                 // 修改业务中的下一步处理人
55                 ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(hisTask.getProcessInstanceId()).singleResult();
56 
57                 String id = instance.getBusinessKey();
58                 TSBaseBus base = systemService.getEntity(TSBaseBus.class, id);
59                 try {
60                     Method getCurrentSteps = base.getClass().getMethod("setCurrentSteps", String.class);
61                     Method getCurrentUsers = base.getClass().getMethod("setCurrentUsers", String.class);
62                     getCurrentSteps.invoke(base, hisTask.getTaskDefinitionKey());
63                     getCurrentUsers.invoke(base, hisTask.getAssignee());
64                     TSPrjstatus tSPrjstatus = systemService.get(TSPrjstatus.class, BaseDataConstants.WF_STATE_REBACK);// 修改状态为退回
65                     base.setTSPrjstatus(tSPrjstatus);
66                 } catch (Exception e) {
67                     logger.error("退回/收回时修改业务表中下一步处理人出错", e);
68                 }
69                 systemService.save(base);
70             }
71         } catch (Exception e) {
72             logger.error("流程退回/收回失败,设置下一步处理人出错,任务ID:" + lastTaskID, e);
73             msg = null;
74         }
75         return msg;
76     }
View Code

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

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

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

以上是关于Activiti工作流几种驳回方式的实现与比较的主要内容,如果未能解决你的问题,请参考以下文章

Activiti 5.22.0 之自由驳回任务实现(亲测)

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

Activiti实战04_简单流程

驰骋工作流引擎JFlow与activiti的对比之4种高级分支同步模式

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

Java使用Activiti创建数据库表的几种方式