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!!格式完全不一样!!!)
注意事项:
- 如果你没有节点的属性界面,打开window->toggle Propperties Panel
- 确认左下角的版本号和你的依赖一致
我的流程代码
- 流程代码bpmn diagram camunda7
- camunda form1
- 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的唯一性(方便代码操作)
我的流程解释:(节点转换使用扳手图标。被绑定表单的节点会自动挂起节点任务,中断流程,流程等待任务完成后继续。)
- 起始节点(default):命名、绑定了一个表单用于配置启动的参数(例如:发起人)(补充:启动流程的参数会自动完成这个节点表单挂起的任务)。
- 表单节点(user task):命名、绑定了一个表单用于获取前端返回数据(n个json)(补充:表单的怕配置只是用它停止流程,启用人工任务,方便从接口处理数据)。
- 网关节点和它的箭头(default}箭头才是本体doge):命名箭头、填写箭头的条件值(#{className==“二班”})(补充:分支的参数名必须和进程一起初始化,且设定一个默认的参数,这意味着流程初始化时必须指定一条默认的流程路径)。
- 服务节点(消费节点service task):命名,使用的Delegate expression=#{class1Server}(会去寻找和class1Server同名的bean类,继承JavaDelegate接口)。
- 结束节点(默认)
第五步 JAVA代码
- 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();
}
}
- 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 !!!");
}
}
- 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 !!!");
}
}
- 使用 访问/start 把start返回的id拿去启动/task,就完成了。
- 解释->从流程模板(processDefine)的key启动流程实例(prcoessInstance),构建附属变量池,类似于数据包?实例携带数据包经过每一个节点->流程自动运行到含有表单(human task人工操作节点)的节点中止,启动任务实例->流程ID获取当前的task(也许可以通过getName自动判断处理,也许也可以和前端11对接接口)->sevice自动匹配处理数据(到这一步基本上就为所欲为了)。
- 前端需要的:1:processDefineKey列表。2.prcoessInstance ID列表 3.task 列表->id->name(确认状态)
结语
希望各位能顺利启动第一个流程,网上我是真没找到一轮全流程代码才写的。