SpringBoot + Flowable的入门篇,完整例子

8,442 阅读5分钟

一、Flowable简介

Flowable是什么

Flowable是一个使用Java编写的轻量级业务流程引擎。Flowable流程引擎可用于部署BPMN 2.0流程定义(用于定义流程的行业XML标准), 创建这些流程定义的流程实例,进行查询,访问运行中或历史的流程实例与相关数据,等等。

Flowable可以十分灵活地加入你的应用/服务/构架。可以将JAR形式发布的Flowable库加入应用或服务,来嵌入引擎。 以JAR形式发布使Flowable可以轻易加入任何Java环境:Java SE;Tomcat、Jetty或Spring之类的servlet容器;JBoss或WebSphere之类的Java EE服务器,等等。 另外,也可以使用Flowable REST API进行HTTP调用。也有许多Flowable应用(Flowable Modeler, Flowable Admin, Flowable IDM 与 Flowable Task),提供了直接可用的UI示例,可以使用流程与任务。

所有使用Flowable方法的共同点是核心引擎。核心引擎是一组服务的集合,并提供管理与执行业务流程的API。 下面的教程从设置与使用核心引擎的介绍开始。

开始

1、创建一个springboot项目,添加依赖

<!-- Flowable spring-boot  -->
<dependency>
    <groupId>org.flowable</groupId>
    <artifactId>flowable-spring-boot-starter</artifactId>
    <version>6.4.1</version>
</dependency>

<!--MySQL驱动,这里采用MySQL数据库,如果采用其它数据库,需要引入对应的依赖。-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.15</version>
</dependency>

<!-- Flowable 内部日志采用 SLF4J -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.21</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.21</version>
</dependency>

resource 目录下新建文件 log4j.properties

log4j.rootLogger=DEBUG, CA
log4j.appender.CA=org.apache.log4j.ConsoleAppender
log4j.appender.CA.layout=org.apache.log4j.PatternLayout
log4j.appender.CA.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n

2、初始化 ProcessEngine

我的配置文件 ProcessEngineConfig.java

依赖

<!-- 配置文件处理器 -->
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-configuration-processor</artifactId>
 </dependency>
 <!-- lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.0</version>
    <scope>provided</scope>
</dependency>

配置文件 ProcessEngineConfig.java

/**
 * 流程引擎配置文件
 * @author: linjinp
 * @create: 2019-10-21 16:49
 **/
@Configuration
@ConfigurationProperties(prefix = "spring.datasource")
@Data
public class ProcessEngineConfig {

    private Logger logger = LoggerFactory.getLogger(ProcessEngineConfig.class);

    @Value("${spring.datasource.url}")
    private String url;

    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;

    @Value("${spring.datasource.username}")
    private String username;

    @Value("${spring.datasource.password}")
    private String password;

    /**
     * 初始化流程引擎
     * @return
     */
    @Primary
    @Bean(name = "processEngine")
    public ProcessEngine initProcessEngine() {
        logger.info("=============================ProcessEngineBegin=============================");

        // 流程引擎配置
        ProcessEngineConfiguration cfg = null;

        try {
            cfg = new StandaloneProcessEngineConfiguration()
                    .setJdbcUrl(url)
                    .setJdbcUsername(username)
                    .setJdbcPassword(password)
                    .setJdbcDriver(driverClassName)
                    // 初始化基础表,不需要的可以改为 DB_SCHEMA_UPDATE_FALSE
                    .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE)
                    // 默认邮箱配置
                    // 发邮件的主机地址,先用 QQ 邮箱
                    .setMailServerHost("smtp.qq.com")
                    // POP3/SMTP服务的授权码
                    .setMailServerPassword("xxxxxxx")
                    // 默认发件人
                    .setMailServerDefaultFrom("1010338399@qq.com")
                    // 设置发件人用户名
                    .setMailServerUsername("管理员")
                    // 解决流程图乱码
                    .setActivityFontName("宋体")
                    .setLabelFontName("宋体")
                    .setAnnotationFontName("宋体");
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 初始化流程引擎对象
        ProcessEngine processEngine = cfg.buildProcessEngine();
        logger.info("=============================ProcessEngineEnd=============================");
        return processEngine;
    }
}

