Camunda如何实现驳回
前言
驳回是中国特色流程的一种方式,驳回在流程图上不应该有具体的流程线,它应该是隐性的,是审批人对自己待办任务的一种操作。而很多教程都是在图上设置连线【如下图】,通过满足连线上的条件来达到驳回的目的,这种实现方式简单一点的流程图还能凑合看,你要是10多个节点还怎么看图啊,严重影响体验。
实现
首先画图:
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_1nbaklk" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.11.1" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.15.0">
<bpmn:process id="Process_0x5jngk" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_1u7hc1h</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_1u7hc1h" sourceRef="StartEvent_1" targetRef="Activity_00r9h9d" />
<bpmn:sequenceFlow id="Flow_0dw67qi" sourceRef="Activity_00r9h9d" targetRef="Gateway_0sorneq" />
<bpmn:parallelGateway id="Gateway_0sorneq">
<bpmn:incoming>Flow_0dw67qi</bpmn:incoming>
<bpmn:outgoing>Flow_1wtbf44</bpmn:outgoing>
<bpmn:outgoing>Flow_04z5j5q</bpmn:outgoing>
</bpmn:parallelGateway>
<bpmn:sequenceFlow id="Flow_1wtbf44" sourceRef="Gateway_0sorneq" targetRef="Activity_1meifsg" />
<bpmn:sequenceFlow id="Flow_04z5j5q" sourceRef="Gateway_0sorneq" targetRef="Activity_03fsxgp" />
<bpmn:sequenceFlow id="Flow_0gtq51l" sourceRef="Activity_1meifsg" targetRef="Gateway_198yb5u" />
<bpmn:parallelGateway id="Gateway_198yb5u">
<bpmn:incoming>Flow_0gtq51l</bpmn:incoming>
<bpmn:incoming>Flow_0txm7u9</bpmn:incoming>
<bpmn:outgoing>Flow_19bdxcr</bpmn:outgoing>
</bpmn:parallelGateway>
<bpmn:sequenceFlow id="Flow_0txm7u9" sourceRef="Activity_03fsxgp" targetRef="Gateway_198yb5u" />
<bpmn:sequenceFlow id="Flow_19bdxcr" sourceRef="Gateway_198yb5u" targetRef="Activity_0i7w7vz" />
<bpmn:endEvent id="Event_15bc0no">
<bpmn:incoming>Flow_0m63kcm</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0m63kcm" sourceRef="Activity_0i7w7vz" targetRef="Event_15bc0no" />
<bpmn:userTask id="Activity_0i7w7vz" name="主管" camunda:assignee="${director}">
<bpmn:incoming>Flow_19bdxcr</bpmn:incoming>
<bpmn:outgoing>Flow_0m63kcm</bpmn:outgoing>
<bpmn:multiInstanceLoopCharacteristics>
<bpmn:loopCardinality xsi:type="bpmn:tFormalExpression">1</bpmn:loopCardinality>
</bpmn:multiInstanceLoopCharacteristics>
</bpmn:userTask>
<bpmn:userTask id="Activity_03fsxgp" name="经理2" camunda:assignee="${managerTwo}">
<bpmn:incoming>Flow_04z5j5q</bpmn:incoming>
<bpmn:outgoing>Flow_0txm7u9</bpmn:outgoing>
<bpmn:multiInstanceLoopCharacteristics>
<bpmn:loopCardinality xsi:type="bpmn:tFormalExpression">1</bpmn:loopCardinality>
</bpmn:multiInstanceLoopCharacteristics>
</bpmn:userTask>
<bpmn:userTask id="Activity_1meifsg" name="经理1" camunda:assignee="${managerOne}">
<bpmn:incoming>Flow_1wtbf44</bpmn:incoming>
<bpmn:outgoing>Flow_0gtq51l</bpmn:outgoing>
<bpmn:multiInstanceLoopCharacteristics>
<bpmn:loopCardinality xsi:type="bpmn:tFormalExpression">1</bpmn:loopCardinality>
</bpmn:multiInstanceLoopCharacteristics>
</bpmn:userTask>
<bpmn:userTask id="Activity_00r9h9d" name="发起人" camunda:assignee="${initiator}">
<bpmn:incoming>Flow_1u7hc1h</bpmn:incoming>
<bpmn:outgoing>Flow_0dw67qi</bpmn:outgoing>
</bpmn:userTask>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_0x5jngk">
<bpmndi:BPMNEdge id="Flow_1u7hc1h_di" bpmnElement="Flow_1u7hc1h">
<di:waypoint x="215" y="177" />
<di:waypoint x="270" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0dw67qi_di" bpmnElement="Flow_0dw67qi">
<di:waypoint x="370" y="177" />
<di:waypoint x="425" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1wtbf44_di" bpmnElement="Flow_1wtbf44">
<di:waypoint x="450" y="152" />
<di:waypoint x="450" y="80" />
<di:waypoint x="540" y="80" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_04z5j5q_di" bpmnElement="Flow_04z5j5q">
<di:waypoint x="450" y="202" />
<di:waypoint x="450" y="250" />
<di:waypoint x="540" y="250" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0gtq51l_di" bpmnElement="Flow_0gtq51l">
<di:waypoint x="640" y="80" />
<di:waypoint x="780" y="80" />
<di:waypoint x="780" y="145" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0txm7u9_di" bpmnElement="Flow_0txm7u9">
<di:waypoint x="640" y="250" />
<di:waypoint x="780" y="250" />
<di:waypoint x="780" y="195" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_19bdxcr_di" bpmnElement="Flow_19bdxcr">
<di:waypoint x="805" y="170" />
<di:waypoint x="920" y="170" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0m63kcm_di" bpmnElement="Flow_0m63kcm">
<di:waypoint x="1020" y="170" />
<di:waypoint x="1142" y="170" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_0byc3vw_di" bpmnElement="Gateway_0sorneq">
<dc:Bounds x="425" y="152" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_0xw7miy_di" bpmnElement="Gateway_198yb5u">
<dc:Bounds x="755" y="145" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_15bc0no_di" bpmnElement="Event_15bc0no">
<dc:Bounds x="1142" y="152" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1waybo4_di" bpmnElement="Activity_0i7w7vz">
<dc:Bounds x="920" y="130" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0lg8dwo_di" bpmnElement="Activity_03fsxgp">
<dc:Bounds x="540" y="210" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0ox02bd_di" bpmnElement="Activity_1meifsg">
<dc:Bounds x="540" y="40" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0jch17a_di" bpmnElement="Activity_00r9h9d">
<dc:Bounds x="270" y="137" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
设计一个发起人节点,驳回后发起人可以修改数据然后再提交,这里的再提交其实就是发起人审批完成待办任务。
我把代码一次性放出来,方便大家复制:
//部署流程定义
@GetMapping("/deployBpmn")
public void deployBpmn(){
Deployment deployment = repositoryService.createDeployment().name("驳回demo").addClasspathResource("one.bpmn").deploy();
System.out.println(deployment.getId());
}
//发起流程实例
@PostMapping("/startProcessInstance")
public String startProcessInstance(@RequestParam("processDefineKey") String processDefineKey,
@RequestParam("businessKey") String businessKey,
@RequestParam("processInitiator") String processInitiator){
Map<String, Object> map = new HashMap<>(4);
map.put("initiator", processInitiator);
map.put("managerOne","10086");
map.put("managerTwo","10087");
map.put("director","10088");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefineKey, businessKey, map);
return processInstance.getProcessInstanceId();
}
//审批同意,完成待办
@GetMapping("/agreeTask")
public void agreeTask(@RequestParam("taskId")String taskId){
taskService.complete(taskId);
}
当流程实例跑到并行节点时,如下图:
此时根据需求的不同会有两种情况,第一种:经理1驳回时希望不会影响经理2,发起人再发起后只会给经理1派发待办任务,不会重复给经理2派发任务。
@GetMapping("/rejectTask")
public boolean rejectTask(@RequestParam("processInstanceId")String processInstanceId,
@RequestParam("targetNodeId")String targetNodeId,
@RequestParam("taskId")String taskId){
//获取任务对应环节id
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
String currentActivityId = task.getTaskDefinitionKey();
//获取当前环节实例 这是个树结构
ActivityInstance activity = runtimeService.getActivityInstance(processInstanceId);
ActivityInstance[] childActivityInstances = activity.getChildActivityInstances();
String activityInstanceId = "";
for (ActivityInstance childActivityInstance : childActivityInstances) {
String activityId = childActivityInstance.getId();
if (activityId.contains(currentActivityId)){
activityInstanceId = activityId;
break;
}
}
runtimeService.createProcessInstanceModification(processInstanceId)
.cancelActivityInstance(activityInstanceId)
.setAnnotation("驳回")
.startBeforeActivity(targetNodeId)
.execute();
return true;
}
但是实际情况是,还会给经理2再次派发任务
所以建议使用监听器去设置审批人,这个我们在之前的【深入探究Camunda监听器】文章中已经说过了,不赘述,给出关键代码,主要的意思就是:原来经理2节点上已经有10087这个审批人了,这次就移除掉。这样驳回后再次到这个节点就不会给这个审批人派发任务了。注意:taskService查出来的是运行中的任务
//查出节点上所有的任务
List<Task> taskList = taskService.createTaskQuery()
.processInstanceId(processInstanceId)
.taskDefinitionKey(nodeId).list();
//assigneeList就是该节点需要设置的审批人,如果节点的审批人已经有a了就移除掉
if (CollUtil.isNotEmpty(taskList)){
List<String> existAssignees = taskList.stream().map(Task::getAssignee).collect(Collectors.toList());
assigneeList.removeIf(existAssignees::contains);
}
execution.setVariable("assigneeList",assigneeList);
第二种:经理1和经理2只要有驳回,就取消他们的待办任务
@GetMapping("/rejectTask")
public boolean rejectTask(@RequestParam("processInstanceId")String processInstanceId,
@RequestParam("targetNodeId")String targetNodeId,
@RequestParam("taskId")String taskId){
//获取任务对应环节id
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
String currentActivityId = task.getTaskDefinitionKey();
//获取当前环节实例
ActivityInstance activity = runtimeService.getActivityInstance(processInstanceId);
// ActivityInstance[] childActivityInstances = activity.getChildActivityInstances();
// String activityInstanceId = "";
// for (ActivityInstance childActivityInstance : childActivityInstances) {
// String activityId = childActivityInstance.getId();
// if (activityId.contains(currentActivityId)){
// activityInstanceId = activityId;
// break;
// }
// }
runtimeService.createProcessInstanceModification(processInstanceId)
.cancelActivityInstance(activity.getId())
.setAnnotation("驳回")
.startBeforeActivity(targetNodeId)
.execute();
return true;
}
这种情况即便发起人再次发起也不会产生冗余的待办任务,但是经理2都已经审批同意了,此时驳回后再到这个节点又得重新审批,虽然我觉得这种逻辑是不正确的,但是每个项目的需求是不一样的,都示例一下吧。
驳回后经理2的已办也没了
这样我们解决了并行中节点的驳回,如果从【主管】节点驳回,可能需要控制一下驳回的节点,如果只驳回到【经理1】或者【经理2】其中一个节点,然后审批通过,就会造成以下尴尬场景,流程会卡在并行网关那里。
以上的并行的情况,如果是排他网关,甚至没有网关的情况下就更简单了:
ActivityInstance activity = runtimeService.getActivityInstance(processInstanceId);
runtimeService.createProcessInstanceModification(processInstanceId)
.cancelActivityInstance(activity.getId())
.setAnnotation("驳回")
.startBeforeActivity(targetNodeId)
.execute();
以上都是没有监听器,或者监听器在节点上所做的驳回处理,如果是在连线上的执行监听器,就需要找到驳回目标节点之前的连线
//获取实例的流程定义 这里主要是为了拿到节点前的那条线的Id
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
BpmnModelInstance bpmnModel = repositoryService.getBpmnModelInstance(processInstance.getProcessDefinitionId());
ModelElementInstance modelElement = bpmnModel.getModelElementById(nodeId);
UserTask userTask = (UserTask) modelElement;
Collection<SequenceFlow> incoming = userTask.getIncoming();
String transitionId = "";
if (incoming.stream().findFirst().isPresent()) {
transitionId = incoming.stream().findFirst().get().getId();
} else {
throw new 自定义异常;
}
//获取当前环节实例
ActivityInstance activity = runtimeService.getActivityInstance(processInstanceId);
runtimeService.createProcessInstanceModification(processInstanceId)
.cancelActivityInstance(activity.getId())
.setAnnotation(annotation)
.startTransition(transitionId)
.execute();
有问题先查阅专栏文章 或评论区留言 解决不了可私信我
- 点赞
- 收藏
- 关注作者
评论(0)