撤回-撤销、驳回-退回、作废-终止、挂起-中止
流程撤回-撤销
1、流程定义
<process id="test2" isClosed="false" isExecutable="true" processType="None">
<startEvent id="_2" name="StartEvent"/>
<userTask activiti:assignee="sp1" activiti:exclusive="true" id="_3" name="审批1"/>
<userTask activiti:assignee="sp2" activiti:exclusive="true" id="_4" name="审批2"/>
<endEvent id="_5" name="EndEvent"/>
<sequenceFlow id="_6" sourceRef="_2" targetRef="_3"/>
<sequenceFlow id="_7" sourceRef="_3" targetRef="_4"/>
<sequenceFlow id="_8" sourceRef="_4" targetRef="_5"/>
</process>
2、部署流程,开启流程,完成第一个节点"审批1"审批
/**
* 部署流程,开启流程,完成第一个节点“审批1”审核
*/
@Test
public void shenpi1(){
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("bpmn/test2.bpmn").deploy();
log.info("流程部署成功:{}",deploy.getId());
//设置开启人
Authentication.setAuthenticatedUserId("sp1");
ProcessInstance instance = runtimeService.startProcessInstanceByKey("test2");
log.info("流程已开启:id:{},key:{},startUser:{}",instance.getId(),instance.getBusinessKey(),instance.getStartUserId());
String instanceId=instance.getId();
// String instanceId="2ac7e417-e2f5-11eb-9ffb-34f39ab07ea4";
//完成第一个节点审批
Task task = taskService.createTaskQuery()
.processInstanceId(instanceId)
.taskAssignee("sp1")
.singleResult();
taskService.complete(task.getId());
log.info("任务已完成:id:{}",task.getId());
}
/**
* 获取流程审批历史记录
*/
@Test
public void history(){
String instanceId="2ac7e417-e2f5-11eb-9ffb-34f39ab07ea4";
HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult();
String startUserId = instance.getStartUserId();
Date startTime = instance.getStartTime();
String startActivityId = instance.getStartActivityId();
Task startTask = taskService.createTaskQuery().taskId(startActivityId).singleResult();
List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
.processInstanceId(instanceId)
.orderByHistoricTaskInstanceStartTime()
.asc()
.list();
for(HistoricTaskInstance task:list){
List<Comment> comments = taskService.getTaskComments(task.getId());
for(Comment c:comments){
log.info("任务备注:id:{},userid:{},message:{}",c.getTaskId(),c.getUserId(),c.getFullMessage());
}
List<Attachment> attachments = taskService.getTaskAttachments(task.getId());
log.info("任务id:{},name:{},assignee:{}",task.getId(),task.getName(),task.getAssignee());
}
}
3、撤回案例
撤回实现逻辑:
- 根据业务逻辑判断,当前任务是否可以撤回;这里只是简单判断,当前任务是当前用户审批的,并且流程下一个节点任务人还未处理任务时,此时任务是可以撤回的。比如流程开启人
sp1
,开启任务后,提交审批“审批1”,流程走到“审批2”节点。审核人sp1
还未审批任务,此时sp1
想撤回刚才的提交,此时是可以的。如果“审批2”处理了审核,此时流程走过当前“审批2”节点了(对于这个流程案例是流程结束),sp1
就不能撤回之前的提交了。 - 如何实现撤回功能?当前流程审核节点在“审批2”节点,可以手动修改“审批2”节点的输出流,既把“审批2”-“EndEvent”输出流删除,添加1条新输出流 “审批2”-“审批1”,这样“审批2”节点任务complete 流程自动就走到“审批1”了;
- “审批2”提交任务后,由于是撤回操作,流程到达“审批1”,历史任务记录中存在“审批2”提交到“审批1”历史记录。但对于业务上来说,可能这个历史记录不是必须要存在的(审批备注里面已经添加“撤回”备注信息),此时就可以把这个历史任务记录删除;当然如果需求要求也可以不删除。
- “审批2”提交任务后,流程走到“审批1”节点后,此时需要恢复原来的流程走向“审批2”-“EndEvent”输出流;
- 对于流程实现撤回功能,实现方式有很多种,比如也可以修改历史记录信息,同时在历史任务表添加新的任务记录等操作都是可以实现撤回功能。这里只是以修改输出流的方式作为说明,也是工作中用的比较多的一种实现方式。
/**
* 流程撤回:
* 当前用户审批通过后(流程到下一节点后),又想撤回时。撤回到上一节点;
*/
@Test
public void proBack(){
//1、判断是否可撤回? 上一个节点是当前用户审核的,并且下一节点审批人还未处理时,可撤回到上一节点;
//2、如何撤回?动态修改BpmnModel当前流程节点输出流。
//把当前流程节点输出流方向改成上一个节点,原有输出流删除;
//审批通过后,再次恢复当前节点的原有输出流;
//根据业务需求,判断是否删除上次审批的历史记录,不需删除时,不用处理;要求删除时,需删除撤回前的历史记录;
String instanceId="2ac7e417-e2f5-11eb-9ffb-34f39ab07ea4";
String canBackTaskId="2acc29dc-e2f5-11eb-9ffb-34f39ab07ea4";
String userId="sp1";
Tuple result = canBack(instanceId, canBackTaskId, userId);
boolean canBack=result.get(0);
HistoricTaskInstance currentTask=result.get(1);
if(!canBack){
log.debug("流程:{}-任务:{},已经处理,不可撤回",instanceId,canBackTaskId);
return;
}
//流程撤回
HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());
HistoricTaskInstance canBackTask = historyService.createHistoricTaskInstanceQuery().taskId(canBackTaskId).taskAssignee(userId).singleResult();
if(ObjectUtil.isEmpty(canBack)){
log.debug("未找到要回退的任务");
return;
}
FlowNode canBackNode = (FlowNode)bpmnModel.getFlowElement(canBackTask.getTaskDefinitionKey());
FlowNode currentNode=(FlowNode)bpmnModel.getFlowElement(currentTask.getTaskDefinitionKey());
//记录原始输出流程
List<SequenceFlow> sourceOuts =new ArrayList<>();
sourceOuts.addAll(currentNode.getOutgoingFlows());
//清理活动方向
currentNode.getOutgoingFlows().clear();
//创建新流
List<SequenceFlow> newFlows=new ArrayList<>();
SequenceFlow newSequenceFlow = new SequenceFlow();
newSequenceFlow.setId("newSequenceFlowId");
newSequenceFlow.setSourceFlowElement(currentNode);
newSequenceFlow.setTargetFlowElement(canBackNode);
newFlows.add(newSequenceFlow);
currentNode.setOutgoingFlows(newFlows);
//记录备注信息
Authentication.setAuthenticatedUserId(userId);
taskService.addComment(currentTask.getId(), currentTask.getProcessInstanceId(), "撤回");
//完成任务
taskService.complete(currentTask.getId());
//恢复原方向
currentNode.setOutgoingFlows(sourceOuts);
//删除当前任务历史记录
historyService.deleteHistoricTaskInstance(currentTask.getId());
}
/**
* 判断流程是否能够撤回
* 上一个节点是当前用户审核的,并且下一节点审批人还未处理时,可撤回到上一节点;
* @return
*/
// @Test
public Tuple canBack(String instanceId,String canBackTaskId,String userId){
// String instanceId="2ac7e417-e2f5-11eb-9ffb-34f39ab07ea4";
// String canBackTaskId="2acc29dc-e2f5-11eb-9ffb-34f39ab07ea4";
// String userId="sp1";
boolean canBack=false;
//查看流程历史节点
List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
.processInstanceId(instanceId)
.orderByHistoricTaskInstanceStartTime()
.asc()
.list();
boolean hasUserTask=false;
HistoricTaskInstance currentTask=null;
for(HistoricTaskInstance task:list) {
if(task.getId().equalsIgnoreCase(canBackTaskId)
&&task.getAssignee().equalsIgnoreCase(userId)){
//找到了处理人处理的任务,查看下一个任务是否已经处理
hasUserTask=true;
continue;
}
if(hasUserTask){
//上一个任务是当前人处理的,查看当前任务是否已经被处理
hasUserTask=false;
if(ObjectUtil.isEmpty(task.getEndTime())){
canBack=true;
currentTask=task;
break;
}
}
}
if(canBack){
log.debug("未处理的流程可撤回,任务ID:{},任务接收人:{}",currentTask.getId(),currentTask.getAssignee());
}
//方法返回多个值,是否可回退,当前任务
return new Tuple(canBack,currentTask);
}
流程驳回-退回-退回到历史节点的任何节点
流程驳回,和流程撤回功能实现逻辑基本相同。
撤回功能是上一个节点审批人发起撤回;而驳回功能是当前审批节点人审核处理的,都可以通过修改当前审批节点的输出流进行处理,一般驳回功能需要记录历史记录,不会删除当前任务的历史记录。
具体驳回实现代码,这里不再记录,可借助撤回功能完成。
通过修改节点输出流向的操作,会发现,其实退回可以指定到任何节点上。
比如审批页面列出所有审核历史节点,可以选择历史任务节点,退回到选择的历史节点重新审批操作等功能。
流程挂起、作废
/**
* 流程-作废(终止)、挂起(中止)
*/
@Test
public void deleteProcess(){
String instanceId="2ac7e417-e2f5-11eb-9ffb-34f39ab07ea4";
//流程挂起、激活
runtimeService.suspendProcessInstanceById(instanceId);
runtimeService.activateProcessInstanceById(instanceId);
//作废-删除流程,删除流程历史记录
runtimeService.deleteProcessInstance(instanceId,"删除流程实例");
historyService.deleteHistoricProcessInstance(instanceId);
}