likes
comments
collection
share

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

作者站长头像
站长
· 阅读数 13

最近在思考构建一个服务编排(Service Orchestration)系统,考虑这个系统至少需要具备以下特征:

  1. 使用统一的方法定义服务功能单元
  2. 使用一种通用的方式将一个或多个服务的输出映射到下游服务的输入,映射时支持基础的数据转换与处理
  3. 支持以搭积木的方式将低层服务功能单元组织成更高层抽象的服务功能,直至一个完整的服务
  4. 用户编排服务时,具备较大的灵活性定制业务

流程在各行各业中普遍存在,也是企业IT自动化、提升效率的重头戏。正因为如此,人们制定了相应的BPMBusiness Process Management)标准,设计了BPMNBusiness Process Model and Notation)、DMNDecision Model and Notation)、CMMNCase Management Model and Notation)等规范来标准化流程的设计、实现与管理过程。基于这些规范,出现了jBPM、activiti、camunda、flowable等众多优秀的BPM软件,由此也可以看出BPM应用的普遍和需求的多样性。

1 camunda架构简述

camunda整体架构如下图所示,主要包括两部分:流程建模工具(modeler)和流程引擎(Engine)。业务分析与开发人员(Business Analyst/Developer)通过modeler设计业务流程,将结果存入repository中,业务分析与开发人员一般不需要懂开发。流程引擎则负责流程实例的创建、执行、维护和管理,并通过REST/Java API向用户提供服务,基于这些API,流程用户(End User)通过Tasklist工具参与流程的执行,运维人员(operator)通过Cockpit工具查看、管理和维护流程的状态,管理人员(administrator)通过Admin工具进行用户的权限管理。当然,我们也可以基于REST/Java API开发自己的相应工具。通过下面的实例展示可知,camunda提供的tasklist、cockpit、admin工具非常有可能不是很符合你们团队的需求,需要自己开发部分功能。

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

1.1 流程建模工具modeler

modeler可以在 官方下载地址 下载相应的版本。

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门 安装后启动,可以看到以下界面。点击BPMN diagramDMN diagramForm按钮可以分别创建流程图、DMN决策规则和表单。由于Camunda Platform 8需要很多其他依赖组件,我们一般使用Camunda Platform 7就可以了。

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

流程建模工具工作区主要元素功能介绍:

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

1.2 流程引擎平台

camunda提供了可以直接运行的安装包,可以直接在 官方下载地址 下载使用。 不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门 不过,考虑到流程引擎一般需要开发一些功能,所以一般将Camunda platform集成到我们的spring boot应用中。

pom文件中添加以下依赖:

    <properties>
        <java.version>17</java.version>
        <!--  与java 17兼容的camunda版本 -->
        <camunda.version>7.20.0-alpha4</camunda.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.camunda.bpm</groupId>
                <artifactId>camunda-bom</artifactId>
                <version>${camunda.version}</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- REST API -->
        <dependency>
            <groupId>org.camunda.bpm.springboot</groupId>
            <artifactId>camunda-bpm-spring-boot-starter-rest</artifactId>
        </dependency>

        <!-- web前端 -->
        <dependency>
            <groupId>org.camunda.bpm.springboot</groupId>
            <artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

如果后端使用H2数据库,则在pom文件中添加相关依赖:

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.2</version>
        </dependency>
        
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>

同时在application.yml配置文件中添加H2配置

spring.datasource.url: jdbc:h2:file:./camunda-h2-database

如果后端使用mysql数据库,则在pom文件中添加mysql相关依赖:


        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.13</version>
        </dependency>

同时在application.yml配置文件中添加以下mysql相关配置

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/camunda?serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&failOverReadOnly=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true
    username: root
    password: mysql123@Sys
    driver-class-name: com.mysql.cj.jdbc.Driver

然后在mysql创建camunda数据库:

mysql> CREATE DATABASE IF NOT EXISTS camunda DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;

不管后端使用H2数据库还是mysql数据库,最后在application.yml配置文件中添加一个camunda的管理员账户:

