使用springboot+flowable实现一个简单的订单审批工作流

声明:本文只是使用flowable一个小demo,仅用作个人笔记和抛转引玉用,生产中请根据项目需要作相应调整!!!

先上订单审批的工作流程图

简要介绍下上面的工作流:

1、开始节点,客户填写订单;

2、使用排他网关判断订单金额,如果订单金额<=10万,则直接创建订单,流程结束;

3、如果订单金额超过10万,则自动发送邮件抄送,然后需人工介入处理,经理进行确认;

4、经理确认无误后,再创建订单,流程结束;

5、图片中齿轮图标表示无须人工处理的服务任务,人形图标为须人工处理的用户任务;

6、x表示排他网关,细线的圆圈表示开始节点,粗线的圆圈表示结束节点。

下面使用springboot整合flowable实现上面的工作流处理。

项目总体结构预览:

**准备工作:**在flowable-ui网页端生成模型文件然后导出,文末会告知如何使用flowable-ui创建模型

1、创建springboot工程

在maven中引入必要的依赖

核心依赖是:flowable-spring-boot-starter

XML 复制代码
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter</artifactId>
            <version>7.1.0</version>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.32</version>
        </dependency>
    </dependencies>

2、配置flowable和数据库信息

将flowable相关数据存到Mysql,事先到mysql中创建一个flowable的空数据库。

XML 复制代码
flowable:
  # 是否激活异步执行器
  async-executor-activate: false
  # 数据库模式更新策略,true表示自动更新数据库模式
  database-schema-update: true

spring:
  datasource:
      driverClassName: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://192.168.56.10:3309/flowable?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
      username: root
      password: root

3、拷贝模型文件到项目中

然后在Springboot工程中resources文件夹中创建processes文件夹,将模型xml文件复制到processes文件夹,文末会告知如何导出模型xml文件。

4、启动项目

当项目成功启动后,flowable会向指定数据库生成所需的表,无需手动创建,控制台打印部分信息如下:

performing create on history with resource org/flowable/db/create/flowable.mysql.create.history.sql

performing create on engine with resource org/flowable/db/create/flowable.mysql.create.engine.sql

此时再刷新数据库,会发现多出很多表。

5、创建订单实体类

java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order implements Serializable {

    private String customer;
    private Integer total;
}

6、创建订单处理器和邮件抄送执行器

java 复制代码
package com.example.flowabledemo.process;

import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;

@Slf4j
public class CreateOrderProcessor implements JavaDelegate {

    @Override
    public void execute(DelegateExecution execution) {
       log.info("订单创建成功,{}",execution.getVariable("order"));
    }
}

这段代码是 Flowable 工作流框架中的「服务任务(Service Task)」执行器,实现了 Flowable 提供的 JavaDelegate 接口,核心作用是:当流程执行到绑定该类的「创建订单」服务任务节点时,自动触发 execute 方法,打印订单创建成功的日志,并从流程上下文获取 order 变量。

java 复制代码
package com.example.flowabledemo.process;

import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;

@Slf4j
public class SendEmailProcessor implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) {
        log.info("发送审批邮件,{}",execution.getVariable("order"));
    }
}

7、订单处理相关接口

7.1 创建订单

java 复制代码
@Controller
@RequestMapping("order")
@Slf4j
public class OrderFlowController {
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private ProcessEngine processEngine;

    /* *
     * 创建订单
     **/
    @PostMapping("createOrder")
    public ResponseEntity<String> startFlow(String customer,Integer total){
        HashMap<String,Object>map=new HashMap<>();
        map.put("order",new Order(customer,total));
        map.put("initiator", customer);
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("order-approval-process", map);
        String processId = processInstance.getId();
        log.info("{}流程实例ID:{}",processInstance.getProcessDefinitionName(),processId);
        Task task = taskService.createTaskQuery().processInstanceId(processId).active().singleResult();
        taskService.complete(task.getId());
        return ResponseEntity.ok(processId);
    }

代码解读:

  • 核心作用:接收客户名称和订单金额参数,启动「订单审批流程(order-approval-process)」,并自动完成流程中第一个待办任务(「填写订单」节点),最终返回流程实例 ID,。
  • 初始化 HashMap 存储流程变量(Flowable 流程实例的全局变量,贯穿整个流程生命周期),map.put("order", new Order(...):将订单对象存入流程变量,供后续节点(如创建订单、经理审批)获取;
  • runtimeService.startProcessInstanceByKey(...):Flowable 核心方法,通过「流程定义 Key」启动流程实例,order-approval-process:BPMN 文件中 <process id="order-approval-process"> 的 ID,是流程的唯一标识,必须保持一致;
  • taskService.createTaskQuery():创建任务查询器,筛选「当前流程实例的活跃任务」;
    • processInstanceId(processId):仅查询该流程实例的任务;
    • active():仅查询未完成的活跃任务;
    • singleResult():返回单个任务(预期流程第一个节点是「填写订单」用户任务,仅一个活跃任务);
  • taskService.complete(task.getId()):完成该任务,流程会自动流转到下一个节点(排他网关,判断金额分支)。

7.2 查询经理要审批的订单列表

java 复制代码
@GetMapping("list")
public String getOrderList(){
	List<Task> taskList = taskService.createTaskQuery().taskAssignee("manager").list();
	StringBuilder sb = new StringBuilder();
	for(Task task:taskList){
		Object obj = runtimeService.getVariable(task.getExecutionId(), "order");
		if(obj instanceof Order){
			 sb.append(task.getId()+ ":" + (Order) obj);
		}

	}
   return sb.toString();
}

代码解读:

  • 核心作用是:查询并返回分配给「manager」(经理)的所有待审批订单任务列表,具体逻辑是先获取经理的待办任务,再从每个任务对应的流程实例中提取 order 变量(订单对象),最终拼接任务 ID 和订单信息返回字符串。
  • taskService:Flowable 核心服务类,用于操作「用户任务」(待办、已办、完成任务等);
  • createTaskQuery():创建任务查询器,Flowable 采用「查询器模式」封装数据库查询逻辑;
  • taskAssignee("manager"):筛选条件 ------ 仅查询分配给「manager」(经理)的任务(对应 BPMN 中flowable:assignee="manager"的节点);
  • list():执行查询,返回符合条件的所有任务列表(区别于singleResult()返回单个任务);
  • task.getExecutionId():获取任务所属的「流程执行 ID」(对应单个流程实例的执行分支 ID,可关联到流程实例);
  • runtimeService.getVariable(...):Flowable 核心服务类,用于操作流程实例的「流程变量」,这里根据执行 ID 获取名为order的变量;
  • obj instanceof Order:类型校验,避免强制类型转换时抛出ClassCastException
  • sb.append(...):拼接「任务 ID: 订单对象」,最终返回拼接后的字符串。

7.3 经理确认和审批订单

java 复制代码
 @PostMapping("/confirm/{taskId}")
    public ResponseEntity<String> confirmOrder(@PathVariable String taskId){
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        //通过审核
        HashMap<String, Object> map = new HashMap<>();
        map.put("verified",true);
        taskService.complete(taskId,map);
        return ResponseEntity.ok("success");
    }

代码解读:

  • 核心作用是:接收「任务 ID(taskId)」路径参数,完成该任务(对应经理审批订单的节点),并通过流程变量 verified 标记「审批通过」,最终返回成功提示,触发流程向下一个节点(创建订单)流转。
  • 构造 HashMap 存储「任务完成时的流程变量」:verified=true 标记订单「审批通过」(若有驳回场景,可传 false);
  • taskService.complete(taskId, map):Flowable 核心方法,完成指定 ID 的任务,并将变量传入流程上下文:

8、注意事项:

8.1、邮件抄送的类,必须与代码中声明的保持一致,使用全限定名,邮件抄送是一样的处理逻辑,可打开工作流程图,点击任务节点,在下方的节点详细信息中进行编辑,记得要保存,参照下图:

8.2、经理确认,必须指定分配级哪个用户进行处理,这里必须与接口list中

复制代码
taskAssignee("manager")保持一致

8.3、排他网关,当订单金额超过10万时,走上面的分支进行处理,须指定工作流条件和是否默认流

这里的流条件order.total与代码中声明的流变量保持对应关系,包括map中的key,total为order对象中一个属性。

复制代码
map.put("order",new Order(customer,total));

金额不超过10时的工作流,这个不用设置条件,执行默认流处理:

8.4、执行了修改,一定要点击左上角的第1个保存按钮才会生效,第2个按钮对勾为检查语法有无错误:

8.5、模块的Key必须与接口中createOrder中这一行代码中保持一致:

复制代码
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("order-approval-process", map);

8.6、flowable-ui模型导入

flowable-ui网页端可以对模型执行导入操作,导入的模型名称必须是**.bpmn.xml或.bpmn20.xml**,注意以点号开头,如果文件命名为其他,导入的时候会提示文件名错误,无法解析。

8.7、flowable-ui模型导出

在flowable-ui模型列表中点击要导出的模型,在弹出的模型详情页面中点击右上角的下载图标,即可将模型导出为xml文件,点击后自动生成文件名,使用默认的文件名即可

8.8、创建模型

输入模型名称和模型的key

8.8.1 模型名称

输入的模型名称是什么,导出时模型文件名称自动在模型名称后面添加后缀,如这里我命名为OrderFlow,导出的文件名则为OrderFlow.bpmn20.xml

8.8.2 模型key

这里输入的key是什么,那么在启动流程时变量命名必须与它保持一致,我这里命名为order,所以在启动流程时就命名为order,否则会报错

9、接口请求示例

相关推荐
左左右右左右摇晃2 小时前
Java并发——偏向锁
java
牧天白衣.2 小时前
07-常用API
java
Meepo_haha2 小时前
Tomcat闪退问题以及解决原因(三种闪退原因有解决办法)
java·tomcat·firefox
兑生2 小时前
【灵神题单·贪心】3010. 将数组分成最小总代价的子数组 I | Java
java·开发语言·算法
IT_陈寒2 小时前
JavaScript 闭包陷阱:90%开发者踩过的5个坑,你中招了吗?
前端·人工智能·后端
Java面试题总结2 小时前
go从零单排之方法
开发语言·后端·golang
小堃学编程2 小时前
【项目实战】基于protobuf的发布订阅式消息队列(1)—— 准备工作
java·大数据·开发语言
ZHOUPUYU2 小时前
PHP性能分析与调优:从定位瓶颈到实战优化
开发语言·后端·html·php
吴声子夜歌2 小时前
JavaScript——数组
java·javascript·算法