package com.mortals.flowable.service;

import com.alibaba.fastjson.JSON;
import com.mortals.flowable.common.constant.ProcessConstants;
import com.mortals.flowable.common.enums.FlowComment;
import com.mortals.flowable.dao.flowable.IFlowableTaskDao;
import com.mortals.flowable.dao.flowable.IHisFlowableActinstDao;
import com.mortals.flowable.dao.flowable.IRunFlowableActinstDao;
import com.mortals.flowable.factory.FlowServiceFactory;
import com.mortals.flowable.flow.FindNextNodeUtil;
import com.mortals.flowable.flow.FlowableUtils;
import com.mortals.framework.exception.AppException;
import com.mortals.framework.model.PageInfo;
import com.mortals.framework.model.Result;
import com.mortals.xhx.common.code.YesNoEnum;
import com.mortals.xhx.common.code.flowable.runtime.CommentTypeEnum;
import com.mortals.xhx.common.pdu.api.ApiRespPdu;
import com.mortals.xhx.common.pdu.flow.*;
import com.mortals.xhx.feign.flowable.IApiFlowTaskFeign;
import com.mortals.xhx.flowable.AttachmentEntity;
import com.mortals.xhx.flowable.BaseTaskReq;
import com.mortals.xhx.flowable.task.*;
import com.mortals.xhx.module.comment.model.CommentInfoEntity;
import com.mortals.xhx.module.comment.service.CommentInfoService;
import com.mortals.xhx.module.extend.model.ExtendHisprocinstEntity;
import com.mortals.xhx.module.extend.service.ExtendHisprocinstService;
import lombok.extern.apachecommons.CommonsLog;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
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.impl.persistence.entity.ActivityInstanceEntity;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ActivityInstance;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Attachment;
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.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
import org.springframework.beans.factory.annotation.Autowired;
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 {

    @Autowired
    private CommentInfoService commentInfoService;
    @Autowired
    private ExtendHisprocinstService extendHisprocinstService;
    @Autowired
    private IBpmnModelService bpmnModelService;

    @Autowired
    private IRunFlowableActinstDao runFlowableActinstDao;
    @Autowired
    private IHisFlowableActinstDao hisFlowableActinstDao;
    @Autowired
    private IFlowableTaskDao flowableTaskDao;
    private int total;


    @Override
    public ApiRespPdu<Result<FlowTaskPdu>> myProcess(CommonTaskReq req) {
        PageInfo pageInfo = req.getPageInfo();
        log.info(String.format("查询我的流程列表！query userCode:%s,PageInfo:%s", req.getUserCode(), 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(req.getUserCode())
                    .processInstanceTenantId(req.getPlatformSn())
                    .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(req.getPlatformSn())
                        .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(RevokeTaskReq revokeTaskReq) {

        log.info(String.format("撤回流程！revokeTaskReq:%s", JSON.toJSONString(revokeTaskReq)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>(YesNoEnum.NO.getValue(), "撤回失败!");

        if (StringUtils.isNotBlank(revokeTaskReq.getProcessInstanceId())) {

            Task task = taskService.createTaskQuery()
                    .processInstanceId(revokeTaskReq.getProcessInstanceId()).singleResult();
            if (ObjectUtils.isEmpty(task)) {
                log.error("流程未启动或已执行完成，无法撤回");
                rsp.setCode(YesNoEnum.NO.getValue());
                rsp.setMsg("流程未启动或已执行完成，无法撤回");
                return rsp;
            }

            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(revokeTaskReq.getProcessInstanceId()).singleResult();
            if (processInstance != null) {
                //1.添加撤回意见
                //this.addComment(revokeTaskReq.getUserCode(), revokeTaskReq.getProcessInstanceId(), CommentTypeEnum.CH.toString(), revokeTaskReq.getMessage());
                taskService.addComment(revokeTaskReq.getTaskId(), revokeTaskReq.getProcessInstanceId(), FlowComment.REBACK.getType(), revokeTaskReq.getMessage());

                //2.设置提交人
                runtimeService.setVariable(revokeTaskReq.getProcessInstanceId(), ProcessConstants.FLOW_SUBMITTER_VAR, processInstance.getStartUserId());
                //3.执行撤回
                Activity disActivity = bpmnModelService.findActivityByName(processInstance.getProcessDefinitionId(), ProcessConstants.FLOW_SUBMITTER);
                //4.删除运行和历史的节点信息
                this.deleteActivity(disActivity.getId(), revokeTaskReq.getProcessInstanceId());
                //5.执行跳转
                List<Execution> executions = runtimeService.createExecutionQuery().parentId(revokeTaskReq.getProcessInstanceId()).list();
                List<String> executionIds = new ArrayList<>();
                executions.forEach(execution -> executionIds.add(execution.getId()));
                this.moveExecutionsToSingleActivityId(executionIds, disActivity.getId());
                rsp = new ApiRespPdu<>(YesNoEnum.YES.getValue(), "撤回成功!");
            }
        } else {
            rsp = new ApiRespPdu<>(YesNoEnum.NO.getValue(), "流程实例id不能为空!");
        }
        return rsp;

        // 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(CommonTaskReq req) {
        log.info(String.format("取消流程！flowTaskPdu:%s", JSON.toJSONString(req)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());

        try {
            List<Task> task = taskService.createTaskQuery().processInstanceId(req.getProcessInstanceId()).list();
            if (CollectionUtils.isEmpty(task)) {
                log.error("流程未启动或已执行完成，取消申请失败");
                rsp.setCode(YesNoEnum.NO.getValue());
                rsp.setMsg("流程未启动或已执行完成，取消申请失败");
                return rsp;
            }
            ProcessInstance processInstance =
                    runtimeService.createProcessInstanceQuery().processInstanceId(req.getProcessInstanceId()).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(req.getUserCode());
//                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(CommonTaskReq req) {
        PageInfo pageInfo = req.getPageInfo();
        log.info(String.format("查询我的代办列表！query userCode:%s,tenantId:%s,PageInfo:%s", req.getUserCode(), req.getPlatformSn(), JSON.toJSONString(pageInfo)));
        ApiRespPdu<Result<FlowTaskPdu>> rsp = new ApiRespPdu<>();
        Result<FlowTaskPdu> result = new Result<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {

            CommonTaskQuery commonTaskQuery = new CommonTaskQuery();
            if (!ObjectUtils.isEmpty(req.getUserCode())) {
                commonTaskQuery.setUserCode(req.getUserCode());
            }

            if (!ObjectUtils.isEmpty(req.getCandidateUsers())) {
                commonTaskQuery.setCandidateUsers(req.getCandidateUsers());
            }


            if (!ObjectUtils.isEmpty(req.getCandidateGroups())) {
                commonTaskQuery.setCandidateGroups(req.getCandidateGroups());
            }

            if (!ObjectUtils.isEmpty(req.getPlatformSn())) {
                commonTaskQuery.setPlatformSn(req.getPlatformSn());
            }

            commonTaskQuery.setPer(pageInfo.getPrePageResult() == -1 ? 999 : pageInfo.getPrePageResult());
            commonTaskQuery.setSize((pageInfo.getCurrPage() - 1) * commonTaskQuery.getPer());
            int total = flowableTaskDao.getApplyingTasksCount(commonTaskQuery);
            List<FlowTaskPdu> applyedTasks = flowableTaskDao.getApplyingTasks(commonTaskQuery);

            pageInfo.setTotalResult(total);

            result.setList(applyedTasks);
            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(CommonTaskReq req) {
        PageInfo pageInfo = req.getPageInfo();
        log.info(String.format("查询我的已办列表！query userCode:%s,tenantId:%s,PageInfo:%s", req.getUserCode(), req.getPlatformSn(), JSON.toJSONString(pageInfo)));
        ApiRespPdu<Result<FlowTaskPdu>> rsp = new ApiRespPdu<>();
        Result<FlowTaskPdu> result = new Result<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {

            CommonTaskQuery commonTaskQuery = new CommonTaskQuery();

            if (!ObjectUtils.isEmpty(req.getUserCode())) {
                commonTaskQuery.setUserCode(req.getUserCode());
            }
            if (!ObjectUtils.isEmpty(req.getPlatformSn())) {
                commonTaskQuery.setPlatformSn(req.getPlatformSn());
            }

            commonTaskQuery.setPer(pageInfo.getPrePageResult() == -1 ? 999 : pageInfo.getPrePageResult());
            commonTaskQuery.setSize((pageInfo.getCurrPage() - 1) * commonTaskQuery.getPer());

            List<FlowTaskPdu> applyedTasks = flowableTaskDao.getApplyedTasks(commonTaskQuery);
            int total = flowableTaskDao.getApplyedTasksCount(commonTaskQuery);
            pageInfo.setTotalResult(total);

            result.setPageInfo(pageInfo);
            result.setList(applyedTasks);
            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(CommonTaskReq req) {
        log.info(String.format("流程记录！procInsId:%s", req.getProcessInstanceId()));
        ApiRespPdu<List<FlowTaskPdu>> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            //流程历史流转记录
            //查询子任务 todo
            if (!ObjectUtils.isEmpty(req.getProcessInstanceId())) {
                List<HistoricActivityInstance> list = historyService
                        .createHistoricActivityInstanceQuery()
                        .processInstanceId(req.getProcessInstanceId())
                        .orderByHistoricActivityInstanceStartTime()
                        .asc().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());
                        }

                        buildPerson(histIns.getTaskId(), flowTask);

                        flowTask.setDuration(histIns.getDurationInMillis() == null || histIns.getDurationInMillis() == 0 ? null : getDate(histIns.getDurationInMillis()));
                        // 获取意见评论内容

                        List<Comment> commentList = taskService.getProcessInstanceComments(histIns.getProcessInstanceId());

                        Boolean bool1 = false;
                        for (Comment comment : commentList) {
                            if (histIns.getTaskId().equals(comment.getTaskId())) {
                                flowTask.setComment(FlowCommentPdu.builder().type(comment.getType()).comment(comment.getFullMessage()).build());
                                bool1 = true;
                                break;
                            }
                        }
                        if (bool1) {
                            //获取附件信息
                            List<AttachmentEntity> attachmentEntities = buildAttachments(histIns.getTaskId());
                            flowTask.setAttachmentList(attachmentEntities);
                            hisFlowList.add(flowTask);
                        }

                        //查询当前任务是否存在子任务
                        List<HistoricTaskInstance> subList = historyService.createHistoricTaskInstanceQuery()
                                .taskParentTaskId(histIns.getTaskId())
                                .orderByHistoricTaskInstanceStartTime()
                                .asc().list();
                        for (HistoricTaskInstance item : subList) {
                            log.info(String.format("sub,id%s,type%s", item.getId(), item.getScopeType()));
                            FlowTaskPdu flowTaskPdu = new FlowTaskPdu();
                            flowTaskPdu.setTaskId(item.getId());
                            flowTaskPdu.setTaskName(item.getName());
                            flowTaskPdu.setCreateTime(item.getCreateTime());
                            flowTaskPdu.setFinishTime(item.getEndTime());
                            if (StringUtils.isNotBlank(item.getAssignee())) {
                                flowTaskPdu.setAssigneeId(item.getAssignee());
                            }

                            flowTaskPdu.setSubTask(YesNoEnum.YES.getValue());
                            flowTaskPdu.setParentTaskId(item.getParentTaskId());
                            buildPerson(item.getId(), flowTaskPdu);
                            flowTaskPdu.setDuration(item.getDurationInMillis() == null || item.getDurationInMillis() == 0 ? null : getDate(item.getDurationInMillis()));

                            // 获取意见评论内容
                            List<Comment> commentList1 = taskService.getProcessInstanceComments(item.getProcessInstanceId());

                            //如果没有丢弃当前子任务
                            Boolean bool = false;
                            for (Comment comment : commentList1) {
                                if (item.getId().equals(comment.getTaskId())) {
                                    flowTaskPdu.setComment(FlowCommentPdu.builder().type(comment.getType()).comment(comment.getFullMessage()).build());
                                    bool = true;
                                }
                            }
                            if (!bool) {
                                continue;
                            }

                            //获取附件信息
                            List<AttachmentEntity> attachmentEntities1 = buildAttachments(item.getId());
                            flowTaskPdu.setAttachmentList(attachmentEntities1);
                            hisFlowList.add(flowTaskPdu);

                        }
                    }
                }
                rsp.setData(hisFlowList);
            }
            return rsp;

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

    private void buildPerson(String taskId, FlowTaskPdu flowTask) {
        // 展示审批人员
        List<HistoricIdentityLink> linksForTask = historyService.getHistoricIdentityLinksForTask(taskId);
        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));
        }

        if (StringUtils.isNotBlank(stringBuilder)) {
            flowTask.setCandidate(stringBuilder.substring(0, stringBuilder.length() - 1));
        }

    }

    private List<AttachmentEntity> buildAttachments(String taskId) {
        List<Attachment> taskAttachments = taskService.getTaskAttachments(taskId);
        return taskAttachments.stream().map(attachment -> {
            AttachmentEntity attachmentEntity = new AttachmentEntity();
            attachmentEntity.setName(attachment.getName());
            attachmentEntity.setUrl(attachment.getUrl());
            attachmentEntity.setType(attachment.getType());
            attachmentEntity.setDesc(attachment.getDescription());
            return attachmentEntity;
        }).collect(Collectors.toList());
    }

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

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

    @Override
    public ApiRespPdu<String> setProcessVariables(CommonTaskReq req) {
        log.info(String.format("设置流程变量！taskId:%s", req.getTaskId()));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            String processInstanceId = taskService.createTaskQuery().taskId(req.getProcessInstanceId()).singleResult().getProcessInstanceId();
            runtimeService.setVariables(processInstanceId, req.getVariables());
            return rsp;
        } catch (Exception e) {
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("设置流程变量异常:" + e.getMessage());
            return rsp;
        }
    }

    @Override
    public ApiRespPdu<String> complete(CompleteTaskReq completeTaskReq) {
        log.info(String.format("完成任务！completeTaskReq:%s", JSON.toJSONString(completeTaskReq)));
        //todo 完成加签流程  完成任务 携带附件信息
        ApiRespPdu<String> rsp = new ApiRespPdu<>(YesNoEnum.YES.getValue(), "审批成功");
        try {
            if (StringUtils.isNotBlank(completeTaskReq.getProcessInstanceId())
                    && StringUtils.isNotBlank(completeTaskReq.getTaskId())) {
                //1.查看当前任务是存在
                TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(completeTaskReq.getTaskId()).singleResult();
                if (taskEntity != null) {

                    String taskId = completeTaskReq.getTaskId();
                    //2.委派处理
                    if (DelegationState.PENDING.equals(taskEntity.getDelegationState())) {
                        //2.1生成历史记录
                        TaskEntity subTask = this.createSubTask(taskEntity, taskEntity.getParentTaskId(), completeTaskReq.getUserCode());
                        taskService.addComment(completeTaskReq.getTaskId(), completeTaskReq.getProcessInstanceId(), FlowComment.DELEGATE.getType(), completeTaskReq.getMessage());

                        //添加附件
                        if (!ObjectUtils.isEmpty(completeTaskReq.getAttachments())) {
                            completeTaskReq.getAttachments().stream().peek(item -> {
                                taskService.createAttachment(item.getType(), completeTaskReq.getTaskId(), completeTaskReq.getProcessInstanceId(), item.getName(), item.getDesc(), item.getUrl());
                            }).count();
                        }
                        taskService.complete(subTask.getId());
                        taskId = subTask.getId();
                        //2.2执行委派
                        taskService.resolveTask(completeTaskReq.getTaskId(), completeTaskReq.getVariables());
                    } else {

                        //如果是拒绝，则是拒绝意见
                        Map<String, Object> map = completeTaskReq.getVariables();

                        String bool = (String) map.getOrDefault("approve", "true");
                        if ("true".equals(bool)) {
                            taskService.addComment(completeTaskReq.getTaskId(), taskEntity.getProcessInstanceId(), FlowComment.NORMAL.getType(), completeTaskReq.getMessage());
                        } else {
                            taskService.addComment(completeTaskReq.getTaskId(), taskEntity.getProcessInstanceId(), FlowComment.REJECT.getType(), completeTaskReq.getMessage());
                        }

                        if (!ObjectUtils.isEmpty(completeTaskReq.getAttachments())) {
                            completeTaskReq.getAttachments().stream().peek(item -> {
                                taskService.createAttachment(item.getType(), completeTaskReq.getTaskId(), completeTaskReq.getProcessInstanceId(), item.getName(), item.getDesc(), item.getUrl());
                            }).count();
                        }

                        //3.1修改执行人 其实我这里就相当于签收了
                        taskService.setAssignee(completeTaskReq.getTaskId(), completeTaskReq.getUserCode());
                        //3.2执行任务 判断是否有变量
                        if (MapUtils.isNotEmpty(completeTaskReq.getVariables())) {
                            taskService.complete(completeTaskReq.getTaskId(), completeTaskReq.getVariables());
                        } else {
                            taskService.complete(completeTaskReq.getTaskId());
                        }
                        //4.处理加签父任务
                        String parentTaskId = taskEntity.getParentTaskId();
                        if (StringUtils.isNotBlank(parentTaskId)) {
                            String tableName = managementService.getTableName(TaskEntity.class);
                            String sql = "select count(1) from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}";
                            long subTaskCount = taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).count();
                            if (subTaskCount == 0) {
                                Task ptask = taskService.createTaskQuery().taskId(parentTaskId).singleResult();
                                //处理前后加签的任务
                                taskService.resolveTask(parentTaskId, completeTaskReq.getVariables());
                                if (CommentTypeEnum.HJQ.toString().equals(ptask.getScopeType())) {
                                    taskService.complete(parentTaskId, completeTaskReq.getVariables());
                                }
                            }
                        }
                    }

                    completeTaskReq.setTaskId(taskId);
                    completeTaskReq.setActivityId(taskEntity.getTaskDefinitionKey());
                    completeTaskReq.setActivityName(taskEntity.getName());
                    this.addFlowCommentInfoAndProcessStatus(completeTaskReq);
                } else {
                    rsp = new ApiRespPdu<>(YesNoEnum.NO.getValue(), "没有此任务，请确认!");
                }
            } else {
                rsp = new ApiRespPdu<>(YesNoEnum.NO.getValue(), "请输入正确的参数!");
            }

            return rsp;
        } catch (Exception e) {
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("完成任务异常:" + e.getMessage());
            return rsp;
        }

    }

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

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

        }
        // 当前任务 task
        Task task = taskService.createTaskQuery().taskId(rejectTaskReq.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(), rejectTaskReq.getMessage()));

        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(ReturnTaskReq returnTaskReq) {
        log.info(String.format("退回任务！returnTaskReq:%s", JSON.toJSONString(returnTaskReq)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            if (taskService.createTaskQuery().taskId(returnTaskReq.getTaskId()).singleResult().isSuspended()) {
                log.error("任务处于挂起状态");
                rsp.setCode(YesNoEnum.NO.getValue());
                rsp.setMsg("任务处于挂起状态");
                return rsp;

            }
            // 当前任务 task
            Task task = taskService.createTaskQuery().taskId(returnTaskReq.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(), returnTaskReq.getMessage()));

            // 如果父级任务多于 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(DeleteTaskReq deleteTaskReq) {
        log.info(String.format("删除任务！flowTaskPdu:%s", JSON.toJSONString(deleteTaskReq)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            taskService.deleteTask(deleteTaskReq.getTaskId(), deleteTaskReq.getMessage());
            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(DelegateTaskReq delegateTaskReq) {
        log.info(String.format("委派任务！flowTaskPdu:%s", JSON.toJSONString(delegateTaskReq)));
        ApiRespPdu<String> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            TaskEntityImpl currTask = (TaskEntityImpl) taskService.createTaskQuery().taskId(delegateTaskReq.getTaskId()).singleResult();
            if (currTask != null) {
                //1.添加审批意见
                taskService.addComment(currTask.getId(), currTask.getProcessInstanceId(), FlowComment.DELEGATE.getType(), delegateTaskReq.getMessage());
                //this.addComment(delegateTaskReq.getTaskId(), delegateTaskReq.getUserCode(), delegateTaskReq.getProcessInstanceId(), CommentTypeEnum.WP.toString(), delegateTaskReq.getMessage());
                //2.设置审批人就是当前登录人
                taskService.setAssignee(delegateTaskReq.getTaskId(), delegateTaskReq.getUserCode());
                //3.执行委派
                taskService.delegateTask(delegateTaskReq.getTaskId(), delegateTaskReq.getDelegateUserCode());
                rsp = new ApiRespPdu<>(YesNoEnum.YES.getValue(), "委派成功");
            } else {
                rsp = new ApiRespPdu<>(YesNoEnum.NO.getValue(), "没有运行时的任务实例,请确认!");
            }
            return rsp;
        } catch (Exception e) {
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("委派任务任务异常:" + e.getMessage());
            return rsp;
        }
    }

    @Override
    public ApiRespPdu<String> assignTask(TurnTaskReq turnTaskReq) {

        log.info(String.format("转办任务！turnTaskReq:%s", JSON.toJSONString(turnTaskReq)));

        ApiRespPdu<String> rsp = null;
        try {
            TaskEntityImpl currTask = (TaskEntityImpl) taskService.createTaskQuery().taskId(turnTaskReq.getTaskId()).singleResult();
            if (currTask != null) {
                //1.生成历史记录
                TaskEntity task = this.createSubTask(currTask, currTask.getId(), turnTaskReq.getUserCode());
                //2.添加审批意见
                // this.addComment(task.getId(), turnTaskReq.getUserCode(), turnTaskReq.getProcessInstanceId(), CommentTypeEnum.ZB.toString(), turnTaskReq.getMessage());

                taskService.addComment(turnTaskReq.getTaskId(), turnTaskReq.getProcessInstanceId(), FlowComment.ASSIGN.getType(), turnTaskReq.getMessage());

                taskService.complete(task.getId());
                //3.转办
                taskService.setAssignee(turnTaskReq.getTaskId(), turnTaskReq.getTurnToUserId());
                taskService.setOwner(turnTaskReq.getTaskId(), turnTaskReq.getUserCode());
                rsp = new ApiRespPdu<>(YesNoEnum.YES.getValue(), "转办成功");
            } else {
                rsp = new ApiRespPdu<>(YesNoEnum.NO.getValue(), "没有运行时的任务实例,请确认!");
            }
        } catch (Exception e) {
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("转办任务异常:" + e.getMessage());

        }

        return rsp;
    }

    @Override
    public ApiRespPdu<String> addSignTask(AddSignTaskReq req) {
        log.info(String.format("加签任务！AddSignTaskReq:%s", JSON.toJSONString(req)));

        Boolean flag = req.getFlag();
        ApiRespPdu<String> rsp = null;
        try {
            TaskEntityImpl taskEntity = null;
            if (ObjectUtils.isEmpty(req.getTaskId())) {
                taskEntity = (TaskEntityImpl) taskService.createTaskQuery().processInstanceId(req.getProcessInstanceId()).singleResult();
            } else {
                taskEntity = (TaskEntityImpl) taskService.createTaskQuery().taskId(req.getTaskId()).singleResult();
            }
            //1.把当前的节点设置为空
            if (taskEntity != null) {
                //如果是加签再加签
                String parentTaskId = taskEntity.getParentTaskId();
                if (StringUtils.isBlank(parentTaskId)) {
                    taskEntity.setOwner(req.getUserCode());
                    taskEntity.setAssignee(null);
                    taskEntity.setCountEnabled(true);
                    if (flag) {
                        taskEntity.setScopeType(CommentTypeEnum.HJQ.toString());
                    } else {
                        taskEntity.setScopeType(CommentTypeEnum.QJQ.toString());
                    }
                    //1.2 设置任务为空执行者
                    taskService.saveTask(taskEntity);
                }
                //2.添加加签数据
                this.createSignSubTasks(req, taskEntity);
                //3.添加审批意见
                String type = flag ? CommentTypeEnum.HJQ.toString() : CommentTypeEnum.QJQ.toString();
                //添加流程评论信息
                CommentInfoEntity commentInfo = new CommentInfoEntity();
                commentInfo.setType(type);
                commentInfo.setProcessInstanceId(taskEntity.getProcessInstanceId());
                commentInfo.setMessage(req.getMessage() == null ? "" : req.getMessage());
                commentInfo.setCreateTime(new Date());
                commentInfo.setCreateUser(req.getUserCode());
                commentInfoService.save(commentInfo);
                taskService.addComment(taskEntity.getId(), taskEntity.getProcessInstanceId(), FlowComment.ASSIGN.getType(), req.getMessage() == null ? "" : req.getMessage());

                String message = flag ? "后加签成功" : "前加签成功";
                rsp = new ApiRespPdu<>(YesNoEnum.YES.getValue(), message);
            } else {
                rsp = new ApiRespPdu<>(YesNoEnum.NO.getValue(), "没有运行时的任务实例,请确认!");
            }

            rsp = new ApiRespPdu<>(YesNoEnum.YES.getValue(), "加签成功");

        } catch (Exception e) {
            log.error("异常", e);
            rsp.setCode(YesNoEnum.NO.getValue());
            rsp.setMsg("转办任务异常:" + e.getMessage());
        }
        return rsp;
    }

    /**
     * 创建加签子任务
     *
     * @param req        加签参数
     * @param taskEntity 父任务
     */
    private void createSignSubTasks(AddSignTaskReq req, TaskEntity taskEntity) {
        if (CollectionUtils.isNotEmpty(req.getAddSignUserList())) {
            String parentTaskId = taskEntity.getParentTaskId();
            if (StringUtils.isBlank(parentTaskId)) {
                parentTaskId = taskEntity.getId();
            }
            String finalParentTaskId = parentTaskId;
            //1.创建被加签人的任务列表
            req.getAddSignUserList().forEach(userCode -> {
                if (StringUtils.isNotBlank(userCode)) {
                    this.createSubTask(taskEntity, finalParentTaskId, userCode);
                }
            });
            String taskId = taskEntity.getId();
            if (StringUtils.isBlank(taskEntity.getParentTaskId())) {
                //2.创建加签人的任务并执行完毕
                Task task = this.createSubTask(taskEntity, finalParentTaskId, req.getUserCode());
                taskId = task.getId();
            }
            Task taskInfo = taskService.createTaskQuery().taskId(taskId).singleResult();
            if (null != taskInfo) {
                taskService.complete(taskId);
            }
            //如果是候选人，需要删除运行时候选表种的数据。
            long candidateCount = taskService.createTaskQuery().taskId(parentTaskId).taskCandidateUser(req.getUserCode()).count();
            if (candidateCount > 0) {
                taskService.deleteCandidateUser(parentTaskId, req.getUserCode());
            }
        }
    }

    @Override
    public ApiRespPdu<List<FlowUserTaskPdu>> findReturnTaskList(CommonTaskReq commonTaskReq) {

        log.info(String.format("查找可回退任务列表！commonTaskReq:%s", JSON.toJSONString(commonTaskReq)));
        ApiRespPdu<List<FlowUserTaskPdu>> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());
        try {
            // 当前任务 task
            Task task = taskService.createTaskQuery().taskId(commonTaskReq.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(CommonTaskReq commonTaskReq) {
        //todo 实现人员的获取 获取多节点下设置人员情况
        log.info(String.format("获取下一节点任务！commonTaskReq:%s", JSON.toJSONString(commonTaskReq)));
        ApiRespPdu<FlowNextPdu> rsp = new ApiRespPdu<>();
        rsp.setCode(YesNoEnum.YES.getValue());

        Task task = taskService.createTaskQuery().taskId(commonTaskReq.getTaskId()).singleResult();

        // List<FlowNextPdu> flowNextPdus = new ArrayList<>();

        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) {
                    String dataType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_DATA_TYPE);
                    flowNextPdu.setDateType(dataType);
                    flowNextPdu.setName(userTask.getName());
                    MultiInstanceLoopCharacteristics multiInstance = userTask.getLoopCharacteristics();
                    // 判断是否会签节点
                    if (Objects.nonNull(multiInstance)) {
                        log.info(String.format("会签，获取多人审批用户,候选名单%s", userTask.getCandidateUsers()));

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

                        // 指定单个人员
                        if (ProcessConstants.USER_TYPE_ASSIGNEE.equals(userType)) {
                            flowNextPdu.setVars(ProcessConstants.PROCESS_APPROVAL);
                            flowNextPdu.setType(ProcessConstants.USER_TYPE_ASSIGNEE);
                            flowNextPdu.setAssignee(userTask.getAssignee());
                        }
                        // 候选人员(多个)
                        if (ProcessConstants.USER_TYPE_USERS.equals(userType)) {
                            flowNextPdu.setVars(ProcessConstants.PROCESS_APPROVAL);
                            flowNextPdu.setType(ProcessConstants.USER_TYPE_USERS);
                            flowNextPdu.setCandidateUsers(userTask.getCandidateUsers());
                        }
                        // 候选组
                        if (ProcessConstants.USER_TYPE_ROUPS.equals(userType)) {
                            flowNextPdu.setVars(ProcessConstants.PROCESS_APPROVAL);
                            flowNextPdu.setType(ProcessConstants.USER_TYPE_ROUPS);
                            flowNextPdu.setCandidateGroups(userTask.getCandidateGroups());
                        }
                    }
                }
            } else {
                rsp.setCode(YesNoEnum.YES.getValue());
                rsp.setMsg("流程已完结");
                return rsp;
            }
        }

        rsp.setData(flowNextPdu);
        return rsp;
    }


    /**
     * 创建子任务
     *
     * @param ptask    创建子任务
     * @param assignee 子任务的执行人
     * @return
     */
    protected TaskEntity createSubTask(TaskEntity ptask, String ptaskId, String assignee) {
        TaskEntity task = null;
        if (ptask != null) {
            //1.生成子任务
            task = (TaskEntity) taskService.newTask(UUID.randomUUID().toString());
            task.setCategory(ptask.getCategory());
            task.setDescription(ptask.getDescription());
            task.setTenantId(ptask.getTenantId());
            task.setAssignee(assignee);
            task.setName(ptask.getName());
            task.setParentTaskId(ptaskId);
            task.setProcessDefinitionId(ptask.getProcessDefinitionId());
            task.setProcessInstanceId(ptask.getProcessInstanceId());
            task.setTaskDefinitionKey(ptask.getTaskDefinitionKey());
            task.setTaskDefinitionId(ptask.getTaskDefinitionId());
            task.setPriority(ptask.getPriority());
            task.setCreateTime(new Date());
            taskService.saveTask(task);
        }
        return task;
    }


    /**
     * 添加审批意见
     *
     * @param baseTaskReq 参数
     */
    protected void addFlowCommentInfoAndProcessStatus(BaseTaskReq baseTaskReq) {
        //添加自定义评论
        CommentInfoEntity commentInfo = new CommentInfoEntity();
        //commentInfo.setType(baseTaskReq.getCommentTypeEnum().name());
        commentInfo.setPersonalCode(baseTaskReq.getUserCode());
        commentInfo.setProcessInstanceId(baseTaskReq.getProcessInstanceId());
        commentInfo.setMessage(baseTaskReq.getMessage());

        commentInfo.setTaskId(baseTaskReq.getTaskId());
        // commentInfo.setActivityId(baseTaskReq.getActivityId());
        // commentInfo.setActivityName(baseTaskReq.getActivityName());

        commentInfoService.save(commentInfo);
        //2.修改流程实例的状态,
        if (baseTaskReq.getCommentTypeEnum() != null && !baseTaskReq.getCommentTypeEnum().equals(CommentTypeEnum.YY)) {
            if (StringUtils.isBlank(baseTaskReq.getProcessInstanceId())) {
                throw new AppException("请传入流程实例id");
            }
            ExtendHisprocinstEntity extendHisprocinstEntity = new ExtendHisprocinstEntity();
            ExtendHisprocinstEntity condition = new ExtendHisprocinstEntity();
            condition.setProcessInstanceId(baseTaskReq.getProcessInstanceId());
            extendHisprocinstEntity.setProcessStatus(baseTaskReq.getProcessStatusEnum().toString());

            extendHisprocinstService.updateBatch(extendHisprocinstEntity, condition, null);
        }
    }


    /**
     * 删除跳转的历史节点信息
     *
     * @param disActivityId     跳转的节点id
     * @param processInstanceId 流程实例id
     */
    protected void deleteActivity(String disActivityId, String processInstanceId) {
        String tableName = managementService.getTableName(ActivityInstanceEntity.class);
        String sql = "select t.* from " + tableName + " t where t.PROC_INST_ID_=#{processInstanceId} and t.ACT_ID_ = #{disActivityId} " +
                " order by t.END_TIME_ ASC";
        List<ActivityInstance> disActivities = runtimeService.createNativeActivityInstanceQuery().sql(sql)
                .parameter("processInstanceId", processInstanceId)
                .parameter("disActivityId", disActivityId).list();
        //删除运行时和历史节点信息
        if (CollectionUtils.isNotEmpty(disActivities)) {
            ActivityInstance activityInstance = disActivities.get(0);
            sql = "select t.* from " + tableName + " t where t.PROC_INST_ID_=#{processInstanceId} and (t.END_TIME_ >= #{endTime} or t.END_TIME_ is null)";
            List<ActivityInstance> datas = runtimeService.createNativeActivityInstanceQuery().sql(sql).parameter("processInstanceId", processInstanceId)
                    .parameter("endTime", activityInstance.getEndTime()).list();
            List<String> runActivityIds = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(datas)) {
                datas.forEach(ai -> runActivityIds.add(ai.getId()));
                runFlowableActinstDao.deleteRunActinstsByIds(runActivityIds);
                hisFlowableActinstDao.deleteHisActinstsByIds(runActivityIds);
            }
        }
    }

    /**
     * 执行跳转
     */
    protected void moveExecutionsToSingleActivityId(List<String> executionIds, String activityId) {
        runtimeService.createChangeActivityStateBuilder()
                .moveExecutionsToSingleActivityId(executionIds, activityId)
                .changeState();
    }


    /**
     * 流程完成时间处理
     *
     * @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 + "秒";
        }
    }
}
