需求:
针对企业的多步审批,当前节点的审批人对上个节点的审批有意见或者当前接受审批任务的人觉得不应该由自己来审批,那么就需要回退到上个节点的审批,由上一步的人重新办理。
解决方案是:在流程定义中为具有回退功能的任务活动设置专门用于处理回退逻辑的监听器,然后需要回退时 动态的创建一条转移路径,只想退回到目的地。
1)回退的实例流程定义:
<process name="销售订单审批回退流程" key="销售订单审批回退流程_10002">
<start ID="1" name="开始" type="start" x="123" y="123" Radio="20">
<transition ID="5" name="流程线" type="transition" FromElementID="1" ToElementID="3" to="小组审批">
<event-listener class="com.zhg.bpm.operation.withdraw.WithdrawListener"/>
</transition>
</start>
<end ID="2" name="结束" type="end" x="689" y="132" Radio="20"/>
<task ID="3" name="小组审批" type="NodeAudit" x="276" y="118" Width="100" Height="30" candidate-groups="RO2010091500000001">
<on event="end">
<event-listener class="com.zg.bpm.operation.message.AfterMessageListener"/>
</on>
<on event="start">
<event-listener class="com.zg.bpm.operation.message.BeforeMessageListener"/>
</on>
<transition ID="6" name="流程线" type="transition" FromElementID="3" ToElementID="4" to="部门审批">
<event-listener class="com.zhg.bpm.operation.withdraw.WithdrawListener"/>
</transition>
</task>
<task ID="4" name="部门审批" type="NodeAudit" x="450" y="142" Width="100" Height="30" candidate-groups="RO2010091500000001">
<on event="end">
<event-listener class="com.zg.bpm.operation.message.AfterMessageListener"/>
</on>
<on event="start">
<event-listener class="com.zg.bpm.operation.message.BeforeMessageListener"/>
</on>
<transition ID="7" name="结束" type="transition" FromElementID="4" ToElementID="2" to="结束">
<event-listener class="com.zhg.bpm.operation.withdraw.WithdrawListener"/>
</transition>
</task>
</process>
流程图如下:
WithdrawListener就是回退的监听器
2)事件监听器WithdrawListener定义
public class WithdrawListener implements EventListener {
public void notify(EventListenerExecution execution) throws Exception {
TransitionImpl transition=((ExecutionImpl)execution).getTransition();
ActivityImpl destination=transition.getDestination();
ActivityImpl source=transition.getSource();
//如果目的转向与源转向都不为空,则在该任务的转向中建立一条可回退的路径
//转向名称为"目的转向名称to源转向名称"
if(destination!=null&&source!=null){
TransitionImpl newTran=destination.createOutgoingTransition();
newTran.setName(destination.getName()+" to "+source.getName());
newTran.setDestination(source);
}
}
}
3)最后是回退的API了,调用回退服务,删除历史审批信息就行了
直接调用command就行了,比如processEngine.execute(new WithdrawTaskCommand(processId, taskName))
package com.zhg.bpm.command;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.Session;
import org.jbpm.api.Execution;
import org.jbpm.api.ExecutionService;
import org.jbpm.api.HistoryService;
import org.jbpm.api.TaskService;
import org.jbpm.api.cmd.Command;
import org.jbpm.api.cmd.Environment;
import org.jbpm.api.task.Task;
import org.jbpm.pvm.internal.history.model.HistoryActivityInstanceImpl;
/**
*撤消,取回命令,应该在我的历史任务中激活
*
*/
public class WithdrawTaskCommand implements Command {
private static Logger logger = LoggerFactory.getLogger(WithdrawTaskCommand.class);
private String pid;
private String withdrawToActName;
public WithdrawTaskCommand(String pid,String withdrawToActName){
this.pid=pid;
this.withdrawToActName=withdrawToActName;
}
public void setPid(String pid) {
this.pid = pid;
}
public String getPid() {
return pid;
}
public void setWithdrawToActName(String withdrawToActName) {
this.withdrawToActName = withdrawToActName;
}
public String getWithdrawToActName() {
return withdrawToActName;
}
public String execute(Environment environment) throws Exception{
//从流程引擎环境中获取执行,任务,历史3种服务
ExecutionService executionService=environment.get(ExecutionService.class);
TaskService taskService=environment.get(TaskService.class);
HistoryService historyService=environment.get(HistoryService.class);
//这里比较特殊,由于JBPM4没有清除历史数据的服务API直接提供,所以这里要获取的是
//Hibernate Session对象
Session session=environment.get(Session.class);
Execution exec=executionService.findExecutionById(pid);
//获取当前活动名称集合
Set<String> actNames=exec.findActiveActivityNames();
if(actNames==null||actNames.size()==0){
String msg="没有可以取回的活动";
logger.error(msg);
throw new Exception(msg);
}
if(actNames.size()>1){
String msg="存在多个活动的节点 ";
logger.error(msg);
throw new Exception(msg);
}
String actName=actNames.iterator().next();
String withdrawPath=actName+" to "+withdrawToActName;
//获取当前活动的任务
List<Task> tasks=taskService.createTaskQuery().processInstanceId(pid)
.activityName(actName).list();
for(Task task:tasks){
/*
* 结束任务(即取回任务).如果completeTask Api执行失败,很可能是因
* 为withdrawPath不存在,这说明当前活动已非取回目的地活动的下一步了
* 实际应用中可以据此规则做一步的异常 处理
*/
taskService.completeTask(task.getId(), withdrawPath);
}
//这里清除当前活动的历史痕迹,即删除历史活动实例及其历史任务
HistoryActivityInstanceImpl hActInst=(HistoryActivityInstanceImpl)historyService
.createHistoryActivityInstanceQuery()
.activityName(actName)
.executionId(pid)
.uniqueResult();
//直接数据库物理删除
session.delete(hActInst);
return "SUCCESS";
}
}