Flowable初探

目录

一、前景概述

1.1需求起点

1.2 Flowable简介

1.3 支持标准与区别

1.4 BPMN、CMMN和DMN交叉应用

1.5 方向确定

二、接入步骤

2.1 BPMN2.0标准概念介绍

2.1.1 事件

2.1.1.1开始事件(Start Event)

2.1.1.2 定时开始事件(Timer Start Event)

2.1.1.3 结束事件(End Event)

2.1.1.4 终止事件(Terminate Event)

2.1.2 顺序流

2.1.2.1 顺序流(Sequence Flow)

2.1.2.2 条件顺序流(Conditional Sequence Flow)

2.1.2.3 默认顺序流(Default Sequence Flow)

2.1.3 网关

2.1.3.1 单一网关(Exclusive Gateway)

2.1.3.2 并行网关(Parallel Gateway)

2.1.3.3 包容性网关(Inclusive Gateway)

2.1.3.4 基于事件网关(Event-Based Gateway)

2.1.4 核心流程任务

2.1.4.1 脚本任务(Script Task)

2.1.4.2 用户服务(User Task)

2.1.4.3 服务任务(Service Task)

2.2 官网示例流程实现与拓展

2.2.1 引入Flowable的Maven依赖

2.2.2 Flowable官方Java示例

2.2.3 配置项添加

2.2.4 flowable支持的数据库与实例

2.2.5 插件搭建Flowable流程

2.2.5.1 Start Event

2.2.5.2 User Task

2.2.5.3 Exclusive Service

2.2.5.4 Service Task

2.2.5.5 End Event

2.2.6 集成官网请假审批示例

2.2.6.1 提交请假条申请

2.2.6.2 审批请假条

2.2.6.3 已审批请假条


一、前景概述

1.1需求起点

  • 目前公司内部需以工单管理系统为入口研发适合公司业务的OA系统,同时日常业务开展也对接第三方也有基于Flowable的等,而对于流程审批需要一种标准的流程并结合业内标准实现。

1.2 Flowable简介

  • 而Flowable 是一个用 Java 编写的轻量级业务流程引擎。Flowable 流程引擎允许部署 BPMN 2.0 流程定义(用于定义流程的行业 XML 标准)、创建这些流程定义的流程实例、运行查询、访问活动或历史流程实例和相关数据等。

1.3 支持标准与区别

  • Flowable支持BPMN(Business Process Model and Notation)、CMMN(Case Management Model and Notation)和DMN(Decision Model and Notation)这三种标准。

Getting Started · Flowable Open Source Documentation

标准说明优点缺点应用场景

BPMN

(业务流程建模) 

BPMN 是一种广泛使用的业务流程建模标准,以图形方式描述业务过程中的活动、事件、网关等元素,有助于可视化和管理流程。适用于建模和执行结构化的业务流程。易于理解和使用,支持可视化建模。提供了广泛的工具和支持。对于非结构化和动态的业务流程可能较为复杂。可能需要额外的培训,以理解 BPMN 元素和规范审批流程: 一个典型的用例是设计一个流程来处理文档、合同或报销单的审批。这包括定义不同角色的任务、决策网关来确定流程的走向等。
采购流程: 通过BPMN,可以建模整个采购过程,从需求识别到供应商选择、订单生成、交货和支付。

CMMN

(案例管理建模) 

CMMN 是用于建模和执行案例管理的标准,适用于处理不确定、动态的业务情境,允许灵活地适应业务变化。适用于建模和执行灵活、非结构化的业务案例。支持动态适应业务变化。可以处理未知和复杂的业务情境。相对于传统的 BPMN,学习和理解曲线可能较陡峭。可能不适用于一些较为结构化的业务流程。客户服务管理: 为了管理客户投诉,可以使用CMMN建模来处理不同类型的投诉案例。每个案例可能需要不同的步骤和解决方案。
事件管理: 在紧急情况下,例如事故响应或灾难恢复,CMMN可以帮助建模复杂的案例,其中步骤和任务可能不是线性的。

DMN

(决策建模) 

DMN 是用于建模和执行业务决策的标准,它提供了一种图形方式定义决策表和业务规则,使决策可视化、易于管理和修改。适用于建模和执行业务决策,使得决策可视化和易于管理。支持业务用户定义和管理决策表。 可以与其他标准(如BPMN)集成。对于某些复杂的决策场景,可能需要深入的业务领域知识。不是所有业务场景都适用于决策表的建模。信用评估: 金融机构可以使用DMN来建模信用评估决策,根据申请人的信用得分、收入等信息做出决策。
产品定价: 针对不同市场条件和策略,企业可以使用DMN建模来制定产品的定价策略,考虑到各种因素。

1.4 BPMN、CMMN和DMN交叉应用

  • 一种所谓建模方式往往无法适应实际业务场景,需在遵循业务标准,如BPMN2.0基础上灵活应用,以下是BPMN、CMMN和DMN交叉描述说明
