最近因为公司需要整流程,所以研究了下activiti。顺便记录下相关的东西。创建流程,任务认领,任务完成,任务驳回,任务结束,解析节点信息,解析模板,理解启动流程。

 

启动流程两种方式记录:

第一种就是加载模板信息后:

@Test
    public void loadModel(){
        //第一步 部署流程文件
        Deployment deployment = repositoryService//这里repositoryService是直接注入的 引擎包里面已经有了
                .createDeployment()
                .name("测试3")
                .key("123")
                .addClasspathResource("processes/myProcesses.bpmn").deploy();
    }
//根据流程定义id 启动一个 流程实例
        ProcessInstance processInstance = runtimeService.startProcessInstanceById("myProcess01",null,null); //myProcess01 是 模板的id

第二种就是比较灵活的:

这里是你加载了一个模板后都会生成一个 ProcessDefinition 在表act_procdef_info中你可看到相关信息。这种就是比较灵活的,你可以根据它的名称或者key值 得到你想要实例的流程。你每改动一次 你的模板重新加载后才能生效,这个表就会再次生成一个记录,用version控制版本是最新的。中途不要改动模板的key和id

List<ProcessDefinition> list= repositoryService.createProcessDefinitionQuery().latestVersion().list();//这里是你加载了一个模板后都会生成一个 ProcessDefinition 在表act_procdef_info中你可看到相关信息。这种就是比较灵活的,你可以根据它的名称或者key值 得到你想要实例的流程。你每改动一次 你的模板重新加载后才能生效,这个表就会再次生成一个记录,用version控制版本是最新的。中途不要改动模板的key和id
//启动一个流程
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(list.get(0).getId, businessId, variables);

接下来就是写了一些公共的方法抽取出来:

package com.dotmap.iomt.app.workflow.activitiUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.ActivitiTaskAlreadyClaimedException;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * @title 操作流程的工具类
 * @author: wangtingsong
 * @Date: 2020/6/20 11:47
 * @since 版本号
 */
@Service
@Slf4j
public class ActivitiUtil {

    @Autowired
    TaskService taskService;

    @Autowired
    RuntimeService runtimeService;

    @Autowired
    RepositoryService repositoryService;

    @Autowired
    HistoryService historyService;


