本文個人博客地址:Activiti7事件監聽 (leafage.top)
好久沒有記錄筆記了,最近做了一些關於工作流的事情,記錄一下使用activiti 7的一些經驗。
需求:
- 在流程發起和流程操作的過程中,給相關人員發送流程審批的通知提醒;
- 不要在配置流程時手動添加,不能侵入到流程操作的過程,影響流程執行;
<mark>這個怎麼入手呢?沒搞過activiti,activiti7 的官方文檔寫的跟屎一樣爛,感覺好難呀</mark>😔...
文檔參考性不高,那就試試看官方的示例,找到 activiti 的 repository ,有一個示例 module 叫 activiti-examples,<mark>這裏的示例不能直接跑,只能看</mark>,要想跑起來,就複製,粘貼,放到自己的項目中。
跑題了,説會主題。。。
activiti中的幾個關聯的重要的類或接口:
-
activiti 中每個流程信息是通過 ProcessInstance 描述,它有這麼幾個狀態:created、started、completed、cancelled、resumed、updated、suspended,與之對應的相關事件描述類是:ProcessCreatedEvent、ProcessStartedEvent、ProcessCompletedEvent、ProcessCancelledEvent、ProcessResumedEvent、ProcessUpdatedEvent、ProcessSuspendedEvent等。
-
每個流程節點在 activiti 中 通過 Task 來描述,它有這麼幾個個狀態:created、assigned、completed、updated、cancelled、suspended等,與之對應的相關事件描述類是:TaskCreatedEvent、TaskAssignedEvent、TaskCompletedEvent、TaskUpdatedEvent、TaskCancelledEvent、TaskSuspendedEvent等。
如何配置監聽器?
1. 全局事件監聽器:
涉及到兩個類\接口,全局事件監聽器 ActivitiEventListener 和 ProcessEngineConfigurationConfigurer(有一個默認的實現類:DefaultActivityBehaviorFactoryMappingConfigurer)
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));
}
}
如何來監聽事件?
- 實現 ActivitiEventListener 接口,重寫 void onEvent(ActivitiEvent event) 方法;
- 新增配置類,繼承 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 是超類,而一些屬性是直接獲取不到的,如果要獲取,就需要進行向下強轉,而每種事件的類型,實現子類又是不同的,需要做很多的判斷,但是第二種方法就不用,因為當前監聽器中的對象就是改類型對應的事件的相關對象,能夠直接獲取到相關的變量和信息。