案例名称场景描述应用场景
综合案例管理和决策一个金融机构需要处理客户的贷款申请。贷款处理过程涉及到动态的案例管理(CMMN),例如审查客户资格、收集文件等。同时,在案例的某个阶段,需要根据客户的信用评分和收入情况进行贷款审批决策(DMN)。在整个贷款处理过程中,可以使用 BPMN 定义主要的流程,嵌入 CMMN 模型以处理动态情境,而在 CMMN 的某个阶段嵌入 DMN 决策表,以支持审批决策的自动化。
结合订单处理和库存决策一个电子商务平台需要处理订单。在订单处理过程中,涉及到动态的案例管理(CMMN),例如处理异常情况和退货。同时,需要根据库存情况(DMN)来决定是否可以接受订单。使用 BPMN 定义整个订单处理流程,嵌入 CMMN 模型来处理动态情境,同时在某个任务中嵌入 DMN 决策表,以支持库存决策。
结合审批流程和费用决策一个企业需要处理员工的费用报销。在审批流程中,需要根据报销金额和类别来做出费用批准决策(DMN)。同时,可能存在一些非结构化的情况,例如追加材料或说明,这可以通过嵌入 CMMN 模型来处理。使用 BPMN 定义整个审批流程,嵌入 CMMN 模型来处理非结构化情境,同时在审批任务中嵌入 DMN 决策表,以支持费用批准决策。

1.5 方向确定

Flowable提供了一套完整的API,供使用方调用,所有的服务都是无状态的,意味着依赖这套API的服务天然可在分布式环境进行部署。通过直接依赖jar包进行集成,并在各层都留出了接口,使用时可自行灵活扩展。

因此结合使用场景,使用Java API进行开发,同时主要是遵循BPMN2.0进行集成。

这里我们也应看到Flowable集成涉及概念过多和开发工作较宽泛。

而阿里的compileflow是非常轻量、高性能、可集成、可扩展的流程引擎。并且compileflow Process引擎是淘宝工作流TBBPM引擎之一,是专注于纯内存执行,无状态的流程引擎,通过将流程文件转换生成java代码编译执行,简洁高效。当前是阿里业务中台交易等多个核心系统的流程引擎。

虽然原生只支持淘宝BPM规范,为兼容BPMN 2.0规范,做了一定适配,且仅支持部分BPMN 2.0元素,如需其他元素支持,需在原来基础上扩展。

非常轻量、高性能、可集成、可扩展的流程引擎compileflow-阿里云开发者社区

因此我们应该注意参考compileflow不应追求宽泛功能与标准,结合业务实际拓展,甚至可以对使用集成compileflow做进一步调研确认!

二、接入步骤

2.1 BPMN2.0标准概念介绍

https://www.omg.org/spec/BPMN/2.0/PDF

  • 在BPMN 2.0中,主要有五类基本元素,分别是General(泛用元素)、Tasks(任务)、Choreographies(协同)、Events(事件)和Gateways(网关)。

序号

元素

说明

例子

元素集合

1

General

(泛用元素)

泛用元素包括流程图中的一般性元素,如数据对象、文本注释等。数据对象可以表示业务流程中的输入或输出数据,而文本注释用于提供进一步的说明或备注。
2

Tasks

(任务)

任务表示流程中的具体活动或工作单元,可以是人工任务、自动任务等。人工任务可以是某个工作人员执行的手动任务,而自动任务可能是系统自动执行的计算或处理。
3

Choreographies

(协同)

协同元素用于描述多个参与者之间的协作关系,展示业务流程中的交互。协同可以包括多个泳道(Swimlanes),每个泳道代表一个参与者,而活动和消息流用于显示参与者之间的交互关系。
4

Events

(事件)

事件表示流程中的触发点,可以是开始事件、中间事件或结束事件,用于引发流程中的转换。开始事件可以是流程的启动点,中间事件可以是某个条件触发的中间状态,结束事件表示流程的完成或终止。
5

Gateways

(网关)

网关用于控制流程中的流转,根据条件判断选择不同的路径。排他网关(XOR Gateway)用于在多个路径中选择一个,包含条件,而并行网关(Parallel Gateway)用于并行执行多个活动。

结合实际需求,我们并不需要一开始就了解BPMN2.0所有元素并准确运用,并且以上分类也只是基于draw.io软件,也有从描述对象(Artifacts)、流对象(FlowObjects)、连接对象(Connecting Object)、数据(Data)、泳道(Swimlanes)去分类。

BPMN 2.0规范 - 掘金

下面我们介绍主要几种元素并初步归类为【事件】、【顺序流】、【网关】和【核心流程任务】

2.1.1 事件

2.1.1.1开始事件(Start Event)

标志着业务流程的启动点,可以是简单的开始、消息触发或定时器触发,用于启动业务流程的执行。

2.1.1.2 定时开始事件(Timer Start Event)

在预定的时间点触发业务流程的开始,根据预定的时间间隔或指定的日期和时间来启动流程。

2.1.1.3 结束事件(End Event)

标志着业务流程的结束点,可以表示业务流程的正常结束或异常结束,用于指示流程成功完成或被中止。

