package com.njcn.gather.device.service.impl;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.njcn.common.pojo.constant.PatternRegex;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.pojo.poi.PullDown;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.common.utils.EncryptionUtil;
import com.njcn.db.mybatisplus.constant.DbConstant;
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
import com.njcn.gather.detection.util.socket.FormalTestManager;
import com.njcn.gather.device.mapper.PqDevMapper;
import com.njcn.gather.device.pojo.dto.DataCheckResDTO;
import com.njcn.gather.device.pojo.enums.*;
import com.njcn.gather.device.pojo.param.PqDevParam;
import com.njcn.gather.device.pojo.po.PqDev;
import com.njcn.gather.device.pojo.po.PqDevSub;
import com.njcn.gather.device.pojo.vo.*;
import com.njcn.gather.device.service.IPqDevService;
import com.njcn.gather.device.service.IPqDevSubService;
import com.njcn.gather.device.service.PqDevImageFileService;
import com.njcn.gather.monitor.pojo.po.PqMonitor;
import com.njcn.gather.monitor.pojo.vo.PqMonitorExcel;
import com.njcn.gather.monitor.service.IPqMonitorService;
import com.njcn.gather.plan.mapper.AdPlanMapper;
import com.njcn.gather.plan.pojo.enums.DataSourceEnum;
import com.njcn.gather.plan.pojo.po.AdPlan;
import com.njcn.gather.plan.service.IPqDevCheckHistoryService;
import com.njcn.gather.pojo.enums.DetectionResponseEnum;
import com.njcn.gather.storage.service.DetectionDataDealService;
import com.njcn.gather.system.cfg.pojo.enums.SceneEnum;
import com.njcn.gather.system.cfg.pojo.po.SysTestConfig;
import com.njcn.gather.system.cfg.service.ISysTestConfigService;
import com.njcn.gather.system.dictionary.pojo.po.DictData;
import com.njcn.gather.system.dictionary.pojo.po.DictType;
import com.njcn.gather.system.dictionary.service.IDictDataService;
import com.njcn.gather.system.dictionary.service.IDictTypeService;
import com.njcn.gather.type.pojo.po.DevType;
import com.njcn.gather.type.service.IDevTypeService;
import com.njcn.gather.user.user.pojo.po.SysUser;
import com.njcn.gather.user.user.service.ISysUserService;
import com.njcn.http.util.RestTemplateUtil;
import com.njcn.web.factory.PageFactory;
import com.njcn.web.utils.ExcelUtil;
import com.njcn.web.utils.HttpResultUtil;
import com.njcn.web.utils.PoiUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author caozehui
 * @date 2024/11/06
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PqDevServiceImpl extends ServiceImpl<PqDevMapper, PqDev> implements IPqDevService {

    private final IDictDataService dictDataService;
    private final IPqMonitorService pqMonitorService;
    private final DetectionDataDealService detectionDataDealService;
    private final IDevTypeService devTypeService;
    private final ISysTestConfigService sysTestConfigService;
    private final IDictTypeService dictTypeService;
    private final ISysUserService userService;
    private final IPqDevSubService pqDevSubService;
    private final AdPlanMapper adPlanMapper;
    private final IPqDevCheckHistoryService pqDevCheckHistoryService;
    private final RestTemplateUtil restTemplateUtil;
    private final PqDevImageFileService pqDevImageFileService;
    private final ObjectMapper objectMapper;

    @Override
    public Page<PqDevVO> listPqDevs(PqDevParam.QueryParam queryParam) {
        Page<PqDev> page1 = this.page(new Page<>(PageFactory.getPageNum(queryParam), PageFactory.getPageSize(queryParam)), this.getQueryWrapper(queryParam));

        Page<PqDevVO> page2 = new Page<>();
        BeanUtil.copyProperties(page1, page2);

        List<String> idList = page1.getRecords().stream().map(dev -> dev.getId()).collect(Collectors.toList());
        List<PqDevVO> pqDevVOList = new ArrayList<>();
        if (CollUtil.isNotEmpty(idList)) {
            pqDevVOList.addAll(this.baseMapper.listByDevIds(idList));
        }

        page2.setRecords(pqDevVOList);
        page2.getRecords().forEach(p -> {
            DevType devType = devTypeService.getById(p.getDevType());
            if (ObjectUtil.isNotNull(devType)) {
                p.setDevType(devType.getName());
                p.setDevChns(devType.getDevChns());
                p.setDevVolt(devType.getDevVolt());
                p.setDevCurr(devType.getDevCurr());
            }
            if (StrUtil.isNotBlank(p.getSeries())) {
                p.setSeries(EncryptionUtil.decoderString(1, p.getSeries()));
            }
            if (StrUtil.isNotBlank(p.getDevKey())) {
                p.setDevKey(EncryptionUtil.decoderString(1, p.getDevKey()));
            }
        });
        return page2;
    }

    @Override
    @Transactional
    public boolean addPqDev(PqDevParam pqDevParam) {
        pqDevParam.setName(pqDevParam.getName().trim());
        pqDevParam.setCreateId(pqDevParam.getCreateId().trim());
        this.checkRepeat(pqDevParam, false);
        validateAddImages(pqDevParam.getImages());

        PqDev pqDev = new PqDev();
        BeanUtil.copyProperties(pqDevParam, pqDev, CopyOptions.create().setIgnoreProperties("images"));
        String currrentScene = sysTestConfigService.getCurrrentScene();
        this.checkParams(pqDev, currrentScene);

        if (SceneEnum.LEAVE_FACTORY_TEST.getValue().equals(currrentScene)) {
            pqDev.setManufacturer("8fa73802c9e1abab973adcbeb0d58567"); // 南京灿能字典项目对应的id
        }
        String id = UUID.randomUUID().toString().replaceAll("-", "");
        pqDev.setId(id);

        // 新增时默认设置
        PqDevSub pqDevSub = new PqDevSub();
        pqDevSub.setDevId(id);
        pqDevSub.setTimeCheckResult(TimeCheckResultEnum.UNKNOWN.getValue());
        pqDevSub.setFactorCheckResult(FactorCheckResultEnum.UNKNOWN.getValue());
        pqDevSub.setCheckState(CheckStateEnum.UNCHECKED.getValue());
        pqDevSub.setReportState(DevReportStateEnum.UNCHECKED.getValue());
        pqDevSub.setCheckResult(CheckResultEnum.UNCHECKED.getValue());
        pqDevSub.setCheckBy(pqDevParam.getCheckBy());

        // 比对式设备添加监测点
        if (PatternEnum.CONTRAST.getValue().equals(dictDataService.getDictDataById(pqDevParam.getPattern()).getCode())) {
            if (ObjectUtil.isNotEmpty(pqDevParam.getMonitorList())) {
                pqMonitorService.addPqMonitorByDevId(id, pqDevParam.getMonitorList());
            }
        }
        pqDev.setImportFlag(0);
        pqDev.setState(DataStateEnum.ENABLE.getCode());
        pqDevSubService.save(pqDevSub);
        boolean saved = this.save(pqDev);
        if (saved) {
            saveAddImages(id, pqDevParam.getImages());
            syncAppearanceSamplePhotos(id, pqDev);
        }
        return saved;
    }

    private String rebuildAppearanceSamplePhotos(String appearanceJson, List<PqDevImageVO> images) {
        Map<String, Object> appearance = new LinkedHashMap<>();
        if (StrUtil.isNotBlank(appearanceJson)) {
            try {
                appearance.putAll(objectMapper.readValue(appearanceJson, new TypeReference<Map<String, Object>>() {
                }));
            } catch (JsonProcessingException e) {
                throw new BusinessException(CommonResponseEnum.FAIL, "Appearance JSON格式错误");
            }
        }
        if (!appearance.containsKey("rows")) {
            appearance.put("rows", Collections.emptyList());
        }
        List<Map<String, String>> samplePhotos = images.stream().map(image -> {
            Map<String, String> samplePhoto = new LinkedHashMap<>();
            samplePhoto.put("fileName", image.getFileName());
            samplePhoto.put("path", image.getPath());
            return samplePhoto;
        }).collect(Collectors.toList());
        appearance.put("samplePhotos", samplePhotos);
        try {
            return objectMapper.writeValueAsString(appearance);
        } catch (JsonProcessingException e) {
            throw new BusinessException(CommonResponseEnum.FAIL, "Appearance JSON保存失败");
        }
    }

    private void syncAppearanceSamplePhotos(String devId, PqDev pqDev) {
        List<PqDevImageVO> images = pqDevImageFileService.listImages(devId);
        pqDev.setAppearance(rebuildAppearanceSamplePhotos(pqDev.getAppearance(), images));
        this.lambdaUpdate()
                .set(PqDev::getAppearance, pqDev.getAppearance())
                .eq(PqDev::getId, devId)
                .update();
    }

    private void validateAddImages(List<MultipartFile> images) {
        try {
            pqDevImageFileService.validateAddCount(images);
        } catch (IllegalArgumentException e) {
            throw new BusinessException(CommonResponseEnum.FAIL, e.getMessage());
        }
    }

    private void saveAddImages(String id, List<MultipartFile> images) {
        try {
            pqDevImageFileService.saveImages(id, images);
        } catch (IllegalArgumentException e) {
            pqDevImageFileService.deleteDeviceDirectoryQuietly(id);
            throw new BusinessException(CommonResponseEnum.FAIL, e.getMessage());
        } catch (IOException e) {
            pqDevImageFileService.deleteDeviceDirectoryQuietly(id);
            throw new BusinessException(CommonResponseEnum.FAIL, "device image save failed");
        }
    }

    private void validateUpdateImages(PqDevParam.UpdateParam updateParam) {
        try {
            pqDevImageFileService.validateFinalCount(updateParam.getId(), updateParam.getDeleteImageNames(), updateParam.getImages());
        } catch (IllegalArgumentException e) {
            throw new BusinessException(CommonResponseEnum.FAIL, e.getMessage());
        }
    }

    private void saveUpdateImages(PqDevParam.UpdateParam updateParam) {
        try {
            pqDevImageFileService.saveImages(updateParam.getId(), updateParam.getImages());
            pqDevImageFileService.deleteImages(updateParam.getId(), updateParam.getDeleteImageNames());
        } catch (IllegalArgumentException e) {
            throw new BusinessException(CommonResponseEnum.FAIL, e.getMessage());
        } catch (IOException e) {
            throw new BusinessException(CommonResponseEnum.FAIL, "device image save failed");
        }
    }

    /**
     * 校验参数
     *
     * @param pqDev
     * @param currrentScene
     */
    private void checkParams(PqDev pqDev, String currrentScene) {
        SceneEnum sceneEnum = SceneEnum.getSceneEnum(currrentScene);
        if (pqDev.getEncryptionFlag() == 1) {
            if (StrUtil.isNotBlank(pqDev.getSeries()) && StrUtil.isNotBlank(pqDev.getDevKey())) {
                pqDev.setSeries(EncryptionUtil.encodeString(1, pqDev.getSeries()));
                pqDev.setDevKey(EncryptionUtil.encodeString(1, pqDev.getDevKey()));
            } else {
                throw new BusinessException(DetectionResponseEnum.SERIES_AND_DEVKEY_NOT_BLANK);
            }
        }
        switch (sceneEnum) {
            case PROVINCE_PLATFORM:
                if (!Pattern.matches(PatternRegex.PROVINCE_DEV_NAME_REGEX, pqDev.getName())) {
                    throw new BusinessException(DetectionResponseEnum.PROVINCE_DEV_NAME_FORMAT_ERROR);
                }
                break;
            case LEAVE_FACTORY_TEST:
                if (!Pattern.matches(PatternRegex.CN_DEV_NAME_REGEX, pqDev.getName())) {
                    throw new BusinessException(DetectionResponseEnum.CN_DEV_NAME_FORMAT_ERROR);
                }
                break;
            case SELF_TEST:
                break;
            default:
                break;
        }
    }

    @Override
    @Transactional
    public boolean updatePqDev(PqDevParam.UpdateParam updateParam) {
        updateParam.setName(updateParam.getName().trim());
        updateParam.setCreateId(updateParam.getCreateId().trim());
        this.checkRepeat(updateParam, true);
        validateUpdateImages(updateParam);

        PqDev pqDev = new PqDev();
        BeanUtil.copyProperties(updateParam, pqDev, CopyOptions.create().setIgnoreProperties("images", "deleteImageNames"));
        String currrentScene = sysTestConfigService.getCurrrentScene();
        this.checkParams(pqDev, currrentScene);

        // 比对式设备修改监测点
        if (PatternEnum.CONTRAST.getValue().equals(dictDataService.getDictDataById(updateParam.getPattern()).getCode())) {
            pqMonitorService.updatePqMonitorByDevId(updateParam.getId(), updateParam.getMonitorList());
        }
        boolean updated = this.updateById(pqDev);
        if (updated) {
            pqDevSubService.lambdaUpdate()
                    .set(PqDevSub::getCheckBy, updateParam.getCheckBy())
                    .eq(PqDevSub::getDevId, updateParam.getId())
                    .update();
            saveUpdateImages(updateParam);
            syncAppearanceSamplePhotos(updateParam.getId(), pqDev);
        }
        return updated;
    }

    @Override
    @Transactional
    public boolean deletePqDev(PqDevParam.DeleteParam param) {
        if (PatternEnum.CONTRAST.getValue().equals(dictDataService.getDictDataById(param.getPattern()).getCode())) {
            for (String id : param.getIds()) {
                if (ObjectUtils.isNotEmpty(pqMonitorService.listPqMonitorByDevIds(Collections.singletonList(id)))) {
//                    throw new BusinessException(DetectionResponseEnum.PQ_DEV_HAS_MONITOR);
                    pqMonitorService.removeByDevId(id);
                }
            }
        }
        if (CollUtil.isNotEmpty(param.getIds())) {
            MPJLambdaWrapper<PqDev> queryWrapper = new MPJLambdaWrapper<>();
            queryWrapper.selectAll(PqDev.class)
                    .selectAs("t1.Name", PqDev::getPlanId)
                    .leftJoin("ad_plan t1 on t1.Id = t.Plan_Id")
                    .in(PqDev::getId, param.getIds())
                    .eq(PqDev::getState, DataStateEnum.ENABLE.getCode());
            List<PqDev> devList = this.baseMapper.selectJoinList(PqDev.class, queryWrapper);
            Map<String, List<PqDev>> collect = devList.stream().filter(x -> StrUtil.isNotBlank(x.getPlanId())).collect(Collectors.groupingBy(PqDev::getPlanId));
            StringBuffer str;
            if (CollUtil.isNotEmpty(collect)) {
                str = new StringBuffer();
                str.append("&&");
                collect.forEach((k, v) -> {
                    str.append(k + ": ");
                    for (int i = 0; i < v.size(); i++) {
                        if (i == v.size() - 1) {
                            str.append(v.get(i).getName());
                        } else {
                            str.append(v.get(i).getName() + ",");
                        }
                    }
                    str.append(" &&");
                });
            } else {
                str = null;
            }
            if (ObjectUtil.isNotNull(str)) {
                throw new BusinessException(DetectionResponseEnum.DEVICE_DELETE, str.toString());
            }
            pqDevSubService.remove(new QueryWrapper<PqDevSub>().lambda().in(PqDevSub::getDevId, param.getIds()));
            boolean deleted = this.lambdaUpdate().set(PqDev::getState, DataStateEnum.DELETED.getCode()).in(PqDev::getId, param.getIds()).update();
            if (deleted) {
                for (String id : param.getIds()) {
                    pqDevImageFileService.deleteDeviceDirectoryQuietly(id);
                }
            }
            return deleted;
        }
        return true;
    }

    @Override
    @Transactional
    public boolean updatePqDevTimeCheckResult(List<String> ids, TimeCheckResultEnum result) {
        return pqDevSubService.lambdaUpdate().set(PqDevSub::getTimeCheckResult, result.getValue()).in(PqDevSub::getDevId, ids).update();
    }

    @Override
    public List<Map<String, Object>> listUnbound(String pattern) {
        List<PqDev> pqDevList = this.lambdaQuery()
                .select(PqDev::getId, PqDev::getName, PqDev::getDevType, PqDev::getManufacturer,
                        PqDev::getCityName, PqDev::getGdName, PqDev::getSubName)
                .eq(PqDev::getPattern, pattern)
                .eq(PqDev::getState, DataStateEnum.ENABLE.getCode())
                .isNull(PqDev::getPlanId)
                .orderByDesc(PqDev::getCreateTime)
                .orderByAsc(PqDev::getName).list();
        List<Map<String, Object>> result = pqDevList.stream().map(pqDev -> {
            Map<String, Object> map = new HashMap<>();
            map.put("id", pqDev.getId());
            map.put("name", pqDev.getName());
            map.put("devType", pqDev.getDevType());
            map.put("manufacturer", pqDev.getManufacturer());
            map.put("cityName", pqDev.getCityName());
            map.put("gdName", pqDev.getGdName());
            map.put("subName", pqDev.getSubName());
            return map;
        }).collect(Collectors.toList());
        return result;
    }

    @Override
    public List<PqDevVO> listByPlanId(PqDevParam.QueryParam param) {
        if (CollUtil.isEmpty(param.getPlanIdList())) {
            return Collections.emptyList();
        }
        List<PqDevVO> pqDevList = this.baseMapper.selectByQueryParam(param);
        pqDevList.forEach(pqDev -> {
            List<PqMonitor> monitorList = pqMonitorService.listPqMonitorByDevIds(Collections.singletonList(pqDev.getId()));
            List<PqMonitor> enabledMonitorList = monitorList.stream().filter(x -> x.getCheckFlag() == 1).collect(Collectors.toList());
            pqDev.setMonitorResults(enabledMonitorList.stream().map(x -> {
                if (ObjectUtil.isNull(x.getResultType())) {
                    return CheckResultEnum.UNCHECKED.getValue();
                } else {
                    DataSourceEnum dataSourceEnum = DataSourceEnum.ofByValue(x.getResultType());

                    switch (dataSourceEnum) {
                        case REAL_DATA:
                            return x.getRealtimeResult();
                        case MINUTE_STATISTICS_AVG:
                        case MINUTE_STATISTICS_CP95:
                        case MINUTE_STATISTICS_MIN:
                        case MINUTE_STATISTICS_MAX:
                            return x.getStatisticsResult();
                        case WAVE_DATA:
                            return x.getRecordedResult();
                        default:
                            break;
                    }
                }
                return CheckResultEnum.UNCHECKED.getValue();
            }).collect(Collectors.toList()));
        });

        return pqDevList;
    }

    @Override
    @Transactional
    public Integer bind(String planId, List<String> devIds) {
        //先将这个绑定的计划全部剔除掉
        this.lambdaUpdate().set(PqDev::getPlanId, null).eq(PqDev::getPlanId, planId).update();
        //然后进行状态绑定
        if (ObjectUtil.isNotEmpty(devIds)) {
            this.lambdaUpdate().set(PqDev::getPlanId, planId).in(PqDev::getId, devIds).update();

            List<PqDevSub> list = pqDevSubService.lambdaQuery().in(PqDevSub::getDevId, devIds).list();
            //判断是否有处了未检的其他设备
            List<String> notUnchecked = list.stream().filter(x -> !CheckStateEnum.UNCHECKED.getValue().equals(x.getCheckState())).map(PqDevSub::getDevId).distinct().collect(Collectors.toList());

            if (CollUtil.isNotEmpty(notUnchecked)) {
                boolean allArchived = list.stream().allMatch(x -> x.getCheckState() != null && x.getCheckState() >= CheckStateEnum.DOCUMENTED.getValue());
                return allArchived ? CheckStateEnum.CHECKED.getValue() : CheckStateEnum.CHECKING.getValue();
            }
        }
        return CheckStateEnum.UNCHECKED.getValue();
    }

//
//    @Override
//    public List getPieData(String planId) {
//        List<PqDev> pqDevList = this.lambdaQuery().eq(PqDev::getPlanId, planId).eq(PqDev::getState, DataStateEnum.ENABLE.getCode()).list();
//        Map<Integer, Long> checkStateMap = pqDevList.stream().collect(Collectors.groupingBy(PqDev::getCheckState, Collectors.counting()));
//        Map<Integer, Long> checkResultMap = pqDevList.stream().collect(Collectors.groupingBy(PqDev::getCheckResult, Collectors.counting()));
//        Map<Integer, Long> reportStateMap = pqDevList.stream().collect(Collectors.groupingBy(PqDev::getReportState, Collectors.counting()));
//
//        List<Map<String, Object>> checkStateData = getCheckStatePieData(checkStateMap);
//        List<Map<String, Object>> checkResultData = getCheckResultPieData(checkResultMap);
//        List<Map<String, Object>> reportStateData = getReportStatePieData(reportStateMap);
//
//        List<List<Map<String, Object>>> result = new ArrayList<>();
//        result.add(checkStateData);
//        result.add(checkResultData);
//        result.add(reportStateData);
//        return result;
//    }

    @Override
    public PqDevVO getPqDevById(String id) {
        PqDevVO pqDevVO = this.baseMapper.selectByDevId(id);
        if (ObjectUtil.isNull(pqDevVO)) {
            return null;
        }
        setImageListFields(pqDevVO);
        if (StrUtil.isNotBlank(pqDevVO.getSeries())) {
            pqDevVO.setSeries(EncryptionUtil.decoderString(1, pqDevVO.getSeries()));
        }
        if (StrUtil.isNotBlank(pqDevVO.getDevKey())) {
            pqDevVO.setDevKey(EncryptionUtil.decoderString(1, pqDevVO.getDevKey()));
        }
        if (StrUtil.isNotBlank(pqDevVO.getCheckBy())) {
            SysUser sysUser = userService.getById(pqDevVO.getCheckBy());
            if (ObjectUtil.isNotNull(sysUser)) {
                pqDevVO.setCheckBy(sysUser.getName());
            } else {
                pqDevVO.setCheckBy(pqDevVO.getCheckBy());
            }
        }
        DevType devType = devTypeService.getById(pqDevVO.getDevType());
        if (ObjectUtil.isNotNull(devType)) {
            pqDevVO.setDevChns(devType.getDevChns());
            pqDevVO.setDevCurr(devType.getDevCurr());
            pqDevVO.setDevVolt(devType.getDevVolt());
        }

        List<PqMonitor> monitorList = pqMonitorService.listPqMonitorByDevIds(Collections.singletonList(id));
        if (ObjectUtil.isNotEmpty(monitorList)) {
            pqDevVO.setMonitorList(monitorList);
        }
        return pqDevVO;
    }

    @Override
    public void previewImage(String id, String fileName, HttpServletResponse response) {
        writeDeviceImage(id, fileName, response, "inline");
    }

    @Override
    public void downloadImage(String id, String fileName, HttpServletResponse response) {
        writeDeviceImage(id, fileName, response, "attachment");
    }

    private void writeDeviceImage(String id, String fileName, HttpServletResponse response, String disposition) {
        PqDev pqDev = this.getById(id);
        if (ObjectUtil.isNull(pqDev)) {
            throw new BusinessException(CommonResponseEnum.FAIL, "device not found");
        }
        try {
            response.setContentType("image/jpeg");
            response.setContentLength((int) pqDevImageFileService.imageSize(id, fileName));
            response.setHeader("Content-Disposition", disposition + "; filename=\"" + fileName + "\"");
            pqDevImageFileService.writeImage(id, fileName, response.getOutputStream());
            response.flushBuffer();
        } catch (IllegalArgumentException e) {
            throw new BusinessException(CommonResponseEnum.FAIL, e.getMessage());
        } catch (IOException e) {
            throw new BusinessException(CommonResponseEnum.FAIL, "device image output failed");
        }
    }

    private void setImageListFields(PqDevVO pqDevVO) {
        List<PqDevImageVO> images = pqDevImageFileService.listImages(pqDevVO.getId());
        pqDevVO.setImages(images);
        pqDevVO.setHasImage(CollUtil.isNotEmpty(images));
    }

    /**
     * 获取查询条件wrapper
     *
     * @param queryParam 查询条件
     * @return
     */
    private Wrapper getQueryWrapper(PqDevParam.QueryParam queryParam) {
        QueryWrapper<PqDev> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("pq_dev.Id");
        if (ObjectUtil.isNotNull(queryParam)) {
            queryWrapper
                    .like(StrUtil.isNotBlank(queryParam.getName()), "pq_dev.name", queryParam.getName())
                    .eq(StrUtil.isNotBlank(queryParam.getPattern()), "pq_dev.pattern", queryParam.getPattern())
                    .eq(StrUtil.isNotBlank(queryParam.getManufacturer()), "pq_dev.manufacturer", queryParam.getManufacturer())
                    .eq(StrUtil.isNotBlank(queryParam.getCityName()), "pq_dev.City_Name", queryParam.getCityName())
                    .eq(StrUtil.isNotBlank(queryParam.getGdName()), "pq_dev.Gd_Name", queryParam.getGdName())
                    .eq(StrUtil.isNotBlank(queryParam.getSubName()), "pq_dev.Sub_Name", queryParam.getSubName())
                    .in(CollectionUtil.isNotEmpty(queryParam.getPlanIdList()), "pq_dev.Plan_Id", queryParam.getPlanIdList())
                    .isNotNull(DataStateEnum.ENABLE.getCode().equals(queryParam.getAssign()), "pq_dev.Plan_Id")
                    .isNull(DataStateEnum.DELETED.getCode().equals(queryParam.getAssign()), "pq_dev.Plan_Id")
                    .eq(DataStateEnum.DELETED.getCode().equals(queryParam.getAssignSub()), "pq_dev.Plan_Id", queryParam.getPlanId())
                    .ne(DataStateEnum.ENABLE.getCode().equals(queryParam.getAssignSub()), "pq_dev.Plan_Id", queryParam.getPlanId())
                    .between(ObjectUtil.isAllNotEmpty(queryParam.getSearchBeginTime(), queryParam.getSearchEndTime()), "pq_dev.Create_Date", queryParam.getSearchBeginTime(), queryParam.getSearchEndTime());
            if (StrUtil.isNotBlank(queryParam.getRegion())) {
                queryWrapper.and(w -> w.like(StrUtil.isNotBlank(queryParam.getRegion()), "pq_dev.City_Name", queryParam.getRegion())
                        .or().like(StrUtil.isNotBlank(queryParam.getRegion()), "pq_dev.Gd_Name", queryParam.getRegion())
                        .or().like(StrUtil.isNotBlank(queryParam.getRegion()), "pq_dev.Sub_Name", queryParam.getRegion()));
            }
            if (StrUtil.isNotBlank(queryParam.getKeywords())) {
                queryWrapper.and(w -> w.like(StrUtil.isNotBlank(queryParam.getKeywords()), "pq_dev.City_Name", queryParam.getKeywords())
                        .or().like(StrUtil.isNotBlank(queryParam.getKeywords()), "pq_dev.Gd_Name", queryParam.getKeywords())
                        .or().like(StrUtil.isNotBlank(queryParam.getKeywords()), "pq_dev.Sub_Name", queryParam.getKeywords())
                        .or().like(StrUtil.isNotBlank(queryParam.getKeywords()), "pq_dev.name", queryParam.getKeywords())
                );
            }
            //排序
            if (ObjectUtil.isAllNotEmpty(queryParam.getSortBy(), queryParam.getOrderBy())) {
                queryWrapper.orderBy(true, queryParam.getOrderBy().equals(DbConstant.ASC), StrUtil.toUnderlineCase(queryParam.getSortBy()));
            }
        }
        queryWrapper.eq("pq_dev.state", DataStateEnum.ENABLE.getCode())
                .orderByDesc("pq_dev.Create_Time")
                .orderByAsc("pq_dev.name");
        return queryWrapper;
    }

    @Override
    public List<PreDetection> getDevInfo(List<String> devIds) {
        List<PreDetection> preDetections = this.baseMapper.selectDevInfo(devIds);
        if (devIds.size() != preDetections.size()) {
            throw new BusinessException(DetectionResponseEnum.DEVICE_DIS_ERROR);
        }

        for (PreDetection preDetection : preDetections) {
            List<PreDetection.MonitorListDTO> monitorList = preDetection.getMonitorList();
            if (CollUtil.isEmpty(monitorList)) {
                PreDetection.MonitorListDTO monitorListDTO;
                for (int i = 1; i <= preDetection.getDevChns(); i++) {
                    monitorListDTO = new PreDetection.MonitorListDTO();
                    monitorListDTO.setLineId(preDetection.getDevIP() + "_" + i);
                    monitorListDTO.setLine(i);
                    monitorListDTO.setPt(1.0);
                    monitorListDTO.setCt(1.0);
                    monitorList.add(monitorListDTO);
                }
                preDetection.setMonitorList(monitorList);
            }
        }
        return preDetections;
    }

    @Override
    public void clearFormalProgressForNewRun(List<String> devIds) {
        if (CollUtil.isEmpty(devIds)) {
            return;
        }
        List<PqDevSub> devSubs = pqDevSubService.lambdaQuery()
                .in(PqDevSub::getDevId, devIds)
                .list();
        List<String> resetFormalCheckIds = devSubs.stream()
                .filter(dev -> CheckStateEnum.CHECKING.getValue().equals(dev.getCheckState()))
                .map(PqDevSub::getDevId)
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(resetFormalCheckIds)) {
            pqDevSubService.lambdaUpdate()
                    .set(PqDevSub::getFormalCheckTime, 0)
                    .in(PqDevSub::getDevId, resetFormalCheckIds)
                    .update();
        }
        pqDevSubService.lambdaUpdate()
                .set(PqDevSub::getCheckState, CheckStateEnum.CHECKING.getValue())
                .in(PqDevSub::getDevId, devIds)
                .update();
    }

    @Override
    public Integer getMinFormalCheckTime(List<String> devIds) {
        if (CollUtil.isEmpty(devIds)) {
            return 0;
        }
        return pqDevSubService.lambdaQuery()
                .in(PqDevSub::getDevId, devIds)
                .list()
                .stream()
                .map(PqDevSub::getFormalCheckTime)
                .filter(Objects::nonNull)
                .min(Integer::compareTo)
                .orElse(0);
    }


    @Override
    public boolean updateResult(List<String> ids, List<String> adType, String code, String userId, Float temperature, Float humidity, boolean updateCheckNum) {
        if (CollUtil.isNotEmpty(ids)) {

            SysTestConfig config = sysTestConfigService.getOneConfig();
            Map<String, Integer> result = detectionDataDealService.devResult(false, ids, adType, code);
            List<PqDevVO> list = new ArrayList<>();
            if (CollUtil.isNotEmpty(ids)) {
                list.addAll(this.baseMapper.listByDevIds(ids));
            }
            SysUser user = StrUtil.isNotBlank(userId) ? userService.getById(userId) : null;
            String checkByName = ObjectUtil.isNotNull(user) ? user.getName() : null;

            for (PqDevVO pqDevVo : list) {
                if (result.containsKey(pqDevVo.getId())) {
                    Integer checkState;
                    int i;
                    LambdaUpdateWrapper<PqDevSub> wrapper = new LambdaUpdateWrapper<PqDevSub>()
                            .set(PqDevSub::getCheckResult, result.get(pqDevVo.getId()))
                            .set(StrUtil.isBlank(pqDevVo.getCheckBy()) && StrUtil.isNotBlank(checkByName), PqDevSub::getCheckBy, checkByName)
                            .set(updateCheckNum && SourceOperateCodeEnum.ALL_TEST.getValue().equals(FormalTestManager.reCheckType), PqDevSub::getCheckEndTime, LocalDateTime.now())
                            .set(updateCheckNum && SourceOperateCodeEnum.ALL_TEST.getValue().equals(FormalTestManager.reCheckType) && ObjectUtil.isNotNull(FormalTestManager.checkStartTime), PqDevSub::getCheckStartTime, FormalTestManager.checkStartTime)
                            .eq(PqDevSub::getDevId, pqDevVo.getId());
                    String currrentScene = sysTestConfigService.getCurrrentScene();
                    if (SceneEnum.PROVINCE_PLATFORM.getValue().equals(currrentScene)) {
                        wrapper.set(ObjectUtil.isNotNull(temperature), PqDevSub::getTemperature, temperature)
                                .set(ObjectUtil.isNotNull(humidity), PqDevSub::getHumidity, humidity);
                    }
                    if (pqDevVo.getRecheckNum() >= config.getMaxTime()) {
                        // 装置报告生成 todo...
                        //this.baseMapper.updateReportState(pqDev.getId());
                        wrapper.set(PqDevSub::getReportState, DevReportStateEnum.GENERATED.getValue());
                        // 装置归档
                        checkState = CheckStateEnum.DOCUMENTED.getValue();
                        i = pqDevVo.getRecheckNum();
                    } else {
                        checkState = pqDevVo.getCheckState() == CheckStateEnum.DOCUMENTED.getValue() ? CheckStateEnum.DOCUMENTED.getValue() : CheckStateEnum.CHECKED.getValue();
                        if (updateCheckNum) {
                            i = pqDevVo.getRecheckNum() + 1;
                        } else {
                            i = pqDevVo.getRecheckNum();
                        }
                        wrapper.set(PqDevSub::getReportState, DevReportStateEnum.NOT_GENERATED.getValue());
                    }
                    if (ObjectUtil.isNotNull(FormalTestManager.resumeBaseFormalCheckTime)
                            && ObjectUtil.isNotNull(FormalTestManager.resumeFormalStartTime)) {
                        int elapsedSeconds = (int) java.time.Duration.between(
                                FormalTestManager.resumeFormalStartTime,
                                LocalDateTime.now()
                        ).getSeconds();
                        wrapper.set(PqDevSub::getFormalCheckTime,
                                FormalTestManager.resumeBaseFormalCheckTime + Math.max(elapsedSeconds, 0));
                    } else if (updateCheckNum && ObjectUtil.isNotNull(FormalTestManager.checkStartTime)) {
                        int elapsedSeconds = (int) java.time.Duration.between(
                                FormalTestManager.checkStartTime,
                                LocalDateTime.now()
                        ).getSeconds();
                        wrapper.set(PqDevSub::getFormalCheckTime, Math.max(elapsedSeconds, 0));
                    }
                    wrapper.set(PqDevSub::getRecheckNum, i)
                            .set(PqDevSub::getCheckState, checkState);
                    pqDevSubService.update(wrapper);
                    saveCheckHistory(pqDevVo, i, checkState, userId);

                    PqDevParam.QueryParam param = new PqDevParam.QueryParam();
                    param.setPlanIdList(Arrays.asList(pqDevVo.getPlanId()));
                    List<PqDevVO> pqDevVOList = this.baseMapper.selectByQueryParam(param);
                    if (CollUtil.isNotEmpty(pqDevVOList)) {
                        Set<Integer> set = pqDevVOList.stream().map(PqDevVO::getCheckResult).collect(Collectors.toSet());
                        if (set.contains(CheckResultEnum.NOT_ACCORD.getValue())) {
                            this.baseMapper.updatePlanCheckResult(pqDevVo.getPlanId(), CheckResultEnum.NOT_ACCORD.getValue());
                        } else if (set.contains(CheckResultEnum.UNCHECKED.getValue())) {
                            this.baseMapper.updatePlanCheckResult(pqDevVo.getPlanId(), CheckResultEnum.UNCHECKED.getValue());
                        } else {
                            this.baseMapper.updatePlanCheckResult(pqDevVo.getPlanId(), CheckResultEnum.ACCORD.getValue());
                        }

                        boolean allUnchecked = pqDevVOList.stream().allMatch(obj -> CheckStateEnum.UNCHECKED.getValue().equals(obj.getCheckState()));
                        boolean allArchived = pqDevVOList.stream().allMatch(obj -> obj.getCheckState() != null && obj.getCheckState() >= CheckStateEnum.DOCUMENTED.getValue());
                        if (allUnchecked) {
                            this.baseMapper.updatePlanTestState(pqDevVo.getPlanId(), CheckStateEnum.UNCHECKED.getValue());
                        } else if (allArchived) {
                            this.baseMapper.updatePlanTestState(pqDevVo.getPlanId(), CheckStateEnum.CHECKED.getValue());
                        } else {
                            this.baseMapper.updatePlanTestState(pqDevVo.getPlanId(), CheckStateEnum.CHECKING.getValue());
                        }
                    }
                }
            }
        }
        return true;
    }

    @Override
    @Transactional
    public void updateResult(String devId, String userId) {
        PqDev dev = this.getById(devId);
        Integer checkState = pqMonitorService.getDevCheckState(devId);
        Integer checkResult = pqMonitorService.getDevCheckResult(devId);
        if (checkResult == 1) {
            checkResult = CheckResultEnum.ACCORD.getValue();
        }
        if (checkResult == 2) {
            checkResult = CheckResultEnum.NOT_ACCORD.getValue();
        }


        SysUser user = userService.getById(userId);

        // 查询当前设备的检测时间
        PqDevSub currentDevSub = pqDevSubService.lambdaQuery()
                .eq(PqDevSub::getDevId, devId)
                .one();

        LambdaUpdateChainWrapper<PqDevSub> w = pqDevSubService.lambdaUpdate()
                .set(PqDevSub::getCheckState, checkState)
                .set(PqDevSub::getCheckResult, checkResult)
                .set(PqDevSub::getReportState, DevReportStateEnum.NOT_GENERATED.getValue())
                .eq(PqDevSub::getDevId, devId);

        // 只有当checkTime为空时，才设置为当前时间
        if (currentDevSub != null && currentDevSub.getCheckEndTime() == null) {
            w.set(PqDevSub::getCheckEndTime, LocalDateTime.now());
            w.set(ObjectUtil.isNotNull(FormalTestManager.checkStartTime), PqDevSub::getCheckStartTime, currentDevSub.getCheckStartTime());
        }

        if (ObjectUtil.isNotNull(user) && (currentDevSub == null || StrUtil.isBlank(currentDevSub.getCheckBy()))) {
            w.set(PqDevSub::getCheckBy, user.getName());
        }
        if (checkState.equals(CheckStateEnum.CHECKED.getValue())) {
            w.set(PqDevSub::getReportState, DevReportStateEnum.NOT_GENERATED.getValue());
        }
        w.update();
        saveCheckHistory(devId, userId);

        PqDevParam.QueryParam param = new PqDevParam.QueryParam();
        String planId = dev.getPlanId();
        param.setPlanIdList(Arrays.asList(planId));
        List<PqDevVO> pqDevVOList = this.baseMapper.selectByQueryParam(param);

        if (CollUtil.isNotEmpty(pqDevVOList)) {
            Set<Integer> set = pqDevVOList.stream().filter(obj -> obj.getCheckState() != null && obj.getCheckState() >= CheckStateEnum.CHECKED.getValue()).map(PqDevVO::getCheckResult).collect(Collectors.toSet());
            if (checkState == CheckStateEnum.CHECKED.getValue()) {
                set.add(checkResult);
            }
            if (CollUtil.isEmpty(set)) {
                this.baseMapper.updatePlanCheckResult(planId, CheckResultEnum.UNCHECKED.getValue());
            } else {
                if (set.contains(CheckResultEnum.NOT_ACCORD.getValue())) {
                    this.baseMapper.updatePlanCheckResult(planId, CheckResultEnum.NOT_ACCORD.getValue());
                } else if (set.contains(CheckResultEnum.UNCHECKED.getValue())) {
                    this.baseMapper.updatePlanCheckResult(planId, CheckResultEnum.UNCHECKED.getValue());
                } else {
                    this.baseMapper.updatePlanCheckResult(planId, CheckResultEnum.ACCORD.getValue());
                }
            }


            boolean allUnchecked = pqDevVOList.stream().allMatch(obj -> CheckStateEnum.UNCHECKED.getValue().equals(obj.getCheckState()));
            boolean allArchived = pqDevVOList.stream().allMatch(obj -> obj.getCheckState() != null && obj.getCheckState() >= CheckStateEnum.DOCUMENTED.getValue());
            if (allUnchecked) {
                this.baseMapper.updatePlanTestState(planId, CheckStateEnum.UNCHECKED.getValue());
            } else if (allArchived) {
                this.baseMapper.updatePlanTestState(planId, CheckStateEnum.CHECKED.getValue());
            } else {
                this.baseMapper.updatePlanTestState(planId, CheckStateEnum.CHECKING.getValue());
            }
        }
    }

    private void saveCheckHistory(PqDevVO pqDevVo, Integer recheckNum, Integer checkState, String userId) {
        if (ObjectUtil.isNull(pqDevVo)
                || checkState == null
                || checkState < CheckStateEnum.CHECKED.getValue()) {
            return;
        }
        AdPlan plan = adPlanMapper.selectById(pqDevVo.getPlanId());
        if (ObjectUtil.isNull(plan)) {
            return;
        }
        pqDevVo.setRecheckNum(recheckNum);
        pqDevVo.setCheckBy(userId);
        pqDevVo.setCheckEndTime(LocalDateTime.now());
        pqDevCheckHistoryService.saveOrUpdateDeviceHistory(plan, pqDevVo);
    }

    private void saveCheckHistory(String devId, String userId) {
        PqDevVO pqDevVo = this.baseMapper.selectByDevId(devId);
        if (ObjectUtil.isNull(pqDevVo)
                || pqDevVo.getCheckState() == null
                || pqDevVo.getCheckState() < CheckStateEnum.CHECKED.getValue()) {
            return;
        }
        AdPlan plan = adPlanMapper.selectById(pqDevVo.getPlanId());
        if (ObjectUtil.isNull(plan)) {
            return;
        }
        if (StrUtil.isBlank(pqDevVo.getCheckBy())) {
            pqDevVo.setCheckBy(userId);
        }
        pqDevCheckHistoryService.saveOrUpdateDeviceHistory(plan, pqDevVo);
    }

    @Override
    public void updatePqDevReportState(String devId, int reportState) {
        LambdaUpdateWrapper<PqDevSub> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(PqDevSub::getDevId, devId)
                .set(PqDevSub::getReportState, reportState);
        pqDevSubService.update(updateWrapper);
    }

    @Override
    public long countUnReportDev(String planId) {
        // 查询该计划下所有设备的检测状态，是否有未生成的
        PqDevParam.QueryParam param = new PqDevParam.QueryParam();
        param.setPlanIdList(Arrays.asList(planId));
        List<PqDevVO> devVOList = this.baseMapper.selectByQueryParam(param);
        long count = devVOList.stream().filter(obj -> !DevReportStateEnum.GENERATED.getValue().equals(obj.getReportState())).count();
        return count;
    }


    @Override
    public List<PqDev> listByPlanIds(List<String> planIds) {
        return this.lambdaQuery().in(PqDev::getPlanId, planIds).eq(PqDev::getState, DataStateEnum.ENABLE.getCode()).list();
    }

    @Override
    public void exportDev(PqDevParam.QueryParam queryParam) {
        DictData dictData = dictDataService.getDictDataById(queryParam.getPattern());
        if (PatternEnum.CONTRAST.getValue().equals(dictData.getCode())) {
            this.exportContrastDev(queryParam);
        } else {
            String currrentScene = sysTestConfigService.getCurrrentScene();
            SceneEnum sceneEnum = SceneEnum.getSceneEnum(currrentScene);
            switch (sceneEnum) {
                case PROVINCE_PLATFORM:
                    this.exportProvinceDev(queryParam);
                    break;
                case LEAVE_FACTORY_TEST:
                    this.exportCNDev(queryParam);
                    break;
                case SELF_TEST:
                    break;
                default:
                    break;
            }
        }
    }

    @Override
    public void downloadTemplate(String patternId) {
        DictData dictData = dictDataService.getDictDataById(patternId);
        if (PatternEnum.CONTRAST.getValue().equals(dictData.getCode())) {
            ExcelUtil.exportExcelPullDown(new ExportParams(), "被检设备模板.xlsx", 2, this.getContrastDevPullDownList(0), ContrastDevExcel.class, new ArrayList<>());
        } else {
            String currrentScene = sysTestConfigService.getCurrrentScene();
            SceneEnum sceneEnum = SceneEnum.getSceneEnum(currrentScene);
            switch (sceneEnum) {
                case PROVINCE_PLATFORM:
                    ExcelUtil.exportExcelPullDown(new ExportParams(), "被检设备模板.xlsx", 1, this.getProvinceDevPullDownList(0), ProvinceDevExcel.class, new ArrayList<>());
                    break;
                case LEAVE_FACTORY_TEST:
                    ExcelUtil.exportExcelPullDown(new ExportParams(), "被检设备模板.xlsx", 1, this.getCNDevPullDownList(0), CNDevExcel.class, new ArrayList<>());
                    break;
                case SELF_TEST:
                    break;
                default:
                    break;
            }
        }
    }

    @Override
    public HttpResult importDev(MultipartFile file, String patternId, String planId, HttpServletResponse response, Integer cover) {
        DictData dictData = dictDataService.getDictDataById(patternId);
        if (PatternEnum.CONTRAST.getValue().equals(dictData.getCode())) {
            return this.importContrastDev(file, patternId, planId, response, cover);
        } else {
            String currrentScene = sysTestConfigService.getCurrrentScene();
            SceneEnum sceneEnum = SceneEnum.getSceneEnum(currrentScene);
            switch (sceneEnum) {
                case PROVINCE_PLATFORM:
                    boolean result = this.importProvinceDev(file, patternId, planId, response);
                    if (result) {
                        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, "导入成功");
                    }
                case LEAVE_FACTORY_TEST:
                    result = this.importCNDev(file, patternId, planId, response);
                    if (result) {
                        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, "导入成功");
                    }
                case SELF_TEST:
                    break;
                default:
                    break;
            }
        }
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, "导入失败");
    }

    @Override
    @Transactional
    public boolean importCNDev(MultipartFile file, String patternId, String planId, HttpServletResponse response) {
        ImportParams params = new ImportParams();
        params.setStartSheetIndex(0);
        params.setSheetNum(1);
        params.setHeadRows(1);
        params.setNeedVerify(true);

        List<CNDevExcel> cnDevExcelList = null;
        try {
            ExcelImportResult<CNDevExcel> excelImportResult = ExcelImportUtil.importExcelMore(file.getInputStream(), CNDevExcel.class, params);
            if (excelImportResult.isVerifyFail()) {
                // 此处前端要做特殊处理，具体可以参考技术监督的数据导入
                Workbook failWorkbook = excelImportResult.getFailWorkbook();
                PoiUtil.exportFileByWorkbook(failWorkbook, "非法被检设备数据.xlsx", response);
            } else {
                cnDevExcelList = excelImportResult.getList();
            }
        } catch (Exception e) {
            throw new BusinessException(DetectionResponseEnum.IMPORT_DATA_FAIL);
        }
        return this.importCNDev(cnDevExcelList, patternId, planId);
    }

    @Override
    @Transactional
    public boolean importCNDev(List<CNDevExcel> cnDevExcelList, String patternId, String planId) {
        if (CollUtil.isNotEmpty(cnDevExcelList)) {

            List<PqDev> oldDevList = BeanUtil.copyToList(cnDevExcelList, PqDev.class);
            //逆向可视化
            this.reverseVisualizeCNDev(oldDevList, patternId);

            List<PqDev> newDevList = new ArrayList<>();
            PqDevParam param = new PqDevParam();
            oldDevList.forEach(pqDev -> {
                pqDev.setPlanId(planId);
                if (pqDev.getEncryptionFlag() == 1) {
                    if (StrUtil.isNotBlank(pqDev.getSeries()) && StrUtil.isNotBlank(pqDev.getDevKey())) {
                        pqDev.setSeries(EncryptionUtil.encodeString(1, pqDev.getSeries()));
                        pqDev.setDevKey(EncryptionUtil.encodeString(1, pqDev.getDevKey()));
                    } else {
                        throw new BusinessException(DetectionResponseEnum.SERIES_AND_DEVKEY_NOT_BLANK);
                    }
                } else {
                    pqDev.setSeries(null);
                    pqDev.setDevKey(null);
                }

                String name = pqDev.getName();
                if (name.contains("-")) {
                    String[] split = name.split("-");
                    if (split.length == 2) {
                        long start = Long.parseLong(split[0]);
                        long end = Long.parseLong(split[1]);
                        // 避免起始大于结束
                        if (start > end) {
                            long temp = start;
                            start = end;
                            end = temp;
                        }
                        for (long i = start; i <= end; i++) {
                            PqDev dev = new PqDev();
                            BeanUtil.copyProperties(pqDev, dev);
                            dev.setName(String.valueOf(i));
                            dev.setCreateId(String.valueOf(i));
                            dev.setPlanId(planId);
                            dev.setManufacturer("8fa73802c9e1abab973adcbeb0d58567"); // 南京灿能字典项目对应的id

                            param.setName(dev.getName());
                            param.setCreateId(dev.getName());
                            param.setDevType(dev.getDevType());
                            this.checkRepeat(param, false);

                            long count = newDevList.stream().filter(d -> d.getName().equals(dev.getName())).count();
                            if (count == 0) {
                                newDevList.add(dev);
                            }
                        }
                    } else {
                        throw new BusinessException(DetectionResponseEnum.IMPORT_DATA_FORMAT_FAIL);
                    }
                } else {
                    param.setName(pqDev.getName());
                    param.setCreateId(pqDev.getCreateId());
                    param.setDevType(pqDev.getDevType());
                    this.checkRepeat(param, false);
                    pqDev.setManufacturer("8fa73802c9e1abab973adcbeb0d58567"); // 南京灿能字典项目对应的id

                    long count = newDevList.stream().filter(dev -> dev.getName().equals(pqDev.getName()) && dev.getCreateId().equals(pqDev.getCreateId())).count();
                    if (count == 0) {
                        newDevList.add(pqDev);
                    } else {
                        throw new BusinessException(DetectionResponseEnum.NAME_OR_CREATEID_REPEAT);
                    }
                }
            });

            this.saveBatch(newDevList);
            List<PqDevSub> pqDevSubList = new ArrayList<>();
            for (PqDev dev : newDevList) {
                PqDevSub pqDevSub = new PqDevSub();
                pqDevSub.setDevId(dev.getId());
                pqDevSub.setCheckState(CheckStateEnum.UNCHECKED.getValue());
                pqDevSub.setCheckResult(CheckResultEnum.UNCHECKED.getValue());
                pqDevSub.setReportState(DevReportStateEnum.UNCHECKED.getValue());
                pqDevSub.setTimeCheckResult(TimeCheckResultEnum.UNKNOWN.getValue());
                pqDevSub.setFactorCheckResult(FactorCheckResultEnum.UNKNOWN.getValue());
                pqDevSubList.add(pqDevSub);
            }
            pqDevSubService.saveBatch(pqDevSubList);
            return true;
        }
        return false;
    }

    /**
     * 检查设备是否重复
     *
     * @param param         设备参数
     * @param isExcludeSelf 是否排除自己
     */
    private void checkRepeat(PqDevParam param, boolean isExcludeSelf) {
        LambdaQueryWrapper<PqDev> queryWrapper = new LambdaQueryWrapper<PqDev>()
                .eq(PqDev::getState, DataStateEnum.ENABLE.getCode())
                .eq(StrUtil.isNotBlank(param.getIp()), PqDev::getIp, param.getIp())
                .eq(ObjectUtil.isNotNull(param.getPort()), PqDev::getPort, param.getPort())
                .eq(StrUtil.isNotBlank(param.getPattern()), PqDev::getPattern, param.getPattern())
                .eq(StrUtil.isNotBlank(param.getCreateId()), PqDev::getCreateId, param.getCreateId());
        if (isExcludeSelf) {
            if (param instanceof PqDevParam.UpdateParam) {
                queryWrapper.ne(PqDev::getId, ((PqDevParam.UpdateParam) param).getId());
            }
        }
        int count = this.count(queryWrapper);
        if (count > 0) {
            throw new BusinessException(DetectionResponseEnum.PQ_DEV_REPEAT, "【" + param.getName() + "】被检设备已存在");
        }
    }

    /**
     * 导出灿能二楼设备数据
     *
     * @param queryParam
     */
    private void exportCNDev(PqDevParam.QueryParam queryParam) {
        List<PqDev> pqDevs = this.list(this.getQueryWrapper(queryParam));
        List<String> idList = pqDevs.stream().map(dev -> dev.getId()).collect(Collectors.toList());
        List<PqDevVO> pqDevVOList = new ArrayList<>();
        if (CollUtil.isNotEmpty(idList)) {
            pqDevVOList.addAll(this.baseMapper.listByDevIds(idList));
        }

        List<CNDevExcel> pqDevExcels = null;
        if (ObjectUtil.isNotEmpty(pqDevs)) {
            this.visualizeCNDev(pqDevVOList);
            pqDevExcels = BeanUtil.copyToList(pqDevVOList, CNDevExcel.class);
        }

        ExcelUtil.exportExcelPullDown(new ExportParams(), "被检设备导出数据.xlsx", 1, this.getCNDevPullDownList(0), CNDevExcel.class, ObjectUtil.isEmpty(pqDevExcels) ? new ArrayList<>() : pqDevExcels);
    }

    @Override
    public List<PullDown> getProvinceDevPullDownList(int startCol) {
        List<PullDown> pullDowns = new ArrayList<>();
        List<DictData> dictDataList = null;
        PullDown pullDown = null;

        // 设备类型
        List<DevType> devTypeList = devTypeService.listAll();
        pullDown = new PullDown();
        pullDown.setFirstCol(startCol + 2);
        pullDown.setLastCol(startCol + 2);
        pullDown.setStrings(devTypeList.stream().map(DevType::getName).collect(Collectors.toList()));
        pullDowns.add(pullDown);

        // 设备厂家
        DictType dictType = dictTypeService.getByCode("Dev_Manufacturers");
        if (ObjectUtil.isNotNull(dictType)) {
            dictDataList = dictDataService.getDictDataByTypeId(dictType.getId());

            pullDown = new PullDown();
            pullDown.setFirstCol(startCol + 3);
            pullDown.setLastCol(startCol + 3);
            pullDown.setStrings(dictDataList.stream().map(DictData::getName).collect(Collectors.toList()));
            pullDowns.add(pullDown);
        }

        // 通讯协议
        dictType = dictTypeService.getByCode("Protocol");
        if (ObjectUtil.isNotNull(dictType)) {
            dictDataList = dictDataService.getDictDataByTypeId(dictType.getId());

            pullDown = new PullDown();
            pullDown.setFirstCol(startCol + 7);
            pullDown.setLastCol(startCol + 7);
            pullDown.setStrings(dictDataList.stream().map(DictData::getName).collect(Collectors.toList()));
            pullDowns.add(pullDown);
        }

        // 是否加密
        pullDown = new PullDown();
        pullDown.setFirstCol(startCol + 8);
        pullDown.setLastCol(startCol + 8);
        pullDown.setStrings(Arrays.asList("是", "否"));
        pullDowns.add(pullDown);


        // 委托方
        dictType = dictTypeService.getByCode("Delegate");
        if (ObjectUtil.isNotNull(dictType)) {
            dictDataList = dictDataService.getDictDataByTypeId(dictType.getId());

            pullDown = new PullDown();
            pullDown.setFirstCol(startCol + 15);
            pullDown.setLastCol(startCol + 15);
            pullDown.setStrings(dictDataList.stream().map(DictData::getName).collect(Collectors.toList()));
            pullDowns.add(pullDown);
        }

        return pullDowns;
    }

    @Override
    public List<PullDown> getCNDevPullDownList(int startCol) {
        List<PullDown> pullDowns = new ArrayList<>();
        // 预投计划
        List<DictData> dictDataList = null;
        PullDown pullDown = null;

        DictType dictType = dictTypeService.getByCode("Preinvestment_Plan");
        if (ObjectUtil.isNotNull(dictType)) {
            dictDataList = dictDataService.getDictDataByTypeId(dictType.getId());

            pullDown = new PullDown();
            pullDown.setFirstCol(startCol);
            pullDown.setLastCol(startCol);
            pullDown.setStrings(dictDataList.stream().map(DictData::getName).collect(Collectors.toList()));
            pullDowns.add(pullDown);
        }

        // 设备类型
        List<DevType> devTypeList = devTypeService.listAll();
        pullDown = new PullDown();
        pullDown.setFirstCol(startCol + 2);
        pullDown.setLastCol(startCol + 2);
        pullDown.setStrings(devTypeList.stream().map(DevType::getName).collect(Collectors.toList()));
        pullDowns.add(pullDown);


        // 通讯协议
        dictType = dictTypeService.getByCode("Protocol");
        if (ObjectUtil.isNotNull(dictType)) {
            dictDataList = dictDataService.getDictDataByTypeId(dictType.getId());

            pullDown = new PullDown();
            pullDown.setFirstCol(startCol + 3);
            pullDown.setLastCol(startCol + 3);
            pullDown.setStrings(dictDataList.stream().map(DictData::getName).collect(Collectors.toList()));
            pullDowns.add(pullDown);
        }

        // 是否加密
        pullDown = new PullDown();
        pullDown.setFirstCol(startCol + 4);
        pullDown.setLastCol(startCol + 4);
        pullDown.setStrings(Arrays.asList("是", "否"));
        pullDowns.add(pullDown);


        // 是否支持系数校准
        pullDown = new PullDown();
        pullDown.setFirstCol(startCol + 7);
        pullDown.setLastCol(startCol + 7);
        pullDown.setStrings(Arrays.asList("是", "否"));
        pullDowns.add(pullDown);

        return pullDowns;
    }

    /**
     * 可视化灿能二楼设备数据
     *
     * @param pqDevs
     */
    public void visualizeCNDev(List<PqDevVO> pqDevs) {
        pqDevs.forEach(pqDev -> {
            if (StrUtil.isNotBlank(pqDev.getSeries())) {
                pqDev.setSeries(EncryptionUtil.decoderString(1, pqDev.getSeries()));
            }
            if (StrUtil.isNotBlank(pqDev.getDevKey())) {
                pqDev.setDevKey(EncryptionUtil.decoderString(1, pqDev.getDevKey()));
            }
            if (StrUtil.isNotBlank(pqDev.getPreinvestmentPlan())) {
                pqDev.setPreinvestmentPlan(dictDataService.getDictDataById(pqDev.getPreinvestmentPlan()).getName());
            }
            pqDev.setDevType(devTypeService.getById(pqDev.getDevType()).getName());
            pqDev.setProtocol(dictDataService.getDictDataById(pqDev.getProtocol()).getName());
        });
    }

    /**
     * 逆向可视化灿能二楼设备数据
     *
     * @param pqDevs    设备列表
     * @param patternId 模式Id
     */
    public void reverseVisualizeCNDev(List<PqDev> pqDevs, String patternId) {
        pqDevs.forEach(pqDev -> {
            DictData preinvestmentPlanDictData = dictDataService.getDictDataByName(pqDev.getPreinvestmentPlan());
            if (ObjectUtil.isNull(preinvestmentPlanDictData)) {
                throw new BusinessException(DetectionResponseEnum.PREINVESTMENT_PLAN_NOT_EXIST);
            } else {
                pqDev.setPreinvestmentPlan(preinvestmentPlanDictData.getId());
            }

            DevType devType = devTypeService.getByName(pqDev.getDevType());
            if (ObjectUtil.isNull(devType)) {
                throw new BusinessException(DetectionResponseEnum.DEV_TYPE_NOT_EXIST);
            } else {
                pqDev.setDevType(devType.getId());
            }
            pqDev.setPattern(patternId);
            DictData protocolDictData = dictDataService.getDictDataByName(pqDev.getProtocol());
            if (ObjectUtil.isNull(protocolDictData)) {
                throw new BusinessException(DetectionResponseEnum.PROTOCOL_NOT_EXIST);
            } else {
                pqDev.setProtocol(protocolDictData.getId());
            }
            pqDev.setCreateDate(LocalDateTime.now().toLocalDate());
            pqDev.setCreateId(pqDev.getName());

//            pqDev.setTimeCheckResult(TimeCheckResultEnum.UNKNOWN.getValue());
//            pqDev.setFactorCheckResult(FactorCheckResultEnum.UNKNOWN.getValue());
//            pqDev.setCheckState(CheckStateEnum.UNCHECKED.getValue());
//            pqDev.setReportState(DevReportStateEnum.UNCHECKED.getValue());
//            pqDev.setCheckResult(CheckResultEnum.UNCHECKED.getValue());
            pqDev.setState(DataStateEnum.ENABLE.getCode());
        });
    }

    /**
     * 导出比对式设备数据
     *
     * @param queryParam
     */
    private void exportContrastDev(PqDevParam.QueryParam queryParam) {
        List<PqDev> pqDevs = this.list(this.getQueryWrapper(queryParam));
        List<String> idList = pqDevs.stream().map(dev -> dev.getId()).collect(Collectors.toList());
        List<PqDevVO> pqDevVOList = new ArrayList<>();
        if (CollUtil.isNotEmpty(idList)) {
            pqDevVOList.addAll(this.baseMapper.listByDevIds(idList));
        }

        List<ContrastDevExcel> contrastDevExcelList = this.getExportContrastDevData(pqDevVOList);

        ExcelUtil.exportExcelPullDown(new ExportParams(), "被检设备导出数据.xlsx", 2, this.getContrastDevPullDownList(0), ContrastDevExcel.class, ObjectUtil.isEmpty(contrastDevExcelList) ? new ArrayList<>() : contrastDevExcelList);
    }

    /**
     * 导出省级平台设备数据
     *
     * @param queryParam
     */
    private void exportProvinceDev(PqDevParam.QueryParam queryParam) {
        List<PqDev> pqDevs = this.list(this.getQueryWrapper(queryParam));
        List<String> idList = pqDevs.stream().map(dev -> dev.getId()).collect(Collectors.toList());
        List<PqDevVO> pqDevVOList = new ArrayList<>();
        if (CollUtil.isNotEmpty(idList)) {
            pqDevVOList.addAll(this.baseMapper.listByDevIds(idList));
        }

        List<ProvinceDevExcel> pqDevExcels = null;
        if (ObjectUtil.isNotEmpty(pqDevVOList)) {
            this.visualizeProvinceDev(pqDevVOList);
            pqDevExcels = BeanUtil.copyToList(pqDevVOList, ProvinceDevExcel.class);
        }
        ExcelUtil.exportExcelPullDown(new ExportParams(), "被检设备导出数据.xlsx", 1, this.getProvinceDevPullDownList(0), ProvinceDevExcel.class, ObjectUtil.isEmpty(pqDevExcels) ? new ArrayList<>() : pqDevExcels);
    }


    @Override
    @Transactional
    public boolean importProvinceDev(MultipartFile file, String patternId, String planId, HttpServletResponse response) {
        ImportParams params = new ImportParams();
        params.setStartSheetIndex(0);
        params.setSheetNum(1);
        params.setNeedVerify(true);
        params.setHeadRows(1);

        List<ProvinceDevExcel> provincDevExcelList = null;
        try {
            ExcelImportResult<ProvinceDevExcel> excelImportResult = ExcelImportUtil.importExcelMore(file.getInputStream(), ProvinceDevExcel.class, params);
            if (excelImportResult.isVerifyFail()) {
                // 此处前端要做特殊处理，具体可以参考技术监督的数据导入
                Workbook failWorkbook = excelImportResult.getFailWorkbook();
                PoiUtil.exportFileByWorkbook(failWorkbook, "非法被检设备数据.xlsx", response);
            } else {
                provincDevExcelList = excelImportResult.getList();
            }

        } catch (Exception e) {
            throw new BusinessException(DetectionResponseEnum.IMPORT_DATA_FAIL);
        }
        return this.importProvinceDev(provincDevExcelList, patternId, planId);
    }

    @Override
    @Transactional
    public boolean importProvinceDev(List<ProvinceDevExcel> proviceDevExcelList, String patternId, String planId) {
        if (CollUtil.isNotEmpty(proviceDevExcelList)) {
            List<PqDev> oldDevList = BeanUtil.copyToList(proviceDevExcelList, PqDev.class);

            //逆向可视化
            this.reverseVisualizeProvinceDev(oldDevList, patternId);

            List<PqDev> newDevList = new ArrayList<>();
            PqDevParam param = new PqDevParam();
            oldDevList.forEach(pqDev -> {
                DevType devType = devTypeService.getByName(pqDev.getDevType());
                if (ObjectUtil.isNull(devType)) {
                    throw new BusinessException(DetectionResponseEnum.DEV_TYPE_NOT_EXIST);
                } else {
                    pqDev.setDevType(devType.getId());
                }
                pqDev.setCreateDate(LocalDateTime.now().toLocalDate());
                pqDev.setPlanId(planId);
                if (pqDev.getEncryptionFlag() == 1) {
                    if (StrUtil.isNotBlank(pqDev.getSeries()) && StrUtil.isNotBlank(pqDev.getDevKey())) {
                        pqDev.setSeries(EncryptionUtil.encodeString(1, pqDev.getSeries()));
                        pqDev.setDevKey(EncryptionUtil.encodeString(1, pqDev.getDevKey()));
                    } else {
                        throw new BusinessException(DetectionResponseEnum.SERIES_AND_DEVKEY_NOT_BLANK);
                    }
                } else {
                    pqDev.setSeries(null);
                    pqDev.setDevKey(null);
                }

                String name = pqDev.getName();
                if (name.contains("-")) {
                    String[] split = name.split("-");
                    if (split.length == 2) {
                        long start = Long.parseLong(split[0]);
                        long end = Long.parseLong(split[1]);
                        // 避免起始大于结束
                        if (start > end) {
                            long temp = start;
                            start = end;
                            end = temp;
                        }
                        for (long i = start; i <= end; i++) {
                            PqDev dev = new PqDev();
                            BeanUtil.copyProperties(pqDev, dev);
                            dev.setName(String.valueOf(i));
                            // todo 这里建议和实际情况结合
                            dev.setCreateId(String.valueOf(i));
                            dev.setPlanId(planId);

                            param.setName(dev.getName());
                            //param.setCreateId(dev.getName());
                            param.setDevType(dev.getDevType());
                            //this.checkRepeat(param, false);

                            long count = newDevList.stream().filter(d ->
                                            d.getDevType().equals(dev.getDevType()) && (d.getName().equals(dev.getName()) || d.getCreateId().equals(dev.getCreateId())))
                                    .count();
                            if (count == 0) {
                                newDevList.add(dev);
                            }
                        }
                    } else {
                        throw new BusinessException(DetectionResponseEnum.IMPORT_DATA_FORMAT_FAIL);
                    }
                } else {
                    param.setName(pqDev.getName());
                    param.setCreateId(pqDev.getCreateId());
                    param.setDevType(pqDev.getDevType());
                    this.checkRepeat(param, false);

                    long count = newDevList.stream().filter(dev ->
                            dev.getDevType().equals(pqDev.getDevType()) && (dev.getName().equals(pqDev.getName()) || dev.getCreateId().equals(pqDev.getCreateId()))
                    ).count();
                    if (count == 0) {
                        newDevList.add(pqDev);
                    } else {
                        throw new BusinessException(DetectionResponseEnum.NAME_OR_CREATEID_REPEAT);
                    }
                }
            });

            this.saveBatch(newDevList);
            List<PqDevSub> pqDevSubList = new ArrayList<>();
            for (PqDev dev : newDevList) {
                PqDevSub pqDevSub = new PqDevSub();
                pqDevSub.setDevId(dev.getId());
                pqDevSub.setCheckState(CheckStateEnum.UNCHECKED.getValue());
                pqDevSub.setCheckResult(CheckResultEnum.UNCHECKED.getValue());
                pqDevSub.setReportState(DevReportStateEnum.UNCHECKED.getValue());
                pqDevSub.setTimeCheckResult(TimeCheckResultEnum.UNKNOWN.getValue());
                pqDevSub.setFactorCheckResult(FactorCheckResultEnum.UNKNOWN.getValue());
                pqDevSubList.add(pqDevSub);
            }
            pqDevSubService.saveBatch(pqDevSubList);
            return true;
        }
        return false;
    }

    @Override
    public HttpResult importContrastDev(MultipartFile file, String patternId, String planId, HttpServletResponse response, Integer cover) {
        ImportParams params = new ImportParams();
        params.setStartSheetIndex(0);
        params.setSheetNum(1);
        params.setNeedVerify(true);
        params.setHeadRows(2);

        List<ContrastDevExcel> contrastDevExcelList = null;
        try {
            ExcelImportResult<ContrastDevExcel> excelImportResult = ExcelImportUtil.importExcelMore(file.getInputStream(), ContrastDevExcel.class, params);
            if (excelImportResult.isVerifyFail()) {
                // 此处前端要做特殊处理，具体可以参考技术监督的数据导入
                Workbook failWorkbook = excelImportResult.getFailWorkbook();
                PoiUtil.exportFileByWorkbook(failWorkbook, "非法被检设备数据.xlsx", response);
            } else {
                contrastDevExcelList = excelImportResult.getList();
            }
        } catch (Exception e) {
            throw new BusinessException(DetectionResponseEnum.IMPORT_DATA_FAIL);
        }
        return this.importContrastDev(contrastDevExcelList, patternId, planId, cover);
    }

    @Override
    @Transactional
    public HttpResult importContrastDev(List<ContrastDevExcel> contrastDevExcelList, String patternId, String planId, Integer cover) {
        if (CollUtil.isNotEmpty(contrastDevExcelList)) {
            // 根据设备名称分组
            Map<String, List<ContrastDevExcel>> listMap = contrastDevExcelList.stream()
                    .collect(Collectors.groupingBy(ContrastDevExcel::getName, LinkedHashMap::new, Collectors.toList()));
            List<PqDev> newDevList = new ArrayList<>(listMap.size());
            List<PqDev> updateDevList = new ArrayList<>();
            List<PqDev> finalUpdateDevList = new ArrayList<>();
            for (Map.Entry<String, List<ContrastDevExcel>> entry : listMap.entrySet()) {
                String name = entry.getKey();

                List<ContrastDevExcel> devExcelList = entry.getValue();
                // 监测点数据
                List<PqMonitorExcel> pqMonitorExcelList = devExcelList.stream()
                        .map(ContrastDevExcel::getPqMonitorExcelList)
                        .filter(Objects::nonNull)
                        .flatMap(List::stream)
                        // 过滤掉没有线路号的数据
                        .filter(item -> ObjectUtil.isNotNull(item.getNum()))
                        .collect(Collectors.toList());
                // 取第一条为设备基本信息
                ContrastDevExcel devExcel = devExcelList.get(0);
                PqDev importPqDev = BeanUtil.copyProperties(devExcel, PqDev.class);
                if (importPqDev.getEncryptionFlag() == 1) {
                    if (StrUtil.isNotBlank(importPqDev.getSeries()) && StrUtil.isNotBlank(importPqDev.getDevKey())) {
                        importPqDev.setSeries(EncryptionUtil.encodeString(1, importPqDev.getSeries()));
                        importPqDev.setDevKey(EncryptionUtil.encodeString(1, importPqDev.getDevKey()));
                    } else {
                        throw new BusinessException(DetectionResponseEnum.SERIES_AND_DEVKEY_NOT_BLANK);
                    }
                }
                DevType devType = devTypeService.getByName(importPqDev.getDevType());
                if (ObjectUtil.isNull(devType)) {
                    throw new BusinessException(DetectionResponseEnum.DEV_TYPE_NOT_EXIST);
                }
                // 校验监测点数量
                int devChns = devType.getDevChns();
//                if (pqMonitorExcelList.size() != devChns) {
//                    throw new BusinessException(DetectionResponseEnum.IMPORT_DATA_FAIL, "【" + name + "】的设备类型必须具备" + devChns + "个监测点信息！");
//                }
                List<Integer> numList = pqMonitorExcelList.stream().map(PqMonitorExcel::getNum).collect(Collectors.toList());
                // 判断是否有重复的num
                Set<Integer> uniqueNumSet = new HashSet<>(numList);
                if (uniqueNumSet.size() != numList.size()) {
                    throw new BusinessException(DetectionResponseEnum.MONITOR_NUM_REPEAT, "【" + name + "】该被检设备下存在相同线路号的监测点！");
                }
                Integer max = CollectionUtil.max(numList);
                Integer min = CollectionUtil.min(numList);
                if (min < 1 || max > devChns) {
                    throw new BusinessException(DetectionResponseEnum.MONITOR_NUM_OUT_OF_RANGE);
                }

                importPqDev.setDevType(devType.getId());
                importPqDev.setImportFlag(1);
                importPqDev.setId(UUID.randomUUID().toString().replaceAll("-", ""));

                if (StrUtil.isEmpty(importPqDev.getCreateId())) {
                    // 与设备名称相同
                    importPqDev.setCreateId(importPqDev.getName());
                }
                // 根据num排序
                pqMonitorExcelList.sort(Comparator.comparingInt(PqMonitorExcel::getNum));
                StringBuilder inspectChannelBuilder = new StringBuilder();
                for (int i = 0; i < numList.size(); i++) {
                    inspectChannelBuilder.append(numList.get(i));
                    if (i < numList.size() - 1) {
                        inspectChannelBuilder.append(StrUtil.COMMA);
                    }
                }
                importPqDev.setInspectChannel(inspectChannelBuilder.toString());
                List<PqMonitor> monitorList = new ArrayList<>();
                // 1、先判断是否有谐波系统ID
                boolean hasHarmSys = false;
                if (StrUtil.isNotBlank(importPqDev.getHarmSysId())) {
                    // 1.1、如果有则判断是否已存在
                    List<PqDev> hasList = this.lambdaQuery()
                            .eq(PqDev::getState, DataStateEnum.ENABLE.getCode())
                            .eq(PqDev::getPattern, patternId)
                            .eq(PqDev::getHarmSysId, importPqDev.getHarmSysId())
                            .isNull(PqDev::getPlanId)
                            .orderByDesc(PqDev::getCreateTime).list();
                    // 1.2、 存在则放入强制更新list
                    if (CollUtil.isNotEmpty(hasList)) {
                        importPqDev.setId(hasList.get(0).getId());
                        for (PqMonitorExcel pqMonitorExcel : pqMonitorExcelList) {
                            PqMonitor monitor = BeanUtil.copyProperties(pqMonitorExcel, PqMonitor.class);
                            monitor.setDevId(importPqDev.getId());
                            monitor.setPt(pqMonitorExcel.getPt1() + StrUtil.C_COLON + pqMonitorExcel.getPt2());
                            monitor.setCt(pqMonitorExcel.getCt1() + StrUtil.C_COLON + pqMonitorExcel.getCt2());
                            monitorList.add(monitor);
                        }
                        importPqDev.setMonitorList(monitorList);
                        finalUpdateDevList.add(importPqDev);
                        hasHarmSys = true;
                    }

                }
                if (!hasHarmSys) {
                    // 2、查询 ip + 端口 + 序列号 + patternId + Plan_Id为空 是否存在
                    List<PqDev> hasList = this.lambdaQuery()
                            .eq(PqDev::getState, DataStateEnum.ENABLE.getCode())
                            .eq(PqDev::getIp, devExcel.getIp())
                            .eq(PqDev::getPort, devExcel.getPort())
                            .eq(PqDev::getPattern, patternId)
                            .eq(PqDev::getCreateId, devExcel.getCreateId())
                            .isNull(PqDev::getPlanId)
                            .orderByDesc(PqDev::getCreateTime).list();
                    // 2.1、存在则放入更新list
                    if (CollUtil.isNotEmpty(hasList)) {
                        importPqDev.setId(hasList.get(0).getId());
                        for (PqMonitorExcel pqMonitorExcel : pqMonitorExcelList) {
                            PqMonitor monitor = BeanUtil.copyProperties(pqMonitorExcel, PqMonitor.class);
                            monitor.setDevId(importPqDev.getId());
                            monitor.setPt(pqMonitorExcel.getPt1() + StrUtil.C_COLON + pqMonitorExcel.getPt2());
                            monitor.setCt(pqMonitorExcel.getCt1() + StrUtil.C_COLON + pqMonitorExcel.getCt2());
                            monitorList.add(monitor);
                        }
                        importPqDev.setMonitorList(monitorList);
                        updateDevList.add(importPqDev);
                    } else {
                        //2.2、不存在则放入新增list
                        for (PqMonitorExcel pqMonitorExcel : pqMonitorExcelList) {
                            PqMonitor monitor = BeanUtil.copyProperties(pqMonitorExcel, PqMonitor.class);
                            monitor.setDevId(importPqDev.getId());
                            monitor.setPt(pqMonitorExcel.getPt1() + StrUtil.C_COLON + pqMonitorExcel.getPt2());
                            monitor.setCt(pqMonitorExcel.getCt1() + StrUtil.C_COLON + pqMonitorExcel.getCt2());
                            monitorList.add(monitor);
                        }
                        importPqDev.setMonitorList(monitorList);
                        newDevList.add(importPqDev);
                    }
                }
            }
            // 3、是否有重复的设备
            if (CollUtil.isNotEmpty(updateDevList)) {
                // 3.1、有则让用户确认是否覆盖
                if (cover == 0) {
                    List<String> existDevList = new ArrayList<>();
                    updateDevList.forEach(pqDev -> {
                        existDevList.add(pqDev.getName() + "(" + pqDev.getIp() + ":" + pqDev.getPort() + ")");
                    });
                    return HttpResultUtil.assembleResult(DetectionResponseEnum.DEV_IP_PORT_EXIST.getCode(), existDevList, "请确认是否覆盖");

                } else {
                    // 3.2、确认覆盖则放入强制更新list
                    finalUpdateDevList.addAll(updateDevList);
                }
            }


            // 4、新增list
            if (CollUtil.isNotEmpty(newDevList)) {
                //逆向可视化
                this.reverseVisualizeProvinceDev(newDevList, patternId);
                this.saveBatch(newDevList);
                List<PqDevSub> pqDevSubList = new ArrayList<>();
                List<PqMonitor> newMonitorList = new ArrayList<>();
                for (PqDev dev : newDevList) {
                    PqDevSub pqDevSub = new PqDevSub();
                    pqDevSub.setDevId(dev.getId());
                    pqDevSub.setCheckState(CheckStateEnum.UNCHECKED.getValue());
                    pqDevSub.setCheckResult(CheckResultEnum.UNCHECKED.getValue());
                    pqDevSub.setReportState(DevReportStateEnum.UNCHECKED.getValue());
                    pqDevSub.setTimeCheckResult(TimeCheckResultEnum.UNKNOWN.getValue());
                    pqDevSub.setFactorCheckResult(FactorCheckResultEnum.UNKNOWN.getValue());
                    pqDevSubList.add(pqDevSub);
                    newMonitorList.addAll(dev.getMonitorList());
                }
                pqMonitorService.reverseVisualizeMonitor(newMonitorList);
                pqMonitorService.saveBatch(newMonitorList);
                pqDevSubService.saveBatch(pqDevSubList);
            }
            // 5、更新list
            if (CollUtil.isNotEmpty(finalUpdateDevList)) {
                //逆向可视化
                this.reverseVisualizeProvinceDev(finalUpdateDevList, patternId);
                this.updateBatchById(finalUpdateDevList);
                List<PqMonitor> updateMonitorList = new ArrayList<>();
                for (PqDev dev : finalUpdateDevList) {
                    updateMonitorList.addAll(dev.getMonitorList());
                }
                pqMonitorService.reverseVisualizeMonitor(updateMonitorList);
                for (PqMonitor monitor : updateMonitorList) {
                    pqMonitorService.update(monitor, new LambdaUpdateWrapper<PqMonitor>().eq(PqMonitor::getDevId, monitor.getDevId()));
                }
            }
            return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, "导入成功");
        }
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, "导入失败");

    }

    @Override
    public List<PullDown> getContrastDevPullDownList(int startCol) {
        List<PullDown> pullDowns = new ArrayList<>();
        List<DictData> dictDataList = null;
        PullDown pullDown = null;

        // 设备类型
        List<DevType> devTypeList = devTypeService.listAll();
        pullDown = new PullDown();

        pullDown.setFirstCol(startCol + 5);
        pullDown.setLastCol(startCol + 5);

        pullDown.setStrings(devTypeList.stream().map(DevType::getName).collect(Collectors.toList()));
        pullDowns.add(pullDown);

        // 设备厂家
        DictType dictType = dictTypeService.getByCode("Dev_Manufacturers");
        if (ObjectUtil.isNotNull(dictType)) {
            dictDataList = dictDataService.getDictDataByTypeId(dictType.getId());

            pullDown = new PullDown();
            pullDown.setFirstCol(startCol + 6);
            pullDown.setLastCol(startCol + 6);

            pullDown.setStrings(dictDataList.stream().map(DictData::getName).collect(Collectors.toList()));
            pullDowns.add(pullDown);
        }

        // 通讯协议
        dictType = dictTypeService.getByCode("Protocol");
        if (ObjectUtil.isNotNull(dictType)) {
            dictDataList = dictDataService.getDictDataByTypeId(dictType.getId());

            pullDown = new PullDown();
            pullDown.setFirstCol(startCol + 9);
            pullDown.setLastCol(startCol + 9);

            pullDown.setStrings(dictDataList.stream().map(DictData::getName).collect(Collectors.toList()));
            pullDowns.add(pullDown);
        }

        // 是否加密
        pullDown = new PullDown();
        pullDown.setFirstCol(startCol + 10);
        pullDown.setLastCol(startCol + 10);

        pullDown.setStrings(Arrays.asList("是", "否"));
        pullDowns.add(pullDown);

        // 接线方式
        dictType = dictTypeService.getByCode("Dev_Connect");
        if (ObjectUtil.isNotNull(dictType)) {
            dictDataList = dictDataService.getDictDataByTypeId(dictType.getId());
            pullDown = new PullDown();
            pullDown.setFirstCol(startCol + 26);
            pullDown.setLastCol(startCol + 26);

            pullDown.setStrings(dictDataList.stream().map(DictData::getName).collect(Collectors.toList()));
            pullDowns.add(pullDown);
        }

        pullDown = new PullDown();
        pullDown.setFirstCol(startCol + 27);
        pullDown.setLastCol(startCol + 27);
        pullDown.setStrings(Stream.iterate(1, x -> x + 1).limit(10).map(String::valueOf).collect(Collectors.toList()));
        pullDowns.add(pullDown);

        return pullDowns;
    }

    @Override
    public void visualizeProvinceDev(List<PqDevVO> pqDevs) {
        pqDevs.forEach(pqDev -> {
            if (StrUtil.isNotBlank(pqDev.getSeries())) {
                pqDev.setSeries(EncryptionUtil.decoderString(1, pqDev.getSeries()));
            }
            if (StrUtil.isNotBlank(pqDev.getDevKey())) {
                pqDev.setDevKey(EncryptionUtil.decoderString(1, pqDev.getDevKey()));
            }
            pqDev.setDevType(devTypeService.getById(pqDev.getDevType()).getName());
            if (StrUtil.isNotBlank(pqDev.getManufacturer())) {
                pqDev.setManufacturer(dictDataService.getDictDataById(pqDev.getManufacturer()).getName());
            }

            pqDev.setProtocol(dictDataService.getDictDataById(pqDev.getProtocol()).getName());
            if (StrUtil.isNotBlank(pqDev.getDelegate())) {
                pqDev.setDelegate(dictDataService.getDictDataById(pqDev.getDelegate()).getName());
            }
        });
    }

    @Override
    public void reverseVisualizeProvinceDev(List<PqDev> pqDevs, String patternId) {
        pqDevs.forEach(pqDev -> {
            pqDev.setPattern(patternId);
            DictData manufacturerDictData = dictDataService.getDictDataByName(pqDev.getManufacturer());
            if (ObjectUtil.isNull(manufacturerDictData)) {
                throw new BusinessException(DetectionResponseEnum.MANUFACTURER_NOT_EXIST);
            } else {
                pqDev.setManufacturer(manufacturerDictData.getId());
            }
            DictData protocolDictData = dictDataService.getDictDataByName(pqDev.getProtocol());
            if (ObjectUtil.isNull(protocolDictData)) {
                throw new BusinessException(DetectionResponseEnum.PROTOCOL_NOT_EXIST);
            } else {
                pqDev.setProtocol(protocolDictData.getId());
            }
            if (StrUtil.isNotBlank(pqDev.getDelegate())) {
                DictData delegateDictData = dictDataService.getDictDataByName(pqDev.getDelegate());
                if (ObjectUtil.isNull(delegateDictData)) {
                    throw new BusinessException(DetectionResponseEnum.DELEGATE_NOT_EXIST);
                } else {
                    pqDev.setDelegate(delegateDictData.getId());
                }
            }
            pqDev.setState(DataStateEnum.ENABLE.getCode());
        });
    }

    @Override
    public List<ContrastDevExcel> getExportContrastDevData(List<PqDevVO> pqDevVOList) {
        List<ContrastDevExcel> contrastDevExcels = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(pqDevVOList)) {
            this.visualizeProvinceDev(pqDevVOList);
            contrastDevExcels.addAll(BeanUtil.copyToList(pqDevVOList, ContrastDevExcel.class));
            contrastDevExcels.forEach(contrastDevExcel -> {
                List<PqMonitor> monitorList = pqMonitorService.listPqMonitorByDevIds(Collections.singletonList(contrastDevExcel.getId()));
                pqMonitorService.visualizeMonitor(monitorList);
                List<PqMonitorExcel> pqMonitorExcelList = new ArrayList<>();
                for (int i = 0; i < monitorList.size(); i++) {
                    PqMonitor pqMonitor = monitorList.get(i);
                    PqMonitorExcel pqMonitorExcel = BeanUtil.copyProperties(pqMonitor, PqMonitorExcel.class);
                    String pt = pqMonitor.getPt();
                    String[] split1 = pt.split(String.valueOf(StrUtil.C_COLON));
                    pqMonitorExcel.setPt1(split1[0]);
                    pqMonitorExcel.setPt2(split1[1]);

                    String ct = pqMonitor.getCt();
                    String[] split2 = ct.split(String.valueOf(StrUtil.C_COLON));
                    pqMonitorExcel.setCt1(split2[0]);
                    pqMonitorExcel.setCt2(split2[1]);

                    pqMonitorExcelList.add(pqMonitorExcel);
                }
                contrastDevExcel.setPqMonitorExcelList(pqMonitorExcelList);
            });
        }
        return contrastDevExcels;
    }

    @Override
    public Map<String, List<String>> listSelectOptions(String pattern) {
        List<PqDev> pqDevList = this.lambdaQuery()
                .select(PqDev::getCityName, PqDev::getGdName, PqDev::getSubName,
                        PqDev::getHardwareVersion, PqDev::getSoftwareVersion)
                .eq(PqDev::getPattern, pattern)
                .eq(PqDev::getState, DataStateEnum.ENABLE.getCode())
                .list();
        String[] devKeyArray = new String[]{"cityName", "gdName", "subName", "hardwareVersion", "softwareVersion"};
        Map<String, List<String>> result = new HashMap<>();
        for (String key : devKeyArray) {
            List<String> value = pqDevList.stream().map(dev -> {
                String fieldValue = (String) ReflectUtil.getFieldValue(dev, key);
                return fieldValue;
            }).distinct().filter(StrUtil::isNotBlank).collect(Collectors.toList());
            result.put(key, value);
        }

        List<PqMonitor> pqMonitorList = pqMonitorService.list();
        String[] monitorKeyArray = new String[]{"busbar", "pt", "ct"};
        for (String key : monitorKeyArray) {
            List<String> value = pqMonitorList.stream().map(monitor -> {
                String fieldValue = (String) ReflectUtil.getFieldValue(monitor, key);
                return fieldValue;
            }).distinct().filter(StrUtil::isNotBlank).collect(Collectors.toList());
            result.put(key, value);
        }
        return result;
    }

    @Override
    public DataCheckResDTO getDataCheckRes(String monitorId) {
//        restTemplateUtil.postJson();
        DataCheckResDTO dataCheckResDTO = restTemplateUtil.postJson("http://localhost:8080/api/v1/dataCheck/getDataCheckRes", monitorId, DataCheckResDTO.class);
        return dataCheckResDTO;
    }
}
