package org.dromara.wflow.workflow.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.dromara.biz.domain.BizTaskWork;
import org.dromara.biz.domain.bo.BizTaskLogBo;
import org.dromara.biz.domain.vo.BizTaskLogVo;
import org.dromara.biz.mapper.BizTaskWorkMapper;
import org.dromara.biz.service.IBizTaskLogService;
import org.dromara.biz.service.IBizTaskService;
import org.dromara.wflow.bean.do_.UserDeptDo;
import org.dromara.wflow.bean.do_.UserDo;
import org.dromara.wflow.bean.entity.WflowCcTasks;
import org.dromara.wflow.bean.entity.WflowModelHistorys;
import org.dromara.wflow.bean.entity.WflowModels;
import org.dromara.wflow.bean.entity.WflowSubProcess;
import org.dromara.wflow.bean.vo.UserVo;
import org.dromara.wflow.exception.BusinessException;
import org.dromara.wflow.mapper.WflowCcTasksMapper;
import org.dromara.wflow.mapper.WflowModelHistorysMapper;
import org.dromara.wflow.mapper.WflowModelsMapper;
import org.dromara.wflow.mapper.WflowSubProcessMapper;
import org.dromara.wflow.service.OrgRepositoryService;
import org.dromara.wflow.utils.UserUtil;
import org.dromara.wflow.workflow.UELTools;
import org.dromara.wflow.workflow.bean.dto.ProcessInstanceOwnerDto;
import org.dromara.wflow.workflow.bean.process.OperationPerm;
import org.dromara.wflow.workflow.bean.process.OrgUser;
import org.dromara.wflow.workflow.bean.process.ProcessNode;
import org.dromara.wflow.workflow.bean.process.ProcessStatus;
import org.dromara.wflow.workflow.bean.process.enums.ApprovalModeEnum;
import org.dromara.wflow.workflow.bean.process.enums.NodeTypeEnum;
import org.dromara.wflow.workflow.bean.process.enums.ProcessResultEnum;
import org.dromara.wflow.workflow.bean.process.form.Form;
import org.dromara.wflow.workflow.bean.process.props.ApprovalProps;
import org.dromara.wflow.workflow.bean.process.props.RootProps;
import org.dromara.wflow.workflow.bean.vo.*;
import org.dromara.wflow.workflow.config.WflowGlobalVarDef;
import org.dromara.wflow.workflow.service.*;
import org.dromara.wflow.workflow.utils.Executor;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author : willian fu
 * @date : 2022/8/23
 */
@Slf4j
@Service
public class ProcessInstanceServiceImpl implements ProcessInstanceService {

    @Autowired
    private OrgRepositoryService orgRepositoryService;

    @Autowired
    private WflowModelsMapper modelsMapper;

    @Autowired
    private WflowModelHistorysMapper modelHistorysMapper;

    @Autowired
    private ProcessNodeCatchService nodeCatchService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private ManagementService managementService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private UserDeptOrLeaderService userDeptOrLeaderService;

    @Autowired
    private ProcessTaskService processTaskService;

    @Autowired
    private WflowCcTasksMapper ccTasksMapper;

    @Autowired
    private WflowSubProcessMapper subProcessMapper;

    @Autowired
    private BusinessDataStorageService businessDataService;

    @Autowired
    private IBizTaskService bizTaskService;

    @Autowired
    private IBizTaskLogService iBizTaskLogService;

    @Autowired
    private UELTools uelTools;

    @Autowired
    private BizTaskWorkMapper bizTaskWorkMapper;

    @Autowired
    private DefaultNotifyServiceImpl notifyService;

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");