2.1.1.4 终止事件(Terminate Event)

立即终止整个业务流程的执行,触发时立即停止所有活动并结束流程,通常用于处理紧急情况或异常情况。

终止网关是一种网关,用于全局中断整个流程实例。而结束事件是一个事件,通常用于在流程的不同部分或在整个流程结束时触发。

2.1.2 顺序流

2.1.2.1 顺序流(Sequence Flow)

描述了在业务流程中活动之间的顺序关系,表示流程执行的方向性,由箭头表示。

2.1.2.2 条件顺序流(Conditional Sequence Flow)

在特定条件下才会触发的顺序流,根据满足或不满足的条件来确定流程执行的路径。

2.1.2.3 默认顺序流(Default Sequence Flow)

用于指定当没有其他条件顺序流满足条件时,流程将自动执行的默认路径,通常不带有条件限制。

目前业务应用只考虑顺序流。

2.1.3 网关

2.1.3.1 单一网关(Exclusive Gateway)

用于在流程中做出排他性选择,只选择一条路径继续执行。

2.1.3.2 并行网关(Parallel Gateway)

允许流程在多个路径上同时执行,无需等待前一个任务完成。

2.1.3.3 包容性网关(Inclusive Gateway)

基于多个条件判断,允许流程在满足多个条件的情况下选择多个路径继续执行。

2.1.3.4 基于事件网关(Event-Based Gateway)

根据外部事件的发生选择不同的路径,而不是基于数据或条件判断。

这里的基于事件网关是一类网关!

2.1.4 核心流程任务

2.1.4.1 脚本任务(Script Task)

执行预定义的脚本或代码,通常用于执行一些简单的计算、数据转换或其他自动化操作。

2.1.4.2 用户服务(User Task)

代表需要人工参与的任务,通常需要人类用户在流程中执行某些活动或提供输入。

2.1.4.3 服务任务(Service Task)

执行特定的服务或功能,如调用外部系统、执行业务逻辑、发送消息等,通常用于自动化流程中的特定功能或操作。

服务任务在业务应用中是Java Service Task,如:指定的class实现了 JavaDelegate 接口中的 execute 方法,该方法用于执行委托逻辑,接收一个 DelegateExecution 对象作为参数。

2.2 官网示例流程实现与拓展

2.2.1 引入Flowable的Maven依赖

https://github.com/flowable/flowable-engine/blob/flowable-release-6.8.0/pom.xml

<dependency>
    <groupId>org.flowable</groupId>
    <artifactId>flowable-spring-boot-starter</artifactId>
    <version>6.8.0</version>
</dependency>

2.2.2 Flowable官方Java示例

Getting Started · Flowable Open Source Documentation

package com.test.lebron.flowable;
 
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
 
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
/**
 * 假期请求测试类
 * 流程步骤:
 * 1. 部署流程定义
 * 2. 启动流程实例
 * 3. 查询待办任务
 * 4. 完成任务
 * 5. 查询历史活动实例
 */
