activiti7 - SpringBoot集成
概念:
BPM(Business Process Management)- 业务流程管理,是一种规范化的构造端到端的业务流程,以持续的提高组织业务效率。常见商业管理教育如EMBA、MBA等均将BPM包含在内。
BPMN(Business Process Model AndNotation)- 业务流程模型和符号 是由BPMI(BusinessProcess Management Initiative)开发的一套标准的业务流程建模符号,使用BPMN提供的符号可以创建业务流程。
当前使用最广泛工作流引擎对比:
技术组成 | Activiti7.1 | jBPM7.25 | Camunda | Flowable6.3.1 |
---|---|---|---|---|
数据库持久层ORM | MyBatis3.4.2 | JPA2.2二次封装 | Hibernate4.2.0 | JPA 2.2 |
持久化标准 | JPA规范 | JPA规范 | JPA规范 | JPA规范 |
事务管理 | MyBatis机制/Spring事务控制 | Bitronix,基于JTA事务管理 | hibernate机制/Spring事务控制 | hibernate机制,Spring事务控制 |
分布式事务 | MyBatis机制/Spring事务控制 | Bitronix,基于JTA事务管理 | 补偿机制,SAGA 模式 | |
数据库连接方式 | Jdbc/DataSource | Jdbc/DataSource | Jdbc/DataSource | Jdbc/DataSource |
支持数据库 | Oracle、SQL Server、MySQL | Oracle、SQL Server、MySQL | Oracle、SQL Server、MySQL、db2 | Oracle、SQL Server、MySQL、db2 |
设计模式 | Command模式、观察者模式等 | |||
内部服务通讯 | Service间通过API调用 | 基于Apache Mina异步通讯 | Service间通过API调用 | Service间通过API调用 |
集成接口 | SOAP、Mule、RESTful | 消息通讯 | SOAP、Mule、RESTful | SOAP、Mule、RESTful |
支持的流程格式 | BPMN2、xPDL、jPDL等 | 目前仅只支持BPMN2 xml | BPMN2、xPDL、jPDL等 | BPMN2、xPDL、jPDL等 |
引擎核心 | PVM(流程虚拟机) | Drools | PVM | |
架构 | spring boot 2.1.2, spring 5.1.4 | Drools Flow | activiti5 | spring boot 1.5,spring 4.3, spring.security 4.2 |
支持J2EE | 兼容 | 天然支持EJB,J2EE | 兼容 | 兼容 |
技术前身 | jBPM3、jBPM4 | Drools Flow | activiti5 | activiti5 |
API文档 | swagger1.5 | swagger1.5 | ||
日志 | slf4j1.7门面,logback1.2.3 | slf4j1.5门面, log4j | slf4j1.7门面,logback1.2.3 | slf4j1.7门面 |
所属公司 | Alfresco | jBoss.org | Camunda | Flowable |
表结构
表前缀规则
act_ge_ 通用数据表,ge是general的缩写
act_hi_ 历史数据表,hi是history的缩写,对应 HistoryService 接口
act_id_ 身份数据表,id是identity的缩写,对应 IdentityService 接口
act_re_ 流程存储表,re是repository的缩写,对应 RepositoryService 接口,存储流程部署和流程定义等静态数据
act_ru_ 运行时数据表,ru是runtime的缩写,对应 RuntimeService 接口和 TaskService 接口,存储流程实例和用户任务等动态数据
设计架构
服务接口
service名称 | service作用 |
---|---|
RepositoryService | activiti的资源管理类 |
RuntimeService | activiti的流程运行管理类 |
TaskService | activiti的任务管理类 |
HistoryService | activiti的历史管理类 |
ManagerService | activiti的引擎管理类 |
任务监听
任务监听器是发生对应的任务相关事件时执行自定义 java 逻辑 或表达式。 任务相当事件包括:
Create:任务创建后触发
Assignment:任务分配后触发
Delete:任务完成后触发
All:所有事件发生都触发
定义任务监听类,且类必须实现 org.activiti.engine.delegate.TaskListener 接口
public class MyTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
//业务代码
}
}
生命周期
1 onCreate()
:当一个Activity第一次启动时调用,表示一个Activity对象被创建一般情况下,我们会在该方法中做一些初始化的工作(初始化组件,设置事件监听,填充主布局,其他数据)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
2 onStart()
:在onCreate方法之后调用,该方法用于显示界面,完成后我们可以看见界面,但还不能进行交互
@Override
protected void onStart() {
super.onStart();
}
3 onRestart()
: 在当前Activity从停止状态重新被显示时会调用,接着会调用onStart--onResume
@Override
protected void onStart() {
super.onStart();
}
4 onResume()
:在onStart方法之后被调用,此方法完成后,界面可以进行交互,此时,Activity进入运行状态。该状态下可以实现还原状态的操作
@Override
protected void onResume() {
super.onResume();
}
5 onPause()
:当前Activity被另一个Activity不完全覆盖时被调用(或被一个窗口模式的Activity覆盖),此时Activity进入暂停状态,该状态下,如果设备内存不足时有可能被销毁
:当覆盖当前Activity的另一个窗口模式的Activity出栈,此时会调用onResume方法,让Activity重新进入运行状态,该状态下可以保存Activity的状态,已在还原时继续执行
@Override
protected void onPause() {
super.onPause();
}
6 onStop()
:当前Activity被完全覆盖后被调用,此时Activity已经看不见,Activity进入停止状态,该状态下,如果设备内存不足时有可能被销毁
当前Activity重新显示时,会调用onReStart----onStart----onResume
@Override
protected void onStop() {
super.onStop();
}
7 onDestory()
:当Activity被出栈,销毁时调用,表示当前Activity进入killed状态,那么我们一般在该方法进行资源释放工作
@Override
protected void onDestroy() {
super.onDestroy();
}
项目集成
maven引入
<!-- 工作流activiti -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 生成流程图 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-image-generator</artifactId>
</dependency>
注意:排除activiti自带的mybatis,防止与项目的mybatis冲突
yml配置
spring:
activiti:
#自动更新数据库结构
database-schema-update: true
#activiti7默认不生成历史信息表,开启历史表
db-history-used: true
#记录历史等级 可配置的历史级别有none, activity, audit, full
#none:不保存任何的历史数据,因此,在流程执行过程中,这是最高效的。
#activity:级别高于none,保存流程实例与流程行为,其他数据不保存。
#audit:除activity级别会保存的数据外,还会保存全部的流程任务及其属性。audit为history的默认值。
#full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等。
history-level: full
#自动检查、部署流程定义文件 启动时自动部署定义的流程
check-process-definitions: true
# asyncExecutorActivate是指activiti在流程引擎启动就激活AsyncExecutor,异步:true-开启(默认)、false-关闭
async-executor-activate: true
#流程定义文件存放目录,要具体到某个目录
process-definition-location-prefix: classpath:/processes/
#process-definition-location-suffixes: #流程文件格式
# - **.bpmn20.xml
# - **.bpmn
main:
allow-bean-definition-overriding: true
注意: 启动如果bean生成失败,添加allow-bean-definition-overriding:true 允许bean被覆盖解决
执行流程代码
- 代码流程从发布-执行-完成-结束任务
@GetMapping("/publishList")
public AjaxResult publishList() {
List<ProcessDefinition> list = repositoryService
.createProcessDefinitionQuery()
.orderByProcessDefinitionVersion()
.desc()
.list();
for (ProcessDefinition item : list) {
System.out.println("========");
System.out.println(item.getDeploymentId());
System.out.println(item.getId());
System.out.println(item.getKey());
System.out.println(item.getName());
}
return AjaxResult.success();
}
/**
* 开启流程
*
* @return
*/
@GetMapping("/startProcess")
public AjaxResult startProcess(@RequestParam("key") String key) {
ProcessInstance processInstance = runtimeService
.startProcessInstanceByKey(key);
if (processInstance != null) {
System.out.println("=============");
System.out.println(processInstance.getName());
System.out.println(processInstance.getProcessDefinitionKey());
System.out.println(processInstance.getId());
}
return AjaxResult.success();
}
/**
* 查询正在运行的流程列表
*
* @return
*/
@GetMapping("/runningList")
public AjaxResult runningList(@RequestParam("instanceId") String instanceId,
@RequestParam("key") String key) {
List<ProcessInstance> list = runtimeService
.createProcessInstanceQuery()
.processInstanceId(instanceId)
.processDefinitionKey(key)
.list();
list.forEach(item -> {
System.out.println("============");
System.out.println(item.getBusinessKey());
System.out.println(item.getDeploymentId());
System.out.println(item.getStartTime());
System.out.println(item.getProcessDefinitionKey());
System.out.println(item.getName());
});
return AjaxResult.success();
}
/**
* 查询任务并且完成
*
* @param instanceId
* @param key
* @param assignee
* @return
*/
@GetMapping("accomplishTask")
public AjaxResult accomplishTask(@RequestParam("instanceId") String instanceId,
@RequestParam("key") String key,
@RequestParam("assignee") String assignee) {
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskAssignee(assignee)
.processInstanceId(instanceId)
.list();
// 设置任务id
// taskService.setVariableLocal("862e5029-31e4-11ed-ab1a-005056c00008", "num", "4");
// executionId 就当是processInstanceId
// runtimeService.setVariableLocal("862b1bd5-31e4-11ed-ab1a-005056c00008", "num", 3);
for (int i = 0; i < list.size(); i++) {
Task task = list.get(i);
// taskService.complete(task.getId());
System.out.println("==========");
System.out.println(task.getId());
System.out.println(task.getName());
System.out.println(task.getAssignee());
System.out.println(task.getProcessDefinitionId());
System.out.println(task.getProcessInstanceId());
System.out.println(task.getTaskDefinitionKey());
System.out.println(task.getExecutionId());
}
return AjaxResult.success();
}
/**
* 判断进程是否结束
*
* @param instanceId
* @return
*/
@GetMapping("ProcessFinish")
public AjaxResult ProcessFinish(@RequestParam String instanceId) {
HistoricProcessInstance historicProcessInstance = historyService
.createHistoricProcessInstanceQuery()
.processInstanceId(instanceId)
.singleResult();
if (historicProcessInstance != null) {
if (historicProcessInstance.getEndTime() != null) {
System.out.println("流程已结束!");
} else {
System.out.println("流程未结束!");
}
} else {
System.out.println("流程不存在!");
}
return AjaxResult.success();
}
/**
* 查看审批进程
*
* @param instanceId
* @return
*/
@GetMapping("viewApproval")
public AjaxResult viewApproval(@RequestParam String instanceId) {
// 历史节点
List<HistoricActivityInstance> list = historyService
.createHistoricActivityInstanceQuery()
.processInstanceId(instanceId)
// .orderByActivityId()
// .desc()
.orderByHistoricActivityInstanceEndTime()
.asc()
.finished()
// .unfinished()
.list()
.stream()
.filter(item -> !StringUtils.containsAny(item.getActivityType(), "inclusiveGateway", "parallelGateway"))
.collect(Collectors.toList());
// 历史变量
for (int i = 0; i < list.size(); i++) {
System.out.println("========");
HistoricActivityInstance historicActivityInstance = list.get(i);
// System.out.println(historicActivityInstance.getActivityId()); // _2
System.out.println(historicActivityInstance.getActivityName());
String taskId = historicActivityInstance.getTaskId();
System.out.println(taskId);
String result = "";
if (StringUtils.isNotEmpty(taskId)) {
HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery().taskId(taskId).variableName("result").singleResult();
result = (String) historicVariableInstance.getValue();
// 已经经过的节点不适用该方法查询变量
// result = (String) taskService.getVariableLocal(taskId, "result");
}
System.out.println(historicActivityInstance.getAssignee() + "----" + result);
System.out.println(historicActivityInstance.getStartTime());
System.out.println(historicActivityInstance.getEndTime());
// System.out.println("ExecutionId:" + historicActivityInstance.getExecutionId());
// System.out.println("ProcessInstanceId:" + historicActivityInstance.getProcessInstanceId());
// System.out.println(historicActivityInstance.getActivityType()); // userTask
}
return AjaxResult.success();
}
/**
* 申请任务
* @param key
* @param assignee
* @return
*/
@GetMapping("applyTask")
public AjaxResult applyTask(@RequestParam("key") String key,
@RequestParam("assignee") String assignee){
if (StringUtils.isBlank(key)) {
throw new RuntimeException("key不能为空!");
}
if (StringUtils.isBlank(assignee)) {
throw new RuntimeException("assignee不能为空!");
}
Task task = taskService.createTaskQuery()
.taskCandidateUser(assignee)
// .taskId(taskId)
.processInstanceBusinessKey(key)
.singleResult();
if (task != null) {
taskService.claim(task.getId(), assignee);
log.info(assignee + "申领到了任务!");
}
return AjaxResult.success();
}
/**
* 放弃任务
* @param key
* @param assignee
* @return
*/
@GetMapping("abandonTask")
public AjaxResult abandonTask(@RequestParam("key") String key,
@RequestParam("assignee") String assignee){
if (StringUtils.isBlank(key)) {
throw new RuntimeException("businessKey不能为空!");
}
if (StringUtils.isBlank(assignee)) {
throw new RuntimeException("assignee不能为空!");
}
Task task = taskService.createTaskQuery()
.taskAssignee(assignee)
// .taskId(taskId)
.processInstanceBusinessKey(key)
.singleResult();
if (task != null) {
taskService.setAssignee(task.getId(), null);
log.info(assignee + "放弃了任务!");
}
return AjaxResult.success();
}