博客 / 詳情

返回

Activiti7事件監聽

本文個人博客地址:Activiti7事件監聽 (leafage.top)

好久沒有記錄筆記了,最近做了一些關於工作流的事情,記錄一下使用activiti 7的一些經驗。

需求:

  1. 在流程發起和流程操作的過程中,給相關人員發送流程審批的通知提醒;
  2. 不要在配置流程時手動添加,不能侵入到流程操作的過程,影響流程執行;

<mark>這個怎麼入手呢?沒搞過activiti,activiti7 的官方文檔寫的跟屎一樣爛,感覺好難呀</mark>😔...

文檔參考性不高,那就試試看官方的示例,找到 activiti 的 repository ,有一個示例 module 叫 activiti-examples,<mark>這裏的示例不能直接跑,只能看</mark>,要想跑起來,就複製,粘貼,放到自己的項目中。 跑題了,説會主題。。。

activiti中的幾個關聯的重要的類或接口:

  1. activiti 中每個流程信息是通過 ProcessInstance 描述,它有這麼幾個狀態:created、started、completed、cancelled、resumed、updated、suspended,與之對應的相關事件描述類是:ProcessCreatedEvent、ProcessStartedEvent、ProcessCompletedEvent、ProcessCancelledEvent、ProcessResumedEvent、ProcessUpdatedEvent、ProcessSuspendedEvent等。

    image.png

  2. 每個流程節點在 activiti 中 通過 Task 來描述,它有這麼幾個個狀態:created、assigned、completed、updated、cancelled、suspended等,與之對應的相關事件描述類是:TaskCreatedEvent、TaskAssignedEvent、TaskCompletedEvent、TaskUpdatedEvent、TaskCancelledEvent、TaskSuspendedEvent等。

    image.png

如何配置監聽器?

1. 全局事件監聽器:

涉及到兩個類\接口,全局事件監聽器 ActivitiEventListener 和 ProcessEngineConfigurationConfigurer(有一個默認的實現類:DefaultActivityBehaviorFactoryMappingConfigurer)

image.png

ActitiviEventListener 接口有一個 void onEvent(ActivitiEvent activitiEvent) 方法,即在事件狀態發生變化時,可以發生的動作都會在這個方法中進行。其源碼如下:

/**
 * Describes a class that listens for {@link ActivitiEvent}s dispatched by the engine.
 * 

 */
public interface ActivitiEventListener {

  void onEvent(ActivitiEvent event);

  boolean isFailOnException();
}

ActivitiEvent 包含了流程的定義ID,示例ID,執行ID,和事件類型信息,源碼如下:

public interface ActivitiEvent {

  ActivitiEventType getType();

  String getExecutionId();

  String getProcessInstanceId();

  String getProcessDefinitionId();
}

其事件類型包括很多,源碼如下:

public enum ActivitiEventType {

  // ENTITY :流程實例,發起流程時,從流程模板中創建實例
  ENTITY_CREATED,  // 創建

  ENTITY_INITIALIZED,  // 初始化完成(如果這個實體的創建會包含子實體的創建,這個事件會在子實體都創建/初始化完成後被觸發,這是與ENTITY_CREATED的區別)

  ENTITY_UPDATED,  // 更新

  ENTITY_DELETED,  // 刪除

  ENTITY_SUSPENDED,  // 暫停(會被ProcessDefinitions, ProcessInstances 和 Tasks拋出)

  ENTITY_ACTIVATED,  // 激活(會被ProcessDefinitions, ProcessInstances 和 Tasks拋出)

  // 定時器
  TIMER_SCHEDULED,  // 創建

  TIMER_FIRED,  // 觸發

  // 作業
  JOB_CANCELED,  // 取消

  JOB_EXECUTION_SUCCESS,  // 執行成功

  JOB_EXECUTION_FAILURE,  // 執行失敗

  JOB_RETRIES_DECREMENTED,  // 重試減少(因為作業執行失敗,導致重試次數減少)

  CUSTOM,  // 自定義
  
  // 引擎
  ENGINE_CREATED,  // 創建

  ENGINE_CLOSED,  // 關閉

  // 流程節點
  ACTIVITY_STARTED,  // 開始

  ACTIVITY_COMPLETED,  // 完成

  ACTIVITY_CANCELLED,  // 取消

  ACTIVITY_SIGNALED,  // 收到了一個信號

  ACTIVITY_COMPENSATE,  // 將要被補償
  
  ACTIVITY_MESSAGE_SENT,  // 消息發送
 
  ACTIVITY_MESSAGE_WAITING,  // 消息等待

  ACTIVITY_MESSAGE_RECEIVED,  // 消息接收

  ACTIVITY_ERROR_RECEIVED,  // 接收失敗
  
  // 流程歷史
  HISTORIC_ACTIVITY_INSTANCE_CREATED,  // 創建
  
  HISTORIC_ACTIVITY_INSTANCE_ENDED,  // 結束

  // 隊列流程
  SEQUENCEFLOW_TAKEN,  // 已採取

  UNCAUGHT_BPMN_ERROR,  // 未獲取到bpmn 異常

  // 變量
  VARIABLE_CREATED,  // 創建

  VARIABLE_UPDATED,  // 更新

  VARIABLE_DELETED,  // 刪除

  // 任務
  TASK_CREATED,  // 創建(它位於ENTITY_CREATE事件之後。當任務是由流程創建時,這個事件會在TaskListener執行之前被執行)

  TASK_ASSIGNED,  // 分配

  TASK_COMPLETED,  // 完成(它會在ENTITY_DELETE事件之前觸發。當任務是流程一部分時,事件會在流程繼續運行之前,   後續事件將是ACTIVITY_COMPLETE,對應着完成任務的節點)

  // 進程
  PROCESS_STARTED,  // 開始

  PROCESS_COMPLETED,  // 完成(在最後一個節點的ACTIVITY_COMPLETED事件之後觸發。 當流程到達的狀態,沒有任何後續連線時, 流程就會結束。)
 
  PROCESS_COMPLETED_WITH_ERROR_END_EVENT,  // 異常結束

  PROCESS_CANCELLED,  // 取消

  HISTORIC_PROCESS_INSTANCE_CREATED,  // 流程實例創建

  HISTORIC_PROCESS_INSTANCE_ENDED,  // 流程實例創建

  // 成員
  MEMBERSHIP_CREATED,  // 用户被添加到一個組裏

  MEMBERSHIP_DELETED,  // 用户被從一個組中刪除

  MEMBERSHIPS_DELETED;  // 所有成員被從一個組中刪除
  
  // other code ...
}

ProcessEngineConfigurationConfigurer 中的 void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) 方法可以添加自定義的事件監聽器,這個監聽器作用域為整個流程過程。其源碼如下:

public class DefaultActivityBehaviorFactoryMappingConfigurer implements ProcessEngineConfigurationConfigurer {

    private VariablesMappingProvider variablesMappingProvider;

    private ProcessVariablesInitiator processVariablesInitiator;
    
    private final EventSubscriptionPayloadMappingProvider eventSubscriptionPayloadMappingProvider;

    public DefaultActivityBehaviorFactoryMappingConfigurer(VariablesMappingProvider variablesMappingProvider,
                                                           ProcessVariablesInitiator processVariablesInitiator,
                                                           EventSubscriptionPayloadMappingProvider eventSubscriptionPayloadMappingProvider){
        this.variablesMappingProvider = variablesMappingProvider;
        this.processVariablesInitiator = processVariablesInitiator;
        this.eventSubscriptionPayloadMappingProvider = eventSubscriptionPayloadMappingProvider;
    }
    @Override
    public void configure(SpringProcessEngineConfiguration processEngineConfiguration){
        processEngineConfiguration.setEventSubscriptionPayloadMappingProvider(eventSubscriptionPayloadMappingProvider);

        processEngineConfiguration.setActivityBehaviorFactory(new MappingAwareActivityBehaviorFactory(variablesMappingProvider,
                                                                                                      processVariablesInitiator));
    }
}

如何來監聽事件?

  1. 實現 ActivitiEventListener 接口,重寫 void onEvent(ActivitiEvent event) 方法;
  2. 新增配置類,繼承 DefaultActivityBehaviorFactoryMappingConfigurer 類(或實現 ProcessEngineConfigurationConfigurer 接口)並重寫 configura() 方法, 給 SpringProcessEngineConfiguration 屬性添加自定義的監聽器實現類;

示例如下:

@Configuration
public class ActivitiConfiguration extends DefaultActivityBehaviorFactoryMappingConfigurer {

    @Autowired
    ActivitiTaskLogService activitiTaskLogService;

    @Autowired
    UserService userService;

    public ActivitiConfiguration(VariablesMappingProvider variablesMappingProvider, ProcessVariablesInitiator processVariablesInitiator,
                                 EventSubscriptionPayloadMappingProvider eventSubscriptionPayloadMappingProvider) {
        super(variablesMappingProvider, processVariablesInitiator, eventSubscriptionPayloadMappingProvider);
    }

