文章目录
环境准备
-
idea中下载插件 注意idea 2019前和2019之后插件是不一样的
2019及之前版本插件叫做 actiBPM:
(或者去idea官网下载插件 然后在idea中手动导入)2019之后版本插件 Activiti BPMN visualizer (actiBPM已经不适用了):
在插件marketplace里面搜 如果搜不到就去官网找插件 然后手动导入
新版的activiti7 必须使用jdk11编译
下载activiti7 maven依赖时 需要将setting.xml中的第三方mirror去除(如阿里,网易163等)
先将节点都备份删除,否则会下载不成功项目中须存在bean: userDetailsService (spring-security)
导入activiti的核心jar包:
不同的activiti版本 api会有差异 导入也有所不同
activiti7:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-dependencies</artifactId>
<version>7.1.0-M15</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>activiti-releases</id>
<url>https://artifacts.alfresco.com/nexus/content/repositories/activiti-releases</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
<!-- activiti 包, 只需在yml配置activiti 会自动创建表-->
<!-- 注意 activiti7 把image(样式相关)包独立出去了-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
</dependency>
</dependencies>
例如activiti6的导入:
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>6.0.0</version>
</dependency>
yml核心配置:
spring:
activiti:
# flase: 默认值。activiti在启动时,会对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常。(生产环境常用)
# true: activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建。(开发时常用)
#create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)。(单元测试常用)
# drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)。
database-schema-update: true
# 检查process bpmn图文件是否存在 (为true时 不存在会启动报错)
check-process-definitions: true
# 指定bpmn路径 classpath: resources下的相对路径
process-definition-location-prefix: classpath:/processes/
#activiti的历史记录级别分为以上四种:none, activity, audit, full
# 级别分别由低到高能够显示不同的日志级别信息:
# none: 不记录历史流程,性能高,流程结束后不可读取
# activiti: 归档流程实例和活动实例,流程变量不同步
# audit: 默认值,在activiti基础上同步变量值,保存表单属性
# full: 性能较差,记录所有实例和变量细节变化
history-level: full
#开启历史记录,activiti7 默认为false,不生成历史记录相关的表
db-history-used: true
project:
manifest:
file:
# activiti7的官方bug: 修改bpmn文件后 不会重新加载
# 通过创建一个default-project.json
#修改其中的版本号 来表示bpmn文件已修改
path: classpath:/default-project.json
default-project.json:
{
"createdBy": "superadminuser",
"creationDate": "2019-08-16T15:58:46.056+0000",
"lastModifiedBy": "qa-modeler-1",
"lastModifiedDate": "2019-08-16T16:03:41.941+0000",
"id": "c519a458-539f-4385-a937-2edfb4045eb9",
"name": "projectA",
"description": "",
"version": "17"
}
创建bpmn图:
idea2019及之前使用 .bpmn后缀,
idea2020开始 (Activiti BPMN visualizer 插件) 使用 .bpmn20.xml 后缀
.bpm 文件 安装插件后(需重启idea)示例:
.bpmn20.xml 文件 安装插件后示例:
对着 .bpmn20.xml文件右键 找到菜单的最下面
可以在图中 单机鼠标右键画图
有的时候插件会出现bug 可以选择手动改配置:
- bpmn图配置示例:
<?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 唯一标识,与bpmn图保持一致 -->
<process id="test" name="申请工程流程" isExecutable="true">
<!-- 配置流程开始监听 流程结束监听 注意class指向的是我们自己写的监听类 实现ExecutionListener接口-->
<extensionElements>
<activiti:executionListener class="com.bzkj.activiti.listener.ProcessInstanceStartExecutionListener" event="start"/>
<activiti:executionListener class="com.bzkj.activiti.listener.ProcessInstanceEndExecutionListener" event="end"/>
</extensionElements>
<startEvent id="sid-1939d7e3-1592-4272-b396-dcf420245be5" name="开始事件"/>
<userTask activiti:assignee="${applier}" id="sid-49b6d8da-6b72-44bf-a95f-15e5cd140945" name="用户申请"/>
<!-- activiti:candidateUsers 动态候选人 例如用于部门每个人都可以执行审批的场景 -->
<!-- activiti:formKey 可以作为一个全局固定参数传入 即使在任务结束后也可以取出 但是一个流程中的多个任务 第一个任务把formkey放到map参数中传入 第二个任务即使把不同的formkey传入 也不会更新 -->
<userTask activiti:candidateUsers="${testCandidateUser.getCandidateUsers()}" activiti:formKey="${formKey}" id="sid-87f74448-c714-4b6f-8823-4881fa7d7399" name="审批阶段">
<!-- 任务监听 我们自己写的类 要实现TaskListener接口 -->
<extensionElements>
<activiti:taskListener event="complete" class="com.bzkj.activiti.listener.CustomTaskListener" />
</extensionElements>
</userTask>
<exclusiveGateway id="sid-3ef94eba-2a14-4847-9698-6d114688ce1e">
<!-- 网关监听 实现ExecutionListener -->
<extensionElements>
<activiti:executionListener event="start" class="com.bzkj.activiti.listener.ExclusiveGatewayListener">
</activiti:executionListener>
</extensionElements>
</exclusiveGateway>
<endEvent id="sid-ba466cd8-0b3f-4c61-bd0e-bbb4290ad300"/>
<sequenceFlow id="sid-51f3dcf7-7a76-4e9b-b4fd-73e7c330e87f" sourceRef="sid-1939d7e3-1592-4272-b396-dcf420245be5" targetRef="sid-49b6d8da-6b72-44bf-a95f-15e5cd140945"/>
<sequenceFlow id="sid-1a6cbbdc-b0fb-4371-a54d-456a23f99ecb" sourceRef="sid-49b6d8da-6b72-44bf-a95f-15e5cd140945" targetRef="sid-87f74448-c714-4b6f-8823-4881fa7d7399"/>
<sequenceFlow id="sid-360a1ab3-274f-4e9a-a066-b69dd8f63a62" sourceRef="sid-87f74448-c714-4b6f-8823-4881fa7d7399" targetRef="sid-3ef94eba-2a14-4847-9698-6d114688ce1e"/>
<sequenceFlow id="sid-17e8c00e-8a74-4193-96e9-c4a7ed3ff73e" sourceRef="sid-3ef94eba-2a14-4847-9698-6d114688ce1e" targetRef="sid-ba466cd8-0b3f-4c61-bd0e-bbb4290ad300" name="拒绝">
<!-- ${approve==0}排他网关的判断式-->
<conditionExpression xsi:type="tFormalExpression">${approve==0}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="sid-55f41ef7-ed89-4104-9df8-4c6d580712c3" sourceRef="sid-3ef94eba-2a14-4847-9698-6d114688ce1e" targetRef="sid-ba466cd8-0b3f-4c61-bd0e-bbb4290ad300" name="同意">
<conditionExpression xsi:type="tFormalExpression">${approve==1}</conditionExpression>
</sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_workapply">
<bpmndi:BPMNPlane bpmnElement="workapply" id="BPMNPlane_workapply">
<bpmndi:BPMNShape id="shape-204461bd-dc07-4a33-b323-5825c888cdd4" bpmnElement="sid-1939d7e3-1592-4272-b396-dcf420245be5">
<omgdc:Bounds x="-215.0" y="-59.999996" width="30.0" height="30.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-6eadb2d1-2d9c-4aca-8ffc-fb496f0848e1" bpmnElement="sid-49b6d8da-6b72-44bf-a95f-15e5cd140945">
<omgdc:Bounds x="-115.0" y="-75.0" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-f1433e2e-9ef6-41ed-8d45-b000aa333acc" bpmnElement="sid-87f74448-c714-4b6f-8823-4881fa7d7399">
<omgdc:Bounds x="40.0" y="-75.0" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-a9cc5a7d-ef67-48f3-a98d-844e1aa0a2bb" bpmnElement="sid-3ef94eba-2a14-4847-9698-6d114688ce1e">
<omgdc:Bounds x="180.0" y="-54.999996" width="40.0" height="40.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-40a581a2-6bbe-4461-8165-21fc5971a4eb" bpmnElement="sid-ba466cd8-0b3f-4c61-bd0e-bbb4290ad300">
<omgdc:Bounds x="265.0" y="-50.0" width="30.0" height="30.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="edge-212c5a8f-5434-4b14-aaab-ed94dd3a7f76" bpmnElement="sid-51f3dcf7-7a76-4e9b-b4fd-73e7c330e87f">
<omgdi:waypoint x="-185.0" y="-37.499996"/>
<omgdi:waypoint x="-115.0" y="-35.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-d322feb0-d572-4593-a4d9-e976149cc4b8" bpmnElement="sid-1a6cbbdc-b0fb-4371-a54d-456a23f99ecb">
<omgdi:waypoint x="-15.0" y="-35.0"/>
<omgdi:waypoint x="40.0" y="-35.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-e890fa36-c2b3-4e00-8700-26633853de0e" bpmnElement="sid-360a1ab3-274f-4e9a-a066-b69dd8f63a62">
<omgdi:waypoint x="140.0" y="-35.0"/>
<omgdi:waypoint x="180.0" y="-34.999996"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-90537cb5-2c46-4eda-984a-bf6841ae181d" bpmnElement="sid-17e8c00e-8a74-4193-96e9-c4a7ed3ff73e">
<omgdi:waypoint x="220.0" y="-34.999996"/>
<omgdi:waypoint x="219.99998" y="35.000008"/>
<omgdi:waypoint x="247.5" y="35.000004"/>
<omgdi:waypoint x="280.0" y="25.000002"/>
<omgdi:waypoint x="287.5" y="-20.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-40d316ee-8717-4dfa-ad6d-9809274a8ad8" bpmnElement="sid-55f41ef7-ed89-4104-9df8-4c6d580712c3">
<omgdi:waypoint x="200.0" y="-54.999996"/>
<omgdi:waypoint x="252.5" y="-108.75"/>
<omgdi:waypoint x="265.0" y="-42.5"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
任务监听代码示例:
@Slf4j
public class CustomTaskListener implements TaskListener {
// 存放回调方法 方法可以在项目初始化时注册进来
public static ConcurrentMap<String, Function> processTaskMap = new ConcurrentHashMap<>();
@Override
public void notify(DelegateTask task) {
// 转型
TaskEntityImpl delegateTask = (TaskEntityImpl) task;
// 获取回调方法
Function function = processTaskMap.get(ActivitiConsts.SAVE_DETAILS_TABLE);
ActHistoryDetailsBusinessDO detailsBusinessDO = new ActHistoryDetailsBusinessDO();
detailsBusinessDO.setAssignee(delegateTask.getAssignee());
// 回调实现业务
function.apply(detailsBusinessDO);
log.info("任务完成了,实例id是:" + delegateTask.getProcessInstanceId());
}
}
activiti核心api:
RuntimeService :运行时服务
例如开启一个新流程:
@Autowired
private RuntimeService runservice;
// processDefinitionKey 是我们自己定义的 取bpmn图的名字即可,map是我们要传入流程的参数
runservice.startProcessInstanceByKey
(processDefinitionKey, map);
// 或
runservice.startProcessInstanceByKey
(processDefinitionKey, businessKey, map);
再例如运行时查询 (流程结束就没了):
String processInstanceId = xxxx;
runservice.createProcessInstanceQuery().processInstanceId(processInstanceId).list();
TaskService: 任务服务
例如执行任务:
@Autowried
private TaskService taskservice;
............
// assignee用来 设置任务执行人
taskservice.claim(taskId, assignee);
// map: 任务参数
taskservice.complete(taskId, map);
查询任务:
public List<? extends ActivitiTaskVO> getTaskList(ActivitiTaskQuery queryBase) {
TaskQuery taskQuery = taskservice.createTaskQuery();
if (CheckUtils.isNotEmpty(queryBase.getProcessDefinitionKey())) {
taskQuery = taskQuery.processDefinitionKey(queryBase.getProcessDefinitionKey());
}
if (CheckUtils.isNotEmpty(queryBase.getCandidateUserId())) {
taskQuery = taskQuery.taskCandidateOrAssigned(queryBase.getCandidateUserId());
}
if (CheckUtils.isNotEmpty(queryBase.getProcessInstanceId())) {
taskQuery.processInstanceId(queryBase.getProcessInstanceId());
}
List<Task> taskList = taskQuery.list();
return taskList;
}
HistoryService 历史流程服务:
主要是用来查询历史相关信息的(比如已结束的任务、流程)
formService 表单服务,activiti7开始已移除 旧版本可以使用:
@Autowired
private FormService formService;
......
// 提交表单任务
formService.submitTaskFormData(taskId, fromValues);