package com.mossle.bpm.cmd;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.mossle.api.humantask.HumanTaskConnector;
import com.mossle.api.humantask.HumanTaskConstants;
import com.mossle.api.humantask.HumanTaskDTO;
import com.mossle.bpm.graph.ActivitiHistoryGraphBuilder;
import com.mossle.bpm.graph.Edge;
import com.mossle.bpm.graph.Graph;
import com.mossle.bpm.graph.Node;
import com.mossle.bpm.support.HumanTaskBuilder;
import com.mossle.bpm.support.JumpInfo;
import com.mossle.core.spring.ApplicationContextHelper;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.HistoricActivityInstanceQueryImpl;
import org.activiti.engine.impl.Page;
import org.activiti.engine.impl.cmd.GetDeploymentProcessDefinitionCmd;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.HistoricActivityInstanceEntity;
import org.activiti.engine.impl.persistence.entity.HistoricTaskInstanceEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* 退回.
*/
public class RollbackCmd implements Command<Boolean> {
private static Logger logger = LoggerFactory.getLogger(RollbackCmd.class);
public static final String ACTIVITY_PREVIOUS = "ACTIVITY_PREVIOUS";
public static final String ASSIGNEE_LAST = "ASSIGNEE_LAST";
public static final String ASSIGNEE_AUTO = "ASSIGNEE_AUTO";
private String taskId;
private String activityId;
private String userId;
private JumpInfo jumpInfo = new JumpInfo();
/**
* 这个taskId是运行阶段task的id.
*/
public RollbackCmd(String taskId, String activityId, String userId) {
this.taskId = taskId;
this.activityId = activityId;
this.userId = userId;
}
public void initSource() {
// source task
this.jumpInfo.setSourceTaskId(this.taskId);
TaskEntity sourceTask = Context.getCommandContext()
.getTaskEntityManager().findTaskById(this.taskId);
this.jumpInfo.setSourceTask(sourceTask);
ProcessDefinitionEntity processDefinitionEntity = Context
.getProcessEngineConfiguration()
.getDeploymentManager()
.findDeployedProcessDefinitionById(
sourceTask.getProcessDefinitionId());
// source activity
this.jumpInfo.setSourceActivityId(sourceTask.getTaskDefinitionKey());
this.jumpInfo.setSourceActivity(processDefinitionEntity
.findActivity(this.jumpInfo.getSourceActivityId()));
HistoricTaskInstanceEntity sourceHistoryTask = Context
.getCommandContext().getHistoricTaskInstanceEntityManager()
.findHistoricTaskInstanceById(this.jumpInfo.getSourceTaskId());
}
/**
* 退回流程.
*
* @return true success, false failure
*/
public Boolean execute(CommandContext commandContext) {
// 初始化当前的起点信息
this.initSource();
// 找到回退的目标节点
String targetActivityId = this.findTargetActivityId();
if (targetActivityId == null) {
logger.info("cannot find targetActivity for : {}", this.taskId);
return Boolean.FALSE;
}
/*
* // 尝试查找最近的上游userTask String historyTaskId = this.findNearestUserTask();
* logger.info("nearest history user task is : {}", historyTaskId);
*
* if (historyTaskId == null) { logger.info("cannot rollback {}", taskId);
*
* return "activity"; }
*/
// 校验这个节点是否可以回退
boolean isValid = this.validateTargetActivity(targetActivityId);
if (!isValid) {
logger.info("cannot rollback for : {} to {}", this.taskId,
targetActivityId);
return Boolean.FALSE;
}
JdbcTemplate jdbcTemplate = ApplicationContextHelper
.getBean(JdbcTemplate.class);
String historyTaskId = jdbcTemplate
.queryForObject(
"select id_ from ACT_HI_TASKINST where act_id_=? order by END_TIME_ desc",
String.class, targetActivityId);
HistoricTaskInstanceEntity historicTaskInstanceEntity = Context
.getCommandContext().getHistoricTaskInstanceEntityManager()
.findHistoricTaskInstanceById(historyTaskId);
HistoricActivityInstanceEntity historicActivityInstanceEntity = getHistoricActivityInstanceEntity(historyTaskId);
// 开始回退
if (this.isSameBranch(historicTaskInstanceEntity)) {
// 如果退回的目标节点的executionId与当前task的executionId一样,说明是同一个分支
// 只删除当前分支的task
this.deleteActiveTask();
} else {
// 否则认为是从分支跳回主干
// 删除所有活动中的task
this.deleteActiveTasks(historicTaskInstanceEntity
.getProcessInstanceId());
// 获得期望退回的节点后面的所有节点历史
List<String> historyNodeIds = new ArrayList<String>();
Graph graph = new ActivitiHistoryGraphBuilder(
historicTaskInstanceEntity.getProcessInstanceId()).build();
Node node = graph.findById(historicActivityInstanceEntity.getId());
this.collectNodes(node, historyNodeIds);
this.deleteHistoryActivities(historyNodeIds);
}
// 恢复期望退回的任务和历史
this.processHistoryTask(historicTaskInstanceEntity,
historicActivityInstanceEntity);
logger.info("activiti is rollback {}",
historicTaskInstanceEntity.getName());
return Boolean.TRUE;
}
/**
* 找到目的地activityId.
*/
public String findTargetActivityId() {
if (ACTIVITY_PREVIOUS.equals(this.activityId)) {
String taskId = this.findNearestUserTask();
TaskEntity taskEntity = Context.getCommandContext()
.getTaskEntityManager().findTaskById(taskId);
return taskEntity.getTaskDefinitionKey();
} else {
return this.activityId;
}
}
/**
* 校验目标节点是否可以回退.
*/
public boolean validateTargetActivity(String targetActivityId) {
JdbcTemplate jdbcTemplate = ApplicationContextHelper
.getBean(JdbcTemplate.class);
String historyTaskId = jdbcTemplate
.queryForObject(
"select id_ from ACT_HI_TASKINST where act_id_=? order by END_TIME_ desc",
String.class, targetActivityId);
// 先找到历史任务
HistoricTaskInstanceEntity historicTaskInstanceEntity = Context
.getCommandContext().getHistoricTaskInstanceEntityManager()
.findHistoricTaskInstanceById(historyTaskId);
// 再反向查找历史任务对应的历史节点
HistoricActivityInstanceEntity historicActivityInstanceEntity = getHistoricActivityInstanceEntity(historyTaskId);
logger.info("historic activity instance is : {}",
historicActivityInstanceEntity.getId());
Graph graph = new ActivitiHistoryGraphBuilder(
historicTaskInstanceEntity.getProcessInstanceId()).build();
Node node = graph.findById(historicActivityInstanceEntity.getId());
if (!this.checkCouldRollback(node)) {
logger.info("cannot rollback {}", taskId);
return false;
}
return true;
}
public boolean isSameBranch(
HistoricTaskInstanceEntity historicTaskInstanceEntity) {
TaskEntity taskEntity = Context.getCommandContext()
.getTaskEntityManager().findTaskById(taskId);
return taskEntity.getExecutionId().equals(
historicTaskInstanceEntity.getExecutionId());
}
public String findNearestUserTask() {
TaskEntity taskEntity = Context.getCommandContext()
.getTaskEntityManager().findTaskById(taskId);
if (taskEntity == null) {
logger.debug("cannot find task : {}", taskId);
return null;
}
Graph graph = new ActivitiHistoryGraphBuilder(
taskEntity.getProcessInstanceId()).build();
JdbcTemplate jdbcTemplate = ApplicationContextHelper
.getBean(JdbcTemplate.class);
String historicActivityInstanceId = jdbcTemplate.queryForObject(
"select id_ from ACT_HI_ACTINST where task_id_=?",
String.class, taskId);
Node node = graph.findById(historicActivityInstanceId);
String previousHistoricActivityInstanceId = this.findIncomingNode(
graph, node);
if (previousHistoricActivityInstanceId == null) {
logger.debug(
"cannot find previous historic activity instance : {}",
taskEntity);
return null;
}
return jdbcTemplate.queryForObject(
"select task_id_ from ACT_HI_ACTINST where id_=?",
String.class, previousHistoricActivityInstanceId);
}
public String findIncomingNode(Graph graph, Node node) {
for (Edge edge : graph.getEdges()) {
Node src = edge.getSrc();
Node dest = edge.getDest();
String srcType = src.getType();
if (!dest.getId().equals(node.getId())) {
continue;
}
if ("userTask".equals(srcType)) {
boolean isSkip = isSkipActivity(src.getId());
if (isSkip) {
return this.findIncomingNode(graph, src);
} else {
return src.getId();
}
} else if (srcType.endsWith("Gateway")) {
return this.findIncomingNode(graph, src);
} else {
logger.info("cannot rollback, previous node is not userTask : "
+ src.getId() + " " + srcType + "(" + src.getName()
+ ")");
return null;
}
}
logger.info("cannot rollback, this : " + node.getId() + " "
+ node.getType() + "(" + node.getName() + ")");
return null;
}
public HistoricActivityInstanceEntity getHistoricActivityInstanceEntity(
String historyTaskId) {
logger.info("historyTaskId : {}", historyTaskId);
JdbcTemplate jdbcTemplate = ApplicationContextHelper
.getBean(JdbcTemplate.class);
String historicActivityInstanceId = jdbcTemplate.queryForObject(
"select id_ from ACT_HI_ACTINST where task_id_=?",
String.class, historyTaskId);
logger.info("historicActivityInstanceId : {}",
historicActivityInstanceId);
HistoricActivityInstanceQueryImpl historicActivityInstanceQueryImpl = new HistoricActivityInstanceQueryImpl();
historicActivityInstanceQueryImpl
.activityInstanceId(historicActivityInstanceId);
HistoricActivityInstanceEntity historicActivityInstanceEntity = (HistoricActivityInstanceEntity) Context
.getCommandContext()
.getHistoricActivityInstanceEntityManager()
.findHistoricActivityInstancesByQueryCriteria(
historicActivityInstanceQueryImpl, new Page(0, 1))
.get(0);
return historicActivityInstanceEntity;
}
public boolean checkCouldRollback(Node node) {
// TODO: 如果是catchEvent,也应该可以退回,到时候再说
for (Edge edge : node.getOutgoingEdges()) {
Node dest = edge.getDest();
String type = dest.getType();
if ("userTask".equals(type)) {
if (!dest.isActive()) {
boolean isSkip = isSkipActivity(dest.getId());
if (isSkip) {
return checkCouldRollback(dest);
} else {
// logger.info("cannot rollback, " + type + "("
// + dest.getName() + ") is complete.");
// return false;
return true;
}
}
} else if (type.endsWith("Gateway")) {
return checkCouldRollback(dest);
} else {
logger.info("cannot rollback, " + type + "(" + dest.getName()
+ ") is complete.");
return false;
}
}
return true;
}
public void deleteActiveTasks(String processInstanceId) {
Context.getCommandContext().getTaskEntityManager()
.deleteTasksByProcessInstanceId(processInstanceId, "退回", false);
JdbcTemplate jdbcTemplate = ApplicationContextHelper
.getBean(JdbcTemplate.class);
List<Map<String, Object>> list = jdbcTemplate
.queryForList(
"select * from ACT_HI_ACTINST where proc_inst_id_=? and end_time_ is null",
processInstanceId);
Date now = new Date();
for (Map<String, Object> map : list) {
Date startTime = (Date) map.get("start_time_");
long duration = now.getTime() - startTime.getTime();
jdbcTemplate
.update("update ACT_HI_ACTINST set end_time_=?,duration_=? where id_=?",
now, duration, map.get("id_"));
}
}
public void collectNodes(Node node, List<String> historyNodeIds) {
logger.info("node : {}, {}, {}", node.getId(), node.getType(),
node.getName());
for (Edge edge : node.getOutgoingEdges()) {
logger.info("edge : {}", edge.getName());
Node dest = edge.getDest();
historyNodeIds.add(dest.getId());
collectNodes(dest, historyNodeIds);
}
}
public void deleteHistoryActivities(List<String> historyNodeIds) {
/*
* JdbcTemplate jdbcTemplate = ApplicationContextHelper .getBean(JdbcTemplate.class);
* logger.info("historyNodeIds : {}", historyNodeIds);
*
* for (String id : historyNodeIds) { jdbcTemplate.update("delete from ACT_HI_ACTINST where id_=?", id); }
*/
}
/**
* 根据任务历史,创建待办任务.
*/
public void processHistoryTask(
HistoricTaskInstanceEntity historicTaskInstanceEntity,
HistoricActivityInstanceEntity historicActivityInstanceEntity) {
/*
* historicTaskInstanceEntity.setEndTime(null); historicTaskInstanceEntity.setDurationInMillis(null);
* historicActivityInstanceEntity.setEndTime(null); historicActivityInstanceEntity.setDurationInMillis(null);
*/
// 创建新任务
TaskEntity task = TaskEntity.create(new Date());
task.setProcessDefinitionId(historicTaskInstanceEntity
.getProcessDefinitionId());
// task.setId(historicTaskInstanceEntity.getId());
// task.setAssigneeWithoutCascade(historicTaskInstanceEntity.getAssignee());
task.setAssigneeWithoutCascade(this.userId);
task.setParentTaskIdWithoutCascade(historicTaskInstanceEntity
.getParentTaskId());
task.setNameWithoutCascade(historicTaskInstanceEntity.getName());
task.setTaskDefinitionKey(historicTaskInstanceEntity
.getTaskDefinitionKey());
task.setExecutionId(historicTaskInstanceEntity.getExecutionId());
task.setPriority(historicTaskInstanceEntity.getPriority());
task.setProcessInstanceId(historicTaskInstanceEntity
.getProcessInstanceId());
task.setExecutionId(historicTaskInstanceEntity.getExecutionId());
task.setDescriptionWithoutCascade(historicTaskInstanceEntity
.getDescription());
task.setTenantId(historicTaskInstanceEntity.getTenantId());
Context.getCommandContext().getTaskEntityManager().insert(task);
// 把流程指向任务对应的节点
ExecutionEntity executionEntity = Context.getCommandContext()
.getExecutionEntityManager()
.findExecutionById(historicTaskInstanceEntity.getExecutionId());
executionEntity
.setActivity(getActivity(historicActivityInstanceEntity));
// 创建HistoricActivityInstance
Context.getCommandContext().getHistoryManager()
.recordActivityStart(executionEntity);
// 创建HistoricTaskInstance
Context.getCommandContext().getHistoryManager()
.recordTaskCreated(task, executionEntity);
Context.getCommandContext().getHistoryManager().recordTaskId(task);
// 更新ACT_HI_ACTIVITY里的assignee字段
Context.getCommandContext().getHistoryManager()
.recordTaskAssignment(task);
try {
// humanTask
this.createHumanTask(task, historicTaskInstanceEntity);
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
}
}
public ActivityImpl getActivity(
HistoricActivityInstanceEntity historicActivityInstanceEntity) {
ProcessDefinitionEntity processDefinitionEntity = new GetDeploymentProcessDefinitionCmd(
historicActivityInstanceEntity.getProcessDefinitionId())
.execute(Context.getCommandContext());
return processDefinitionEntity
.findActivity(historicActivityInstanceEntity.getActivityId());
}
/**
* 删除未完成任务.
*/
public void deleteActiveTask() {
TaskEntity taskEntity = Context.getCommandContext()
.getTaskEntityManager().findTaskById(this.taskId);
Context.getCommandContext().getTaskEntityManager()
.deleteTask(taskEntity, "回退", false);
JdbcTemplate jdbcTemplate = ApplicationContextHelper
.getBean(JdbcTemplate.class);
List<Map<String, Object>> list = jdbcTemplate
.queryForList(
"select * from ACT_HI_ACTINST where task_id_=? and end_time_ is null",
taskId);
Date now = new Date();
for (Map<String, Object> map : list) {
Date startTime = (Date) map.get("start_time_");
long duration = now.getTime() - startTime.getTime();
jdbcTemplate
.update("update ACT_HI_ACTINST set end_time_=?,duration_=? where id_=?",
now, duration, map.get("id_"));
}
// 处理humanTask
HumanTaskConnector humanTaskConnector = ApplicationContextHelper
.getBean(HumanTaskConnector.class);
HumanTaskDTO humanTaskDto = humanTaskConnector
.findHumanTaskByTaskId(this.taskId);
humanTaskDto.setCompleteTime(new Date());
humanTaskDto.setStatus("rollback");
humanTaskConnector.saveHumanTask(humanTaskDto);
}
/**
* 判断跳过节点.
*/
public boolean isSkipActivity(String historyActivityId) {
JdbcTemplate jdbcTemplate = ApplicationContextHelper
.getBean(JdbcTemplate.class);
String historyTaskId = jdbcTemplate.queryForObject(
"select task_id_ from ACT_HI_ACTINST where id_=?",
String.class, historyActivityId);
HistoricTaskInstanceEntity historicTaskInstanceEntity = Context
.getCommandContext().getHistoricTaskInstanceEntityManager()
.findHistoricTaskInstanceById(historyTaskId);
String deleteReason = historicTaskInstanceEntity.getDeleteReason();
return "跳过".equals(deleteReason);
}
public HumanTaskDTO createHumanTask(DelegateTask delegateTask,
HistoricTaskInstanceEntity historicTaskInstanceEntity)
throws Exception {
HumanTaskConnector humanTaskConnector = ApplicationContextHelper
.getBean(HumanTaskConnector.class);
HumanTaskDTO humanTaskDto = new HumanTaskBuilder().setDelegateTask(
delegateTask).build();
if ("发起流程".equals(historicTaskInstanceEntity.getDeleteReason())) {
humanTaskDto.setCatalog(HumanTaskConstants.CATALOG_START);
}
HistoricProcessInstance historicProcessInstance = Context
.getCommandContext()
.getHistoricProcessInstanceEntityManager()
.findHistoricProcessInstance(
delegateTask.getProcessInstanceId());
humanTaskDto
.setProcessStarter(historicProcessInstance.getStartUserId());
humanTaskDto = humanTaskConnector.saveHumanTask(humanTaskDto);
return humanTaskDto;
}
}