工作流引擎Flowable
这里写目录标题
1.Flowable基础
1.1 入门学习
一、依赖
<dependencies>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-engine</artifactId>
<version>6.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
</dependencies>
二、demo
@Test
public void testProcessEngine() {
// 获取config对象
ProcessEngineConfiguration config = new StandaloneProcessEngineConfiguration();
// 设置数据库连接
config.setJdbcDriver("com.mysql.cj.jdbc.Driver");
config.setJdbcUsername("root");
config.setJdbcPassword("root");
// 注意:要添加nullCatalogMeansCurrent=true属性, 不然会执行报错
config.setJdbcUrl("jdbc:mysql://localhost:3306/flowable?serverTimezone=UTC&nullCatalogMeansCurrent=true");
// 如果数据库中的表结构不存在, 就新建
config.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
// 构建我们需要的对象
ProcessEngine processEngine = config.buildProcessEngine();
}
三、日志文件
在resources中添加日志文件log4j.properties
log4j.rootLogger=DEBUG, CA
log4j.appender.CA=org.apache.log4j.ConsoleAppender
log4j.appender.CA.layout=org.apache.log4j.PatternLayout
log4j.appender.CA.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n
2.流程图设计器
Flowable流程图
- Eclipse Designer, 一款Eclipse插件, 用于图形化建模, 测试与部署BPMN2.0流程
- FlowableUI
- Flowable BPMN visualizer, 一款idea插件
2.1 FlowableUI
从官网下载flowable-6.7.2.zip解压后, 可以看到如下两个文件
将这两个文件, 扔到tomcat中, 并启动tomcat
之后我们就可以在网页中访问FlowableUI
访问地址
账户密码:admin/test
2.2 绘制流程图
一、创建流程
创建一个简单的请假流程
开始 > 提交审核流程(user1) > 总经理审批流程(user2) > 结束
二、部署
绘制好的流程图, 我们只需要一键导出即可(BPMN文件)
下载后, 将文件复制到idea中
private ProcessEngineConfiguration config;
@Before
public void before() {
// 获取config对象
config = new StandaloneProcessEngineConfiguration();
// 设置数据库连接
config.setJdbcDriver("com.mysql.cj.jdbc.Driver");
config.setJdbcUsername("root");
config.setJdbcPassword("root");
// 注意:要添加nullCatalogMeansCurrent=true属性, 不然会执行报错
config.setJdbcUrl("jdbc:mysql://localhost:3306/flowable?serverTimezone=UTC&nullCatalogMeansCurrent=true");
// 如果数据库中的表结构不存在, 就新建
config.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
}
/**
* 部署流程
*/
@Test
public void testDeploy() {
// 1.获取ProcessEngine对象
ProcessEngine processEngine = config.buildProcessEngine();
// 2.获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成流程的部署操作
repositoryService.createDeployment()
.addClasspathResource("MyHolidayUI.bpmn20.xml")
.name("请求流程")
.deploy();
}
}
执行部署后, 我们回头看看表中的数据
act_re_procdef
act_re_deployment
三、执行流程
@Test
public void testRun() {
// 1.获取ProcessEngine对象
ProcessEngine processEngine = config.buildProcessEngine();
// 2.获取runtimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 3.启动流程实例
ProcessInstance processInstance = runtimeService.startProcessInstanceById("MyHolidayUI:1:4");
System.out.println("processDefinitionId = " + processInstance.getProcessDefinitionId());
System.out.println("activityId = " + processInstance.getActivityId());
System.out.println("id = " + processInstance.getId());
}
可以通过act_ru_task查看任务流程
四、用户处理
user1对任务进行处理
@Test
public void testCompleteTask() {
// 1.获取ProcessEngine对象
ProcessEngine processEngine = config.buildProcessEngine();
// 2.获取TaskService
TaskService taskService = processEngine.getTaskService();
// 3.获取任务
Task task = taskService.createTaskQuery()
.processDefinitionKey("MyHolidayUI")
.taskAssignee("user1")
.singleResult();
// 4.创建流程变量
HashMap<String, Object> map = new HashMap<>();
map.put("approved", false);
// 5.完成任务
taskService.complete(task.getId(), map);
}
处理完成后, 任务就流转到user2了
user2对任务进行处理
@Test
public void testCompleteTask() {
// 1.获取ProcessEngine对象
ProcessEngine processEngine = config.buildProcessEngine();
// 2.获取TaskService
TaskService taskService = processEngine.getTaskService();
// 3.获取任务
Task task = taskService.createTaskQuery()
.processDefinitionKey("MyHolidayUI")
.taskAssignee("user2")
.singleResult();
// 4.创建流程变量
HashMap<String, Object> map = new HashMap<>();
map.put("approved", false);
// 5.完成任务
taskService.complete(task.getId(), map);
}
user2处理完成后, 任务就结束了, 在task表中就删除了这条记录
五、查看历史信息
@Test
public void testHistory() {
// 1.获取ProcessEngine对象
ProcessEngine processEngine = config.buildProcessEngine();
// 2.获取historyService
HistoryService historyService = processEngine.getHistoryService();
// 3.获取任务
List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
.processDefinitionId("MyHolidayUI:1:4")
.finished() // 查询已完成的历史记录
.orderByHistoricActivityInstanceEndTime().asc() // 指定排序的字段
.list();
for (HistoricActivityInstance history : list) {
System.out.println(history.getActivityName() + ":" + history.getAssignee() + "--"
+ history.getActivityId() + ":" + history.getDurationInMillis() + "毫秒");
}
}
3.Flowable高级
3.2 启动流程
/**
* 部署流程
*/
@Test
public void testRunInstance() {
// 1.获取ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 2.获取runtimeService, 通过runtimeService来启动流程实例
RuntimeService runtimeService = engine.getRuntimeService();
// 3.启动流程实例
Map<String, Object> variables = new HashMap<>();
variables.put("name", "张三"); // 谁申请请假
variables.put("days", 3); // 请几天
variables.put("reason", "世界这么大, 我想出去转转"); // 请假理由
ProcessInstance processInstance = runtimeService.startProcessInstanceById("MyHolidayUI:1:4", "order10001", variables);
System.out.println("processDefinitionId = " + processInstance.getProcessDefinitionId());
System.out.println("activityId = " + processInstance.getActivityId());
System.out.println("id = " + processInstance.getId());
}
- ACT_RU_EXECUTION 运行时流程执行实例
- ACT_RU_IDENTITYLINK 运行时用户关系信息
- ACT_RU_TASK 运行时任务表
- ACT_RU_VARIABLE 运行时变量表
一、ACT_RU_EXECUTION
(1) 启动伊始, 会初始化两条记录, 其中第一条记录的PARENT_ID_为空, 这意味着这条记录是流程实例的第一个节点, ROOT_PROC_INST_ID_会记录这个节点的ID_, BUSINESS_KEY_是运行时添加的参数
二、ACT_RU_IDENTITYLINK
(1) USER_ID_ 代表当前节点用户
(2) TYPE_ 为participant代表用户为参与者
- assignee 指派人
- candidate 候选人
- owner 拥有者
- starter 启动者
- participant 参与者
三、ACT_RU_VARIABLE
流程变量信息
3.3 处理流程
@Test
public void testCompleteTask() {
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.获取TaskService
TaskService taskService = processEngine.getTaskService();
// 3.获取任务
Task task = taskService.createTaskQuery()
.processInstanceId("20001")
.taskAssignee("user1")
.singleResult();
// 获取当前流程实例绑定的流程变量
Map<String, Object> processVariables = task.getProcessVariables();
for (String key : processVariables.keySet()) {
System.out.println(key + ":" + processVariables.get(key));
}
// 4.创建流程变量
processVariables.put("approved", true);
// 覆盖之前的流程变量
processVariables.put("days", 10);
// 5.完成任务
taskService.complete(task.getId(), processVariables);
}
因为我们一个流程定义启动了两个流程实例, 所以我们如果通过流程定义的key(MyHolidayUI)来处理的话, 就会处理所有流程定义下的进程
正确的方式: 我们找到想要处理的流程实例, 获取到流程实例的唯一编号(ROOT_PROC_INST_ID_), 然后指定相应用户处理流程
3.4 流程结束
@Test
public void testCompleteTask() {
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.获取TaskService
TaskService taskService = processEngine.getTaskService();
// 3.获取任务
Task task = taskService.createTaskQuery()
.processInstanceId("20001")
.taskAssignee("user2")
.singleResult();
// 4.结束任务
taskService.complete(task.getId(), processVariables);
}
结束任务之后, 在_RU表中, 所有跟此流程实例的相关的数据都会删除
- ACT_RU_EXECUTION 运行时流程执行实例
- ACT_RU_IDENTITYLINK 运行时用户关系信息
- ACT_RU_TASK 运行时任务表
- ACT_RU_VARIABLE 运行时变量表
那么如果我们想要查看历史记录, 怎么办
- act_hi_taskinst
- act_hi_varinst
- act_hi_identitylink
- act_hi_procinst
- act_hi_actinst
3.5 任务分配
之前我们任务分配, 是在流程定义时, 就将用户写死了, 例如user1、user2, 在实际开发过程中, 我们并不会使用这个方式
3.5.1 表达式分配
Flowable使用UEL进行表达式解析, Flowable支持两种表达式:
- UEL-value (值表达式)
- UEL-method (方法表达式)
在启动流程实例时, 添加参数
@Test
public void testDeploy() {
// 1.获取processEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成流程的部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("HolidayNew.bpmn20.xml")
.name("请求流程")
.deploy();
System.out.println("deploy.getId() = " + deploy.getId());
System.out.println("deploy.getName() = " + deploy.getName());
}
@Test
public void testRun() {
// 1.获取ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 2.获取runtimeService, 通过runtimeService来启动流程实例
RuntimeService runtimeService = engine.getRuntimeService();
Map<String, Object> variables = new HashMap<>();
variables.put("assignee0", "张三");
variables.put("assignee1", "李四");
// 开启流程实例时, 设置参数
ProcessInstance processInstance = runtimeService.startProcessInstanceById("holiday-new:1:27504", variables);
System.out.println("processDefinitionId = " + processInstance.getProcessDefinitionId());
System.out.println("activityId = " + processInstance.getActivityId());
System.out.println("id = " + processInstance.getId());
}
3.5.2 监听器分配
配置监听器
3.6 流程变量
支持的流程变量有两类
- 全局变量: 主要跟流程实例相关
- 局部变量: 主要跟task相关
3.6.1 实例关系
- 一个流程定义可以创建多个流程实例
3.6.2 出差流程图
一、全局变量
全部变量, 设置相同名称的变量后, 后设置的会覆盖前面设置的变量值
二、局部变量
local变量由于在不同的任务或者不同的执行实例中, 作用域互不影响, 变量名可以相同没有影响, local变量名也可以和全局变量名相同
3.6.3 案例
- 如果${num>=3}则需要总经理审批
- 如果${num<3}则不需要总经理审批
一、流程定义
@Test
public void testDeploy() {
// 1.获取processEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.完成流程的部署操作
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("出差申请单.bpmn20.xml")
.name("出差申请单")
.deploy();
System.out.println("deploy.getId() = " + deploy.getId());
System.out.println("deploy.getName() = " + deploy.getName());
}
流程定义表: act_re_procdef
二、创建流程实例
@Test
public void testRun() {
// 1.获取ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 2.获取runtimeService, 通过runtimeService来启动流程实例
RuntimeService runtimeService = engine.getRuntimeService();
Map<String, Object> variables = new HashMap<>();
variables.put("assignee0", "张三");
variables.put("assignee1", "李四");
variables.put("assignee2", "王五");
variables.put("assignee3", "赵六");
ProcessInstance processInstance = runtimeService.startProcessInstanceById("evection:1:32504", variables);
System.out.println("processDefinitionId = " + processInstance.getProcessDefinitionId());
System.out.println("activityId = " + processInstance.getActivityId());
System.out.println("id = " + processInstance.getId());
}
查看当前节点走到哪儿
3.7 候选人
3.7.1 流程定义和部署
@Test
public void testRun() {
// 1.获取ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 2.获取runtimeService, 通过runtimeService来启动流程实例
RuntimeService runtimeService = engine.getRuntimeService();
Map<String, Object> variables = new HashMap<>();
variables.put("candidate1", "张三");
variables.put("candidate2", "李四");
variables.put("candidate3", "王五");
ProcessInstance processInstance = runtimeService.startProcessInstanceById("holiday-candidate:1:40004", variables);
System.out.println("processDefinitionId = " + processInstance.getProcessDefinitionId());
System.out.println("activityId = " + processInstance.getActivityId());
System.out.println("id = " + processInstance.getId());
}
启动项目后, 发现在task表中并没有assignee, 这是因为候选人并非处理人(指派人)
虽然我们并没有指派处理人, 但我们知道有三个候选者, 在ru_variable表中也能看到
3.7.2 候选任务查询
根据当前登录的用户查询对应的, 可以拾取的任务
@Test
public void queryTaskCandidate() {
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.获取TaskService
TaskService taskService = processEngine.getTaskService();
// 3.获取任务
List<Task> list = taskService.createTaskQuery()
.processInstanceId("42501")
// 候选人
.taskCandidateUser("张三")
.list();
for (Task task : list) {
System.out.println("task.getId() = " + task.getId());
System.out.println("task.getName() = " + task.getName());
}
}
3.7.3 拾取、归还、交接等操作
使用张三拾取任务
@Test
public void claimTask() {
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.获取TaskService
TaskService taskService = processEngine.getTaskService();
// 3.获取任务
Task task = taskService.createTaskQuery()
.processInstanceId("42501")
.taskAssignee("张三")
.singleResult();
if (task != null) {
taskService.claim(task.getId(), "张三");
System.out.println("任务拾取成功");
}
}
- 一个候选人拾取了这个任务之后, 其他用户就没有办法拾取这个任务了
- 所以如果一个用户拾取了任务之后不想处理了, 那么可以退还
@Test
public void unclaimTask() {
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.获取TaskService
TaskService taskService = processEngine.getTaskService();
// 3.获取任务
Task task = taskService.createTaskQuery()
.processInstanceId("42501")
.taskAssignee("张三")
.singleResult();
if (task != null) {
taskService.unclaim(task.getId());
System.out.println("归还成功");
}
}
- 如果我获取了任务, 但是不想执行, 我可以把这个任务交接给其他用户(任务交接)
@Test
public void handoverTask() {
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.获取TaskService
TaskService taskService = processEngine.getTaskService();
// 3.获取任务
Task task = taskService.createTaskQuery()
.processInstanceId("42501")
.taskAssignee("张三")
.singleResult();
if (task != null) {
taskService.setAssignee(task.getId(), "赵六");
System.out.println("交接成功");
}
}
3.8 候选人组
当候选人很多的情况下, 我们可以分组来处理
流程:
1.先创建组
2.把用户分配到组中
@Test
public void createUser() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 通过identityService完成相关的用户和组的管理
IdentityService identityService = processEngine.getIdentityService();
User user = identityService.newUser("田佳");
user.setFirstName("田");
user.setLastName("佳");
user.setEmail("844915283@qq.com");
identityService.saveUser(user);
}
@Test
public void createGroup() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 通过identityService完成相关的用户和组的管理
IdentityService identityService = processEngine.getIdentityService();
// 创建group对象, 并指定相关的信息
Group group = identityService.newGroup("group1");
group.setName("销售部");
group.setType("type1");
// 创建group对应的表结构数据
identityService.saveGroup(group);
}
3.8.1 将用户分配给对应的组
@Test
public void userGroup() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
IdentityService identityService = processEngine.getIdentityService();
// 根据组的编号, 找到对应的group
Group group = identityService.createGroupQuery().groupId("group1").singleResult();
List<User> list = identityService.createUserQuery().list();
list.stream().forEach(t -> {
identityService.createMembership(t.getId(), group.getId());
});
}
3.8.2 部署和运行
@Test
public void testRun() {
// 1.获取ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 2.获取runtimeService, 通过runtimeService来启动流程实例
RuntimeService runtimeService = engine.getRuntimeService();
// 获取组
IdentityService identityService = engine.getIdentityService();
Group group = identityService.createGroupQuery().groupId("group1").singleResult();
Map<String, Object> variables = new HashMap<>();
// 给流程定义中的uel表达式赋值
variables.put("g1", group.getId());
ProcessInstance processInstance = runtimeService.startProcessInstanceById("group:1:50004", variables);
System.out.println("processDefinitionId = " + processInstance.getProcessDefinitionId());
System.out.println("activityId = " + processInstance.getActivityId());
System.out.println("id = " + processInstance.getId());
}
3.8.3 任务查询拾取处理
拾取处理
@Test
public void queryTaskCandidateGroup() {
String userId = "王杰";
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
IdentityService identityService = processEngine.getIdentityService();
// 获取组
Group group = identityService.createGroupQuery().groupMember(userId).singleResult();
// 3.获取任务
TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery()
.processInstanceId("52501")
// 候选人
.taskCandidateGroup(group.getId())
.singleResult();
if (task != null) {
taskService.claim(task.getId(), userId);
}
}
3.9 网关
控制流程的走向