Camunda新手起步全解(教程)

Camunda新手起步全解(教程)

2024-08-20

教程目标:

在这里插入图片描述

背景: 根据研发需要尝试Camunda技术,官网和很多博客都说的不清不楚,所以写这一篇博客,因为写的时候我也是新手,所以只保证有如图所示的全流程代码,不能保证代码的标准性。

第一步 版本选择

本例中使用的 java8+spring-boot.version2.3.12+Camunda7.18.0+sqlsever2022
(对版本测试的时候Camunda7.21可能需要java11,可能最新7.21不支持java17)

第二部 搭建项目(java8的本地运行环境请自行搭建,建议本地java版本和项目一致)

pom.xml 中 camunda 的依赖 其中jdbc用于数据库初始化

        <dependency>
            <groupId>org.camunda.bpm.springboot</groupId>
            <artifactId>camunda-bpm-spring-boot-starter-rest</artifactId>
            <version>7.18.0</version>
        </dependency>
        <dependency>
            <groupId>org.camunda.bpm.springboot</groupId>
            <artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId>
            <version>7.18.0</version>
        </dependency>
        <dependency>
            <groupId>org.camunda.bpm.springboot</groupId>
            <artifactId>camunda-bpm-spring-boot-starter</artifactId>
            <version>7.18.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

application.properties

#项目端口
server.port=8080

#数据库配置
spring.datasource.url=jdbc:sqlserver://localhost:1433;databaseName=xhd;encrypt=true;trustServerCertificate=true
spring.datasource.username=xhd
spring.datasource.password=123456
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver

#camunda配置
#camunda http://localhost:8080/ 的账号/密码/用户名
camunda.bpm.admin-user.id=admin
camunda.bpm.admin-user.password=123456
camunda.bpm.admin-user.firstName=xhd

camunda.bpm.filter.create=All tasks
#设置为true camunda 才会去初始化数据库
camunda.bpm.database.schema-update=true

SpringBootApplication添加注解@EnableProcessApplication

@SpringBootApplication
@EnableProcessApplication
public class CamundaDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(CamundaDemoApplication.class, args);
    }
}

到这里就可以启动项目了,打开数据库观察是否创建表。
在这里插入图片描述

第三步 webapp的使用

访问http://localhost:8080/登录配置的账号
在这里插入图片描述

界面的基础
在这里插入图片描述

第四步 新建流程

