撤回任务:主流程 > 主流程
处室主管【送科长审核】
处室主管【撤回科长审核】
流程日志
撤回任务:子流程 > 子流程
会办接收岗【送处室主管】
会办接收岗【撤回处室主管】
会办接收岗【同意】
撤回任务:子流程 > 主流程
处室主管【送会办】
处室主管【撤回送会办】
处室主管【再次送会办】【接收员同意】
撤回任务:主流程 > 子流程
会办主管【送经办人】
会办主管【撤回经办人】
会办主管【同意】
撤回任务:主流程 > 并行子流程
同时送2个会办,A接收员同意,B主管送经办人
B主管【撤回送经办人】
B主管【同意】
核心代码实现
public class RevokeTaskHandler extends FlowServiceFactory {
@Resource
private ProcessCmdMapper processCmdMapper;
@Resource
private SysDeptMapper deptMapper;
/**
* 检验当前任务是否允许撤回
* @param finishTaskId
*/
public void check(String finishTaskId){
//查询下一个完成任务数量
Integer afterFinishedNum = processCmdMapper.selectCountFinishedAfterTaskId(finishTaskId);
if(afterFinishedNum>0){
log.debug("taskId = {}, afterFinishedNum = {}", finishTaskId, afterFinishedNum);
throw new RuntimeException("子任务已被处理,不允许撤回!");
}
}
//查询撤回任务分组
public List<RevokeBtnBo> findRevokeBtnList(String procInstId){
List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().processInstanceId(procInstId).taskWithoutDeleteReason().orderByTaskCreateTime().asc().finished().list();
Map<String, List<BtnItem>> map = new HashMap<>();
Map<String, List<HistoricTaskInstance>> instMap = list.stream().collect(Collectors.groupingBy(t -> t.getScopeType()+":"+t.getScopeId()));
String zbKey = null;
List<String> keyList = new ArrayList<>();
for(String key : instMap.keySet()){
List<HistoricTaskInstance> instanceList = instMap.get(key);
List<BtnItem> items = new ArrayList<>();
for(HistoricTaskInstance bean : instanceList){
BtnItem item = new BtnItem();
item.setTaskId(bean.getId());
item.setTaskName(bean.getName());
item.setParentTaskId(bean.getParentTaskId());
item.setProcInstId(bean.getProcessInstanceId());
items.add(item);
}
if(key.startsWith("zb")){
zbKey = key;
}
map.put(key, items);
if(!keyList.contains(key) && !"null:null".equals(key)){
keyList.add(key);
}
}
List<BtnItem> zbList = null;
List<BtnItem> nullList = map.remove("null:null");
if(nullList!=null){
zbList = map.get(zbKey);
if(zbList!=null){
zbList.addAll(0, nullList);
}
}
List<RevokeBtnBo> btnBos = new ArrayList<>();
for(String key : keyList){
RevokeBtnBo bo = new RevokeBtnBo();
bo.setKey(key);
String[] split = key.split(":");
String deptType = split[0];
String deptId = split[1];
String type = "hb".equals(deptType) ? "会办流程" : "主办流程";
if(StrUtil.isNotBlank(deptId)){
SysDept sysDept = deptMapper.selectById(deptId);
String deptName = sysDept.getDeptName();
bo.setName(String.format("%s > %s", type, deptName));
}else {
bo.setName(String.format("%s", type));
}
bo.setBthList(map.get(key));
btnBos.add(bo);
}
//只保留会办流程:合并主流程
Map<String, String> nameMap = btnBos.stream().collect(Collectors.toMap(RevokeBtnBo::getKey, RevokeBtnBo::getName));
Map<String, List<BtnItem>> boMap = btnBos.stream().collect(Collectors.toMap(RevokeBtnBo::getKey, RevokeBtnBo::getBthList));
List<RevokeBtnBo> boList = new ArrayList<>();
for(String key : keyList){
if(key.startsWith("hb")){
RevokeBtnBo bo = new RevokeBtnBo();
bo.setKey(key);
bo.setName(nameMap.get(key));
List<BtnItem> hbList = boMap.get(key);
String parentTaskId = hbList.get(0).getParentTaskId();
int index = 0;
List<BtnItem> zbItemList = BeanUtil.copyToList(boMap.get(zbKey), BtnItem.class);
for (int i = 0; i < zbItemList.size(); i++) {
BtnItem item = zbItemList.get(i);
if(parentTaskId.equals(item.getTaskId())){
index = i;
break;
}
}
zbItemList.addAll(index+1, hbList);
bo.setBthList(zbItemList);
boList.add(bo);
}
}
if(boList.size()>0){
return boList;
}else {
return btnBos;
}
}
public void handle(String finishTaskId, String targetTaskId, String comment){
//查询被撤回的已办任务
HistoricTaskInstance targetTask = historyService.createHistoricTaskInstanceQuery().taskId(targetTaskId).singleResult();
//找到所有需要撤回的任务id(包括主子流程)
Set<String> revokeTaskIds = new HashSet<>();
List<RevokeBtnBo> revokeBtnList = findRevokeBtnList(targetTask.getProcessInstanceId());
for(RevokeBtnBo bo : revokeBtnList){
boolean find = false;
List<BtnItem> bthList = bo.getBthList();
for (BtnItem btnItem : bthList){
String taskId = btnItem.getTaskId();
if(!find){
find = targetTaskId.equals(taskId);
}
if(find){
revokeTaskIds.add(taskId);
}
}
}
log.info("revokeTaskIds = {}", revokeTaskIds);
//判断目标节点是否在主流程中
List<FlowNode> targetNodes = findFlowNodesInMainProcess(targetTask.getProcessDefinitionId(), targetTask.getTaskDefinitionKey());
//查询下级待办任务列表
List<ProcessTodoTask> todoTaskList = listTodoTask(finishTaskId);
if(todoTaskList!=null && todoTaskList.size()>0){
for(ProcessTodoTask task : todoTaskList){
//发送撤回消息
pushMessageAddComment(task, comment);
}
ProcessTodoTask todoTask = todoTaskList.get(0);
if(targetNodes.size()==0) {
//目标节点在子流程内,即撤回到子流程内
RevokeType type = todoTask.isSubProcess() ? RevokeType.sub2sub : RevokeType.main2sub;
revokeTodoTask(todoTask, targetTask, type);
}else {
//目标节点在主流程内,即撤回到主流程内
RevokeType type = todoTask.isSubProcess() ? RevokeType.sub2main : RevokeType.main2main;
revokeTodoTask(todoTask, targetTask, type);
}
}else {
throw new RuntimeException("未找到待办任务,不支持撤回!");
}
//撤回节点(任务、流程线、网关等逻辑删除)
for(String taskId : revokeTaskIds){
Integer taskNum = processCmdMapper.updateRevokeTask(taskId);
Integer actNum = processCmdMapper.updateRevokeAct(taskId);
log.info("success revokeTask = {}, revokeAct = {}", taskNum, actNum);
}
}
public void revokeTodoTask(ProcessTodoTask todoTask, HistoricTaskInstance targetTask, RevokeType type) {
String targetName = targetTask.getName();
String targetKey = targetTask.getTaskDefinitionKey();
String instanceId = targetTask.getProcessInstanceId();
if (type == RevokeType.main2main) {
log.info(">>>> 主流程内任务撤回 {} > {}", todoTask.getTaskName(), targetName);
revokeExecutionId(todoTask.getExecutionId(), targetKey);
//更新待办任务类型参数
updateTodoTask(targetTask);
} else if (type == RevokeType.sub2main) {
log.info(">>>> 子流程撤回到主流程 {} > {}", todoTask.getTaskName(), targetName);
revokeExecutionId(todoTask.getExecutionId(), targetKey);
//更新待办任务类型参数
updateTodoTask(targetTask);
} else if (type == RevokeType.main2sub) {
log.info(">>>> 主流程撤回到子流程 {} > {}", todoTask.getTaskName(), targetName);
String scopeType = targetTask.getScopeType();
//查询会办子流程中待办任务数量
Integer number = processCmdMapper.selectCountByScopeType(instanceId, scopeType);
if(number>0){
//普通撤回
revokeExecutionId(todoTask.getExecutionId(), targetKey);
//更新待办任务类型参数
updateTodoTask(targetTask);
}else {
//撤回多个活动
revokeProcessInstanceId(instanceId, targetKey);
//更新待办任务类型参数
updateTodoTask(targetTask);
List<Execution> childList = runtimeService.createExecutionQuery().parentId(instanceId).list();
if(childList!=null && childList.size()==1){
//还原子流程活动实例
ExecutionEntityImpl root = (ExecutionEntityImpl) runtimeService.createExecutionQuery().executionId(instanceId).singleResult();
ExecutionEntityImpl child = (ExecutionEntityImpl) childList.get(0);
Map<String, Object> varMap = this.getInstancesVariables(instanceId);
managementService.executeCommand(new AddChildExecutionCmd(root, child, varMap));
}
}
} else if (type == RevokeType.sub2sub) {
log.info(">>>> 子流程内任务撤回 {} > {}", todoTask.getTaskName(), targetKey);
revokeActivityId(instanceId, todoTask.getTaskKey(), targetKey);
//更新待办任务类型参数
updateTodoTask(targetTask);
} else {
throw new RuntimeException("不支持撤回类型!");
}
}
//更新待办任务,参数与撤回前参数保持一致
private void updateTodoTask(HistoricTaskInstance targetTask){
String targetKey = targetTask.getTaskDefinitionKey();
List<Task> list = taskService.createTaskQuery().processInstanceId(targetTask.getProcessInstanceId()).taskDefinitionKey(targetKey).orderByTaskCreateTime().desc().list();
if(list!=null && list.size()>0){
Task task = list.get(0);
String taskId = task.getId();
String parentTaskId = targetTask.getParentTaskId();
String scopeId = targetTask.getScopeId();
String scopeType = targetTask.getScopeType();
taskService.setAssignee(taskId, targetTask.getAssignee());
if(scopeType!=null){
processCmdMapper.updateRuTaskScopeInfo(taskId, scopeId, scopeType);
processCmdMapper.updateHiTaskScopeInfo(taskId, scopeId, scopeType);
}
if(StrUtil.isNotBlank(parentTaskId)){
processCmdMapper.updateRuParentTaskId(taskId, parentTaskId);
processCmdMapper.updateHiParentTaskId(taskId, parentTaskId);
}
}
}
//查询下一级待办任务
private List<ProcessTodoTask> listTodoTask(String finishTaskId){
List<HistoricTaskInstance> todoList = historyService.createHistoricTaskInstanceQuery().taskParentTaskId(finishTaskId).unfinished().list();
List<ProcessTodoTask> todoTaskList = new ArrayList<>();
for(HistoricTaskInstance task : todoList){
todoTaskList.add(build(task));
}
return todoTaskList;
}
private ProcessTodoTask build(HistoricTaskInstance task){
ProcessTodoTask bean = new ProcessTodoTask();
bean.setTaskId(task.getId());
bean.setTaskName(task.getName());
bean.setTaskKey(task.getTaskDefinitionKey());
bean.setProcInstId(task.getProcessInstanceId());
bean.setExecutionId(task.getExecutionId());
String targetKey = task.getTaskDefinitionKey();
List<FlowNode> todoNodes = findFlowNodesInMainProcess(task.getProcessDefinitionId(), targetKey);
bean.setSubProcess(todoNodes.size()==0);
return bean;
}
//撤回已经办理的任务
private void revokeActivityId(String procInstId, String todoTaskKey, String targetKey){
runtimeService.createChangeActivityStateBuilder().processInstanceId(procInstId).moveActivityIdTo(todoTaskKey, targetKey).changeState();
}
//撤回已经办理的任务
private void revokeProcessInstanceId(String procInstId, String targetKey){
List<String> executionIds = new ArrayList<>();
List<Execution> executions = runtimeService.createExecutionQuery().parentId(procInstId).list();
executions.forEach(execution -> executionIds.add(execution.getId()));
if(executionIds.size()>0){
runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, targetKey).changeState();
}
}
//撤回已经办理的任务
private void revokeExecutionId(String executionId, String targetKey){
runtimeService.createChangeActivityStateBuilder().moveExecutionToActivityId(executionId, targetKey).changeState();
}
//添加撤回意见并给用户发送消息
private void pushMessageAddComment(ProcessTodoTask todoTask, String comment){
if (StrUtil.isBlank(comment)) {
comment = "撤回意见:无";
}else {
if(!comment.contains("撤回意见")){
comment = String.format("撤回意见:%s", comment);
}
}
// 设置办理人为当前登录人
taskService.setOwner(todoTask.getTaskId(), sessionService.getUserId());
//添加驳回意见
taskService.addComment(todoTask.getTaskId(), todoTask.getProcInstId(), CommentType.revoke.name(), comment);
//给撤回的子任务发送消息通知
messageHandler.doHandle(SysMsgType.revoke.name(), todoTask.getTaskId(), todoTask.getTaskName());
}
private Map<String, Object> getInstancesVariables(String procInstId) {
Integer nrOfInstances = 0, nrOfActive = 0, nrOfCompleted = 0;
//查询所有活动ids
List<RuExecutionItem> ruExecutionList = processCmdMapper.selectRuExecutionIdList(procInstId);
for(RuExecutionItem item : ruExecutionList){
Map<String, Object> map = getExecutionInstancesVariables(item.getId());
Integer instances = (Integer) map.get("nrOfInstances");
Integer active = (Integer) map.get("nrOfActiveInstances");
Integer completed = (Integer) map.get("nrOfCompletedInstances");
if(instances!=null){
nrOfInstances += instances;
}
if(active!=null){
nrOfActive += active;
}
if(completed!=null){
nrOfCompleted += completed;
}
}
Map<String, Object> example = new HashMap<>();
example.put("nrOfInstances", nrOfInstances);
example.put("nrOfActiveInstances", nrOfActive);
example.put("nrOfCompletedInstances", nrOfCompleted);
return example;
}
}
/**
* 流程任务自定义接口
*
* @author: mx
* @date: 2024-08-08 14:59
*/
@Mapper
public interface ProcessCmdMapper {
//查询taskId之后的已完成任务(如果存在则不可以撤回)
@Select("select count(*) from act_hi_taskinst where PARENT_TASK_ID_=#{taskId} and END_TIME_ is not null and DELETE_REASON_ is null")
Integer selectCountFinishedAfterTaskId(String taskId);
//查询taskId之后的未完成任务(如果存在则可以撤回)
@Select("select count(*) from act_hi_taskinst where PARENT_TASK_ID_=#{taskId} and END_TIME_ is null and DELETE_REASON_ is null")
Integer selectCountUnfinishedAfterTaskId(String taskId);
//查询会办分组下待办任务数量
@Select("select count(*) from act_hi_taskinst where PROC_INST_ID_=#{procInstId} and SCOPE_TYPE_=#{scopeType} and END_TIME_ is null and DELETE_REASON_ is null")
Integer selectCountByScopeType(String procInstId, String scopeType);
//查询流程任务中第1个targetKey对应完成的节点
TaskItem selectTaskItem(String procInstId, String targetKey);
//查询已完成任务节点
List<TaskItem> selectFinishTaskListByRevoke(String procInstId);
//查询procInstId节点(重复节点取最新一条记录)
List<ActItem> selectActInstList(String procInstId);
@Select("select GROUP_ID_ from act_hi_identitylink where TASK_ID_=#{taskId} and TYPE_='candidate' ")
String selectRoleBYTaskId(String taskId);
//查询节点出现数量
@Select("select ACT_ID_, count(1) as number from act_hi_actinst where PROC_INST_ID_=#{procInstId} and (DELETE_REASON_ is null or DELETE_REASON_ not in('MI_END')) and ACT_TYPE_='userTask' group by ACT_ID_")
List<ActIdNum> selectActIdNumList(String procInstId);
//查询当前任务key
@Select("select TASK_DEF_KEY_ from act_hi_taskinst where ID_=#{taskId}")
String selectTaskKey(String taskId);
//更新任务描述(自动完成日志说明)
@Update("update act_hi_taskinst set DESCRIPTION_=#{message} where ID_=#{taskId}")
Integer updateTaskDescription(String taskId, String message);
//更新任务类型(scopeId为部门ID, SCOPE_TYPE_为任务类型)
@Update("update act_hi_taskinst set SCOPE_ID_=#{scopeId}, SCOPE_TYPE_=#{scopeType} where ID_=#{taskId}")
Integer updateHiTaskScopeInfo(String taskId, String scopeId, String scopeType);
//更新任务类型(scopeId为部门ID, SCOPE_TYPE_为任务类型)
@Update("update act_ru_task set SCOPE_ID_=#{scopeId}, SCOPE_TYPE_=#{scopeType} where ID_=#{taskId}")
Integer updateRuTaskScopeInfo(String taskId, String scopeId, String scopeType);
//更新任务对应的上个完成任务id
@Update("update act_hi_taskinst set PARENT_TASK_ID_=#{parentTaskId} where ID_=#{taskId} and PARENT_TASK_ID_ is null")
Integer updateHiParentTaskId(String taskId, String parentTaskId);
//更新任务对应的上个完成任务id
@Update("update act_ru_task set PARENT_TASK_ID_=#{parentTaskId} where ID_=#{taskId} and PARENT_TASK_ID_ is null")
Integer updateRuParentTaskId(String taskId, String parentTaskId);
//更新节点对应的上个完成任务id and TASK_ID_ is null
@Update("update act_hi_actinst set TASK_ID_=#{finishTaskId} where PROC_INST_ID_=#{procInstId} and START_TIME_>#{endTime}")
Integer updateHiActTaskId(String procInstId, String finishTaskId, Date endTime);
//更新节点对应的上个完成任务id
@Update("update act_ru_actinst set TASK_ID_=#{finishTaskId} where PROC_INST_ID_=#{procInstId} and TASK_ID_ is null and START_TIME_>#{endTime}")
Integer updateRuActTaskId(String procInstId, String finishTaskId, Date endTime);
//查询公文ID
@Select("select BUSINESS_KEY_ from act_hi_procinst where PROC_INST_ID_=#{procInstId}")
String selectBusinessKey(String procInstId);
//查询所有公文相关的procInstId
@Select("select PROC_INST_ID_ from act_hi_procinst where BUSINESS_KEY_=#{businessKey} order by START_TIME_ asc")
List<String> selectProcInstIdList(String businessKey);
//查询procInstId对应的活动idList
@Select("select ID_, ACT_ID_, IS_ACTIVE_, IS_SCOPE_ from act_ru_execution where PROC_INST_ID_=#{procInstId} and VAR_COUNT_>0 and IS_ACTIVE_=1")
List<RuExecutionItem> selectRuExecutionIdList(String procInstId);
//撤回已完成历史任务
@Update("update act_hi_taskinst set DELETE_REASON_='撤回' where PARENT_TASK_ID_=#{finishTaskId} or ID_=#{finishTaskId}")
Integer updateRevokeTask(String finishTaskId);
//撤回已完成历史节点
@Update("update act_hi_actinst set DELETE_REASON_='撤回' where TASK_ID_=#{finishTaskId}")
Integer updateRevokeAct(String finishTaskId);
}