flowable已结束的流程,如何复活到指定节点,然后继续审批
参考
书籍《深入Flowable流程》,作者贺波、刘晓鹏、胡海琴
第27章 27.1 流程复活
实现
包含3个类
1、RestartProcessInstanceCmd :实现cmd命令,为流程复活的核心
2、BpmRestartProcessInstanceService:封装的退回service
3、ProcessInstanceServiceImpl:该类因为调用样例
1 、RestartProcessInstanceCmd
流程复活的核心代码
package com.flowable.core.cmd;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableIllegalArgumentException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.HistoricProcessInstanceEntityImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.flowable.variable.service.impl.persistence.entity.HistoricVariableInstanceEntityImpl;
import org.flowable.variable.service.impl.persistence.entity.HistoricVariableInstanceEntityManager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@AllArgsConstructor
public class RestartProcessInstanceCmd implements Command<ProcessInstance> {
// 待复活流程实例编号
protected String processInstanceId;
// 要复活到的节点列表
protected List<String> activityIds;
@Override
public ProcessInstance execute(CommandContext commandContext) {
ProcessEngineConfiguration procEngineConf = CommandContextUtil.getProcessEngineConfiguration(commandContext);
HistoryService historyService = procEngineConf.getHistoryService();
// 校验和查询历史流程实例
HistoricProcessInstance hisProcInst = checkAndGetHistoricProcessInstance(historyService);
// 校验待复活节点
checkActivityIds(hisProcInst.getProcessDefinitionId());
// 校验流程定义
ProcessDefinition processDefinition = ProcessDefinitionUtil.getProcessDefinition(hisProcInst.getProcessDefinitionId());
if (processDefinition == null) {
throw new FlowableException("流程定义不存在");
}
// 获取流程启动节点
Process process = ProcessDefinitionUtil.getProcess(processDefinition.getId());
StartEvent initialFlowElement = (StartEvent) process.getInitialFlowElement();
// 重建运行时流程实例的主执行实例
ExecutionEntity processInstance = reCreateProcessInstance(processDefinition, initialFlowElement, hisProcInst);
// 创建子执行实例
ExecutionEntity childExecution = CommandContextUtil.getExecutionEntityManager().createChildExecution(processInstance);
childExecution.setCurrentFlowElement(initialFlowElement);
HistoricVariableInstanceEntityManager historicVariableInstanceEntityManager = CommandContextUtil.getProcessEngineConfiguration()
.getVariableServiceConfiguration().getHistoricVariableInstanceEntityManager();
// 收集待复活的流程变量
Map<String, Object> varMap = collectVariables(historyService, hisProcInst, historicVariableInstanceEntityManager);
// 如果自定义了流程状态,可自行修改
// varMap.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, ProcessInstanceStatusEnum.RUNNING.getCode());
// 设置历史流程实例结束节点和结束时间为空
((HistoricProcessInstanceEntityImpl) hisProcInst).setEndActivityId(null);
((HistoricProcessInstanceEntityImpl) hisProcInst).setEndTime(null);
// 执行动态跳转
procEngineConf.getRuntimeService()
.createChangeActivityStateBuilder()
.processInstanceId(processInstance.getProcessInstanceId())
.moveSingleExecutionToActivityIds(childExecution.getId(), activityIds)
.processVariables(varMap)
.changeState();
return procEngineConf.getRuntimeService().createProcessInstanceQuery()
.processInstanceId(processInstance.getId()).singleResult();
}
/**
* 校验和查询历史流程实例
*
* @param historyService 流程引擎历史服务
* @return 历史任务
*/
private HistoricProcessInstance checkAndGetHistoricProcessInstance(HistoryService historyService) {
if (StrUtil.isBlank(processInstanceId)) {
throw new FlowableIllegalArgumentException("processInstanceId不能为空");
}
HistoricProcessInstance hisProcInst = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(processInstanceId).singleResult();
if (hisProcInst == null) {
throw new FlowableException("id为" + processInstanceId + "的流程实例不存在");
} else if (hisProcInst.getEndTime() == null) {
throw new FlowableException("id为" + processInstanceId + "的流程实例没有结束");
}
return hisProcInst;
}
/**
* 校验复活节点
*
* @param processDefinitionId 流程实例id
*/
private void checkActivityIds(String processDefinitionId) {
if (CollUtil.isEmpty(activityIds)) {
throw new FlowableIllegalArgumentException("activityIds不能为空");
}
BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionId);
List<String> notExistedFlowElements = new ArrayList<>();
for (String activityId : activityIds) {
if (!bpmnModel.getMainProcess().containsFlowElementId(activityId)) {
notExistedFlowElements.add(activityId);
}
}
if (!CollUtil.isEmpty(notExistedFlowElements)) {
throw new FlowableIllegalArgumentException("Id为" + String.join("、", notExistedFlowElements) + "节点不存在");
}
}
/**
* 重建流程实例: ExecutionEntity 继承自 ProcessInstance
*
* @param processDefinition 流程定义
* @param initialFlowElement 发起节点
* @param hisProcInst 流程历史
* @return 流程实例
*/
private ExecutionEntity reCreateProcessInstance(ProcessDefinition processDefinition, StartEvent initialFlowElement, HistoricProcessInstance hisProcInst) {
// 创建流程实例
ExecutionEntity procInst = CommandContextUtil.getExecutionEntityManager().createProcessInstanceExecution(processDefinition, hisProcInst.getId(),
hisProcInst.getBusinessKey(), hisProcInst.getBusinessStatus(),
hisProcInst.getName(), hisProcInst.getCallbackId(),
hisProcInst.getCallbackType(), hisProcInst.getReferenceId(),
hisProcInst.getReferenceType(),
hisProcInst.getPropagatedStageInstanceId(),
hisProcInst.getTenantId(), initialFlowElement.getInitiator(),
hisProcInst.getStartActivityId());
// 设置流程实例为活跃状态
procInst.setActive(true);
// 重设流程开始事件
procInst.setStartTime(hisProcInst.getStartTime());
// 设置流程的发起人
procInst.setStartUserId(hisProcInst.getStartUserId());
return procInst;
}
/**
* 收集待复活的流程变量
*
* @param historyService 历史服务
* @param hisProcInst 历史实力哦
* @return 变量
*/
private Map<String, Object> collectVariables(HistoryService historyService,
HistoricProcessInstance hisProcInst,
HistoricVariableInstanceEntityManager historicVariableInstanceEntityManager) {
Map<String, Object> varMap = new HashMap<>();
// 1 查询历史流程变量
List<HistoricVariableInstance> hisVariables = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(hisProcInst.getId())
.executionId(hisProcInst.getId()).list();
for (HistoricVariableInstance hisVariable : hisVariables) {
// 2 收集变量
varMap.put(hisVariable.getVariableName(), hisVariable.getValue());
// 3 删除历史变量(防止历史数据存在2份-可选)
historicVariableInstanceEntityManager.delete((HistoricVariableInstanceEntityImpl) hisVariable);
}
return varMap;
}
}
2 、BpmRestartProcessInstanceService
调用核心代码,为必要的service实现
package com.flowable.core.service;
import com.flowable.core.cmd.RestartProcessInstanceCmd;
import jakarta.annotation.Resource;
import org.flowable.engine.ManagementService;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BpmRestartProcessInstanceService {
@Resource
private ManagementService managementService;
public void executeRestart(String processInstanceId, List<String> activityIds) {
// 实例化流程复活command 类
RestartProcessInstanceCmd restartProcessInstanceCmd = new RestartProcessInstanceCmd(processInstanceId, activityIds);
// 通过 managementService 管理服务,执行流程复活command类
managementService.executeCommand(restartProcessInstanceCmd);
}
}
3 、ProcessInstanceServiceImpl
调用流程复活样例
package com.flowable.process.service.impl;
@Slf4j
@Service
public class ProcessInstanceServiceImpl implements ProcessInstanceService {
@Resource
private TaskService taskService;
@Resource
private BpmRestartProcessInstanceService bpmRestartProcessInstanceService;
@Override
public void restartProcessInstance(Long userId, ProcessInstanceRestartParam param) {
// 1 执行复活到指定节点
bpmRestartProcessInstanceService.executeRestart(param.getId(), param.getTaskDefinitionKeys());
// 2 添加任务复活评论
// taskService.addComment(null, param.getId(), ProcessCommentTypeEnum.REACTIVATE.getType()
, ProcessCommentTypeEnum.REACTIVATE.formatComment(user.getNickName(), param.getReason()));
}
}
#-----------------------------------------------------------------------------
package com.flowable.process.entity.param;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
/**
* 管理后台 - 流程实例 请求参数
*/
@Data
public class ProcessInstanceRestartParam {
/**
* 任务编号
*/
@NotEmpty(message = "流程实例编号不能为空")
private String id;
/**
* 送办的节点 Key
*/
@NotNull(message = "回退节点不能为空")
private List<String> taskDefinitionKeys;
/**
* 送办原因
*/
@NotEmpty(message = "复活原因不能为空")
private String reason;
}