server:
  port: 8888

camunda.bpm.admin-user:
  # 用户名
  id: demo
  # 密码
  password: demo

程序启动后,我们查看mysql camunda库(或H2),可以发现camunda自动添加了49张表。这些表的功能分类如下:

  1. ACT_RE_*: 'RE'表示流程资源,这个前缀的表包含了流程定义和流程静态资源(图片,规则等),共5张表。
  2. ACT_RU_*: 'RU'表示流程运行时。 这些运行时的表,包含流程实例,任务,变量,Job等运行中的数据。 Camunda只在流程实例执行过程中保存这些数据,在流程结束时就会删除这些记录, 这样运行时表的数据量最小,可以最快运行。共15张表。
  3. ACT_ID_*: 'ID'表示组织用户信息,比如用户,组等,共6张表。
  4. ACT_HI_*: 'HI'表示流程历史记录。 这些表包含历史数据,比如历史流程实例,变量,任务等,共18张表。
  5. ACT_GE_*: ‘GE’表示流程通用数据, 用于不同场景下,共3张表

camunda提供了相应的类来处理相关的功能,如下所示:

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门 同时,我们可以通过web访问camunda的Tasklist、Cockpit和Admin,访问http://localhost:8888:

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门 输入application.yml文件中配置的管理账号名和密码:demo/demo,就可以登录了:

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门 不过这时继续查看Tasklist、Cockpit、Admin中的内容都是空的

2 第一个简单例子

我们设计一个简单的贷款流程来说明流程的编排、部署、执行和管理功能。贷款流程的功能为:用户提交贷款申请,提供贷款申请额度和贷款的工程项目,然后流程进行贷款发放。

2.1 流程编排

2.1.1 编辑流程基本信息

如下图所示,新建一个BPMN diagram文件,不选中任何流程组件,在右侧的流程属性配置栏中配置流程的基本信息。name为流程显示的名字,ID是camunda系统内流程的唯一标识,camunda中每一个流程、每一个组件都有一个ID标识,如果用户没有配置,系统会自动生成一个。Executable需要选中,该流程才能启动执行。

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

2.1.2 编辑开始节点

所有的流程都必须有开始节点和结束节点。如下图所示,选中开始节点,然后配置开始节点。我们为流程开始节点添加了两个表单变量,同时我们定义了loanApplicant流程变量,存储流程发起人信息。流程变量相当于流程实例的全局变量,在任何流程节点可以获取或修改流程变量的值

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

2.1.3 编辑任务

如下图所示,我们继续添加一个服务任务(Service Task),它的实现类型选Delegate expresssion,表达式填写${loanGrantService},我们将创建一个名字为loanGrantService的spring bean来实现该服务的功能。任务(Task)是camunda的核心组件,也是最常用的组件,表示流程中的一个具体功能。当流程流转到服务任务节点时,服务任务会自动执行。如果你添加一个用户任务(User Task),则需要具备执行权限的用户执行相应的任务。 不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

loanGrantService任务的代码如下所示,它实现了camunda的JavaDelegate接口,并通过DelegateExecution获取用户发起贷款申请事件设置的流程变量值,也就是申请人、申请金额、申请用途等信息的内容

package movee.camunda;

import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Service;

@Service("loanGrantService")
public class LoanGrantService implements JavaDelegate {

    @Override
    public void execute(DelegateExecution execution) throws Exception {
        // 申请人
        final String loanApplicant = String.valueOf(execution.getVariable("loanApplicant"));
        // 申请金额
        final long loanAmount = (long) execution.getVariable("loanAmount");
        // 用途
        final String useProject = (String) execution.getVariable("useProject");

        System.out.println("贷款申请人:" + loanApplicant +
                ",贷款金额(元):" + loanAmount + ", 用途项目:" + useProject);
    }
}

2.1.4 编辑结束节点

最后添加结束节点,我们没有为结束节点配置任何属性,保存流程,文件名为:diagram_loan.bpmn。完整的流程如下图所示:

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