public class HolidaysRequestTest {
    public static void main(String[] args) {
        // 1. 配置流程引擎
        ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration()
                .setJdbcUrl("jdbc:mysql://127.0.0.1:3306/iot")
                .setJdbcUsername("root")
                .setJdbcPassword("123456")
                .setJdbcDriver("com.mysql.cj.jdbc.Driver")
                .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
 
        // 构建流程引擎
        ProcessEngine processEngine = cfg.buildProcessEngine();
 
        // 获取流程仓库服务
        RepositoryService repositoryService = processEngine.getRepositoryService();
 
        // 2. 部署流程定义
        Deployment deployment = repositoryService.createDeployment().addClasspathResource("processes/test.holiday.bpmn20.xml").deploy();
 
        // 获取流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
        System.out.println("找到流程定义: " + processDefinition.getName());
 
        // 3. 启动流程实例
        System.out.println("你是谁?");
        System.out.println("Curry");
        System.out.println("你想请几天假?");
        System.out.println("3");
        System.out.println("为什么需要这些假期?");
        System.out.println("回家!");
 
        // 获取运行时服务
        RuntimeService runtimeService = processEngine.getRuntimeService();
        Map<String, Object> variables = new HashMap<>();
        variables.put("employee", "lebron");
        variables.put("nrOfHolidays", "3");
        variables.put("description", "Go home!");
        ProcessInstance processInstance =  runtimeService.startProcessInstanceByKey("test.holiday", variables);
 
        // 获取任务服务
        TaskService taskService = processEngine.getTaskService();
 
        // 4. 查询待办任务
        List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("managers").list();
        System.out.println("你有 " + tasks.size() + " 个任务:");
        for (int i = 0; i < tasks.size(); i++) {
            System.out.println((i + 1) + ") " + tasks.get(i).getName());
        }
        System.out.println("你想完成哪个任务?");
        System.out.println("1");
        Task task = tasks.get(0);
 
        // 5. 完成任务
        Map<String, Object> processVariables = taskService.getVariables(task.getId());
        System.out.println(processVariables.get("employee") + " 请求 " + processVariables.get("nrOfHolidays") + " 天的假期。你是否批准?");
        System.out.println("n");
        variables = new HashMap<>();
        variables.put("rejected", "n");
        taskService.complete(task.getId(), variables);
 
        // 获取历史服务
        HistoryService historyService = processEngine.getHistoryService();
 
        // 6. 查询历史活动实例
        List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstance.getId())
                .finished()
                .orderByHistoricActivityInstanceEndTime().asc()
                .list();
        for (HistoricActivityInstance activity : activities) {
            System.out.println(activity.getActivityId() + " 花费 " + activity.getDurationInMillis() + " 毫秒");
        }
    }
}
<?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">
  <process id="test.holiday" name="test.holiday" isExecutable="true">
    <startEvent id="sid-c46ad10e-f4d1-4b0f-82d6-caa32467ba85"/>
    <userTask id="sid-f9850a6c-8f29-4107-bff7-1effb54f7b3b" name="Approve or reject request" flowable:candidateGroups="managers">
      <documentation/>
    </userTask>
    <sequenceFlow id="sid-304040fc-3931-4ec0-9df7-035415d0ab9c" sourceRef="sid-c46ad10e-f4d1-4b0f-82d6-caa32467ba85" targetRef="sid-f9850a6c-8f29-4107-bff7-1effb54f7b3b"/>
    <exclusiveGateway id="sid-462af185-fa7d-4ede-9c40-d814cc35af03"/>
    <sequenceFlow id="sid-a7d7be24-87fe-4262-b113-e598e49bcfac" sourceRef="sid-f9850a6c-8f29-4107-bff7-1effb54f7b3b" targetRef="sid-462af185-fa7d-4ede-9c40-d814cc35af03"/>
    <serviceTask id="sid-f49e52a4-7e4d-44eb-9174-e36deefe516c" flowable:exclusive="true" name="Enter holidays in external system" flowable:class="com.test.lebron.flowable.CallExternalSystemDelegate"/>
    <sequenceFlow id="sid-ee84a7e7-7728-476b-b287-889df7091cdb" sourceRef="sid-462af185-fa7d-4ede-9c40-d814cc35af03" targetRef="sid-f49e52a4-7e4d-44eb-9174-e36deefe516c" name="approved">
      <conditionExpression xsi:type="tFormalExpression"/>
    </sequenceFlow>
    <serviceTask id="sid-6526f91f-2061-4fa5-b5d0-a01ce3b2df1f" flowable:exclusive="true" name="Send out rejection email" flowable:delegateExpression="Send out rejection email!"/>
    <sequenceFlow id="sid-ee7f46bd-fde7-4249-8bca-6f1da4f9fd46" sourceRef="sid-462af185-fa7d-4ede-9c40-d814cc35af03" targetRef="sid-6526f91f-2061-4fa5-b5d0-a01ce3b2df1f" name="rejected">
      <conditionExpression xsi:type="tFormalExpression"/>
    </sequenceFlow>
    <endEvent id="sid-970c37be-596f-46ff-8b08-c9154cbb0b3f"/>
    <sequenceFlow id="sid-a26cba9f-6c6f-47e7-9e72-c11df07eacfc" sourceRef="sid-6526f91f-2061-4fa5-b5d0-a01ce3b2df1f" targetRef="sid-970c37be-596f-46ff-8b08-c9154cbb0b3f"/>
    <endEvent id="sid-3d7274ce-fc03-475c-aaa2-084014930f05"/>
    <userTask id="sid-0dadee5a-2b73-4fd6-9b9d-7b4eda9cb2da" name="Holiday approved"/>
    <sequenceFlow id="sid-a45ef11f-9ddc-4d88-a5de-899cda480ab5" sourceRef="sid-0dadee5a-2b73-4fd6-9b9d-7b4eda9cb2da" targetRef="sid-3d7274ce-fc03-475c-aaa2-084014930f05"/>
    <sequenceFlow id="sid-50d871f4-a8b5-4dd6-956b-d805972b131b" sourceRef="sid-f49e52a4-7e4d-44eb-9174-e36deefe516c" targetRef="sid-0dadee5a-2b73-4fd6-9b9d-7b4eda9cb2da"/>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_test.holiday">
    <bpmndi:BPMNPlane bpmnElement="test.holiday" id="BPMNPlane_test.holiday">
      <bpmndi:BPMNShape id="shape-84b72f3a-ad66-444a-ab90-264f224ab35d" bpmnElement="sid-c46ad10e-f4d1-4b0f-82d6-caa32467ba85">
        <omgdc:Bounds x="-1390.0" y="-565.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-f636ae03-a15a-4f3e-85b7-040a39ba5f3d" bpmnElement="sid-f9850a6c-8f29-4107-bff7-1effb54f7b3b">
        <omgdc:Bounds x="-1240.0" y="-590.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-c0c32359-1ddd-4f54-aeff-e4e2585f03f5" bpmnElement="sid-304040fc-3931-4ec0-9df7-035415d0ab9c">
        <omgdi:waypoint x="-1360.0" y="-550.0"/>
        <omgdi:waypoint x="-1240.0" y="-550.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-2817011d-01bd-4d2c-a2b4-b336b261a8fb" bpmnElement="sid-462af185-fa7d-4ede-9c40-d814cc35af03">
        <omgdc:Bounds x="-1070.0" y="-570.0" width="40.0" height="40.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-19dd4521-7cca-4e54-86f6-78514ad512a9" bpmnElement="sid-a7d7be24-87fe-4262-b113-e598e49bcfac">
        <omgdi:waypoint x="-1140.0" y="-550.0"/>
        <omgdi:waypoint x="-1070.0" y="-550.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-eaed7821-bc3a-4658-93cb-8f1bf2956ace" bpmnElement="sid-f49e52a4-7e4d-44eb-9174-e36deefe516c">
        <omgdc:Bounds x="-890.0001" y="-590.00024" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-3424c4a8-db2a-4091-b045-2eb575f3f58c" bpmnElement="sid-ee84a7e7-7728-476b-b287-889df7091cdb">
        <omgdi:waypoint x="-1030.0" y="-550.0"/>
        <omgdi:waypoint x="-890.0001" y="-550.00024"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-be1a6771-b0a3-4fda-9d28-c050318ad39b" bpmnElement="sid-6526f91f-2061-4fa5-b5d0-a01ce3b2df1f">
        <omgdc:Bounds x="-890.00006" y="-455.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-6ab2b4f3-2fe3-464b-8d45-0e99b6006bdb" bpmnElement="sid-ee7f46bd-fde7-4249-8bca-6f1da4f9fd46">
        <omgdi:waypoint x="-1050.0" y="-530.0"/>
        <omgdi:waypoint x="-1050.0" y="-415.0"/>
        <omgdi:waypoint x="-890.00006" y="-414.99997"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-520c47a6-b34c-4c85-adf6-37573c419386" bpmnElement="sid-970c37be-596f-46ff-8b08-c9154cbb0b3f">
        <omgdc:Bounds x="-680.00006" y="-430.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-f849597d-cb08-4b31-9cdd-cde3922e9299" bpmnElement="sid-a26cba9f-6c6f-47e7-9e72-c11df07eacfc">
        <omgdi:waypoint x="-790.00006" y="-415.0"/>
        <omgdi:waypoint x="-680.00006" y="-415.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-9314797e-32cd-4cc2-aa94-087ce98ca8e4" bpmnElement="sid-3d7274ce-fc03-475c-aaa2-084014930f05">
        <omgdc:Bounds x="-545.00006" y="-565.0002" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-40d735b7-bbea-4183-b73d-a5e3d3bc99c5" bpmnElement="sid-0dadee5a-2b73-4fd6-9b9d-7b4eda9cb2da">
        <omgdc:Bounds x="-715.0" y="-590.00024" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-9eee61ba-61cf-4cb6-b5e4-afc0fbd0d67b" bpmnElement="sid-a45ef11f-9ddc-4d88-a5de-899cda480ab5">
        <omgdi:waypoint x="-615.0" y="-550.00024"/>
        <omgdi:waypoint x="-545.00006" y="-550.0002"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-970997ea-88c9-4270-ada4-65412071386a" bpmnElement="sid-50d871f4-a8b5-4dd6-956b-d805972b131b">
        <omgdi:waypoint x="-790.0001" y="-550.00024"/>
        <omgdi:waypoint x="-715.0" y="-550.00024"/>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

test.holiday.bpmn20.xml文件这里通过参照官方示例流程通过IDEA插件Flowable BPMN visualizer构造

当然你可以引入Flowable UI构造

Flowable BPMN visualizer - IntelliJ IDEs Plugin | Marketplace

2.2.3 配置项添加

flowable:
  # 检查流程定义的正确性
  check-process-definitions: true
  # 流程定义文件路径前缀
  process-definition-location-prefix: "classpath*:/processes/"
  # 流程定义文件后缀
  process-definition-location-suffixes: "**.bpmn20.xml,**.bpmn"

2.2.4 flowable支持的数据库与实例

Configuration · Flowable Open Source Documentation

数据库类型

JDBC URL 示例

数据库介绍

h2jdbc:h2:tcp://localhost/flowable基于 Java 的内存数据库,适用于开发和测试环境。
mysqljdbc:mysql://localhost:3306/flowable?autoReconnect=true开源的关系型数据库管理系统,广泛用于生产环境。
oraclejdbc:oracle:thin:@localhost:1521:xe一种商业关系型数据库管理系统,提供高性能和可伸缩性。
postgresjdbc:postgresql://localhost:5432/flowable开源的关系型数据库管理系统,具有高度的可扩展性和灵活性。
db2jdbc:db2://localhost:50000/flowableIBM 提供的关系型数据库管理系统,广泛应用于企业环境。
mssql使用 Microsoft JDBC Driver:jdbc:sqlserver://localhost:1433;databaseName=flowable Microsoft 提供的商业关系型数据库管理系统,用于企业级应用。
server:
  port: 8081
  servlet:
    context-path: /local