启动项目后,会自动创建flowable的数据库表

flowable命名规则:

  • ACT_RE_* :’ RE ’表示repository(存储)。RepositoryService接口操作的表。带此前缀的表包含的是静态信息,如,流程定义,流程的资源(图片,规则等)。
  • ACT_RU_* :’ RU ’表示runtime。这是运行时的表存储着流程变量,用户任务,变量,职责(job)等运行时的数据。flowable只存储实例执行期间的运行时数据,当流程实例结束时,将删除这些记录。这就保证了这些运行时的表小且快。
  • ACT_ID_* : ’ ID ’表示identity(组织机构)。这些表包含标识的信息,如用户,用户组,等等。
  • ACT_HI_* : ’ HI ’表示history。就是这些表包含着历史的相关数据,如结束的流程实例,变量,任务,等等。
  • ACT_GE_* : 普通数据,各种情况都使用的数据。

3、部署流程定义

要构建的流程是一个非常简单的请假流程。Flowable引擎需要流程定义为BPMN 2.0格式,这是一个业界广泛接受的XML标准。

在Flowable术语中,我们将其称为一个流程定义(process definition)。一个流程定义可以启动多个流程实例(process instance)。流程定义可以看做是重复执行流程的蓝图。 在这个例子中,流程定义定义了请假的各个步骤,而一个流程实例对应某个雇员提出的一个请假申请。

BPMN 2.0存储为XML,并包含可视化的部分:使用标准方式定义了每个步骤类型(人工任务,自动服务调用,等等)如何呈现,以及如何互相连接。这样BPMN 2.0标准使技术人员与业务人员能用双方都能理解的方式交流业务流程。

我们要使用的流程定义为:

