Activiti7_使用
一、Activiti7
- 为了实现后端的咨询流转功能,学习Activiti7,记录下使用的过程及遇到的问题
二、绘制工作流
- 使用spring3的后端项目为基础,开发工具idea 2023.3.2,jdk,jdk17。
点此链接link去idea官网下载插件Activiti BPMN,在idea的Settings里Plugins使用install plugin from disk把刚才的jar包导进去 - 在bpmn文件夹下创建bpmn文件:
! - 使用插件以画图形式编辑bpmn:
主要使用了三种部件:开始、任务、结束
任务的内容填写如下,连线按住右上角的箭头拖拽到目标即可,里面的${user}表明设置为user的变量,一会可以在使用的时候绑定用户:
其中的分支路线我们直接使用两个互斥条件判断,不使用排他网关:
排他网关(也叫异或(XOR)网关,或叫基于数据的排他网关),用来在流程中实现决策。 当流程执行到这个网关,所有分支都会判断条件是否为 true,如果为 true 则执行该分支,注意,排他网关只会选择一个为 true 的分支执行。 (即使有两个分支条件都为 true, 排他网关也会只选择一条分支去执行)。如果从网关出去的线所有条件都不满足则系统抛出异常。说明 :经过排他网关必须要有一条且只有一条分支走。
bpmn文件绘制完成了,保存成同名png文件:
- 完整的bpmn代码如下:
xml
<?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="consult" name="consult" isExecutable="true">
<startEvent id="sid-c41ecd07-74cd-41d5-9d7e-046bf71954c1"/>
<userTask id="sid-7e47fd72-c056-43f6-8575-c229ff5b58b4" name="用户填写咨询表单" activiti:assignee="${user}">
<documentation>用户填写咨询表单</documentation>
</userTask>
<userTask id="sid-03f0aec0-32b1-41e2-8826-5b8c73d51854" name="管理员指定业务人员" activiti:assignee="${admin}">
<documentation>管理员指定业务人员</documentation>
</userTask>
<userTask id="sid-7b539102-6d65-479a-9958-5259a84c28b1" name="业务人员回复" activiti:assignee="${busPerson}">
<documentation>业务人员回复</documentation>
</userTask>
<userTask id="sid-3af84618-4ba9-4686-a97f-6db4da96e916" name="管理员决定是否公示" activiti:assignee="${admin}">
<documentation>管理员决定是否公示</documentation>
</userTask>
<endEvent id="sid-bd329163-e466-4ca7-bad1-9b95787e28a6"/>
<sequenceFlow id="sid-c2da4d94-8eac-4acd-9cb0-16328237a136" sourceRef="sid-c41ecd07-74cd-41d5-9d7e-046bf71954c1" targetRef="sid-7e47fd72-c056-43f6-8575-c229ff5b58b4"/>
<sequenceFlow id="sid-5be6635f-b5ef-4e0e-98af-1b35c6e085ae" sourceRef="sid-7e47fd72-c056-43f6-8575-c229ff5b58b4" targetRef="sid-03f0aec0-32b1-41e2-8826-5b8c73d51854"/>
<sequenceFlow id="sid-5dc1e8b5-e2bc-45b7-b559-bffe2d3b5c24" sourceRef="sid-03f0aec0-32b1-41e2-8826-5b8c73d51854" targetRef="sid-7b539102-6d65-479a-9958-5259a84c28b1"/>
<sequenceFlow id="sid-c3c3594c-d7d1-462f-b057-59b12dbeb190" sourceRef="sid-3af84618-4ba9-4686-a97f-6db4da96e916" targetRef="sid-bd329163-e466-4ca7-bad1-9b95787e28a6"/>
<sequenceFlow id="sid-8a860827-e9ad-4c62-b931-939d312ddcd8" sourceRef="sid-7b539102-6d65-479a-9958-5259a84c28b1" targetRef="sid-03f0aec0-32b1-41e2-8826-5b8c73d51854">
<conditionExpression>${evection.num>1}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="sid-f4dc4277-d16a-415e-9cac-fe51d1c6ee65" sourceRef="sid-7b539102-6d65-479a-9958-5259a84c28b1" targetRef="sid-3af84618-4ba9-4686-a97f-6db4da96e916">
<conditionExpression>${evection.num<=1}</conditionExpression>
</sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_consult">
<bpmndi:BPMNPlane bpmnElement="consult" id="BPMNPlane_consult">
<bpmndi:BPMNShape id="shape-b1791cfb-c3c9-42a9-9bd8-b7e0a8906f99" bpmnElement="sid-c41ecd07-74cd-41d5-9d7e-046bf71954c1">
<omgdc:Bounds x="-15.0" y="-120.0" width="30.0" height="30.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-33324ea7-5905-4b2a-b615-2bfe666a0117" bpmnElement="sid-7e47fd72-c056-43f6-8575-c229ff5b58b4">
<omgdc:Bounds x="-50.0" y="-60.0" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-955afe5f-f9b7-40f0-887f-70aeb5afc173" bpmnElement="sid-03f0aec0-32b1-41e2-8826-5b8c73d51854">
<omgdc:Bounds x="-50.0" y="40.0" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-7a2e9e19-c268-4a46-af9c-aa6621dd9b86" bpmnElement="sid-7b539102-6d65-479a-9958-5259a84c28b1">
<omgdc:Bounds x="-50.0" y="150.0" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-86aead71-8e5b-4de3-8d8c-ca5f0dcc63ea" bpmnElement="sid-3af84618-4ba9-4686-a97f-6db4da96e916">
<omgdc:Bounds x="-50.0" y="255.0" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-1d13b250-9f5c-42db-a745-482b2c9acf56" bpmnElement="sid-bd329163-e466-4ca7-bad1-9b95787e28a6">
<omgdc:Bounds x="-15.0" y="360.0" width="30.0" height="30.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="edge-c60fd374-56ca-4630-9f88-c9d61baa048b" bpmnElement="sid-c2da4d94-8eac-4acd-9cb0-16328237a136">
<omgdi:waypoint x="0.0" y="-90.0"/>
<omgdi:waypoint x="0.0" y="-60.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-4a1da47a-a9b5-4db2-a8a9-002220e5c52a" bpmnElement="sid-5be6635f-b5ef-4e0e-98af-1b35c6e085ae">
<omgdi:waypoint x="0.0" y="20.0"/>
<omgdi:waypoint x="0.0" y="40.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-3d9c4ead-a6cd-4aa2-82c7-ad1f20171327" bpmnElement="sid-5dc1e8b5-e2bc-45b7-b559-bffe2d3b5c24">
<omgdi:waypoint x="0.0" y="120.0"/>
<omgdi:waypoint x="0.0" y="150.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-2116f8bb-51a7-4c43-a48b-7c4954868724" bpmnElement="sid-c3c3594c-d7d1-462f-b057-59b12dbeb190">
<omgdi:waypoint x="0.0" y="335.0"/>
<omgdi:waypoint x="0.0" y="360.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-ce0e6fc3-032f-4333-83ec-7807846b046f" bpmnElement="sid-8a860827-e9ad-4c62-b931-939d312ddcd8">
<omgdi:waypoint x="-50.0" y="190.0"/>
<omgdi:waypoint x="-125.0" y="135.0"/>
<omgdi:waypoint x="-50.0" y="80.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-87fa2036-1930-4551-bede-92f275696c68" bpmnElement="sid-f4dc4277-d16a-415e-9cac-fe51d1c6ee65">
<omgdi:waypoint x="0.0" y="230.0"/>
<omgdi:waypoint x="0.0" y="242.5"/>
<omgdi:waypoint x="0.0" y="255.0"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
三、通过代码部署流程,再对流程进行实例化,完整运行一遍流程即可
需要序列化一个evection:
java
package com.sumo.ipd;
import java.io.Serializable;
public class Evection implements Serializable {
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
private int num;
}
在test里面创建ActivitiDemo类,内容:
java
package com.sumo.ipd;
import org.activiti.engine.*;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ActivitiDemo {
/**
* 部署流程定义测试,查询,删除
*/
@Test
public void DeployTest() {
// 1. 创建ProcessEngine流程引擎对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、得到RepositoryService实例
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3、使用RepositoryService进行部署,定义一个流程名字,把bpmn和png部署到数据库中
Deployment deployment = repositoryService.createDeployment() // 创建一个部署流程
.name("咨询流程") // 给部署流程命名
.addClasspathResource("bpmn/consult.bpmn20.xml") // 添加类路径下的资源文件
.addClasspathResource("bpmn/consult.png")
.deploy(); // 执行流程部署
// 4、输出部署信息
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
@Test
public void queryProceccDefinition() {
// 流程定义key
String processDefinitionKey = "consult";
// 创建ProcessEngine流程引擎对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 查询流程定义
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//遍历查询结果
List<ProcessDefinition> list = processDefinitionQuery.processDefinitionKey(processDefinitionKey).orderByProcessDefinitionVersion().desc().list();
for (ProcessDefinition processDefinition : list) {
System.out.println("------------------------");
System.out.println(" 流 程 部 署 id : " +processDefinition.getDeploymentId());
System.out.println("流程定义id: " + processDefinition.getId());
System.out.println("流程定义名称: " + processDefinition.getName());
System.out.println("流程定义key: " + processDefinition.getKey());
System.out.println("流程定义版本: " + processDefinition.getVersion());
}
}
@Test
public void deleteDeployment() {
// 流程部署id
String deploymentId = "47501";
// 通过流程引擎获取repositoryService
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
// 删除启动的流程实例
RuntimeService runtimeService = processEngine.getRuntimeService();
runtimeService.deleteProcessInstance("50001","用户撤销");
//删除流程定义, 如果该流程定义已有流程实例启动则删除时出错
//repositoryService.deleteDeployment(deploymentId);
//设置true 级联删除流程定义,即使该流程有流程实例启动也可以删除,设
//置为false非级别删除方式,如果流程
repositoryService.deleteDeployment(deploymentId, true);
}
/**
* 启动流程实例
*/
@Test
public void startProcessTest() {
//businessKey用于绑定外部任务表单的id
String businessKey = "1";
// 1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、获取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 流程变量的map
Map<String, Object> variables = new HashMap<>();
// 设定任务的负责人
variables.put("user", "523097198902120431");
variables.put("admin", "123456");
variables.put("busPerson", "123456789");
Evection evection = new Evection();
evection.setNum(3);
// 把流程变量的pojo放入map
variables.put("evection",evection);
// 3、根据流程定义Id启动流程,并获取流程实例
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("consult", businessKey, variables);
// 输出内容
System.out.println("流程定义id:" + processInstance.getProcessDefinitionId());
System.out.println("流程实例id:" + processInstance.getId());
System.out.println("当前活动Id:" + processInstance.getActivityId());
System.out.println("businessKey==" + processInstance.getBusinessKey());
}
/**
* 查询当前个人待执行的任务
*/
public void findTaskListTest(String assignee) {
// 1.创建流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.获取TaskService
TaskService taskService = processEngine.getTaskService();
// 3.根据流程key 和 任务负责人 查询任务
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey("consult") //流程Key
.taskAssignee(assignee) //只查询当前任务负责人的任务
.list();
Task task = taskService.createTaskQuery()
// .processInstanceId("15001") //流程实例id
.processDefinitionKey("consult") //流程Key
.taskAssignee(assignee) //只查询当前任务负责人的任务
.singleResult();
String processInstanceId = task.getProcessInstanceId();
// 2、获取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
String businessKey = processInstance.getBusinessKey();
// list.forEach(task -> {
System.out.println("-----------------------------------------");
System.out.println("流程实例id:" + task.getProcessInstanceId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
System.out.println("businessKey==" + businessKey);
// });
Evection eve = (Evection)taskService.getVariable(task.getId(), "evection");
System.out.println(eve.getNum());
System.out.println("获取流程变量成功");
}
/**
* 完成个人代办任务及查询下一步
*/
@Test
public void completTaskTest() {
findTaskListTest("523097198902120431");
completTask("523097198902120431");
findTaskListTest("123456");
completTask2("123456");
findTaskListTest("123456789");
completTask("123456789");
findTaskListTest("123456");
completTask("123456");
}
/**
* 普通的完成任务
* @param assignee
*/
public void completTask(String assignee) {
// 1.创建流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2. 获取TaskService实例
TaskService taskService = processEngine.getTaskService();
// 3.根据流程key 和 任务的负责人 查询任务
Task task = taskService.createTaskQuery()
.processDefinitionKey("consult") //流程Key
.taskAssignee(assignee) //只查询当前任务负责人的任务
.singleResult();
System.out.println("流程实例id:" + task.getProcessInstanceId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName()+" 完成!");
// 4.完成待办任务,参数:任务id
taskService.complete(task.getId());
}
/**
* 在完成任务时修改变量调整分支走向
* @param assignee
*/
public void completTask2(String assignee) {
// 1.创建流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2. 获取TaskService实例
TaskService taskService = processEngine.getTaskService();
// 3.根据流程key 和 任务的负责人 查询任务
Task task = taskService.createTaskQuery()
.processDefinitionKey("consult") //流程Key
.taskAssignee(assignee) //只查询当前任务负责人的任务
.singleResult();
System.out.println("流程实例id:" + task.getProcessInstanceId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName()+" 完成!");
// 流程变量的map
Map<String, Object> variables = new HashMap<>();
Evection evection = new Evection();
evection.setNum(1);
// 把流程变量的pojo放入map
variables.put("evection",evection);
// 4.完成待办任务,参数:任务id
taskService.complete(task.getId(),variables);
}
/**
* 如果一个流程中有多个任务,可以用list
*/
@Test
public void completeTask3() {
// 获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取操作任务的服务 TaskService
TaskService taskService = processEngine.getTaskService();
// 完成 经理 '李四' 的任务
List<Task> tasks = taskService.createTaskQuery()
.processDefinitionKey("evection")
.taskAssignee("李四")
.list(); // 多个任务,用list
for (Task task : tasks) {
if (true) {
// 判断是哪任务
System.out.println("流程实例id=" + task.getProcessInstanceId());
System.out.println("任务Id=" + task.getId());
System.out.println("任务负责人=" + task.getAssignee());
System.out.println("任务名称=" + task.getName());
taskService.complete(task.getId());
}
}
}
}
运行ActivitiDemo完整运行一遍流程:
1DeployTest部署流程
2startProcessTest启动流程实例
3completTaskTest运行一遍流程
四、在springbooot中使用
依赖会出现报错,需要修改pom.xml
- 使用log4j会和springboot本身的logback冲突,排除logback
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
-
已经有mybatis了,把不用的注释掉
-
排除slf4j-log4j12的log4j,避免和外面的log4j冲突
xml
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
<exclusions><!-- 排除slf4j-log4j12的log4j,避免和外面的log4j冲突 -->
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
</exclusions>
</dependency>