Flowable之任务撤回(支持主流程、子流程相互撤回)

撤回任务:主流程 > 主流程

处室主管【送科长审核】

处室主管【撤回科长审核】

流程日志

撤回任务:子流程 > 子流程

会办接收岗【送处室主管】

会办接收岗【撤回处室主管】

会办接收岗【同意】

撤回任务:子流程 > 主流程

处室主管【送会办】

处室主管【撤回送会办】

处室主管【再次送会办】【接收员同意】

撤回任务:主流程 > 子流程

会办主管【送经办人】

会办主管【撤回经办人】

会办主管【同意】

撤回任务:主流程 > 并行子流程

同时送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);
}

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星梦天河

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值