flowable工作流的简单使用

发布于 2022-06-18  184 次阅读


简介

首先,官方用户手册

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

我对工作流的定义是:一件事通过一系列的判断与分支,走向终点的流程的定义。工作流是复杂版本的状态机

而工作流程引擎,则是对工作流的一个整体的抽象,用来创建、销毁、管理、执行工作流。

日常的生活我们就面临审批这审批那的,审批的流程也是不固定的。如果通过代码实现的话,是可以用状态机完成这种一两级的审批,一旦流程较长或者变动、代码就会难以维护。

所以flowable流程引擎把这部分抽象出来,将配置的流程保存在数据库,流程控制代替我们操作,我们只需要在流程每个节点到达的时候做对应的事情,不需要关心下个节点是什么节点,上一个节点是什么节点。

flowable用BPMN 2.0描绘流程,BPMN 协议是一种国际通用的建模语言,他能让自然人轻松阅读,更能被计算机所解析,就像你说中文我能听得懂,说日文我听着就费劲一样。

flowable基于activiti6衍生出来的版本,flowable目前最新版本是v6.7.0,开发团队是从activiti中分裂出来的,修复了一众activiti6的bug,并在其基础上研发了DMN支持,BPEL支持等等。

flowable发展过来,已经不单单关注流程引擎部分开发,而是以流程引擎为基础,衍生到决策引擎、表单引擎、案例模型引擎等其他大功能模块开发。

以下对 Flowable 进行简要说明:

  • 是一款轻量级的流程引擎,以 java 语言编写
  • 支持 BPMN2.0
  • 非常灵活,可以方便的部署到以 java 编程的环境中去
  • 多个应用开箱即用(Flowable Modeler, Flowable Admin, Flowable IDM and Flowable Task)

