Acitiviti 流程引擎在Spring Boot中使用教程

一、什么是acitiviti流程引擎

Activiti是一个面向业务人员、开发人员和系统管理员的轻量级工作流和业务流程管理(BPM)平台。它的核心是一个用于Java的超快速且坚如磐石的BPMN2过程引擎。它是开源的,并在Apache许可证下分发。Activiti在任何Java应用程序、服务器、集群或云中运行。它与Spring完美集成,非常轻量级,基于简单的概念。

二、如何使用

1.基本介绍

微信图片_20230524225515.png
目录或文件 说明
diagram-viewer 图表查看器
editor-app 编辑器app
modeler.html 流程图编辑页面
index.html 流程列表页面

swagger文档

  • swagger访问地址: /swagger-ui/index.htm
  • swagger配置:
    // application.properties
    springdoc.swagger-ui.path=/swagger-ui.html
    
微信图片_20230525094759.png

2. 创建模型

java代码:

/**
     * 新建一个空模型
     *
     * @return
     * @throws UnsupportedEncodingException
     */
    @ApiOperation(value = "新建一个空模型")
   @ApiParam(required = false)
    @RequestMapping("/createNewEmptyModel")
    public Map<String,Object> newModel(String name, String key) throws UnsupportedEncodingException {
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //初始化一个空模型
        Model model = repositoryService.newModel();

        //设置一些默认信息,可以用参数接收
        String description = "";
        int revision = 1;

        ObjectNode modelNode = objectMapper.createObjectNode();
        modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
        modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
        modelNode.put(ModelDataJsonConstants.MODEL_REVISION, revision);

        model.setName(name);
        model.setKey(key);
        model.setMetaInfo(modelNode.toString());

        repositoryService.saveModel(model);
        String id = model.getId();

        //完善ModelEditorSource
        ObjectNode editorNode = objectMapper.createObjectNode();
        editorNode.put("id", "canvas");
        editorNode.put("resourceId", "canvas");
        ObjectNode stencilSetNode = objectMapper.createObjectNode();
        stencilSetNode.put("namespace",
                "http://b3mn.org/stencilset/bpmn2.0#");
        editorNode.put("stencilset", stencilSetNode);
        repositoryService.addModelEditorSource(id, editorNode.toString().getBytes("utf-8"));
        return success(model);
    }

接口信息:

类型
url /models/createNewEmptyModel
method GET
swagger url /swagger-ui/index.html#/model-controller/newModel

接口参数:

参数 说明
name 流程名称
key 流程key(需唯一)

流程列表页面

创建完之后可以在流程列表页面看到新创建的流程,访问地址: /static/index.html:

微信图片_20230525094408 - 副本.png

3. 编辑流程

在流程列表页面点击"编辑流程"按钮:

微信图片_20230525094408 - 副本.png

设置流程图

点击右侧空白区域:

微信图片_20230530092642.png

可以看到底部出现流程图设置栏,在这里进行一些流程图 必须的设置:

设置项 说明
流程唯一标识 设置流程图的 process_key , 最好同模型的 key 一致,因为部署流程时会从流程图的文件里读取这个值作为 process_key 而不是从模型的数据库记录里读取
名称 设置流程图的名称

开始事件

关于开始事件:

  • 每一个流程都会有一个唯一的开始节点 开始事件,每一个流程创建后都从这个节点开始。
     

操作方法:

  • 在左侧的菜单里,找到 开始事件 的下拉菜单里的 开始事件 (同一名字)按钮
  • 鼠标左键长按点击按钮拖拽到右侧的空白区域
微信图片_20230530095006.png

需要在底侧的编辑栏对 开始事件 节点做一些 设置 :

设置项 说明
ID 为节点设置一个ID,如 id_start
名称 为节点设置一个名称,如 开始
执行监听器 链接
表单的标识Key 链接
表单属性 链接

任务节点