logging:
  level:
    root: info
  file:
    path: /mnt/var/logs/test-logs
  config: classpath:logback-spring.xml
spring:
  application:
    name: local-test-service
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://172.16.16.60:3306/iot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&userSSL=true&autoReconnect=true&failOverReadOnly=false
    username: root
    password: 123456
  jpa:
    database: mysql
    show-sql: true
    properties:
      hibernate:
       hbm2ddl:
         auto: update
       dialect: org.hibernate.dialect.MySQL8Dialect
    hibernate:
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
 
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
 
flowable:
  check-process-definitions: true
  process-definition-location-prefix: "classpath*:/processes/"
  process-definition-location-suffixes: "**.bpmn20.xml,**.bpmn"

2.2.5 插件搭建Flowable流程

2.2.5.1 Start Event

ID(标识符):ID 属性是为 Start Event 分配的唯一标识符。它用于在流程定义中唯一地标识此特定 Start Event。ID 通常是一个字符串,但必须在整个 BPMN 文件中是唯一的。

Name(名称):Name 属性用于为 Start Event 指定一个描述性的名称。名称通常用于标识 Start Event 执行的目的或其所代表的活动。

Documentation(文档):Documentation 属性用于提供有关 Start Event 的额外描述性文档或说明。这可以是关于 Start Event 触发条件、执行顺序或其他相关信息的文档。

Form properties(表单属性):Form properties 属性用于定义与 Start Event 相关联的表单属性。在某些情况下,业务流程可能需要与用户交互以收集输入或提供信息。Form properties 可以定义表单字段的名称、类型、默认值和其他属性。

Execution listeners(执行监听器):Execution listeners 属性允许在 Start Event 上添加执行监听器,以便在 Start Event 执行期间执行特定的操作。执行监听器可以捕获事件,如开始事件的触发、执行之前或之后的事件,以及其他自定义事件。这些监听器可以用于执行各种操作,例如记录日志、触发其他业务逻辑或发送通知。

2.2.5.2 User Task

ID(标识符):用于唯一标识用户任务。

Name(名称):用户任务的名称,用于描述用户任务的目的或内容。

Documentation(文档):提供有关用户任务的额外描述性文档或说明。

Is for compensation(补偿用):指示用户任务是否是用于补偿(compensation)流程中的任务。

Asynchronous(异步):指示用户任务是否是异步执行的,即是否可以由其他进程或系统异步执行。

Assignee(受让人):指定用户任务的受让人(assignee),即负责执行用户任务的用户或组织。

Candidate Users(候选用户):指定可以执行用户任务的候选用户列表。

Candidate Groups(候选组):指定可以执行用户任务的候选组列表。

Skip expression(跳过表达式):定义一个表达式,用于在运行时确定是否跳过用户任务。

Due date(截止日期):指定用户任务的截止日期,即任务应该在此日期之前完成。

Category(类别):用于对用户任务进行分类或分组的属性。

Form key(表单键):关联用户任务的表单,指定用户任务关联的表单的键。

Form field validation(表单字段验证):定义用于验证表单字段的验证规则。

Priority(优先级):指定用户任务的优先级,用于确定任务的执行顺序。

Form properties(表单属性):定义用户任务关联的表单属性,包括表单字段的名称、类型、默认值等。

Execution listeners(执行监听器):定义用户任务的执行监听器,允许在用户任务执行期间执行特定的操作。

2.2.5.3 Exclusive Service

ID(标识符):用于唯一标识 Exclusive Gateway 网关。

Name(名称):Exclusive Gateway 网关的名称,用于描述网关的目的或内容。

Documentation(文档):提供有关 Exclusive Gateway 网关的额外描述性文档或说明。

Default flow element(默认流元素):指定 Exclusive Gateway 网关的默认流元素,即当网关根据条件无法决定选择哪个路径时,流程将会继续执行的默认路径。默认流元素可以是下一个任务、网关、结束事件等,用于指示流程的后续执行路径。

2.2.5.4 Service Task

ID(标识符):用于唯一标识 Service Task 任务。

Name(名称):Service Task 任务的名称,用于描述任务的目的或内容。

Documentation(文档):提供有关 Service Task 任务的额外描述性文档或说明。

Is for compensation(补偿用):指示 Service Task 任务是否是用于补偿(compensation)流程中的任务。

Asynchronous(异步):指示 Service Task 任务是否是异步执行的,即是否可以由其他进程或系统异步执行。

Exclusive(排他):指示 Service Task 任务是否是排他的,即是否在同一时间只能有一个实例执行。

Expression(表达式):定义 Service Task 任务的执行逻辑,通常是一个表达式或脚本,用于执行任务的逻辑操作。

Delegate expression(委托表达式):指定一个委托类或委托表达式,用于动态地决定在运行时执行的具体逻辑。

Class(类):指定一个 Java 类,用于执行 Service Task 任务的具体逻辑。

Skip expression(跳过表达式):定义一个表达式,用于在运行时确定是否跳过 Service Task 任务。

