导语
为什么我们需要用到工作流? 它能给我们带来什么?
我的个人理解如下:
- 过去的工作流就是一种
状态机,但随着业务复杂度的提升,对于状态的维护越来越难 - 现在的工作流可以
处理复杂业务场景,他就类似钉钉的审批流程,我们可以自定义审批流(在activiti工作流中称之为审批流定义,这也是activiti的开始) - activiti就相当于别人帮我们写好了工作流, 我们只需要调用他们提供的接口(服务)来生成审批流的数据库记录
- 基于
BPMN的画图来定义审批流非常直观
什么样的工作适合工作流
- 分成不同阶段。
- 有多方人员(组织)参与。
- 重复重复再重复的流程。
- 有流程标准化,分工明晰化,责任明确化的需求。
- 凡是平时工作中需要走流程,而且经常会被卡住的地方,也许应该使用工作流。
- 工作流就是责任流,每一件由用户参与的工作都意味着一份责任。
- 工作流实现的应该是由
不同人员参与的一个流程。所以工作流的环节设置就和现实中的职位设置息息相关。 - 工作流是从上帝的视角,俯瞰整个业务流程,而每个环节的参与者看到的只是的自己的工作。
- 工作流是用来固化一个流程的,如果职位变动频繁,说明业务模式不明朗,是不适合使用工作流的。工作流用在业务模式相对明朗的情况下来固化流程,确定责任,明晰分工。
市面上开源主流的三大工作引擎activiti, flowable, Camunda
| 对比项/引擎 | Activiti-7.x | Flowable-6.x | Camunda |
|---|---|---|---|
| 商业化 | √ | √ | √ |
| 路线(Roadmap) | 云 | 工具型 | 轻量&工具型 |
| BPMN2引擎 | √ | √ | √ |
| 建模工具内容 | BPMN2 | BPMN2/CMMN/DMN | BPMN2/CMMN/DMN |
| 扩展节点(Mule\Http等) | × | √ | √ |
| Spring Boot | √ | √ | √ |
| Spring Cloud | √ | × | × |
| Web控制台 | √ | √ | √ |
| Rest接口 | √ | √ | √ |
大致流程
- 定义审批流程
- 启动流程实例
- 获取未操作的任务列表 (可指定人,例如查看指向自己的审批操作列表,查看哪个xd需要请假的,我给批一下^_^)
- 审批操作
- 其他人也可以新增备注
- 查看该审批流的历史信息
开始
IDEA安装activiti插件
它可以帮助我们画审批流程,然后自动生成xml文件,这也就是之前说的审批流定义,就像下图:
这原理实际上就是BPMN,想要了解BPMN可以移步官网 (bpmn.io/)
pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.imason</groupId>
<artifactId>activiti</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>activiti</name>
<description>activiti</description>
<properties>
</properties>
<dependencies>
<!--数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.activiti/activiti-spring-boot-starter -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M6</version>
</dependency>
<!-- 流程图-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-diagram-rest</artifactId>
<version>5.23.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- mysql数据库 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
bpmn的xml文件
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/processdef">
<process id="leaveProcess-test" name="leaveProcess-test" isExecutable="true">
<startEvent id="sid-4c854455-30a7-4807-b98a-48a426c35e65"/>
<userTask id="sid-7d84a68f-abd0-4715-ae01-0e773aaf7d29" name="部门经理审批" activiti:assignee="李四"/>
<userTask id="sid-57055cb5-b5f0-4e21-83ab-5fca6160cb89" name="人事审批" activiti:assignee="王五"/>
<sequenceFlow id="sid-d5e5c009-8d92-430e-a35d-59ae1bc1e835" sourceRef="sid-7d84a68f-abd0-4715-ae01-0e773aaf7d29" targetRef="sid-57055cb5-b5f0-4e21-83ab-5fca6160cb89"/>
<endEvent id="sid-b80fbf85-aac6-45f2-b789-0e00e026e176"/>
<sequenceFlow id="sid-3d52546e-bd9a-44df-8509-236a16b4e906" sourceRef="sid-57055cb5-b5f0-4e21-83ab-5fca6160cb89" targetRef="sid-b80fbf85-aac6-45f2-b789-0e00e026e176"/>
<sequenceFlow id="sid-f5cb4f54-ef5e-4bce-819a-81abba6484d3" sourceRef="sid-4c854455-30a7-4807-b98a-48a426c35e65" targetRef="sid-7d84a68f-abd0-4715-ae01-0e773aaf7d29"/>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_demo2">
<bpmndi:BPMNPlane bpmnElement="leaveProcess-test" id="BPMNPlane_demo2">
<bpmndi:BPMNShape id="shape-6ee1978a-fc56-406b-9eb6-e6c4c8ab4c92" bpmnElement="sid-4c854455-30a7-4807-b98a-48a426c35e65">
<omgdc:Bounds x="-160.0" y="-120.0" width="30.0" height="30.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="sid-fb6fa50f-beb0-468c-9994-26bfe0a0ee18" bpmnElement="sid-7d84a68f-abd0-4715-ae01-0e773aaf7d29">
<omgdc:Bounds x="-172.5" y="-64.75" width="55.0" height="30.000002"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="sid-b2c42948-b4bc-42fd-9f67-bee07b00fe43" bpmnElement="sid-57055cb5-b5f0-4e21-83ab-5fca6160cb89">
<omgdc:Bounds x="-172.5" y="-16.75" width="55.0" height="30.000002"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="edge-9e36c60c-2ee5-433d-90b8-8fa9de582333" bpmnElement="sid-d5e5c009-8d92-430e-a35d-59ae1bc1e835">
<omgdi:waypoint x="-145.0" y="-34.75"/>
<omgdi:waypoint x="-145.0" y="-16.75"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="shape-d151c3dd-a423-4403-bc5b-0682954e6611" bpmnElement="sid-b80fbf85-aac6-45f2-b789-0e00e026e176">
<omgdc:Bounds x="-160.0" y="35.0" width="30.0" height="30.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="edge-b9e1b61b-8bb5-4f52-9665-c5a746819437" bpmnElement="sid-3d52546e-bd9a-44df-8509-236a16b4e906">
<omgdi:waypoint x="-145.0" y="13.25"/>
<omgdi:waypoint x="-145.0" y="35.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-e41e6c32-06a3-45a7-93c4-f8d56fd6e26b" bpmnElement="sid-f5cb4f54-ef5e-4bce-819a-81abba6484d3">
<omgdi:waypoint x="-145.0" y="-90.0"/>
<omgdi:waypoint x="-145.0" y="-64.75"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
在resources中新增activiti.cfg.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/contex
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://xxx:xxx/platform_activiti?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&nullCatalogMeansCurrent=true" />
<property name="username" value="xxx" />
<property name="password" value="xxxx" />
</bean>
<!-- 默认id对应的值 为processEngineConfiguration -->
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="dataSource" ref="dataSource"/>
<!--
activiti数据库表处理策略
false(默认值):检查数据库的版本和依赖库的版本,如果不匹配就抛出异常
true:构建流程引擎时,执行检查,如果需要就执行更新。如果表不存在,就创建。
create-drop:构建流程引擎时创建数据库报表,关闭流程引擎时就删除这些表。
drop-create:先删除表再创建表。
create:构建流程引擎时创建数据库表,关闭流程引擎时不删除这些表
-->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
</beans>
Service总览
| Service接口 | 说明 |
|---|---|
| RepositoryService | Activiti的资源管理接口 |
| RuntimeService | Activiti的流程运行管理接口 |
| TaskService | Activiti的任务管理接口 |
| HistoryService | Activiti的历史管理接口 |
| ManagementService | Activiti的引擎管理接口 |
RepositoryService,是Activiti的资源管理接口,提供了管理和控制流程发布包和流程定义的操作。使用工作流建模工具设计的业务流程图需要使用此Service将流程定义文件的内容部署到计算机中。RuntimeService,是Activiti的流程运行管理接口,可以从这个接口中获取很多关于流程执行相关的信息。TaskService,是Activiti的任务管理接口,可以从这个接口中获取任务的信息。HistoryService,是Activiti的历史管理类,可以查询历史信息,执行流程时,引擎会包含很多数据(根据配置),比如流程实例启动时间,任务的参与者,完成任务的时间,每个流程实例的执行路径,等等。ManagementService,是Activiti的引擎管理接口,提供了对Activiti流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于Activiti系统的日常维护。
部署审批流程 (定义流程就是画图的那部分)
@Test
public void testDeploy(){
//创建ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取RepositoryService对象
RepositoryService repositoryService = processEngine.getRepositoryService();
//进行部署
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("processes/leave.bpmn20.xml")
.addClasspathResource("processes/leave.png")
.name("请假流程")
.deploy();
System.out.println("流程部署ID:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
启动流程实例
@Test
public void testStartProcess(){
//创建ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
System.out.println(processEngine);
RuntimeService runtimeService = processEngine.getRuntimeService();
//根据流程定义的key启动流程实例,这个key是在定义bpmn的时候设置的
ProcessInstance instance = runtimeService.
startProcessInstanceByKey("leaveProcess-test");
//获取流程实例的相关信息
System.out.println("流程定义的id = " + instance.getProcessDefinitionId());
System.out.println("流程实例的id = " + instance.getId());
}
查看未操作的任务列表(领导: 谁给我发了请假审批,我瞅瞅)
@Test
public void testSelectTodoTaskList(){
//任务负责人
String assignee = "李四";
//创建ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取TaskService
TaskService taskService = processEngine.getTaskService();
//获取任务集合
List<Task> taskList = taskService.createTaskQuery()
.processDefinitionKey("leaveProcess-test")
.taskAssignee(assignee)
.list();
//遍历任务列表
for(Task task:taskList){
System.out.println("流程定义id = " + task.getProcessDefinitionId());
System.out.println("流程实例id = " + task.getProcessInstanceId());
System.out.println("任务id = " + task.getId());
System.out.println("任务名称 = " + task.getName());
}
}
领导审批 (李四:部门经理)
//任务负责人
String assignee = "李四";
//创建ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取TaskService
TaskService taskService = processEngine.getTaskService();
//获取任务集合
List<Task> taskList = taskService.createTaskQuery()
.processDefinitionKey("leaveProcess-test")
.taskAssignee(assignee)
.list();
//遍历任务列表
for(Task task:taskList){
taskService.complete(task.getId());
}
新增备注(王五: 人事 人事:这也没个备注啥的,我补充下)
@Test
public void testAddComment(){
//任务负责人
String assignee = "王五";
//创建ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取TaskService
TaskService taskService = processEngine.getTaskService();
//获取任务集合
List<Task> taskList = taskService.createTaskQuery()
.processDefinitionKey("leaveProcess-test")
.taskAssignee(assignee)
.list();
//遍历任务列表
for(Task task:taskList){
//在任务执行之前任务添加批注信息
taskService.addComment(task.getId(),task.getProcessInstanceId(),task.getName()+"审批通过");
taskService.complete(task.getId());
}
}
查看审批的历史 (我:发起人 我:审核通过没?我还着急回老家呢,看看去)
@Test
public void testSelectHistoryTask(){
//流程实例ID
String processInstanceId = "17501";
//任务审核人
String taskAssignee = "王五";
//创建ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取historyService
HistoryService historyService = processEngine.getHistoryService();
//获取taskService
TaskService taskService = processEngine.getTaskService();
//获取历史审核信息
List<HistoricActivityInstance> list = historyService
.createHistoricActivityInstanceQuery()
// .activityType("userTask")//只获取用户任务
.processInstanceId(processInstanceId)
.taskAssignee(taskAssignee)
// .finished()
.list();
for(HistoricActivityInstance instance: list){
System.out.println("任务名称:"+instance.getActivityName());
System.out.println("任务开始时间:"+instance.getStartTime());
System.out.println("任务结束时间:"+instance.getEndTime());
System.out.println("任务耗时:"+instance.getDurationInMillis());
//获取审核批注信息
List<Comment> taskComments = taskService.getTaskComments(instance.getTaskId());
if(taskComments.size()>0){
//在控制台输出
System.out.println("审批批注:"+taskComments.get(0).getFullMessage());
}
}
}
结语
参考了以下文章做出的自己的总结, 参考地址: juejin.cn/post/710653…