1. 参考网上
参考1
activiti 手动回退一个结束的流程
参考2
activiti已结束子流程退回
2. 思路
ExecutionEntity 类实例化需要自己实现,按照ACT_RU_EXECUTION表结构,自定义实体类
act_hi_actinst 中有endEvent节点,需要删除这条记录,也需要自己实现
其他的对照表测试,调整数据
我这里是activiti5,activtiti6 差不多,把TaskRollBackService类中操作的类换成内置的,如 TaskEntityImpl, IdentityLinkEntityImpl,VariableInstanceEntityImpl,HistoricTaskInstanceEntityImpl,HistoricActivityInstanceEntityImpl ,对应TaskRollBackMapper.xml中类型也替换
3. 具体实现
TaskRollBackService类
package com.yl.activiti.service;
import com.yl.activiti.bean.ExecutionInstEntity;
import com.yl.activiti.mapper.TaskRollBackMapper;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricIdentityLink;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.impl.persistence.entity.HistoricTaskInstanceEntity;
import org.activiti.engine.impl.persistence.entity.IdentityLinkEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.persistence.entity.VariableInstanceEntity;
import org.activiti.engine.impl.variable.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
/**
* 实现回退activiti5流程,结束的流程回退任务
*
* @author liuxb
* @date 2023/5/20 23:26
*/
@Slf4j
@Service
public class TaskRollBackService {
@Autowired
private ProcessEngine processEngine;
@Autowired
private RepositoryService repositoryService;
@Autowired
private HistoryService historyService;
@Autowired
private TaskRollBackMapper taskRollBackMapper;
/**
* 结束的流程回退任务
*
* @param taskId
*/
@Transactional(rollbackFor = Exception.class)
public void rollback(String taskId) {
log.info("begin to rollback task: [{}]", taskId);
//1. 查询历史任务实例
HistoricTaskInstance hstTask = processEngine.getHistoryService()
.createHistoricTaskInstanceQuery()
.taskId(taskId)
.singleResult();
if (hstTask == null) {
throw new RuntimeException("任务[" + taskId + "] 不存在");
}
//2. 创建 execution 这里采用自定义对象,activiti5内置对象实例化复杂,存在其他对象引用
FlowElement flowElement = repositoryService.getBpmnModel(hstTask.getProcessDefinitionId())
.getMainProcess()
.getFlowElement(hstTask.getTaskDefinitionKey());
ExecutionInstEntity executionInstEntity = new ExecutionInstEntity();
executionInstEntity.setId(hstTask.getExecutionId());
executionInstEntity.setRevision(1);
executionInstEntity.setProcessInstanceId(hstTask.getProcessInstanceId());
// 可能需要
executionInstEntity.setBusinessKey(null);
executionInstEntity.setProcessDefinitionId(hstTask.getProcessDefinitionId());
executionInstEntity.setActivityId(hstTask.getTaskDefinitionKey());
executionInstEntity.setIsActive(true);
executionInstEntity.setIsConcurrent(false);
// 一般是主流程,如果回退的是有多个分支,含有子流程的,这里可能不是主流程
executionInstEntity.setIsScope(true);
executionInstEntity.setParentId(null);
executionInstEntity.setSuperExecutionId(null);
executionInstEntity.setSuspensionState(1);
executionInstEntity.setCachedEntityState(2);
executionInstEntity.setTenantId(null);
executionInstEntity.setName(null);
log.info("创建一个新的 run execution");
taskRollBackMapper.insertExecution(executionInstEntity);
//3.将历史数据task添加至ACT_RU_TASK
TaskEntity runTask = new TaskEntity();
runTask.setId(hstTask.getId());
runTask.setExecutionId(hstTask.getExecutionId());
runTask.setProcessInstanceId(hstTask.getProcessInstanceId());
runTask.setProcessDefinitionId(hstTask.getProcessDefinitionId());
runTask.setName(hstTask.getName());
runTask.setTaskDefinitionKey(hstTask.getTaskDefinitionKey());
runTask.setAssignee(hstTask.getAssignee());
runTask.setPriority(hstTask.getPriority());
runTask.setCreateTime(hstTask.getCreateTime());
runTask.setSuspensionState(1);
log.info("添加 task[{}] into ACT_RU_TASK", taskId);
taskRollBackMapper.insertTask(runTask);
//4. 恢复当前任务相关处理人到ACT_RUN_IDENTITYLINK
List<HistoricIdentityLink> hstIdentityLinks = historyService.getHistoricIdentityLinksForProcessInstance(hstTask.getProcessInstanceId());
List<IdentityLinkEntity> identityLinkEntities = new ArrayList<>();
for (HistoricIdentityLink historicIdentityLink : hstIdentityLinks) {
IdentityLinkEntity identityLink = new IdentityLinkEntity();
identityLink.setId(historicIdentityLink.getUserId());
identityLink.setType(historicIdentityLink.getType());
identityLink.setUserId(historicIdentityLink.getUserId());
identityLink.setTaskId(historicIdentityLink.getTaskId());
identityLink.setProcessInstanceId(historicIdentityLink.getProcessInstanceId());
identityLinkEntities.add(identityLink);
}
log.info("批量 insert task 相关处理人 into ACT_RUN_IDENTITYLINK");
taskRollBackMapper.bulkInsertIdentityLink(identityLinkEntities);
// 5. 把变量从历史变量表中插入运行变量表中
// insert variables into ACT_RU_VARIABLE 运行时变量表和历史变量表id相同,流程流转时,自动把运行时变量表同步到历史变量表
final List<HistoricVariableInstance> hstVariables = processEngine.getHistoryService()
.createHistoricVariableInstanceQuery()
.processInstanceId(hstTask.getProcessInstanceId())
.executionId(hstTask.getExecutionId())
.list();
List<VariableInstanceEntity> variables = new ArrayList<>();
for (HistoricVariableInstance hstVariable : hstVariables) {
VariableType type;
if (hstVariable.getVariableTypeName().equals("boolean")) {
type = new BooleanType();
} else if (hstVariable.getVariableTypeName().equals("integer")) {
type = new IntegerType();
} else if (hstVariable.getVariableTypeName().equals("short")) {
type = new ShortType();
} else if (hstVariable.getVariableTypeName().equals("long")) {
type = new LongType();
} else if (hstVariable.getVariableTypeName().equals("double")) {
type = new DoubleType();
} else {
type = new StringType(100);
}
VariableInstanceEntity variableInstance = VariableInstanceEntity.create(hstVariable.getVariableName(), type, hstVariable.getValue());
variableInstance.setId(hstVariable.getId());
variableInstance.setTypeName(hstVariable.getVariableTypeName());
variableInstance.setName(hstVariable.getVariableName());
variableInstance.setExecutionId(hstVariable.getProcessInstanceId());
variableInstance.setProcessInstanceId(hstVariable.getProcessInstanceId());
variableInstance.setTaskId(hstVariable.getTaskId());
variableInstance.setValue(hstVariable.getValue());
variables.add(variableInstance);
}
log.info("批量 insert task variables into ACT_RU_VARIABLE");
taskRollBackMapper.bulkInsertVariableInstance(variables);
// 至此数据已经恢复到待办中了,下一步还要回退已办中的数据
// 6. 历史任务结束时间、用时、删除原因 修改为null
HistoricTaskInstanceEntity hstTaskUpdate = new HistoricTaskInstanceEntity();
hstTaskUpdate.setId(hstTask.getId());
hstTaskUpdate.setProcessDefinitionId(hstTask.getProcessDefinitionId());
hstTaskUpdate.setExecutionId(hstTask.getExecutionId());
hstTaskUpdate.setName(hstTask.getName());
hstTaskUpdate.setProcessInstanceId(hstTask.getProcessInstanceId());
hstTaskUpdate.setOwner(hstTask.getOwner());
hstTaskUpdate.setAssignee(hstTask.getAssignee());
hstTaskUpdate.setClaimTime(hstTask.getClaimTime());
hstTaskUpdate.setEndTime(null);
hstTaskUpdate.setDurationInMillis(null);
hstTaskUpdate.setDeleteReason(null);
hstTaskUpdate.setTaskDefinitionKey(hstTask.getTaskDefinitionKey());
hstTaskUpdate.setPriority(hstTask.getPriority());
hstTaskUpdate.setDueDate(hstTask.getDueDate());
hstTaskUpdate.setCategory(hstTask.getCategory());
log.info("history task 结束时间设置为null");
taskRollBackMapper.updateHistoricTaskInstance(hstTaskUpdate);
// 7. 删除历史节点表 节点类型是endEvent的 记录
List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(hstTask.getProcessInstanceId())
.executionId(hstTask.getExecutionId())
.activityType("endEvent")
.list();
log.info("删除 act_hi_actinst 中 endEvent节点");
taskRollBackMapper.deleteHisActInst(historicActivityInstanceList.get(0));
// 8. 修改业务表,修改业务相关的逻辑
// ...
log.info("rollback task: [{}] end", taskId);
}
}
TaskRollBackMapper
package com.yl.activiti.mapper;
import com.yl.activiti.bean.ExecutionInstEntity;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.impl.persistence.entity.HistoricTaskInstanceEntity;
import org.activiti.engine.impl.persistence.entity.IdentityLinkEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.persistence.entity.VariableInstanceEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 实现回退activiti流程,结束的流程回退任务 对activiti5的表操作
* 参考 activiti-engine-5.23.0.jar org.activiti.db.mapping.entity 下XX.xml
*
* @author liuxb
* @date 2023/5/21 14:33
*/
@Mapper
public interface TaskRollBackMapper {
/**
* 添加到ACT_RU_EXECUTION
*
* @param executionInstEntity
* @return
*/
int insertExecution(ExecutionInstEntity executionInstEntity);
/**
* 添加到ACT_RU_TASK
*
* @param taskEntity
* @return
*/
int insertTask(TaskEntity taskEntity);
/**
* 批量添加ACT_RU_IDENTITYLINK
*
* @param identityLinkEntities
* @return
*/
int bulkInsertIdentityLink(@Param("list") List<IdentityLinkEntity> identityLinkEntities);
/**
* 批量插入到ACT_RU_VARIABLE
*
* @param variables
* @return
*/
int bulkInsertVariableInstance(@Param("list") List<VariableInstanceEntity> variables);
/**
* 修改历史任务
*
* @param historicTaskInstanceEntity
* @return
*/
int updateHistoricTaskInstance(HistoricTaskInstanceEntity historicTaskInstanceEntity);
/**
* 删除历史活动实例
*
* @param historicActivityInstance
* @return
*/
int deleteHisActInst(HistoricActivityInstance historicActivityInstance);
}
ExecutionInstEntity 类
package com.yl.activiti.bean;
import lombok.Data;
/**
* 运行时流程实例
* <p> activiti5内置的ExecutionEntity属性存在引用对象,不容易创建,这里自定义
*
* @author liuxb
* @date 2023/5/21 16:10
*/
@Data
public class ExecutionInstEntity {
/**
* id,executionId
*/
private String id;
/**
* 版本号,默认1
*/
private Integer revision;
/**
* 流程实例ID
*/
private String processInstanceId;
/**
* 业务表主键 业务key
*/
private String businessKey;
/**
* 流程定义ID
*/
private String processDefinitionId;
/**
* 当前流程所在的节点ID
*/
private String activityId;
/**
* 是否处于激活状态(0否,1是) 一般为1
*/
private Boolean isActive;
/**
* 是否处于并发状态(0否,1是) 一般为0
*/
private Boolean isConcurrent;
/**
* 是否是主流程实例(0否,1是)一般为1
*/
private Boolean isScope;
/**
* 是否是事件(0否,1是) 一般为0
*/
private Boolean isEventScope;
/**
* 父id
*/
private String parentId;
/**
* 父运行时流程实例id
*/
private String superExecutionId;
/**
* 挂起状态(1正常,2挂起)
*/
private Integer suspensionState;
/**
* 流程实体的缓冲,取值为0~7,无法确定 可设置为2
*/
private Integer cachedEntityState;
/**
* 租户id
*/
private Integer tenantId;
/**
* 名称
*/
private String name;
}
TaskRollBackMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace='com.yl.activiti.mapper.TaskRollBackMapper'>
<insert id="insertExecution" parameterType="com.yl.activiti.bean.ExecutionInstEntity">
insert into ACT_RU_EXECUTION (ID_, REV_, PROC_INST_ID_, BUSINESS_KEY_, PROC_DEF_ID_, ACT_ID_, IS_ACTIVE_, IS_CONCURRENT_, IS_SCOPE_,IS_EVENT_SCOPE_, PARENT_ID_, SUPER_EXEC_, SUSPENSION_STATE_, CACHED_ENT_STATE_, TENANT_ID_, NAME_)
values (
#{id ,jdbcType=VARCHAR},
1,
#{processInstanceId, jdbcType=VARCHAR},
#{businessKey, jdbcType=VARCHAR},
#{processDefinitionId ,jdbcType=VARCHAR},
#{activityId ,jdbcType=VARCHAR},
#{isActive ,jdbcType=BOOLEAN},
#{isConcurrent ,jdbcType=BOOLEAN},
#{isScope ,jdbcType=BOOLEAN},
#{isEventScope ,jdbcType=BOOLEAN},
#{parentId, jdbcType=VARCHAR},
#{superExecutionId, jdbcType=VARCHAR},
#{suspensionState, jdbcType=INTEGER},
#{cachedEntityState, jdbcType=INTEGER},
#{tenantId, jdbcType=VARCHAR},
#{name, jdbcType=VARCHAR}
)
</insert>
<insert id="insertTask" parameterType="org.activiti.engine.impl.persistence.entity.TaskEntity">
insert into ACT_RU_TASK (ID_, REV_, NAME_, PARENT_TASK_ID_, DESCRIPTION_, PRIORITY_, CREATE_TIME_, OWNER_,
ASSIGNEE_, DELEGATION_, EXECUTION_ID_, PROC_INST_ID_, PROC_DEF_ID_, TASK_DEF_KEY_, DUE_DATE_, CATEGORY_, SUSPENSION_STATE_, TENANT_ID_, FORM_KEY_)
values (#{id, jdbcType=VARCHAR},
1,
#{name, jdbcType=VARCHAR},
#{parentTaskId, jdbcType=VARCHAR},
#{description, jdbcType=VARCHAR},
#{priority, jdbcType=INTEGER},
#{createTime, jdbcType=TIMESTAMP},
#{owner, jdbcType=VARCHAR},
#{assignee, jdbcType=VARCHAR},
#{delegationStateString, jdbcType=VARCHAR},
#{executionId, jdbcType=VARCHAR},
#{processInstanceId, jdbcType=VARCHAR},
#{processDefinitionId, jdbcType=VARCHAR},
#{taskDefinitionKey, jdbcType=VARCHAR},
#{dueDate, jdbcType=TIMESTAMP},
#{category, jdbcType=VARCHAR},
#{suspensionState, jdbcType=INTEGER},
#{tenantId, jdbcType=VARCHAR},
#{formKey, jdbcType=VARCHAR}
)
</insert>
<insert id="bulkInsertIdentityLink" parameterType="org.activiti.engine.impl.persistence.entity.IdentityLinkEntity">
insert into ACT_RU_IDENTITYLINK (ID_, REV_, TYPE_, USER_ID_, GROUP_ID_, TASK_ID_, PROC_INST_ID_, PROC_DEF_ID_)
values
<foreach collection="list" item="identityLink" index="index" separator=",">
(#{identityLink.id, jdbcType=VARCHAR},
1,
#{identityLink.type, jdbcType=VARCHAR},
#{identityLink.userId, jdbcType=VARCHAR},
#{identityLink.groupId, jdbcType=VARCHAR},
#{identityLink.taskId, jdbcType=VARCHAR},
#{identityLink.processInstanceId, jdbcType=VARCHAR},
#{identityLink.processDefId, jdbcType=VARCHAR})
</foreach>
</insert>
<insert id="bulkInsertVariableInstance" parameterType="java.util.List">
INSERT INTO ACT_RU_VARIABLE (ID_, REV_,
TYPE_, NAME_, PROC_INST_ID_, EXECUTION_ID_, TASK_ID_, BYTEARRAY_ID_,
DOUBLE_, LONG_ , TEXT_, TEXT2_) VALUES
<foreach collection="list" item="variable" index="index" separator=",">
(#{variable.id, jdbcType=VARCHAR},
1,
#{variable.typeName, jdbcType=VARCHAR },
#{variable.name, jdbcType=VARCHAR},
#{variable.processInstanceId, jdbcType=VARCHAR},
#{variable.executionId, jdbcType=VARCHAR},
#{variable.taskId, jdbcType=VARCHAR},
#{variable.byteArrayRef, typeHandler=org.activiti.engine.impl.persistence.ByteArrayRefTypeHandler},
#{variable.doubleValue, jdbcType=DOUBLE},
#{variable.longValue, jdbcType=BIGINT},
#{variable.textValue, jdbcType=VARCHAR},
#{variable.textValue2, jdbcType=VARCHAR})
</foreach>
</insert>
<update id="updateHistoricTaskInstance" parameterType="org.activiti.engine.impl.persistence.entity.HistoricTaskInstanceEntity">
update ACT_HI_TASKINST set
PROC_DEF_ID_ = #{processDefinitionId, jdbcType=VARCHAR},
EXECUTION_ID_ = #{executionId, jdbcType=VARCHAR},
NAME_ = #{name, jdbcType=VARCHAR},
PARENT_TASK_ID_ = #{parentTaskId, jdbcType=VARCHAR},
DESCRIPTION_ = #{description, jdbcType=VARCHAR},
OWNER_ = #{owner, jdbcType=VARCHAR},
ASSIGNEE_ = #{assignee, jdbcType=VARCHAR},
CLAIM_TIME_ = #{claimTime, jdbcType=TIMESTAMP},
END_TIME_ = #{endTime, jdbcType=TIMESTAMP},
DURATION_ = #{durationInMillis ,jdbcType=BIGINT},
DELETE_REASON_ = #{deleteReason ,jdbcType=VARCHAR},
TASK_DEF_KEY_ = #{taskDefinitionKey ,jdbcType=VARCHAR},
FORM_KEY_ = #{formKey ,jdbcType=VARCHAR},
PRIORITY_ = #{priority, jdbcType=INTEGER},
DUE_DATE_ = #{dueDate, jdbcType=TIMESTAMP},
CATEGORY_ = #{category, jdbcType=VARCHAR}
where ID_ = #{id}
</update>
<delete id="deleteHisActInst">
delete from ACT_HI_ACTINST where
PROC_INST_ID_ = #{processInstanceId} and
EXECUTION_ID_= #{executionId} and
ACT_TYPE_ = #{activityType}
</delete>
</mapper>