Is activity triggerable(活动可触发):指示 Service Task 任务是否可以被触发。

Result variable name(结果变量名):指定用于存储 Service Task 任务执行结果的变量名。

Use local scope for result variable(使用本地范围的结果变量):指示是否在本地范围内存储结果变量。

Failed job retry cycle(失败作业重试周期):定义失败作业的重试周期。

Fields(字段):定义 Service Task 任务的字段,包括输入参数、输出参数等。

Execution listeners(执行监听器):定义 Service Task 任务的执行监听器,允许在任务执行期间执行特定的操作。

package com.test.lebron.flowable;
 
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
 
/**
 * 调用外部系统委托类
 */
public class CallExternalSystemDelegate implements JavaDelegate {
    @Override
    public void execute(DelegateExecution delegateExecution) {
        // 执行委托逻辑,调用外部系统
        System.out.println("正在调用外部系统,员工姓名:" + delegateExecution.getVariable("employee"));
    }
}
2.2.5.5 End Event

ID(标识符):用于唯一标识结束事件。

Name(名称):结束事件的名称,用于描述事件的目的或内容。

Documentation(文档):提供有关结束事件的额外描述性文档或说明。

2.2.6 集成官网请假审批示例

2.2.6.1 提交请假条申请

2.2.6.2 审批请假条

2.2.6.3 已审批请假条

package com.test.lebron.flowable.vaction.controller;
 
import com.test.lebron.common.model.ResponseResult;
import com.test.lebron.flowable.vaction.service.VacationService;
import com.test.lebron.flowable.vaction.vo.VacationApproveVO;
import com.test.lebron.flowable.vaction.vo.VacationRequestVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
 
@RestController
@RequestMapping("flowable")
public class FlowableController {
    @Autowired
    VacationService vacationService;
 
    /**
     * 请假条新增页面
     * @return
     */
    @GetMapping("/add")
    public ModelAndView add(){
        ModelAndView modelAndView = new ModelAndView("vacation");
        return modelAndView;
    }
 
    /**
     * 请假条审批列表
     * @return
     */
    @GetMapping("/aList")
    public ModelAndView aList(){
        return new ModelAndView("list");
    }
 
    /**
     * 请假条查询列表
     * @return
     */
    @GetMapping("/sList")
    public ModelAndView sList(){
        return new ModelAndView("search");
    }
 
    /**
     * 请假请求方法
     * @param vacationRequestVO
     * @return
     */
    @PostMapping("/askForLeave")
    public ResponseResult askForLeave(@RequestBody VacationRequestVO vacationRequestVO) {
        return vacationService.askForLeave(vacationRequestVO);
    }
 
    /**
     * 获取待审批列表
     * @param identity
     * @return
     */
    @GetMapping("/list")
    public ResponseResult leaveList(String identity) {
        return vacationService.leaveList(identity);
    }
 
    /**
     * 拒绝或同意请假
     * @param vacationApproveVO
     * @return
     */
    @PostMapping("/handler")
    public ResponseResult askForLeaveHandler(@RequestBody VacationApproveVO vacationApproveVO) {
        return vacationService.askForLeaveHandler(vacationApproveVO);
    }
 
    /**
     * 请假查询
     * @param name
     * @return
     */
    @GetMapping("/search")
    public ResponseResult searchResult(String name) {
        return vacationService.searchResult(name);
    }
}
package com.test.lebron.flowable.vaction.service.impl;
 
import com.test.lebron.common.model.ResponseResult;
import com.test.lebron.flowable.vaction.dto.VacationInfoDTO;
import com.test.lebron.flowable.vaction.service.VacationService;
import com.test.lebron.flowable.vaction.vo.VacationApproveVO;
import com.test.lebron.flowable.vaction.vo.VacationRequestVO;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.api.Task;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import java.util.*;
 
@Service
public class VacationServiceImpl implements VacationService {
    private static final Logger LOGGER = LoggerFactory.getLogger(VacationServiceImpl.class);
    @Autowired
    RuntimeService runtimeService;
    @Autowired
    TaskService taskService;
    @Autowired
    HistoryService historyService;
    @Autowired
    RepositoryService repositoryService;
 
    @Transactional
    public ResponseResult askForLeave(VacationRequestVO vacationRequestVO) {
        Map<String, Object> variables = new HashMap<>();
        variables.put("name", vacationRequestVO.getName());
        variables.put("days", vacationRequestVO.getDays());
        variables.put("reason", vacationRequestVO.getReason());
        List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery().list();
        // 输出流程定义信息
        for (ProcessDefinition processDefinition : processDefinitions) {
            LOGGER.info("流程定义ID: " + processDefinition.getId());
            LOGGER.info("流程定义键: " + processDefinition.getKey());
            LOGGER.info("流程定义版本: " + processDefinition.getVersion());
            LOGGER.info("流程定义名称: " + processDefinition.getName());
            LOGGER.info("流程定义部署ID: " + processDefinition.getDeploymentId());
            LOGGER.info("---------------------------");
        }
        try {
            //指定业务流程
            runtimeService.startProcessInstanceByKey("test.holiday", vacationRequestVO.getName(), variables);
            return ResponseResult.ok().data("已提交请假申请");
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
            e.printStackTrace();
        }
        return ResponseResult.error(-1, "提交申请失败");
    }
 
