一、activiti依赖及工具
1、springboot依赖
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.0.0.Beta2</version>
<exclusions>
<exclusion>
<artifactId>mybatis</artifactId>
<groupId>org.mybatis</groupId>
</exclusion>
<exclusion>
<artifactId>el-api</artifactId>
<groupId>javax.el</groupId>
</exclusion>
</exclusions>
</dependency>
2、下载idea相关插件
二、配置activiti
1、参数相关介绍如下
spring:
activiti:
#自动更新数据库结构
#1.flase:默认值。activiti在启动时,对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常
#2.true: activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建
#3.create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)
#4.drop-create: 在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/holliday/
#process-definition-location-suffixes: #流程文件格式
# - **.bpmn20.xml
# - **.bpmn
2、创建流程文件
创建新流程文件默认在此目录下,当然也可以在yml文件中通过配置process–definition–location–prefix指定位置。
3、创建新流程文件
4、springboot排除springSecurity自动装配
@SpringBootApplication(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
//@SpringBootApplication
@MapperScan(basePackages = "com.jt.dao")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
5、增加config跳过springSecurity验证
此config仅仅是为了跳过springSecurity验证
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* @author jiangtao
* 该配置类仅仅为了跳过spring-security的验证
*/
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
}
6、启动项目
启动该springboot项目,会在mysql中创建25张表
三、画流程图
<?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="leaveDemo" name="leaveDemo" isExecutable="true">
<userTask id="_3" name="secondUser" activiti:assignee="李四"/>
<startEvent id="_1" name="startEvent"/>
<endEvent id="_4" name="endEvent"/>
<userTask id="_2" name="firstUser" activiti:assignee="张三"/>
<sequenceFlow id="sid-f594c092-ac67-40ee-a41d-ce952d8844ad" sourceRef="_1" targetRef="_2"/>
<sequenceFlow id="sid-c685f76f-b3a2-4edd-8a58-f8b8c055a070" sourceRef="_2" targetRef="_3"/>
<sequenceFlow id="sid-145ed254-20fd-47d3-9c08-1f545b99f35b" sourceRef="_3" targetRef="_4"/>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_leave">
<bpmndi:BPMNPlane bpmnElement="leaveDemo" id="BPMNPlane_leave">
<bpmndi:BPMNShape id="shape-3c81f674-87f0-4fea-8040-7700dbb20a94" bpmnElement="_3">
<omgdc:Bounds x="44.04801" y="80.552" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-0a797082-f4d4-4378-a571-751e7a94fcda" bpmnElement="_1">
<omgdc:Bounds x="60.385612" y="-149.3312" width="30.0" height="30.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-938cb97e-5123-433f-9822-4cfb66208a80" bpmnElement="_4">
<omgdc:Bounds x="69.5556" y="206.69162" width="30.0" height="30.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-0eef7a46-93d8-4571-aba2-fa4524294cd1" bpmnElement="_2">
<omgdc:Bounds x="34.555603" y="-69.0972" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="edge-6965c761-2b2c-4a73-a3bd-5aa9fa568784" bpmnElement="sid-f594c092-ac67-40ee-a41d-ce952d8844ad">
<omgdi:waypoint x="75.38561" y="-119.33121"/>
<omgdi:waypoint x="84.5556" y="-69.0972"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-2f655aef-dfd0-415d-9e2b-c4730cc74b73" bpmnElement="sid-c685f76f-b3a2-4edd-8a58-f8b8c055a070">
<omgdi:waypoint x="84.5556" y="10.9028015"/>
<omgdi:waypoint x="84.5556" y="80.552"/>
<omgdi:waypoint x="94.04801" y="80.552"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-424dfd94-8841-44ca-bfbf-2542b172bfc2" bpmnElement="sid-145ed254-20fd-47d3-9c08-1f545b99f35b">
<omgdi:waypoint x="94.04801" y="160.552"/>
<omgdi:waypoint x="92.0556" y="206.69162"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
四、开启流程并运行
1、查看所有已经发布的流程定义
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());
}
2、开启流程
String processDefinitionKey = "candidateUser";
Map<String, Object> map = new HashMap<>();
map.put("num", 2);
ProcessInstance processInstance = runtimeService
.startProcessInstanceByKey(processDefinitionKey);
if (processInstance != null) {
System.out.println("=============");
System.out.println(processInstance.getName());
System.out.println(processInstance.getProcessDefinitionKey());
System.out.println(processInstance.getId());
}
3、查询已经启动的流程实例
List<ProcessInstance> list = runtimeService
.createProcessInstanceQuery()
.processInstanceId("e7f7f241-317e-11ed-8ab0-005056c00008")
.processDefinitionKey("leave")
.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());
});
4、查询正在执行的任务
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey("varAndParaGateway")
// .taskAssignee("张三")
// .taskId("d9cc9362-3124-11ed-8715-005056c00008")
// .processInstanceId("cca885a4-31a3-11ed-a478-005056c00008")
.list();
for (int i = 0; i < list.size(); i++) {
Task task = list.get(i);
System.out.println("===========");
System.out.println(task.getProcessDefinitionId());
System.out.println(task.getProcessInstanceId());
System.out.println(task.getAssignee());
System.out.println(task.getName());
System.out.println(task.getId());
}
5、完成个人任务
String assignee = "张三";
String processInstanceId = "3dc5fbc4-3249-11ed-bf5a-005056c00008";
String processDefinitionKey = "gateway1";
List<Task> list = taskService.createTaskQuery()
// .processDefinitionKey(processDefinitionKey)
// .taskAssignee(assignee)
.processInstanceId(processInstanceId)
.list();
// 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());
}
6、拒绝个人任务
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
// .processInstanceId(processInstanceId)
.processInstanceBusinessKey(businessKey)
.singleResult();
if (processInstance != null) {
runtimeService.deleteProcessInstance(processInstance.getId(), endReason);
log.info("流程删除成功!");
} else {
throw new ProcessNotFoundException("流程不存在!");
}
7、判断流程是否结束
HistoricProcessInstance historicProcessInstance = historyService
.createHistoricProcessInstanceQuery()
.processInstanceId("d8c1137a-31bf-11ed-aee8-005056c00008")
.singleResult();
if (historicProcessInstance != null) {
if (historicProcessInstance.getEndTime() != null) {
System.out.println("流程已结束!");
} else {
System.out.println("流程未结束!");
}
} else {
System.out.println("流程不存在!");
}
8、查询审批过程
// 历史节点
List<HistoricActivityInstance> list = historyService
.createHistoricActivityInstanceQuery()
// .processInstanceId("5e787d34-31a6-11ed-95b6-005056c00008")
.processInstanceId("70ebc1e0-3312-11ed-b4b0-005056c00008")
// .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
}
9、申领任务
// 获取当前用户
String candidateUser = ActivitiUtils.getActivitiUser();
if (StringUtils.isBlank(candidateUser)) {
throw new RuntimeException("candidateUser不能为空!");
}
if (StringUtils.isBlank(businessKey)) {
throw new RuntimeException("businessKey不能为空!");
}
Task task = taskService.createTaskQuery()
.taskCandidateUser(candidateUser)
// .taskId(taskId)
.processInstanceBusinessKey(businessKey)
.singleResult();
if (task != null) {
taskService.claim(task.getId(), candidateUser);
log.info(candidateUser + "申领到了任务!");
return true;
}
return false;
10、放弃申领到的任务
// 获取当前用户
String assignee = ActivitiUtils.getActivitiUser();
if (StringUtils.isBlank(businessKey)) {
throw new RuntimeException("businessKey不能为空!");
}
if (StringUtils.isBlank(assignee)) {
throw new RuntimeException("assignee不能为空!");
}
Task task = taskService.createTaskQuery()
.taskAssignee(assignee)
// .taskId(taskId)
.processInstanceBusinessKey(businessKey)
.singleResult();
if (task != null) {
taskService.setAssignee(task.getId(), null);
log.info(assignee + "放弃了任务!");
}
五、动态指定审批人
1、EL表达式动态指定审批人
在bpmn文件中借助EL表达式,并通过变量赋值,达到动态指定审批人的效果。
以下是流程图,审批人处并没有直接指定相应的审批人,而是通过EL表达式站位。
对应的xml文件:
<process id="varDemo" name="varDemo" isExecutable="true">
<startEvent id="_1"/>
<userTask id="_2" name="一级审批" activiti:assignee="${user1}"/>
<userTask id="_3" name="二级审批" activiti:assignee="${user2}"/>
<endEvent id="_4"/>
<sequenceFlow id="sid-fba31a25-c2aa-487d-9ae5-89c0e2e04524" sourceRef="_1" targetRef="_2"/>
<sequenceFlow id="sid-8576ba0f-6a70-4ee2-b940-9a336015b744" sourceRef="_2" targetRef="_3"/>
<sequenceFlow id="sid-e4ff88d6-62e7-42bc-8ef9-a981f31d0f3e" sourceRef="_3" targetRef="_4"/>
</process>
对应的一二级审批人为变量user1和user2。通过下面api完成当前任务节点并填充变量。
HashMap<String, Object> map = new HashMap<>();
map.put("user2", "王五");
taskService.complete(task.getId(), map, true);
2、EL+javaBean动态指定候选人
候选人概念:候选人和审批人的区别在于候选人只有领取到了任务才能审批任务,否则不能审批任务。常用场景是一个节点多人审批,其中一个人审批通过则该节点通过。
对应的xml文件:
<process id="candidateUser" name="candidateUser" isExecutable="true">
<startEvent id="_1" name="开始"/>
<userTask id="_2" name="一级审批" activiti:candidateUsers="${authService.getCandidateUsers()}"/>
<endEvent id="_3" name="结束"/>
<sequenceFlow id="sid-f1c43cee-5b56-4a07-94fe-25d9a271a855" sourceRef="_1" targetRef="_2"/>
<sequenceFlow id="sid-bcd5f698-98b4-4153-bd8e-fcc10b141ec5" sourceRef="_2" targetRef="_3"/>
</process>
创建java类,定义方法,方法逻辑为候选人选取逻辑,方法返回list。
@Service
public class AuthService implements Serializable {
public List<String> getCandidateUsers() {
List<String> users = new ArrayList<>();
users.add("小二");
users.add("小三");
users.add("小四");
users.add("小五");
return users;
}
候选人领取任务:
// candidateUser为String类型
taskService.claim(task.getId(), candidateUser);
六、网关
1、排他网关
排他网关的特点就是流程只能根据条件走一条流程线,例如:
该审批流程只能经选择二级审批一或者二级审批二其中一条,排他网关不需要新的网关去汇总分支。
2、并行网关
并行网关顾名思义,经过该网关时所有流程都会被触发,例如:
该网关需要一个并行网关进行汇总,否则就就会在一次网关分流之后各自进行流程,也就是并行网关之后的节点会被执行两次。
3、包含网关
该网关是排他网关和并行网关的结合,该网关与排他网关不同的是,排他网关只能执行满足条件一条线的流程,而包含网关可以执行所有满足条件的流程;包含网关与并行网关不同的是,包含网关支持设置条件进行限制可以经过哪些流程,并不是所有线的流程都可以被执行。同样的,为了避免经过网关之后产生不同的分支,也是需要相同类型的网关进行汇总。
七、避坑点
1. 如果排他网关设置条件会怎么样?
不设置条件,并不影响程序执行,不过在经过网关时,会选择xml中最先出现sequenceFlow那条线,所以在使用排他网关时,最好设置上条件。
2. processInstance和execution的联系是什么?
如下两张图片最直观表现他们的关系,一个流程实例会对应一个父级execution,然后真正走实例流程的execution是另外生成的,是父级execution的子executiion。
3. taskService.setVariableLocal 和 runtimeService.setVariableLocal的区别?
前者的作用域是当前execution的当前节点上,且数据库当前数据行的task_id有数据,且经过测试,变量仅用于该节点,不能用于节点后面的流程线;后者的作用域是当前execution的所有节点上。
4. taskService.setVariable 和 runtimeService.setVariable的区别?
前者是如果当前流程变量中没有没有此变量,则默认是最外层execution,即全局变量,如果流程中有此变量,测覆盖此变量,作用域范围不变;后者是最外层execution,也是全局变量。
5. 没有到达任务节点,会不会对后面的节点进行赋值?赋值之后改变流程变量会不会改变已经赋值的节点的值?
到达二级审批之前,尽管变量中有user2的信息,是不会给user2赋值的,只有到达二级审批节点的时候才会给user2赋值;并且已经给user1赋值之后改变流程变量并不会改变user1的值。
原文地址:https://blog.csdn.net/java_eiji/article/details/126815986
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_14549.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!