Flowable工作流——基础篇

1. 介绍

Flowable是BPMN的一个基于Java的软件实现,但是不仅仅限于BPMN,还有DMN决策表和CMMN Case管理引擎,并且有自己的用户管理,微服务API的功能,是一个服务平台。
是由开发了Acitivity6的开发人员,再次升级开发的。

2. 项目搭建

2.1 初始化

  • 构建一个普通maven项目。
  • 引入依赖:
    <dependencies>
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-engine</artifactId>
            <version>6.3.0</version>
        </dependency>

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

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.21</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.1.7</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.1.7</version>
        </dependency>
    </dependencies>
  • 新建测试类。
 @Test
    public void testProcessEngine(){
        // 流程配置对象
        ProcessEngineConfiguration configuration = new StandaloneProcessEngineConfiguration();

        // 数据库配置
        configuration.setJdbcDriver("com.mysql.jdbc.Driver");
        configuration.setJdbcUsername("root");
        configuration.setJdbcPassword("root");
        configuration.setJdbcUrl("jdbc:mysql://localhost:3306/flowable-learn?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8");
        // 配置如果表结构不存在就新建
        configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);


        // 构造核心流程对象
        ProcessEngine processEngine = configuration.buildProcessEngine();
        System.err.println(processEngine);
    }
  • 耐心等待运行(创建表结构)
    在这里插入图片描述
  • 配置slf4j,在resource下新建配置文件log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/logFile.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
  • 配置logback,resource下新建logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <property name="APP_NAME" value="MY_APP_NAME" />
    <property name="LOG_DIR" value="logs" />
    <property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS}  %-5level [%thread] %logger{15} - %msg%n" />
    <!-- 彩色日志格式 -->
    <property name="CONSOLE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) %boldYellow([%thread])  %cyan(%logger{15}) %msg%n"/>

    <contextName>${APP_NAME}</contextName>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>${LOG_DIR}/logFile.log</file>
        <append>true</append>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_DIR}/dayLogFile.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- 使用root的appender-ref -->
    <logger name="com.example.Logger1" level="DEBUG" additivity="true">
    </logger>

    <!-- 不使用root的appender-ref -->
    <logger name="com.example.Logger2" level="DEBUG" additivity="false">
    </logger>

    <logger name="com.example.Logger3" level="DEBUG" additivity="false">
        <appender-ref ref="STDOUT"/>
    </logger>

    <root level="DEBUG">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
        <appender-ref ref="RollingFile" />
    </root>
</configuration>
  • 再次启动测试,可以看到是彩色的日志
    在这里插入图片描述

3. 部署流程测试

使用官方给的请假测试流程。

3.1 流程图

  • 流程图如下:
    在这里插入图片描述

    1. 发起请求之后由管理员去审批:通过或者拒绝
    2. 通过/拒绝,之后触发不同的流程。
  • 流程图说明

    1. 左侧的圆圈叫做启动事件(start event)。这是一个流程实例的起点。
    2. 第一个矩形是一个用户任务(user task)。这是流程中用户操作的步骤。在这个例子中,管理员需要批准或驳回申请。
    3. 取决于管理员的决定,排他网关(exclusive gateway) (带叉的菱形)会将流程实例路由至批准或驳回路径。
    4. 如果批准,则需要将申请注册至某个外部系统,并跟着另一个用户任务,将经理的决定通知给申请人。当然也可以改为发送邮件。
    5. 如果驳回,则为雇员发送一封邮件通知他。

3.2 流程文件

  • 对应的xml文件:将xml放到resource文件夹下,并命名为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"/>
<!--        来源:startEvent 目的地:approveTask-->
        <sequenceFlow sourceRef="startEvent" targetRef="approveTask"/>

<!--        用户任务-->
        <userTask id="approveTask" name="Approve or reject request"/>
<!--        用户任务目的地排他网关-->
        <sequenceFlow sourceRef="approveTask" targetRef="decision"/>