关于任务节点:

  • 在Activiti流程引擎中,活动(Activity)是指流程中的一个节点,代表了一个任务或者一组类似任务。活动可以是用户任务(User Task),服务任务(Service Task),脚本任务(Script Task),自动任务(Automatic Task)等等。在流程执行过程中,活动表示了一个节点的状态,即正在进行的任务或已经完成的任务。这里主要介绍用户任务(User Task)
  • 当一个流程执行到一个活动时,它会等待相应的事件,如用户完成任务或者收到服务任务响应。当事件发生后,流程就会继续执行下一个节点。Activiti引擎确保这些节点在正确的时间和顺序被执行,从而使流程在正确的时间内达到预期的结果。
  • 总之,活动在Activiti中扮演着至关重要的角色,代表了流程中的一个节点和状态,通过正确的活动设置和执行,流程能够达到预期的效果。
     

操作方法:

  • 在左侧的菜单里,找到 活动 的下拉菜单里的 人工服务 按钮
  • 鼠标左键长按点击按钮拖拽到右侧的空白区域
微信图片_20230530101359.png
设置项 说明
ID 为节点设置一个ID,如 id_node_a
名称 为节点设置一个名称,如 A
任务派遣 分配该节点的审批人或者分组,具体说明 见底下
执行监听器 链接
表单的标识Key 链接
表单属性 链接

关于任务派遣:

  • 任务派遣在Activiti中是一个常见的应用场景。在Activiti中,任务派遣可以通过用户任务的完成和代理人来实现。
     
  • 用户任务是一个需要用户完成的工作流任务,当用户完成该任务后,工作流会自动进入下一个任务或任务节点。在Activiti中,通过设置任务的candidateUsers或candidateGroups属性来指定该任务被哪些用户或用户组拥有权限进行操作。完成用户任务的用户需要具有相应的权限才能操作该任务。
     
  • 代理人则是一个具有代理权限的用户,代理人可以代替其他用户进行任务派遣或任务处理。在Activiti中,通过设置任务的assignee属性来指定该任务的代理人。代理人可以进行任务的处理、完成和删除等操作。
     

设置任务派遣:

  • 在编辑区域底部设置栏里,找到 任务派遣 并单击
  • 在弹出的弹框里设置Assignee、Candidate users、Candidate groups
微信图片_20230530142105.png

方向箭头

关于方向箭头:

  • 在Activiti中,方向箭头指示流程从一个任务或节点到另一个的流程。箭头将源任务或节点连接到目标任务或节点,表示流程从源流向目标。
  • Activiti中有不同类型的方向箭头,每种箭头都有特定的含义。例如,带箭头的实线表示规则序列流,而带菱形箭头的虚线表示条件序列流。这里主要介绍带箭头的实线
  • 在使用该平台设计和创建工作流时,理解Activiti中不同方向箭头的含义非常重要。
     

操作方式:

  • 鼠标左键点击不同的节点,会在节点周围出现一些按钮
  • 长按鼠标左键点击点击 实心的箭头图标
  • 拖拽要连接的下一个节点,然后松开鼠标左键:
微信图片_20230530102650.png
微信图片_20230530102713.png

一个节点连接多个节点:

微信图片_20230530104619.png

实线箭头设置弯曲点:

  • 首先点击左上角倒数第二个蓝色按钮
  • 然后再点击要调整的箭头
  • 最后拖拽箭头的弯曲处调整箭头
微信图片_20230530104820.png

设置路径选择条件表达式

  • 点击箭头
  • 在底部设置找到 跳转条件 栏, 填写为统一表达式语言 (UEL)

 
