1. BPMN介绍
1.1 Event - 事件
1.1.1 开始事件1.1.2 中间事件1.1.3 边界事件1.1.4 结束事件 1.2 Tasks - 任务
1.2.1 Servcie Task - 调用外部服务
调用服务方式
调用Java代码 - 实现JavaDelegateExternal Task调用web服务(REST、SOAP) 外部任务优先级PrioritiesField注入 1.2.2 Business Rule Task - 业务规则任务(可绑定DMN)1.2.3 User Task - 委托给具体用户处理的任务,需要对应的用户进行处理1.2.4 Input/Output Paramerters 1.3 Gateway - 网关
1.3.1 Exclusive Gateway - 排他网关(1入多出,仅1出执行,出带条件)1.3.2 Parallel Gateway - 并行网关(多入多出,无条件)1.3.3 Inclusive Gateway - 包含网关(多入多出,出带条件)1.3.4 Event based Gateway - 基于事件的网关(1入多出,仅一出执行,出带事件) 1.4 Pool/Lane - 组织/角色1.5 子流程
1.5.1 嵌入式子流程1.5.2 事件子流程1.5.3 事务子流程1.5.4 调用活动 2. DMN介绍
2.1 Decision - 决策
2.1.1 Decision Table(id, name) - 决策表格
Input - 输入(1个或多个)Output - 输出(1个或多个)Rule - 规则,一个决策表格由多行规则组成Hit Policy - 规则匹配策略 2.1.2 Literal expression - 字面量表达式 3. Form介绍
User Task Forms类型
1. BPMN介绍BPMN - Business Process Model and Notation
可以类比上小学时学的流程图,
将流程图和BPMN做个最简单的类比如下表:
| 流程图元素 | 流程图示例 | BPMN对应元素 | BPMN示例 |
|---|---|---|---|
| 开始 | 开始事件Start Event | ||
| 活动 | 任务Task | ||
| 判断 | 网关Gateway | ||
| 结束 | 结束事件End Event | ||
| 连接线 | 顺序流Sequence flow |
可以发现从流程图可以很平滑的过渡到BPMN模型,
简单理解:BPMN就是流程图的升级版,
当然BPMN的功能远不止表格中列出的这些,
接下来的会逐步探索BPMN的相关功能。
接收事件(等待事件发生),触发事件(发送事件),
进而触发流程执行、改变流程走向、结束流程等。
关于事件的分类详见下表(其中橘色的事件为Camunda支持的)
捕获事件(阻塞等待事件通知) 都是空心图标
抛出事件 (抛出结束事件 或者 抛出事件通知后继续执行) 都是实心图标
中断事件 (中断原流程 且 开启新流程) 都是实线圆(子流程中断开始事件、边界型中断中间事件)
非中断事件 (不中断原流程 且 开启新流程) 都是虚线圆(子例程非中断开始事件、边界型非中断中间事件)
开始事件(单实线圆,空心图标,子流程虚线中断)都是捕获事件
结束事件(加粗单实线圆,实心图标)都是抛出事件
中间事件(双实线圆)包括捕捉事件(空心图标)、抛出事件(实心图标)
边界型中间事件(双实线圆,附加在活动、子流程边界,虚线中断)都是捕获事件
空白开始事件Blank定时器开始事件Timer*消息开始事件Message*信号开始事件Signal条件开始事件Conditional
1.1.2 中间事件
捕获事件(Catch Event,空心图标)- 接收事件通知后,触发流程继续执行
抛出事件(Throw Event,实心图标)- 主动抛出事件通知,然后继续执行流程
边界事件(Boundary Evant,亦成为附加中间事件,Attached Intermediate Events),附加到任务、子流程上的中间事件
中断 - 边界事件
若任务执行过程中发生附加事件,则取消当前任务,继续执行附加事件对应的流程,
否则继续执行当前任务正常流程(非附件事件对应分支)非中断 - 边界事件
若任务执行过程中发生附加事件,则继续执行当前任务(即后续流程),同时执行附加事件对应的流程,
若当前任务执行过程中重复发生附加事件,则重复执行附加事件对应的流程,
否则继续执行当前任务正常流程(非附件事件对应分支)补偿 - 边界事件
仅在任务执行完成后,才会执行补偿边界事件连接的的补偿任务,
补偿边界事件可由补偿结束事件、补偿中间抛出事件触发,亦可由事务子流程的取消结束事件触发。
1.1.4 结束事件
Task即为流程活动(对应单个执行步骤)的抽象。
Service Task用于调用外部服务。
调用服务方式调用服务的方式分为:
调用Java代码External Task调用web服务(REST、SOAP) 调用Java代码 - 实现JavaDelegate
Java Class - 指定代理类全路径(实现接口JavaDelegate、ActivityBehavior,抛异常则回滚到上一步),如:com.luo.demo.MyDelegateDelegate expression - 使用表达式来提取代理对象(实现接口JavaDelegate、ActivityBehavior),更适合SpringBoot环境,如:${myDelegateBean}expression - 调用方法表达式,如:${myBean.doWork()}expression - 解析值表达式(evaluating a value expression) External Task
External (External Task)
指定task对应的topic由外部Worker通过REST API(Long Polling)主动获取并锁定(可指定超时时间)任务(亦支持Java API)外部worker通知流程 完成Complete 或 失败Failure
Java API示例代码(需配合外层循环):
Listtasks = processEngine.getExternalTaskService() //最多一次拉取10条task,设置当前workerId为externalWorkerId(需唯一) .fetchAndLock(10, "externalWorkerId") //指定查询的topic,及任务锁定超时时间(超时后可被其他worker获取到) .topic("AddressValidation", 60L * 1000L) .topic("ShipmentScheduling", 120L * 1000L) .execute(); for (LockedExternalTask task : tasks) { try { String topic = task.getTopicName(); // work on task for that topic ... // if the work is successful, mark the task as completed if(success) { externalTaskService.complete(task.getId(), variables); } else { // if the work was not successful, mark it as failed externalTaskService.handleFailure( task.getId(), "externalWorkerId", "Address could not be validated: Address database not reachable", 1, 10L * 60L * 1000L); } } catch(Exception e) { //... handle exception } }
REST API代码示例:
ExternalTaskClient client = ExternalTaskClient.create()
.baseUrl("http://localhost:8080/engine-rest")
.asyncResponseTimeout(10000) // long polling timeout
.build();
// subscribe to an external task topic as specified in the process
client.subscribe("charge-card")
.lockDuration(1000) // the default lock duration is 20 seconds, but you can override this
.handler((externalTask, externalTaskService) -> {
// Put your business logic here
// Get a process variable
String item = (String) externalTask.getVariable("item");
Long amount = (Long) externalTask.getVariable("amount");
LOGGER.info("Charging credit card with an amount of '" + amount + "'€ for the item '" + item + "'...");
try {
Desktop.getDesktop().browse(new URI("https://docs.camunda.org/get-started/quick-start/complete"));
} catch (Exception e) {
e.printStackTrace();
}
// Complete the task
externalTaskService.complete(externalTask);
})
.open();
}
SpringBoot ExternalTaskHandler示例代码:
@ExternalTaskSubscription(topicName = "payment-biz")
@Bean
public ExternalTaskHandler paymentBizHandler() {
return (externalTask, externalTaskService) -> {
//获取流程变量
String productName = externalTask.getVariable(PRODUCT_NAME);
Double productPrice = externalTask.getVariable(PRODUCT_PRICE);
String paymentAssignee = externalTask.getVariable(PAYMENT_ASSIGNEE);
Double productDiscountPrice = externalTask.getVariable(PRODUCT_DISCOUNT_PRICE);
log.info("The External Task {} has been checked!", externalTask.getId());
//完成任务
externalTaskService.complete(externalTask);
//完成任务且添加流程变量
//externalTaskService.complete(externalTask, Variables.putValueTyped("creditScores", creditScoresObject));
log.info("Payment SUCCESS - paymentAssignee={}, productName={}, productPrice={}, productDiscountPrice={}",
paymentAssignee,
productName,
productPrice,
productDiscountPrice);
//任务失败
//externalTaskService.handleFailure(
// externalTask,
// "errorMsg",
// "errorDetails",
// 1,
// 10L * 60L * 1000L);
};
}
调用web服务(REST、SOAP)
connector - 支持Connector ID:http-connector, soap-http-connector
关于使用connector的细节可参见:
https://docs.camunda.org/manual/latest/user-guide/process-engine/connectors
https://github.com/camunda/camunda-bpm-examples/tree/master/servicetask
外部任务优先级Priorities
External Task Priority,数字类型,值越大优先级越大,越先被外部worker查询到
ListField注入tasks = //第三个参数为true,则表示启用安装优先级查询external task externalTaskService.fetchAndLock(10, "externalWorkerId", true) .topic("AddressValidation", 60L * 1000L) .topic("ShipmentScheduling", 120L * 1000L) .execute(); for (LockedExternalTask task : tasks) { String topic = task.getTopicName(); // work on task for that topic ... }
其中Java Class、Delegate expression、External实现方式,支持Field注入,
可通过Service Task对应的标签页Field Injections进行设置,
Camunda官方建议提供Setter方法支持Filed注入(兼容CDI)。
JavaDelegate示例代码:
//实现JavaDelegate(每次执行task都会新建对象实例)
public class ReverseStringsFieldInjected implements JavaDelegate {
//定义待注入的Field(Camunda建议配置Setter方法进行注入)
private expression text1;
private expression text2;
public void execute(DelegateExecution execution) {
//获取流程变量
String var = (String) execution.getVariable("input");
//处理逻辑实现
var = var.toUpperCase();
//重写 或 设置新的流程变量
execution.setVariable("input", var);
//获取注入的Field
String value1 = (String) text1.getValue(execution);
execution.setVariable("var1", new StringBuffer(value1).reverse().toString());
String value2 = (String) text2.getValue(execution);
execution.setVariable("var2", new StringBuffer(value2).reverse().toString());
}
//Camunda官方建议提供Setter方法支持Filed注入(兼容CDI)
public void setText1(expression text1) {
this.text1 = text1;
}
public void setText2(expression text2) {
this.text2 = text2;
}
}
注:
在SpringBoot环境慎用Field注入,由于SpringBoot Bean默认Singleton(单例),
并发访问可能存在field覆盖冲突。
如下图可通过Business Rule Task绑定单独的DMN文件中的Decision决策表格,
以此实现根据不同输入产生不同输出的效果,
DMN输出结果需通过Result Variable等设置进行映射,
映射之后才可供后续流程元素进行访问。
例如如上图中的Decision Ref中product-discount-rule即对应DMN模型中的Decision.id,
如下即为具体决策表格内容,关于DMN的介绍可参见本文第2章:2. DMN介绍。
参考:https://docs.camunda.org/manual/latest/reference/bpmn20/tasks/user-task
assignee - 受托人(处理任务的人)candidate users - 候选人(处理任务的多个人,由逗号分隔)candidate groups - 候选组织(处理任务的多个组织,由逗号分隔)注: assignee、candidate users、candidate groups均支持表达式,例如:
${bizUser} - 支持流程变量bizUser作为受托人${ldapService.findAllSales()} - 支持Spring/CDI bean的方法调用,返回结果为String或Collection 参考:https://docs.camunda.org/manual/latest/user-guide/process-engine/variables/#input-output-variable-mapping Variable Assignment Type=String orexpressionVariable Assignment Value=具体的表达式
表达式示例如下: ${myVar}${myResultMap.key1}
并将提取的值作为当前元素(如task)的: 输入参数(Input Parameters) - 作为输入变量供当前活动使用
Local Variable Name - 输入变量名 输出参数(Output Parameters) - 作为当前活动的输出变量
Process Variable Name - 输出变量名 网关,即根据条件产生不同流程分支。 仅执行第一个条件为真的分支, 并发执行(不评估条件)Fork或Join, Fork - 分支Join - 汇合(在其他分支都执行成功后统一汇合) 可看出Exclusive和Paralles网关的结合体, Fork - 排他网关带条件多分支,并行执行Join - 并行网关汇合 通过捕获事件来决定执行分支(区别于排他网关根据条件), Pool - 池,表示组织、公司、系统 与主流程元素存在顺序流连接仅以唯一空白开始事件开始嵌入子流程为实线边框(区别于事件子流程为虚线) 由事件触发的子流程 与主流程元素不存在顺序流连接支持消息、定时器、信号、错误、补偿等开始事件(不支持空白开始事件)事件子流程为虚线边框(区别于嵌入事件子流程为实线)支持中断、非中断事件子流程
中断(开始事件为实线边框) - 取消当前任务执行,转而执行事件子流程非中断(开始事件为虚线边框) - 继续执行当前任务,进而并发执行事件子流程 嵌入式子流程,对一组活动进行分组,作为一个整体的所有活动同时成功或失败(终止和取消)。 与主流程元素存在顺序流连接事务子流程为双实线边框可由事务子流程内的取消结束事件触发事务子流程回滚(触发子流程内的补偿边界事件的执行)亦可由事务子流程(成功执行)之后的中间抛出补偿事件触发事务子流程回滚(触发子流程内的补偿边界事件的执行)可在事务子流程上添加取消边界事件来接管取消后的事务子流程的流程走向事务子流程内的错误事件若未在事务子流程的作用域内被捕获,那么事务子流程被终止,且不会执行补偿。 调用独立部署的流程(作为当前流程的子流程执行), DMN - Decision Model and Notation 定义规则模型,可与BPMN中Business Rule Task通过Decision.id进行绑定。 如下图新建Decision,默认类型即为Decision Table,可通过扳手图标切换类型。 ID - 唯一IDlabel - 显示标签expression - 提取输入值的表达式(根据Process Variables生成input值)
变量名包含变量名的表达式 expression Language - 表达式语言类型(默认FEEL)type - 提取出的输入值的类型(非必须,建议添加,可避免非法输入类型转换)Input Variable - 提取出的输入值的存储变量名(默认cellInput),可用于后续规则中inputEntry表达式中
Output - 输出(1个或多个)
IDLabel - 显示标签Ouput Name - 输出结果的存储变量名(后续流程中可通过变量名称引用此变量)Type - 输出结果的类型(非必须,建议添加,可避免非法输出类型转换) Input Entry(Condition) - 规则的输入条件空的InputEntry表示永远为真(永远满足)Output Entry(Conclusion) - 规则的输出结果InputEntry和OutputEntry支持表达式当同一行的(多个)InputEntry都满足时则输出对应的(多个)OutputEntry Collect(Sum) - 求和输出Collect(Min) - 最小值输出Collect(Max) - 最大值输出Collect(Count) - 结果数量输出 注: Input expression: sets the input value for an input column of the decision tableInput Entry: used to determine if a rule of the decision table is applicableOutput Entry: returns a value which is added to the output of a matched rule of the decision tableLiteral expression: used to determine the value of a decision literal expression
如下图新建Literal expression, 即自定义表单,用于与BPMN Task进行绑定。 参考: 注: BPMN2.0中仅 StartEvent和UserTask支持链接form FormType: Embeeded Task Forms Form Key FORM-TYPE:LOCATION:FORM.NAME
FORM-TYPE
camunda-formsembedded LOCATION
demployment app 例如 camunda-forms:deployment:payment.formembedded:app:forms/FORM_NAME.html
Form Ref camunda:formRef - keycamunda:formRefBinding
deployment - 位于同一deploymentlatested - 最新版本version - 指定版本,通过camunda:formRefVersion设置版本 camunda:formRefVersion
更多: Process Variable BindingExternal Task Forms…
camunda提供输入、输出参数的转换映射,
即可通过表达式(解析之前已存在的流程变量并转换为新的变量)来提取变量,
1.3 Gateway - 网关
若均不为真,则报异常(可添加默认流)
顺序流分为条件流、默认流。
设置默认流参考:https://forum.camunda.org/t/gateway-default-flow/447/5
1.3.3 Inclusive Gateway - 包含网关(多入多出,出带条件)
1.3.4 Event based Gateway - 基于事件的网关(1入多出,仅一出执行,出带事件)
仅执行第一个发生事件对应的分支,
且每个序列流分支均需连接到一个中间捕获事件。
Lane - 泳道,表示参与者、角色
1.5.2 事件子流程
1.5.3 事务子流程
1.5.4 调用活动
在调用活动执行完成后,再继续执行当前流程。
简单理解:类似可视化的Drools编辑器(规则引擎),
底层默认使用FEEL表达式。
点击Decision左上角的蓝色图标,即可进入决策表编辑界面,决策表各组成部分如下图:
Rule - 规则,一个决策表格由多行规则组成
Hit Policy - 规则匹配策略
Hit Policy 说明 示例 Unique 仅有一条规则满足 或者 都不满足,若仅有一条满足则输出 Any 任何一条规则满足则输出(允许多条规则同时满足,但这些规则必须有相同的输出OutputEntry) First 第一条满足则输出(允许多条规则同时满足,但仅第一条会被输出) Rule order 允许多条规则满足,则按照规则顺序全部输出 Collect 允许多条规则满足,则按照任意(不确定)顺序全部输出
Collect策略允许指定聚合器Aggregators(仅输出一条)
DMN中表达式(默认使用FEEL)支持范围:
即根据表达式提取决策结果,如调用bean方法获取结果。
https://docs.camunda.org/manual/latest/user-guide/task-forms
内嵌HTML或JS表单到TaskList中Camunda Forms
集成Camunda Modeler创建的Form,支持Form Key和Form Ref格式引用External Task Forms
链接自定义应用,Form不内嵌在TaskList中展示
作为部署的一部分,在通过Modeler部署时需通过附件一起上传
form文件集成在war应用中,目录需放在 src/main/webapp/forms
仅支持绑定Camunda Form(Modeler创建)



