我们经常需要工作流中退回上一步,或者退回历史某一个节点。但由于流程的场景是很复杂的,回退有以下一些场景:
1.串行路线上的退回:流程中没有任何网关(排他网关/并行网关)和会签多实例。
2.退回到并行网关分支中的某一个节点上。
3.并行网关中的某一个分支节点上发起退回,退回到并行网关前面的某一个节点上。
4.子流程中退回到主干流程中某一个节点/主干流程退回到子流程中某一个节点。
flowable版本:6.6.0
我们都知道驳回的api:
runtimeService.createChangeActivityStateBuilder().processInstanceId(proInstanceId)
.moveActivityIdTo(currentActivityId, newActivityId).changeState();
或者
runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(
currentExecutionIds, newActivityId).changeState();
但是其实不好处理的是如何获取可驳回节点信息,task任务是没有驳回需要的activityId的,言归正传,下面提供下我的实现方案:
/**
* 获取可驳回节点,排序规则-最近的节点在前面
*
* @param taskId 任务id
* @param processInstanceId 流程实例id
* @return
*/
public List<FlowNodeDto> getCanRejectedFlowNode(String taskId, String processInstanceId) {
List<FlowNodeDto> backNodes = new ArrayList<>();
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
String currActId = task.getTaskDefinitionKey();
//获取运行节点表中usertask
String sql = "select t.* from ACT_RU_ACTINST t where t.ACT_TYPE_ = 'userTask' " +
" and t.PROC_INST_ID_=#{processInstanceId} and t.END_TIME_ is not null ";
List<ActivityInstance> activityInstances = runtimeService.createNativeActivityInstanceQuery().sql(sql)
.parameter("processInstanceId", processInstanceId)
.list();
//获取运行节点表的parallelGateway节点并出重
sql = "SELECT t.ID_, t.REV_,t.PROC_DEF_ID_,t.PROC_INST_ID_,t.EXECUTION_ID_,t2.ACT_ID_, t.TASK_ID_, t.CALL_PROC_INST_ID_, t.ACT_NAME_, t.ACT_TYPE_, " +
" t.ASSIGNEE_, t.START_TIME_, t2.END_TIME_, t.DURATION_, t.DELETE_REASON_, t.TENANT_ID_" +
" FROM ACT_RU_ACTINST t JOIN (SELECT ACT_ID_,max(END_TIME_) AS END_TIME_ FROM ACT_RU_ACTINST GROUP BY ACT_ID_) t2 WHERE t.ACT_TYPE_ = 'parallelGateway' AND t.PROC_INST_ID_ = #{processInstanceId} and t.END_TIME_ is not null" +
" and t.ACT_ID_ <> #{actId}";
List<ActivityInstance> parallelGatewaies = runtimeService.createNativeActivityInstanceQuery().sql(sql)
.parameter("processInstanceId", processInstanceId)
.parameter("actId", currActId)
.list();
//排序
if (CollectionUtils.isNotEmpty(parallelGatewaies)) {
activityInstances.addAll(parallelGatewaies);
activityInstances.sort(Comparator.comparing(ActivityInstance::getEndTime));
}
//分组节点
int count = 0;
//并行网关节点
Map<ActivityInstance, List<ActivityInstance>> parallelGatewayUserTasks = new HashMap<>();
List<ActivityInstance> userTasks = new ArrayList<>();
ActivityInstance currActivityInstance = null;
for (ActivityInstance activityInstance : activityInstances) {
//网关处理
if (BpmnXMLConstants.ELEMENT_GATEWAY_PARALLEL.equals(activityInstance.getActivityType())) {
count++;
if (count % 2 != 0) {
List<ActivityInstance> datas = new ArrayList<>();
currActivityInstance = activityInstance;
parallelGatewayUserTasks.put(currActivityInstance, datas);
}
}
//用户节点
if (BpmnXMLConstants.ELEMENT_TASK_USER.equals(activityInstance.getActivityType())) {
if (count % 2 == 0) {
userTasks.add(activityInstance);
} else {
if (parallelGatewayUserTasks.containsKey(currActivityInstance)) {
parallelGatewayUserTasks.get(currActivityInstance).add(activityInstance);
}
}
}
}
//组装人员名称
List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId).finished().list();
Map<String, List<HistoricTaskInstance>> taskInstanceMap = new HashMap<>();
List<String> userCodes = new ArrayList<>();
historicTaskInstances.forEach(historicTaskInstance -> {
userCodes.add(historicTaskInstance.getAssignee());
String taskDefinitionKey = historicTaskInstance.getTaskDefinitionKey();
if (taskInstanceMap.containsKey(historicTaskInstance.getTaskDefinitionKey())) {
taskInstanceMap.get(taskDefinitionKey).add(historicTaskInstance);
} else {
List<HistoricTaskInstance> tasks = new ArrayList<>();
tasks.add(historicTaskInstance);
taskInstanceMap.put(taskDefinitionKey, tasks);
}
});
//组装usertask的数据
List<User> userList = identityService.createUserQuery().userIds(userCodes).list();
Map<String, String> activityIdUserNames = getApplyers(processInstanceId, userList, taskInstanceMap);
if (CollectionUtils.isNotEmpty(userTasks)) {
userTasks.forEach(activityInstance -> {
FlowNodeDto node = new FlowNodeDto();
node.setNodeId(activityInstance.getActivityId());
node.setNodeName(activityInstance.getActivityName());
node.setEndTime(activityInstance.getEndTime());
node.setUserName(activityIdUserNames.get(activityInstance.getActivityId()));
backNodes.add(node);
});
}
//组装会签节点数据
if (MapUtils.isNotEmpty(taskInstanceMap)) {
parallelGatewayUserTasks.forEach((activity, activities) -> {
FlowNodeDto node = new FlowNodeDto();
node.setNodeId(activity.getActivityId());
node.setEndTime(activity.getEndTime());
StringBuffer nodeNames = new StringBuffer("会签:");
StringBuffer userNames = new StringBuffer("审批人员:");
if (CollectionUtils.isNotEmpty(activities)) {
activities.forEach(activityInstance -> {
nodeNames.append(activityInstance.getActivityName()).append(",");
userNames.append(activityIdUserNames.get(activityInstance.getActivityId())).append(",");
});
node.setNodeName(nodeNames.toString());
node.setUserName(userNames.toString());
backNodes.add(node);
}
});
}
//去重合并
List<FlowNodeDto> flowNodeDtos = backNodes.stream().collect(
Collectors.collectingAndThen(Collectors.toCollection(() ->
new TreeSet<>(Comparator.comparing(FlowNodeDto::getNodeId))), ArrayList::new));
//排序
flowNodeDtos.sort(Comparator.comparing(FlowNodeDto::getEndTime).reversed());
return flowNodeDtos;
}
FlowNodeDto:
@Getter
@Setter
public class FlowNodeDto implements Serializable {
private static final long serialVersionUID = 1L;
private String nodeId;
protected String nodeName;
protected String activityType;
protected Date endTime;
protected String userName;
}
获取审批人getApplyers:
private Map<String, String> getApplyers(String processInstanceId, List<User> userList, Map<String, List<HistoricTaskInstance>> taskInstanceMap) {
Map<String, User> userMap = userList.stream().collect(Collectors.toMap(User::getId, user -> user));
Map<String, String> applyMap = new HashMap<>();
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
taskInstanceMap.forEach((activityId, taskInstances) -> {
StringBuilder applyers = new StringBuilder();
StringBuffer finalApplyers = new StringBuffer();
taskInstances.forEach(taskInstance -> {
if (!taskInstance.getName().equals(FlowableConstants.FLOW_SUBMITTER)) {
User user = userMap.get(taskInstance.getAssignee());
if (user != null) {
if (StringUtils.indexOf(finalApplyers.toString(), user.getDisplayName()) == -1) {
finalApplyers.append(user.getDisplayName()).append(",");
}
}
} else {
String startUserId = processInstance.getStartUserId();
User user = identityService.createUserQuery().userId(startUserId).singleResult();
if (user != null) {
finalApplyers.append(user.getDisplayName()).append(",");
}
}
});
if (applyers.length() > 0) {
applyers.deleteCharAt(applyers.length() - 1);
}
applyMap.put(activityId, applyers.toString());
});
return applyMap;
}
这样就可以获取到了可驳回的节点信息,那我们就可以根据节点信息进行选择做驳回操作了
后续我们要做的就是驳回操作了:activiti&flowable节点驳回操作