2.2 流程部署

在modeler中设计完流程后,可以点击modeler下方的deploy图标,填写流程引擎rest api地址将流程部署到流程引擎中。如下图所示:

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

也可以将流程图文件diagram_loan.bpmn放到项目的java/main/resouces/BPMN目录下,重新启动程序进行部署。

流程部署后,我们可以在流程引擎dashboard的cockpit页面看到部署的流程。

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

2.3 发起流程

我们可以在流程引擎dashboard的TaskList页面点击Start process图标,在弹窗的对话框中填写表单参数贷款金额和应用工程信息,然后启动流程,如下图所示:

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门 这时可以看到以下日志内容,表明发放代码任务顺利完成了,贷款流程正常结束

贷款申请人:demo,贷款金额(元):10000, 用途项目:education

3 增加审批环节

3.1 流程编排

上面的例子中,用户发起贷款申请后,就发放贷款了,缺少了一个审批环节,我们可以在流程中增加一个用户任务:贷款审批。相关配置如下:

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

Multi-instance表示支持流程中存在多个审批人,且这多个审批人是串行审批的。Multi-instanceCollection配置值为${approvers}表示审批人列表从流程变量approvers获取,Element variable配置值为approver,表示流程内部遍历approvers值时,每个元素的变量名为approver

贷款审批任务还配置了一个approve表单变量,审批人审批时填写该表单变量表示审批是否通过。

贷款审批任务引入了一个流程变量approvers,流程进入贷款审批前需要设置流程变量approvers值,指定审批人。我们通过在贷款审批任务的入边添加一个Execution listener来实现分配审批人的功能,具体功能由spring bean assignApprover实现。配置如下图所示:

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

assignApprover实现代码如下:

import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service("assignApprover")
public class AssignApprover implements JavaDelegate {

    @Override
    public void execute(DelegateExecution execution) throws Exception {
        List<String> approvers = new ArrayList<>();
        approvers.add("movee");
        // 设置approvers流程变量的值
        execution.setVariable("approvers", approvers);
    }
}

如果贷款审批通过,则进入发放贷款任务,进行贷款发放。如果审批不通过,则直接结束流程。这个逻辑可以通过在贷款审批任务的后面添加一个排他网关(Exclusive Gateway)实现,这个排他网关有两条出边,根据审批人设置的approve流程变量值(代表审批是否通过),如果approve为true,则出边连接发放贷款任务,如果approve为false,则出边连接流程结束节点。

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门 改造后的流程图如下:

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

3.2 流程部署和执行

流程编排完后,保存流程,然后deploy到流程引擎。因为assignApprover Execution listener监听器添加了名字为movee的审批人用户,我们先通过流程引擎的Admin页面添加movee用户。

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

然后跟上一节一样在Tasklist页面发起贷款流程,流程发起后,在Cockpit页面发现多了一个运行中的流程实例:

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

然后使用movee账号登录camunda,在Tasklist页面就可以审批并结束流程了:

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

4 智能审批

现在很多互联网金融产品实现了秒批功能,我们在这里也实现一个非常简单的根据设定规则自动进行审批的功能

4.1 流程编排

4.1.1 新增一个DMN规则引擎

在modeler中新建一个diagram_auto_approver.dmn文件,该规则引擎配置如下所示:

自动审批规则基本信息:

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门 规则内容: 不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

贷款金额条件取流程变量loanAmount的值:

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门 用途条件取流程变量useProject的值:

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门 通过流程变量autoApprove输出规则引擎匹配结果: 不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

4.1.2 更新流程

原先的贷款流程增加一个贷款自动审批任务(Business Rule Task),关键配置如下面相关截图所示:

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

不要被camunda的繁复庞杂吓住了,我们先用十分钟时间入个门

4.1.3 流程部署和执行

将上面的DMN和BPMN文件部署到流程引擎,创建贷款流程实例可以发现,如果贷款金额<=10000且用途为education,则无需人工审批,直接发送贷款。否则需要走人审批流程