<!--        排他网关-->
        <exclusiveGateway id="decision"/>
<!--        排他网关目标1-->
        <sequenceFlow sourceRef="decision" targetRef="externalSystemCall">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[
          ${approved}
        ]]>
            </conditionExpression>
        </sequenceFlow>

<!--        排他网关目标2-->
        <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>

3.3 部署文件

  • 新建单元测试文件
public class DoFlowable {

    ProcessEngineConfiguration configuration = null;

    /**
     * 获取flowable流引擎对象
     */
    @Before
    public void testProcessEngine(){
        // 流程配置对象
        configuration = new StandaloneProcessEngineConfiguration();
        // 数据库配置
        configuration.setJdbcDriver("com.mysql.jdbc.Driver");
        configuration.setJdbcUsername("root");
        configuration.setJdbcPassword("root");
        configuration.setJdbcUrl("jdbc:mysql://localhost:3306/flowable-learn?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8");
        // 配置如果表结构不存在就新建
        configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
    }

    /**
     * 部署流程
     */
    @Test
    public void testDeploy(){
        // 获取 processEngine 对象
        ProcessEngine processEngine = configuration.buildProcessEngine();

        // 获取 repositoryService
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 获取 deployment 对象
        Deployment deployment = repositoryService.createDeployment()// 创建Deployment对象
                .addClasspathResource("holiday-request.bpmn20.xml") // 添加流程部署文件
                .name("请求流程") // 设置部署流程的名称
                .deploy(); // 执行

        System.err.println("deployment.getId() = " + deployment.getId());
        System.out.println("deployment.getName() = " + deployment.getName());
    }

}
  • 执行之后:
    在这里插入图片描述
  • 查看数据库:可以看到数据
    在这里插入图片描述

4. 流程API

  • 先封装一个工具类,方便用来获取ProcessEngine对象
public class ProcessEngineUtils {

    /**
     * 获取flowable流引擎对象
     */
    public static ProcessEngine getProcessEngine(){
        // 流程配置对象
        ProcessEngineConfiguration configuration = new StandaloneProcessEngineConfiguration();
        // 数据库配置
        configuration.setJdbcDriver("com.mysql.jdbc.Driver");
        configuration.setJdbcUsername("root");
        configuration.setJdbcPassword("root");
        configuration.setJdbcUrl("jdbc:mysql://localhost:3306/flowable-learn?serverTimezone=UTC&nullCatalogMeansCurrent=true&useSSL=true");
        // 配置如果表结构不存在就新建
        configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);

        // 构造核心流程对象
        return configuration.buildProcessEngine();
    }
}

4.1 查询操作

    /**
     * 查询流程定义信息
     */
    @Test
    public void deploymentQuery(){
        ProcessEngine processEngine = ProcessEngineUtils.getProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        // 获取查询对象
        ProcessDefinitionQuery processQuery = repositoryService.createProcessDefinitionQuery();
        // 定义查询信息(单个查询)
        ProcessDefinition definition = processQuery.deploymentId("2501").singleResult();
    }

4.2 删除操作

    /**
     * 删除流程定义
     */
    @Test
    public void deployDel(){
        ProcessEngine processEngine = ProcessEngineUtils.getProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        repositoryService.deleteDeployment("1"); // 删除对应id的流程,启动中不可以删除
//        repositoryService.deleteDeployment("1", true); // 删除对应id的流程(包含其中的过程数据),不考虑启动状态。
    }

4.3 启动流程

    /**
     * 启动流程
     */
    @Test
    public void deployRun(){
        // 配置传递的参数
        Map<String, Object> params = new HashMap<>();
        params.put("employee","王五");
        params.put("days",3);
        params.put("description","累了");

        ProcessEngine processEngine = ProcessEngineUtils.getProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        // 1:这里的 key 对应配置文件中的 process 的 id
        // 2:传递的参数
        ProcessInstance holidayRequest = runtimeService.startProcessInstanceByKey("holidayRequest", params);
    }
  • 查看传入的数据
    在这里插入图片描述

  • 查看启动的流程
    在这里插入图片描述

  • 查看执行进度
    在这里插入图片描述