XME65HVASP3{8SF4{B1$N.png

在src/main/resources文件夹下创建为employeeLeave.bpmn20.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:flowable="http://flowable.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.flowable.org/processdef" exporter="Flowable Open Source Modeler">
    <process id="employeeLeave" name="职工请假流程" isExecutable="true">
        <documentation>职工请假</documentation>
        <startEvent id="bsin1" flowable:formKey="employeeLeave" flowable:formFieldValidation="true"></startEvent>
        <userTask id="bsin3" name="经理" flowable:assignee="${auditor}" flowable:formFieldValidation="true">
            <extensionElements>
                <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
            </extensionElements>
        </userTask>
        <serviceTask id="bsin2" name="服务任务"></serviceTask>
        <endEvent id="sid-74F2CDB2-1D4F-4DD4-B848-51158EE18A84"></endEvent>
        <sequenceFlow id="sid-DB4E84AD-78A8-4F7F-B14F-682A7FC92790" sourceRef="bsin1" targetRef="bsin2"></sequenceFlow>
        <sequenceFlow id="sid-23FB4CEA-43F8-46E8-9690-6E6C038491D4" sourceRef="bsin2" targetRef="bsin3"></sequenceFlow>
        <sequenceFlow id="sid-E818BD0A-E382-4B16-97DB-5AEE4825A4B3" sourceRef="bsin3" targetRef="sid-74F2CDB2-1D4F-4DD4-B848-51158EE18A84"></sequenceFlow>
    </process>
    <bpmndi:BPMNDiagram id="BPMNDiagram_employeeLeave">
        <bpmndi:BPMNPlane bpmnElement="employeeLeave" id="BPMNPlane_employeeLeave">
            <bpmndi:BPMNShape bpmnElement="bsin1" id="BPMNShape_bsin1">
                <omgdc:Bounds height="30.0" width="30.0" x="100.0" y="160.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="bsin3" id="BPMNShape_bsin3">
                <omgdc:Bounds height="80.0" width="100.0" x="465.0" y="135.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="bsin2" id="BPMNShape_bsin2">
                <omgdc:Bounds height="80.0" width="100.0" x="240.0" y="135.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="sid-74F2CDB2-1D4F-4DD4-B848-51158EE18A84" id="BPMNShape_sid-74F2CDB2-1D4F-4DD4-B848-51158EE18A84">
                <omgdc:Bounds height="28.0" width="28.0" x="708.8" y="159.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNEdge bpmnElement="sid-23FB4CEA-43F8-46E8-9690-6E6C038491D4" id="BPMNEdge_sid-23FB4CEA-43F8-46E8-9690-6E6C038491D4" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0">
                <omgdi:waypoint x="339.94999999983776" y="175.0"></omgdi:waypoint>
                <omgdi:waypoint x="465.0" y="175.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="sid-DB4E84AD-78A8-4F7F-B14F-682A7FC92790" id="BPMNEdge_sid-DB4E84AD-78A8-4F7F-B14F-682A7FC92790" flowable:sourceDockerX="15.0" flowable:sourceDockerY="15.0" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0">
                <omgdi:waypoint x="129.94999940317362" y="175.0"></omgdi:waypoint>
                <omgdi:waypoint x="240.0" y="175.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="sid-E818BD0A-E382-4B16-97DB-5AEE4825A4B3" id="BPMNEdge_sid-E818BD0A-E382-4B16-97DB-5AEE4825A4B3" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="14.0" flowable:targetDockerY="14.0">
                <omgdi:waypoint x="564.95" y="175.0"></omgdi:waypoint>
                <omgdi:waypoint x="636.9" y="175.0"></omgdi:waypoint>
                <omgdi:waypoint x="636.9" y="173.0"></omgdi:waypoint>
                <omgdi:waypoint x="708.8" y="173.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
        </bpmndi:BPMNPlane>
    </bpmndi:BPMNDiagram>
</definitions>

flowable有个8个核心服务,springboot里使用 flowable-spring-boot-starter 依赖,会自动帮忙注册好,不需要自己再注册,直接使用即可。

使用RepositoryService,可以通过XML文件的路径创建一个新的部署(Deployment),并调用deploy()方法实际执行:

// 部署流程
repositoryService.createDeployment()
        .name("employeeLeave")
        .addClasspathResource("employeeLeave.bpmn20.xml")
        .deploy();

4、启动流程实例

现在已经在流程引擎中部署了流程定义,因此可以使用这个流程定义作为“蓝图”启动流程实例。

要启动流程实例,需要提供一些初始化流程变量。一般来说,可以通过呈现给用户的表单,或者在流程由其他系统自动触发时通过REST API,来获取这些变量。

image.png

image.png

启动流程实例有两种方式

runtimeService.startProcessInstanceByKey(processInstanceByKey,variables);
runtimeService.startProcessInstanceById(processInstanceById,variables);

variables就是用来存储流程变量的

5、查询与完成任务

flowable的任务有很多,常见的有服务任务和用户任务

职工通过表单填写的基本信息都会存储在流程变量,流程启动后,会经过服务任务,服务任务会职工信息分配代理人,将代理人信息添加到流程变量中 image.png 实现服务任务调用的服务,需要创建一个类,实现JavaDelegate接口,实现execute方法,这个方法可以写很多业务逻辑

public class ServiceTask implements JavaDelegate {

    @Autowired
    private RuntimeService runtimeService;

    @Override
    public void execute(DelegateExecution delegateExecution) {
        // 业务逻辑我就不写了
        final String executionId  = delegateExecution.getId();
        runtimeService.setVariable(executionId,"assignee","admin");
    }
}

在流转到用户任务之前,我们需要设置代理人,我们可以使用用户任务监听器动态设置代理人

image.png

public class UserTaskListener implements TaskListener {
    @Autowired
    private TaskService taskService;
    /**
     * 用户任务监听器
     */
    @Override
    public void notify(DelegateTask delegateTask) {
        String taskId = delegateTask.getId();
        Map<String, Object> assignee = taskService.getVariables(taskId);
        // 设置办理人
        taskService.setAssignee(taskId, (String) assignee.get("assignee"));
    }

用户要获得实际的任务列表,需要通过TaskService创建一个TaskQuery。

public List<Task> TaskQuery(String assignee) {
    TaskQuery taskQuery = taskService.createTaskQuery();
    List<Task> taskList = taskQuery.taskAssignee(assignee).list();
    return taskList;
}

经理现在就可以完成任务了

taskService.complete(taskId,variables);

欢迎加入我们:gitee.com/bsin-paas/a…

c16c1da74dd789cc3a4850f24e269f2.png