    public ResponseResult leaveList(String identity) {
        List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup(identity).list();
        List<Map<String, Object>> list = new ArrayList<>();
        for (int i = 0; i < tasks.size(); i++) {
            Task task = tasks.get(i);
            Map<String, Object> variables = taskService.getVariables(task.getId());
            variables.put("id", task.getId());
            list.add(variables);
        }
        return ResponseResult.ok().data(list);
    }
 
    public ResponseResult askForLeaveHandler(VacationApproveVO vacationApproveVO) {
        try {
            boolean approved = vacationApproveVO.getApprove();
            Map<String, Object> variables = new HashMap<>();
            variables.put("approved", approved);
            variables.put("employee", vacationApproveVO.getName());
            Task task = taskService.createTaskQuery().taskId(vacationApproveVO.getTaskId()).singleResult();
            taskService.complete(task.getId(), variables);
            if (approved) {
                //如果是同意,还需要继续走一步
                Task t = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
                taskService.complete(t.getId());
            }
            return ResponseResult.success("操作成功!");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseResult.error(-1, "操作失败!");
    }
 
    public ResponseResult searchResult(String name) {
        List<VacationInfoDTO> vacationInfoDTOArrayList = new ArrayList<>();
 
        List<HistoricProcessInstance> historicProcessInstanceList = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(name).finished().orderByProcessInstanceEndTime().desc().list();
 
        for(int i = 0; i < historicProcessInstanceList.size(); i++) {
            VacationInfoDTO vacationInfoDTO = new VacationInfoDTO();
 
            HistoricProcessInstance historicProcessInstance = historicProcessInstanceList.get(i);
            Date startTime = historicProcessInstance.getStartTime();
            Date endTime = historicProcessInstance.getEndTime();
 
            List<HistoricVariableInstance> historicVariableInstanceList = historyService.createHistoricVariableInstanceQuery().processInstanceId(historicProcessInstance.getId()).list();
 
            for(int j = 0; j < historicVariableInstanceList.size(); j++) {
                HistoricVariableInstance historicVariableInstance = historicVariableInstanceList.get(j);
                String variableName = historicVariableInstance.getVariableName();
                Object value = historicVariableInstance.getValue();
                LOGGER.info("variableName {}, value {}", variableName, value);
                if ("reason".equals(variableName)) {
                    vacationInfoDTO.setReason((String) value);
                } else if ("days".equals(variableName)) {
                    vacationInfoDTO.setDays(Integer.parseInt(value.toString()));
                } else if ("approved".equals(variableName)) {
                    vacationInfoDTO.setStatus((Boolean) value);
                } else if ("name".equals(variableName)) {
                    vacationInfoDTO.setName((String) value);
                }
            }
            vacationInfoDTO.setStartTime(startTime);
            vacationInfoDTO.setEndTime(endTime);
            vacationInfoDTOArrayList.add(vacationInfoDTO);
        }
        return ResponseResult.ok().data(vacationInfoDTOArrayList);
    }
}

  • 30
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flowable 是一个基于 Reactive Streams 标准的响应式编程库,它是 RxJava 2.x 的背压实现。Flowable 提供了一种异步、非阻塞的编程模型,可以处理大量的数据流,并且能够有效地处理背压问题。 在面试中,可能会涉及到以下几个方面的问题: 1. 什么是 FlowableFlowable 是 RxJava 2.x 中的一个类,它实现了 Reactive Streams 标准,用于处理异步数据流。与 Observable 不同,Flowable 支持背压(Backpressure)机制,可以控制数据流的速率,避免数据产生速度过快而导致的内存溢出等问题。 2. Flowable 与 Observable 的区别是什么? Flowable 和 Observable 都是 RxJava 中用于处理数据流的类,但它们之间有一些区别。最主要的区别是 Flowable 支持背压机制,而 Observable 不支持。Flowable 在处理大量数据流时更加稳定,能够控制数据的生产和消费速率,避免内存溢出等问题。 3. 如何处理 Flowable 的背压问题? Flowable 提供了多种处理背压问题的策略,可以根据实际需求选择合适的策略。常见的策略包括: - BackpressureStrategy.BUFFER:缓存所有数据,直到消费者准备好接收。 - BackpressureStrategy.DROP:如果消费者无法及时处理数据,丢弃一部分数据。 - BackpressureStrategy.LATEST:只保留最新的数据,丢弃旧的数据。 - BackpressureStrategy.ERROR:如果消费者无法及时处理数据,抛出异常。 4. Flowable 的使用场景有哪些? Flow 适用于处理大量的异步数据流,特别是在数据产生速度和消费速度不一致的情况下。常见的使用场景包括网络请求、数据库查询、文件读写等需要处理大量数据的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值