4.4 管理员查询

  • 修改xml中的审批处理人
<!--        用户任务-->
        <userTask id="approveTask" name="Approve or reject request" flowable:assignee="Ekko"/>
<!--        用户任务目的地排他网关-->
        <sequenceFlow sourceRef="approveTask" targetRef="decision"/>
  • 使用删除api,第二个参数传入true,级联删除任务数据。
  • 重新部署流程。
  • 再次重新启动流程。可以看到我们指定的审批人为:Ekko。
    在这里插入图片描述
  • 查询代码
    /**
     * 任务查询
     */
    @Test
    public void taskQuery(){
        ProcessEngine processEngine = ProcessEngineUtils.getProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey("holidayRequest") // 指定流程key
                .taskAssignee("Ekko") // 指定处理人
                .list();
        for (Task task : list) {
            System.out.println(task.getProcessDefinitionId());
            System.out.println(task.getName());
            System.out.println(task.getAssignee());
            System.out.println(task.getDescription());
            System.out.println(task.getId());
        }
    }

4.5 管理员审批

  • 修改配置文件中排他网关的两种处理走的类这里的配置修改之后需要重新部署一下流程。
<!--        同意流程节点-->
        <serviceTask id="externalSystemCall" name="Enter holidays in external system"
                     flowable:class="com.yy.flowable.service.AppService"/>
        <sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/>
<!--        拒绝流程让任务节点-->
        <serviceTask id="sendRejectionMail" name="Send out rejection email"
                     flowable:class="com.yy.flowable.service.RejectService"/>
<!--        拒绝-指向结束-->
        <sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>
  • 新增流程处理类
    在这里插入图片描述
  • 请假通过接口
/**
 * @author : JinMing Zhou
 * @description: 通过类
 * @date : 2023/1/9 17:48
 */
public class AppService implements JavaDelegate {

    /**
     * 触发器,流程走到这里执行该方法
     * @param delegateExecution
     */
    @Override
    public void execute(DelegateExecution delegateExecution) {
        // 通过,执行逻辑
        System.out.println("通过。");
    }
}
  • 请假拒绝接口
/**
 * @author : JinMing Zhou
 * @description: 拒绝类
 * @date : 2023/1/9 17:48
 */
public class RejectService implements JavaDelegate {

    /**
     * 请假流程拒绝触发器
     * @param delegateExecution
     */
    @Override
    public void execute(DelegateExecution delegateExecution) {
        System.err.println("不给通过!");
    }
}

  • 处理操作
    /**
     * 处理流程
     */
    @Test
    public void completeTask(){
        // 指定审批参数
        Map<String, Object> params = new HashMap<>();
        params.put("approved",false);

        ProcessEngine processEngine = ProcessEngineUtils.getProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        // 查询任务

        Task task = taskService.createTaskQuery()
                .processDefinitionKey("holidayRequest") // 指定流程key
                .taskAssignee("Ekko") // 指定处理人
                .singleResult();
        // 处理操作
        taskService.complete(task.getId(), params);
    }

在这里插入图片描述

