最近公司做的一个项目要实现工作流程的收回,驳回等操作,而采用的工作流引擎并不支持驳回功能,这个项目恰好就我和一个实习生一块做,所以这个问题就落到我的头上来解决了。。。
客户提出的要求是驳回时要记录日志,但是审批意见之类的需要清除掉,收回时不记录日志,审批意见同样清除。
百度了一下,总结了5个解决方案:
第一种是清除当前任务的出口,实现回到上一步的功能。
根据任务ID获取当前的任务节点,进而获取流程实例,然后取到流程的定义,找到当前活动的节点与上一步活动的节点,然后清除当前活动的出口,将上一步活动的出口作为最新的活动节点。
第二种是直接将当前的activiti引擎更换为第三方的支持退会驳回的引擎。
第三种是自己编译activiti引擎,加入退回,驳回功能。
第四种是直接操作数据库,使用历史任务替换正在进行的任务。
第五种是直接实现Command接口,实现工作流程可以任意跳转。
因为时间紧急所以直接将3.编译activiti引擎给pass掉了,本来项目是接手的离职员工的项目,更换流程引擎可能会引起其他的问题,此计划暂放。
最后在1、4、5之间决定了采用第一种,代码如下
1 public String rollBackWorkFlow(String taskId) { 2 try { 3 Mapvariables; 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 nextTransitionList = currActivity 34 .getIncomingTransitions(); 35 // 清除当前活动的出口 36 List oriPvmTransitionList = new ArrayList (); 37 //新建一个节点连线关系集合 38 39 List pvmTransitionList = currActivity 40 .getOutgoingTransitions(); 41 // 42 for (PvmTransition pvmTransition : pvmTransitionList) { 43 oriPvmTransitionList.add(pvmTransition); 44 } 45 pvmTransitionList.clear(); 46 47 // 建立新出口 48 List newTransitions = new ArrayList (); 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 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 }
原本一切按计划进行着,但不知为什么在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 }
重写的Command接口
1 public class JumpCmd implements Command{ 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 }
最后手动清除流程细节信息
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 ListactivitiList = 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 }
中间一边写一边调试,终于可以实现驳回功能了。但是这样在实现方式上可能会出问题,因为自己操作数据代码,与activiti操作数据的代码,不在一个事物中,很可能出现流程退回去了,签字信息没有被擦除掉。
以后再使用工作流时尽量根据需求选用合适的流程引擎。
(在具体实现上参考了许多人的代码,思想)