Flowable工作流实战快速入门(一)

1. 工作流入门介绍

1.1 什么是工作流?

实际生活中有一些例如办公类流程需要多个环节人员进行审批,以达到对整个工作流程的控制与掌握。为提高效率,借助计算机对业务流程自动化执行管理,而Flowable框架就可以帮助我们进行工作流程进行管理工作。而Flowable可以帮助我们即是业务流程发送了变更,我们的业务流程代码也无需进行更新。

1.2 工作流的原理

如何做到我们在业务流程发生变更后,我们的业务系统代码可以不发生改变?原理如下:
在这里插入图片描述

将业务流程的每个节点读取到数据库中,这样每个节点(包括开始节点和结束节点)就是数据库中的一条记录,当发生业务流程的时候,不断的从业务流程图中读取下一个节点,其实就相当于操作节点对应的数据库记录,这样就实现流程管理和状态字段无关。

1.3 BPM

  • BPM(Business Process Management),即业务流程管理,是一种以规范化的构造端到端的卓越业务流程为中心,以持续的提高组织业务绩效为目的的系统化方法,常见商业管理教育如EMBA、MBA等均将BPM包含在内。
  • 企业流程管理主要是对企业内部改革,改变企业只能管理机构重叠、中间层次多、流程不闭环等,做到机构不重叠、业务不复杂,达到缩短流程周期、节约运作资本、提高企业效益的作用。

1.4 BPMN

BPMN(Business Process Model And Notation),业务流程模型和符号,是由BPMI(Business Process Management Initiative)开发的一套的业务流程建模符号,使用BPMN提供的符号可以创建业务流程。2004年5月发布了BPMN1.0规范。BPMI于2005年9月并入OMG(The Object Management Group,对象管理组织)组织。OMG于2011年1月发布BPMN2.0的最终版本。

1.5 Activiti 还是flowable?

下表是Activiti7 与flowable的相关功能特性对比说明。

