纵览Gitee搜索Flowable开源项目,大多都是已开发好的项目,而笔者从零开始搭建属于自己的Flowable引擎,并且是可以拿到生产上使用的。这里搭建一个Springboot + Flowable6.7.2的开箱即用的流程引擎开源软件,代码仓库上传到Gitee,想要的文章末尾拿链接。
当前Flowable最新版本是6.7.2,学习肯定用最新稳定版本啦。
Springboot + Flowable + modeler + idm + Mysql
SrpingBoot version:2.7.5
Flowable version:6.7.2
Mysql version:8.0.26
JDK:8
步骤:
1,打开idea,创建工程:file -> newproject ,选择spring initializer;
2,然后填写项目命名后,选择JDK8,Maven;
3,选择功能模块,选Web和lombok即可;
4,点Finish,就能够自动创建工程,并能导入相应依赖了;
5,Maven加载依赖后,运行主程序类看是否成功。
引入Flowable依赖,这里选择Springboot-starter,毕竟是springboot项目。
添加必要的依赖。
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.2.2
com.baomidou
mybatis-plus-boot-starter
3.0.3
com.alibaba
fastjson
1.2.79
mysql
mysql-connector-java
runtime
org.flowable
flowable-spring-boot-starter
6.7.2
在application.yml文件中加入以下配置。
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/spider-flowable?charset=utf8mb4&useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf-8
username: root
password: 12345678
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
flowable:
#关闭定时任务JOB
async-executor-activate: false
#将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。
database-schema-update: true
项目启动的时候检查如果数据库对应的表结构没有创建,会帮助我们先创建对应的表结构。到此Springboot整合Flowable就搞定了,
项目启动后,刷新数据库,你会发现多了79张表,都是以ACT和FLW为前缀的表名。
知道为什么Flowable自带的数据库的表名为啥是以ACT为前缀吗?
因为Flowable是基于Activiti6衍生出来的版本。
数据库结构如图:

FLowable流程的运行,其实是用一个满足BPMN格式的XML文件来执行的,至于XML内容格式是怎么样的后续在学习。
在项目中的resources下新建一个processes文件夹,processes目录下的任何BPMN 2.0流程定义都会被自动部署。
1,在processes文件夹下新建holiday-request.bpmn20.xml文件
2,文件内容复制如下:
咱来测试一下功能,小试牛刀,更多功能在后面文章再细谈。
在test文件夹下新建测试类SpiderFlowableTest,代码如下。
package com.example.spiderFlowable;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author xiong.bo
* @version 1.0
* @date 2022/11/27 10:41 上午
*/
@SpringBootTest
public class SpiderFlowableTest {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
/**
* 模拟-部署流程
* 说明:
* 流程定义有版本的概念,bpmn文件有改动,需要部署后才生效
*/
@Test
void createDeployment() {
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("processes/holiday-request.bpmn20.xml")
.deploy();
System.out.println(deployment.getId());
System.out.println(JSON.toJSONString(deployment));
}
/**
* 获取流程定义
*/
@Test
void getProcessDefinition() {
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.deploymentId("17504")
.singleResult();
System.out.println("definitionId:" + processDefinition.getId());
System.out.println("definition:" + JSONUtil.toJsonStr(processDefinition));
}
/**
* 启动流程
* 模拟用户发起一个请假流程
*/
@Test
void startProcessDefinition() {
Map variables = new HashMap<>();
variables.put("employee", "李四");
variables.put("nrOfHolidays", "4");
variables.put("description", "外出请假");
//启动流程实例有多个方法,这里调用流程key的方法来启动
ProcessInstance holidayProcessInstance = runtimeService.startProcessInstanceByKey("holidayRequest", variables);
System.out.println("processInstanceId:" + holidayProcessInstance.getProcessInstanceId());
}
/**
* 获取任务
* 用户发起流程后,相关的人员能够查询该任务
*
*/
@Test
void getTask(){
List tasks = taskService.createTaskQuery()
.active()
.includeProcessVariables()
//值为admin是管理员可以查看所有的,测试
.taskCandidateOrAssigned("admin")
.list();
System.out.println("You have " + tasks.size() + " tasks:");
for (int i = 0; i < tasks.size(); i++) {
Task task = tasks.get(i);
System.out.println((i + 1) + ") " + "taskId: " + task.getId() + ", taskName: " + task.getName());
}
}
/**
* 审批任务
* 说明:
* 1,变量approved同流程定义文件里面顺序流定义的变量
* 2,taskId是上一个获取用户任务的taskId值,也就是要指定哪一个用户任务往下执行
*/
@Test
void completeTask(){
Map variables = new HashMap<>();
variables.put("approved", true);
String taskId = "20008";
taskService.complete(taskId,variables);
}
/**
* 查看历史任务
* 说明:
* taskAssignee: 分配人
* finished:已完成状态的
*
*/
@Test
void historyTask(){
List hisList = historyService.createHistoricActivityInstanceQuery()
.taskAssignee("admin")
.finished()
.list();
hisList.stream().forEach(e -> System.out.println(JSONUtil.toJsonStr(e)));
}
}
1,部署流程
/**
* 模拟-部署流程
* 说明:
* 流程定义有版本的概念,bpmn文件有改动,需要部署后才生效
*/
@Test
void createDeployment() {
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("processes/holiday-request.bpmn20.xml")
.deploy();
System.out.println(deployment.getId());
System.out.println(JSON.toJSONString(deployment));
}
运行结果:
27501
ps: System.out.println(JSON.toJSONString(deployment));输出内容太多这里不贴上了
2,获取流程定义
/**
* 获取流程定义
*/
@Test
void getProcessDefinition() {
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.deploymentId("17504")
.singleResult();
System.out.println("definitionId:" + processDefinition.getId());
System.out.println("definition:" + JSONUtil.toJsonStr(processDefinition));
}
运行结果:
definitionId:holidayRequest:5:17506
definition:{"name":"请假流程","key":"holidayRequest","version":5,"category":"http://www.flowable.org/processdef","deploymentId":"17504","resourceName":"processes/holiday-request.bpmn20.xml","tenantId":"","isGraphicalNotationDefined":false,"hasStartFormKey":false,"suspensionState":1,"derivedVersion":0,"id":"holidayRequest:5:17506","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false,"originalPersistentState":{"suspensionState":1,"category":"http://www.flowable.org/processdef"}}
3,启动流程
/**
* 启动流程
* 模拟用户发起一个请假流程
*/
@Test
void startProcessDefinition() {
Map<String, Object> variables = new HashMap<>();
variables.put("employee", "李四");
variables.put("nrOfHolidays", "4");
variables.put("description", "外出请假");
//启动流程实例有多个方法,这里调用流程key的方法来启动
ProcessInstance holidayProcessInstance = runtimeService.startProcessInstanceByKey("holidayRequest", variables);
System.out.println("processInstanceId:" + holidayProcessInstance.getProcessInstanceId());
}
运行结果:
processInstanceId:30001
4,获取用户任务
/**
* 获取任务
* 用户发起流程后,相关的人员能够查询该任务
*
*/
@Test
void getTask(){
List<Task> tasks = taskService.createTaskQuery()
.active()
.includeProcessVariables()
//值为admin是管理员可以查看所有的,测试
.taskCandidateOrAssigned("admin")
.list();
System.out.println("You have " + tasks.size() + " tasks:");
for (int i = 0; i < tasks.size(); i++) {
Task task = tasks.get(i);
System.out.println((i + 1) + ") " + "taskId: " + task.getId() + ", taskName: " + task.getName());
}
}
运行结果:
You have 4 tasks:
1) taskId: 10008, taskName: 同意或驳回请求
2) taskId: 12508, taskName: 同意或驳回请求
3) taskId: 20008, taskName: 同意或驳回请求
4) taskId: 7508, taskName: 同意或驳回请求
5,审批任务
/**
* 审批任务
* 说明:
* 1,变量approved同流程定义文件里面顺序流定义的变量
* 2,taskId是上一个获取用户任务的taskId值,也就是要指定哪一个用户任务往下执行
*/
@Test
void completeTask(){
Map<String, Object> variables = new HashMap<>();
variables.put("approved", true);
String taskId = "20008";
taskService.complete(taskId,variables);
}
运行结果:
结果回调了 ApprovalSuccessDelegate ,打印出"审批通过了"
6,查看历史任务
/**
* 查看历史任务
* 说明:
* taskAssignee: 分配人
* finished:已完成状态的
*
*/
@Test
void historyTask(){
List<HistoricActivityInstance> hisList = historyService.createHistoricActivityInstanceQuery()
.taskAssignee("admin")
.finished()
.list();
hisList.stream().forEach(e -> System.out.println(JSONUtil.toJsonStr(e)));
}
运行结果:
{"activityId":"approveTask","activityName":"同意或驳回请求","activityType":"userTask","executionId":"20005","assignee":"admin","taskId":"20008","tenantId":"","processInstanceId":"20001","processDefinitionId":"holidayRequest:5:17506","startTime":1669521326282,"endTime":1669521596820,"durationInMillis":270538,"id":"20007","revision":2,"isInserted":false,"isUpdated":false,"isDeleted":false,"originalPersistentState":{"executionId":"20005","durationInMillis":270538,"endTime":1669521596820,"assignee":"admin","taskId":"20008"}}
{"activityId":"approveTask","activityName":"同意或驳回请求","activityType":"userTask","executionId":"30005","assignee":"admin","taskId":"30008","tenantId":"","processInstanceId":"30001","processDefinitionId":"holidayRequest:6:27503","startTime":1669643905412,"endTime":1669644033366,"durationInMillis":127954,"id":"30007","revision":2,"isInserted":false,"isUpdated":false,"isDeleted":false,"originalPersistentState":{"executionId":"30005","durationInMillis":127954,"endTime":1669644033366,"assignee":"admin","taskId":"30008"}}
最基本的完成一个流程的核心功能在上面列举了,先部署流程,再启动流程,再获取任务,再完成某一个任务。
其实你会发现,流程引擎并没有那么神秘,它强大的功能,就是通过满足BPM2.0规范的xml文件和数据库进行流转的,后面文章在学习BPM2.0规范和Flowable数据库各个表的大致作用等。
以上代码已放到笔者的Gitee仓库地址:https://gitee.com/xiongbomy/spider-flowable.git
欢迎大家star和fork。
建议大家fork此项目到你个人仓库,方便你测试或者基于此开发属于你的流程引擎,当然,基于此进行二次扩展,再拿到公司当内部系统也是可以的,只要不商用拿去卖钱~
题外话,笔者在公司主要负责公司内部的流程引擎系统的开发,使用的是Flowable,后续会记录学习Flowable的笔记,一是让自己的知识形成体系化,二是让更多想要学习Flowable的同学们得到一点点帮助也好。