springboot集成Camunda审核流程(二):Camunda Modeler设计器设置BPMN流程
作者:mmseoamin日期:2023-12-21

Springboot集成Camunda

一、Camunda Modeler

​ Camunda Modeler -为流程设置器(建模工具),用来构建我们的流程模型。Camunda Modeler流程绘图工具,支持三种协议类型流程文件分别为:BPMN、DMN、Form。

​ Camunda Modeler下载地址:https://camunda.com/download/modeler/

下载完成之后解压之后,打开Camunda Modeler.exe 即可使用。

springboot集成Camunda审核流程(二):Camunda Modeler设计器设置BPMN流程,在这里插入图片描述,第1张

下面分别简单介绍一下 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三人,三人会同时收到审批,只要其中任意一人审批即可到下一审批节点。

      会签的分类

      ​ 一个流程节点可以有一个至多个任务,也称之为工作项。如果是多个任务则称这个节点为会签。

      1. 串行会签:串行会签也叫顺序会签,指按照提交流程处理人的次序user1、user2、user3依次接收待办任务,并按顺序处理流程。(Camunda Modeler中用 “ 三 ” 标识)

      2. 并行会签:指user1、user2、user3同时接收到流程待办任务,并行处理。(Camunda Modeler中用III标识)

      会签的通过规则介绍

      1. 全部通过:会签人全部审批通过表决后,会签通过。
      2. 按数量通过:达到一定数量的通过表决后,会签通过。
      3. 按比例通过:达到一定比例的通过表决后,会签通过。
      4. 一票通过:只要有一个表决通过的,会签通过。
      5. 一票否决:只要有一个表决时否定的,会签不通过。

      2、UserTask任务参数

      ​ 用户结点的相关设如下图所示:

      springboot集成Camunda审核流程(二):Camunda Modeler设计器设置BPMN流程,在这里插入图片描述,第2张

      • NAME:表示该结点名称
      • ID:该节点定义id
      • User assignment:审核人相关的设置
        • Assignee:审核人,填写方式为 ${ userOne } 的方式

          流程设置完成之后,生成的 .BPMN 文件中就会详细记录我们设置的流程图,文件中详细记录了流程定义的相关数据信息。

          springboot集成Camunda审核流程(二):Camunda Modeler设计器设置BPMN流程,在这里插入图片描述,第3张

          ​ **注:**BPMN文件中的 process标签下的参数

          1. id参数:对应的就是该流程的定义 KEY_ 在文件中的该id值没有改变,则重新部署流程定义之后,流程引擎会默认是版本的迭代,并取最新版本来发起新的流程实例。
          2. name参数:对应的是在构建流程时,给流程设置的名称,对于数据库中的 NAME_字段。
          

          3、会签和或签流程的创建

          会签 / 或签 中的参数介绍:

          • nrOfInstances:会签中总共的实例数(实例总数)

          • nrOfActiviteInstances:当前活动的实例数量,即还没有完成的实例数量对应串行而言该值始终为1

          • loopCounter :循环计数器,办理人在列表中的索引

          • nrOfCompletedInstances:已经完成的实例数量

          • loop cardinality:循环基数。可选项。可以直接填整数,表示会签的人数。

          • Collection:集合。可选项。会签人数的集合,通常为list,和loop cardinality二选一。

          • Element variable:元素变量。选择Collection时必选,为collection集合每次遍历的元素。

          • Completion condition:完成条件。可选。比如设置一个人完成后会签结束,那么其他人的代办任务都会消失。

          • 或签节点的创建

            springboot集成Camunda审核流程(二):Camunda Modeler设计器设置BPMN流程,在这里插入图片描述,第4张

            ​ 通过 Completion condition中的表达式来确定节点是否通过,从来通过表达式还设置为 会签 / 或签

            4、流程设计器中的监听器

            ​ 流程设计器中的每个节点、节点间的连接线都可以设置监听器,同时监听器中也有多种执行方式。

            4.1 监听器类型

            可以分为三种类型来使用,分别是:

            • 连接线上的 Execution Listeners(take)监听器,如下图所示

              springboot集成Camunda审核流程(二):Camunda Modeler设计器设置BPMN流程,在这里插入图片描述,第5张

            • 任务节点上的Task Listeners 执行监听器:

              springboot集成Camunda审核流程(二):Camunda Modeler设计器设置BPMN流程,在这里插入图片描述,第6张

              任务节点上的任务监听器有分多钟类型,不同类型(Event type)的任务监听器的执行时机是不相同的,所以在实际使用时要根据实际需求来使用。

              • 任务节点上的Execution Listeners 任务监听器:

                springboot集成Camunda审核流程(二):Camunda Modeler设计器设置BPMN流程,在这里插入图片描述,第7张

                任务节点上的 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 cn.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【任务指定负责人触发】、complete:【任务完成后触发】、delete:【任务删除前触发】

                    package cn.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 )包括连接线上的监听器。启动该流程测试各个监听器的执行时机顺序。

                    • 节点上设置的监听器示意图

                      springboot集成Camunda审核流程(二):Camunda Modeler设计器设置BPMN流程,在这里插入图片描述,第8张

                      • UserTask 用户任务节点的连线上设置的Listeners

                        springboot集成Camunda审核流程(二):Camunda Modeler设计器设置BPMN流程,在这里插入图片描述,第9张

                        ​ 在后台的Java代码中我们对应的都要实现这几个监听器。当然这些监听器也可以重复的指向同一个java实现类,后台的监听器类:

                        /**
                         * @Description: 测试任务监听器执行时机
                         * 用于[Listeners触发时机测试流程] 的任务监听器测试
                         * @ClassName: FlowTaskListeners
                         */
                        @Component
                        @RequiredArgsConstructor
                        @Slf4j
                        public class FlowTaskListeners implements TaskListener {
                            /**
                             * 1、在create事件之前不会触发其他与任务相关的事件
                             * 2、当分配人、所有者等属性发生变更时会触发update事件,任务的初始化是不会触发update事件的。
                             * 3、在流程定义中显式定义了具有受让人的任务被创建时,assignment事件会在create事件之后触发。
                             * 更改任务assignee属性,assignment事件会在update事件之后触发。
                             * 4、当任务成功完成时,触发complete事件。
                             * 5、delete事件发生在任务从运行时数据中删除之前。
                             * 6、complete事件和delete事件是互斥的。
                             */
                            @Override
                            public void notify(DelegateTask delegateTask) {
                                String eventName = delegateTask.getEventName();
                                log.info("【TaskListener 执行】=========="+eventName);
                            }
                        }
                        -------------------分割线-----------------
                            
                        /**
                         * @Description: 执行监听器测试流程
                         * 用于[Listeners触发时机测试流程]的执行监听器
                         * @ClassName: FlowExecutionListeners
                         */
                        @Component
                        @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");
                                 * */
                            }
                        }
                        

                        通过启用流程、节点审核等操作之后,可以得到流程中监听器的执行时机顺序。如下所示:

                        
                        

                        5、 SendTask 抄送任务节点设置

                        ​ Send task 是流程中的一种任务节点,我们可以通过该节点设置自定义活动,实现流程异步执行任务流程中所设计到的其他相关操作(eg:发送邮件等操作) 。

                        springboot集成Camunda审核流程(二):Camunda Modeler设计器设置BPMN流程,在这里插入图片描述,第10张

                        ​ 抄送任务的事项方式与监听器的实现方式类型,其中的 type 字段中可选择Send Task 节点的实现方式,这里我们选择的是通过java代码来实现,所以就需要后天有与之对应的SendTask 任务节点的实现类,当流程执行到该位置后,会自动调用后台该实现类。

                        • 后台实现代码
                          package cn.zhidasifang.camundaproject.camundaTaskOtherMethods;
                          import cn.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.Component;
                          /**
                           * @Description: SendTask抄送环节任务 委托实现类
                           * @ClassName: MySendMessageTask
                           *
                           * Camunda的Send Task用于向外部系统或服务发送消息。消息可以是同步或异步的,可以发送到队列、主题或其他类型的消息中间件。
                           * Send Task通常用于将消息发送到外部系统,而无需等待响应或结果。相反,它只是向外部系统发出信号,通知其执行某些操作或启动某个过程。
                           *
                           */
                          @Component
                          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流程中设置的参数

                            springboot集成Camunda审核流程(二):Camunda Modeler设计器设置BPMN流程,在这里插入图片描述,第11张

                            如上图所示,在Listeners监听器中设置了两个参数,其中 name表示参数名称,该名称必须要与后台监听器实现类获取参数时的名称保持一致,否则会获取不到传递的参数数据。

                            • 后代java代码获取监听器中设置的参数方式:
                              package cn.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.Component;
                              import java.util.ArrayList;
                              /**
                               * @Description:节点开始前的监听器,通过input中的参数设置对应的审核人信息!---动态地为任务分配办理人,而不是在流程定义中静态地指定。
                               * @ClassName: StartListennersSetAssignee
                               */
                              @Component
                              @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标签中设置参数。

                                springboot集成Camunda审核流程(二):Camunda Modeler设计器设置BPMN流程,在这里插入图片描述,第12张

                                ​ 如同所示我们在对应节点上设置参数数据,同时我们需要给节点设置一个Listeners监听器(Execution Listeners 、Task Listeners 两种监听器均可),当节点的监听器触发时,在对应的监听器中我们就能获取到设置的Inputs参数!

                                • 后台Java实现代码
                                  @Component
                                  @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数据进行赋值处理!