Activiti 是一个开源架构的工作流引擎,基于bpmn2.0 标准进行流程定义。其前身是JBPM,Activiti 通过嵌入到业务系统开发中进行使用。
整合springboot
引入相关依赖
<!-- 引入Activiti7 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M4</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
配置yml
spring:
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/activiti?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&rewriteBatchedStatements=true
username: root
password: 159741
type: com.zaxxer.hikari.HikariDataSource
hikari:
max-lifetime: 120000
# activiti7配置
activiti:
# 自动部署验证设置:true-开启(默认)、false-关闭
check-process-definitions: true
# 保存历史数据
history-level: full
# 检测历史表是否存在
db-history-used: true
# 关闭自动部署,如果不关闭,每次重新启动项目的时候,总是会在 ACT_RE_DEPLOYMENT 自动创建一个名为 SpringAutoDeployment 工作流记录。
deployment-mode: never-fail
# flase: 默认值。activiti在启动时,会对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常。(生产环境常用)
# true: activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建。(开发时常用)
# create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)。(单元测试常用)
# drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)。
database-schema-update: true
# 解决频繁查询SQL问题
async-executor-activate: false
项目主类排除security相关依赖,否则启动会报错
@SpringBootApplication(exclude = {
SecurityAutoConfiguration.class,
ManagementWebSecurityAutoConfiguration.class
})
public class ActivitiApplication {
public static void main(String[] args) {
SpringApplication.run(ActivitiApplication.class, args);
}
}
数据库建名为activiti的库,启动让Activiti表自动生成
由于7.1.0.M4 版本自动生成的表字段不全所以需要添加2个字段
alter table ACT_RE_DEPLOYMENT add column PROJECT_RELEASE_VERSION_ varchar(255) DEFAULT NULL;
alter table ACT_RE_DEPLOYMENT add column VERSION_ varchar(255) DEFAULT NULL;
在resources目录下创建processes文件夹,在该目录下创建自己的bpmn文件,遵循命名规范,项目启动会进行自动部署
手动部署的方式
//提供流程定义和部署等功能。比如说,实现流程的的部署、删除,暂停和激活以及流程的查询等功能
@Autowired
private RepositoryService repositoryService;
//流程部署
public void initDeployment() {
Deployment deployment =repositoryService.createDeployment()
.addClasspathResource("bpmn/Leave.bpmn")
.addClasspathResource("bpmn/Leave.myleave.png")
.name("流程部署测试")
.deploy();
System.out.println("流程部署名称:" + deployment.getName());
System.out.println("流程部署名称:" + deployment.getId());
}
//流程部署zip形式(zip若添加图片类型,则需要遵循命名规范,bpmn/Leave.bpmn,bpmn/Leave.myleave.png)
public void initDeployment() {
InputStream inputStream = this.getClass().getClass().getClassLoader()
.getResourceAsStream("bpmn/Leave.zip");
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
Deployment deployment = repositoryService.createDeployment()
.addZipInputStream(zipInputStream)
.name("流程部署测试")
.deploy();
System.out.println("流程部署名称:" + deployment.getName());
System.out.println("流程部署名称:" + deployment.getId());
}
构建bpmn文件
xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
xmlns:activiti="http://activiti.org/bpmn"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:tns="http://www.activiti.org/test"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
expressionLanguage="http://www.w3.org/1999/XPath"
id="m1686980591933"
name=""
targetNamespace="http://www.activiti.org/test"
typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="myleave" isClosed="false" isExecutable="true" name="员工请假申请流程"
processType="None">
<startEvent id="_2" name="请假流程"/>
<userTask activiti:assignee="${leave.userId}" activiti:exclusive="true" id="_3"
name="员工请假申请"/>
<sequenceFlow id="_4" sourceRef="_2" targetRef="_3"/>
<userTask activiti:assignee="${leave.approver1}" activiti:exclusive="true" id="_7"
name="技术组长"/>
<userTask activiti:assignee="${leave.approver2}" activiti:exclusive="true" id="_8"
name="项目经理"/>
<sequenceFlow id="_10" sourceRef="_3" targetRef="_7"/>
<sequenceFlow id="_11" sourceRef="_7" targetRef="_8"/>
<exclusiveGateway gatewayDirection="Unspecified" id="_12" name="ExclusiveGateway"/>
<sequenceFlow id="_5" sourceRef="_8" targetRef="_12"/>
<userTask activiti:assignee="${leave.approver3}" activiti:exclusive="true" id="_6"
name="总经理"/>
<sequenceFlow id="_9" name="大于等于3天" sourceRef="_12" targetRef="_6">
<conditionExpression xsi:type="tFormalExpression">${leave.num>=3}</conditionExpression>
</sequenceFlow>
<endEvent id="_13" name="EndEvent"/>
<sequenceFlow id="_14" name="小于3天" sourceRef="_12" targetRef="_13">
<conditionExpression xsi:type="tFormalExpression">${leave.num<3}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="_15" sourceRef="_6" targetRef="_13"/>
</process>
<bpmndi:BPMNDiagram xmlns=""
documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0"
id="Diagram-_1"
name="New Diagram">
<bpmndi:BPMNPlane bpmnElement="myleave">
<bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
<omgdc:Bounds height="32.0" width="32.0" x="25.0" y="20.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
<omgdc:Bounds height="55.0" width="85.0" x="135.0" y="20.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_7" id="Shape-_7">
<omgdc:Bounds height="55.0" width="85.0" x="140.0" y="115.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_8" id="Shape-_8">
<omgdc:Bounds height="55.0" width="85.0" x="145.0" y="215.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_12" id="Shape-_12" isMarkerVisible="false">
<omgdc:Bounds height="32.0" width="32.0" x="270.0" y="220.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_6" id="Shape-_6">
<omgdc:Bounds height="55.0" width="85.0" x="330.0" y="150.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_13" id="Shape-_13">
<omgdc:Bounds height="32.0" width="32.0" x="380.0" y="285.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_15" id="BPMNEdge__15" sourceElement="_6" targetElement="_13">
<omgdi:waypoint x="396.0" y="205.0"/>
<omgdi:waypoint x="396.0" y="285.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_14" id="BPMNEdge__14" sourceElement="_12" targetElement="_13">
<omgdi:waypoint x="302.0" y="236.0"/>
<omgdi:waypoint x="380.0" y="301.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_4" id="BPMNEdge__4" sourceElement="_2" targetElement="_3">
<omgdi:waypoint x="57.0" y="36.0"/>
<omgdi:waypoint x="135.0" y="47.5"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_5" id="BPMNEdge__5" sourceElement="_8" targetElement="_12">
<omgdi:waypoint x="230.0" y="242.5"/>
<omgdi:waypoint x="270.0" y="236.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_9" id="BPMNEdge__9" sourceElement="_12" targetElement="_6">
<omgdi:waypoint x="302.0" y="236.0"/>
<omgdi:waypoint x="330.0" y="177.5"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_11" id="BPMNEdge__11" sourceElement="_7" targetElement="_8">
<omgdi:waypoint x="185.0" y="170.0"/>
<omgdi:waypoint x="185.0" y="215.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_10" id="BPMNEdge__10" sourceElement="_3" targetElement="_7">
<omgdi:waypoint x="180.0" y="75.0"/>
<omgdi:waypoint x="180.0" y="115.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
需要的entity
@Data
public class Leave implements Serializable {
//申请人id
private Integer userId;
//申请天数
private Integer num;
//请假原因
private String reason;
//技术组长审批
private Integer approver1;
//项目经理审批
private Integer approver2;
//总经理审批
private Integer approver3;
//总经理审批
private String taskId;
}
Controller包含整个流程
@RestController
@RequestMapping("/leave")
public class LeaveController {
//RuntimeService:提供了处理流程实例不同步骤的结构和行为。包括启动流程实例、暂停和激活流程实例等功能
@Autowired
private RuntimeService runtimeService;
//TaskService:提供有关任务相关功能的服务。包括任务的查询、删除以及完成等功能
@Autowired
private TaskService taskService;
//HistoryService:提供 Activiti 引擎收集的历史记录信息服务。主要用于历史信息的查询功能
@Autowired
private HistoryService historyService;
//activiti核心对象可提供各个ActivitiService
@Autowired
private ProcessEngine processEngine;
/**
* 启动流程
* @param userId
* @return
*/
@GetMapping("/start")
public String start(@RequestParam Integer userId){
Map<String, Object> vars = new HashMap<>();
Leave leave = new Leave();
leave.setUserId(userId);
vars.put("leave",leave);
runtimeService.startProcessInstanceByKey("myleave",vars);
return "流程启动成功!";
}
/**
* 申请请假
* @param leave
* @return
*/
@PostMapping(value="/apply")
public String apply(@RequestBody Leave leave){
Map<String, Object> vars = new HashMap<>();
vars.put("leave", leave);
taskService.setVariables(leave.getTaskId(),vars);
taskService.complete(leave.getTaskId());
return "申请成功!";
}
/**
* 查询用户流程
* @param userId
* @return
*/
@GetMapping("/queryTask")
public Map<String, Object> find(@RequestParam("userId")String userId) throws JsonProcessingException {
List<Task> taskList = taskService.createTaskQuery().taskAssignee(userId).list();
List<Leave> resultList = new ArrayList<>();
if(!CollectionUtils.isEmpty(taskList)){
for(Task task : taskList){
//获取指定任务id,leave前缀的信息
Object leave = taskService.getVariable(task.getId(), "leave");
ObjectMapper objectMapper = new ObjectMapper();
Leave leave1 = objectMapper.readValue(leave.toString(), Leave.class);
leave1.setTaskId(task.getId());
leave1.setTaskId(task.getId());
resultList.add(leave1);
}
}
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("data", resultList);
return resultMap;
}
/**
* 技术组长审批
* @param leave
* @return
*/
@PostMapping(value = "/approve1")
public String approve1(@RequestBody Leave leave){
taskService.complete(leave.getTaskId());
return "组长审批通过!";
}
/**
* 项目经理审批
* @param leave
* @return
*/
@PostMapping(value = "/approve2")
public String approve2(@RequestBody Leave leave){
taskService.complete(leave.getTaskId());
return "项目经理审批通过!";
}
/**
* 总经理审批
* @param leave
* @return
*/
@PostMapping(value = "/approve3")
public String approve3(@RequestBody Leave leave){
taskService.complete(leave.getTaskId());
return "总经理审批通过!";
}
/**
* 查看历史记录
* @param userId
* @return
*/
@GetMapping("/queryHistory")
public Map<String, Object> findClosed(String userId){
// 1.根据部署对象ID删除流程定义
//
// repositoryService.deleteDeployment(deploymentId, true);
//
// 2.删除历史流程实例根据流程实例ID
//
// historyService.deleteHistoricProcessInstance(processInstan);
//
// 3.删除正在运行中的流程根据流程实例ID
// runtimeService.deleteProcessInstance(processInstanceId,"");
HistoryService historyService = processEngine.getHistoryService();
List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().processDefinitionKey("myleave").variableValueEquals("leave.userId",userId).list();
List<Leave> leaves = new ArrayList<>();
for(HistoricProcessInstance pi : list){
leaves.add((Leave) pi.getProcessVariables().get("leave"));
}
Map<String, Object> resultMap =new HashMap<>();
resultMap.put("data", leaves);
return resultMap;
}
按流程测试
1.启动流程
2.申请人查询自己的任务
3.申请人提交请假申请
4.技术组长查看自己任务
5.技术组长审批
6.项目经理查看自己任务
7.项目经理审批
由于请假日期不超过3天,到此流程就结束了。
以上就是工作流的一个简单例子。