springboot集成Camunda
一、Camunda Modeler
Camunda Modeler -为流程设置器(建模工具),用来构建我们的流程模型。Camunda Modeler流程绘图工具,支持三种协议类型流程文件分别为:BPMN、DMN、Form。
Camunda Modeler下载地址:https://camunda.***/download/modeler/
下载完成之后解压之后,打开Camunda Modeler.exe 即可使用。
下面分别简单介绍一下 Camunda Modeler 建模工具分别支持的三种协议区别。 我文档主要是基于 BPMN协议来实现的流程相关开发,BPMN协议的使用介绍–跳转连接
打开流程设计器后,选择BPMN协议,就会出现我们的设计界面,如下图所示:
-
BPMN协议
BPMN diagram:Business Process Management And Notation 业务流程管理和符号。 由BPMI(The Business Process Management Initiative)开发了一套标准叫业务流程建模符号(BPMN - Business Process Modeling Notation),BPMN规范是由标准组织BPMI发布的BPMN 1.0规范发布于2004年5月。此规范展示了BPMI组织两年多的努力成果。BPMN的主要目标就是要提供被所有业务用户理解的一套标记语言,包括业务分析者、软件开发者以及业务管理者与监察者。BPMN还将支持生成可执行的BPEL4WS语言。所以,BPMN在业务流程设计与流程实现之间搭建了一条标准化的桥梁。
-
DMN协议
DMN diagram:Decision Management And Notation 决策管理和符号。
DMN倾向于决策(规则引擎),DMN是由对象管理组于2015年发布。它是用于业务决策的图形语言。
DMN的主要目的是为分析人员提供一种工具,用于将业务决策逻辑与业务流程分离。 -
Form协议
Form:一个简单的表单模型,可以低代码生成简单的页面。在Camunda中,表单用于在业务流程中收集和显示数据。Camunda支持两种类型的表单:生成的表单和自定义表单。生成的表单是由Camunda自动创建的,基于流程变量的类型和结构。自定义表单则允许开发人员自定义表单的外观和行为。
1、相关概念
- 流程定义与流程实例:二者就像java中的类与new的对象的关系,对象是类的实例,类是对象的模板。
- 流程定义key:标识一类流程定义,流程定义带有版本功能,而流程定义id才代表具体的流程定义。
- 流程定义Id: 具有唯一性,同一个流程定义有不同版本,他们的流程定义Key相同,但是流程定义id不同。
- 流程实例Id:代表具体的某条流程实例,具有唯一性。
- 任务:任务是直接对应人的,每个节点上会有多个任务,但是每个任务只会对应一个处理人。
- 会签:指同一个审批节点设置多个人,如ABC三人,三人会同时收到审批,需全部同意之后,审批才可到下一审批节点。
- 或签:指同一个审批节点设置多个人,如ABC三人,三人会同时收到审批,只要其中任意一人审批即可到下一审批节点。
会签的分类
一个流程节点可以有一个至多个任务,也称之为工作项。如果是多个任务则称这个节点为会签。
-
串行会签:串行会签也叫顺序会签,指按照提交流程处理人的次序user1、user2、user3依次接收待办任务,并按顺序处理流程。(Camunda Modeler中用 “ 三 ” 标识)
-
并行会签:指user1、user2、user3同时接收到流程待办任务,并行处理。(Camunda Modeler中用III标识)
会签的通过规则介绍
- 全部通过:会签人全部审批通过表决后,会签通过。
- 按数量通过:达到一定数量的通过表决后,会签通过。
- 按比例通过:达到一定比例的通过表决后,会签通过。
- 一票通过:只要有一个表决通过的,会签通过。
- 一票否决:只要有一个表决时否定的,会签不通过。
2、UserTask任务参数
用户结点的相关设如下图所示:
- NAME:表示该结点名称
- ID:该节点定义id
- User assignment:审核人相关的设置
- Assignee:审核人,填写方式为 ${ userOne } 的方式
流程设置完成之后,生成的 .BPMN 文件中就会详细记录我们设置的流程图,文件中详细记录了流程定义的相关数据信息。
**注:**BPMN文件中的 process标签下的参数
1. id参数:对应的就是该流程的定义 KEY_ 在文件中的该id值没有改变,则重新部署流程定义之后,流程引擎会默认是版本的迭代,并取最新版本来发起新的流程实例。
2. name参数:对应的是在构建流程时,给流程设置的名称,对于数据库中的 NAME_字段。
3、会签和或签流程的创建
会签 / 或签 中的参数介绍:
-
nrOfInstances:会签中总共的实例数(实例总数)
-
nrOfActiviteInstances:当前活动的实例数量,即还没有完成的实例数量对应串行而言该值始终为1
-
loopCounter :循环计数器,办理人在列表中的索引
-
nrOf***pletedInstances:已经完成的实例数量
-
loop cardinality:循环基数。可选项。可以直接填整数,表示会签的人数。
-
Collection:集合。可选项。会签人数的集合,通常为list,和loop cardinality二选一。
-
Element variable:元素变量。选择Collection时必选,为collection集合每次遍历的元素。
-
***pletion condition:完成条件。可选。比如设置一个人完成后会签结束,那么其他人的代办任务都会消失。
-
或签节点的创建
通过 ***pletion condition中的表达式来确定节点是否通过,从来通过表达式还设置为 会签 / 或签
4、流程设计器中的监听器
流程设计器中的每个节点、节点间的连接线都可以设置监听器,同时监听器中也有多种执行方式。
4.1 监听器类型
可以分为三种类型来使用,分别是:
-
连接线上的 Execution Listeners(take)监听器,如下图所示
-
任务节点上的Task Listeners 执行监听器:
任务节点上的任务监听器有分多钟类型,不同类型(Event type)的任务监听器的执行时机是不相同的,所以在实际使用时要根据实际需求来使用。
- 任务节点上的Execution Listeners 任务监听器:
任务节点上的 Execution Listeners 执行监听器有两种类型分别是 end 和 start
4.1 监听器相关参数介绍
Event type :触发时机
Listener ID:
listener type :监听器实施类型
Field Injection :该模块下可以设置监听器参数,后续能传递到后端使用,主要key要保持一致
在 实施类型 中大概有一下几种:
Delegate expression : 会调用java代码执行在Spring中的配置同名的Bean,参数填写 ${BeanName}
Java Class :参数填写目标类的全路径。 同时该类需要实现对应的接口并重写接口中对应的方法(执行体)
Expression:利用El表达式调用对应的 java 类。
4.3 监听器实现类
- 后台执行监听器:ExecutionListener
在流程实例执行的过程中触发某个事件时,执行监听器允许你去执行额外的java代码或者对指定的表达式求值。
其中 Field Injection 可以用来给监听器传参,key需要与监听器实现类中的key保持一致。
在java代码中实现的方式有多种:
eg:通过java类实现 ExecutionListener 类,重写@Override 方法 notify()即可
package ***.zhidasifang.camundaproject.camundaListener;
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.ExecutionListener;
import org.camunda.bpm.engine.delegate.Expression;
/**
* @Description:---任务结点的监听器:映射的java类实现ExecutionListener接口,一旦出发绑定的节点则会自动触发notify方法
* @ClassName: MyExecutionListener
*/
public class MyExecutionListener implements ExecutionListener {
//与流程设计器中的参数name字段对应!!!---在java代码中需要定义成Expression的对象 名称和name保持一致
private Expression ListenersParameter;
/**
*@Description--todo--映射的java类实现ExecutionListener接口,一旦出发绑定的节点则会自动触发notify方法
*@Param [delegateExecution]
*@return void
*/
@Override
public void notify(DelegateExecution delegateExecution) throws Exception {
System.out.println("message.getValue(delegateExecution) = " + ListenersParameter.getValue(delegateExecution));
}
}
- 任务监听器:TaskListener
与 ExecutionListener 大体一致,不同的是 TaskListener 的java实现类是 TaskListener 接口
属性值有:create【创建任务时触发】、assignment【任务指定负责人触发】、***plete:【任务完成后触发】、delete:【任务删除前触发】
package ***.zhidasifang.camundaproject.camundaListener;
import org.camunda.bpm.engine.delegate.DelegateTask;
import org.camunda.bpm.engine.delegate.Expression;
import org.camunda.bpm.engine.delegate.TaskListener;
/**
* @Description:--TaskListener可以监听流程创建销毁等等与业务无关的操作java代码与ExepressionListener大体一致,
* 不同的是TaskListener的java类实现的是TaskListener接口
* @ClassName: MyTaskListener
*/
public class MyTaskListener implements TaskListener {
private Expression message;
/**
*@Description--监听到任务,会自动执行notify方法!!
*@Param [delegateTask]
*@return void
*/
@Override
public void notify(DelegateTask delegateTask) {
}
}
4.4 Listeners监听器触发时机
根据上面介绍的Listenners相关的设置方式,通过创建一个节点,并实现它的所有监听器(Task Listeners 和 Execution Listeners )包括连接线上的监听器。启动该流程测试各个监听器的执行时机顺序。
- 节点上设置的监听器示意图
- UserTask 用户任务节点的连线上设置的Listeners
在后台的Java代码中我们对应的都要实现这几个监听器。当然这些监听器也可以重复的指向同一个java实现类,后台的监听器类:
/**
* @Description: 测试任务监听器执行时机
* 用于[Listeners触发时机测试流程] 的任务监听器测试
* @ClassName: FlowTaskListeners
*/
@***ponent
@RequiredArgsConstructor
@Slf4j
public class FlowTaskListeners implements TaskListener {
/**
* 1、在create事件之前不会触发其他与任务相关的事件
* 2、当分配人、所有者等属性发生变更时会触发update事件,任务的初始化是不会触发update事件的。
* 3、在流程定义中显式定义了具有受让人的任务被创建时,assignment事件会在create事件之后触发。
* 更改任务assignee属性,assignment事件会在update事件之后触发。
* 4、当任务成功完成时,触发***plete事件。
* 5、delete事件发生在任务从运行时数据中删除之前。
* 6、***plete事件和delete事件是互斥的。
*/
@Override
public void notify(DelegateTask delegateTask) {
String eventName = delegateTask.getEventName();
log.info("【TaskListener 执行】=========="+eventName);
}
}
-------------------分割线-----------------
/**
* @Description: 执行监听器测试流程
* 用于[Listeners触发时机测试流程]的执行监听器
* @ClassName: FlowExecutionListeners
*/
@***ponent
@Slf4j
@RequiredArgsConstructor
public class FlowExecutionListeners implements ExecutionListener {
@Override
public void notify(DelegateExecution execution) throws Exception {
String eventName = execution.getEventName();
log.info("【ExecutionListener 执行】=========="+eventName);
/**
* 设置受理人Assignee
* execution.getVariable("type");
* execution.setVariable("key","value");
* */
}
}
通过启用流程、节点审核等操作之后,可以得到流程中监听器的执行时机顺序。如下所示:
<!--
1.开始节点ExecutionListener
【ExecutionListener 执行】==========start
【ExecutionListener 执行】==========end
2.连线ExecutionListener
【ExecutionListener 执行】==========take
3.Task任务节点上设置的Listeners
Active任务节点:ExecutionListener(start节点开始)
【ExecutionListener 执行】==========start
Active任务节点:TaskListener(create ~~> assignment ~~> ***plete)
【TaskListener 执行】==========create
【TaskListener 执行】==========assignment
【TaskListener 执行】==========***plete
Active任务节点:ExecutionListener(end节点结束)
【ExecutionListener 执行】==========end
4.连线ExecutionListener
【ExecutionListener 执行】==========take
5.结束节点ExecutionListener
【ExecutionListener 执行】==========start
【ExecutionListener 执行】==========end
-->
5、 SendTask 抄送任务节点设置
Send task 是流程中的一种任务节点,我们可以通过该节点设置自定义活动,实现流程异步执行任务流程中所设计到的其他相关操作(eg:发送邮件等操作) 。
抄送任务的事项方式与监听器的实现方式类型,其中的 type 字段中可选择Send Task 节点的实现方式,这里我们选择的是通过java代码来实现,所以就需要后天有与之对应的SendTask 任务节点的实现类,当流程执行到该位置后,会自动调用后台该实现类。
- 后台实现代码
package ***.zhidasifang.camundaproject.camundaTaskOtherMethods;
import ***.zhidasifang.camundaproject.utils.LogUtils;
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.***ponent;
/**
* @Description: SendTask抄送环节任务 委托实现类
* @ClassName: MySendMessageTask
*
* Camunda的Send Task用于向外部系统或服务发送消息。消息可以是同步或异步的,可以发送到队列、主题或其他类型的消息中间件。
* Send Task通常用于将消息发送到外部系统,而无需等待响应或结果。相反,它只是向外部系统发出信号,通知其执行某些操作或启动某个过程。
*
*/
@***ponent
public class MySendMessageTask implements JavaDelegate {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void execute(DelegateExecution delegateExecution) throws Exception {
System.out.println("执行消息推送接口成功!!");
LogUtils.writeLogger(logger,"!!!!!!!!!!","SendTask任务触发成功!!");
}
}
6、流程节点参数传递方式
在BPMN流程设计图中,我们可以在流程中设置各种参数,使得在我们的Java代码中可以获取到设置的相关参数,这一步主要是为了在流程设计后,在审核中,我们可以通过获取对应的参数。从而在后台中通过各种Listeners 来动态的设置下一节点的审核人。实现流程节点的审核人实现动态设置!
流程中设置参数的方式主要是有两中方式去设置参数,然后通过java代码中获取,分别如下:
6.1 在监听器中设置参数
该方式就是通过task任务节点的监听器中设置参数,然后后台在对应的监听器中获取到设置的对应参数。
- BPMN流程中设置的参数
如上图所示,在Listeners监听器中设置了两个参数,其中 name表示参数名称,该名称必须要与后台监听器实现类获取参数时的名称保持一致,否则会获取不到传递的参数数据。
- 后代java代码获取监听器中设置的参数方式:
package ***.zhidasifang.camundaproject.camundaListeners.tasklisteners;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.delegate.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.***ponent;
import java.util.ArrayList;
/**
* @Description:节点开始前的监听器,通过input中的参数设置对应的审核人信息!---动态地为任务分配办理人,而不是在流程定义中静态地指定。
* @ClassName: StartListennersSetAssignee
*/
@***ponent
@Slf4j
@RequiredArgsConstructor
public class TestTempListeners implements TaskListener{
//这里定义的Expression 字段名称必须要和流程中 Field Injection 名称一致
private Expression listenerField1;
private Expression listenerField2;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
*@Description---任务监听器实现类
*@Param [delegateTask]
*@return void
*/
@Override
public void notify(DelegateTask delegateTask) {
//LogUtils.writeLogger(LoggerFactory.getLogger(this.getClass()),delegateTask,"节点创建监听器-CreateListener执行!");
//LogUtils.writeLogger(logger,delegateTask,"节点创建监听器-CreateListener执行!");
logger.info("监听器Listeners执行成功!。。。。。。。。。。。。");
/**
* 获取Listeners Field injection 参数。 监听器中注入的参数获取方式!
* */
String value = (String) listenerField1.getValue(delegateTask);
String value2 = (String) listenerField2.getValue(delegateTask);
System.out.println("监听器Listeners中注入的参数为 listenerField1="+value+" listenerField2="+value2);
/**
* 获取监听器类型
* */
String eventName = delegateTask.getEventName();
System.out.println("eventName = " + eventName);
/**
* 获取camunda中的各种服务Service
* delegateTask.getProcessEngine() //基础引擎获取其他各类Service
* */
/**
* 获取生成流程时就已经存在的参数variable [可以获取原有设置的参数值!!]
* */
String initiator = (String) delegateTask.getVariable("initiator");
System.out.println("initiator = " + initiator);
delegateTask.setAssignee(type1);
// TODO: 正常情况下会去数据库获取对应上级userId setAssignee
}
}
6.2 在Inputs标签中设置参数
在Inputs中设置的惨参数本质上也是类似与通过 setVariable() 的方式设置了对应的数据在流程中。同样,我们的流程也可以通过该方式来实现动态设置受理人,通过Inputs标签的方式设置的流程节点参数,同样能通过我们 的监听器去执行时通过调用getVariable(Stirng key) ,来获取设置的参数。
- BPMN-Inputs标签中设置参数。
如同所示我们在对应节点上设置参数数据,同时我们需要给节点设置一个Listeners监听器(Execution Listeners 、Task Listeners 两种监听器均可),当节点的监听器触发时,在对应的监听器中我们就能获取到设置的Inputs参数!
- 后台Java实现代码
@***ponent
@Slf4j
@RequiredArgsConstructor
public class TestTempListeners implements TaskListener{
//这里定义的Expression 字段名称必须要和流程中 Field Injection 名称一致
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
*@Description---任务监听器实现类
*@Param [delegateTask]
*@return void
*/
@Override
public void notify(DelegateTask delegateTask) {
/**
* 获取监听器类型
* */
String eventName = delegateTask.getEventName();
System.out.println("eventName = " + eventName);
/**
* 获取camunda中的各种服务Service
* delegateTask.getProcessEngine() //基础引擎获取其他各类Service
* */
/**
* 监听器中,获取inputs中注入的参数数据:
* */
//方式1:
String type1 = (String) delegateTask.getVariable("type");
System.out.println("type1 = " + type1);
//方式2:
String type2 =(String) delegateTask.getProcessEngine().getRuntimeService().getVariables(delegateTask.getExecutionId()).get("type");
System.out.println("type2 = " + type2);
/**
* 获取生成流程时就已经存在的参数variable [可以获取原有设置的参数值!!]
* */
String initiator = (String) delegateTask.getVariable("initiator");
System.out.println("initiator = " + initiator);
delegateTask.setAssignee(type1);
// TODO: 正常情况下会去数据库获取对应上级userId setAssignee
}
**注:**在动态设置节点审核人时,普通的User Task 用户审核节点,可以通过task任务节点上的Listeners监听器来获取参数根据实际场景来设置受理人(通过setAssignee)。
但是会签节点则不能通过task任务节点上的监听器来动态设置审核人,因为在执行到会签节点时,由于会签节点中含有 ${assignee} 等表达式,需要在执行到该节点之前要对这些变量赋值(通过setVariable),所有在存在会签节点时,我们必须在会签节点之前将对应的变量赋值,这时就需要在会签节点的连接线上通过Listeners来对这些表达式中的Variable数据进行赋值处理!