package com.mortals.flowable.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.mortals.flowable.common.constant.ProcessConstants;
import com.mortals.flowable.common.enums.FlowComment;
import com.mortals.flowable.factory.FlowServiceFactory;
import com.mortals.flowable.flow.FindNextNodeUtil;
import com.mortals.flowable.flow.FlowableUtils;
import com.mortals.framework.model.PageInfo;
import com.mortals.framework.model.Result;
import com.mortals.xhx.common.code.YesNoEnum;
import com.mortals.xhx.common.pdu.api.ApiRespPdu;
import com.mortals.xhx.common.pdu.flow.*;
import com.mortals.xhx.feign.flowable.IApiFlowInstanceFeign;
import com.mortals.xhx.feign.flowable.IApiFlowTaskFeign;
import lombok.extern.apachecommons.CommonsLog;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.identitylink.api.IdentityLink;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 任务服务类
 *
 * @author: finegirl
 * @date: 2021/8/2 10:50
 * @description:
 **/
@RestController
@CommonsLog
public class FlowTaskServiceImpl extends FlowServiceFactory implements IApiFlowTaskFeign {


    @Override
    public ApiRespPdu<Result<FlowTaskPdu>> myProcess(String userId,String tenantId, PageInfo pageInfo) {
        log.info(String.format("查询我的流程列表！query userId:%s,PageInfo:%s", JSON.toJSONString(userId), JSON.toJSONString(pageInfo)));
        ApiRespPdu<Result<FlowTaskPdu>> rsp = new ApiRespPdu<>();
        Result<FlowTaskPdu> result = new Result<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery()
                    .startedBy(userId)
                    .processInstanceTenantId(tenantId)
                    .orderByProcessInstanceStartTime()
                    .desc()
                    .listPage(pageInfo.getCurrPage() - 1, pageInfo.getPrePageResult());
            pageInfo.setTotalResult(historicProcessInstances.size());
            List<FlowTaskPdu> flowList = new ArrayList<>();
            for (HistoricProcessInstance hisIns : historicProcessInstances) {
                FlowTaskPdu flowTask = new FlowTaskPdu();
                flowTask.setCreateTime(hisIns.getStartTime());
                flowTask.setFinishTime(hisIns.getEndTime());
                flowTask.setProcInsId(hisIns.getId());

                // 计算耗时
                if (Objects.nonNull(hisIns.getEndTime())) {
                    long time = hisIns.getEndTime().getTime() - hisIns.getStartTime().getTime();
                    flowTask.setDuration(getDate(time));
                } else {
                    long time = System.currentTimeMillis() - hisIns.getStartTime().getTime();
                    flowTask.setDuration(getDate(time));
                }
                // 流程定义信息
                ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                        .processDefinitionId(hisIns.getProcessDefinitionId())
                        .processDefinitionTenantId(tenantId)
                        .singleResult();
                flowTask.setDeployId(pd.getDeploymentId());
                flowTask.setProcDefName(pd.getName());
                flowTask.setProcDefVersion(pd.getVersion());
                flowTask.setCategory(pd.getCategory());
                flowTask.setProcDefVersion(pd.getVersion());

                // 当前所处流程
//            Task task = taskService.createTaskQuery().processInstanceId(hisIns.getId()).singleResult();
                List<Task> taskList = taskService.createTaskQuery().processInstanceId(hisIns.getId()).list();
                if (CollectionUtils.isNotEmpty(taskList)) {
                    //初始发起，取第一个task
                    Task task = taskList.get(0);
                    flowTask.setTaskName(task.getName());
                    flowTask.setTaskId(task.getId());
                    // 指定审批人员信息
                    if (StringUtils.isNotBlank(task.getAssignee())) {
                        flowTask.setAssigneeId(task.getAssignee());
                        flowTask.setAssigneeName(task.getAssignee());
                        //flowTask.setDeptName(sysUser.getDept().getDeptName());
                    } else {
                        // 候选审批人员信息
                        List<IdentityLink> linksForTask = taskService.getIdentityLinksForTask(task.getId());
                        StringBuilder stringBuilder = new StringBuilder();
                        for (IdentityLink identityLink : linksForTask) {
                            if ("candidate".equals(identityLink.getType())) {
                                if (StringUtils.isNotBlank(identityLink.getUserId())) {
                                    //SysUser sysUser = sysUserService.selectUserById(Long.parseLong(identityLink.getUserId()));
                                    stringBuilder.append(identityLink.getUserId()).append(",");
                                }
                                if (StringUtils.isNotBlank(identityLink.getGroupId())) {
                                    // SysRole sysRole = sysRoleService.selectRoleById(Long.parseLong(identityLink.getGroupId()));
                                    stringBuilder.append(identityLink.getGroupId()).append(",");
                                }
                            }
                        }
                        if (StringUtils.isNotBlank(stringBuilder)) {
                            flowTask.setCandidate(stringBuilder.substring(0, stringBuilder.length() - 1));
                        }

                    }
                } else {
                    List<HistoricTaskInstance> historicTaskInstance = historyService.createHistoricTaskInstanceQuery()
                            .processInstanceId(hisIns.getId())
                            .orderByHistoricTaskInstanceEndTime().desc().list();
                    flowTask.setTaskId(historicTaskInstance.get(0).getId());
                }
                flowList.add(flowTask);
            }
            result.setPageInfo(pageInfo);
            result.setList(flowList);
            rsp.setData(result);

            log.info(String.format("查询我的流程列表！resp:%s", JSON.toJSONString(rsp)));
            return rsp;
        } catch (Exception e) {
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("查询我的流程列表异常:" + e.getMessage());
            return rsp;
        }
    }

    @Override
    public ApiRespPdu<String> revokeProcess(FlowTaskPdu flowTaskPdu) {
        // todo 未完成
        log.info(String.format("撤回流程！flowTaskPdu:%s", JSON.toJSONString(flowTaskPdu)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());


        Task task = taskService.createTaskQuery().processInstanceId(flowTaskPdu.getProcInsId()).singleResult();
        if (ObjectUtils.isEmpty(task)) {
            log.error("流程未启动或已执行完成，无法撤回");
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("流程未启动或已执行完成，无法撤回");
            return rsp;
        }
        List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .orderByTaskCreateTime()
                .asc()
                .list();
        String myTaskId = null;
        HistoricTaskInstance myTask = null;
        for (HistoricTaskInstance hti : htiList) {
            if (hti.getAssignee().equals(flowTaskPdu.getUserId())) {
                myTaskId = hti.getId();
                myTask = hti;
                break;
            }

        }
        if (ObjectUtils.isEmpty(myTaskId)) {
            log.error("该任务非当前用户提交，无法撤回");
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("该任务非当前用户提交，无法撤回");
            return rsp;
        }

        String processDefinitionId = myTask.getProcessDefinitionId();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

        //变量
//      Map<String, VariableInstance> variables = runtimeService.getVariableInstances(currentTask.getExecutionId());
        String myActivityId = null;
        List<HistoricActivityInstance> haiList = historyService.createHistoricActivityInstanceQuery()
                .executionId(myTask.getExecutionId()).finished().list();
        for (HistoricActivityInstance hai : haiList) {
            if (myTaskId.equals(hai.getTaskId())) {
                myActivityId = hai.getActivityId();
                break;
            }
        }
        FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(myActivityId);

        Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
        String activityId = execution.getActivityId();
        FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);

        //记录原活动方向
        List<SequenceFlow> oriSequenceFlows = new ArrayList<>(flowNode.getOutgoingFlows());

        log.info(String.format("撤回流程！resp:%s", JSON.toJSONString(rsp)));

        return rsp;
    }

    @Override
    public ApiRespPdu<String> stopProcess(FlowTaskVoPdu flowTaskPdu) {
        log.info(String.format("取消流程！flowTaskPdu:%s", JSON.toJSONString(flowTaskPdu)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());

        try {
            List<Task> task = taskService.createTaskQuery().processInstanceId(flowTaskPdu.getInstanceId()).list();
            if (CollectionUtils.isEmpty(task)) {
                log.error("流程未启动或已执行完成，取消申请失败");
                rsp.setCode(YesNoEnum.NO.getValue());
                rsp.setMsg("流程未启动或已执行完成，取消申请失败");
                return rsp;
            }
            ProcessInstance processInstance =
                    runtimeService.createProcessInstanceQuery().processInstanceId(flowTaskPdu.getInstanceId()).singleResult();
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
            if (Objects.nonNull(bpmnModel)) {
                Process process = bpmnModel.getMainProcess();
                List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class, false);
                if (CollectionUtils.isNotEmpty(endNodes)) {
                    Authentication.setAuthenticatedUserId(flowTaskPdu.getUserId());
//                taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.STOP.getType(),
//                        StringUtils.isBlank(flowTaskVo.getComment()) ? "取消申请" : flowTaskVo.getComment());
                    String endId = endNodes.get(0).getId();
                    List<Execution> executions =
                            runtimeService.createExecutionQuery().parentId(processInstance.getProcessInstanceId()).list();
                    List<String> executionIds = new ArrayList<>();
                    executions.forEach(execution -> executionIds.add(execution.getId()));
                    runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds,
                            endId).changeState();
                }
            }
            return rsp;
        } catch (Exception e) {
            log.error("停止异常", e);
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg(e.getMessage());
            return rsp;
        }
    }

    @Override
    public ApiRespPdu<Result<FlowTaskPdu>> todoList(String userId,String tenantId, PageInfo pageInfo) {
        log.info(String.format("查询我的代办列表！query userId:%s,tenantId:%s,PageInfo:%s", JSON.toJSONString(userId),tenantId, JSON.toJSONString(pageInfo)));
        ApiRespPdu<Result<FlowTaskPdu>> rsp = new ApiRespPdu<>();
        Result<FlowTaskPdu> result = new Result<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            TaskQuery taskQuery = taskService.createTaskQuery()
                    .active()
                    .taskTenantId(tenantId)
                    .includeProcessVariables()
                    .taskAssignee(userId)
                    .orderByTaskCreateTime().desc();

            pageInfo.setTotalResult(((Long) taskQuery.count()).intValue());
            List<Task> taskList = taskQuery.listPage(pageInfo.getCurrPage() - 1, pageInfo.getPrePageResult());
            List<FlowTaskPdu> flowList = new ArrayList<>();
            for (Task task : taskList) {
                FlowTaskPdu flowTask = new FlowTaskPdu();
                // 当前流程信息
                flowTask.setTaskId(task.getId());
                flowTask.setTaskDefKey(task.getTaskDefinitionKey());
                flowTask.setCreateTime(task.getCreateTime());
                flowTask.setProcDefId(task.getProcessDefinitionId());
                flowTask.setTaskName(task.getName());
                // 流程定义信息
                ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                        .processDefinitionId(task.getProcessDefinitionId())
                        .singleResult();
                flowTask.setDeployId(pd.getDeploymentId());
                flowTask.setProcDefName(pd.getName());
                flowTask.setProcDefVersion(pd.getVersion());
                flowTask.setProcInsId(task.getProcessInstanceId());

                // 流程发起人信息
                HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                        .processInstanceId(task.getProcessInstanceId())
                        .singleResult();
                flowTask.setStartUserId(historicProcessInstance.getStartUserId());
                flowTask.setStartUserName(historicProcessInstance.getStartUserId());
                flowList.add(flowTask);
            }
            result.setList(flowList);
            result.setPageInfo(pageInfo);
            rsp.setData(result);
            log.info(String.format("查询我的代办列表！resp:%s", JSON.toJSONString(rsp)));
            return rsp;
        } catch (Exception e) {
            log.error("异常：", e);
            rsp.setCode(YesNoEnum.NO.getValue());
            return rsp;
        }
    }

    @Override
    public ApiRespPdu<Result<FlowTaskPdu>> finishedList(String userId,String tenantId, PageInfo pageInfo) {
        log.info(String.format("查询我的已办列表！query userId:%s,tenantId:%s,PageInfo:%s", JSON.toJSONString(userId),tenantId, JSON.toJSONString(pageInfo)));
        ApiRespPdu<Result<FlowTaskPdu>> rsp = new ApiRespPdu<>();
        Result<FlowTaskPdu> result = new Result<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            HistoricTaskInstanceQuery taskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
                    .includeProcessVariables()
                    .finished()
                    .taskTenantId(tenantId)
                    .taskAssignee(userId)
                    .orderByHistoricTaskInstanceEndTime()
                    .desc();
            List<HistoricTaskInstance> historicTaskInstanceList = taskInstanceQuery.listPage(pageInfo.getCurrPage() - 1, pageInfo.getPrePageResult());
            List<FlowTaskPdu> hisTaskList = Lists.newArrayList();
            for (HistoricTaskInstance histTask : historicTaskInstanceList) {
                FlowTaskPdu flowTask = new FlowTaskPdu();
                // 当前流程信息
                flowTask.setTaskId(histTask.getId());
                // 审批人员信息
                flowTask.setCreateTime(histTask.getCreateTime());
                flowTask.setFinishTime(histTask.getEndTime());
                flowTask.setDuration(getDate(histTask.getDurationInMillis()));
                flowTask.setProcDefId(histTask.getProcessDefinitionId());
                flowTask.setTaskDefKey(histTask.getTaskDefinitionKey());
                flowTask.setTaskName(histTask.getName());

                // 流程定义信息
                ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                        .processDefinitionId(histTask.getProcessDefinitionId())
                        .processDefinitionTenantId(tenantId)
                        .singleResult();
                flowTask.setDeployId(pd.getDeploymentId());
                flowTask.setProcDefName(pd.getName());
                flowTask.setProcDefVersion(pd.getVersion());
                flowTask.setProcInsId(histTask.getProcessInstanceId());
                flowTask.setHisProcInsId(histTask.getProcessInstanceId());

                // 流程发起人信息
                HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                        .processInstanceId(histTask.getProcessInstanceId())
                        .processInstanceTenantId(tenantId)
                        .singleResult();
                flowTask.setStartUserId(historicProcessInstance.getStartUserId());
                flowTask.setStartUserName(historicProcessInstance.getStartUserId());
                flowTask.setStartDeptName(historicProcessInstance.getStartUserId());
                hisTaskList.add(flowTask);
            }
            pageInfo.setTotalResult(hisTaskList.size());

            result.setPageInfo(pageInfo);
            result.setList(hisTaskList);
            rsp.setData(result);
            log.info(String.format("查询我的已办列表！resp:%s", JSON.toJSONString(rsp)));
            return rsp;
        } catch (Exception e) {
            log.error("异常：", e);
            rsp.setCode(YesNoEnum.NO.getValue());
            return rsp;
        }
    }

    @Override
    public ApiRespPdu<List<FlowTaskPdu>> flowRecord(String procInsId, String deployId) {
        log.info(String.format("流程记录！procInsId:%s", procInsId));
        ApiRespPdu<List<FlowTaskPdu>> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            //流程历史流转记录
            if (!ObjectUtils.isEmpty(procInsId)) {
                List<HistoricActivityInstance> list = historyService
                        .createHistoricActivityInstanceQuery()
                        .processInstanceId(procInsId)
                        .orderByHistoricActivityInstanceStartTime()
                        .desc().list();
                List<FlowTaskPdu> hisFlowList = new ArrayList<>();
                for (HistoricActivityInstance histIns : list) {
                    if (StringUtils.isNotBlank(histIns.getTaskId())) {
                        FlowTaskPdu flowTask = new FlowTaskPdu();
                        flowTask.setTaskId(histIns.getTaskId());
                        flowTask.setTaskName(histIns.getActivityName());
                        flowTask.setCreateTime(histIns.getStartTime());
                        flowTask.setFinishTime(histIns.getEndTime());
                        if (StringUtils.isNotBlank(histIns.getAssignee())) {
                            flowTask.setAssigneeId(histIns.getAssignee());
                        }
                        // 展示审批人员
                        List<HistoricIdentityLink> linksForTask = historyService.getHistoricIdentityLinksForTask(histIns.getTaskId());
                        StringBuilder stringBuilder = new StringBuilder();
                        for (HistoricIdentityLink identityLink : linksForTask) {
                            if ("candidate".equals(identityLink.getType())) {
                                if (StringUtils.isNotBlank(identityLink.getUserId())) {
                                    stringBuilder.append(identityLink.getUserId()).append(",");
                                }
                                if (StringUtils.isNotBlank(identityLink.getGroupId())) {
                                    stringBuilder.append(identityLink.getGroupId()).append(",");
                                }
                            }
                        }
                        if (StringUtils.isNotBlank(stringBuilder)) {
                            flowTask.setCandidate(stringBuilder.substring(0, stringBuilder.length() - 1));
                        }

                        flowTask.setDuration(histIns.getDurationInMillis() == null || histIns.getDurationInMillis() == 0 ? null : getDate(histIns.getDurationInMillis()));
                        // 获取意见评论内容
                        List<Comment> commentList = taskService.getProcessInstanceComments(histIns.getProcessInstanceId());
                        commentList.forEach(comment -> {
                            if (histIns.getTaskId().equals(comment.getTaskId())) {
                                flowTask.setComment(FlowCommentPdu.builder().type(comment.getType()).comment(comment.getFullMessage()).build());
                            }
                        });
                        hisFlowList.add(flowTask);
                    }
                }
                rsp.setData(hisFlowList);
            }
            return rsp;

        } catch (Exception e) {
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("流程记录异常:" + e.getMessage());
            return rsp;
        }
    }

    @Override
    public ApiRespPdu<Map<String, Object>> processVariables(String taskId) {
        log.info(String.format("获取流程变量！taskId:%s", JSON.toJSONString(taskId)));
        ApiRespPdu<Map<String, Object>> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            // 流程变量
            HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery()
                    .includeProcessVariables()
                    .finished()
                    .taskId(taskId).singleResult();
            if (Objects.nonNull(historicTaskInstance)) {
                rsp.setData(historicTaskInstance.getProcessVariables());
            } else {
                Map<String, Object> variables = taskService.getVariables(taskId);
                rsp.setData(variables);
            }
            return rsp;

        } catch (Exception e) {
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("获取流程变量异常:" + e.getMessage());
            return rsp;
        }
    }

    @Override
    public ApiRespPdu<String> complete(FlowTaskVoPdu flowTaskPdu) {
        log.info(String.format("完成任务！flowTaskPdu:%s", JSON.toJSONString(flowTaskPdu)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());

        try {

            Task task = taskService.createTaskQuery()
                    .taskId(flowTaskPdu.getTaskId())
                   // .taskTenantId(flowTaskPdu.getTenantId())
                    .singleResult();
            if (ObjectUtils.isEmpty(task)) {
                log.error("任务不存在");
                rsp.setCode(YesNoEnum.NO.getValue());
                rsp.setMsg("任务不存在");
                return rsp;
            }
            if (DelegationState.PENDING.equals(task.getDelegationState())) {
                taskService.addComment(flowTaskPdu.getTaskId(), flowTaskPdu.getInstanceId(), FlowComment.DELEGATE.getType(), flowTaskPdu.getComment());
                taskService.resolveTask(flowTaskPdu.getTaskId(), flowTaskPdu.getValues());
            } else {
                taskService.addComment(flowTaskPdu.getTaskId(), flowTaskPdu.getInstanceId(), FlowComment.NORMAL.getType(), flowTaskPdu.getComment());
                String userId = flowTaskPdu.getUserId();
                taskService.setAssignee(flowTaskPdu.getTaskId(), userId);
                taskService.complete(flowTaskPdu.getTaskId(), flowTaskPdu.getValues());
            }
            return rsp;
        } catch (Exception e) {
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("完成任务异常:" + e.getMessage());
            return rsp;
        }
    }

    @Override
    public ApiRespPdu<String> reject(FlowTaskVoPdu flowTaskPdu) {
        log.info(String.format("驳回任务！flowTaskPdu:%s", JSON.toJSONString(flowTaskPdu)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());

        if (taskService.createTaskQuery().taskId(flowTaskPdu.getTaskId()).singleResult().isSuspended()) {
            log.error("任务处于挂起状态");
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("任务处于挂起状态");
            return rsp;

        }
        // 当前任务 task
        Task task = taskService.createTaskQuery().taskId(flowTaskPdu.getTaskId()).singleResult();

        // 获取流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(task.getProcessDefinitionId())
                .processDefinitionTenantId(task.getTenantId())
                .singleResult();
        // 获取所有节点信息
        Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        // 获取全部节点列表，包含子节点
        Collection<FlowElement> allElements = FlowableUtils.getAllElements(process.getFlowElements(), null);
        // 获取当前任务节点元素
        FlowElement source = null;
        if (allElements != null) {
            for (FlowElement flowElement : allElements) {
                // 类型为用户节点
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    // 获取节点信息
                    source = flowElement;
                }
            }
        }

        // 目的获取所有跳转到的节点 targetIds
        // 获取当前节点的所有父级用户任务节点
        // 深度优先算法思想：延边迭代深入
        List<UserTask> parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null);
        if (parentUserTaskList == null || parentUserTaskList.size() == 0) {
            log.error("当前节点为初始任务节点，不能驳回");
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("当前节点为初始任务节点，不能驳回");
            return rsp;
        }
        // 获取活动 ID 即节点 Key
        List<String> parentUserTaskKeyList = new ArrayList<>();
        parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId()));
        // 获取全部历史节点活动实例，即已经走过的节点历史，数据采用开始时间升序
        List<HistoricTaskInstance> historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .taskTenantId(task.getTenantId())
                .orderByHistoricTaskInstanceStartTime().asc().list();
        // 数据清洗，将回滚导致的脏数据清洗掉
        List<String> lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicTaskInstanceList);
        // 此时历史任务实例为倒序，获取最后走的节点
        List<String> targetIds = new ArrayList<>();
        // 循环结束标识，遇到当前目标节点的次数
        int number = 0;
        StringBuilder parentHistoricTaskKey = new StringBuilder();
        for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) {
            // 当会签时候会出现特殊的，连续都是同一个节点历史数据的情况，这种时候跳过
            if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) {
                continue;
            }
            parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey);
            if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) {
                number++;
            }
            // 在数据清洗后，历史节点就是唯一一条从起始到当前节点的历史记录，理论上每个点只会出现一次
            // 在流程中如果出现循环，那么每次循环中间的点也只会出现一次，再出现就是下次循环
            // number == 1，第一次遇到当前节点
            // number == 2，第二次遇到，代表最后一次的循环范围
            if (number == 2) {
                break;
            }
            // 如果当前历史节点，属于父级的节点，说明最后一次经过了这个点，需要退回这个点
            if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) {
                targetIds.add(historicTaskInstanceKey);
            }
        }


        // 目的获取所有需要被跳转的节点 currentIds
        // 取其中一个父级任务，因为后续要么存在公共网关，要么就是串行公共线路
        UserTask oneUserTask = parentUserTaskList.get(0);
        // 获取所有正常进行的任务节点 Key，这些任务不能直接使用，需要找出其中需要撤回的任务
        List<Task> runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
        List<String> runTaskKeyList = new ArrayList<>();
        runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey()));
        // 需驳回任务列表
        List<String> currentIds = new ArrayList<>();
        // 通过父级网关的出口连线，结合 runTaskList 比对，获取需要撤回的任务
        List<UserTask> currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null);
        currentUserTaskList.forEach(item -> currentIds.add(item.getId()));


        // 规定：并行网关之前节点必须需存在唯一用户任务节点，如果出现多个任务节点，则并行网关节点默认为结束节点，原因为不考虑多对多情况
        if (targetIds.size() > 1 && currentIds.size() > 1) {
            log.error("任务出现多对多情况，无法撤回");
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("任务出现多对多情况，无法撤回");
            return rsp;
        }

        // 循环获取那些需要被撤回的节点的ID，用来设置驳回原因
        List<String> currentTaskIds = new ArrayList<>();
        currentIds.forEach(currentId -> runTaskList.forEach(runTask -> {
            if (currentId.equals(runTask.getTaskDefinitionKey())) {
                currentTaskIds.add(runTask.getId());
            }
        }));
        // 设置驳回意见
        currentTaskIds.forEach(item -> taskService.addComment(item, task.getProcessInstanceId(), FlowComment.REJECT.getType(), flowTaskPdu.getComment()));

        try {
            // 如果父级任务多于 1 个，说明当前节点不是并行节点，原因为不考虑多对多情况
            if (targetIds.size() > 1) {
                // 1 对 多任务跳转，currentIds 当前节点(1)，targetIds 跳转到的节点(多)
                runtimeService.createChangeActivityStateBuilder()
                        .processInstanceId(task.getProcessInstanceId())
                        .moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState();
            }
            // 如果父级任务只有一个，因此当前任务可能为网关中的任务
            if (targetIds.size() == 1) {
                // 1 对 1 或 多 对 1 情况，currentIds 当前要跳转的节点列表(1或多)，targetIds.get(0) 跳转到的节点(1)
                runtimeService.createChangeActivityStateBuilder()
                        .processInstanceId(task.getProcessInstanceId())
                        .moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState();
            }
        } catch (FlowableObjectNotFoundException e) {
            log.error("未找到流程实例，流程可能已发生变化", e);
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("未找到流程实例，流程可能已发生变化");
            return rsp;
        } catch (FlowableException e) {
            log.error("无法取消或开始活动", e);
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("无法取消或开始活动");
            return rsp;
        }
        return rsp;
    }

    @Override
    public ApiRespPdu<String> taskReturn(FlowTaskVoPdu flowTaskPdu) {
        log.info(String.format("退回任务！flowTaskPdu:%s", JSON.toJSONString(flowTaskPdu)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            if (taskService.createTaskQuery().taskId(flowTaskPdu.getTaskId()).singleResult().isSuspended()) {
                log.error("任务处于挂起状态");
                rsp.setCode(YesNoEnum.NO.getValue());
                rsp.setMsg("任务处于挂起状态");
                return rsp;

            }
            // 当前任务 task
            Task task = taskService.createTaskQuery().taskId(flowTaskPdu.getTaskId()).singleResult();
            // 获取流程定义信息
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
            // 获取所有节点信息
            Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
            // 获取全部节点列表，包含子节点
            Collection<FlowElement> allElements = FlowableUtils.getAllElements(process.getFlowElements(), null);
            // 获取当前任务节点元素
            FlowElement source = null;
            if (allElements != null) {
                for (FlowElement flowElement : allElements) {
                    // 类型为用户节点
                    if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                        // 获取节点信息
                        source = flowElement;
                    }
                }
            }

            // 目的获取所有跳转到的节点 targetIds
            // 获取当前节点的所有父级用户任务节点
            // 深度优先算法思想：延边迭代深入
            List<UserTask> parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null);
            if (parentUserTaskList == null || parentUserTaskList.size() == 0) {
                log.error("当前节点为初始任务节点，不能驳回");
                rsp.setCode(YesNoEnum.NO.getValue());
                rsp.setMsg("当前节点为初始任务节点，不能驳回");
                return rsp;
            }
            // 获取活动 ID 即节点 Key
            List<String> parentUserTaskKeyList = new ArrayList<>();
            parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId()));
            // 获取全部历史节点活动实例，即已经走过的节点历史，数据采用开始时间升序
            List<HistoricTaskInstance> historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceStartTime().asc().list();
            // 数据清洗，将回滚导致的脏数据清洗掉
            List<String> lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicTaskInstanceList);
            // 此时历史任务实例为倒序，获取最后走的节点
            List<String> targetIds = new ArrayList<>();
            // 循环结束标识，遇到当前目标节点的次数
            int number = 0;
            StringBuilder parentHistoricTaskKey = new StringBuilder();
            for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) {
                // 当会签时候会出现特殊的，连续都是同一个节点历史数据的情况，这种时候跳过
                if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) {
                    continue;
                }
                parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey);
                if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) {
                    number++;
                }
                // 在数据清洗后，历史节点就是唯一一条从起始到当前节点的历史记录，理论上每个点只会出现一次
                // 在流程中如果出现循环，那么每次循环中间的点也只会出现一次，再出现就是下次循环
                // number == 1，第一次遇到当前节点
                // number == 2，第二次遇到，代表最后一次的循环范围
                if (number == 2) {
                    break;
                }
                // 如果当前历史节点，属于父级的节点，说明最后一次经过了这个点，需要退回这个点
                if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) {
                    targetIds.add(historicTaskInstanceKey);
                }
            }
            // 目的获取所有需要被跳转的节点 currentIds
            // 取其中一个父级任务，因为后续要么存在公共网关，要么就是串行公共线路
            UserTask oneUserTask = parentUserTaskList.get(0);
            // 获取所有正常进行的任务节点 Key，这些任务不能直接使用，需要找出其中需要撤回的任务
            List<Task> runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
            List<String> runTaskKeyList = new ArrayList<>();
            runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey()));
            // 需驳回任务列表
            List<String> currentIds = new ArrayList<>();
            // 通过父级网关的出口连线，结合 runTaskList 比对，获取需要撤回的任务
            List<UserTask> currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null);
            currentUserTaskList.forEach(item -> currentIds.add(item.getId()));


            // 规定：并行网关之前节点必须需存在唯一用户任务节点，如果出现多个任务节点，则并行网关节点默认为结束节点，原因为不考虑多对多情况
            if (targetIds.size() > 1 && currentIds.size() > 1) {
                log.error("任务出现多对多情况，无法撤回");
                rsp.setCode(YesNoEnum.NO.getValue());
                rsp.setMsg("任务出现多对多情况，无法撤回");
                return rsp;
            }

            // 循环获取那些需要被撤回的节点的ID，用来设置驳回原因
            List<String> currentTaskIds = new ArrayList<>();
            currentIds.forEach(currentId -> runTaskList.forEach(runTask -> {
                if (currentId.equals(runTask.getTaskDefinitionKey())) {
                    currentTaskIds.add(runTask.getId());
                }
            }));
            // 设置驳回意见
            currentTaskIds.forEach(item -> taskService.addComment(item, task.getProcessInstanceId(), FlowComment.REJECT.getType(), flowTaskPdu.getComment()));

            // 如果父级任务多于 1 个，说明当前节点不是并行节点，原因为不考虑多对多情况
            if (targetIds.size() > 1) {
                // 1 对 多任务跳转，currentIds 当前节点(1)，targetIds 跳转到的节点(多)
                runtimeService.createChangeActivityStateBuilder()
                        .processInstanceId(task.getProcessInstanceId()).
                        moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState();
            }
            // 如果父级任务只有一个，因此当前任务可能为网关中的任务
            if (targetIds.size() == 1) {
                // 1 对 1 或 多 对 1 情况，currentIds 当前要跳转的节点列表(1或多)，targetIds.get(0) 跳转到的节点(1)
                runtimeService.createChangeActivityStateBuilder()
                        .processInstanceId(task.getProcessInstanceId())
                        .moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState();
            }
        } catch (FlowableObjectNotFoundException e) {
            log.error("未找到流程实例，流程可能已发生变化:", e);
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("未找到流程实例，流程可能已发生变化");
            return rsp;
        } catch (FlowableException e) {
            log.error("无法取消或开始活动", e);
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("无法取消或开始活动");
            return rsp;
        }

        return rsp;
    }

    @Override
    public ApiRespPdu<String> delete(FlowTaskVoPdu flowTaskPdu) {
        log.info(String.format("删除任务！flowTaskPdu:%s", JSON.toJSONString(flowTaskPdu)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            taskService.deleteTask(flowTaskPdu.getTaskId(), flowTaskPdu.getComment());
            return rsp;
        } catch (Exception e) {
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("删除任务异常:" + e.getMessage());
            return rsp;
        }
    }

    @Override
    public ApiRespPdu<String> claim(FlowTaskVoPdu flowTaskPdu) {

        log.info(String.format("认领/签收任务！flowTaskPdu:%s", JSON.toJSONString(flowTaskPdu)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        try {
            rsp.setCode(YesNoEnum.YES.getValue());
            taskService.claim(flowTaskPdu.getTaskId(), flowTaskPdu.getUserId());
            return rsp;
        } catch (Exception e) {
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("认领/签收任务异常:" + e.getMessage());
            return rsp;
        }
    }

    @Override
    public ApiRespPdu<String> unClaim(FlowTaskVoPdu flowTaskPdu) {
        log.info(String.format("取消认领/签收任务！flowTaskPdu:%s", JSON.toJSONString(flowTaskPdu)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        try {
            rsp.setCode(YesNoEnum.YES.getValue());
            taskService.unclaim(flowTaskPdu.getTaskId());
            return rsp;
        } catch (Exception e) {
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("认领/签收任务异常:" + e.getMessage());
            return rsp;
        }
    }

    @Override
    public ApiRespPdu<String> delegateTask(FlowTaskVoPdu flowTaskPdu) {
        log.info(String.format("委派任务！flowTaskPdu:%s", JSON.toJSONString(flowTaskPdu)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            taskService.delegateTask(flowTaskPdu.getTaskId(), flowTaskPdu.getAssignee());

            return rsp;
        } catch (Exception e) {
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("认领/签收任务异常:" + e.getMessage());
            return rsp;
        }
    }

    @Override
    public ApiRespPdu<String> assignTask(FlowTaskVoPdu flowTaskPdu) {

        log.info(String.format("转办任务！flowTaskPdu:%s", JSON.toJSONString(flowTaskPdu)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            taskService.setAssignee(flowTaskPdu.getTaskId(), flowTaskPdu.getComment());
            return rsp;
        } catch (Exception e) {
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("认领/签收任务异常:" + e.getMessage());
            return rsp;
        }
    }

    @Override
    public ApiRespPdu<List<FlowUserTaskPdu>> findReturnTaskList(FlowTaskVoPdu flowTaskVoPdu) {

        log.info(String.format("查找可回退任务列表！flowTaskVoPdu:%s", JSON.toJSONString(flowTaskVoPdu)));
        ApiRespPdu<List<FlowUserTaskPdu>> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            // 当前任务 task
            Task task = taskService.createTaskQuery().taskId(flowTaskVoPdu.getTaskId()).singleResult();
            // 获取流程定义信息
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
            // 获取所有节点信息，暂不考虑子流程情况
            Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
            Collection<FlowElement> flowElements = process.getFlowElements();
            // 获取当前任务节点元素
            UserTask source = null;
            if (flowElements != null) {
                for (FlowElement flowElement : flowElements) {
                    // 类型为用户节点
                    if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                        source = (UserTask) flowElement;
                    }
                }
            }


            // 获取节点的所有路线
            List<List<UserTask>> roads = FlowableUtils.findRoad(source, null, null, null);
            // 可回退的节点列表
            List<UserTask> userTaskList = new ArrayList<>();
            for (List<UserTask> road : roads) {
                if (userTaskList.size() == 0) {
                    // 还没有可回退节点直接添加
                    userTaskList = road;
                } else {
                    // 如果已有回退节点，则比对取交集部分
                    userTaskList.retainAll(road);
                }
            }
            if (ObjectUtils.isEmpty(userTaskList)) {
                List<FlowUserTaskPdu> collect = userTaskList.stream().map(item -> {
                    FlowUserTaskPdu userTaskPdu = new FlowUserTaskPdu();
                    userTaskPdu.setId(item.getId());
                    userTaskPdu.setName(item.getName());
                    return userTaskPdu;
                }).collect(Collectors.toList());
                rsp.setData(collect);
            } else {
                rsp.setData(Collections.emptyList());
            }

            return rsp;

        } catch (Exception e) {
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("查找可回退任务列表:" + e.getMessage());
            return rsp;
        }
    }

    @Override
    public ApiRespPdu<FlowNextPdu> getNextFlowNode(FlowTaskVoPdu flowTaskVoPdu) {
        //todo 实现人员的获取
        log.info(String.format("获取下一节点任务！flowTaskVoPdu:%s", JSON.toJSONString(flowTaskVoPdu)));
        ApiRespPdu<FlowNextPdu> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());

        Task task = taskService.createTaskQuery().taskId(flowTaskVoPdu.getTaskId()).singleResult();
        FlowNextPdu flowNextPdu = new FlowNextPdu();
        if (Objects.nonNull(task)) {
            List<UserTask> nextUserTask = FindNextNodeUtil.getNextUserTasks(repositoryService, task, new HashMap<>());
            if (CollectionUtils.isNotEmpty(nextUserTask)) {
                for (UserTask userTask : nextUserTask) {
                    MultiInstanceLoopCharacteristics multiInstance = userTask.getLoopCharacteristics();
                    // 会签节点
                    if (Objects.nonNull(multiInstance)) {

                        log.info(String.format("会签，获取多人审批用户,候选名单%s", userTask.getCandidateUsers()));
                        //List<SysUser> list = sysUserService.selectUserList(new SysUser());

                        flowNextPdu.setVars(ProcessConstants.PROCESS_MULTI_INSTANCE_USER);
                        flowNextPdu.setType(ProcessConstants.PROCESS_MULTI_INSTANCE);
                        // flowNextPdu.setUserList(list);
                    } else {
                        // 读取自定义节点属性 判断是否是否需要动态指定任务接收人员、组
                        String dataType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_DATA_TYPE);
                        String userType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_USER_TYPE);

                        if (ProcessConstants.DATA_TYPE.equals(dataType)) {
                            // 指定单个人员
                            if (ProcessConstants.USER_TYPE_ASSIGNEE.equals(userType)) {
                                // List<SysUser> list = sysUserService.selectUserList(new SysUser());

                                flowNextPdu.setVars(ProcessConstants.PROCESS_APPROVAL);
                                flowNextPdu.setType(ProcessConstants.USER_TYPE_ASSIGNEE);
                                //flowNextDto.setUserList(list);
                            }
                            // 候选人员(多个)
                            if (ProcessConstants.USER_TYPE_USERS.equals(userType)) {
                                //  List<SysUser> list = sysUserService.selectUserList(new SysUser());

                                flowNextPdu.setVars(ProcessConstants.PROCESS_APPROVAL);
                                flowNextPdu.setType(ProcessConstants.USER_TYPE_USERS);
                                // flowNextDto.setUserList(list);
                            }
                            // 候选组
                            if (ProcessConstants.USER_TYPE_ROUPS.equals(userType)) {
                                // List<SysRole> sysRoles = sysRoleService.selectRoleAll();

                                flowNextPdu.setVars(ProcessConstants.PROCESS_APPROVAL);
                                flowNextPdu.setType(ProcessConstants.USER_TYPE_ROUPS);
                                //flowNextDto.setRoleList(sysRoles);
                            }
                        }
                    }
                }
            } else {
                rsp.setCode(YesNoEnum.YES.getValue());
                rsp.setMsg("流程已完结");
                return rsp;
            }
        }

        rsp.setData(flowNextPdu);
        return rsp;
    }


    /**
     * 流程完成时间处理
     *
     * @param ms
     * @return
     */
    private String getDate(long ms) {

        long day = ms / (24 * 60 * 60 * 1000);
        long hour = (ms / (60 * 60 * 1000) - day * 24);
        long minute = ((ms / (60 * 1000)) - day * 24 * 60 - hour * 60);
        long second = (ms / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60);

        if (day > 0) {
            return day + "天" + hour + "小时" + minute + "分钟";
        }
        if (hour > 0) {
            return hour + "小时" + minute + "分钟";
        }
        if (minute > 0) {
            return minute + "分钟";
        }
        if (second > 0) {
            return second + "秒";
        } else {
            return 0 + "秒";
        }
    }
}
