Camunda使用表达式操作列表类型的变量

背景

Camunda BPM通过JUEL(Java Unified Expression Language)来支持表达式,详细内容可以参看https://docs.oracle.com/javaee/5/tutorial/doc/bnahq.html。

Camunda Modeler中可以在顺序流(Sequence Flow)、服务任务(Service Task)等大部分BPMN元素上使用表达式。

举个例子,我们现在有一个模拟请假的流程,通过一个代表请假天数的流程实例变量days来判断后续流程的走向,即,如果请假天数大于3天,就要由更高权限的总经理来审批。用最简单的用法实现就是:${days > 3},表达式写在美元符号后面跟着的一对花括号中间${}

在上面的例子中流程实例变量使用的是一个基本数据类型,但是在实际的应用中我们往往需要用更复杂的数据结构来组织我们的变量数据,比较常用的是通过“列表”类型的数据结构:比如java.lang.List或者JsonArray。

在下面的例子中我们设想了一种场景,通过判断一个“列表”结构中的第一个元素中的某个属性来决定后续流程的走向。这个臆想的场景可能并不是很贴切,本质上只是为了演示如何获得到数组或列表中的某个元素的某个属性。

配置

ArrayList

解决方案

变量名称后用中括号指定列表的元素在List的index的位置,圆点后直接跟属性名:

1
${users[0].days > 3}

流程图示

image-20210113162753709

流程定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<?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:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1buqbeu" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.1.1">
<bpmn:process id="Process_1c4zh99" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_0epdrej</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_0epdrej" sourceRef="StartEvent_1" targetRef="Activity_0h7rcl0" />
<bpmn:exclusiveGateway id="Gateway_0ge24e7" default="Flow_1iaknmr">
<bpmn:incoming>Flow_1kyhprf</bpmn:incoming>
<bpmn:outgoing>Flow_1iaknmr</bpmn:outgoing>
<bpmn:outgoing>Flow_1kudzrl</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:sequenceFlow id="Flow_1kyhprf" sourceRef="Activity_0h7rcl0" targetRef="Gateway_0ge24e7" />
<bpmn:sequenceFlow id="Flow_1iaknmr" sourceRef="Gateway_0ge24e7" targetRef="Activity_1c8m888" />
<bpmn:sequenceFlow id="Flow_1kudzrl" sourceRef="Gateway_0ge24e7" targetRef="Activity_0vql5zm">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${users.elements().get(0).prop("days").numberValue() &gt; 3}</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:endEvent id="Event_01kct37">
<bpmn:incoming>Flow_0nsq4ft</bpmn:incoming>
<bpmn:incoming>Flow_02v23vm</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0nsq4ft" sourceRef="Activity_1c8m888" targetRef="Event_01kct37" />
<bpmn:sequenceFlow id="Flow_02v23vm" sourceRef="Activity_0vql5zm" targetRef="Event_01kct37" />
<bpmn:userTask id="Activity_0h7rcl0" name="助理审批" camunda:assignee="demo">
<bpmn:incoming>Flow_0epdrej</bpmn:incoming>
<bpmn:outgoing>Flow_1kyhprf</bpmn:outgoing>
</bpmn:userTask>
<bpmn:userTask id="Activity_1c8m888" name="部门经理审批">
<bpmn:incoming>Flow_1iaknmr</bpmn:incoming>
<bpmn:outgoing>Flow_0nsq4ft</bpmn:outgoing>
</bpmn:userTask>
<bpmn:userTask id="Activity_0vql5zm" name="总经理审批" camunda:assignee="demo">
<bpmn:incoming>Flow_1kudzrl</bpmn:incoming>
<bpmn:outgoing>Flow_02v23vm</bpmn:outgoing>
</bpmn:userTask>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1c4zh99">
<bpmndi:BPMNEdge id="Flow_02v23vm_di" bpmnElement="Flow_02v23vm">
<di:waypoint x="600" y="330" />
<di:waypoint x="666" y="330" />
<di:waypoint x="666" y="220" />
<di:waypoint x="732" y="220" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0nsq4ft_di" bpmnElement="Flow_0nsq4ft">
<di:waypoint x="600" y="140" />
<di:waypoint x="666" y="140" />
<di:waypoint x="666" y="220" />
<di:waypoint x="732" y="220" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1kudzrl_di" bpmnElement="Flow_1kudzrl">
<di:waypoint x="420" y="245" />
<di:waypoint x="420" y="330" />
<di:waypoint x="500" y="330" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1iaknmr_di" bpmnElement="Flow_1iaknmr">
<di:waypoint x="420" y="195" />
<di:waypoint x="420" y="140" />
<di:waypoint x="500" y="140" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1kyhprf_di" bpmnElement="Flow_1kyhprf">
<di:waypoint x="340" y="220" />
<di:waypoint x="395" y="220" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0epdrej_di" bpmnElement="Flow_0epdrej">
<di:waypoint x="188" y="220" />
<di:waypoint x="240" y="220" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="152" y="202" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_0ge24e7_di" bpmnElement="Gateway_0ge24e7" isMarkerVisible="true">
<dc:Bounds x="395" y="195" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_01kct37_di" bpmnElement="Event_01kct37">
<dc:Bounds x="732" y="202" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_07s4exu_di" bpmnElement="Activity_0h7rcl0">
<dc:Bounds x="240" y="180" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_172iz9h_di" bpmnElement="Activity_1c8m888">
<dc:Bounds x="500" y="100" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0or73na_di" bpmnElement="Activity_0vql5zm">
<dc:Bounds x="500" y="290" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