关于统一表达式语言 (UEL)
在 Activiti BPMN 引擎中,可以使用一种称为统一表达式语言 (UEL) 的表达式语言来定义条件和表达式。 Activiti 中条件的语法基于 Java EL 语法。下面是一些 Activiti 条件表达式的例子:
 

  • ${task.priority > 50} (判断task.priority变量是否大于 50)
  • ${task.assignee == "John Doe"} (判断task.assignee是否等于"John Doe")
  • ${approvedByManager and (approvedByLegal or approvedByFinance)} (approvedByManager、approvedByLegal、approvedByFinance 都是布尔变量, 这里的是意思是 是否由 管理员审批(approvedByManager) 由 法务部门审批(approvedByLegal ) 或者 财务部门审批(approvedByFinance)至少其中的一个审批角色审批
     

在 统一表达式语言 (UEL) 语法中,表达式必须被包裹在 ${} 中, 可以使用 >、<、>=、<=、==、!= 等各种运算符和 and、or 和 not 等逻辑运算符来创建复杂的条件。此外,可以在表达式中使用 变量函数创建动态条件
 
在acitiviti 6.0.0版本中变量名采用带下划线小写的格式( 如 name_of_variable )无法正常识别变量,可能是基于Java EL 语法缘故因此需要遵循Java的变量命名规范即驼峰的命名方法(如 nameOfVariable )。

设置默认路线

  • 点击箭头
  • 在底部设置找到 默认路线 栏, 勾选

网关

在Activiti工作流中,单一网关和并行网关都是控制流程流转的关键点:
 

  • 单一网关(Exclusive Gateway):它允许流程按照条件只选择一条流程线路进行流转。一般地,在网关的输出路线上会关联一个条件表达式。当流程执行到这个网关的时候,就会根据条件表达式的结果,自动选择到对应的输出路线流转。
     
  • 并行网关(Parallel Gateway):它允许流程按照一组条件同时进行多条流程线路同时流转。当并行网关的输入路线的开始节点执行完成后,可以同时同时进入并行网关的所有输出路线所关联的任务节点,从而支持分支、合并、汇聚等复杂的流程模式。
     

在Activiti中使用这些网关非常简单。当您在设计流程时,选择一个网关类型并将其拖放到流程图中。然后,您可以通过连接线将任务节点与网关的输入和输出路线连接起来。在在输出路线上输入conditions表达式,以完成流程的逻辑分支或并行需求。详情可参考Activiti或BPMN文档。
 
操作方法

  • 在左侧的菜单里,找到 网关 的下拉菜单里的 单一网关 按钮
  • 鼠标左键长按点击按钮拖拽到右侧的空白区域
  • 然后用箭头连接起来

如下图:

微信图片_20230530112024.png

注意: 需要给网关的不同箭头设置跳转条件 ,且执行中不能 出现 同时满足两个路线 的情况,这是 单一网关并行网关 中在使用中的区别 ,关于并行网关就不再示例说明

结束事件

关于结束事件:

  • 每一个流程都会有一个唯一的结束节点 结束事件,当流程到达这一节点时整个流程就结束了。

 
操作方法:

  • 在左侧的菜单里,找到 结束事件 的下拉菜单里的 结束事件 按钮
  • 鼠标左键长按点击按钮拖拽到流程的结束位置
  • 然后用箭头连接起来

保存

流程图编辑完成之后要保存修改:

  • 点击左上角第一个蓝色按钮
  • 在弹出的弹框里输入流程名称和描述
  • 点击右下角 Save and close editor 或者 Save 按钮保存
微信图片_20230530150156.png

4. 部署流程

关于部署流程:

  • 流程图编辑保存之后,要使其生效还需要部署到流程引擎中
  • 不同的部署版本根据业务需求可能会有不同的key区分,比如对于key为 example_process_key的流程, 可以用example_process_key_deployed_v_01 , example_process_key_deployed_v_02来区分同一个流程的不同迭代版本,方便在业务代码里切换到不同版本的流程
     

操作方法:

  • 访问 /static/index.html 列表页面
  • 点击要部署的流程的那一行里的 部署流程 按钮
微信图片_20230525094408.png

5. 新建任务

部署完流程之后,需要新建一个任务实例测试流程

接口信息:

类型
url /demo/newTask
method GET
swagger url /swagger-ui/index.html#/demo-controller/newTask

接口参数:

参数 说明
key 流程图里设置的唯一标识
businessId 业务id,用来保存关联业务表的主键

后端代码:

    @ApiOperation(value = "新任务")
    @GetMapping("/newTask")
    public Object newTask(String key,@RequestParam(defaultValue = "") String businessId)
    {
        ProcessInstanceBuilder processInstanceBuilder = runtimeService.createProcessInstanceBuilder();
        // 设置业务key
        processInstanceBuilder.businessKey(businessId);
        // 设置流程定义key
        processInstanceBuilder.processDefinitionKey(key);

        // 保存一些自定义的变量
        Map<String,Object> variables =new HashMap<>();
        variables.put("a","a1");
        variables.put("b","b1");
        processInstanceBuilder.variables(variables);
        processInstanceBuilder.transientVariables(variables);
        // 启动任务
        processInstanceBuilder.start();

        return success();
    }

6. 查询任务

创建完任务后,查询待办任务

类型
url /demo/todo
method GET
swagger url /swagger-ui/index.html#/demo-controller/todo

接口参数:

参数 说明
processKey 流程图里设置的唯一标识
userId 用户id,过滤 Candidate UserAssignee
role 角色,过滤 Candidate Group
pageIndex 分页索引,从1开始
pageSize 分页大小

当 查询条件过滤 Candidate User 或者 Candidate Group 时,生成的sql语句会带有 'assignee is null', 因此如果要以 Candidate User 或者 Candidate Group 为条件过滤时要确保 Assignee 不管是流程设置还是代码里不要设置值,这个是在这个demo所使用的Activiti 6.0.0版本出现的问题,其他版本是否存在同样问题不确定。

接口返回示例:

{
    "records": [
        {
            "taskId": "152507",
            "taskDefinitionKey": "id_a",
            "taskName": "A",
            "businessId": null,
            "processDefinitionId": "test-process:2:147514",
            "taskLocalVariables": {},
            "processVariables": {},
            "processInstanceId": "152501",
            "createTime": "2023-05-30 11:29:27",
            "businessData": {
                "id": "32382ced059d498491185137f728ef43",
                "status": null,
                "title": "faked"
            }
        }
    ],
    "total": 1
}

后端代码:

    @ApiOperation(value = "待办分页")
    @GetMapping("/todo")
    public PageModel todo(TodoReqModel todoReqModel) { 
        TaskService taskService = processEngine.getTaskService();
        TaskQuery taskQuery = taskService.createTaskQuery();

        taskQuery.active(); 
        
        // 查询流程类型(通过processKey)
        if(!StringUtils.isEmpty(todoReqModel.getProcessKey()))
        {
            taskQuery.processDefinitionKey(todoReqModel.getProcessKey());
        }
  
        // 查询分组
        if(!StringUtils.isEmpty(todoReqModel.getRole()))
        {
            taskQuery.taskCandidateGroup(todoReqModel.getRole());
        }

        // 查询用户
        if(!StringUtils.isEmpty(todoReqModel.getUserId()))
        {
            taskQuery.taskCandidateOrAssigned(todoReqModel.getUserId());
        }
        // 是否包含流程变量
        // taskQuery.includeProcessVariables();

        // 按创建时间倒序排序
        taskQuery.orderByTaskCreateTime().desc();
 
        Integer startAt = (todoReqModel.getPageIndex() - 1) * todoReqModel.getPageSize();
        Integer endAt = todoReqModel.getPageIndex() * todoReqModel.getPageSize();
        Long count = taskQuery.count();
        List<Task> tasks = taskQuery.listPage(startAt, endAt);

        List<TaskAndBusiness<DemoBusinessEntity>> records =new LinkedList<>();
        tasks.forEach(task->{
            TaskAndBusiness<DemoBusinessEntity> record =(TaskAndBusiness<DemoBusinessEntity>)TaskAndBusiness.makeFromTask(task);
            record.setBusinessData(getDemoBusinessById(record.getBusinessId()));
            records.add(record);
        });

        PageModel pageModel =new PageModel();
        pageModel.setRecords(records);
        pageModel.setTotal(count);
 
        return pageModel;
    }

7. 审批

接口信息:

类型
url /demo/complete
method POST
swagger url /swagger-ui/index.html#/demo-controller/complete

接口参数:

参数 说明
taskId 任务id
params 对象类型,用来提交自定义变量如: { key:val } , 在activiti 6.0.0 版本使用中存在用如 next_node 的命名规范的变量抛出异常,但是 驼峰 的写法( nextNode ) 可以正常传递变量

请求参数示例:

{
    "taskId":"1",
    "params":{
        "alpha":"value of alpha",
        "beta":"value of beta",
        "nextNodeOfA":"B2", 
        "approved":false
    }
}

后端代码:

    @ApiOperation(value = "完成")
    @PostMapping("/complete")
    public Object complete(@RequestBody TaskCompleteModel taskCompleteModel) {

        TaskService taskService = processEngine.getTaskService();
        // 取出变量
        Map<String, VariableInstance> variableInstances = taskService.getVariableInstances(taskCompleteModel.getTaskId());

        // 取出变量
        Map<String, Object> variables1 = taskService.getVariables(taskCompleteModel.getTaskId());
        if(variables1!=null&& taskCompleteModel.getParams()!=null)
        {
            variables1.putAll(taskCompleteModel.getParams());
        }

        doSomethingBeforeTaskComplete(taskCompleteModel.getTaskId(),variables1);
        taskService.complete(taskCompleteModel.getTaskId(),taskCompleteModel.getParams());
        doSomethingAfterTaskComplete(taskCompleteModel.getTaskId(),variables1);
        return success();
    }

    public void doSomethingBeforeTaskComplete(String taskId,Map<String, Object> variables)
    {
        log.info("doSomethingBeforeTaskComplete in supper");
    }
    public   void doSomethingAfterTaskComplete(String taskId,Map<String, Object> variables)
    {
        log.info("doSomethingAfterTaskComplete in supper");
    }

8. 事件处理

全局任务监听

  • 可以创建一个 ActivitiListenerRegister 的服务类, 在应用启动时注册Bean时会实例化该类,因此可以在类的构造类或者 @PostConstruct 注解的方法里注册一个 ActivitiEventListener 事件监听器,Activiti会在事件发生时把事件分发到每个注册过的 ActivitiEventListener

  • ActivitiEventType 是一个枚举类,定义了不同的事件类型,这里只处理了 PROCESS_COMPLETED(流程完成) 的事件类型,可以在这时处理流程完成后的业务逻辑,比如修改业务表的状态等


@Slf4j
@Service
public class ActivitiListenerRegister {

    private ProcessEngine processEngine;
    private RuntimeService runtimeService;
    private IProcessLeaveEventCompletelyHandler processLeaveCompletelyHandler;

    public ActivitiListenerRegister(ProcessEngine processEngine, RuntimeService runtimeService, ProcessLeaveCompletelyHandler processLeaveCompletelyHandler) {
        this.processEngine = processEngine;
        this.runtimeService = runtimeService;
        this.processLeaveCompletelyHandler = processLeaveCompletelyHandler;
    }

    @PostConstruct
    public void init() {
        
        runtimeService.addEventListener(new ActivitiEventListener() {
            @SneakyThrows
            @Override
            public void onEvent(ActivitiEvent event) {

                ActivitiEventType type = event.getType();

                if (event.getType().equals(ActivitiEventType.TASK_COMPLETED)) {
                    log.info(" event type: " + ActivitiEventType.TASK_COMPLETED);
                }

                // 判断流程状态是否为已完成
                if (event.getType() == ActivitiEventType.PROCESS_COMPLETED) {
                    String processInstanceId = event.getProcessInstanceId();

                    HistoricProcessInstance historicProcessInstance = getHistoricProcessInstanceByProcessInstanceId(processInstanceId);

                    if (historicProcessInstance == null) {
                        throw new Exception(" processInstance not exists ");
                    }
                    
                    // 获取到业务key
                    String businessKey = historicProcessInstance.getBusinessKey();
                    // 获取流程定义key(区分哪种流程)
                    String processDefinitionKey = historicProcessInstance.getProcessDefinitionKey();
                    switch (processDefinitionKey) {
                        // 处理离职流程
                        case ProcessDefinitionKeys.PROCESS_LEAVE:
                            processLeaveCompletelyHandler.handle(event, businessKey, processDefinitionKey);
                            break;
                        default:
                            break;
                    }

                    // Execute your code here
                    System.out.println("Process completed");
                }
            }

            @Override
            public boolean isFailOnException() {
                return false;
            }
        });
    }

    /**
     * 根据executionId获取单条Execution数据
     * @param executionId
     * @return
     */
    protected Execution getExecutionById(String executionId) {
        ExecutionQuery executionQuery = runtimeService.createExecutionQuery();
        executionQuery.executionId(executionId);
        Execution execution = executionQuery.singleResult();
        return execution;
    }


    /**
     * 根据processInstanceId获取单条HistoricProcessInstance
     * @param processInstanceId
     * @return HistoricProcessInstance
     */
    protected HistoricProcessInstance getHistoricProcessInstanceByProcessInstanceId(String processInstanceId) {
        HistoricProcessInstanceQuery historicProcessInstanceQuery = processEngine.getHistoryService().createHistoricProcessInstanceQuery();
        historicProcessInstanceQuery.processInstanceId(processInstanceId);
        HistoricProcessInstance historicProcessInstance = historicProcessInstanceQuery.singleResult();
        return historicProcessInstance;
    }
}


任务监听器

后端代码:

@Data
@Slf4j
public class ActivitiTaskEventListener implements  TaskListener  {

    private Expression test;
  
    /**
     * 处理 任务监听器 通知的任务
     * @param delegateTask
     */
    @SneakyThrows
    @Override
    public void notify(DelegateTask delegateTask) {
        // 获取自定义字段接收的值(如果有配置传值的话)
        Object value = test.getValue(delegateTask);
        String strValue = value == null ? null : value.toString();
        // 获取事件名称
        String eventName = delegateTask.getEventName();
        switch (eventName) {
            case BaseTaskListener.EVENTNAME_CREATE:
                break;
            case BaseTaskListener.EVENTNAME_ASSIGNMENT:
                break;
            case BaseTaskListener.EVENTNAME_COMPLETE:
                break;
            case BaseTaskListener.EVENTNAME_DELETE:
                break;
            default:
                throw new Exception("未知的事件名称");
        }

        log.info("on notify with DelegateTask in ActivitiTaskCompleteEventListener");
    }
}

关于任务监听器TaskListener :

  • 在Activiti流程引擎里, 任务监听器 TaskListener 是用来处理不同类型的任务: create、complete、assignment、delete事件,分别代表创建、完成、分发、删除事件
     

配置任务监听器 TaskListener:

  1. 需要在后端创建一个自定义类实现org.activiti.engine.delegate.TaskListener 接口, 重写 notify(DelegateTask delegateTask)方法以处理事件
  2. 可以在自定义类里定义一个或者多个 org.activiti.engine.delegate.Expression 类型的自定义名称的字段,然后在流程编辑时设置对应的字段名称传字符串的值
  3. 点击某个需要配置任务监听器的节点
  4. 在底部找到 `任务监听器 或者 Task Listener 的栏,点击右侧弹出弹框
  5. 点击左边从上往下第一排蓝色按钮里的 + 按钮,右上角出现配置任务监听器的表单
  6. 参考下列表格配置任务监听器表单
字段 描述
Event 用来选择事件的类型,有create、complete、assignment、delete分别代表创建、完成、分发、删除事件,选择其中一种事件类型
Class 用来填写后端实现了 TaskListener 接口的自定义事件监听器,必须填写类的完整路径, 如 com.java.listener.ActivitiTaskEventListener
Expression 代替使用在监听器声明的 Class 字段的值, expression 被定义用来当事件触发的时候 估算值或调用方法(evaluated/invoked),如:
<activiti:taskListener >expression="${myPojo.myMethod(execution.eventName)}" event="end" />
<activiti:taskListener expression="${a == b}" event="end" />
Delegate Expression delegateExpression 允许指定一个表达式它解析一个实现了TaskListener 接口的bean对象,如:
<activiti:taskListener event="start" >delegateExpression="${myTaskListenerBean}" />

配置自定义字段:

  • 点击左上角要配置自定义字段的事件配置项
  • 点击左边从上往下第二排蓝色按钮里的 + 按钮,右下角出现配置自定义字段的表单
  • 参考下列表格配置表单
字段 描述
Name 用来填写自定义的字段名称, 该字段名称必须和对应的TaskListener里的字段名称匹配, 且类型为org.activiti.engine.delegate.Expression
String value 用来填写传递到该字段的字符串类型的值
Expression 使用 expression 如: ${myVar}
String 在 Activiti 5.12 版本, 使用新的execution监听器类型org.activiti.engine.impl.bpmn.listener.ScriptExecutionListener 允许你为execution 监听事件执行一小块的逻辑脚本

org.activiti.engine.impl.bpmn.listener.ScriptExecutionListener

<activiti:executionListener event="start" class="org.activiti.engine.impl.bpmn.listener.ScriptExecutionListener" >
 <activiti:field name="script">
   <activiti:string>
     def bar = "BAR";  // local variable
     foo = "FOO"; // pushes variable to execution context
     execution.setVariable("var1", "test"); // test access to execution instance
     bar // implicit return value
   </activiti:string>
 </activiti:field>
 <activiti:field name="language" stringValue="groovy" />
 <activiti:field name="resultVariable" stringValue="myVar" />
</activiti:executionListener>

图片:

微信图片_20230531133147.png

执行监听器

后端代码:

@Data
@Slf4j
public class ActivitiExecutionEventListener implements  ExecutionListener {

    private Expression test;

    /**
     * 处理 执行监听器 通知的任务
     * @param delegateExecution
     */
    @SneakyThrows
    @Override
    public void notify(DelegateExecution delegateExecution) {

        Object value = test.getValue(delegateExecution);
        String strValue = value == null ? null : value.toString();
        String eventName = delegateExecution.getEventName();
        switch (eventName) {
            case BaseExecutionListener.EVENTNAME_START:
                break;
            case BaseExecutionListener.EVENTNAME_TAKE:
                break;
            case BaseExecutionListener.EVENTNAME_END:
                break;
            default:
                throw new Exception("未知的事件名称");
        }

        log.info("on notify with DelegateExecution in ActivitiTaskCompleteEventListener");
    } 
}

关于执行监听器 ExecutionListener:

  • 在Activiti流程引擎里, 任务监听器 ExecutionListener是用来处理任务start、end、take,即开始、结束、获取事件的
     

配置执行监听器 ExecutionListener:

  1. 需要在后端创建一个自定义类实现org.activiti.engine.delegate.ExecutionListener接口, 重写 notify(DelegateExecution delegateExecution)方法
  2. 可以在自定义类里定义一个或者多个 org.activiti.engine.delegate.Expression 类型的自定义名称的字段,然后在流程编辑时设置对应的字段名称传字符串的值。
  3. 点击某个需要配置任务监听器的节点
  4. 在底部找到 执行监听器 或者 Execution Listener 的栏,点击右侧弹出弹框
    org.activiti.engine.delegate.Expression 类型的自定义名称的字段,然后在流程编辑时设置对应的字段名称传字符串的值。
  5. 点击左边从上往下第一排蓝色按钮里的 + 按钮,右上角出现配置任务监听器的表单
  6. 参考下方表格配置表单
字段 描述
Event 用来选择事件的类型,有create、end、take分别代表创建、结束、获取事件,选择其中一种事件类型
Class 用来填写后端实现了 ExecutionListener 接口的自定义事件监听器,必须填写类的完整路径, 如 com.java.listener.ActivitiExecutionEventListener
Expression 代替使用在监听器声明的 Class的值, expression 被定义用来当事件触发的时候 估算值或调用方法(evaluated/invoked),如:
<activiti:executionListener >expression="${myPojo.myMethod(execution.eventName)}" event="end" />
<activiti:executionListener expression="${a == b}" event="end" />
Delegate Expression delegateExpression 允许指定一个表达式它解析一个实现了ExecutionListener 接口的bean对象,如:
<activiti:executionListener event="start" >delegateExpression="${myExecutionListenerBean}" />

配置自定义字段:

  • 点击左上角要配置自定义字段的事件配置项
  • 点击左边从上往下第二排蓝色按钮里的 + 按钮,右下角出现配置自定义字段的表单
  • 参考下列表格配置表单
字段 描述
Name 用来填写自定义的字段名称, 该字段名称必须和对应的TaskListener里的字段名称匹配, 且类型为org.activiti.engine.delegate.Expression
String value 用来填写传递到该字段的字符串类型的值
Expression 使用 expression 如: ${myVar}
String 在 Activiti 5.12 版本, 使用新的execution监听器类型org.activiti.engine.impl.bpmn.listener.ScriptExecutionListener 允许你为execution 监听事件执行一小块的逻辑脚本
微信图片_20230531163434.png

git仓库地址: https://gitee.com/lai-xuxiang/activiti-demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容