4.6 历史记录查询

    /**
     * 历史信息查询
     */
    @Test
    public void historyQuery(){
        ProcessEngine processEngine = ProcessEngineUtils.getProcessEngine();
        HistoryService historyService = processEngine.getHistoryService();
        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
                .processDefinitionId("holidayRequest:1:17503")
                .finished()
                .orderByHistoricActivityInstanceEndTime().asc()
                .list();
        for (HistoricActivityInstance historicActivityInstance : list) {
            System.err.println(historicActivityInstance.getActivityId() + " took "
                    + historicActivityInstance.getDurationInMillis() + " milliseconds");
        }
    }

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基础讲起,结合应用场景,由浅到深细化讲解BPMN和Flowable的相关组件,并结合具体实例,演示功能的使用和注意事项。最终结合Springboot搭建一套工作流系统,囊括一般项目中所需要的知识点,理论结合实际,让真正入门到熟练。 1 简介 2 学习指南 2.1 Flowable初体验 2.1.1 Flowable是什么? 2.1.2 Flowable 和 Activiti 2.1.3 构建命令行应用程序 2.1.3.1 创建一个流程引擎 2.1.3.2 部署一个流程定义 2.1.3.3 启动一个流程实例 2.1.3.4 查询和完成一个任务 2.1.3.5 写一个JavaDelegate 2.1.3.6 查询历史数据 2.2 Flowable整合Spring 2.3 Flowable整合SpringBoot 2.4 Flowable流程定义部署 2.4.1 使用xml部署 2.4.2 使用压缩文件部署 2.4.3 使用IO流部署 3 BPMN2.0简介 3.1 什么是BPMN2.0 3.2 创建一个BPMN 3.2.1 直接编写XML文件 3.2.2 使用插件编写 3.2.2.1 在线安装插件 3.2.2.2 离线安装 3.2.2.3 插件使用说明 4 BPMN2.0组成 4.1 事件 4.1.1 事件定义 4.1.2 计时器事件定义 4.1.2.1 timeDate 4.1.2.1.1 开始事件TimerStartEvent 4.1.2.1.2 中间事件TimerCatchingEvent 4.1.2.1.3 边界事件TimerBoundaryEvent 4.1.2.2 timeDuration 4.1.2.2.1 开始事件TimerStartEvent 4.1.2.1.2 中间事件TimerCatchingEvent 4.1.2.1.3 边界事件TimerBoundaryEvent 4.1.2.3 timeCycle 4.1.2.3.1 开始事件TimerStartEvent 4.1.2.3.2 中间事件TimerCatchingEvent 4.1.2.3.3 边界事件TimerBoundaryEvent 4.1.3 消息事件 4.1.3.1 开始事件MessageStartEvent 4.1.3.2 中间事件MessagecatchingEvent 4.1.3.3 边界事件MessageBoundaryEvent 4.1.4 错误事件 4.1.4.1 开始事件ErrorStartEvent 4.1.4.2 边界事件ErrorBoundaryEvent 4.1.5 信号事件 4.1.5.1 开始事件SignalStartEvent 4.1.5.2 中间事件 4.1.5.2.1 捕捉事件SignalCatchingEvent 4.1.5.2.2 抛出事件SignalThrowingEvent 4.1.5.3 边界事件SignalBoundaryEvent dream21st 4.1.6结束事件 4.1.6.1 错误结束事件ErrorEndEvent 4.1.6.2 中断结束事件TerminateEndEvent 4.1.6.2.1 中断结束事件案例一 4.1.6.2.2 中断结束事件案例二 4.1.6.3 取消结束事件 CancelEndEvent 4.1.7 补偿事件CompensationThrowing 4.1.8 网关 4.1.8.1 并行网关ParallelGateway 4.1.8.2 排他网关ExclusiveGateway 4.1.8.3 包容网关InclusiveGateWay 4.1.8.4 事件网关EventGateway 4.2 任务 4.2.1 用户任务UserTask 4.2.1.1 用户任务入门案例Assignee指定 4.2.1.2 CandidateUser和CandidateGroup指定 4.2.1.3 多人会签MultiInstance 4.2.1.4 动态表单 4.2.2 服务任务ServiceTask 4.2.3 手工任务ManualTask 4.2.4 接受任务ReceiveTask 4.2.5 调用流程CallActivity 4.2.5.1 固定子流程 4.2.5.2 动态子流程 4.3 容器 5 工作流实战案例 5.1 实战案例一 5.1.1 部署流程定义 5.1.2 启动流程实例 5.1.3 查询待办任务 5.1.4 提交任务 5.1.5 查询候选任务 5.1.6 获取候选任务 5.1.7 通过流程实例ID查询任务流转图 5.2 实战案例二

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值