    /**
     * @param processDefinitionId 流程定义id 必须
     * @param candidateUserIds    设置第一步领取审核的人多个, 可为空
     * @param variables           全局参数内容 可为空
     * @param businessId          业务参数 可为空
     * @return
     */
    public String startProcess(String processDefinitionId, List<String> candidateUserIds, Map<String, Object> variables, String businessId) {
        log.info("启动流程参数processDefinitionId:{},candidateUserIds:{}, variables:{}, businessId:{}", processDefinitionId, candidateUserIds, variables, businessId);
        //启动一个流程
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId, businessId, variables);
        //设置用户审核
        if (!CollectionUtils.isEmpty(candidateUserIds)) {
            //查询出第一步的task任务 并设置有权限审核的用户
            Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
            for (String candidateUserId : candidateUserIds) {
                taskService.addCandidateUser(task.getId(), candidateUserId);
            }
        }
        return processInstance.getId();
    }

    /**
     * 获取最新版本的所有的流程定义列表
     *
     * @return
     */
    public List<ProcessDefinition> getLastVersionProcessDefinitions() {
        //我们需要的是最新的版本
        return repositoryService.createProcessDefinitionQuery().latestVersion().list();
    }

    /**
     * 根据流程定义id 获取到对应的模板节点信息 节点名称,节点id,监听事件也可以获取
     *
     * @param processDefinitionId
     * @return
     */
    public List<FlowElement> getFlowElementByProcessDefId(String processDefinitionId) {

        log.info("根据流程定义获取节点信息入参:{}", processDefinitionId);
        //流程定义中的模板信息
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

        //模板中的所有流程
        List<Process> processes = bpmnModel.getProcesses();
        log.info("流程定义id:{}的流程条数大小:{}", processDefinitionId, processes.size());

        //取得流程的节点包括 连线实体
        Process process = processes.get(0);

        //把所有节点 用map装好。
        Map<String, FlowElement> flowElements = process.getFlowElementMap();
        List<FlowElement> flowElementList = new ArrayList<>();
        if (CollectionUtils.isEmpty(flowElements)) {
            return flowElementList;
        }
        //转换一下 成为list
        for (Map.Entry<String, FlowElement> flowElement : flowElements.entrySet()) {
            flowElementList.add(flowElement.getValue());
        }

        return flowElementList;
    }

    /**
     * 根据流程实例id 获取到当前的流程进度 task
     *
     * @param processInstanceId
     * @return
     */
    public Task getCurrentTaskByProcessIntanceId(String processInstanceId) {
        return taskService
                .createTaskQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
    }

    /**
     * 根据用户id 或者该用户的所有 有权限审核的流程task
     *
     * @param userId
     * @return
     */
    public List<Task> getAuthorityTaskByUserId(String userId) {
        return taskService
                .createTaskQuery()
                .taskCandidateUser(userId)
                .taskUnassigned()
                .list();
    }

    /**
     * 用户认领task,如果该用户有权限并且该task未被认领
     *
     * @param taskId
     * @param userId
     */
    public void claimTaskByUserId(String taskId, String userId) throws Exception {
        try {
            taskService.claim(taskId, userId);
        } catch (ActivitiTaskAlreadyClaimedException e) {
            log.warn("该任务已经被认领taskId:{},userId:{}", taskId, userId);
            throw new Exception("该任务已经被其他的人认领");
        } catch (Exception e) {
            log.warn("无权限认领该任务:{},userId:{}", taskId, userId);
        }

    }

    /**
     * 根据用户id 获取需要立即审核的流程task
     *
     * @param userId
     * @return
     */
    public List<Task> getAssignedTaskByUserId(String userId) {
        return taskService.createTaskQuery().taskAssignee(userId).list();
    }

    /**
     * 完成当前节点 进入到下一步。
     *
     * @param taskId    任务id
     * @param variables 下一步需要的参数 可为空
     * @param userId    用户id
     * @param comment 完成时的批注内容
     * @throws Exception
     */
    public void completeTask(String taskId, Map<String, Object> variables, String userId,String comment) throws Exception {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (Objects.isNull(task)) {
            log.info("该任务taskId:{},已被完成或者不存在", taskId);
            throw new Exception("任务已被完成或者不存在");
        }
        if (Objects.nonNull(task) && !userId.equals(task.getAssignee())) {
            log.info("该用户没有权限完成处理该任务taskId:{},userId:{},yUserId:{}", taskId, userId, task.getAssignee());
            throw new Exception("该用户没有权限完成处理该任务");
        }
        //如果批注信息不为空,新增批注信息内容
        if (StringUtils.isNotBlank(comment)){
            taskService.addComment(taskId, task.getProcessInstanceId(), comment);
        }
        taskService.complete(taskId, variables);
    }

    /**
     * 根据实例id 获取已经完成的节点
     *
     * @param processInstanceId
     * @return
     */
    public List<HistoricTaskInstance> getHistoryTaskByProcessInstanceId(String processInstanceId) {
        return historyService
                .createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .finished().orderByTaskCreateTime().desc().list();
    }

    /**
     * 根据用户id获取到 用户处理过的所有任务信息
     * @param userId
     * @return
     */
    public List<HistoricTaskInstance> getHistoryTaskByUserId(String userId){
        return historyService
                .createHistoricTaskInstanceQuery()
                .taskAssignee(userId).finished()
                .orderByTaskCreateTime().desc().list();
    }

    /**
     * 根据流程实例id 结束任务 任何时候都可以结束
     *
     * @param processId
     * @return
     */
    public void finishProcess(@RequestParam("processId") String processId) throws Exception {

        try {
            runtimeService.deleteProcessInstance(processId, "结束");
        } catch (Exception e) {
            log.error("流程结束异常processId:{},异常信息:{}", processId, e);
            throw new Exception("任务结束异常");
        }
        Task task = taskService.createTaskQuery().processInstanceId(processId).singleResult();
        if (null == task) {
            log.info("已结束该任务实例id:{}", processId);
        }
    }

    /**
     * 根据taskId 获取批注消息
     * @param taskId 任务id
     * @return
     */
    public Comment getComment(@RequestParam("taskId") String taskId){
        return taskService.getComment(taskId);
    }

    /**
     * 撤回或者驳回任务, 撤回是当前处理人 处理任务后后悔了,在下一个处理人还未处理的情况可以撤回
     * 驳回:当前处理人 查看任务后发现上一步处理人未处理到位,可以驳回到上一个人。
     *
     * @param processInstanceId 流程实例id
     * @param userId            当前用户id
     * @param setUserId         撤回或者驳回后的 设置用户处理人id
     * @param recallOrRebutFlag 是撤回还是驳回标识  1代表撤回 2代表驳回
     * @param comment           撤回或者驳回时的批注内容
     * @throws Exception
     */
    public void cancel(String processInstanceId, String userId, String setUserId, Integer recallOrRebutFlag, String comment) throws Exception {
        log.info("撤回或者驳回的任务的参数processInstanceId:{}, userId:{},serUserId:{}, recallOrRebutFlag:{}",
                processInstanceId, userId, setUserId, recallOrRebutFlag);


        //参数验证
        if (StringUtils.isBlank(processInstanceId)) {
            throw new Exception("流程实例id不能为空");
        }
        if (StringUtils.isBlank(userId)) {
            throw new Exception("当前用户id不能为空");
        }
        if (StringUtils.isBlank(setUserId)) {
            throw new Exception("撤回或者驳回后的处理人id不能为空");
        }
        if (Objects.isNull(recallOrRebutFlag)) {
            throw new Exception("撤回或这驳回类型不能为空");
        }

        //获取当前的task
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();

        if (task == null) {
            log.info("任务节点已完成或者未启动,无法撤回processInstanceId:{}", processInstanceId);
            throw new Exception("任务节点已完成或者未启动");
        }


        //根据时间的正序 输出,最后的一个就是正在进行中的节点任务。
        List<HistoricTaskInstance> historicTaskInstances = historyService
                .createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByTaskCreateTime().asc().list();
        //撤回的应该是 正在进行中的上一个节点
        HistoricTaskInstance myTask = null;
        for (int i = 0; i < historicTaskInstances.size(); i++) {
            if (historicTaskInstances.get(i).getId().equals(task.getId()) && i > 0) {
                myTask = historicTaskInstances.get(i - 1);
                break;
            }
        }
        if (myTask == null) {
            log.error("流程实例id,{},上一步任务节点为空");
            throw new Exception("上一步任务节点为空,无法撤回或驳回");
        }
        log.info("流程实例id,{}的上一个节点的id:{},节点处理人:{}", myTask.getId(), myTask.getAssignee());
        //权限校验
        if (recallOrRebutFlag.equals(1)) {

            if (!userId.equals(myTask.getAssignee())) {
                log.info("流程实例id:{},用户id:{}无权限,需要用户id为:{}处理撤回", processInstanceId, userId, myTask.getAssignee());
                throw new Exception("无权限撤回");
            }
        } else if (recallOrRebutFlag.equals(2)) {
            if (!userId.equals(task.getAssignee())) {
                log.info("流程实例id:{},用户id:{}无权限,需要用户id为:{}处理驳回", processInstanceId, userId, myTask.getAssignee());
                throw new Exception("无权限驳回");
            }
        } else {
            log.info("流程实例id:{},类型标识:{},不是撤回也不是驳回类型", processInstanceId, recallOrRebutFlag);
            throw new Exception("类型标识无法识别");
        }

        //获取到当前节点的上一个节点的id
        String myTaskId = myTask.getId();

        String processDefinitionId = myTask.getProcessDefinitionId();

        //获取上一个节点流程定义
        ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService
                .createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

        //获取到 历史的activity 节点
        List<HistoricActivityInstance> haiList = historyService
                .createHistoricActivityInstanceQuery()
                .executionId(myTask.getExecutionId())
                .finished().list();

        String myActivityId = null;
        for (HistoricActivityInstance hai : haiList) {
            if (myTaskId.equals(hai.getTaskId())) {
                myActivityId = hai.getActivityId();
                break;
            }
        }

        FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(myActivityId);


        Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
        //获取当前节点的 活动节点
        String activityId = execution.getActivityId();
        log.info("流程实例id:{},需要撤回的活动id:{},当前活动id:{}", processInstanceId, myActivityId, activityId);
        //获取当前的 node
        FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);

        //记录当前task的节点活动方向
        List<SequenceFlow> oriSequenceFlows = new ArrayList<>();
        oriSequenceFlows.addAll(flowNode.getOutgoingFlows());

        //清理当前的活动方向
        flowNode.getOutgoingFlows().clear();
        //建立新的方向
        List<SequenceFlow> newSequenceFlowList = new ArrayList<>();
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(flowNode);
        newSequenceFlow.setTargetFlowElement(myFlowNode);
        newSequenceFlowList.add(newSequenceFlow);
        flowNode.setOutgoingFlows(newSequenceFlowList);

        taskService.addComment(task.getId(), task.getProcessInstanceId(), comment);
        taskService.complete(task.getId());

        //设置回原来的 方向
        flowNode.setOutgoingFlows(oriSequenceFlows);
        //再次获取到当前的 活动节点就已经是 撤销后的节点了
        Task cuTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        //设置受理人
        taskService.setAssignee(cuTask.getId(), setUserId);
    }


}