操作步骤

我们可以先通过REST API添加一个流程实例变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
curl --location --request POST 'http://camunda/engine-rest/process-definition/CPHSYS-20201230134633:7:3f7914de-5570-11eb-b228-0a58ac0009d7/start' \
--header 'Content-Type: application/json' \
--data-raw '{
"variables": {
"HTXXMB": {
"value": [
{
"name": "川建国",
"days": 10
},
{
"name": "拜振华",
"days": 2
}
]
}
},
"businessKey": "TonyTest"
}'

在Camunda Cockpit中可以看到这个变量的类型不再是简单数据结构String,而是Object

image-20210114101707580

点击Value列的java.util.ArrayList这个蓝色链接,可以看到反序列化后的变量内容是一个Java List:

image-20210114102600969

JSONArray

解决方案

变量名称后用elements方法获得到所有的JSONArray中的JSON对象,再使用get方法指定index的位置,使用props方法取得某个JSONObject的属性,最后通过numberValue方法解析出这个属性的数值:

1
${users.elements().get(0).prop("days").numberValue() > 3

流程图示

image-20210113162633977

流程定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<?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:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1buqbeu" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.1.1">
<bpmn:process id="Process_1c4zh99" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_0epdrej</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_0epdrej" sourceRef="StartEvent_1" targetRef="Activity_0h7rcl0" />
<bpmn:exclusiveGateway id="Gateway_0ge24e7" default="Flow_1iaknmr">
<bpmn:incoming>Flow_1kyhprf</bpmn:incoming>
<bpmn:outgoing>Flow_1iaknmr</bpmn:outgoing>
<bpmn:outgoing>Flow_1kudzrl</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:sequenceFlow id="Flow_1kyhprf" sourceRef="Activity_0h7rcl0" targetRef="Gateway_0ge24e7" />
<bpmn:sequenceFlow id="Flow_1iaknmr" sourceRef="Gateway_0ge24e7" targetRef="Activity_1c8m888" />
<bpmn:sequenceFlow id="Flow_1kudzrl" sourceRef="Gateway_0ge24e7" targetRef="Activity_0vql5zm">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${users.elements().get(0).prop("days").numberValue() &gt; 3}</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:endEvent id="Event_01kct37">
<bpmn:incoming>Flow_0nsq4ft</bpmn:incoming>
<bpmn:incoming>Flow_02v23vm</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0nsq4ft" sourceRef="Activity_1c8m888" targetRef="Event_01kct37" />
<bpmn:sequenceFlow id="Flow_02v23vm" sourceRef="Activity_0vql5zm" targetRef="Event_01kct37" />
<bpmn:userTask id="Activity_0h7rcl0" name="助理审批" camunda:assignee="demo">
<bpmn:incoming>Flow_0epdrej</bpmn:incoming>
<bpmn:outgoing>Flow_1kyhprf</bpmn:outgoing>
</bpmn:userTask>
<bpmn:userTask id="Activity_1c8m888" name="部门经理审批">
<bpmn:incoming>Flow_1iaknmr</bpmn:incoming>
<bpmn:outgoing>Flow_0nsq4ft</bpmn:outgoing>
</bpmn:userTask>
<bpmn:userTask id="Activity_0vql5zm" name="总经理审批" camunda:assignee="demo">
<bpmn:incoming>Flow_1kudzrl</bpmn:incoming>
<bpmn:outgoing>Flow_02v23vm</bpmn:outgoing>
</bpmn:userTask>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1c4zh99">
<bpmndi:BPMNEdge id="Flow_02v23vm_di" bpmnElement="Flow_02v23vm">
<di:waypoint x="600" y="330" />
<di:waypoint x="666" y="330" />
<di:waypoint x="666" y="220" />
<di:waypoint x="732" y="220" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0nsq4ft_di" bpmnElement="Flow_0nsq4ft">
<di:waypoint x="600" y="140" />
<di:waypoint x="666" y="140" />
<di:waypoint x="666" y="220" />
<di:waypoint x="732" y="220" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1kudzrl_di" bpmnElement="Flow_1kudzrl">
<di:waypoint x="420" y="245" />
<di:waypoint x="420" y="330" />
<di:waypoint x="500" y="330" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1iaknmr_di" bpmnElement="Flow_1iaknmr">
<di:waypoint x="420" y="195" />
<di:waypoint x="420" y="140" />
<di:waypoint x="500" y="140" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1kyhprf_di" bpmnElement="Flow_1kyhprf">
<di:waypoint x="340" y="220" />
<di:waypoint x="395" y="220" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0epdrej_di" bpmnElement="Flow_0epdrej">
<di:waypoint x="188" y="220" />
<di:waypoint x="240" y="220" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="152" y="202" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_0ge24e7_di" bpmnElement="Gateway_0ge24e7" isMarkerVisible="true">
<dc:Bounds x="395" y="195" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_01kct37_di" bpmnElement="Event_01kct37">
<dc:Bounds x="732" y="202" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_07s4exu_di" bpmnElement="Activity_0h7rcl0">
<dc:Bounds x="240" y="180" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_172iz9h_di" bpmnElement="Activity_1c8m888">
<dc:Bounds x="500" y="100" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0or73na_di" bpmnElement="Activity_0vql5zm">
<dc:Bounds x="500" y="290" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

操作步骤

我们可以先通过REST API添加一个流程实例变量:

1
2
3
4
5
6
7
8
9
10
curl --location --request POST 'http://11.11.176.126:48086/engine-rest/process-definition/Process_1c4zh99:1:ad3f08dc-553c-11eb-b228-0a58ac0009d7/start' \
--header 'Content-Type: application/json' \
--data-raw '{
"variables": {
"users": {
"value": "[{\"name\":\"川建国\",\"days\":10}, {\"name\":\"拜振华\",\"days\":2}]]",
"type": "json"
}
}
}'

在Camunda Cockpit中可以看到这个变量的类型不再是简单数据结构String,而是Json

image-20210114104419361

点击Value列的那个字符串链接,可以看到反序列化后的变量内容是一个JSON Array的字符串表示:

image-20210114104446747

参考

EOF