    @Override
    public void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) {
        super.configure(springProcessEngineConfiguration);

        springProcessEngineConfiguration.setEventListeners(
                Collections.singletonList(new ActivitiTaskEventListener(activitiTaskLogService, userService)));
    }
}
public class ActivitiTaskEventListener implements ActivitiEventListener {

    @Override
    public void onEvent(ActivitiEvent activitiEvent) {
        if (!StringUtils.hasText(activitiEvent.getProcessInstanceId())) {
            return;
        }
        processEngine = ProcessEngines.getDefaultProcessEngine();

        // 流程實例
        ProcessInstance processInstance = processEngine.getRuntimeService().createProcessInstanceQuery()
                .processInstanceId(activitiEvent.getProcessInstanceId()).singleResult();
        if (processInstance == null) {
            return;
        }

        List<ActivitiTaskLog> taskLogList = new ArrayList<>();
        switch (activitiEvent.getType()) {
            // 任務執行後
            case TASK_COMPLETED:
                // 任務被處理,給創建人, 下一個任務的候選人
                List<ActivitiTaskLog> taskCompletedLogs = this.taskCompleted(processInstance, activitiEvent.getExecutionId());
                if (!CollectionUtils.isEmpty(taskCompletedLogs)) {
                    taskLogList.addAll(taskCompletedLogs);
                }
                break;
            case PROCESS_COMPLETED:
                // 流程完成,給創建人,協助人,抄送人發通知
                List<ActivitiTaskLog> processCompletedLogs = this.processCompleted(processInstance);
                if (!CollectionUtils.isEmpty(processCompletedLogs)) {
                    taskLogList.addAll(processCompletedLogs);
                }
                break;
            default:
        }

        //執行日誌操作
        if (!CollectionUtils.isEmpty(taskLogList)) {
            activitiTaskLogService.createBatch(taskLogList);
        }
    }
}

2. 運行時狀態監聽器:

在實例中有一個 activiti-api-basic-process-example 和 activiti-api-basic-task-example 兩個示例工程,展示瞭如何進行配置運行時的process和task的監聽。

activiti-api-basic-process-example 中,流程監聽示例:

@Bean
public ProcessRuntimeEventListener<ProcessCompletedEvent> processCompletedListener() {
    return processCompleted -> logger.info(">>> Process Completed: '"
            + processCompleted.getEntity().getName() +
            "' We can send a notification to the initiator: " + processCompleted.getEntity().getInitiator());
}

activiti-api-basic-task-example 中,任務監聽示例:

@Bean
public TaskRuntimeEventListener<TaskAssignedEvent> taskAssignedListener() {
    return taskAssigned -> logger.info(">>> Task Assigned: '"
            + taskAssigned.getEntity().getName() +
            "' We can send a notification to the assginee: " + taskAssigned.getEntity().getAssignee());
}

@Bean
public TaskRuntimeEventListener<TaskCompletedEvent> taskCompletedListener() {
    return taskCompleted -> logger.info(">>> Task Completed: '"
            + taskCompleted.getEntity().getName() +
            "' We can send a notification to the owner: " + taskCompleted.getEntity().getOwner());
}

參照示例,我們可以進行自定義的流程中事件監聽的配置,這種方式,不需要實現 ActivitiEventlistener 接口,也不需要繼承 DefaultActivityBehaviorFactoryMappingConfigurer 類或實現 ProcessEngineConfigurationConfigurer 接口,只需要註冊相關事件的監聽器即可。示例如下:

@Configuration
public class ActivitiConfiguration {

    private final RuntimeService runtimeService;

    private final ActRuTaskLogService actRuTaskLogService;

    public ActivitiConfiguration(RuntimeService runtimeService, ActRuTaskLogService actRuTaskLogService) {
        this.runtimeService = runtimeService;
        this.actRuTaskLogService = actRuTaskLogService;
    }

