Activiti工作流引擎的使用、思考與總結

從業(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工作量引擎的使用與思考總結起來了,感興趣的小伙伴可以參與一起討論。

?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容