功能特性

  • 异步处理历史数据 当前版本处理历史数据与运行时数据处在同一个线程,大量使用案例表明,处理历史数据占用较长时间而用户不得不等待该线程事务的结束。改为异步处理后性能明显得到改善。
  • 回退功能 通过 API 方式,让工作流当前状态回滚到之前的状态。
  • 增加和拓展对事件子流程的支持
  • 提高对事件监听器事务生命周期的支持
  • 新增全局 Counter 功能

      Flowable 是 Activiti 的衍生版本,相对于 Activiti 来说,它具有以下更加丰富的特性:

      • flowable 已经支持所有的历史数据使用 mongdb 存储,activiti 没有。
      • flowable 支持事务子流程,activiti 没有。
      • flowable 支持多实例加签、减签,activiti 没有。
      • flowable 支持 httpTask 等新的类型节点,activiti 没有。
      • flowable 支持在流程中动态添加任务节点,activiti 没有。
      • flowable 支持历史任务数据通过消息中间件发送,activiti 没有。
      • flowable 支持 java11,activiti 没有。
      • flowable 支持动态脚本,,activiti 没有。
      • flowable 支持条件表达式中自定义 juel 函数,activiti 没有。
      • flowable 支持 cmmn 规范,activiti 没有。
      • flowable 修复了 dmn 规范设计器,activit 用的 dmn 设计器还是旧的框架,bug 太多。
      • flowable 屏蔽了 pvm,activiti6 也屏蔽了 pvm(因为 6 版本官方提供了加签功能,发现 pvm 设计的过于臃肿,索性直接移除,这样加签实现起来更简洁、事实确实如此,如果需要获取节点、连线等信息可以使用 bpmnmodel 替代)。
      • flowable 与 activiti 提供了新的事务监听器。activiti5 版本只有事件监听器、任务监听器、执行监听器。-
      • flowable 对 activiti 的代码大量的进行了重构。
      • activiti 以及 flowable 支持的数据库有 h2、hsql、mysql、oracle、postgres、mssql、db2。其他数据库不支持的。

      flowable的基本操作

      首先创建一个Maven项目,pom中引入的依赖如下

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
      
          <groupId>ykuee</groupId>
          <artifactId>flowable-yktest</artifactId>
          <version>1.0-SNAPSHOT</version>
      
          <properties>
              <maven.compiler.source>8</maven.compiler.source>
              <maven.compiler.target>8</maven.compiler.target>
          </properties>
      
          <dependencies>
              <!-- flowable流程引擎引入 -->
              <dependency>
                  <groupId>org.flowable</groupId>
                  <artifactId>flowable-engine</artifactId>
                  <version>6.3.0</version>
              </dependency>
              <!-- mysql jdbc驱动 -->
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>8.0.28</version>
              </dependency>
              <!-- 单元测试 -->
              <dependency>
                  <groupId>junit</groupId>
                  <artifactId>junit</artifactId>
                  <version>4.12</version>
                  <scope>test</scope>
              </dependency>
              <!-- flowable使用slf4j作为内部日志框架 -->
              <dependency>
                  <groupId>org.slf4j</groupId>
                  <artifactId>slf4j-api</artifactId>
                  <version>1.7.21</version>
              </dependency>
              <!-- 使用log4j作为slf4j的实现 -->
              <dependency>
                  <groupId>org.slf4j</groupId>
                  <artifactId>slf4j-log4j12</artifactId>
                  <version>1.7.21</version>
                  <scope>test</scope>
              </dependency>
          </dependencies>
      </project>
      

       

      创建流程引擎实例

      1.我们可以通过直接手写配置类的方式构建

              // 获取配置器实例
              ProcessEngineConfiguration config = new StandaloneProcessEngineConfiguration();
              config.setJdbcDriver("com.mysql.cj.jdbc.Driver")
                      .setJdbcUrl("jdbc:mysql://localhost:3306/flowable_test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE")
                      .setJdbcUsername("ykuee")
                      .setJdbcPassword("123456")
                      /*
                      * flase:       默认值。activiti在启动时,会对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常。(生产环境常用)
                      * true:        activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建。(开发时常用)
                      * create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)。(单元测试常用)
                      * drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)。
                      */
                      .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
              ProcessEngine processEngine = config.buildProcessEngine();
              processEngine.getRepositoryService();

      flowable提供的配置类有以下几个

      • JtaProcessEngineConfiguration 通过类名也知道该类支持 JTA事物
      • MultiSchemaMultiTenantProcessEngineConfiguration “多数据库多租户” 流程引擎配置类,通过此类为开发人员提供了自动路由机制,这样当流程引擎需要连接对各数据库进行操作时,客户端无需关心引擎到底连接的是哪个数据库,该类通过路由规则自动选择需要自动操作的数据库,数据库的操作对客户端来说是透明的,客户端无需关心其内部路由的实现机制。
      • StandaloneInMemProcessEngineConfiguration 该类通常可以在开发环境中自测使用,默认采用 H2 数据可存储数据
      • StandaloneProcessEngineConfiguration 标准的流程引擎配置类

      偷偷看下源码,发现标准配置类初始化了非常多的东西

       

      2.除了上面的硬编码的方式外,我们还可以在 resources 目录下创建一个 flowable.cfg.xml 文件,注意这个名称是固定的哦。内容如下:

      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
          <bean id="processEngineConfiguration"
                class="org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration">
              <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/flowable_test?useUnicode=true&amp;characterEncoding=utf8&amp;autoReconnect=true&amp;rewriteBatchedStatements=true" />
              <property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver" />
              <property name="jdbcUsername" value="ykuee" />
              <property name="jdbcPassword" value="123456" />
              <property name="databaseSchemaUpdate" value="true" />
              <property name="asyncExecutorActivate" value="false" />
          </bean>
      </beans>

      通过以下代码也获取流程引擎实例

             processEngine = ProcessEngines.getDefaultProcessEngine();
              processEngine.getRepositoryService();

      再看看源码是怎么读取的配置文件

      进入init方法

      ProcessEngines 会扫描 flowable.cfg.xml 与 flowable-context.xml 文件。对于 flowable.cfg.xml 文件,流程引擎会以标准 Flowable 方式构建引擎:ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(inputStream).buildProcessEngine()。对于 flowable-context.xml 文件,流程引擎会以 Spring 的方式构建:首先构建 Spring 应用上下文,然后从该上下文中获取流程引擎。

      所有的服务都是无状态的。这意味着你可以很容易的在集群环境的多个节点上运行 Flowable,使用同一个数据库,而不用担心上一次调用实际在哪台机器上执行。不论在哪个节点执行,对任何服务的任何调用都是幂等(idempotent)的。

       

      在配置文件装入内存后,配置类的初始化就和之前的是一样的了。

      运行代码后,查看数据库就发现已经有表创建进去了。

       

      引擎表结构

      在获取到引擎实例后,数据库中会自动创建引擎所需要的表结构。

      工作流程的相关操作都是操作存储在对应的表结构中,为了能更好的弄清楚 flowable 的实现原理和细节,我们有必要先弄清楚 flowable 的相关表结构及其作用。在 flowable 中的表结构在初始化的时候会创建五类表结构,具体如下:

      • ACT_RE :'RE’表示 repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
      • ACT_RU:'RU’表示 runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Flowable 只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。
      • ACT_HI:'HI’表示 history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。
      • ACT_GE: GE 表示 general。 通用数据, 用于不同场景下
      • ACT_ID: ’ID’表示 identity (组织机构)。这些表包含标识的信息,如用户,用户组,等等。

      具体的表结构的含义

      表分类 表名 解释
      一般数据
      [ACT_GE_BYTEARRAY] 通用的流程定义和流程资源
      [ACT_GE_PROPERTY] 系统相关属性
      流程历史记录
      [ACT_HI_ACTINST] 历史的流程实例
      [ACT_HI_ATTACHMENT] 历史的流程附件
      [ACT_HI_COMMENT] 历史的说明性信息
      [ACT_HI_DETAIL] 历史的流程运行中的细节信息
      [ACT_HI_IDENTITYLINK] 历史的流程运行过程中用户关系
      [ACT_HI_PROCINST] 历史的流程实例
      [ACT_HI_TASKINST] 历史的任务实例
      [ACT_HI_VARINST] 历史的流程运行中的变量信息
      流程定义表
      [ACT_RE_DEPLOYMENT] 部署单元信息
      [ACT_RE_MODEL] 模型信息
      [ACT_RE_PROCDEF] 已部署的流程定义
      运行实例表
      [ACT_RU_EVENT_SUBSCR] 运行时事件
      [ACT_RU_EXECUTION] 运行时流程执行实例
      [ACT_RU_IDENTITYLINK] 运行时用户关系信息,存储任务节点与参与者的相关信息
      [ACT_RU_JOB] 运行时作业
      [ACT_RU_TASK] 运行时任务
      [ACT_RU_VARIABLE] 运行时变量表
      用户用户组表
      [ACT_ID_BYTEARRAY] 二进制数据表
      [ACT_ID_GROUP] 用户组信息表
      [ACT_ID_INFO] 用户信息详情表
      [ACT_ID_MEMBERSHIP] 人与组关系表
      [ACT_ID_PRIV] 权限表
      [ACT_ID_PRIV_MAPPING] 用户或组权限关系表
      [ACT_ID_PROPERTY] 属性表
      [ACT_ID_TOKEN] 记录用户的 token 信息
      [ACT_ID_USER] 用户表

       

      service服务接口

      Service 是工作流引擎提供用于进行工作流部署、执行、管理的服务接口,我们使用这些接口可以就是操作服务对应的数据表

      通过 ProcessEngine 创建 Service

      RuntimeService runtimeService = processEngine.getRuntimeService();
      RepositoryService repositoryService = processEngine.getRepositoryService();
      TaskService taskService = processEngine.getTaskService();
      // ...
      service 名称 service 作用
      RepositoryService Flowable 的资源管理类
      RuntimeService Flowable 的流程运行管理类
      TaskService Flowable 的任务管理类
      HistoryService Flowable 的历史管理类
      ManagerService Flowable 的引擎管理类

      简单介绍:

      • RepositoryService 很可能是使用 Flowable 引擎要用的第一个服务。这个服务提供了管理与控制部署 (deployments) 与流程定义 (process definitions) 的操作。管理静态信息,
      • RuntimeService 用于启动流程定义的新流程实例。
      • IdentityService 很简单。它用于管理(创建,更新,删除,查询……)组与用户。请注意,Flowable 实际上在运行时并不做任何用户检查。例如任务可以分派给任何用户,而引擎并不会验证系统中是否存在该用户。这是因为 Flowable 有时要与 LDAP、Active Directory 等服务结合使用。
      • FormService 是可选服务。也就是说 Flowable 没有它也能很好地运行,而不必牺牲任何功能。这个服务引入了开始表单 (start form) 与任务表单 (task form) 的概念。 开始表单是在流程实例启动前显示的表单,而任务表单是用户完成任务时显示的表单。Flowable 可以在 BPMN 2.0 流程定义中定义这些表单。表单服务通过简单的方式暴露这些数据。再次重申,表单不一定要嵌入流程定义,因此这个服务是可选的。
      • TaskService 所有任务相关的东西都组织在其中,需要用户操作的任务。例如创建、独立运行 (standalone)任务,决定任务的执行用户 (assignee),或者将用户通过某种方式与任务关联,认领 (claim) 与完成 (complete) 任务
      • HistoryService 暴露 Flowable 引擎收集的所有历史数据。当执行流程时,引擎会保存许多数据(可配置),例如流程实例启动时间、谁在执行哪个任务、完成任务花费的事件、每个流程实例的执行路径,等等。这个服务主要提供查询这些数据的能力。
      • ManagementService 通常在用 Flowable 编写用户应用时不需要使用。它可以读取数据库表与表原始数据的信息,也提供了对作业 (job) 的查询与管理操作。Flowable 中很多地方都使用作业,例如定时器 (timer),异步操作 (asynchronous continuation),延时暂停 / 激活 (delayed suspension/activation) 等等。后续会详细介绍这些内容。
      • DynamicBpmnService 可用于修改流程定义中的部分内容,而不需要重新部署它。例如可以修改流程定义中一个用户任务的办理人设置,或者修改一个服务任务中的类名。

       

      创建流程实例

      我们这里使用官网的流程示例,是一个非常简单的请假流程。Flowable 引擎需要流程定义为 BPMN 2.0 格式,这是一个业界广泛接受的 XML 标准。 在 Flowable 术语中,我们将其称为一个流程定义 (process definition)。一个流程定义可以启动多个流程实例 (process instance)流程定义可以看做是重复执行流程的蓝图。 在这个例子中,流程定义定义了请假的各个步骤,而一个流程实例对应某个员工提出的一个请假申请。

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

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

      这个流程very简单。但为了明确起见,说明一下几个要点:

      • 我们假定启动流程需要提供一些信息,例如雇员名字、请假时长以及说明。当然,这些可以单独建模为流程中的第一步。 但是如果将它们作为流程的 “输入信息”,就能保证只有在实际请求时才会建立一个流程实例。否则(将提交作为流程的第一步),用户可能在提交之前改变主意并取消,但流程实例已经创建了。 在某些场景中,就可能影响重要的指标(例如启动了多少申请,但还未完成),取决于业务目标。
      • 左侧的圆圈叫做启动事件 (start event)。这是一个流程实例的起点。
      • 第一个矩形是一个用户任务 (user task)。这是流程中人类用户操作的步骤。在这个例子中,经理需要批准或驳回申请。
      • 取决于经理的决定,排他网关 (exclusive gateway) (带叉的菱形) 会将流程实例路由至批准或驳回路径。
      • 如果批准,则需要将申请注册至某个外部系统,并跟着另一个用户任务,将经理的决定通知给申请人。当然也可以改为发送邮件。
      • 如果驳回,则为雇员发送一封邮件通知他。

      一般来说,这样的流程定义使用可视化建模工具建立,如 Flowable Designer (Eclipse) 或 Flowable Web Modeler (Web 应用)。

      但在这里我们直接撰写 XML,以熟悉 BPMN 2.0 及其概念。

      与上面展示的流程图对应的 BPMN 2.0 XML 在下面显示。请注意这只包含了 “流程部分”。如果使用图形化建模工具,实际的 XML 文件还将包含 “可视化部分”,用于描述图形信息,如流程定义中各个元素的坐标(所有的图形化信息包含在 XML 的 BPMNDiagram 标签中,作为 definitions 标签的子元素)。

      将下面的 XML 保存在 src/main/resources 文件夹下名为 holiday-request.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: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"
        xmlns:flowable="http://flowable.org/bpmn"
        typeLanguage="http://www.w3.org/2001/XMLSchema"
        expressionLanguage="http://www.w3.org/1999/XPath"
        targetNamespace="http://www.flowable.org/processdef">
      
        <process id="holidayRequest" name="Holiday Request" isExecutable="true">
      
          <startEvent id="startEvent"/>
          <sequenceFlow sourceRef="startEvent" targetRef="approveTask"/>
      
          <userTask id="approveTask" name="Approve or reject request"/>
          <sequenceFlow sourceRef="approveTask" targetRef="decision"/>
      
          <exclusiveGateway id="decision"/>
          <sequenceFlow sourceRef="decision" targetRef="externalSystemCall">
            <conditionExpression xsi:type="tFormalExpression">
              <![CDATA[
                ${approved}
              ]]>
            </conditionExpression>
          </sequenceFlow>
          <sequenceFlow  sourceRef="decision" targetRef="sendRejectionMail">
            <conditionExpression xsi:type="tFormalExpression">
              <![CDATA[
                ${!approved}
              ]]>
            </conditionExpression>
          </sequenceFlow>
      
          <serviceTask id="externalSystemCall" name="Enter holidays in external system"
              flowable:class="org.flowable.CallExternalSystemDelegate"/>
          <sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/>
      
          <userTask id="holidayApprovedTask" name="Holiday approved"/>
          <sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/>
      
          <serviceTask id="sendRejectionMail" name="Send out rejection email"
              flowable:class="org.flowable.SendRejectionMail"/>
          <sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>
      
          <endEvent id="approveEnd"/>
      
          <endEvent id="rejectEnd"/>
      
        </process>
      
      </definitions>

      流程文件可以使用工具画出来,然后生成xml,flowable-ui也有相应功能。

      现在我们已经有了流程 BPMN 2.0 XML 文件,下来需要将它部署 (deploy) 到引擎中。部署一个流程定义意味着:

      • 流程引擎会将 XML 文件存储在数据库中,这样可以在需要的时候获取它。
      • 流程定义转换为内部的、可执行的对象模型,这样使用它就可以启动流程实例

      将流程定义部署至 Flowable 引擎,需要使用 RepositoryService,其可以从 ProcessEngine 对象获取。使用 RepositoryService,可以通过 XML 文件的路径创建一个新的部署 (Deployment),并调用 deploy() 方法实际执行:

              RepositoryService repositoryService = processEngine.getRepositoryService();
              Deployment deployment = repositoryService.createDeployment()
                      .addClasspathResource("holiday-request.bpmn20.xml")
                      .deploy();
              log.info("deployment id:{}",deployment.getId());

      日志中也打印出流程ID

      11:41:51,180 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource  - Returned connection 1934932165 to pool.
      11:41:51,180 [main] DEBUG org.flowable.common.engine.impl.interceptor.LogInterceptor  - --- DeployCmd finished --------------------------------------------------------
      11:41:51,180 [main] INFO  FlowableTest  - deployment id:5001
      
      进程已结束,退出代码0

      查看数据库表,也已创建成功

       

      查询流程定义信息

      使用刚刚的流程id查询

              RepositoryService repositoryService = processEngine.getRepositoryService();
              ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                      .deploymentId("5001")
                      .singleResult();
              log.info("name:{}",processDefinition.getName());

      日志打印出任务名称

      11:44:27,927 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource  - Returned connection 1934932165 to pool.
      11:44:27,928 [main] DEBUG org.flowable.common.engine.impl.interceptor.LogInterceptor  - --- ProcessDefinitionQueryImpl finished --------------------------------------------------------
      11:44:27,928 [main] INFO  FlowableTest  - name:HolidayRequest
      
      进程已结束,退出代码0

       

      删除流程定义信息

      第二个参数是否级联删除,一般流程在运行是不能被删除的,true会强制删除,与之关联的数据也会一并删除。

              RepositoryService repositoryService = processEngine.getRepositoryService();
              //是否级联删除
              repositoryService.deleteDeployment("5001",true);

      此时再查看数据库,发现流程已经被删除掉了。

       

      启动流程实例

      接下来,我们使用 RuntimeService 启动一个流程实例。传入参数放到MAP里传递,其中的键就是之后用于获取变量的标识符。这个流程实例使用 BPMN 2.0 XML 文件中设置的 id 属性启动,在这个例子里是 holidayRequest。

              //通过RuntimeService启动流程实例
              RuntimeService runtimeService = processEngine.getRuntimeService();
              //构建流程变量
              Map<String, Object> variables = new HashMap<String, Object>();
              variables.put("employee", "Ykuee");
              variables.put("nrOfHolidays", 100);
              variables.put("description", "寄");
              //启动流程实例
              ProcessInstance holidayRequest = runtimeService.startProcessInstanceByKey("holidayRequest", variables);
              log.info("processDefinitionId:{}",holidayRequest.getProcessDefinitionId());
              log.info("activityId:{}",holidayRequest.getActivityId());
              log.info("id:{}",holidayRequest.getId());

      日志输出

      12:11:52,205 [main] DEBUG org.flowable.common.engine.impl.interceptor.LogInterceptor  - --- StartProcessInstanceCmd finished --------------------------------------------------------
      12:11:52,205 [main] INFO  FlowableTest  - processDefinitionId:holidayRequest:1:7503
      12:11:52,205 [main] INFO  FlowableTest  - activityId:null
      12:11:52,205 [main] INFO  FlowableTest  - id:10001
      
      进程已结束,退出代码0

      查看数据库,看到参数已经保存了,并且任务也有了流转。



       

      查询当前并处理任务

      现在我们需要处理任务,但之前BPMN文件里没有定义审批人,所以需要修改一下,相应的,之前的任务也会作废,删掉任务,重新部署再重新提交一个请假申请。

      <userTask id="approveTask" name="Approve or reject request" flowable:assignee="boss"/>

      通过 assignee,指定一个审批请假的人 boss。

      重新提交后,我们通过boss查看流转到他这里的任务

              TaskService taskService = processEngine.getTaskService();
              List<Task> taskList = taskService.createTaskQuery()
                      .processDefinitionKey("holidayRequest")
                      .taskAssignee("boss")
                      .list();
              for(Task task : taskList){
                  log.info("taskId:{}",task.getId());
                  log.info("name:{}",task.getName());
                  log.info("assignee:{}",task.getAssignee());
                  log.info("desc:{}",task.getDescription());
                  log.info("procDefId:{}",task.getProcessDefinitionId());
              }

      日志打印

      12:36:14,368 [main] DEBUG org.flowable.common.engine.impl.interceptor.LogInterceptor  - --- TaskQueryImpl finished --------------------------------------------------------
      12:36:14,368 [main] INFO  FlowableTest  - taskId:15009
      12:36:14,368 [main] INFO  FlowableTest  - name:Approve or reject request
      12:36:14,368 [main] INFO  FlowableTest  - assignee:boss
      12:36:14,368 [main] INFO  FlowableTest  - desc:null
      12:36:14,368 [main] INFO  FlowableTest  - procDefId:holidayRequest:1:12503
      
      进程已结束,退出代码0

      查询到任务,我们就可以以boss的身份处理它,直接拒绝掉。

              TaskService taskService = processEngine.getTaskService();
              Task task = taskService.createTaskQuery()
                      .processDefinitionKey("holidayRequest")
                      .taskAssignee("boss")
                      .singleResult();
              Map<String, Object> variables = new HashMap<String, Object>();
              variables.put("approved", false);
              taskService.complete(task.getId(),variables);

      这里我们传入的变量approved正是BPMN中定义的变量,在xml定义里,如果被拒绝,还会进行下一步,发送邮件

              <serviceTask id="sendRejectionMail" name="Send out rejection email"
                           flowable:class="org.flowable.SendRejectionMail"/>

      这其实就像一个触发器,审批拒绝之后就会触发,来整一个。

      package org.flowable;
      
      import org.flowable.engine.delegate.DelegateExecution;
      import org.flowable.engine.delegate.JavaDelegate;
      
      /**
       * @program: flowable-yktest
       * @description:
       * @author: 27872
       * @create: 2022-06-19 12:48
       **/
      public class SendRejectionMail implements JavaDelegate {
      
          @Override
          public void execute(DelegateExecution execution) {
              //执行触发器逻辑
              System.out.println("撸起袖子加油干");
          }
      }

      查看日志,可以发现已经触发了这段代码,任务也结束了流程。

      12:50:34,206 [main] DEBUG org.flowable.engine.impl.history.AbstractHistoryManager  - Current history level: AUDIT, level required: ACTIVITY
      12:50:34,206 [main] DEBUG org.flowable.engine.impl.agenda.ContinueProcessOperation  - Executing activityBehavior class org.flowable.engine.impl.bpmn.helper.ClassDelegate on activity 'sendRejectionMail' with execution 15005
      撸起袖子加油干
      12:50:34,208 [main] DEBUG org.flowable.engine.impl.agenda.DefaultFlowableEngineAgenda  - Operation class org.flowable.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation added to agenda

      act_ru_task中已经不见了

      在历史任务中可以查到踪迹

       

      历史任务查询

      选择使用 Flowable 这样的流程引擎的原因之一,是它可以自动存储所有流程实例的审计数据历史数据。这些数据可以用于创建报告,深入展现组织运行的情况,瓶颈在哪里,等等。

      例如,如果希望显示流程实例已经执行的时间,就可以从 ProcessEngine 获取 HistoryService,并创建历史活动 (historical activities) 的查询。在下面的代码片段中,可以看到我们添加了一些额外的过滤条件:

      • 只选择一个特定流程实例的活动
      • 只选择已完成的活动

      结果按照结束时间排序,代表其执行顺序。

              HistoryService historyService = processEngine.getHistoryService();
              List<HistoricActivityInstance> activities =
                      historyService.createHistoricActivityInstanceQuery()
                              .processDefinitionId("holidayRequest:1:12503")
                              .finished()
                              .orderByHistoricActivityInstanceEndTime().asc()
                              .list();
              for (HistoricActivityInstance activity : activities) {
                  log.info(activity.getActivityId() + " took "
                          + activity.getDurationInMillis() + " milliseconds");
              }

      查看控制台

      12:59:46,264 [main] DEBUG org.flowable.common.engine.impl.interceptor.LogInterceptor  - --- HistoricActivityInstanceQueryImpl finished --------------------------------------------------------
      12:59:46,264 [main] INFO  FlowableTest  - startEvent took 2 milliseconds
      12:59:46,264 [main] INFO  FlowableTest  - _flow_startEvent__approveTask took 0 milliseconds
      12:59:46,264 [main] INFO  FlowableTest  - approveTask took 1355323 milliseconds
      12:59:46,264 [main] INFO  FlowableTest  - _flow_approveTask__decision took 0 milliseconds
      12:59:46,264 [main] INFO  FlowableTest  - decision took 12 milliseconds
      12:59:46,264 [main] INFO  FlowableTest  - _flow_decision__sendRejectionMail took 0 milliseconds
      12:59:46,264 [main] INFO  FlowableTest  - sendRejectionMail took 2 milliseconds
      12:59:46,264 [main] INFO  FlowableTest  - _flow_sendRejectionMail__rejectEnd took 0 milliseconds
      12:59:46,264 [main] INFO  FlowableTest  - rejectEnd took 1 milliseconds
      
      进程已结束,退出代码0

       

      结尾

      Flowable 引擎支持的功能与特性还有很多,这仅仅是个开始。

      从以上的示例流程看出,引擎在实际使用过程中大致流程:

      • 前端配置流程,将流程配置信息以 XML 或者 JSON 的格式给到后端
      • 后端调用流程引擎的部署方法,部署流程
      • 后端调用流程引擎的发起流程方法,发起一个流程,流程引擎根据我们配置的流程信息,流程推进到第一个任务节点
      • 流程引擎的每个任务节点到达时可以发出一个事件,我们在不同的事件可以处理一些任务到达时的逻辑
      • 后端调用流程引擎的推动流程方法,对流程进行推动,流程引擎会推动到下一个任务节点
      • 重复以上操作,直到流程结束

      啦啦啦!