去官网(https://downloads.camunda.cloud/release/camunda-modeler/5.26.0/camunda-modeler-5.26.0-win-x64.zip)下载camunda modeler(一定要用这个建立bpmn!!格式完全不一样!!!)
注意事项:

  1. 如果你没有节点的属性界面,打开window->toggle Propperties Panel
  2. 确认左下角的版本号和你的依赖一致
    在这里插入图片描述

我的流程代码

  1. 流程代码bpmn diagram camunda7
  2. camunda form1
  3. camunda form2
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_1dfbq7u" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.25.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.18.0">
  <bpmn:process id="work" name="work" isExecutable="true">
    <bpmn:extensionElements />
    <bpmn:startEvent id="startNode" camunda:formRef="startNodeForm" camunda:formRefBinding="latest">
      <bpmn:extensionElements />
      <bpmn:outgoing>Flow_0hvv6ir</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:endEvent id="ee">
      <bpmn:incoming>Flow_1p0ycm3</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="Flow_0hvv6ir" sourceRef="startNode" targetRef="stuInfoNode" />
    <bpmn:sequenceFlow id="Flow_0y83x2m" sourceRef="stuInfoNode" targetRef="Gateway_176mzwj" />
    <bpmn:userTask id="stuInfoNode" name="stuInfoNode" camunda:formRef="stuInfoForm" camunda:formRefBinding="latest">
      <bpmn:extensionElements>
        <camunda:properties>
          <camunda:property name="className" />
        </camunda:properties>
      </bpmn:extensionElements>
      <bpmn:incoming>Flow_0hvv6ir</bpmn:incoming>
      <bpmn:outgoing>Flow_0y83x2m</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:sequenceFlow id="Flow_1p0ycm3" sourceRef="serve1" targetRef="ee" />
    <bpmn:serviceTask id="serve1" name="serve" camunda:delegateExpression="#{class2Server}">
      <bpmn:incoming>Flow2</bpmn:incoming>
      <bpmn:outgoing>Flow_1p0ycm3</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:sequenceFlow id="Flow2" sourceRef="Gateway_176mzwj" targetRef="serve1">
      <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">#{className=="二班"}</bpmn:conditionExpression>
    </bpmn:sequenceFlow>
    <bpmn:exclusiveGateway id="Gateway_176mzwj" name="ww">
      <bpmn:incoming>Flow_0y83x2m</bpmn:incoming>
      <bpmn:outgoing>Flow2</bpmn:outgoing>
      <bpmn:outgoing>Flow1</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:serviceTask id="Activity_0yqnxqs" camunda:delegateExpression="#{class1Server}">
      <bpmn:incoming>Flow1</bpmn:incoming>
      <bpmn:outgoing>Flow_13sm52t</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:sequenceFlow id="Flow1" sourceRef="Gateway_176mzwj" targetRef="Activity_0yqnxqs">
      <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">#{className=="一班"}</bpmn:conditionExpression>
    </bpmn:sequenceFlow>
    <bpmn:sequenceFlow id="Flow_13sm52t" sourceRef="Activity_0yqnxqs" targetRef="Event_0wamb04" />
    <bpmn:endEvent id="Event_0wamb04">
      <bpmn:incoming>Flow_13sm52t</bpmn:incoming>
    </bpmn:endEvent>
  </bpmn:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="work">
      <bpmndi:BPMNShape id="Activity_0qcdx0f_di" bpmnElement="stuInfoNode">
        <dc:Bounds x="230" y="177" width="100" height="80" />
        <bpmndi:BPMNLabel />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="startNode">
        <dc:Bounds x="152" y="199" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Gateway_1mksrgi_di" bpmnElement="Gateway_176mzwj" isMarkerVisible="true">
        <dc:Bounds x="375" y="192" width="50" height="50" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="435" y="210" width="16" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_1r5owff_di" bpmnElement="serve1">
        <dc:Bounds x="460" y="270" width="100" height="80" />
        <bpmndi:BPMNLabel />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_1udp1mp_di" bpmnElement="Activity_0yqnxqs">
        <dc:Bounds x="460" y="80" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_17y8fpi_di" bpmnElement="ee">
        <dc:Bounds x="652" y="292" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_0wamb04_di" bpmnElement="Event_0wamb04">
        <dc:Bounds x="652" y="102" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="Flow_1p0ycm3_di" bpmnElement="Flow_1p0ycm3">
        <di:waypoint x="560" y="310" />
        <di:waypoint x="652" y="310" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_13sm52t_di" bpmnElement="Flow_13sm52t">
        <di:waypoint x="560" y="120" />
        <di:waypoint x="652" y="120" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0hvv6ir_di" bpmnElement="Flow_0hvv6ir">
        <di:waypoint x="188" y="217" />
        <di:waypoint x="230" y="217" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0y83x2m_di" bpmnElement="Flow_0y83x2m">
        <di:waypoint x="330" y="217" />
        <di:waypoint x="375" y="217" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0phsi6g_di" bpmnElement="Flow2">
        <di:waypoint x="400" y="242" />
        <di:waypoint x="400" y="310" />
        <di:waypoint x="460" y="310" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_04x3hsj_di" bpmnElement="Flow1">
        <di:waypoint x="400" y="192" />
        <di:waypoint x="400" y="120" />
        <di:waypoint x="460" y="120" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>
{
  "components": [],
  "type": "default",
  "id": "startNodeForm",
  "exporter": {
    "name": "Camunda Modeler",
    "version": "5.25.0"
  },
  "executionPlatform": "Camunda Platform",
  "executionPlatformVersion": "7.18.0",
  "schemaVersion": 16
}
{
  "components": [
    {
      "label": "Text field",
      "type": "textfield",
      "layout": {
        "row": "Row_0ujezyc",
        "columns": null
      },
      "id": "nameList",
      "key": "nameList"
    }
  ],
  "type": "default",
  "id": "stuInfoForm",
  "exporter": {
    "name": "Camunda Modeler",
    "version": "5.25.0"
  },
  "executionPlatform": "Camunda Platform",
  "executionPlatformVersion": "7.18.0",
  "schemaVersion": 16
}

使用工具部署(火箭图标,不需要输入任何东西)
在这里插入图片描述

在浏览器8080->Cockpit管理你的流程资源,保证key的唯一性(方便代码操作)
在这里插入图片描述

我的流程解释:(节点转换使用扳手图标。被绑定表单的节点会自动挂起节点任务,中断流程,流程等待任务完成后继续。)

  1. 起始节点(default):命名、绑定了一个表单用于配置启动的参数(例如:发起人)(补充:启动流程的参数会自动完成这个节点表单挂起的任务)。
  2. 表单节点(user task):命名、绑定了一个表单用于获取前端返回数据(n个json)(补充:表单的怕配置只是用它停止流程,启用人工任务,方便从接口处理数据)。
  3. 网关节点和它的箭头(default}箭头才是本体doge):命名箭头、填写箭头的条件值(#{className==“二班”})(补充:分支的参数名必须和进程一起初始化,且设定一个默认的参数,这意味着流程初始化时必须指定一条默认的流程路径)。
  4. 服务节点(消费节点service task):命名,使用的Delegate expression=#{class1Server}(会去寻找和class1Server同名的bean类,继承JavaDelegate接口)。
  5. 结束节点(默认)

第五步 JAVA代码

在这里插入图片描述

  1. task 测试用接口
import com.alibaba.fastjson2.JSONObject;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.engine.variable.Variables;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.*;

@RestController
@RequestMapping(value = "/task")
public class task {

    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    TaskService taskService;
    @Autowired
    ManagementService managementService;
    @Autowired
    IdentityService identityService;
    @Autowired
    HistoryService historyService;
    @Autowired
    FormService formService;

    @GetMapping(value = "/status")
    public String status() {
        JSONObject result = new JSONObject();

        List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery().active().listPage(0, 10);

        List<String> ids = new ArrayList<>();
        Map<String, String> pmap = new HashMap<>();

        processInstanceList.forEach(processInstance -> {
            String pid = processInstance.getId();
            String pDid = processInstance.getProcessDefinitionId();

            pmap.put(pid, pDid);
            ids.add(pid);

            System.out.println("Pid=" + pid);
            taskService.createTaskQuery().processInstanceId(pid).listPage(0, 10).forEach((task) -> {
                System.out.println("Tid=" + task.getId());
                System.out.println("TDid=" + task.getTaskDefinitionKey());
                System.out.println("TName=" + task.getName());
            });
        });

        result.put("processes", pmap);
        return result.toString();
    }

    @GetMapping(value = "/start")
    public String start() {
        String processKey = "work";
        String sponsor = "谢淮东";
        String startTime = Calendar.getInstance().getTime().toString();

        Map<String, Object> variables = Variables.createVariables().putValue("className", "一班");
        variables.put("发起人", sponsor);
        variables.put("发起时间", startTime);

        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processKey, variables);

        String currentProcessID = processInstance.getId();
        return processInstance.getId();
    }

    @GetMapping(value = "/task")
    public String task(@RequestParam("pid") String pid) {

        Task task = taskService.createTaskQuery().processInstanceId(pid).singleResult();

        Map<String, Object> variables = new HashMap<>();
        variables.put("className", "二班");
        List<String> nameList = new ArrayList<>();
        nameList.add("xx1");
        nameList.add("xx2");
        variables.put("nameList", nameList);

        taskService.complete(task.getId(), variables);
        return task.getId();
    }
}
  1. class1Server消费类1
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;

