最近因为公司需要整流程,所以研究了下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);
}
}