介绍
文章主要从SpringBoot整合Flowable讲起,关于Flowable是什么?数据库表解读以及操作的Service请查看SpringBoot整合Flowable最新教程(一);
其他说明:Springboot版本是2.6.13,java版本是1.8。如果你使用的是一些开源项目如:ruoyi里面已经集成了这些无须自己手动引入。
文章代码地址(包含bpmn文件):文章源码地址
pom文件引入
<!-- https://mvnrepository.com/artifact/org.flowable/flowable-spring-boot-starter -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.7.2</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.12</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
因为加入flowable后项目启动时要往数据库加入流程表,所以这里就直接把druid也加上,数据库也配置上。
server:
port: 8099
spring:
datasource:
druid:
url: jdbc:mysql://localhost:3306/spring_flowable?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: root
password: 1101165230
driver-class-name: com.mysql.cj.jdbc.Driver
# 初始化物理链接的个数
initial-size: 5
# 最大连接池数量
max-active: 30
# 最小 连接池数量
min-idle: 5
# 获取连接时最大等待时间,单位毫秒 timeout链接超时
max-wait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 检测非核心链接的时间
#既作为检测的间隔时间又作为testWhileIdel执行的依据
time-between-eviction-runs-millis: 60000
# 连接保持空闲而不被驱逐的最小时间
min-evictable-idle-time-millis: 30000
# 用来检测连接是否有效的sql,要求是一个查询语句 8小时问题 8个小时没跟mysql通信mysql会主动关闭该链接
validation-query: SELECT 1 FROM DUAL
# 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,
# 如果空闲时间大于min-evictable-idle-time-millis,执行validationQuery检测连接是否有效。
test-while-idle: true
# 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
test-on-borrow: false
# 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
test-on-return: false
# 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,
# 比如说oracle。在mysql下建议关闭。不是mysql端,链接端,select * from student 查询缓存
pool-prepared-statements: true
# 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。
max-pool-prepared-statement-per-connection-size: 50
#调优相关:基本上任何一个项目的性能瓶颈 1.io 2.cpu计算量
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计
filters: stat,wall
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录 慢查询:增删改查
connection-properties: druid.stat.mergeSql=true;
filter:
stat:
slow-sql-millis: 1
# 合并多个DruidDataSource的监控数据
use-global-data-source-stat: true
# druid连接池监控
stat-view-servlet:
login-username: admin
login-password: 1101165230
# 排除一些静态资源,以提高效率
web-stat-filter:
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
# flowable配置
flowable:
# 请在第一次启动时使用true,之后使用false
database-schema-update: true
#pg用户使用指定,mysql不用
# database-schema: spring_flowable
activity-font-name: "宋体"
label-font-name: "宋体"
annotation-font-name: "宋体"
async-executor-activate: false
2、配置Mysql连接以及druid后,启动项目
如果启动报错请看.act_ge_property‘ doesn‘t exist
3、接着开始部署我们的bpmn文件
在线bpmn绘制地址:bpmn在线绘制
文章配套xml文件在最上方,请在最上方点击staff-leave.bpmn20.xml下载资源
1.下载好文件后,在我们的resources文件夹下创建一个processes文件夹(项目启动时候会自动加载该文件夹下的xml),并将xml文件放进去。
这里我创建的流程是一个员工休假申请,如图:
流程非常简单主要是为了让大家熟悉如何启动流程。(因为是1.0版本所以我并没有控制网关节点流向控制值,所以加入项目启动后会有一个提示说这个网关没有配置值)
4、业务与代码讲解
整个业务流程也非常简单就是员工填写休假申请,我们就开启流程。由于我们只讲解启动的流程,所以大家不用纠结每个业务所在节点的状态。
题外话:就拿一级审批节点来说,我启动流程后进入一级审批,这个时候的状态应该是什么?应该是一个待审核状态,只有等审核人员审核后才会变成已审核,已审核后经过网关是进入二级审批还是修改信息节点就看审核人的具体操作。其实也有特别简单的理解,就是将整个业务和流程进行一个拆分,将流程中的状态变更提取成一张表,我觉得大家就更好理解。后续我可能会出一个视频去讲解。
4.1创建factory文件夹,创建一个WorkflowService将flowable的服务类全部放进去进行统一管理
import org.flowable.engine.*;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author by Guoshun
* @version 1.0.0
* @description flowable 工作流服务类
* @date 2024/2/4 10:13
*/
public class WorkflowService {
/**
* 流程引擎
*/
@Autowired
public ProcessEngine processEngine;
/**
* 流程仓库服务类
*/
@Autowired
public RepositoryService repositoryService;
/**
* 查询运行信息
*/
@Autowired
public RuntimeService runtimeService;
/**
* 查询任务信息
*/
@Autowired
public TaskService taskService;
/**
* 查询历史信息
*/
@Autowired
public HistoryService historyService;
}
4.2接着我们还是按照标准开发流程创建Service、实体类这些步骤
请假表单实体类:
import lombok.*;
import java.io.Serializable;
import java.util.Date;
/**
* @author by Guoshun
* @version 1.0.0
* @description 申请信息入参
* @date 2024/2/4 10:21
*/
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class ApproveInfoBO implements Serializable {
private String name;
private Date startTime;
private Date endTime;
private String remarks;
}
创建service
import com.sg.flowable.entity.ApproveInfoBO;
import javax.servlet.http.HttpServletResponse;
/**
* @author by Guoshun
* @version 1.0.0
* @description IStaffLeaveApproveService
* @date 2024/2/4 10:10
*/
public interface IStaffLeaveApproveService{
/**
* 休假申请
*/
String approve(ApproveInfoBO approveInfoBO);
/**
* 获取流程图
* @param httpServletResponse
* @param processId
*/
void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) throws Exception;
}
创建实现类
import com.sg.flowable.entity.ApproveInfoBO;
import com.sg.flowable.factory.WorkflowService;
import com.sg.flowable.service.IStaffLeaveApproveService;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.List;
/**
* @author by Guoshun
* @version 1.0.0
* @description StaffLeaveApproveServiceImpl
* @date 2024/2/4 10:10
*/
@Service
public class StaffLeaveApproveServiceImpl extends WorkflowService implements IStaffLeaveApproveService {
@Override
public String approve(ApproveInfoBO approveInfoBO){
//数据的id(假设这是申请数据的id)
String id = UUID.randomUUID().toString().replaceAll("-", "");
//放入流程中的数据
Map<String, Object> variables = new HashMap<>();
variables.put("status", 1);
variables.put("data", approveInfoBO);
//启动流程
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("staff_leave", id, variables);
String processInstanceId = processInstance.getId();
System.out.println("流程启动id是:"+processInstanceId);
return id;
}
/**
* 返回流程图
* @param httpServletResponse
* @param processId
*/
@Override
public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) throws Exception {
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
//流程走完的不显示图
if (pi == null) {
return;
}
Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
//使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
String InstanceId = task.getProcessInstanceId();
List<Execution> executions = runtimeService
.createExecutionQuery()
.processInstanceId(InstanceId)
.list();
//得到正在执行的Activity的Id
List<String> activityIds = new ArrayList<>();
List<String> flows = new ArrayList<>();
for (Execution exe : executions) {
List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
activityIds.addAll(ids);
}
//获取流程图
BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0,true);
OutputStream out = null;
byte[] buf = new byte[1024];
int legth = 0;
try {
out = httpServletResponse.getOutputStream();
while ((legth = in.read(buf)) != -1) {
out.write(buf, 0, legth);
}
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}
创建Controller
import com.sg.flowable.entity.ApproveInfoBO;
import com.sg.flowable.service.IStaffLeaveApproveService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
/**
* @author by Guoshun
* @version 1.0.0
* @description StaffLeaveApproveController
* @date 2024/2/4 10:06
*/
@RestController
@RequestMapping("/staff_leave")
public class StaffLeaveApproveController {
@Resource
private IStaffLeaveApproveService iStaffLeaveApproveService;
/**
* 休假申请
*/
@PostMapping("/approve")
public String approve(@RequestBody ApproveInfoBO approveInfoBO){
return iStaffLeaveApproveService.approve(approveInfoBO);
}
/**
* 返回流程图
* @param httpServletResponse
* @param processId
* @throws Exception
*/
@GetMapping("/genProcessDiagram/{processId}")
public void genProcessDiagram(HttpServletResponse httpServletResponse,@PathVariable("processId") String processId) throws Exception {
iStaffLeaveApproveService.genProcessDiagram(httpServletResponse, processId);
}
}
测试调用
使用控制台实例id调用返回流程图接口,查看申请所在位置: