目錄
- 簡介
- 準備
- JDK
- MySQL
- flowable-ui
- 創建流程圖
- 要注意的地方
- 編碼
- 依賴和配置
- 控制器
- 實體
- Flowable任務處理類
- 驗證
- 啟動程序
- 調用接口
- 本文源碼
- 參考
簡介
- Flowable是一個輕量的Java業務流程引擎,用于實現業務流程的管理和自動化。相較于老牌的Activiti做了一些改進和擴展,實現更高的性能和更小的內存占用,支持更多的數據庫類型。
準備
JDK
- JDK 17
MySQL
- flowable程序初始化會生產表,所以要數據庫。
MySQL
數據庫,我使用的是phpstudy
(下載地址:https://www.xp.cn/phpstudy#phpstudy)集成環境的MySQL8.0.12
。
flowable-ui
- 需要事先建一個流程給flowable,所以要個可視化界面創建流程。
flowable-ui
:使用docker安裝,使用命令:
docker run -d --name fu -p 8080:8080 flowable/flowable-ui
- 運行起來的網頁效果,地址是 http://ip:8080/flowable-ui
默認帳號密碼:
admin
test
- 如果拉不下來鏡像,請嘗試以下方案:
- 方案一、設置代理,參考拙作Docker設置代理
- 方案二、更換鏡像源,如下配置(編輯
/etc/docker/daemon.json
):
{"registry-mirrors": ["https://docker.m.daocloud.io"]
}
創建流程圖
- 實現一個創建采購訂單,
order.totalPrice
金額大于1000要經理確認的功能。
- 完整流程圖
要注意的地方
-
設置分支條件,
order.totalPrice
大于1000要經理確認。
-
任務要綁定處理類
-
經理確認節點要綁定參數 Assignee
manager
編碼
依賴和配置
- 引入的包
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.11</version></dependency><!-- 阿里數據庫連接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.23</version></dependency><!-- Mysql驅動包 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>7.1.0</version></dependency>
- 程序配置文件
spring:application:name: flowable-sampleprofiles:active: dev
server:port: 8080# MyBatis配置
mybatis-plus:# 搜索指定包別名typeAliasesPackage: com.zzq.domain# 配置mapper的掃描,找到所有的mapper.xml映射文件mapperLocations: classpath*:mapper/**/*Mapper.xml# 加載全局的配置文件configLocation: classpath:mybatis/mybatis-config.xml# 日志配置
logging:level:com.zzq: debug
flowable:# 是否激活異步執行器async-executor-activate: false# 數據庫模式更新策略,true表示自動更新數據庫模式database-schema-update: true
- application-dev.yml
# 數據源配置
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/flowable_sample?nullCatalogMeansCurrent=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username: rootpassword: root
- 下載文件放到項目中
resources/processes
- FlowableConfig
package com.zzq.config;import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;/*** FlowableConfig** @Description: 解決Diagram生成的流程圖文字顯示為”口口口“ 這是因為本地沒有默認的字體,安裝字體或者修改配置解決* @Author: zzq* @Date 2025/4/5 15:16* @since 1.0.0*/
@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {@Overridepublic void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) {springProcessEngineConfiguration.setActivityFontName("宋體");springProcessEngineConfiguration.setLabelFontName("宋體");springProcessEngineConfiguration.setAnnotationFontName("宋體");}
}
控制器
- OrderFlowController
package com.zzq.controller;import com.zzq.domain.Order;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricActivityInstanceQuery;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.bind.annotation.*;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;/*** Zhou Zhongqing* 2025-04-01* 訂單流程控制器*/
@RestController
@RequestMapping("/orderFlow")
public class OrderFlowController {private static final Logger log = LoggerFactory.getLogger(OrderFlowController.class);@Resourceprivate HistoryService historyService;@Resourceprivate RepositoryService repositoryService;@Resourceprivate RuntimeService runtimeService;@Resourceprivate TaskService taskService;@Resourceprivate ProcessEngine processEngine;/*** 開始流程* @param content* @param totalPrice* @return*/@PostMapping("/create_order")public ResponseEntity<String> startFlow(String content, Integer totalPrice) {Map<String, Object> map = new HashMap<>();map.put("order", new Order(content, totalPrice));ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("flowable-sample", 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);}/*** 訂單列表,待確認的,返回任務id* @return*/@RequestMapping("/order_list")public String getOrderList() {List<Task> list = taskService.createTaskQuery().taskAssignee("manager").list();StringBuffer stringBuffer = new StringBuffer();list.stream().forEach(task -> stringBuffer.append(task.getId()+ " : " + runtimeService.getVariable(task.getExecutionId(), "order") + "\n"));return stringBuffer.toString();}/*** 經理確認* @param taskId* @return*/@PostMapping("/confirm/{taskId}")public ResponseEntity<String> confirm(@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");}/*** 生成圖,某個流程處理進度顯示* @param response* @param processId* @throws Exception*/@GetMapping(value = "/processDiagram/{processId}")public void genProcessDiagram(HttpServletResponse response, @PathVariable("processId") String processId) throws Exception{ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();if (null == pi) {return;}Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();//使用流程實例ID,查詢正在執行的執行對象表,返回流程實例對象String instanceId = task.getProcessInstanceId();List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(instanceId).list();//得到正在執行的Activity的IdList<String> activityIds = new ArrayList<>();List<String> flows = new ArrayList<>();List<HistoricActivityInstance> historyList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processId).orderByHistoricActivityInstanceStartTime().asc().list();for (HistoricActivityInstance historicActivityInstance : historyList) {String activityId = historicActivityInstance.getActivityId();if("sequenceFlow".equals(historicActivityInstance.getActivityType())){flows.add(activityId);}}for (Execution exe : executions) {List<String> ids = runtimeService.getActiveActivityIds(exe.getId());activityIds.addAll(ids);}// 獲取流程圖BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());ProcessEngineConfiguration engConf = processEngine.getProcessEngineConfiguration();ProcessDiagramGenerator diagramGenerator = engConf.getProcessDiagramGenerator();String format = "png";InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engConf.getActivityFontName(), engConf.getLabelFontName(), engConf.getAnnotationFontName(), engConf.getClassLoader(), 1.0, false);
// OutputStream out = null;
// byte[] buf = new byte[1024];
// int legth = 0;
// try {
// out = response.getOutputStream();
// while ((legth = in.read(buf)) != -1) {
// out.write(buf, 0, legth);
// }
// } finally {
// if (in != null) {
// in.close();
// }
// if (out != null) {
// out.close();
// }
// }IOUtils.copy(in, response.getOutputStream());}}
實體
- Order
package com.zzq.domain;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;import java.io.Serializable;
import java.io.Serial;
@TableName(value = "t_order")
public class Order implements Serializable {@Serialprivate static final long serialVersionUID = 8347055723013141158L;public Order() {}public Order(String content, Integer totalPrice) {this.content = content;this.totalPrice = totalPrice;}public Order(Integer id, String content, Integer totalPrice) {this.id = id;this.content = content;this.totalPrice = totalPrice;}@TableId(value = "id",type = IdType.AUTO)private Integer id;private String content;private Integer totalPrice;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public Integer getTotalPrice() {return totalPrice;}public void setTotalPrice(Integer totalPrice) {this.totalPrice = totalPrice;}
}
Flowable任務處理類
- CreateOderProcess
package com.zzq.process;import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class CreateOderProcess implements JavaDelegate {private static final Logger log = LoggerFactory.getLogger(CreateOderProcess.class);@Overridepublic void execute(DelegateExecution delegateExecution) {log.info("訂單創建成功 {}",delegateExecution.getVariable("order"));}
}
- SendMailProcess
package com.zzq.process;import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class SendMailProcess implements JavaDelegate {private static final Logger log = LoggerFactory.getLogger(SendMailProcess.class);@Overridepublic void execute(DelegateExecution delegateExecution) {log.info("發送審核郵件 {} ",delegateExecution.getVariable("order"));}
}
驗證
啟動程序
- 配置了
database-schema-update: true
第一次啟動會自動創建表
調用接口
-
創建采購訂單, /orderFlow/create_order ,返回流程實例ID
- 不需要經理確認
- 需要經理確認
- 不需要經理確認
-
查看待確認的訂單,返回任務id拼接Order
-
查看某個流程處理進度顯示,傳入流程實例id
-
經理調用確認采購訂單,傳入taskId
-
確認后再調用待確認訂單接口也就沒有剛才的任務id了
-
驗證完成
本文源碼
- https://github.com/1030907690/flowable-sample
參考
- https://www.bilibili.com/video/BV1gnkJYJEbg/
- https://blog.csdn.net/qq_34162294/article/details/143806673
- https://blog.51cto.com/u_16213663/10188533
- https://blog.csdn.net/houyj1986/article/details/85546680