番禺网站制作价格/江苏网站seo
1. 工作流
简单地来讲,工作流就是在计算机的协助下实现流程的自动化控制。目前,笔者熟知的主流的框架有:Camunda 、Flowable 、Activiti 、jBPM、还有我们国产的盘古BPM、云程。其中,Camunda 、Flowable 都有商业版(企业版)和非商业版(社区开源版)。
技术产品各有千秋,Flowable专注于流程引擎,Activiti现在专注于Cloud。笔者最推荐Camunda,盘古BPM还没用过看起来应该也挺好用的。
关于Camunda补充几篇文章
- Camunda开源版与商业版的差异
- idea开发工作流使用camunda-model进行activiti7的开发
- 基于camunda如何实现会签:camunda会签流程配置与原理解析
2. 流程设计器
笔者亲测,IntelliJ IDEA 2021.1 (Ultimate Edition) 不支持 actiBPM插件。
强烈推荐用 camunda-modeler ,或者用 bpmn-js
首先,下载Camunda
Download Camunda Platform 7 Community Edition
解压以后,直接双击.exe文件运行
也可以在IDEA中把它作为外部工具用
笔者更习惯直接双击.exe打开
Activiti为Eclipse开发了一个BPM插件“Activiti Eclipse Designer”
Activiti User Guide
为了使用Activiti Designer,笔者又下载了Eclipse IDE,专门为了Activiti开发
3. Activiti7 快速开始
工作流的作用是实现流程的自动化控制。使用Activiti这种工作流框架大致都分为以下几个步骤:
- 流程定义
- 部署流程定义
- 启动流程实例
- 查询代表任务
- 完成任务
- 结束流程
术语补充:
- BPM :业务流程管理
- BPMN :业务流程模型和符号
首先,来引入依赖
1 <dependency> 2 <groupId>org.activiti</groupId> 3 <artifactId>activiti-spring-boot-starter</artifactId> 4 <version>7.1.0.M6</version> 5 </dependency>
学习Activiti主要是学习这些Service的使用
3.1. 创建ProcessEngine
1 package com.cjs.example.activiti;2 3 import org.activiti.engine.ProcessEngine;4 import org.activiti.engine.ProcessEngineConfiguration;5 import org.activiti.engine.ProcessEngines;6 import org.junit.jupiter.api.Test;7 8 /**9 * @Author 10 * @Date 2021/7/6 11 */ 12 public class ProcessEngineTests { 13 14 @Test 15 public void testProcessEngine1() { 16 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); 17 System.out.println(processEngine); 18 } 19 20 @Test 21 public void testProcessEngine2() { 22 ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml"); 23 ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine(); 24 System.out.println(processEngine); 25 } 26 27 }
ProcessEngineConfiguration是用来创建ProcessEngine,默认情况下,会读取classpath下的activiti.cfg.xml文件,当然也可以不叫这个名字
这里,由于还没有与Spring Boot整合,也不是一个Web环境,所以,姑且先建一个这样的文件吧,真正开发的时候肯定不是这样做的
1 <?xml version="1.0" encoding="UTF-8"?>2 <beans xmlns="http://www.springframework.org/schema/beans"3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">5 6 <bean id="processEngineConfiguration" name="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">7 <property name="jdbcDriver" value="com.mysql.jdbc.Driver"/>8 <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=utf8"/>9 <property name="jdbcUsername" value="root"/> 10 <property name="jdbcPassword" value="123456"/> 11 <property name="databaseSchemaUpdate" value="true"/> 12 </bean> 13 </beans>
只要ProcessEngine被成功创建,就会生成25张表
这里我们可以看出,Activiti最本质最核心的东西就是将流程定义转换成表记录,表面上看好像是一个图片,其实它是一个xml文件,通过解析xml文件,进而将其转成表数据,后续从表中读数据就可以了。
3.2. 流程定义
用 Camunda Modeler 或者 Activiti Eclipse Designer 画好流程图
1 <?xml version="1.0" encoding="UTF-8"?>2 <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4 xmlns:xsd="http://www.w3.org/2001/XMLSchema"5 xmlns:activiti="http://activiti.org/bpmn"6 xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"7 xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"8 xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"9 typeLanguage="http://www.w3.org/2001/XMLSchema" 10 expressionLanguage="http://www.w3.org/1999/XPath" 11 targetNamespace="http://www.activiti.org/test"> 12 13 <process id="holiday" name="holiday" isExecutable="true"> 14 <startEvent id="startevent1" name="Start"></startEvent> 15 <userTask id="usertask1" name="填写请假单" activiti:assignee="${assignee1}"></userTask> 16 <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow> 17 <userTask id="usertask2" name="部门经理审批" activiti:assignee="${assignee2}"></userTask> 18 <sequenceFlow id="flow2" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow> 19 <userTask id="usertask3" name="人事审批" activiti:candidateUsers="tom,jerry"></userTask> 20 <sequenceFlow id="flow3" sourceRef="usertask2" targetRef="usertask3"></sequenceFlow> 21 <endEvent id="endevent1" name="End"></endEvent> 22 <sequenceFlow id="flow4" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow> 23 </process> 24 25 <bpmndi:BPMNDiagram id="BPMNDiagram_holiday"> 26 <bpmndi:BPMNPlane bpmnElement="holiday" id="BPMNPlane_holiday"> 27 <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1"> 28 <omgdc:Bounds height="35.0" width="35.0" x="130.0" y="220.0"></omgdc:Bounds> 29 </bpmndi:BPMNShape> 30 <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1"> 31 <omgdc:Bounds height="55.0" width="105.0" x="210.0" y="210.0"></omgdc:Bounds> 32 </bpmndi:BPMNShape> 33 <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2"> 34 <omgdc:Bounds height="55.0" width="105.0" x="360.0" y="210.0"></omgdc:Bounds> 35 </bpmndi:BPMNShape> 36 <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3"> 37 <omgdc:Bounds height="55.0" width="105.0" x="510.0" y="210.0"></omgdc:Bounds> 38 </bpmndi:BPMNShape> 39 <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1"> 40 <omgdc:Bounds height="35.0" width="35.0" x="660.0" y="220.0"></omgdc:Bounds> 41 </bpmndi:BPMNShape> 42 <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1"> 43 <omgdi:waypoint x="165.0" y="237.0"></omgdi:waypoint> 44 <omgdi:waypoint x="210.0" y="237.0"></omgdi:waypoint> 45 </bpmndi:BPMNEdge> 46 <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2"> 47 <omgdi:waypoint x="315.0" y="237.0"></omgdi:waypoint> 48 <omgdi:waypoint x="360.0" y="237.0"></omgdi:waypoint> 49 </bpmndi:BPMNEdge> 50 <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3"> 51 <omgdi:waypoint x="465.0" y="237.0"></omgdi:waypoint> 52 <omgdi:waypoint x="510.0" y="237.0"></omgdi:waypoint> 53 </bpmndi:BPMNEdge> 54 <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4"> 55 <omgdi:waypoint x="615.0" y="237.0"></omgdi:waypoint> 56 <omgdi:waypoint x="660.0" y="237.0"></omgdi:waypoint> 57 </bpmndi:BPMNEdge> 58 </bpmndi:BPMNPlane> 59 </bpmndi:BPMNDiagram> 60 61 </definitions>
1 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();2 3 RepositoryService repositoryService = processEngine.getRepositoryService();4 5 Deployment deployment = repositoryService.createDeployment()6 .addClasspathResource("diagram/holiday.bpmn")7 .addClasspathResource("diagram/holiday.png")8 .name("请假流程")9 .key("holiday") 10 .deploy(); 11 12 13 // Deployment deployment = repositoryService.createDeployment() 14 // .addZipInputStream() 15 // .name() 16 // .key() 17 // .deploy(); 18 19 System.out.println(deployment.getId());
也可以把这两个文件放在一起打包程一个zip压缩包
可以看到,act_re_procdef表中关联了act_re_deployment的ID,act_ge_bytearray表中也关联了act_re_deployment的ID
processDefinitionId是holiday:1:4
deploymentId是1
1 RepositoryService repositoryService = processEngine.getRepositoryService();2 3 // 查询流程部署4 Deployment deployment = repositoryService.createDeploymentQuery().deploymentKey("holiday").singleResult();5 // 查询流程定义6 ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();7 // 流程定义是否被挂起/暂停8 boolean suspended = processDefinition.isSuspended();9 // 删除部署 10 repositoryService.deleteDeployment(deployment.getId()); 11 // 激活流程定义 12 repositoryService.activateProcessDefinitionById(processDefinition.getId()); 13 // 挂起/暂停流程定义 14 repositoryService.suspendProcessDefinitionById(processDefinition.getId()); 15 // 查看流程图图片 16 InputStream is = repositoryService.getResourceAsStream(deployment.getId(), processDefinition.getDiagramResourceName());
3.3. 流程实例
1 RuntimeService runtimeService = processEngine.getRuntimeService();2 3 Map<String, Object> variables = new HashMap<>();4 variables.put("assignee1", "zhangsan");5 variables.put("assignee2", "lisi");7 8 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday", variables);9 10 System.out.println(processInstance.getProcessInstanceId()); 11 System.out.println(processInstance.getProcessDefinitionId());
3.5. 任务
1 TaskService taskService = processEngine.getTaskService(); 2 // 查询待办任务 3 List<Task> taskList = taskService.createTaskQuery().taskAssignee("zhangsan").list(); 4 5 for (Task task : taskList) { 6 // 完成任务 7 taskService.complete(task.getId()); 8 }
现在,已经完成了zhangsan和lisi的任务,流程已经走到人事审批了,看表
接下来,查询tom和jerry的任务时,就不能用taskAssignee("tom")这样了,因为人事审批这个节点设置的是两个候选者,他们都可以看到任务,但是最终只能由一个人去完成
首先,需要声明任务由谁负责,然后再完成,不然任务不会分配给任何人
1 TaskService taskService = processEngine.getTaskService();2 3 // 查询待办任务4 Task task = taskService.createTaskQuery()5 .processDefinitionKey("holiday")6 .taskCandidateUser("tom")7 .singleResult();8 9 // 声明任务的责任人是谁 10 taskService.claim(task.getId(), "tom"); 11 12 // 完成任务 13 taskService.complete(task.getId());
在tom拾取了这个任务以后,当前任务就分配给了tom
假设,拾取任务以后不想办理了,可以选择将自己当前的任务指派给其他人办理,或者再还回去
当我们把任务指派给jack以后
1 /**2 * 指派3 */4 5 // 指派的第一种方式6 taskService.setAssignee(task.getId(), "jack");7 8 // 指派的第二种方式9 taskService.deleteCandidateUser(task.getId(), "tom"); 10 taskService.addCandidateUser(task.getId(), "jack");
还可以将任务委派给他人做
委派:是将任务节点分给其他人处理,等其他人处理好之后,委派任务会自动回到委派人的任务中
1 // 将任务进行委派 2 taskService.delegateTask(task.getId(), "rose"); 3 // 被委派人办理任务后,委派人标记任务已完成 4 taskService.resolveTask(task.getId());
将任务重新放回去
1 /** 2 * 将任务重新放回去 3 */ 4 // 第一种写法 5 taskService.unclaim(task.getId()); 6 // 第二种写法 7 taskService.setAssignee(task.getId(), null);
完整代码片段如下:
1 // 查询待办任务2 Task task = taskService.createTaskQuery()3 .processDefinitionKey("holiday")4 // .taskCandidateUser("tom")5 .taskAssignee("jack")6 .singleResult();7 8 // 拾取任务9 taskService.claim(task.getId(), "tom"); 10 11 // 指派 12 taskService.setAssignee(task.getId(), "jack"); 13 14 taskService.deleteCandidateUser(task.getId(), "tom"); 15 taskService.addCandidateUser(task.getId(), "jack"); 16 17 // 重新放回去 18 taskService.setAssignee(task.getId(), null); 19 taskService.unclaim(task.getId()); 20 21 // 完成任务 22 taskService.complete(task.getId());
人事审批后,整个流程就结束了
一个流程实例走完以后,后续只能通过历史记录去查询它了
1 HistoryService historyService = processEngine.getHistoryService(); 2 // 历史查询 3 List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery() 4 .processInstanceId("7501").orderByHistoricActivityInstanceStartTime().asc().list(); 5 for (HistoricActivityInstance historicActivityInstance : list) { 6 System.out.println(historicActivityInstance.getActivityName() + ":" + historicActivityInstance.getAssignee()); 7 }
3.6. 网关
前面的请假流程比较简单(PS:故意简单设置的),接下来再看一下稍微复杂一点的报销流程(PS:也是故意复杂设置的)
1 <?xml version="1.0" encoding="UTF-8"?>2 <definitions3 xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"5 xmlns:xsd="http://www.w3.org/2001/XMLSchema"6 xmlns:activiti="http://activiti.org/bpmn"7 xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"8 xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"9 xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"10 typeLanguage="http://www.w3.org/2001/XMLSchema"11 expressionLanguage="http://www.w3.org/1999/XPath"12 targetNamespace="http://www.activiti.org/test">13 14 <process id="expense" name="expense" isExecutable="true">15 <documentation>报销流程</documentation>16 <startEvent id="startevent1" name="Start"></startEvent>17 <userTask id="usertask1" name="填写报销单" activiti:assignee="${expense.username}"></userTask>18 <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>19 <userTask id="usertask2" name="部门经理审批" activiti:assignee="${deptManager}"></userTask>20 <sequenceFlow id="flow2" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow>21 <exclusiveGateway id="exclusivegateway2" name="Exclusive Gateway"></exclusiveGateway>22 <sequenceFlow id="flow3" sourceRef="usertask2" targetRef="exclusivegateway2"></sequenceFlow>23 <userTask id="usertask3" name="人事审批" activiti:assignee="${cho}"></userTask>24 <sequenceFlow id="flow6" sourceRef="exclusivegateway2" targetRef="usertask3">25 <conditionExpression xsi:type="tFormalExpression"><![CDATA[${expense.amount <= 500}]]></conditionExpression>26 </sequenceFlow>27 <userTask id="usertask4" name="总经理审批" activiti:assignee="${ceo}"></userTask>28 <sequenceFlow id="flow8" sourceRef="usertask4" targetRef="usertask3"></sequenceFlow>29 <sequenceFlow id="flow10" sourceRef="exclusivegateway2" targetRef="usertask4">30 <conditionExpression xsi:type="tFormalExpression"><![CDATA[${expense.amount > 500}]]></conditionExpression>31 </sequenceFlow>32 <userTask id="usertask5" name="打印申请单" activiti:assignee="zhangsan"></userTask>33 <userTask id="usertask6" name="粘贴发票" activiti:assignee="lisi"></userTask>34 <parallelGateway id="parallelgateway1" name="Parallel Gateway"></parallelGateway>35 <sequenceFlow id="flow11" sourceRef="usertask3" targetRef="parallelgateway1"></sequenceFlow>36 <sequenceFlow id="flow12" sourceRef="parallelgateway1" targetRef="usertask5"></sequenceFlow>37 <sequenceFlow id="flow13" sourceRef="parallelgateway1" targetRef="usertask6"></sequenceFlow>38 <parallelGateway id="parallelgateway2" name="Parallel Gateway"></parallelGateway>39 <sequenceFlow id="flow15" sourceRef="usertask5" targetRef="parallelgateway2"></sequenceFlow>40 <sequenceFlow id="flow16" sourceRef="usertask6" targetRef="parallelgateway2"></sequenceFlow>41 <userTask id="usertask7" name="财务打款" activiti:assignee="${cfo}"></userTask>42 <sequenceFlow id="flow17" sourceRef="parallelgateway2" targetRef="usertask7"></sequenceFlow>43 <endEvent id="endevent1" name="End"></endEvent>44 <sequenceFlow id="flow18" sourceRef="usertask7" targetRef="endevent1"></sequenceFlow>45 </process>46 <bpmndi:BPMNDiagram id="BPMNDiagram_expense">47 <bpmndi:BPMNPlane bpmnElement="expense" id="BPMNPlane_expense">48 <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">49 <omgdc:Bounds height="35.0" width="35.0" x="70.0" y="255.0"></omgdc:Bounds>50 </bpmndi:BPMNShape>51 <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">52 <omgdc:Bounds height="55.0" width="105.0" x="140.0" y="245.0"></omgdc:Bounds>53 </bpmndi:BPMNShape>54 <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">55 <omgdc:Bounds height="55.0" width="105.0" x="280.0" y="245.0"></omgdc:Bounds>56 </bpmndi:BPMNShape>57 <bpmndi:BPMNShape bpmnElement="exclusivegateway2" id="BPMNShape_exclusivegateway2">58 <omgdc:Bounds height="40.0" width="40.0" x="430.0" y="252.0"></omgdc:Bounds>59 </bpmndi:BPMNShape>60 <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">61 <omgdc:Bounds height="55.0" width="105.0" x="520.0" y="245.0"></omgdc:Bounds>62 </bpmndi:BPMNShape>63 <bpmndi:BPMNShape bpmnElement="usertask4" id="BPMNShape_usertask4">64 <omgdc:Bounds height="55.0" width="105.0" x="520.0" y="130.0"></omgdc:Bounds>65 </bpmndi:BPMNShape>66 <bpmndi:BPMNShape bpmnElement="usertask5" id="BPMNShape_usertask5">67 <omgdc:Bounds height="55.0" width="105.0" x="750.0" y="159.0"></omgdc:Bounds>68 </bpmndi:BPMNShape>69 <bpmndi:BPMNShape bpmnElement="usertask6" id="BPMNShape_usertask6">70 <omgdc:Bounds height="55.0" width="105.0" x="750.0" y="320.0"></omgdc:Bounds>71 </bpmndi:BPMNShape>72 <bpmndi:BPMNShape bpmnElement="parallelgateway1" id="BPMNShape_parallelgateway1">73 <omgdc:Bounds height="40.0" width="40.0" x="680.0" y="252.0"></omgdc:Bounds>74 </bpmndi:BPMNShape>75 <bpmndi:BPMNShape bpmnElement="parallelgateway2" id="BPMNShape_parallelgateway2">76 <omgdc:Bounds height="40.0" width="40.0" x="880.0" y="252.0"></omgdc:Bounds>77 </bpmndi:BPMNShape>78 <bpmndi:BPMNShape bpmnElement="usertask7" id="BPMNShape_usertask7">79 <omgdc:Bounds height="55.0" width="105.0" x="961.0" y="245.0"></omgdc:Bounds>80 </bpmndi:BPMNShape>81 <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">82 <omgdc:Bounds height="35.0" width="35.0" x="1100.0" y="255.0"></omgdc:Bounds>83 </bpmndi:BPMNShape>84 <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">85 <omgdi:waypoint x="105.0" y="272.0"></omgdi:waypoint>86 <omgdi:waypoint x="140.0" y="272.0"></omgdi:waypoint>87 </bpmndi:BPMNEdge>88 <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">89 <omgdi:waypoint x="245.0" y="272.0"></omgdi:waypoint>90 <omgdi:waypoint x="280.0" y="272.0"></omgdi:waypoint>91 </bpmndi:BPMNEdge>92 <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">93 <omgdi:waypoint x="385.0" y="272.0"></omgdi:waypoint>94 <omgdi:waypoint x="430.0" y="272.0"></omgdi:waypoint>95 </bpmndi:BPMNEdge>96 <bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">97 <omgdi:waypoint x="470.0" y="272.0"></omgdi:waypoint>98 <omgdi:waypoint x="520.0" y="272.0"></omgdi:waypoint>99 </bpmndi:BPMNEdge> 100 <bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8"> 101 <omgdi:waypoint x="572.0" y="185.0"></omgdi:waypoint> 102 <omgdi:waypoint x="572.0" y="245.0"></omgdi:waypoint> 103 </bpmndi:BPMNEdge> 104 <bpmndi:BPMNEdge bpmnElement="flow10" id="BPMNEdge_flow10"> 105 <omgdi:waypoint x="450.0" y="252.0"></omgdi:waypoint> 106 <omgdi:waypoint x="450.0" y="157.0"></omgdi:waypoint> 107 <omgdi:waypoint x="520.0" y="157.0"></omgdi:waypoint> 108 </bpmndi:BPMNEdge> 109 <bpmndi:BPMNEdge bpmnElement="flow11" id="BPMNEdge_flow11"> 110 <omgdi:waypoint x="625.0" y="272.0"></omgdi:waypoint> 111 <omgdi:waypoint x="680.0" y="272.0"></omgdi:waypoint> 112 </bpmndi:BPMNEdge> 113 <bpmndi:BPMNEdge bpmnElement="flow12" id="BPMNEdge_flow12"> 114 <omgdi:waypoint x="700.0" y="252.0"></omgdi:waypoint> 115 <omgdi:waypoint x="700.0" y="186.0"></omgdi:waypoint> 116 <omgdi:waypoint x="750.0" y="186.0"></omgdi:waypoint> 117 </bpmndi:BPMNEdge> 118 <bpmndi:BPMNEdge bpmnElement="flow13" id="BPMNEdge_flow13"> 119 <omgdi:waypoint x="700.0" y="292.0"></omgdi:waypoint> 120 <omgdi:waypoint x="700.0" y="347.0"></omgdi:waypoint> 121 <omgdi:waypoint x="750.0" y="347.0"></omgdi:waypoint> 122 </bpmndi:BPMNEdge> 123 <bpmndi:BPMNEdge bpmnElement="flow15" id="BPMNEdge_flow15"> 124 <omgdi:waypoint x="855.0" y="186.0"></omgdi:waypoint> 125 <omgdi:waypoint x="900.0" y="186.0"></omgdi:waypoint> 126 <omgdi:waypoint x="900.0" y="252.0"></omgdi:waypoint> 127 </bpmndi:BPMNEdge> 128 <bpmndi:BPMNEdge bpmnElement="flow16" id="BPMNEdge_flow16"> 129 <omgdi:waypoint x="855.0" y="347.0"></omgdi:waypoint> 130 <omgdi:waypoint x="900.0" y="347.0"></omgdi:waypoint> 131 <omgdi:waypoint x="900.0" y="292.0"></omgdi:waypoint> 132 </bpmndi:BPMNEdge> 133 <bpmndi:BPMNEdge bpmnElement="flow17" id="BPMNEdge_flow17"> 134 <omgdi:waypoint x="920.0" y="272.0"></omgdi:waypoint> 135 <omgdi:waypoint x="961.0" y="272.0"></omgdi:waypoint> 136 </bpmndi:BPMNEdge> 137 <bpmndi:BPMNEdge bpmnElement="flow18" id="BPMNEdge_flow18"> 138 <omgdi:waypoint x="1066.0" y="272.0"></omgdi:waypoint> 139 <omgdi:waypoint x="1100.0" y="272.0"></omgdi:waypoint> 140 </bpmndi:BPMNEdge> 141 </bpmndi:BPMNPlane> 142 </bpmndi:BPMNDiagram> 143 </definitions>
引入了排他网关(Exclusive Gateway)和并行网关(Parallel Gateway)
1 @Data 2 public class Expense implements Serializable { 3 // 申请人 4 private String username; 5 // 报销金额 6 private Integer amount; 7 }
启动流程实例
1 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();2 3 RepositoryService repositoryService = processEngine.getRepositoryService();4 RuntimeService runtimeService = processEngine.getRuntimeService();5 TaskService taskService = processEngine.getTaskService();6 7 // 部署流程定义8 Deployment deployment = repositoryService.createDeployment()9 .addClasspathResource("diagram/expense.bpmn") 10 .addClasspathResource("diagram/expense.png") 11 .name("报销流程") 12 .key("expense") 13 .deploy(); 14 15 ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() 16 .deploymentId(deployment.getId()) 17 .singleResult(); 18 System.out.println(processDefinition.getId()); 19 20 Expense expense = new Expense(); 21 expense.setUsername("chengcheng"); 22 expense.setAmount(520); 23 24 Map<String, Object> variables = new HashMap<>(); 25 variables.put("expense", expense); 26 variables.put("deptManager", "tom"); 27 variables.put("ceo", "jerry"); 28 variables.put("cho", "rose"); 29 variables.put("cfo", "jack"); 30 31 // 启动流程实例 32 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("expense", variables); 33 System.out.println(processInstance.getId()); 34 35 // 查询cheng的待办任务 36 Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).taskAssignee("chengcheng").singleResult(); 37 if (null != task) { 38 taskService.complete(task.getId()); 39 } 40 41 // 完成tom的待办任务 42 task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); 43 String assignee = task.getAssignee(); 44 Assertions.assertEquals("tom", assignee); 45 taskService.complete(task.getId()); 46 47 // 判断当前任务走到总经理审批 48 task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); 49 assignee = task.getAssignee(); 50 Assertions.assertEquals("jerry", assignee); 51 taskService.complete(task.getId()); 52 53 // 完成rose的任务 54 task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).taskAssignee("rose").singleResult(); 55 if (null != task) { 56 taskService.complete(task.getId()); 57 } 58 59 // 断言当前有2个激活的任务 60 List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list(); 61 taskList.forEach(x->{ 62 System.out.println(x.getName() + " : " + x.getAssignee()); 63 }); 64 Assertions.assertEquals(2, taskList.size());
当我们完成了zhangsan和lisi的任务以后
接下来,演示包含网关(Inclusive Gateway)
1 <?xml version="1.0" encoding="UTF-8"?>2 <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4 xmlns:xsd="http://www.w3.org/2001/XMLSchema"5 xmlns:activiti="http://activiti.org/bpmn"6 xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"7 xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"8 xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"9 typeLanguage="http://www.w3.org/2001/XMLSchema"10 expressionLanguage="http://www.w3.org/1999/XPath"11 targetNamespace="http://www.activiti.org/test">12 13 <process id="HealthExamination" name="HealthExamination" isExecutable="true">14 <startEvent id="startevent1" name="Start"></startEvent>15 <userTask id="usertask1" name="填写体检申请" activiti:assignee="${username}"></userTask>16 <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>17 <inclusiveGateway id="inclusivegateway1" name="Inclusive Gateway"></inclusiveGateway>18 <userTask id="usertask2" name="常规体检" activiti:assignee="${username}"></userTask>19 <userTask id="usertask3" name="癌症筛查" activiti:assignee="${username}"></userTask>20 <userTask id="usertask4" name="乙肝检查" activiti:assignee="${username}"></userTask>21 <sequenceFlow id="flow2" sourceRef="inclusivegateway1"22 targetRef="usertask2">23 <conditionExpression xsi:type="tFormalExpression"><![CDATA[${userType == 1 || userType ==2}]]></conditionExpression>24 </sequenceFlow>25 <sequenceFlow id="flow4" sourceRef="inclusivegateway1"26 targetRef="usertask3">27 <conditionExpression xsi:type="tFormalExpression"><![CDATA[${userType == 2}]]></conditionExpression>28 </sequenceFlow>29 <sequenceFlow id="flow5" sourceRef="inclusivegateway1"30 targetRef="usertask4">31 <conditionExpression xsi:type="tFormalExpression"><![CDATA[${userType == 1 || userType ==2}]]></conditionExpression>32 </sequenceFlow>33 <sequenceFlow id="flow6" sourceRef="usertask1" targetRef="inclusivegateway1"></sequenceFlow>34 <inclusiveGateway id="inclusivegateway2" name="Inclusive Gateway"></inclusiveGateway>35 <sequenceFlow id="flow8" sourceRef="usertask4" targetRef="inclusivegateway2"></sequenceFlow>36 <sequenceFlow id="flow9" sourceRef="usertask2" targetRef="inclusivegateway2"></sequenceFlow>37 <sequenceFlow id="flow10" sourceRef="usertask3" targetRef="inclusivegateway2"></sequenceFlow>38 <userTask id="usertask5" name="吃早餐" activiti:assignee="${username}"></userTask>39 <endEvent id="endevent1" name="End"></endEvent>40 <sequenceFlow id="flow11" sourceRef="usertask5" targetRef="endevent1"></sequenceFlow>41 <sequenceFlow id="flow12" sourceRef="inclusivegateway2" targetRef="usertask5"></sequenceFlow>42 </process>43 44 <bpmndi:BPMNDiagram id="BPMNDiagram_HealthExamination">45 <bpmndi:BPMNPlane bpmnElement="HealthExamination" id="BPMNPlane_HealthExamination">46 <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">47 <omgdc:Bounds height="35.0" width="35.0" x="100.0" y="254.0"></omgdc:Bounds>48 </bpmndi:BPMNShape>49 <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">50 <omgdc:Bounds height="55.0" width="105.0" x="180.0" y="244.0"></omgdc:Bounds>51 </bpmndi:BPMNShape>52 <bpmndi:BPMNShape bpmnElement="inclusivegateway1" id="BPMNShape_inclusivegateway1">53 <omgdc:Bounds height="40.0" width="40.0" x="370.0" y="251.0"></omgdc:Bounds>54 </bpmndi:BPMNShape>55 <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">56 <omgdc:Bounds height="55.0" width="105.0" x="490.0" y="150.0"></omgdc:Bounds>57 </bpmndi:BPMNShape>58 <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">59 <omgdc:Bounds height="55.0" width="105.0" x="490.0" y="340.0"></omgdc:Bounds>60 </bpmndi:BPMNShape>61 <bpmndi:BPMNShape bpmnElement="usertask4" id="BPMNShape_usertask4">62 <omgdc:Bounds height="55.0" width="105.0" x="490.0" y="244.0"></omgdc:Bounds>63 </bpmndi:BPMNShape>64 <bpmndi:BPMNShape bpmnElement="inclusivegateway2" id="BPMNShape_inclusivegateway2">65 <omgdc:Bounds height="40.0" width="40.0" x="690.0" y="251.0"></omgdc:Bounds>66 </bpmndi:BPMNShape>67 <bpmndi:BPMNShape bpmnElement="usertask5" id="BPMNShape_usertask5">68 <omgdc:Bounds height="55.0" width="105.0" x="800.0" y="244.0"></omgdc:Bounds>69 </bpmndi:BPMNShape>70 <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">71 <omgdc:Bounds height="35.0" width="35.0" x="950.0" y="254.0"></omgdc:Bounds>72 </bpmndi:BPMNShape>73 <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">74 <omgdi:waypoint x="135.0" y="271.0"></omgdi:waypoint>75 <omgdi:waypoint x="180.0" y="271.0"></omgdi:waypoint>76 </bpmndi:BPMNEdge>77 <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">78 <omgdi:waypoint x="390.0" y="251.0"></omgdi:waypoint>79 <omgdi:waypoint x="390.0" y="177.0"></omgdi:waypoint>80 <omgdi:waypoint x="490.0" y="177.0"></omgdi:waypoint>81 </bpmndi:BPMNEdge>82 <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">83 <omgdi:waypoint x="390.0" y="291.0"></omgdi:waypoint>84 <omgdi:waypoint x="390.0" y="367.0"></omgdi:waypoint>85 <omgdi:waypoint x="490.0" y="367.0"></omgdi:waypoint>86 </bpmndi:BPMNEdge>87 <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">88 <omgdi:waypoint x="410.0" y="271.0"></omgdi:waypoint>89 <omgdi:waypoint x="490.0" y="271.0"></omgdi:waypoint>90 </bpmndi:BPMNEdge>91 <bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">92 <omgdi:waypoint x="285.0" y="271.0"></omgdi:waypoint>93 <omgdi:waypoint x="370.0" y="271.0"></omgdi:waypoint>94 </bpmndi:BPMNEdge>95 <bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8">96 <omgdi:waypoint x="595.0" y="271.0"></omgdi:waypoint>97 <omgdi:waypoint x="690.0" y="271.0"></omgdi:waypoint>98 </bpmndi:BPMNEdge>99 <bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9"> 100 <omgdi:waypoint x="595.0" y="177.0"></omgdi:waypoint> 101 <omgdi:waypoint x="710.0" y="177.0"></omgdi:waypoint> 102 <omgdi:waypoint x="710.0" y="251.0"></omgdi:waypoint> 103 </bpmndi:BPMNEdge> 104 <bpmndi:BPMNEdge bpmnElement="flow10" id="BPMNEdge_flow10"> 105 <omgdi:waypoint x="595.0" y="367.0"></omgdi:waypoint> 106 <omgdi:waypoint x="710.0" y="367.0"></omgdi:waypoint> 107 <omgdi:waypoint x="710.0" y="291.0"></omgdi:waypoint> 108 </bpmndi:BPMNEdge> 109 <bpmndi:BPMNEdge bpmnElement="flow11" id="BPMNEdge_flow11"> 110 <omgdi:waypoint x="905.0" y="271.0"></omgdi:waypoint> 111 <omgdi:waypoint x="950.0" y="271.0"></omgdi:waypoint> 112 </bpmndi:BPMNEdge> 113 <bpmndi:BPMNEdge bpmnElement="flow12" id="BPMNEdge_flow12"> 114 <omgdi:waypoint x="730.0" y="271.0"></omgdi:waypoint> 115 <omgdi:waypoint x="800.0" y="271.0"></omgdi:waypoint> 116 </bpmndi:BPMNEdge> 117 </bpmndi:BPMNPlane> 118 </bpmndi:BPMNDiagram> 119 </definitions>
用一个userType=1的用户测试一下
1 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();2 3 RepositoryService repositoryService = processEngine.getRepositoryService();4 RuntimeService runtimeService = processEngine.getRuntimeService();5 TaskService taskService = processEngine.getTaskService();6 7 Deployment deployment = repositoryService.createDeployment()8 .addClasspathResource("diagram/HealthExamination.bpmn")9 .addClasspathResource("diagram/HealthExamination.png") 10 .name("体检流程") 11 .key("HealthExamination") 12 .deploy(); 13 14 ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() 15 .deploymentId(deployment.getId()) 16 .singleResult(); 17 System.out.println(processDefinition.getId()); 18 19 20 Map<String, Object> variables = new HashMap<>(); 21 variables.put("username", "cheng"); 22 variables.put("userType", 1); 23 24 // 启动流程实例 25 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("HealthExamination", variables); 26 System.out.println(processInstance.getId()); 27 28 // 查询cheng的待办任务 29 Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).taskAssignee("cheng").singleResult(); 30 if (null != task) { 31 taskService.complete(task.getId()); 32 } 33 34 // 断言进入包含网关之后cheng有两个待办任务,因为他的userType=1 35 List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list(); 36 Assertions.assertEquals(2, taskList.size());
1 Map<String, Object> variables = new HashMap<>();2 variables.put("username", "chengcheng");3 variables.put("userType", 2);4 5 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("HealthExamination", variables);6 7 Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).taskAssignee("chengcheng").singleResult();8 if (null != task) {9 taskService.complete(task.getId()); 10 } 11 12 List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list(); 13 Assertions.assertEquals(3, taskList.size());