    @Bean
    public TaskRuntimeEventListener<TaskAssignedEvent> taskAssignedListener() {
        return taskAssigned -> {

            ExecutionEntity execution = (ExecutionEntity) runtimeService.createProcessInstanceQuery()
                    .processInstanceId(taskAssigned.getProcessInstanceId()).singleResult();
            String startUserId = execution.getStartUserId();
            String fileProcInstId = this.fileProcInstId(execution);

            // 排除發起申請的任務,給 assignee 發消息
            if (!taskAssigned.getEntity().getAssignee().equals(startUserId)) {
                Task task = taskAssigned.getEntity();
                ActRuTaskLog taskLog = new ActRuTaskLog(task.getProcessInstanceId(), task.getId(),
                        taskAssigned.getEntity().getAssignee(), String.format(NotifyConstants.PENDING_WARN,
                        this.userName(startUserId), this.processType(fileProcInstId), this.projName(execution)),
                        NotifyTypeConstants.CANDIDATE);
                actRuTaskLogService.create(taskLog);
            }
        };
    }

    @Bean
    public TaskRuntimeEventListener<TaskCompletedEvent> taskCompletedListener() {
        return taskCompleted -> {

            ExecutionEntity execution = (ExecutionEntity) runtimeService.createProcessInstanceQuery()
                    .processInstanceId(taskCompleted.getProcessInstanceId()).singleResult();
            String startUserId = execution.getStartUserId();
            String fileProcInstId = this.fileProcInstId(execution);
            Task task = taskCompleted.getEntity();
            // 發起審批,給抄送人、協助人發消息
            if (!taskCompleted.getEntity().getAssignee().equals(startUserId)) {
                // 任務所有人
                String owner = taskCompleted.getEntity().getOwner();
                ActRuTaskLog taskLog = new ActRuTaskLog(task.getProcessInstanceId(), task.getId(),
                        this.userName(owner), String.format(NotifyConstants.PENDING_WARN,
                        this.userName(startUserId), this.processType(fileProcInstId), this.projName(execution)),
                        NotifyTypeConstants.CANDIDATE);
                actRuTaskLogService.create(taskLog);
            } else {
                // 給發起人發送任務處理結果的通知
                ActRuTaskLog taskLog = new ActRuTaskLog(task.getProcessInstanceId(), task.getId(),
                        taskCompleted.getEntity().getAssignee(), String.format(NotifyConstants.PENDING,
                        this.userName(startUserId), this.processType(fileProcInstId), this.projName(execution),
                        this.userName(task.getAssignee()), ""), NotifyTypeConstants.PENDING);
                actRuTaskLogService.create(taskLog);
            }
        };
    }

    @Bean
    public TaskCandidateEventListener<TaskCandidateUserAddedEvent> taskCandidateUserEventListener() {
        return taskCandidateEvent -> log.info(">>> Task Candidate User Add: '"
                + taskCandidateEvent.getEntity().toString());
    }

    @Bean
    public TaskCandidateEventListener<TaskCandidateGroupAddedEvent> taskCandidateGroupEventListener() {
        return taskCandidateEvent -> log.info(">>> Task Candidate Group Add: '"
                + taskCandidateEvent.getEntity().toString());
    }

    @Bean
    public ProcessRuntimeEventListener<ProcessCompletedEvent> processCompletedEventListener() {
        return processCompletedEvent -> log.info("===>>> Process Completed: '"
                + processCompletedEvent.getEntity().toString());
    }

    /**
     * 獲取流程表單名
     *
     * @param executionEntity 執行對象
     * @return 表單名
     */
    private String projName(ExecutionEntity executionEntity) {
        Object processInstanceName = executionEntity.getVariable("projName");
        return null == processInstanceName ? "" : processInstanceName.toString();
    }

    /**
     * 獲取流程文件ID
     *
     * @param executionEntity 執行對象
     * @return 文件ID
     */
    private String fileProcInstId(ExecutionEntity executionEntity) {
        Object fileProcInstId = executionEntity.getVariable("fileProcInstId");
        return fileProcInstId == null ? "" : fileProcInstId.toString();
    }

    /**
     * 審批類型
     *
     * @param fileProcInstId 文件ID
     * @return 類型
     */
    private String processType(String fileProcInstId) {
        return StringUtils.hasText(fileProcInstId) ? "用印" : "合同";
    }

    /**
     * 獲取姓名
     *
     * @param userId 用户ID
     * @return 用户姓名
     */
    private String userName(String userId) {
        return userId + "操作人";
    }
}

上面兩種方式,更推薦使用第二種,因為第一種方案中,ActivitiEvent 是超類,而一些屬性是直接獲取不到的,如果要獲取,就需要進行向下強轉,而每種事件的類型,實現子類又是不同的,需要做很多的判斷,但是第二種方法就不用,因為當前監聽器中的對象就是改類型對應的事件的相關對象,能夠直接獲取到相關的變量和信息。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.