一,什么是Camunda?
Camunda是一个开源的工作流引擎和业务流程管理平台,基于Java和Spring框架构建。它支持BPMN 2.0标准,允许用户通过图形界面或编程方式定义复杂的工作流和业务流程。Camunda可以嵌入到任何Java应用程序中,也可以作为独立的服务运行,为开发者提供了极大的灵活性。
1、什么是工作流
工作流(Workflow),是对工作流程及其各操作步骤之间业务规则的抽象、概括描述。指在组织中,多个任务、活动或过程之间的顺序和控制流。这些任务通常涉及多个参与者、系统或部门,工作流的目标是将复杂的业务逻辑分解为多个可管理的逻辑段,并统一控制这些逻辑段的执行条件、执行顺序以及相互通信,从而实现业务逻辑的解耦与优化。这种方式不仅提高了业务流程的灵活性和可维护性,还促进了各个环节之间的协同工作。
2、为什么要使用工作流
提高效率:通过自动化和标准化重复性任务,减少人工干预,提升整体工作效率。
增强透明度:工作流提供清晰的任务分配和进度跟踪,使团队成员能够实时了解工作状态,促进协作。
降低错误率:通过预定义的流程和自动化步骤,减少人为错误,提高数据准确性。
灵活性和适应性:可以根据业务需求快速调整和优化流程,适应变化的市场环境。
优化资源管理:通过有效分配和调度资源,确保任务按时完成,提高资源利用率。
监控和分析:提供实时监控和数据分析功能,帮助识别瓶颈和改进机会,持续优化业务流程。
合规性和标准化:确保业务流程符合行业标准和法规要求,提高合规性,减少风险。
二,Camunda应用架构
Camunda是一个基于 Java 的分布式应用架构,主要包括以下几个核心组件:
1、Camunda BPM 引擎
核心功能:执行 BPMN 2.0 流程、CMMN 案例和 DMN 决策模型。
高性能:经过优化,支持高并发和复杂业务逻辑的执行。
集成:可嵌入到 Java 应用中,或通过 REST API 与外部系统交互。
2、建模工具
Camunda Modeler:图形化工具,用于设计和编辑 BPMN、CMMN 和 DMN 模型,便于业务用户进行流程建模。
3、用户任务管理
Tasklist:Web 应用,用户可以在此管理和处理任务,查看分配的任务并通过表单完成。
4、监控与分析工具
Cockpit:用于实时监控流程执行,提供流程实例状态、性能指标和历史数据分析,帮助识别瓶颈和优化流程。
5、REST API
接口服务:提供全面的 RESTful API,支持流程的启动、任务查询、用户管理等操作,便于与其他系统集成。
6、外部任务
任务处理:支持外部任务模式,将任务分配给外部服务或微服务,提高灵活性和可扩展性。
7、事件处理
事件驱动:支持处理多种事件(如消息、信号、定时器),实现复杂的事件驱动工作流。
8、多租户支持
多租户架构:支持在同一引擎实例中管理多个租户,确保数据隔离和安全性,适合 SaaS 解决方案。
9、前端用户界面
Cockpit:流程监控和管理界面。
Tasklist:任务管理和审批界面。
Admin:系统管理和配置界面。
三、camunda使用实例
本实例中我们实现以下功能:1.工作流程设计 2.工作流中包含用户任务和服务任务 3.工作流中使用网关 4.工作流和外部服务结合实现任务执行前提醒任务执行者 5.实现工作流实例的启动 6.实现工作流实例启动时设置各个任务的执行人 7.实现代办任务获取和已办任务获取。
项目环境:JDK(openJDK17)、SpringBoot3.2.2、Camunda7.20.0
1.下载camunda流程设计器
下载地址:Releases · camunda/camunda-modeler · GitHub
Download The Camunda BPMN / DMN Process Modeler | Camunda
2个地址都可以下载流程设计器,github上可以下载历史版本。
流程设计器下载、解压后可直接使用。
2.创建camunda流程引擎服务
自动创建服务地址:Camunda Automation Platform 7 Initializr
3.使用流程设计器设计业务流程
流程设计器选择匹配的camunda版本。
流程开始
新建运单任务
货物入仓任务
排他网关
分支条件1
分支条件2
货物收运任务
流程结束
4.camunda引擎项目结构及代码
项目结构
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>infosky</groupId>
<artifactId>camnuda-engine</artifactId>
<version>1.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.2.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.camunda.bpm</groupId>
<artifactId>camunda-bom</artifactId>
<version>7.20.0</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter-rest</artifactId>
</dependency>
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId>
</dependency>
<dependency>
<groupId>org.camunda.bpm</groupId>
<artifactId>camunda-engine-plugin-spin</artifactId>
</dependency>
<dependency>
<groupId>org.camunda.spin</groupId>
<artifactId>camunda-spin-dataformat-all</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.2.2</version>
</plugin>
</plugins>
</build>
</project>
application.yaml
server:
port: 9999
spring:
datasource:
url: jdbc:mysql://172.20.220.114:3306/lt?nullDatabaseMeansCurrent=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
username: aaaaa
password: bbbbbbbbb
driverClassName: com.mysql.cj.jdbc.Driver
camunda:
bpm:
database:
type: mysql
auto-deployment-enabled: false
admin-user:
id: demo
password: demo
Application.java
java">package infosky;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* springboot启动类
*/
@SpringBootApplication
public class Application {
static Logger logger = LoggerFactory.getLogger(Application.class);
public static void main(String... args) {
try {
SpringApplication.run(Application.class, args);
logger.info("★★★★★★★★★★★★★★★★★★★★启动正常★★★★★★★★★★★★★★★★★★★★");
} catch (Exception e) {
logger.error("■■■■■■■■■■■■■■■■■■启动异常", e);
}
}
}
FlowController.java
java">package infosky.controller;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* camunda流程
*/
@RestController
@RequestMapping("/flow")
public class FlowController {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
/**
* 部署流程
* @return
*/
@RequestMapping("/deploy")
public String deploy(){
Deployment dm = repositoryService.createDeployment()
.name("运单流程") //定义部署文件的名称
.addClasspathResource("bpmn/awb.bpmn") //绑定需要部署的流程文件
.deploy(); //部署流程
return dm.getName() + "|" + dm.getId();
}
/**
* 启动流程
*/
@RequestMapping("/startFlow")
public String startFlow(){
//定义map集合,存储相关流程的变量信息
Map<String,Object> map = new HashMap<>();
map.put("user1","wangwu");
map.put("user2","lisi");
//启动流程
// 启动流程可以根据id或者key,其中id是唯一的它由3部分组成(流程定义的key:版本号:自动生成的id)
// id例=> Process_awb:3:d9afa57b-dfbb-11ef-818c-4e5f7046682e
// key例=> Process_awb ■key对应的流程定义不唯一,同一个key的流程定义可以有多个版本。
// 流程启动时可指定key和对应的版本 例=>runtimeService.startProcessInstanceByKey("Process_awb","3");
// 流程启动时也可只指定key,此时默认启动该key的最新版本 例=>runtimeService.startProcessInstanceByKey("Process_awb");
// 根据id启动流程 例=>runtimeService.startProcessInstanceById("Process_awb:3:d9afa57b-dfbb-11ef-818c-4e5f7046682e");
// 以上指的id和key及版本存储在表[ACT_RE_PROCDEF] 另外,此处的key是在流程设计器中的[流程ID]
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("Process_awb",map);
//返回流程实例id
return processInstance.getId();
}
@RequestMapping("/completeTask")
public void completeTask(){
//获取任务 可根据流程实例id或者taskid,对应表[ACT_RU_TASK]
// ■任务执行完后将清除[ACT_RU_TASK]表中对应的task数据
Task task = taskService.createTaskQuery()
.processInstanceId("c2861623-e044-11ef-aa57-4e5f7046682e")
// .taskId("6db24541-dfbc-11ef-98b5-4e5f7046682e")
.singleResult();
//判断任务是否存在
if(task != null){
System.out.println("==================");
//完成任务 参数:taskID
taskService.complete(task.getId());
}
}
/**
* 查询任务(代办)
*
* 流程定义ID:processDefinition =>流程部署时产生的ID,一个流程部署多次每次都会产生一个ID
* 流程实例ID:processInstance =>启动流程实例时产生的实例ID
* @return
*/
@RequestMapping("/queryTaskByHandle")
public void queryTaskByHandle(){
List<Task> list = taskService.createTaskQuery()
.taskAssignee("lisi")
.list();
for (Task task : list) {
System.out.println("#############:" + task.getId());
}
}
/**
* 查询已完成任务
* [ACT_HI_TASKINST]
*/
@RequestMapping("/queryTaskByHistory")
public void queryTaskByHistory(){
List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
.taskAssignee("wangwu")
.finished()
.list();
for (HistoricTaskInstance hti : list) {
System.out.println("#############:" + hti.getProcessDefinitionKey() + "|" + hti.getName() + "|" + hti.getStartTime() + "|" + hti.getEndTime());
}
}
}
NewawbStartListener.java
java">package infosky.listener;
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.ExecutionListener;
import org.springframework.stereotype.Component;
/**
* 监听器
* 用于给对应处理人发送消息
* 可将消息存储到DB中或者发送到MQ中
*/
@Component
public class NewawbStartListener implements ExecutionListener {
@Override
public void notify(DelegateExecution delegateExecution) throws Exception {
Object user1 = delegateExecution.getVariable("user1");
System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&:" + user1);
}
}
FohService.java
java">package infosky.service;
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Service;
/**
* 货物入仓处理
*/
@Service("fohService")
public class FohService implements JavaDelegate {
@Override
public void execute(DelegateExecution delegateExecution) throws Exception {
//处理完成后设置网关变量
delegateExecution.setVariable("aaa",1);
System.out.println("$$$$$$$$$$$$$$$$$完成任务");
}
}
5.项目启动后会自动将camunda需要的表生成到对应的数据库中(共49张表)
6.部署流程
将流程设计器中导出的流程文件(awb.bpmn),复制到项目的bpmn文件夹下
启动项目,执行/flow/deploy,部署对应流程。
7.启动流程
8.执行新建运单任务
9.执行货物收运任务
任务完成后,task表中删除对应的记录。
以上处理中在流程启动时设置了变量user1,user2。在执行新建运单任务前,向执行人发送信息这里用监听器[NewawbStartListener]中方法[notify]模拟实现消息发送。在任务货物入仓中设置流程执行变量aaa的值,从而控制网关的执行方向。代办任务和已完成任务在FlowController.java中均已实现,具体可参考代码。
附:参考资料
camunda中文网站:Camunda 中文站 | docs.camunda.org
camunda生成项目网站:Camunda Automation Platform 7 Initializr
camunda下载流程设计器:Download The Camunda BPMN / DMN Process Modeler | Camunda