工作流引擎Camunda

news/2025/2/2 23:40:29 标签: java, spring boot

一,什么是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


http://www.niftyadmin.cn/n/5840367.html

相关文章

如何为用户设置密码

[rootxxx ~]# passwd aa #交互式的为用户设置密码 或者 [rootxxx ~]# echo 123 | passwd --stdin aa #不交互式的为用户设置密码 &#xff08;适用于批量的为用户更改密码&#xff0c;比如一次性为100个用户初始化密码&#xff09;

Windsurf cursor vscode+cline 与Python快速开发指南

Windsurf简介 Windsurf是由Codeium推出的全球首个基于AI Flow范式的智能IDE&#xff0c;它通过强大的AI助手功能&#xff0c;显著提升开发效率。Windsurf集成了先进的代码补全、智能重构、代码生成等功能&#xff0c;特别适合Python开发者使用。 Python环境配置 1. Conda安装…

攻防世界 php2

你能验证这个网站吗&#xff1f; 根据提示是PHP&#xff0c;我们知道PHP代码在服务器端执行,可以连接数据库&#xff0c;查询数据&#xff0c;并根据查询结果动态生成网页内容。 PHP代码通常是保存在以.PHP为扩展名的文件上,这些文件存储在web 服务器的文档根目录中&#xff…

文本复制兼容方案最佳实现落地。

文章目录 一、navigator.clipboard.writeText二、方案落地总结 一、navigator.clipboard.writeText navigator.clipboard.writeText 是一个Web API&#xff0c;它允许网页脚本将文本数据写入用户的系统剪贴板。这个API是异步的&#xff0c;并且设计用于提高安全性和用户体验&a…

解锁豆瓣高清海报(一) 深度爬虫与requests进阶之路

前瞻 PosterBandit 这个脚本能够根据用户指定的日期&#xff0c;爬取你看过的影视最高清的海报&#xff0c;然后使用 PixelWeaver.py 自动拼接成指定大小的长图。 你是否发现直接从豆瓣爬取下来的海报清晰度很低&#xff1f; 使用 .pic .nbg img CSS 选择器&#xff0c;在 我…

MATLAB实现多种群遗传算法

多种群遗传算法&#xff08;MPGA, Multi-Population Genetic Algorithm&#xff09;是一种改进的遗传算法&#xff0c;它通过将种群分成多个子种群并在不同的子种群之间进行交叉和交换&#xff0c;旨在提高全局搜索能力并避免早期收敛。下面是多种群遗传算法的主要步骤和流程&a…

安全防护前置

就业概述 网络安全工程师/安全运维工程师/安全工程师 安全架构师/安全专员/研究院&#xff08;数学要好&#xff09; 厂商工程师&#xff08;售前/售后&#xff09; 系统集成工程师&#xff08;所有计算机知识都要会一点&#xff09; 学习目标 前言 网络安全事件 蠕虫病毒--&…

Python 梯度下降法(六):Nadam Optimize

文章目录 Python 梯度下降法&#xff08;六&#xff09;&#xff1a;Nadam Optimize一、数学原理1.1 介绍1.2 符号定义1.3 实现流程 二、代码实现2.1 函数代码2.2 总代码 三、优缺点3.1 优点3.2 缺点 四、相关链接 Python 梯度下降法&#xff08;六&#xff09;&#xff1a;Nad…