对比项\引擎Activiti-7.xFlowable-6.x
商业化
路线(Roadmap)工具型
PVM引擎××
BPMN2引擎
CMMN引擎×
DMN引擎×√(开源版支持不太好)
[建模工具选型√(AngularJS)√(AngularJS)
建模工具内容BPMN2BPMN2/CMMN/DMN
扩展节点(Mule\Http等)×
Spring Boot
Spring Cloud×
Web控制台
Rest接口
历史异步归档×
异步任务全局锁×
  • Activiti

Activiti在目前来看有点不思进取,核心功能和内核的优化并没有太大进步,着力点全在商业版和云上面,核心只支持BPMN2协议,跟6版本没有什么区别。如果你是一个老的Activiti使用者,并且只是用BPMN2协议,可以选用Activiti(非Cloud版本)。

  • Flowable

Flowable不管是功能层面还是在代码层面来讲,都是这2个中最重的,当初跟Activiti分道扬镳的原因也是因为理念不一样,Flowable更注重其功能性、和性能。在上面表格中,历史异步归档和全局锁都是对性能的极大优化,特别是异步任务这一项,当年在使用Activiti的使用是一个极大的困扰,因为异步任务的吞吐反而会随着实例数的增加而加速恶化。Flowable比较臃肿,它支持了太多的东西,以致于如果想做POC或者Demo,环境搭建这一步都够呛。但是如果你本身就想做一个扩展性强的,性能高的工作流平台(SaaS\PaaS),Flowable是不二的选择。
而且flowable和activiti6是同一个团队开发的,activiti先开发,flowable后开发,所以flowable 算是 activiti6的升级版。

2. flowable入门helloWorld

2.1 导入依赖

首先导入Flowable相关Pom依赖如下所示:

  <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
         <logback.version>1.2.3</logback.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-engine</artifactId>
            <version>6.3.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-access</artifactId>
            <version>${logback.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>${logback.version}</version>
        </dependency>
    </dependencies>

2.2 初始化数据库表结构

新增测试类初始化Flowable数据库表的配置类如下:

  private ProcessEngineConfiguration processEngineConfiguration;

    public DataSource initDataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setUrl("jdbc:mysql://1.14.49.145:23306/flowable6?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true");
        dataSource.setUsername("root");
        dataSource.setPassword("Xxxxx");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setMaxActive(3);
        dataSource.setMinIdle(1);
        return dataSource;
    }


    /**
     * 初始化数据库表34张表
     */
    @Before
    public void initDataBaseTables() {
        processEngineConfiguration = new StandaloneProcessEngineConfiguration();
        processEngineConfiguration.setDataSource(initDataSource());
        processEngineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
        processEngineConfiguration.buildProcessEngine();
    }

运行测试类输出如下结果:同时查看Flowable数据库,发现新增了34张表如下所示:
在这里插入图片描述

2.4 flowable表命名规则

Flowable的表都是以ACT_开头。第二部分是表示表的用途的两个字母标识。用途也和服务的API对应。
ACT_RE_:'RE’表示Repository。这个前缀的表包含了流程定义和流程静态资源(图片、规则等等)。
ACT_RU_
:'RU’表示Runtime。这些运行时的表,包含流程实例,任务、变量,异步任务等运行中的数据。Flowable只在流程实例执行过程中保存这些数据,在流程结束时就会删除这些记录。这些运行时表可以一直很小并且速度很快。
ACT_HI_:'HI’表示History。这些表包含历史数据,比如历史流程实例,变量,任务等等。
ACT_GE_
:'GE’表示General。通用数据,用于不同场景下。
ACT_ID_*:'ID’表示Identity。组织结构,包含标识的信息,如用户,用户组,等等。

3. Flowable服务

flowable 默认重要服务体系图如下所示:
在这里插入图片描述

3.1 重要Service类介绍

  • RepositoryService

Flowable的资源管理类,提供了管理和控制流程发布包和流程定义的操作。除了部署定义流程之外,此服务还支持查询引擎的发布包和流程定义。

  • RuntimeService

Flowable的流程运行管理类,可以从这个服务类中获取很多关于流程执行的相关信息。

  • TaskService

Flowable的任务管理类,可以获取当前任务信息。

  • HistoryService

Flowable历史管理类,可以查询历史信息,执行流程时,引擎会保存很多数据,比如流程实例启动时间,任务参与者等等。

  • ManagementService

Flowable引擎管理类,提供了Flowable流程引擎管理的管理和维护功能,主要用于Activity系统的日常维护。
下面我们将使用Flowable提供的相关service实现一个简单流程入门。

3.2 定义并流程

Flowable官网给我们提供了一个请假流程图如下所示:
在这里插入图片描述

首先这个流程开启了一个请假流程,此请求会被通过或拒绝,若请求通过,就进入外部系统去通过假期请求并结束整个流程。若请求拒绝就发送拒绝邮件然后结束整个流程。
Flowable支持BPMN2,0,所以整个流程可以用xml进行表示,所以上面的流程图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>

现在我们可借助于RepositoryService 完成流程部署,首先将上面流程图片与流程XML保存在resource目录下如下所示:
在这里插入图片描述

然后将此流程发布到Flowable引擎代码如下所示:

    /**
     * 部署请假流程
     */
    @Test
    public void deployWorkflow() {
        // 部署流程
        RepositoryService repositoryService = processEngineConfiguration.getRepositoryService();
        // 关联resource下xml文件与图片
        Deployment deployment = repositoryService.createDeployment().addClasspathResource("diagram/holiday-request.png")
                .addClasspathResource("diagram/holiday-request.bpmn20.xml")
                // 命名并部署
                .name("请假流程").category("人事流程管理").key("holiday-request").deploy();
        System.out.println("部署id:" + deployment.getId());
        System.out.println("部署名称:" + deployment.getName());
    }

运行结果如下所示:
在这里插入图片描述

可以在表 act_re_deployment 中 查看到此次流程部署信息入下图所示:
在这里插入图片描述

xml流程定义的内容被解析并将相关信息保存在 act_re_procdef 中如下所示:
在这里插入图片描述

Xml文档与流程图片直接保存在 act_ge_bytearray 中如下所示:
在这里插入图片描述

3.3 查询流程信息

Flowable提供了相关API可以让我们查询流程部署的相关信息,我们同样可借助RepositoryService构建_ProcessDefinitionQuery_ 对象,我们可以通过act_re_deployment 部署id为1的记录关联查询流程定义信息,act_re_deployment 表记录如下所示:
在这里插入图片描述
流程定义表内容如下所示:
在这里插入图片描述

查询代码如下所示:

    /**
     * 查询部署信息与请假流程信息
     */
    @Test
    public void queryDeployWorkflow() {
        // 部署流程
        RepositoryService repositoryService = processEngineConfiguration.getRepositoryService();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId("1")
                .singleResult();

        System.out.println("Found process definition : " + processDefinition.getName());
        System.out.println("Found process definition : " + processDefinition.getKey());
        System.out.println("Found process definition : " + processDefinition.getCategory());
        System.out.println("--------------------------------------------------------------");
        Deployment deployment = repositoryService.createDeploymentQuery().deploymentId("1").singleResult();
        System.out.println("Found deployment : " + deployment.getName());
        System.out.println("Found deployment : " + deployment.getCategory());
        System.out.println("Found deployment : " + deployment.getKey());
    }

运行结果如下:
在这里插入图片描述

3.4 删除流程

当定义的流程并发布到Flowable引擎上时,我们使用RepositoryService提供 deleteDeployment 删除流程定义的相关内容信息,具体代码如下:

    /**
     * 删除流程
     */
    @Test
    public void removeWorkflow() {
        RepositoryService repositoryService = processEngineConfiguration.getRepositoryService();
        // 第一个指定删除的流程id信息,第二参数若为true,不管流程是否已经启动都会进行强制删除,一般不推荐使用
        repositoryService.deleteDeployment("1",false);
    }

我们检查相关的三个表act_ge_bytearray、act_re_deployment、act_re_procdef 定义的内容都为空了。需要注意的是 deleteDeployment 方法如果为true,不管流程是否已经启动都会进行强制删除,一般不推荐使用。

4.3 启动流程实例

在上面的代码中我们已经将流程定义并发布到Flowable引擎上了,下面就可以启动定义的流程了,所以我们修改一下流程定义的xml并将流程重新部署发布到Flowable引擎上。
在这里插入图片描述
在这里插入图片描述

具体修改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" flowable:assignee="zhangsan"/>
    <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" flowable:assignee="lisi"/>
    <sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/>

    <serviceTask id="sendRejectionMail" name="Send out rejection email"
        flowable:class="com.galengao.SendRejectMail"/>
    <sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>

    <endEvent id="approveEnd"/>

    <endEvent id="rejectEnd"/>

  </process>

</definitions>

再次运行发布流程的代码,运行结果如下所示:
在这里插入图片描述

下面我们使用Flowable提供的RuntimeService提供的startProcessInstanceByKey方法开启一个流程实例,此方法可接收流程变量,具体代码如下所示:

    /**
     * 开启一个流程实例
     */
    @Test
    public void startProcessInstance() {
        RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();

        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("employee", "zhangsan");
        variables.put("duration", 5);
        variables.put("description", "go climbing");
        ProcessInstance processInstance =
                // 开启流程实例
                runtimeService.startProcessInstanceByKey("holidayRequest", variables);
        String deploymentId = processInstance.getDeploymentId();
        String activityId = processInstance.getActivityId();
        System.out.println("Deployment Id: "+ deploymentId+" Activity Id: "+ activityId);
    }

运行结果如下:
在这里插入图片描述

此次开启的流程实例可在act_ru_task 表查看如下所示;
在这里插入图片描述

而刚刚我们传入的Map参数保存在了act_ru_variable表中如下所示:
在这里插入图片描述

此次流程节点从开始流转到网关节点,一共经历了2个流程节点,我们可通过act_ru_execution 查看相关信息如下:
在这里插入图片描述

4.4 查询流程

流程任务信息可通过TaskService的createTaskQuery方法进行查询,具体实现代码如下所示:

    /**
     * 查询流程任务
     */
    @Test
    public void queryProcessInstance(){
        TaskService taskService = processEngineConfiguration.getTaskService();
        List<Task> list = taskService.createTaskQuery().processDefinitionKey("holidayRequest")
                .taskAssignee("zhangsan").list();
        for (Task task : list) {
            String processInstanceId = task.getProcessInstanceId();
            String name = task.getName();
            String assignee = task.getAssignee();
            String description = task.getDescription();
            String id = task.getId();
            System.out.println("processInstanceId: "+processInstanceId+" name: "+name+" assignee: "+assignee+" description: "+description+" id: "+id);
        }
    }

输出结果如下所示:
在这里插入图片描述

4.5 同意与拒绝流程

  • 拒绝流程

在流程xml定义文件中我们可以查看到如下内容:
在这里插入图片描述

Flowable提供了JavaDelegate接口,通过此接口可以做额外业务逻辑处理,例如当审批拒绝后实现发送拒绝邮件的逻辑,我们的发送邮件代码可以这样实现:

public class SendRejectMail implements JavaDelegate {

    /**
     * Flowable的触发器
     */
    @Override
    public void execute(DelegateExecution delegateExecution) {
        // TODO 此处省略真实发送邮件逻辑
        System.out.println("发送拒绝邮件通知------");
    }
}

然后再来编写拒绝流程的相关代码如下所示:

    /**
     * 拒绝流程,发送拒绝邮件
     */
    @Test
    public void completeProcessInstanceTask() {
        TaskService taskService = processEngineConfiguration.getTaskService();
        Task task = taskService.createTaskQuery().processDefinitionKey("holidayRequest")
                .taskAssignee("zhangsan").singleResult();
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("approved",false);
        taskService.complete(task.getId(),variables);
    }

运行结果如下所示:
在这里插入图片描述

同意流程与拒绝流程类似,只需要将上面变量Map **variables 的 **approved 设置为true即可。
JavaDelegate 接口是个非常有用的接口,我们可以通过此接口在审批通过或者拒绝时,额外进行相关业务处理。

4.6 查询流程历史信息

选择使用Flowable这样的流程引擎的众多原因之一是,它自动存储所有流程实例的审计数据或历史数据,这对我们进行后续进行工作汇总、报告提供前期的相关数据支持,例如我们想看到流程运行的时长,我们可借助HistoryService实现我们的需求,具体代码如下所示:

    /**
     * 查询流程历史信息
     */
    @Test
    public void queryHistoryProcessInstance(){
        HistoryService historyService = processEngineConfiguration.getHistoryService();
        // 查询流程历史激活信息
        HistoricActivityInstanceQuery activityInstanceQuery = historyService.createHistoricActivityInstanceQuery();
        // 查询结束的流程信息
        List<HistoricActivityInstance> activityInstances = activityInstanceQuery.processDefinitionId("holidayRequest:1:4").finished()
                // 按照流程结束时间倒排查询
                .orderByHistoricActivityInstanceEndTime().desc().list();
        for (HistoricActivityInstance instance : activityInstances) {
            String activityName = instance.getActivityName();
            String activityId = instance.getActivityId();
            Long durationInMillis = instance.getDurationInMillis();
            System.out.println("Activity: " + activityName + " activityId:" + activityId + " durationInMillis:" + durationInMillis);
        }
    }

4. 总结

本文Flowable和BPMN 2.0的概念和术语,同时借助与Flowable 提供的各种Service完成如下内容:

  • 流程定义
  • 流程定义查询
  • 删除流程定义
  • 启动流程实例
  • 查询流程实例
  • 查询流程相关历史信息

后面我们将继续更新Flowable相关内容,敬请期待。

5. 代码仓库

https://gitee.com/codegeekgao/frameworks-learn/tree/master/flowable-element

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值