项目地址:activiti-workflow
在通用审批流中都会有驳回的功能,activiti提供的接口并没有驳回。本文通过对activiti提供接口的扩展实现流程驳回功能,主要代码如下
String processInstanceId = taskCurrent.getProcessInstanceId();
FlowElement targetFlowElement = null;
if (StringUtil.isNotEmpty(processRejectParam.getTargetNodeId())) {
//找到目标节点元素
targetFlowElement = bpmnModel.getMainProcess().getFlowElement(processRejectParam.getTargetNodeId());
} else {
//开始节点的下一个节点
targetFlowElement = BpmnUtil.startEventNextTaskId(bpmnModel);
}
//当前待审批节点定义Id集合
List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
if (CollectionUtil.isNotEmpty(taskList)) {
BpmnModel newBpmnModel = bpmnModel;
Map<String, List<SequenceFlow>> stringListMap = BpmnUtil.invokeSequenceFlows(newBpmnModel, taskList, targetFlowElement);
for (Task task : taskList) {
//记录原活动方向
List<SequenceFlow> oriSequenceFlows = new ArrayList<>();
//当前节点
oriSequenceFlows.addAll(stringListMap.get(task.getTaskDefinitionKey()));
FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());
try {
Map<String, Object> variables = new HashMap<>();
//当前操作节点
if(task.getId().equals(taskCurrent.getId())){
//设置当前审批人为提交人
taskService.setAssignee(task.getId(), userId);
// 保存任务评价
if (StringUtil.isNotEmpty(rejectComment)) {
taskService.addComment(task.getId(), task.getProcessInstanceId(), rejectComment);
}
//设置节点状态
taskService.setVariablesLocal(task.getId(), variables);
//完成
taskService.complete(task.getId());
}else{
//完成
taskService.complete(task.getId());
//删除任务
historyService.deleteHistoricTaskInstance(task.getId());
}
} catch (Exception e) {
e.printStackTrace();
throw new ProcessException("流程撤回异常,异常原因:" + e.getMessage());
} finally {
//恢复原方向
currentFlowNode.setOutgoingFlows(oriSequenceFlows);
}
}
}
invokeSequenceFlows方法,具体代码在项目中查看。
/**
* 处理撤回连线 可能存在分支
* @param bpmnModel
* @param taskList
* @param targetFlowElement
* @return
*/
public static Map<String,List<SequenceFlow>> invokeSequenceFlows(BpmnModel bpmnModel , List<Task> taskList, FlowElement targetFlowElement) {
Map<String,List<SequenceFlow>> flowElements = new HashMap<>(2);
//并行网关
ParallelGateway parallelGateway = new ParallelGateway();
parallelGateway.setId("parallelGateway" + targetFlowElement.getId());
parallelGateway.setBehavior(new ParallelGatewayActivityBehavior());
List<SequenceFlow> parallelSequenceFlowInCome = new ArrayList<>();
for (Task task : taskList) {
//当前节点
FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());
flowElements.put(currentFlowNode.getId(),currentFlowNode.getOutgoingFlows());
//重新绘制流程图,从当前节点到到并行网关
List<SequenceFlow> parallelSequenceFlowList = new ArrayList<>();
SequenceFlow parallelSequenceFlow = new SequenceFlow();
parallelSequenceFlow.setId("newSequenceFlowId" + System.currentTimeMillis());
parallelSequenceFlow.setSourceFlowElement(currentFlowNode);
parallelSequenceFlow.setTargetFlowElement(parallelGateway);
parallelSequenceFlowList.add(parallelSequenceFlow);
parallelSequenceFlowInCome.add(parallelSequenceFlow);
currentFlowNode.setOutgoingFlows(parallelSequenceFlowList);
}
//重新绘制流程图,从并行网关到开始节点
List<SequenceFlow> newSequenceFlowList = new ArrayList<>();
//绘制连线,加入流程信息,并组装到流程图
SequenceFlow newSequenceFlow = new SequenceFlow();
newSequenceFlow.setId("newSequenceFlowId" + targetFlowElement.getId());
newSequenceFlow.setSourceFlowElement(parallelGateway);
newSequenceFlow.setTargetFlowElement(targetFlowElement);
newSequenceFlowList.add(newSequenceFlow);
parallelGateway.setIncomingFlows(parallelSequenceFlowInCome);
parallelGateway.setOutgoingFlows(newSequenceFlowList);
return flowElements;
}
整理一些思路,驳回可选择传入目标节点的定义ID,如果没有就默认选择开始节点的下一个节点。如果当前存在多个审批任务此次执行的任务完成不做其他操作,其他任务完成后删除(不留下审批痕迹)。
invokeSequenceFlows主要处理的当前审批可能存在多个任务,处理方式为,在当前节点前面任务增加一个并行网关,将当前审批节点连线到网关,网关连线到目标节点。这样处理,不管当前有多少审批任务,由于并行网关是等待前面所有节点全部之下完成才会继续后续节点,保证当前审批任务全执行完成。
大概过程是这样
该方法也使用于撤回(发起人操作),可能存在问题,还需大量测试。