import java.util.Map;

//set bean name
@Component(value="class1Server")
public class class1Server implements JavaDelegate {

    public void execute(DelegateExecution execution) throws Exception {
        Map<String, Object> map = execution.getVariables();
        map.forEach((key, value) -> {
            System.out.println("Key = " + key + ", Value = " + value);
        });
        System.out.println("send class1Server !!!");
    }
}
  1. class1Server消费类2
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;

import java.util.Map;

//set bean name
@Component(value="class2Server")
public class class2Server implements JavaDelegate {

    public void execute(DelegateExecution execution) throws Exception {
        Map<String, Object> map = execution.getVariables();
        map.forEach((key, value) -> {
            System.out.println("Key = " + key + ", Value = " + value);
        });
        System.out.println("send class2Server !!!");
    }
}
  1. 使用 访问/start 把start返回的id拿去启动/task,就完成了。
  2. 解释->从流程模板(processDefine)的key启动流程实例(prcoessInstance),构建附属变量池,类似于数据包?实例携带数据包经过每一个节点->流程自动运行到含有表单(human task人工操作节点)的节点中止,启动任务实例->流程ID获取当前的task(也许可以通过getName自动判断处理,也许也可以和前端11对接接口)->sevice自动匹配处理数据(到这一步基本上就为所欲为了)。
  3. 前端需要的:1:processDefineKey列表。2.prcoessInstance ID列表 3.task 列表->id->name(确认状态)

结语

希望各位能顺利启动第一个流程,网上我是真没找到一轮全流程代码才写的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值