從業(yè)務角度來說,至少需要滿足以下功能:
1.查詢待辦事項列表
2.待辦事項的辦理
3.查看已辦歷史
從技術角度來說:
1.activiti引擎提供的api功能過于分散,對于開發(fā)人員來說,沒有一個統(tǒng)一的接口。最終導致開發(fā)效率不高,代碼維護困難。
2.畫流程圖不確定性比較多,節(jié)點名稱和監(jiān)聽器設置沒有統(tǒng)一規(guī)范。同樣也會導致效率問題。
用請假的流程來舉例,首先,看一下流程圖怎么畫?
針對于畫圖,我們先定一個小規(guī)范,要求所有的節(jié)點上的Listeners全部指定為一個我們的自定義listener,無論是ExecutionListener還是TaskListener,全部指定為一個固定的自定義listener。
// 全部統(tǒng)一設置成ActivitiListener監(jiān)聽器
public class ActivitiListener implements TaskListener, ExecutionListener {
private static final long serialVersionUID = 6071133014325140738L;
// 用戶任務監(jiān)聽器被觸發(fā)的話,就會調用下面的notify方法
@Override
public void notify(DelegateTask delegateTask) {
// 獲取當前任務的taskDefinitionKey,其實就是節(jié)點id
String taskDefinitionKey = delegateTask.getTaskDefinitionKey();
// 通過taskDefinitionKey和目標類型去IOC容器中查找指定的Bean
UserTaskListener userTaskHandler =
ApplicationContextUtil.getBean(taskDefinitionKey, UserTaskListener.class);
if (Objects.nonNull(userTaskHandler)) {
// 執(zhí)行目標Bean中的方法
userTaskHandler.notify(delegateTask);
}
}
// ExecutionListener回調方法
@Override
public void notify(DelegateExecution execution) {
// 獲取IOC容器
ApplicationContext webApplicationContext = ApplicationContextUtil.getApplicationContext();
// 從容器中查找目標類型Bean列表
Map<String, AbstractElement> beans =
webApplicationContext.getBeansOfType(AbstractElement.class);
if (CollectionUtils.isEmpty(beans)) {
return;
}
// 通過activityId查找符合要求的Bean實例。在bpmn中,其實就是元素的id。
String activityId = execution.getCurrentActivityId();
AbstractElement abstractElement = beans.get(activityId);
if (Objects.nonNull(abstractElement)) {
// 執(zhí)行目標Bean的notify方法
abstractElement.notify(execution);
return;
}
// 如果通過activityId找不到,就通過processDefinitionKey查找目標Bean
String processDefinitionKey =
CastUtil.castString(execution.getVariable(Const.PROCESS_DEFINITION_KEY));
abstractElement = beans.get(processDefinitionKey);
if (Objects.nonNull(abstractElement)) {
// 執(zhí)行目標Bean的notify方法
abstractElement.notify(execution);
}
}
}
按照上述設定,那么一旦觸發(fā)ExecutionListener就會去IOC容器中找AbstractElement類,我們來看一下這個類:
// 這個接口是需要所有UserTask來實現的
public interface UserTaskListener {
/** @param delegateTask */
void notify(DelegateTask delegateTask);
}
// 把所有的節(jié)點都抽象成AbstractElement,主要在于需要讓每一個節(jié)點都是ExecutionListener的子類
// 這個設計的靈感來自于bpmn流程圖里面任何組件元素都可以指定ExecutionListener,那意味著我如果想要統(tǒng)一規(guī)范所有的組件元素的Listener為ActivitiListener的話,那么我就需要把所有的組件都抽象成一個目標,這樣我在ActivitiListener的實現里面就可以通過一定的規(guī)則找到目標組件的實現了。
// 最終,通過總結,要求所有元素的id都作為Bean的名字,也就是說,通過這個元素的id可以在IOC容器中找到對應的Bean實例
public abstract class AbstractElement extends AbstractActivitiServiceImpl
implements ExecutionListener {
private static final long serialVersionUID = 4836235578847862052L;
@Autowired private ActivitiBusinessDataRepository activitiBusinessDataRepository;
@Transactional(rollbackFor = Exception.class)
@Override
public void notify(DelegateExecution execution) {
// 如果當前對象不支持,就直接結束
if (!support(execution)) {
return;
}
Map<String, Object> variables = Maps.newHashMap();
Map<String, Object> data = Maps.newHashMap();
// 執(zhí)行前
beforeExecute(execution, variables, data);
// 真正的執(zhí)行目標代碼
execute(execution, variables, data);
// 執(zhí)行后
afterExecute(execution, data);
}
/**
* 執(zhí)行業(yè)務
*
* @param execution
* @param variables
* @param data
*/
protected abstract void execute(
DelegateExecution execution, Map<String, Object> variables, Map<String, Object> data);
/**
* 后置處理
*
* @param execution
* @param data
*/
protected abstract void afterExecute(DelegateExecution execution, Map<String, Object> data);
/**
* @param execution
* @return
*/
protected boolean support(DelegateExecution execution) {
return false;
}
/**
* 讀取前置系統(tǒng)參數
*
* @param execution
* @param variables
* @param data
*/
protected void beforeExecute(
DelegateExecution execution, Map<String, Object> variables, Map<String, Object> data) {
String taskDefinitionKey = execution.getCurrentActivityId();
String processDefinitionKey =
CastUtil.castString(execution.getVariable(Const.PROCESS_DEFINITION_KEY));
// 從數據庫里面把自定義配置讀取到variables里面。如果沒有這種需要的話,可以去掉。
activitiBusinessDataRepository.readProcessInstanceVariable(
processDefinitionKey, taskDefinitionKey, variables);
if (!CollectionUtils.isEmpty(variables)) {
for (Map.Entry<String, Object> e : variables.entrySet()) {
String name = e.getKey();
Object value = e.getValue();
execution.setVariable(e.getKey(), e.getValue());
data.put(name, value);
}
}
}
protected void saveData(DelegateExecution execution, Map<String, Object> data) {
Timestamp now = new Timestamp(System.currentTimeMillis());
String id = execution.getId();
String taskDefinitionKey = execution.getCurrentActivityId();
String name = execution.getCurrentFlowElement().getName();
String processInstanceId = execution.getProcessInstanceId();
String businessKey = execution.getProcessInstanceBusinessKey();
TaskNodeInfo taskNodeInfo = TaskNodeInfo.newInstance(id, name, taskDefinitionKey);
// 自動保存的流程節(jié)點數據;把一些我認為一定要保存的數據自動保存下來,也就是說,不依賴于開發(fā)人員,我自己認為一定要保存的數據,比如businessKey一定要保存
List<FieldInfo> autoSaveTaskNodeVariables =
activitiBusinessDataRepository.getAutoSaveTaskNodeVariables(
Executor.defaultExecutor(), businessKey, data);
// processInstanceData是抽象方法,需要每一個子類實現;返回的數據是當前流程實例需要保存的信息,以便后續(xù)節(jié)點可以使用
List<FieldInfo> processInstanceDataList = processInstanceData(execution, data);
// taskNodeData是抽象方法,子類需要實現;返回的數據是當前節(jié)點的快照信息
List<FieldInfo> taskNodeDataList = taskNodeData(execution, data);
// 記錄流程實例數據;保存當前流程實例數據
activitiBusinessDataRepository.saveProcessInstanceData(
processInstanceDataList, null, processInstanceId, businessKey, now);
// 記錄節(jié)點數據;把當前節(jié)點的相關信息保存起來
activitiBusinessDataRepository.saveTaskNodeData(
autoSaveTaskNodeVariables, taskNodeDataList, processInstanceId, taskNodeInfo, now);
// 自動記錄節(jié)點歷史,不需要開發(fā)者操心
// 一般歷史數據大概可以歸納為時間、節(jié)點、任務、流程實例ID,附加信息等等,所以花點心思總結一下,應該是可以固定下來的,所以就不需要子類去實現
activitiBusinessDataRepository.saveHistory(
businessKey,
processInstanceId,
Executor.defaultExecutor(),
taskNodeInfo,
data,
now);
}
// 返回的數據是當前流程實例需要保存的信息
protected abstract List<FieldInfo> processInstanceData(
DelegateExecution execution, Map<String, Object> data);
// 返回的數據是當前節(jié)點的快照信息
protected abstract List<FieldInfo> taskNodeData(
DelegateExecution execution, Map<String, Object> data);
/**
* 讀取變量參數
*
* @param processDefinitionKey
* @param taskDefinitionKey
* @param variables
*/
protected void readTransactorAndSendNoticeVariable(
String processDefinitionKey, String taskDefinitionKey, Map<String, Object> variables) {
activitiBusinessDataRepository.readTransactorAndSendNoticeVariable(
processDefinitionKey, taskDefinitionKey, variables);
}
}
綜上所述,我們總結兩點規(guī)范:
1.所有的Listener都是ActivitiListener
2.所有的組件id都作為Bean的名字,通過組件id去IOC容器中查找目標Bean;如果找不到,就通過processDefinitionKey查找目標Bean
關于AbstractActivitiServiceImpl這個類,其實就是集成了activiti里面一些常用的api,我認為這是大多數組件都會調用的,所以特地放在這個抽象類里面,以便所有AbstractElement的子類都可以使用里面的方法。只和activiti引擎相關數據表打交道的邏輯可以放在這個類里面實現。
public abstract class AbstractActivitiServiceImpl
implements IActivitiService, ApplicationContextAware {
@Autowired protected TaskService taskService;
@Autowired protected FormService formService;
@Autowired protected HistoryService historyService;
@Autowired protected IdentityService identityService;
@Autowired protected ManagementService managementService;
@Autowired protected RepositoryService repositoryService;
@Autowired protected RuntimeService runtimeService;
@Autowired private IActRunTaskRepository actRunTaskRepository;
private static ApplicationContext APPLICATION_CONTEXT;
protected ApplicationContext applicationContext() {
return APPLICATION_CONTEXT;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
APPLICATION_CONTEXT = applicationContext;
}
/**
* 統(tǒng)計指定用戶的待辦事項數量
*
* @param userIds
* @return
*/
public List<ActivitiCountGroupByResult> countUnassignedTaskGroupByUserId(List<String> userIds) {
List<Map<String, Object>> list = actRunTaskRepository.countRunTaskForEachOne(userIds);
if (CollectionUtils.isEmpty(list)) {
return null;
}
Map<String, Integer> userTaskMap = Maps.newHashMap();
for (Map<String, Object> e : list) {
String userId = CastUtil.castString(e.get("userId"));
Integer taskNum = CastUtil.castInt(e.get("taskNum"));
userTaskMap.put(userId, taskNum);
}
List<ActivitiCountGroupByResult> result = Lists.newArrayList();
for (String userId : userIds) {
Integer taskNum = userTaskMap.getOrDefault(userId, 0);
result.add(new ActivitiCountGroupByResult(userId, taskNum));
}
return result;
}
/**
* 待辦事項最少的人
*
* @param userIds
* @return
*/
public ActivitiCountGroupByResult minUnassignedTask(List<String> userIds) {
return countUnassignedTask(userIds, false);
}
/**
* 待辦事項最多的人
*
* @param userIds
* @return
*/
public ActivitiCountGroupByResult maxUnassignedTask(List<String> userIds) {
return countUnassignedTask(userIds, true);
}
/**
* 待辦事項最少或最多的人
*
* @param userIds
* @return
*/
private ActivitiCountGroupByResult countUnassignedTask(List<String> userIds, boolean isMax) {
List<ActivitiCountGroupByResult> list = countUnassignedTaskGroupByUserId(userIds);
if (CollectionUtils.isEmpty(list)) {
// 都沒有待辦事項
return null;
}
if (isMax) {
return Collections.max(
list, Comparator.comparingInt(ActivitiCountGroupByResult::getNum));
} else {
return Collections.min(
list, Comparator.comparingInt(ActivitiCountGroupByResult::getNum));
}
}
@Override
public Task queryUnassignedTask(String taskId) {
return taskService.createTaskQuery().taskId(taskId).singleResult();
}
/**
* 通過taskId查詢task
*
* @param taskId
* @return
*/
@Override
public Task queryUnassignedTask(String taskId, String userId) {
return taskService
.createTaskQuery()
.taskId(taskId)
.taskCandidateOrAssigned(userId)
.singleResult();
}
/**
* 查詢待辦任務
*
* @param userId
* @return
*/
@Override
public List<Task> queryUnassignedTaskByUser(
String userId, List<String> processInstaceIds, final int page, int size) {
int size_val = resetSize(size);
int firstResult = countFirstResult(page, size_val);
TaskQuery taskQuery = taskService.createTaskQuery().taskCandidateOrAssigned(userId);
if (!CollectionUtils.isEmpty(processInstaceIds)) {
taskQuery = taskQuery.processInstanceIdIn(processInstaceIds);
}
return taskQuery.orderByTaskCreateTime().desc().listPage(firstResult, size_val);
}
/**
* 計算第一條結果的索引
*
* @param page
* @param size
* @return
*/
private int countFirstResult(final int page, final int size) {
int startIndex = 1;
// 如果減1后還小于等于0,就重置為0(因為達成統(tǒng)一規(guī)定:分頁的第一頁page指定從1開始,但是jpa中是從0開始)
startIndex = page - startIndex;
int page_val = startIndex <= 0 ? 0 : startIndex;
return page_val * size;
}
/**
* 計算每頁大小
*
* @param size
* @return
*/
private int resetSize(final int size) {
return size <= 0 ? 10 : size;
}
/**
* 查詢已辦任務
*
* @param userId
* @param page
* @param size
* @return
*/
@Override
public List<HistoricTaskInstance> queryDoneTasks(String userId, int page, int size) {
int size_val = resetSize(size);
int firstResult = countFirstResult(page, size_val);
return historyService
.createHistoricTaskInstanceQuery()
.taskAssignee(userId)
.finished()
.orderByTaskCreateTime()
.desc()
.listPage(firstResult, size_val);
}
/**
* 統(tǒng)計待辦事項數量
*
* @param userId
* @return
*/
@Override
public long countUnassignedTask(String userId) {
return taskService.createTaskQuery().taskCandidateOrAssigned(userId).count();
}
/**
* 統(tǒng)計已辦事項數量
*
* @param userId
* @return
*/
public long countDoneTask(String userId) {
return historyService.createHistoricTaskInstanceQuery().taskAssignee(userId).count();
}
/**
* 查詢當前流程沒有辦理人的任務
*
* @param processInstanceId
* @return
*/
@Override
public List<Task> currentUnassignedTasks(String processInstanceId) {
return taskService
.createTaskQuery()
.processInstanceId(processInstanceId)
.taskUnassigned()
.list();
}
/**
* 查詢當前沒有被辦理的任務
*
* @param processInstanceId
* @return
*/
@Override
public Task currentUndoneTask(String processInstanceId) {
return taskService
.createTaskQuery()
.processInstanceId(processInstanceId)
.singleResult();
}
}
從下面開始,我們就要開始做具體的區(qū)分了,開始區(qū)分流程實例和任務處理器了
-
流程實例
// 考慮到每一個流程都有一個開始節(jié)點,那么我就抓住它們的共同點,做了一個抽象類,供所有的實例類來繼承 public abstract class BaseProcessServiceImpl extends AbstractElement { private ProcessDefinition processDefinition; private BpmnModel bpmnModel; private StartEvent startEvent; private Collection<FlowElement> xmlNodeElements; private static final String START_EVENT_KEY = "0"; // 通過IOC容器獲取所有的用戶任務處理器對象;后續(xù)會提到UserTaskHandler @Autowired private Map<String, UserTaskHandler> userTaskHandlers; // 持久化工具;專門用來存儲數據 @Autowired private ActivitiBusinessDataRepository activitiBusinessDataRepository; // 初始化工作;這些解析出來的屬性都是為了方便以后擴展使用。 @PostConstruct private void init() { // 把流程定義解析出來 processDefinition = repositoryService .createProcessDefinitionQuery() .processDefinitionKey(processDefinitionKey()) .latestVersion() .singleResult(); // 解析出bpmn模型 bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); Process process = bpmnModel.getProcessById(processDefinitionKey()); // 解析出所有的xml節(jié)點元素 this.xmlNodeElements = process.getFlowElements(); // 解析出啟動節(jié)點 this.startEvent = (StartEvent) process.getInitialFlowElement(); } /** * 啟動流程實例 * * @param businessKey * @param executor 執(zhí)行人 * @param variables 需要放進流程實例中的變量值 * @param data 用來貫穿整個方法,可以把數據放在里面,最后在調用的方法中取出來使用 * @return * @throws GlobalException */ @Transactional(rollbackFor = Exception.class) public ProcessInstance startProcessInstance( String businessKey, Executor executor, Map<String, Object> variables, Map<String, Object> data) throws GlobalException { Preconditions.checkState(StringUtils.isNotBlank(businessKey), "businessKey不能為空"); // 特地創(chuàng)建一個map對象,用作啟動流程實例的參數;注意要與data區(qū)分開,data是不會與流程實例打交道的,但是data的作用是用來貫穿整個方法的,專門用于數據傳遞;也就是說,data里面的數據不會弄臟流程實例。 Map<String, Object> allVariables = Maps.newHashMap(); if (!CollectionUtils.isEmpty(variables)) { allVariables.putAll(variables); } // processDefinitionKey是一個抽象方法,需要子類實現,不同的流程定義不一樣 String processDefinitionKey = processDefinitionKey(); allVariables.put(Const.PROCESS_DEFINITION_KEY, processDefinitionKey); // 前置操作 beforeStartProcessInstance(processDefinitionKey, businessKey, executor, allVariables, data); // 啟動流程實例 ProcessInstance processInstance; if (CollectionUtils.isEmpty(allVariables)) { processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey); } else { processInstance = runtimeService.startProcessInstanceByKey( processDefinitionKey, businessKey, allVariables); } // 后置操作 afterStartProcessInstance(processInstance, businessKey, executor, variables, data); return processInstance; } // 前置操作 protected void beforeStartProcessInstance( String processDefinitionKey, String businessKey, Executor executor, Map<String, Object> variables, Map<String, Object> data) throws GlobalException { // 通過startEventKey獲取啟動節(jié)點 UserTaskHandler userTaskHandler = userTaskHandlers.get(startEventKey()); if (Objects.nonNull(userTaskHandler)) { // 設置啟動節(jié)點的執(zhí)行人 identityService.setAuthenticatedUserId(executor.getId()); // 同樣的引入任務處理前置操作 userTaskHandler.beforeTask(null, businessKey, executor, variables, data); } // 讀取一些自定義配置,把配置信息存儲進variables;這些配置將被用在啟動流程實例,這樣就可以在這個流程實例存活的任意時刻和節(jié)點獲取相關配置,方便后續(xù)擴展;比如后續(xù)可以擴展動態(tài)指派任務、待辦任務消息提示等業(yè)務功能 activitiBusinessDataRepository.readProcessInstanceVariable( processDefinitionKey(), startEventKey(), variables); } // 后置操作 protected void afterStartProcessInstance( ProcessInstance processInstance, String businessKey, Executor executor, Map<String, Object> variables, Map<String, Object> data) { Timestamp now = new Timestamp(System.currentTimeMillis()); String processInstanceId = processInstance.getId(); String processDefinitionKey = processInstance.getProcessDefinitionKey(); String processDefinitionName = processInstance.getProcessDefinitionName(); String startUserId = processInstance.getStartUserId(); long startTime = processInstance.getStartTime().getTime(); TaskNodeInfo taskNodeInfo = TaskNodeInfo.newInstance(START_EVENT_KEY, startEventName(), startEventKey()); // 根據任務節(jié)點taskDefinitionKey獲取指定的UserTaskHandler UserTaskHandler userTaskHandler = userTaskHandlers.get(taskNodeInfo.getDefinitionKey()); List<FieldInfo> processInstanceDataList = null; List<FieldInfo> taskNodeDataList = null; if (Objects.nonNull(userTaskHandler)) { // 獲取審核結果和建議;默認用戶任務都會有審核結果和建議。 Object result = userTaskHandler.approvalResult(variables, data); if (Objects.nonNull(result)) { data.put(Approval.APPROVAL_RESULT.name(), result); } Object opinion = userTaskHandler.approvalOpinion(variables, data); if (Objects.nonNull(opinion)) { data.put(Approval.APPROVAL_OPINION.name(), opinion); } // 用戶任務中,需要保存到流程實例中的數據 processInstanceDataList = userTaskHandler.processInstanceData( processInstance, executor, businessKey, variables, data); // 用戶任務中,需要保存到節(jié)點快照的數據 taskNodeDataList = userTaskHandler.taskNodeData( processInstance, executor, businessKey, variables, data); } // 和上面兩個對應,下面是程序自動保存的數據,不依賴于開發(fā)者 // 自動保存的流程實例數據 List<FieldInfo> autoSaveProcessInstanceData = activitiBusinessDataRepository.getAutoSaveBusinessInstanceVariables( processDefinitionKey, processDefinitionName, startUserId, startTime); // 自動保存的流程節(jié)點數據 List<FieldInfo> autoSaveTaskNodeVariables = activitiBusinessDataRepository.getAutoSaveTaskNodeVariables( executor, businessKey, data); // 保存該流程實例數據 activitiBusinessDataRepository.saveProcessInstanceData( processInstanceDataList, autoSaveProcessInstanceData, processInstanceId, businessKey, now); // 保存節(jié)點快照數據 activitiBusinessDataRepository.saveTaskNodeData( autoSaveTaskNodeVariables, taskNodeDataList, processInstanceId, taskNodeInfo, now); // 保存節(jié)點歷史數據;歷史數據屬于自動保存的,不需要開發(fā)者操心。 activitiBusinessDataRepository.saveHistory( businessKey, processInstanceId, executor, taskNodeInfo, data, now); } /** * 所有任務辦理入口 * @description */ @Transactional(rollbackFor = GlobalException.class) public Task completeTask( String taskId, String businessKey, Executor executor, Map<String, Object> variables, Map<String, Object> data) throws GlobalException { // 通過taskId作為查詢參數,還有一個就是當前辦理人的id Task task = queryUnassignedTask(taskId, executor.getId()); if (Objects.isNull(task)) { throw GlobalException.newInstance("TASK_NOT_EXIST", "任務不存在"); } // 同上面的流程啟動;作為辦理任務的參數容器 Map<String, Object> allVariables = Maps.newHashMap(); if (!CollectionUtils.isEmpty(variables)) { allVariables.putAll(variables); } // 前置操作 beforeCompleteTask(task, businessKey, executor, allVariables, data); // 任務辦理 if (CollectionUtils.isEmpty(allVariables)) { taskService.complete(task.getId()); } else { taskService.complete(task.getId(), allVariables); } // 后置操作 afterCompleteTask(task, businessKey, executor, allVariables, data); return task; } // 前置操作 protected void beforeCompleteTask( Task task, String businessKey, Executor executor, Map<String, Object> variables, Map<String, Object> data) throws GlobalException { // 獲取當前任務的taskDefinitionKey String taskDefinitionKey = task.getTaskDefinitionKey(); // 查詢目標UserTaskHandler UserTaskHandler userTaskHandler = userTaskHandlers.get(taskDefinitionKey); if (Objects.nonNull(userTaskHandler)) { // 執(zhí)行任務處理的前置操作 userTaskHandler.beforeTask(task, businessKey, executor, variables, data); } // 讀取該節(jié)點相關配置;只有執(zhí)行到該節(jié)點,才會讀取該節(jié)點的配置,而不是一次性讀取流程的所有配置;注意按需加載 activitiBusinessDataRepository.readProcessInstanceVariable( processDefinitionKey(), taskDefinitionKey, variables); } // 后置操作 protected void afterCompleteTask( Task task, String businessKey, Executor executor, Map<String, Object> variables, Map<String, Object> data) { Timestamp now = new Timestamp(System.currentTimeMillis()); String processInstanceId = task.getProcessInstanceId(); TaskNodeInfo taskNodeInfo = TaskNodeInfo.newInstance(task.getId(), task.getName(), task.getTaskDefinitionKey()); // 拿到UserTaskHandler UserTaskHandler userTaskHandler = userTaskHandlers.get(taskNodeInfo.getDefinitionKey()); // 說白了,下面還是準備數據,因為后面需要把數據作持久化 List<FieldInfo> processInstanceDataList = null; List<FieldInfo> taskNodeDataList = null; if (Objects.nonNull(userTaskHandler)) { // 審核意見和建議 Object result = userTaskHandler.approvalResult(variables, data); if (Objects.nonNull(result)) { data.put(Approval.APPROVAL_RESULT.name(), result); } Object opinion = userTaskHandler.approvalOpinion(variables, data); if (Objects.nonNull(opinion)) { data.put(Approval.APPROVAL_OPINION.name(), opinion); } // 流程實例數據 processInstanceDataList = userTaskHandler.processInstanceData( task, executor, businessKey, variables, data); // 節(jié)點數據 taskNodeDataList = userTaskHandler.taskNodeData(task, executor, businessKey, variables, data); } // 自動保存的流程節(jié)點數據 List<FieldInfo> autoSaveTaskNodeVariables = activitiBusinessDataRepository.getAutoSaveTaskNodeVariables( executor, businessKey, data); // 數據準備好后,下面開始持久化 // 保存流程實例數據 activitiBusinessDataRepository.saveProcessInstanceData( processInstanceDataList, null, processInstanceId, businessKey, now); // 保存節(jié)點快照數據 activitiBusinessDataRepository.saveTaskNodeData( autoSaveTaskNodeVariables, taskNodeDataList, processInstanceId, taskNodeInfo, now); // 保存節(jié)點歷史數據;其實是當前節(jié)點的時間、節(jié)點、辦理人等相關信息,結合上面的快照數據,就可以完全恢復當前節(jié)點辦理的任務 activitiBusinessDataRepository.saveHistory( businessKey, processInstanceId, executor, taskNodeInfo, data, now); } /** * 指定processDefinitionKey,需要子類實現 * * @return */ protected abstract String processDefinitionKey(); /** * 指定startEventKey * * @return */ protected String startEventKey() { return getStartEvent().getId(); } /** * 指定startEventName * * @return */ protected String startEventName() { return getStartEvent().getName(); } protected ProcessDefinition getProcessDefinition() { return this.processDefinition; } protected BpmnModel getBpmnModel() { return this.bpmnModel; } protected StartEvent getStartEvent() { return this.startEvent; } protected Collection<FlowElement> getXmlNodeElements() { return this.xmlNodeElements; } // 實現ExecutionListener前置操作,避免子類不需要實現的時候也要寫一個空實現;子類如果真的需要實現,可以重寫該方法 @Override protected void beforeExecute( DelegateExecution execution, Map<String, Object> variables, Map<String, Object> data) {} // 實現ExecutionListener的notify操作,避免子類不需要實現的時候也要寫一個空實現;子類如果真的需要實現,可以重寫該方法 @Override protected void execute( DelegateExecution execution, Map<String, Object> variables, Map<String, Object> data) { } // 實現ExecutionListener后置操作,避免子類不需要實現的時候也要寫一個空實現;子類如果真的需要實現,可以重寫該方法 @Override protected void afterExecute(DelegateExecution execution, Map<String, Object> data) { } /** * 不能重寫;在流程實例的實現中,不需要實現這個方法,只有任務處理器需要實現 * * @param execution * @param data * @return */ @Override protected final List<FieldInfo> processInstanceData( DelegateExecution execution, Map<String, Object> data) { return null; } /** * 不能重寫;在流程實例的實現中,不需要實現這個方法,只有任務處理器需要實現 * * @param execution * @param data * @return */ @Override protected final List<FieldInfo> taskNodeData( DelegateExecution execution, Map<String, Object> data) { return null; } }大家一定要仔細看上面這段代碼的注釋,里面闡述了這樣設計的原因和思路
-
任務處理器
// 實現AbstractElement,開始分支出任務處理器 public abstract class AbstractTaskHandler extends AbstractElement { private static final long serialVersionUID = -1470919793703094859L; @Autowired private IBusinessNodeVariablesRepository businessNodeVariablesRepository; // 通過任務ID查詢歷史任務 public Object historyTaskInfo(String taskId){ List<TaskNodeVariables> historyDataList = businessNodeVariablesRepository.findByTaskId(taskId); if (CollectionUtils.isEmpty(historyDataList)) { return null; } Map<String, Object> map = Maps.newHashMap(); for (TaskNodeVariables nodeVariables : historyDataList) { map.put(nodeVariables.getFieldName(), nodeVariables); } return map; } } // 注意UserTaskHandler實現了UserTaskListener,一定要回頭去看ActivitiListener public abstract class UserTaskHandler extends AbstractTaskHandler implements UserTaskListener { /** * 辦理任務之前準備variables;同樣的空實現,是為了避免子類一定要去寫這個實現 * * @param task * @param businessKey * @param executor * @param variables * @param data */ public void beforeTask( Task task, String businessKey, Executor executor, Map<String, Object> variables, Map<String, Object> data) throws GlobalException { } /** * 這個是啟動流程實例和完成待辦任務的實現中都會調用的函數,自定義保存節(jié)點數據;同樣的空實現,是為了避免子類一定要去寫這個實現 * * @param obj * @param executor * @param businessKey * @param variables * @return */ public List<FieldInfo> taskNodeData( Object obj, Executor executor, String businessKey, Map<String, Object> variables, Map<String, Object> data) { return null; } /** * 這個是啟動流程實例和完成待辦任務的實現中都會調用的函數,自定義保存實例數據;同樣的空實現,是為了避免子類一定要去寫這個實現 * * @param obj * @param executor * @param businessKey * @param variables * @return */ public List<FieldInfo> processInstanceData( Object obj, Executor executor, String businessKey, Map<String, Object> variables, Map<String, Object> data) { return null; } /** * 審批結果;這個就是要求所有的用戶任務必須要有審核結果 * * @return */ public abstract Object approvalResult(Map<String, Object> variables, Map<String, Object> data); /** * 審批意見;要求所有的用戶任務必須要有審核意見 * * @return */ public abstract Object approvalOpinion(Map<String, Object> variables, Map<String, Object> data); /** * Execute前置處理;同樣的空實現,是為了避免子類一定要去寫這個實現 * * @param execution * @param variables * @param data */ @Override protected void beforeExecute( DelegateExecution execution, Map<String, Object> variables, Map<String, Object> data) { } /** * 后置處理;同樣的空實現,是為了避免子類一定要去寫這個實現 * * @param execution * @param data */ @Override protected final void afterExecute(DelegateExecution execution, Map<String, Object> data) { } // 這個基本沒用,設計上還需要調整;同樣的空實現,是為了避免子類一定要去寫這個實現 @Override protected final List<FieldInfo> processInstanceData( DelegateExecution execution, Map<String, Object> data) { return null; } // 這個基本沒用,設計上還需要調整;同樣的空實現,是為了避免子類一定要去寫這個實現 @Override protected final List<FieldInfo> taskNodeData( DelegateExecution execution, Map<String, Object> data) { return null; } // 這個就是實現ExecutionListener的空實現 @Override protected void execute( DelegateExecution execution, Map<String, Object> variables, Map<String, Object> data) { } // 如果是任務被創(chuàng)建的事件的話,那么先設置辦理人,這就是動態(tài)實現設置辦理人的思路;有需要可以擴展其他功能。 @Override public void notify(DelegateTask delegateTask) { if (StringUtils.equalsIgnoreCase(delegateTask.getEventName(), "create")) { setAssignee(delegateTask); } } /** * 設置辦理人 * * @param delegateTask */ protected void setAssignee(DelegateTask delegateTask) { // 獲取processDefinitionKey String processDefinitionKey = CastUtil.castString(delegateTask.getVariable(Const.PROCESS_DEFINITION_KEY)); // 獲取taskDefinitionKey String taskDefinitionKey = delegateTask.getTaskDefinitionKey(); Map<String, Object> variables = Maps.newHashMap(); // 查詢節(jié)點配置 readTransactorAndSendNoticeVariable(processDefinitionKey, taskDefinitionKey, variables); // 根據配置查詢辦理人;TODO String assignee = ""; delegateTask.setAssignee(assignee); } // 需要特殊處理的啟動節(jié)點;我把它當用戶任務處理掉 public abstract static class StartEventTaskHandler extends UserTaskHandler { // 把不可能調用的直接final處理掉 @Override public final void notify(DelegateTask delegateTask) { } @Override public List<FieldInfo> taskNodeData( Object obj, Executor executor, String businessKey, Map<String, Object> variables, Map<String, Object> data) { return null; } @Override public List<FieldInfo> processInstanceData( Object obj, Executor executor, String businessKey, Map<String, Object> variables, Map<String, Object> data) { return null; } // 把不可能調用的直接final處理掉 @Override public final Object approvalResult(Map<String, Object> variables, Map<String, Object> data) { return null; } // 把不可能調用的直接final處理掉 @Override public final Object approvalOpinion(Map<String, Object> variables, Map<String, Object> data) { return null; } } // 需要特殊處理的結束節(jié)點;我把它當用戶任務處理掉 public abstract static class EndEventTaskHandler extends UserTaskHandler { // 把不可能調用的直接final處理掉 @Override public final List<FieldInfo> taskNodeData( Object obj, Executor executor, String businessKey, Map<String, Object> variables, Map<String, Object> data) { return null; } // 把不可能調用的直接final處理掉 @Override public final List<FieldInfo> processInstanceData( Object obj, Executor executor, String businessKey, Map<String, Object> variables, Map<String, Object> data) { return null; } // 把不可能調用的直接final處理掉 @Override public final Object approvalResult(Map<String, Object> variables, Map<String, Object> data) { return null; } // 把不可能調用的直接final處理掉 @Override public final Object approvalOpinion(Map<String, Object> variables, Map<String, Object> data) { return null; } // 把不可能調用的直接final處理掉 @Override public final void notify(DelegateTask delegateTask) { } } } // 這個類和ActivitiListener類似的原理,但是是針對ServiceTask的,因為ServiceTask組件需要指定一個JavaDelegate的實現 public class ServiceTaskDelegate implements JavaDelegate { @Override public void execute(DelegateExecution execution) { // 獲取組件id String taskDefinitionKey = execution.getCurrentActivityId(); // 查找ServiceTaskHandler對象 ServiceTaskHandler serviceTaskHandler = ApplicationContextUtil.getBean(taskDefinitionKey, ServiceTaskHandler.class); if (Objects.nonNull(serviceTaskHandler)) { // 調用目標方法 serviceTaskHandler.notify(execution); } } } // 這個是系統(tǒng)任務處理器,activiti里面還有一個ServiceTask的組件 public abstract class ServiceTaskHandler extends AbstractTaskHandler { // 重寫ExecutionListener后置操作,保存數據 @Override protected void afterExecute(DelegateExecution execution, Map<String, Object> data) { saveData(execution, data); } // 空實現 @Override protected List<FieldInfo> processInstanceData( DelegateExecution execution, Map<String, Object> data) { return null; } // 空實現 @Override protected List<FieldInfo> taskNodeData(DelegateExecution execution, Map<String, Object> data) { return null; } // 實現ExecutionListener的操作 @Override protected void execute( DelegateExecution execution, Map<String, Object> variables, Map<String, Object> data) { Object result = approvalResult(execution, variables, data); Object opinion = approvalOpinion(execution, variables, data); if (Objects.nonNull(result)) { data.put(Approval.APPROVAL_RESULT.name(), result); } if (Objects.nonNull(opinion)) { data.put(Approval.APPROVAL_OPINION.name(), opinion); } } /** * 審批結果 * * @return */ public abstract Object approvalResult( DelegateExecution execution, Map<String, Object> variables, Map<String, Object> data); /** * 審批意見 * * @return */ public abstract Object approvalOpinion( DelegateExecution execution, Map<String, Object> variables, Map<String, Object> data); } // 網關任務處理器 public abstract class GateTaskHandler extends AbstractElement { private static final long serialVersionUID = -1425610003326612058L; // 空實現 @Override protected void afterExecute(DelegateExecution execution, Map<String, Object> data) {} // 把不可能調用的直接final處理掉 @Override protected final List<FieldInfo> processInstanceData( DelegateExecution execution, Map<String, Object> data) { return null; } // 把不可能調用的直接final處理掉 @Override protected final List<FieldInfo> taskNodeData( DelegateExecution execution, Map<String, Object> data) { return null; } }按照這個,我把最主要的UserTask、ServiceTask等幾個常見的任務給處理掉了。下面來看一下這些抽象類都怎么使用。
-
具體實現
// "leave"是流程圖的id
@Service("leave")
public class LeaveServiceImpl extends BaseProcessServiceImpl
implements ILeaveService {
/**
* 流程定義key
*/
private static final String LEAVE_PROCESS_DEFINITION_KEY = "leave";
@Override
protected String processDefinitionKey() {
return LEAVE_PROCESS_DEFINITION_KEY;
}
// 接口方法,需要實現,申請假期
@Transactional(rollbackFor = Exception.class)
@Override
public String apply(Object data, String userId) throws GlobalException {
// 準備數據TODO
// 調用BaseProcessServiceImpl啟動方法啟動流程實例
ProcessInstance processInstance =
startProcessInstance(businessKey, Executor.defaultExecutor(userId),
variables, data);
return processInstance.getId();
}
// 接口方法,需要實現,審批業(yè)務
@Transactional(rollbackFor = Exception.class)
@Override
public String approval(Object approval, String userId)
throws GlobalException {
// 準備數據
Map<String, Object> variables = handleApprovalVariables(approval, userId);
Map<String, Object> data = handleApprovalData(approval);
// 調用父類的辦理方法
Task task =
completeTask(
approval.getTaskId(), approval.getBusinessKey(),
Executor.defaultExecutor(userId), variables, data);
return task.getId();
}
}
// 啟動節(jié)點ID
@Component("leave_apply")
public class ApplyTask extends UserTaskHandler.StartEventTaskHandler {
// 任務前置操作
@Override
public void beforeTask(
Task task,
String businessKey,
Executor executor,
Map<String, Object> variables,
Map<String, Object> data)
throws GlobalException {
}
// 任務數據快照
@Override
public List<FieldInfo> taskNodeData(
Object obj,
Executor executor,
String businessKey,
Map<String, Object> variables,
Map<String, Object> data) {
if (CollectionUtils.isEmpty(data)) {
return null;
}
FieldInfo.Builder builder = new FieldInfo.Builder();
for (Map.Entry<String, Object> entry : data.entrySet()) {
String value = CastUtil.castString(entry.getValue(), StringUtils.EMPTY);
builder.normalFieldInfo(entry.getKey(), value, "");
}
return builder.build();
}
// 保存在流程實例中的數據
@Override
public List<FieldInfo> processInstanceData(
Object obj,
Executor executor,
String businessKey,
Map<String, Object> variables,
Map<String, Object> data) {
String leave_reason = CastUtil.castString(variables.get("leave_reason"), StringUtils.EMPTY);
String leave_date = CastUtil.castString(variables.get("leave_date"), StringUtils.EMPTY);
return new FieldInfo.Builder()
.normalFieldInfo(
"leave_reason", leave_reason, "請假原因")
.normalFieldInfo("leave_date", leave_date, "請假日期")
.build();
}
@Override
protected void execute(
DelegateExecution execution, Map<String, Object> variables, Map<String, Object> data) {
}
}
// 審批節(jié)點id
@Component("leave_approval")
public class UserApprovalTask extends UserTaskHandler {
@Override
public void beforeTask(
Task task,
String businessKey,
Executor executor,
Map<String, Object> variables,
Map<String, Object> data)
throws GlobalException {
}
// 節(jié)點快照數據;這里我存儲的是審批數據
@Override
public List<FieldInfo> taskNodeData(
Object obj,
Executor executor,
String businessKey,
Map<String, Object> variables,
Map<String, Object> data) {
String approvalUserId = CastUtil.castString(variables.get("approvalUserId"), StringUtils.EMPTY);
String approvalResult = CastUtil.castString(data.get("approvalResult"), StringUtils.EMPTY);
String approvalOpinion = CastUtil.castString(data.get("approvalOpinion"), StringUtils.EMPTY);
FieldInfo.Builder builder = new FieldInfo.Builder();
builder.normalFieldInfo("approvalUserId", approvalUserId, "審批人ID")
.normalFieldInfo("approvalResult", approvalResult, "審批結果")
.normalFieldInfo("approvalOpinion", approvalOpinion, "審批意見");
return builder.build();
}
// 同樣的,我把需要的數據存儲進流程實例中
@Override
public List<FieldInfo> processInstanceData(
Object obj,
Executor executor,
String businessKey,
Map<String, Object> variables,
Map<String, Object> data) {
String approvalUserId = CastUtil.castString(variables.get("approvalUserId"), StringUtils.EMPTY);
String approvalResult = CastUtil.castString(variables.get("approvalResult"), StringUtils.EMPTY);
String approvalOpinion = CastUtil.castString(variables.get("approvalOpinion"), StringUtils.EMPTY);
return new FieldInfo.Builder()
.normalFieldInfo(
"approvalUserId",
approvalUserId,
"審批人ID")
.normalFieldInfo("approvalResult",
approvalResult,
"審批結果")
.normalFieldInfo(
"approvalOpinion",
approvalOpinion,
"審批意見")
.build();
}
// 審批結果
@Override
public Object approvalResult(Map<String, Object> variables, Map<String, Object> data) {
return data.getOrDefault("approvalResult", StrUtil.EMPTY);
}
// 審批意見
@Override
public Object approvalOpinion(Map<String, Object> variables, Map<String, Object> data) {
return data.getOrDefault("approvalOpinion", StrUtil.EMPTY);
}
// 自定義設置辦理人
@Override
protected void setAssignee(DelegateTask delegateTask) {
Object approvalUserId = delegateTask.getVariable("approvalUserId");
if (Objects.isNull(approvalUserId)) {
super.setAssignee(delegateTask);
} else {
String userId = CastUtil.castString(approvalUserId);
delegateTask.setAssignee(userId);
}
}
}
通過以上代碼及相關注釋,我已經把我對于activiti工作量引擎的使用與思考總結起來了,感興趣的小伙伴可以參與一起討論。