    @Override
    @Transactional
    public String startProcess(String defId, String userId, ProcessStartParamsVo params) {
        Map<String, Object> processVar = new HashMap<>();
        processVar.putAll(params.getFormData());
        processVar.putAll(params.getProcessUsers());
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(defId).latestVersion().singleResult();
        if (ObjectUtil.isNotNull(processDefinition) && processDefinition.isSuspended()) {
            throw new BusinessException("流程未启用，请先启用");
        }
        //设置发起人部门ID，此处减小流程变量表数据改成只放ID
        processVar.put(WflowGlobalVarDef.START_DEPT, params.getDeptId());
        WflowModels wflowModels = modelsMapper.selectOne(new LambdaQueryWrapper<WflowModels>()
                .select(WflowModels::getProcess, WflowModels::getFormItems).eq(WflowModels::getProcessDefId, defId));
        Map<String, ProcessNode<?>> nodeMap = nodeCatchService.reloadProcessByStr(wflowModels.getProcess());
        Map<String, Object> propsMap = nodeMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
                v -> null == v.getValue().getProps() ? new HashMap<>() : v.getValue().getProps()));
        //将表单及流程配置设置为变量，跟随版本
        processVar.put(WflowGlobalVarDef.WFLOW_NODE_PROPS, propsMap);
        processVar.put(WflowGlobalVarDef.INITIATOR, userId);
        //构造流程实例名称
        UserDo userDo = orgRepositoryService.getUserById(userId);
        String instanceName = userDo.getUserName() + "发起的" + processDefinition.getName();
        //这样做貌似无效果，变量表不会多INITIATOR变量，但是流程表发起人有效
        Authentication.setAuthenticatedUserId(userId);
        //自定义流程实例ID
        String instanceId = "wf" + LocalDateTime.now().format(formatter) + String.format("%04d", new Random().nextInt(10000));
        ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
                .predefineProcessInstanceId(instanceId).name(instanceName)
                .businessStatus(ProcessStatus.RUNNING.toString())
                .processDefinitionId(defId).variables(processVar).start();
        businessDataService.saveInstanceFormData(processInstance.getProcessInstanceId(), params.getFormData());
        //保存到biz_task任务表
        //bizTaskService.saveInstanceFormData(instanceId, params.getFormData());
        //保存到biz_task_log表
        iBizTaskLogService.saveToBizTaskLog(instanceId, userDo.getUserId(), "1", "发起流程", null, null);
        bizTaskService.saveInstanceFormData(instanceId, params.getFormData());
        log.info("启动 {} 流程实例 {} 成功", processInstance.getProcessDefinitionName(), processInstance.getProcessInstanceId());
        //自动完成发起人节点任务，发起人是一个UserTask，发起后触发start事件然后分配Task给发起人，所以这里要自动完成
        Task rootTask = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).active().singleResult();
        if (Objects.nonNull(rootTask)) {
            rootTask.setDescription(ProcessHandlerParamsVo.Action.complete.toString());
            taskService.complete(rootTask.getId());
        }
        Authentication.setAuthenticatedUserId(null);
        return processInstance.getProcessInstanceId();
    }

    @Override
    public String getBeforeTask(String instanceId, String task) {
        return null;
    }

    @Override
    @Transactional
    public void delProcessInstance(String instanceId) {
        // 删除流程实例
        try {
            runtimeService.deleteProcessInstance(instanceId, "删除");
        } catch (Exception ignored) {
        }
        // 删除流程历史实例
        historyService.deleteHistoricProcessInstance(instanceId);
        //删除的同时相关抄送表数据也要删除，不然分页数据会错
        ccTasksMapper.delete(new LambdaQueryWrapper<WflowCcTasks>().eq(WflowCcTasks::getInstanceId, instanceId));
        //删除物理表表单数据及修改记录
        businessDataService.deleteFormData(instanceId);
        //删除biz_task表数据
        bizTaskService.deleteByInstanceId(instanceId);
        bizTaskWorkMapper.delete(new LambdaQueryWrapper<BizTaskWork>().eq(BizTaskWork::getTaskNo, instanceId));
        log.info("删除流程实例[{}]成功", instanceId);
    }


    //todo 优化方向：taskRecords现在用于节点是否可编辑，想办法优化它，或者找其他替代是否可编辑的方式，这样就不用查询taskRecords了，可以节省200ms
    @Override
    public ProcessProgressVo getInstanceProgressRuansi(String nodeId, String instanceId) {
        //先查实例，然后判断是子流程还是主流程
        HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(instanceId).singleResult();
        //数据分类 表单配置及数据、审批任务结果、
        ProcessInstanceOwnerDto owner = null;
        Map<String, Object> formDatas = new HashMap<>();
        Map<String, ProcessHandlerParamsVo.Action> approvalResults = new HashMap<>();
        Map<String, Object> nodeProps = new HashMap<>();
        Map<String, String> signs = new HashMap<>();
        //是否是子流程
        boolean isSub = StrUtil.isNotBlank(instance.getSuperProcessInstanceId());
        HistoricProcessInstance mainInst = isSub ? null : instance;
        if (isSub) {
            //查出主流程表单数据
            mainInst = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(instance.getSuperProcessInstanceId()).singleResult();
            formDatas = businessDataService.getProcessInstanceFormData(mainInst.getId());
        }
        //优化查询，把之前查好几次的一次性查出来然后再分类
        List<HistoricVariableInstance> variables = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(instanceId).executionId(instanceId).list();
        Map<String, Object> vars = Objects.nonNull(instance.getEndTime()) ?
                new HashMap<>() : uelTools.getContextVar(instanceId, instance.getProcessDefinitionId());
        //遍历所有变量，将数据分类
        for (HistoricVariableInstance var : variables) {
            vars.put(var.getVariableName(), var.getValue());
            if (var.getVariableName().startsWith(WflowGlobalVarDef.TASK_RES_PRE)) {
                approvalResults.put(var.getVariableName(), (ProcessHandlerParamsVo.Action) var.getValue());
            } else if (!isSub && var.getVariableName().startsWith("field")) {
                formDatas.put(var.getVariableName(), var.getValue());
            } else if (var.getVariableName().startsWith("sign_")) {
                signs.put(var.getVariableName().substring(5), String.valueOf(var.getValue()));
            } else if (WflowGlobalVarDef.WFLOW_NODE_PROPS.equals(var.getVariableName())) {
                nodeProps = (Map<String, Object>) var.getValue();
            } else if (WflowGlobalVarDef.OWNER.equals(var.getVariableName())) {
                owner = (ProcessInstanceOwnerDto) var.getValue();
            } else if (WflowGlobalVarDef.START_DEPT.equals(var.getVariableName())) {
                String key = instance.getStartUserId() + "_" + var.getValue();
                Map<String, UserDeptDo> infoMap = orgRepositoryService.getUserDeptInfos(CollectionUtil.newArrayList(key));
                UserDeptDo userDeptDo = infoMap.getOrDefault(key, new UserDeptDo());
                owner = ProcessInstanceOwnerDto.builder().ownerDeptId(String.valueOf(var.getValue()))
                        .owner(instance.getStartUserId()).ownerName(userDeptDo.getUserName())
                        .ownerDeptId(userDeptDo.getDeptId()).ownerDeptName(userDeptDo.getDeptName()).build();
            }
        }
        ProcessNode<?> currentNode = null;
        String modelFormConfig = null;
        ProcessProgressVo.InstanceExternSetting externSetting = null;
        OperationPerm operationPerms = null;
        Map<String, ProcessNode<?>> nodeMap = Collections.emptyMap();
        //搜索当前版本流程的配置
        WflowModelHistorys modelHistory = modelHistorysMapper.selectOne(new LambdaQueryWrapper<>(WflowModelHistorys.builder()
                .processDefId(mainInst.getProcessDefinitionId()).version(mainInst.getProcessDefinitionVersion()).build()));
        List<Form> forms = JSONArray.parseArray(modelHistory.getFormItems(), Form.class);
        /*
        if (isSub) {
            WflowSubProcess subProcess = subProcessMapper.selectOne(new LambdaQueryWrapper<>(WflowSubProcess.builder()
                    .procDefId(instance.getProcessDefinitionId()).build()));
            nodeMap = nodeCatchService.reloadProcessByStr(subProcess.getProcess());
            currentNode = nodeMap.get(nodeId);
            operationPerms = getOperationPerm(currentNode);
            //获取主流程节点配置信息
            nodeProps = getNodeProps(nodeMap);
        }
        if (StrUtil.isNotBlank(nodeId)) {
            nodeMap = nodeCatchService.reloadProcessByStr(modelHistory.getProcess());
            currentNode = nodeMap.get(isSub ? instance.getBusinessKey() : nodeId);
            if (!isSub) {
                operationPerms = getOperationPerm(currentNode);
            }
            modelFormConfig = modelHistory.getFormConfig();
            externSetting = getExternSetting(modelHistory.getSettings(), instance.getStartUserId());
            nodeProps = getNodeProps(nodeMap);
        }
        if (Objects.isNull(nodeProps)){
            nodeProps = getNodeProps(nodeCatchService.reloadProcessByStr(modelHistory.getProcess()));
        }*/
        //表单的 currentNode 以主流程为准
        List<Form> formItems = businessDataService.filterFormAndDataByPermConfig(forms, formDatas, currentNode);
        //下面都是一样的
        UserDo users = orgRepositoryService.getUserById(instance.getStartUserId());
        OrgUser startUser = OrgUser.builder().id(users.getUserId()).name(users.getUserName()).nickName(users.getNickName()).avatar(users.getAvatar()).build();
        List<ProcessProgressVo.ProgressNode> taskRecords = getHisTaskRecords(instanceId, nodeProps, approvalResults, signs, instance.getEndTime());
        //添加抄送
//        taskRecords.addAll(getCcTaskRecords(instanceId));
        if (ObjectUtil.isNull(instance.getEndTime())) {
            taskRecords.addAll(processTaskService.getFutureTask(instance, owner.getOwnerDeptId(), vars, nodeMap));
        }
        //按开始时间对节点进行排序
        taskRecords = taskRecords.stream()
                .sorted(Comparator.comparing(ProcessProgressVo.ProgressNode::getStartTime))
                .collect(Collectors.toList());
        ProcessProgressVo.ProgressNode progressNode = taskRecords.stream()
                .filter(n -> "root".equals(n.getNodeId()) && Objects.isNull(n.getResult()))
                .findFirst().orElse(null);
        if (Objects.nonNull(progressNode)) {
            //辅助前端UI构建，第一条一般都是
            progressNode.setResult(ProcessHandlerParamsVo.Action.complete);
            progressNode.setNodeType(NodeTypeEnum.ROOT);
        } else {
            //没有就进行构建，兼容之前的流程
            taskRecords.add(0, ProcessProgressVo.ProgressNode.builder()
                    .nodeId("root")
                    .name(isSub ? "发起子流程" : "提交申请")
                    .user(startUser)
                    .nodeType(NodeTypeEnum.ROOT)
                    .startTime(instance.getStartTime())
                    .finishTime(instance.getStartTime())
                    .taskId("root")
                    .result(ProcessHandlerParamsVo.Action.complete)
                    .build());
        }
        //提取全量表单数据
        if (StrUtil.isBlank(modelFormConfig)) {
//            modelFormConfig = modelHistory.getFormConfig();
//            externSetting = getExternSetting(modelHistory.getSettings(), instance.getStartUserId());
        }
        ProcessResultEnum processResult = ProcessResultEnum.resolveResult(instance.getEndActivityId());
        return ProcessProgressVo.builder()
                .instanceId(instanceId)
                .version(instance.getProcessDefinitionVersion())
                .formItems(formItems)
//                .formConfig(JSONObject.parseObject(modelFormConfig))
                .formData(formDatas)
                .processDefName(instance.getProcessDefinitionName())
                .staterUser(startUser)
                .starterDept(null == owner ? null : owner.getOwnerDeptName())
                .status(processResult.getDesc())
                .result(processResult)
                .startTime(instance.getStartTime())
                .finishTime(instance.getEndTime())
                .progress(taskRecords)
//                .operationPerm(operationPerms)
//                .externSetting(externSetting)
                .build();
    }


    @Override
    public ProcessProgressVo getInstanceProgress(String nodeId, String instanceId) {
        //先查实例，然后判断是子流程还是主流程
        HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(instanceId).singleResult();
        //数据分类 表单配置及数据、审批任务结果、
        ProcessInstanceOwnerDto owner = null;
        Map<String, Object> formDatas = new HashMap<>();
        Map<String, ProcessHandlerParamsVo.Action> approvalResults = new HashMap<>();
        Map<String, Object> nodeProps = new HashMap<>();
        Map<String, String> signs = new HashMap<>();
        //是否是子流程
        boolean isSub = StrUtil.isNotBlank(instance.getSuperProcessInstanceId());
        HistoricProcessInstance mainInst = isSub ? null : instance;
        if (isSub) {
            //查出主流程表单数据
            mainInst = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(instance.getSuperProcessInstanceId()).singleResult();
            formDatas = businessDataService.getProcessInstanceFormData(mainInst.getId());
        }
        //优化查询，把之前查好几次的一次性查出来然后再分类
        List<HistoricVariableInstance> variables = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(instanceId).executionId(instanceId).list();
        Map<String, Object> vars = Objects.nonNull(instance.getEndTime()) ?
                new HashMap<>() : uelTools.getContextVar(instanceId, instance.getProcessDefinitionId());
        //遍历所有变量，将数据分类
        for (HistoricVariableInstance var : variables) {
            vars.put(var.getVariableName(), var.getValue());
            if (var.getVariableName().startsWith(WflowGlobalVarDef.TASK_RES_PRE)) {
                approvalResults.put(var.getVariableName(), (ProcessHandlerParamsVo.Action) var.getValue());
            } else if (!isSub && var.getVariableName().startsWith("field")) {
                formDatas.put(var.getVariableName(), var.getValue());
            } else if (var.getVariableName().startsWith("sign_")) {
                signs.put(var.getVariableName().substring(5), String.valueOf(var.getValue()));
            } else if (WflowGlobalVarDef.WFLOW_NODE_PROPS.equals(var.getVariableName())) {
                nodeProps = (Map<String, Object>) var.getValue();
            } else if (WflowGlobalVarDef.OWNER.equals(var.getVariableName())) {
                owner = (ProcessInstanceOwnerDto) var.getValue();
            } else if (WflowGlobalVarDef.START_DEPT.equals(var.getVariableName())) {
                String key = instance.getStartUserId() + "_" + var.getValue();
                Map<String, UserDeptDo> infoMap = orgRepositoryService.getUserDeptInfos(CollectionUtil.newArrayList(key));
                UserDeptDo userDeptDo = infoMap.getOrDefault(key, new UserDeptDo());
                owner = ProcessInstanceOwnerDto.builder().ownerDeptId(String.valueOf(var.getValue()))
                        .owner(instance.getStartUserId()).ownerName(userDeptDo.getUserName())
                        .ownerDeptId(userDeptDo.getDeptId()).ownerDeptName(userDeptDo.getDeptName()).build();
            }
        }
        ProcessNode<?> currentNode = null;
        String modelFormConfig = null;
        ProcessProgressVo.InstanceExternSetting externSetting = null;
        OperationPerm operationPerms = null;
        Map<String, ProcessNode<?>> nodeMap = Collections.emptyMap();
        //搜索当前版本流程的配置
        WflowModelHistorys modelHistory = modelHistorysMapper.selectOne(new LambdaQueryWrapper<>(WflowModelHistorys.builder()
                .processDefId(mainInst.getProcessDefinitionId()).version(mainInst.getProcessDefinitionVersion()).build()));
        List<Form> forms = JSONArray.parseArray(modelHistory.getFormItems(), Form.class);
        if (isSub) {
            WflowSubProcess subProcess = subProcessMapper.selectOne(new LambdaQueryWrapper<>(WflowSubProcess.builder()
                    .procDefId(instance.getProcessDefinitionId()).build()));
            nodeMap = nodeCatchService.reloadProcessByStr(subProcess.getProcess());
            currentNode = nodeMap.get(nodeId);
            operationPerms = getOperationPerm(currentNode);
            //获取主流程节点配置信息
            nodeProps = getNodeProps(nodeMap);
        }
        if (StrUtil.isNotBlank(nodeId)) {
            nodeMap = nodeCatchService.reloadProcessByStr(modelHistory.getProcess());
            currentNode = nodeMap.get(isSub ? instance.getBusinessKey() : nodeId);
            if (!isSub) {
                operationPerms = getOperationPerm(currentNode);
            }
            modelFormConfig = modelHistory.getFormConfig();
            externSetting = getExternSetting(modelHistory.getSettings(), instance.getStartUserId());
            nodeProps = getNodeProps(nodeMap);
        }
        if (Objects.isNull(nodeProps)) {
            nodeProps = getNodeProps(nodeCatchService.reloadProcessByStr(modelHistory.getProcess()));
        }
        //表单的 currentNode 以主流程为准
        List<Form> formItems = businessDataService.filterFormAndDataByPermConfig(forms, formDatas, currentNode);
        //下面都是一样的
        UserVo users = orgRepositoryService.getUserDetail(instance.getStartUserId());
        OrgUserSon startUser = OrgUserSon.builder().id(users.getUserId()).name(users.getUsername()).nickName(users.getNickName()).avatar(users.getAvatar()).deptName(users.getDepts().get(0)).build();

//        Map<String, Object> resultMap = new HashMap<>();
//        BeanUtils.copyProperties(startUser, resultMap);
//        resultMap.put("deptName",users.getDepts().get(0));
        List<ProcessProgressVo.ProgressNode> taskRecords = getHisTaskRecords(instanceId, nodeProps, approvalResults, signs, instance.getEndTime());
        //添加抄送
        taskRecords.addAll(getCcTaskRecords(instanceId));
        if (ObjectUtil.isNull(instance.getEndTime())) {
            taskRecords.addAll(processTaskService.getFutureTask(instance, owner.getOwnerDeptId(), vars, nodeMap));
        }
        //按开始时间对节点进行排序
        taskRecords = taskRecords.stream()
                .sorted(Comparator.comparing(ProcessProgressVo.ProgressNode::getStartTime))
                .collect(Collectors.toList());
        ProcessProgressVo.ProgressNode progressNode = taskRecords.stream()
                .filter(n -> "root".equals(n.getNodeId()) && Objects.isNull(n.getResult()))
                .findFirst().orElse(null);
        if (Objects.nonNull(progressNode)) {
            //辅助前端UI构建，第一条一般都是
            progressNode.setResult(ProcessHandlerParamsVo.Action.complete);
            progressNode.setNodeType(NodeTypeEnum.ROOT);
        } else {
            //没有就进行构建，兼容之前的流程
            taskRecords.add(0, ProcessProgressVo.ProgressNode.builder()
                    .nodeId("root")
                    .name(isSub ? "发起子流程" : "提交申请")
                    .user(startUser)
                    .nodeType(NodeTypeEnum.ROOT)
                    .startTime(instance.getStartTime())
                    .finishTime(instance.getStartTime())
                    .taskId("root")
                    .result(ProcessHandlerParamsVo.Action.complete)
                    .build());
        }
        //提取全量表单数据
        if (StrUtil.isBlank(modelFormConfig)) {
            modelFormConfig = modelHistory.getFormConfig();
            externSetting = getExternSetting(modelHistory.getSettings(), instance.getStartUserId());
        }
        ProcessResultEnum processResult = ProcessResultEnum.resolveResult(instance.getEndActivityId());

        //获取BizTaskLog
        BizTaskLogBo bizTaskLogBo = new BizTaskLogBo();
        bizTaskLogBo.setTaskNo(instanceId);
        List<BizTaskLogVo> bizTaskLogVos = iBizTaskLogService.queryList(bizTaskLogBo);
        if (CollectionUtil.isEmpty(bizTaskLogVos)) {
            bizTaskLogVos = Collections.emptyList();
        }
        return ProcessProgressVo.builder()
                .instanceId(instanceId)
                .version(instance.getProcessDefinitionVersion())
                .formItems(formItems)
                .formConfig(JSONObject.parseObject(modelFormConfig))
                .formData(formDatas)
                .processDefName(instance.getProcessDefinitionName())
                .staterUser(startUser)
                .starterDept(null == owner ? null : owner.getOwnerDeptName())
                .status(processResult.getDesc())
                .result(processResult)
                .startTime(instance.getStartTime())
                .finishTime(instance.getEndTime())
                .progress(taskRecords)
                .operationPerm(operationPerms)
                .externSetting(externSetting)
                .taskLogVos(bizTaskLogVos)
                .build();
    }

    /**
     * 获取流程的审批历史记录
     *
     * @param instanceId  审批实例ID
     * @param nodeProps   节点设置
     * @param varMap      变量
     * @param signs       签名
     * @param instEndTime 流程实例结束时间
     * @return 历史记录列表
     */
    private List<ProcessProgressVo.ProgressNode> getHisTaskRecords(String instanceId, Map<String, Object> nodeProps,
                                                                   Map<String, ProcessHandlerParamsVo.Action> varMap,
                                                                   Map<String, String> signs, Date instEndTime) {
        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(instanceId).orderByHistoricActivityInstanceStartTime().asc().list();
        Set<String> userSet = new HashSet<>();
        //获取节点处理结果
        Map<String, List<TaskCommentVo>> commentMap = new HashMap<>();
        //统一处理所有评论数据，省的多次查询
        List<TaskCommentVo> cmvos = taskService.getProcessInstanceComments(instanceId).stream().map(comment -> {
            userSet.add(comment.getUserId());
            TaskCommentVo commentVo = TaskCommentVo.builder()
                    .id(comment.getId())
                    .taskId(comment.getTaskId())
                    .commentType(comment.getType())
                    .type("COMMENT")
                    .createTime(comment.getTime())
                    .user(OrgUser.builder().id(comment.getUserId()).build())
                    .build();
            ProcessHandlerParamsVo.ProcessComment processComment = JSONObject.parseObject(comment.getFullMessage(), ProcessHandlerParamsVo.ProcessComment.class);
            commentVo.setText(processComment.getText());
            commentVo.setAttachments(processComment.getAttachments());
            return commentVo;
        }).collect(Collectors.toList());
        cmvos.forEach(cm -> {
            //把评论数据按照task进行归类
            String taskId = Optional.ofNullable(cm.getTaskId()).orElse(instanceId);
            List<TaskCommentVo> vos = commentMap.computeIfAbsent(taskId, k -> new ArrayList<>());
            vos.add(cm);
        });
        List<ProcessProgressVo.ProgressNode> progressNodes = list.stream()
                .filter(his -> ObjectUtil.isNotNull(his.getTaskId()) || "callActivity".equals(his.getActivityType()))
                .map(his -> {
                    Object props = nodeProps.get(his.getActivityId());
                    List<TaskCommentVo> commentVos = commentMap.getOrDefault(his.getTaskId(), Collections.emptyList());
                    ProcessProgressVo.ProgressNode node = ProcessProgressVo.ProgressNode.builder()
                            .nodeId(his.getActivityId())
                            .isFuture(false)
                            .name(his.getActivityName())
                            .startTime(his.getStartTime())
                            .finishTime(Optional.ofNullable(his.getEndTime()).orElse(instEndTime))
                            .comment(commentVos).build();
                    if ("callActivity".equals(his.getActivityType())) {
                        node.setNodeType(NodeTypeEnum.SUBPROC);
                        //取子流程实例
                        HistoricProcessInstance subInst = historyService.createHistoricProcessInstanceQuery()
                                .processInstanceId(his.getCalledProcessInstanceId())
                                .singleResult();
                        userSet.add(subInst.getStartUserId());
                        node.setUser(OrgUser.builder().id(subInst.getStartUserId()).build());
                        node.setTaskId(subInst.getId());
                        ProcessResultEnum result = ProcessResultEnum.resolveResult(subInst.getEndActivityId());
                        switch (result) {
                            case PASS:
                                node.setResult(ProcessHandlerParamsVo.Action.agree);
                                break;
                            case REFUSE:
                                node.setResult(ProcessHandlerParamsVo.Action.refuse);
                                break;
                            case CANCEL:
                                node.setResult(ProcessHandlerParamsVo.Action.cancel);
                                break;
                        }
                    } else {
                        ApprovalModeEnum approvalMode = null;
                        if (props instanceof ApprovalProps) {
                            approvalMode = ((ApprovalProps) props).getMode();
                        }
                        userSet.add(his.getAssignee());
                        node.setNodeType(NodeTypeEnum.APPROVAL);
                        node.setApprovalMode(approvalMode);
                        node.setUser(OrgUser.builder().id(his.getAssignee()).build());
                        node.setTaskId(his.getTaskId());
                        node.setSignature(signs.get(his.getTaskId()));
                        node.setResult(varMap.get(WflowGlobalVarDef.TASK_RES_PRE + his.getTaskId()));
                    }
                    return node;
                }).collect(Collectors.toList());
        //将非任务类的评论转换成流程节点
        progressNodes.addAll(commentMap.getOrDefault(instanceId, Collections.emptyList()).stream().map(cm ->
                ProcessProgressVo.ProgressNode.builder()
                        .nodeId(cm.getId())
                        .name("参与评论")
                        .user(cm.getUser())
                        .startTime(cm.getCreateTime())
                        .finishTime(cm.getCreateTime())
                        .taskId(cm.getId())
                        .comment(CollectionUtil.newArrayList(cm))
                        .result(ProcessHandlerParamsVo.Action.comment)
                        .build()).collect(Collectors.toList()));
        if (CollectionUtil.isNotEmpty(userSet)) {
            //过滤掉系统节点
            Map<String, OrgUser> map = userDeptOrLeaderService.getUserMapByIds(userSet.stream()
                    .filter(v -> !WflowGlobalVarDef.WFLOW_TASK_AGRRE.equals(v) && !WflowGlobalVarDef.WFLOW_TASK_REFUSE.equals(v))
                    .collect(Collectors.toSet()));
            progressNodes.forEach(n -> {
                if (WflowGlobalVarDef.WFLOW_TASK_AGRRE.equals(n.getUser().getId())
                        || WflowGlobalVarDef.WFLOW_TASK_REFUSE.equals(n.getUser().getId())) {
                    n.setUser(WflowGlobalVarDef.SYS);
                } else {
                    n.setUser(map.get(n.getUser().getId()));
                    n.getComment().forEach(c -> c.setUser(map.get(c.getUser().getId())));
                }
            });
        }
        return progressNodes;
    }

    @Override
    public List<Task> getProcessInstanceTaskList(String instanceId) {
        return taskService.createTaskQuery().processInstanceId(instanceId).active().list();
    }

    @Override
    public Page<ProcessInstanceVo> getUserSubmittedList(Integer pageSize, Integer pageNo, String startUser, String code,
                                                        Boolean finished, String[] startTimes, String keyword, String fieldId, String fieldVal) {
        HistoricProcessInstanceQuery instanceQuery = historyService.createHistoricProcessInstanceQuery();
        Executor.builder()
                .ifNotBlankNext(startUser, instanceQuery::startedBy)
                .ifNotBlankNext(code, instanceQuery::processDefinitionKey)
                .ifTrueNext(null != startTimes && startTimes.length > 1, () -> {
                    instanceQuery.startedAfter(DateUtil.parse(startTimes[0]));
                    instanceQuery.startedBefore(DateUtil.parse(startTimes[1]));
                })
                .ifNotBlankNext(keyword, v -> instanceQuery.processInstanceNameLike(StrUtil.format("%{}%", keyword)))
                .ifTrueNext(Boolean.TRUE.equals(finished), instanceQuery::finished)
                .ifTrueNext(Boolean.FALSE.equals(finished), instanceQuery::unfinished)
                .ifNotBlankNext(fieldId, id -> {
                    if (StrUtil.isBlank(code)) {
                        throw new BusinessException("搜索表单值必须先指定表单流程类型");
                    }
                    if (StrUtil.isNotBlank(fieldVal)) {
                        instanceQuery.variableValueLike(fieldId, "%" + fieldVal + "%");
                    }
                });
        List<HistoricProcessInstance> historicProcessInstances = instanceQuery
                .orderByProcessInstanceStartTime().desc()
                .orderByProcessInstanceEndTime().desc()
                .listPage(pageSize * (pageNo - 1), pageSize);
        Page<ProcessInstanceVo> page = new Page<>();
        page.setTotal(instanceQuery.count());
        page.setCurrent(pageNo);
        page.setSize(pageSize);
        Map<String, String> instanceNodeMap = new HashMap<>();
        page.setRecords(getInstances(instanceNodeMap, historicProcessInstances));
        return page;
    }

    @Override
    public Page<ProcessInstanceVo> getCcMeInstance(Integer pageSize, Integer pageNo, String code, String[] startTimes) {
        LambdaQueryWrapper<WflowCcTasks> queryWrapper = new LambdaQueryWrapper<WflowCcTasks>()
                .eq(WflowCcTasks::getUserId, UserUtil.getLoginUserId());
        HistoricProcessInstanceQuery instanceQuery = historyService.createHistoricProcessInstanceQuery();
        Executor.builder()
                .ifNotBlankNext(code, v -> queryWrapper.eq(WflowCcTasks::getCode, code))
                .ifNotBlankNext(code, instanceQuery::processDefinitionKey)
                .ifTrueNext(null != startTimes && startTimes.length > 1, () -> {
                    queryWrapper.ge(WflowCcTasks::getCreateTime, DateUtil.parse(startTimes[0]));
                    queryWrapper.le(WflowCcTasks::getCreateTime, DateUtil.parse(startTimes[1]));
                });
        Page<WflowCcTasks> tasks = ccTasksMapper.selectPage(new Page<>(pageNo, pageSize),
                queryWrapper.orderByDesc(WflowCcTasks::getCreateTime));
        Map<String, String> instanceMap = tasks.getRecords().stream().collect(
                Collectors.toMap(WflowCcTasks::getInstanceId, WflowCcTasks::getNodeId, (v1, v2) -> v2));
        Page<ProcessInstanceVo> page = new Page<>();
        page.setCurrent(pageNo);
        page.setSize(pageSize);
        if (CollectionUtil.isNotEmpty(instanceMap)) {
            List<HistoricProcessInstance> historicProcessInstances = instanceQuery
                    .processInstanceIds(instanceMap.keySet())
                    .orderByProcessInstanceStartTime().desc()
                    .orderByProcessInstanceEndTime().desc().list();
            List<ProcessInstanceVo> instances = getInstances(instanceMap, historicProcessInstances);
            //构造key，用于后面判断是否有缺失的instanceId和nodeId
            Set<String> ccInstKeys = new HashSet<>();
            Map<String, ProcessInstanceVo> voMap = instances.stream().peek(v -> ccInstKeys.add(v.getInstanceId() + v.getNodeId()))
                    .collect(Collectors.toMap(ProcessInstanceVo::getInstanceId, v -> v));
            //tasks里面与instanceMap数量不一致时，需要找到缺少的instanceId和nodeId，补记录
            if (tasks.getSize() != instances.size()) {
                tasks.getRecords().stream().filter(v -> !ccInstKeys.contains(v.getInstanceId() + v.getNodeId()))
                        .forEach(v -> {
                            ProcessInstanceVo vo = ProcessInstanceVo.builder().build();
                            BeanUtils.copyProperties(voMap.get(v.getInstanceId()), vo);
                            vo.setNodeId(v.getNodeId());
                            instances.add(vo);
                        });
                //对instances进行startTime倒序排序
                instances.sort(Comparator.comparing(ProcessInstanceVo::getStartTime).reversed());
            }
            page.setTotal(tasks.getTotal());
            page.setRecords(instances);
        } else {
            page.setTotal(0);
            page.setRecords(Collections.emptyList());
        }
        return page;
    }

    @Override
    public InstanceCountVo getProcessInstanceCount() {
        String userId = UserUtil.getLoginUserId();
        Long cc = ccTasksMapper.selectCount(new LambdaQueryWrapper<WflowCcTasks>().eq(WflowCcTasks::getUserId, userId));
        Long todo = taskService.createTaskQuery().taskCandidateOrAssigned(userId).count();
        Long mySubmited = historyService.createHistoricProcessInstanceQuery().startedBy(userId).unfinished().count();
        return InstanceCountVo.builder().todo(todo).mySubmited(mySubmited).cc(cc.intValue()).build();
    }

    /**
     * 获取抄送的流程实例信息
     *
     * @param instanceId 实例ID
     * @return 抄送我的流程
     */
    private List<ProcessProgressVo.ProgressNode> getCcTaskRecords(String instanceId) {
        Set<String> ccUsers = new HashSet<>();
        List<ProcessProgressVo.ProgressNode> ccList = ccTasksMapper.selectList(new LambdaQueryWrapper<WflowCcTasks>()
                .eq(WflowCcTasks::getInstanceId, instanceId)).stream().map(task -> {
            ccUsers.add(task.getUserId());
            return ProcessProgressVo.ProgressNode.builder()
                    .nodeId(task.getNodeId())
                    .nodeType(NodeTypeEnum.CC)
                    .isFuture(false)
                    .name(task.getNodeName())
                    .comment(Collections.emptyList())
                    .user(OrgUser.builder().id(task.getUserId()).build())
                    .startTime(task.getCreateTime())
                    .finishTime(task.getCreateTime())
                    .build();
        }).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(ccUsers)) {
            Map<String, OrgUser> userMap = userDeptOrLeaderService.getUserMapByIds(ccUsers);
            ccList.stream().peek(v -> v.setUser(userMap.get(v.getUser().getId()))).collect(Collectors.toList());
        }
        return ccList;
    }

    /**
     * 构造流程实例列表
     *
     * @param instanceNodeMap 流程实例ID与对应节点ID映射
     * @param Instances       流程实例
     * @return 流程实例列表
     */
    private List<ProcessInstanceVo> getInstances(Map<String, String> instanceNodeMap, List<HistoricProcessInstance> Instances) {
        Set<String> staterUsers = new HashSet<>();
        //获取表单摘要数据
        Map<String, List<FormAbstractsVo>> formAbstractsVoMap = businessDataService.getInstanceAbstractDatas(Instances.stream()
                .collect(Collectors.toMap(HistoricProcessInstance::getId, HistoricProcessInstance::getProcessDefinitionId)));
        //构造流程实例列表数据
        Map<String, ProcessInstanceVo> runInst = new HashMap<>(instanceNodeMap.size());
        List<ProcessInstanceVo> instanceVos = Instances.stream().map(ist -> {
            staterUsers.add(ist.getStartUserId());
            ProcessResultEnum processResult = ProcessResultEnum.resolveResult(ist.getEndActivityId());
            ProcessInstanceVo instanceVo = ProcessInstanceVo.builder()
                    .processDefId(ist.getProcessDefinitionId())
                    .instanceId(ist.getId())
                    .instanceName(ist.getName())
                    .superInstanceId(Optional.ofNullable(ist.getSuperProcessInstanceId()).orElse(ist.getId()))
                    .nodeId(instanceNodeMap.get(ist.getId()))
                    .formId(ist.getProcessDefinitionKey())
                    .staterUserId(ist.getStartUserId())
                    .startTime(ist.getStartTime())
                    .finishTime(ist.getEndTime())
                    .processDefName(ist.getProcessDefinitionName())
                    .status(processResult.getDesc())
                    .result(processResult)
                    .version(ist.getProcessDefinitionVersion())
                    .formAbstracts(formAbstractsVoMap.getOrDefault(ist.getId(), Collections.emptyList()))
                    .build();
            if (ProcessResultEnum.RUNNING.equals(instanceVo.getResult())) {
                //没有结束，还在走流程，获取任务
                runInst.put(ist.getId(), instanceVo);
            } else {
                instanceVo.setTaskName(instanceVo.getResult().getDesc());
            }
            return instanceVo;
        }).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(runInst.keySet())) {
            taskService.createTaskQuery().processInstanceIdIn(runInst.keySet()).active().list().stream()
                    .collect(Collectors.groupingBy(Task::getProcessInstanceId)).forEach((istId, tasks) -> {
                        Optional.ofNullable(runInst.get(istId)).ifPresent(ist -> {
                            ist.setNodeId(Optional.ofNullable(ist.getNodeId()).orElseGet(() -> {
                                if (CollectionUtil.isNotEmpty(tasks)) {
                                    return tasks.get(0).getTaskDefinitionKey();
                                }
                                return null;
                            }));
                            ist.setTaskName(StrUtil.join("、", tasks.stream().map(TaskInfo::getName).collect(Collectors.toSet())));
                        });
                    });
        }
        if (CollectionUtil.isNotEmpty(staterUsers)) {
            Map<String, OrgUser> userMap = userDeptOrLeaderService.getUserMapByIds(staterUsers);
            return instanceVos.stream().map(v -> {
                v.setStaterUser(userMap.get(v.getStaterUserId()));
                return v;
            }).collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    /**
     * 获取流程处理操作权限
     *
     * @param currentNode 当前的节点
     * @return 操作权限设置
     */
    private OperationPerm getOperationPerm(ProcessNode<?> currentNode) {
        OperationPerm operationPerm = null;
        if (Objects.nonNull(currentNode)) {
            if (NodeTypeEnum.ROOT.equals(currentNode.getType())) {
                RootProps props = (RootProps) currentNode.getProps();
                operationPerm = props.getOperationPerm();
                if (Objects.isNull(operationPerm)) {
                    operationPerm = OperationPerm.builder()
                            .complete(new OperationPerm.Operations("提交", true))
                            .build();
                } else {
                    OperationPerm.Operations agree = operationPerm.getAgree();
                    if (Objects.nonNull(agree) && Objects.isNull(operationPerm.getComplete())) {
                        agree.setAlisa("提交");
                        operationPerm.setComplete(agree);
                    }
                    operationPerm.setAgree(null);
                }
            } else if (NodeTypeEnum.APPROVAL.equals(currentNode.getType())) {
                ApprovalProps props = (ApprovalProps) currentNode.getProps();
                operationPerm = props.getOperationPerm();
                if (Objects.isNull(operationPerm)) {
                    operationPerm = OperationPerm.builder()
                            .agree(new OperationPerm.Operations("同意", true))
                            .refuse(new OperationPerm.Operations("拒绝", true))
                            .transfer(new OperationPerm.Operations("转交", true))
                            .afterAdd(new OperationPerm.Operations("加签", true))
                            .recall(new OperationPerm.Operations("退回", true))
                            .build();
                }
            } else if (NodeTypeEnum.TASK.equals(currentNode.getType())) {
                ApprovalProps props = (ApprovalProps) currentNode.getProps();
                operationPerm = props.getOperationPerm();
                if (Objects.isNull(operationPerm)) {
                    operationPerm = OperationPerm.builder()
                            .complete(new OperationPerm.Operations("提交", true))
                            .transfer(new OperationPerm.Operations("转办", true))
                            .afterAdd(new OperationPerm.Operations("加签", true))
                            .recall(new OperationPerm.Operations("退回", true))
                            .build();
                } else {
                    OperationPerm.Operations agree = operationPerm.getAgree();
                    if (Objects.nonNull(agree) && Objects.isNull(operationPerm.getComplete())) {
                        agree.setAlisa("提交");
                        operationPerm.setComplete(agree);
                    }
                    operationPerm.setAgree(null);
                }
            }
        }
        return operationPerm;
    }

    private ProcessProgressVo.InstanceExternSetting getExternSetting(String setting, String startUser) {
        JSONObject parsed = JSONObject.parseObject(setting);
        return ProcessProgressVo.InstanceExternSetting.builder()
                .enableSign(parsed.getBoolean("sign"))
                .enableCancel(Boolean.TRUE.equals(parsed.getBoolean("enableCancel"))
                        && startUser.equals(UserUtil.getLoginUserId()))
                .build();
    }

    private Map<String, Object> getNodeProps(Map<String, ProcessNode<?>> nodeMap) {
        return nodeMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
                v -> null == v.getValue().getProps() ? new HashMap<>() : v.getValue().getProps()));
    }
}
