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


import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.*;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONTokener;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.WriteTable;
import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.algorithm.pojo.api.PqReasonableRangeFeignClient;
import com.njcn.algorithm.pojo.dto.PqReasonableRangeDto;
import com.njcn.algorithm.pojo.param.DataCleanParam;
import com.njcn.common.pojo.dto.SimpleDTO;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.dataProcess.enums.DataCleanEnum;
import com.njcn.device.common.mapper.onlinerate.OnLineRateMapper;
import com.njcn.device.common.service.GeneralDeviceService;
import com.njcn.device.line.mapper.LineMapper;
import com.njcn.device.line.service.DeptLineService;
import com.njcn.device.pq.constant.Param;
import com.njcn.device.pq.enums.LineBaseEnum;
import com.njcn.device.pq.mapper.PqDataVerifyBakMapper;
import com.njcn.device.pq.pojo.dto.GeneralDeviceDTO;
import com.njcn.device.pq.pojo.param.DeviceInfoParam;
import com.njcn.device.pq.pojo.param.OnlineRateParam;
import com.njcn.device.pq.pojo.param.dataClean.MonitorBaseParam;
import com.njcn.device.pq.pojo.po.DeptLine;
import com.njcn.device.pq.pojo.po.PqDataVerifyBak;
import com.njcn.device.pq.pojo.vo.*;
import com.njcn.device.pq.pojo.vo.dataClean.*;
import com.njcn.device.pq.service.CommTerminalService;
import com.njcn.device.pq.service.IPqDataVerifyBakService;
import com.njcn.device.pq.utils.DataLineExcelUtil;
import com.njcn.device.rstatintegrity.mapper.RStatIntegrityDMapper;
import com.njcn.oss.utils.FileStorageUtil;
import com.njcn.system.api.DictTreeFeignClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.njcn.device.pq.utils.FixedDynamicExcelExport.exportExcelByDataSize;
import static com.njcn.device.pq.utils.FixedDynamicExcelExport.writeCenterStyle;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author cdf
 * @since 2025-06-10
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class PqDataVerifyBakServiceImpl extends ServiceImpl<PqDataVerifyBakMapper, PqDataVerifyBak> implements IPqDataVerifyBakService {

    private static final String SEPARATOR = "-";
    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN);
    private final CommTerminalService commTerminalService;
    private final PqReasonableRangeFeignClient pqReasonableRangeFeignClient;
    private final LineMapper lineMapper;
    private final DictTreeFeignClient dictTreeFeignClient;
    private final FileStorageUtil fileStorageUtil;
    private final DeptLineService deptLineService;
    private final GeneralDeviceService deviceService;
    private final RStatIntegrityDMapper integrityDMapper;
    private final OnLineRateMapper onLineRateMapper;

    @Override
    public VerifyMonitorVO getMonitorVerifyData(MonitorBaseParam monitorBaseParam) {
        if (Objects.isNull(monitorBaseParam.getErrorTimeCount())) {
            monitorBaseParam.setErrorTimeCount(720);
        }
        List<String> monitorIds = commTerminalService.getRunMonitorByDept(monitorBaseParam);
        VerifyMonitorVO verifyMonitorVO = new VerifyMonitorVO();
        if (CollUtil.isNotEmpty(monitorIds)) {
            List<PqDataVerifyBak> dataVerifyList = baseDataVerifyQuery(monitorIds, monitorBaseParam);
            verifyMonitorVO.setRunNum(monitorIds.size());
            verifyMonitorVO.setAbnormalNum(dataVerifyList.stream().collect(Collectors.groupingBy(PqDataVerifyBak::getLineId)).size());
//            List<PqDataVerifyBak> dataSumVerifyList = baseDataSumVerifyQuery(monitorIds, monitorBaseParam);
            verifyMonitorVO.setMapList(getRangeAbnormalMonitor(monitorBaseParam, dataVerifyList));
            verifyMonitorVO.setTargetList(getAbnormalTarget(dataVerifyList, monitorBaseParam.getErrorTimeCount()));
            verifyMonitorVO.setMonitorAlarmInfo(getAbnormalTable(dataVerifyList, monitorBaseParam));
        } else {
            verifyMonitorVO.setRunNum(0);
            verifyMonitorVO.setAbnormalNum(0);
            verifyMonitorVO.setMapList(getRangeAbnormalMonitor(monitorBaseParam, new ArrayList<>()));
            verifyMonitorVO.setTargetList(getAbnormalTarget(new ArrayList<>(), monitorBaseParam.getErrorTimeCount()));
            verifyMonitorVO.setMonitorAlarmInfo(getAbnormalTable(new ArrayList<>(), monitorBaseParam));
        }

        return verifyMonitorVO;
    }


    public List<PowerQualityIndicatorsVO> getAbnormalTable(List<PqDataVerifyBak> dataVerifyList, MonitorBaseParam monitorBaseParam) {
        List<PowerQualityIndicatorsVO> result = new ArrayList<>();
        Map<String, List<PqDataVerifyBak>> dataMap = dataVerifyList.stream().collect(Collectors.groupingBy(PqDataVerifyBak::getLineId));
        dataMap.forEach((key, list) -> {
            List<LocalDate> dateList = list.stream().map(PqDataVerifyBak::getTimeId).sorted(Comparator.comparing(Function.identity())).collect(Collectors.toList());
            processTable(monitorBaseParam, dateList, result, key);
        });
        dealTableResult(result, monitorBaseParam.getSearchValue());
        return result;
    }

    /**
     * 判断测点是否存在连续异常数据
     */
    private void processTable(MonitorBaseParam monitorBaseParam, List<LocalDate> dateList, List<PowerQualityIndicatorsVO> result, String lineId) {
        Integer warnDay = monitorBaseParam.getWarnDayLimit();
        Integer alarmDay = monitorBaseParam.getAlarmDayLimit();
        //检查是否有连续指定天数的告警
        boolean warnFlag = verData(dateList, warnDay);
        boolean alarmFlag = verData(dateList, alarmDay);
        if (warnFlag) {
            PowerQualityIndicatorsVO vo = new PowerQualityIndicatorsVO();
            vo.setMonitorId(lineId);
            if (alarmFlag) {
                vo.setSeverity(1);
            } else {
                vo.setSeverity(0);
            }
            vo.setAbnormalDay(dateList.size());
            result.add(vo);
        }
    }

    private void dealTableResult(List<PowerQualityIndicatorsVO> result, String searchValue) {
        if (CollUtil.isNotEmpty(result)) {
//            List<SysDicTreePO> dicTreePOList = dictTreeFeignClient.queryAllByType(Integer.valueOf(DicDataEnum.Obj_Type.getCode())).getData();
//            Map<String, SysDicTreePO> sysDicTreePOMap = dicTreePOList.stream().collect(Collectors.toMap(SysDicTreePO::getId, Function.identity()));
            List<String> ids = result.stream().map(PowerQualityIndicatorsVO::getMonitorId).distinct().collect(Collectors.toList());
            List<AreaLineInfoVO> areaLineInfoVOList = lineMapper.getBaseLineAreaInfo(ids, searchValue, null);
            Map<String, PowerQualityIndicatorsVO> areaMap = result.stream().collect(Collectors.toMap(PowerQualityIndicatorsVO::getMonitorId, Function.identity()));
            List<DeptLine> deptLines = deptLineService.selectDeptLine(ids);
            Map<String, String> deptName = deptLines.stream().collect(Collectors.toMap(DeptLine::getLineId, DeptLine::getId));
//            List<String> objIds = areaLineInfoVOList.stream().map(AreaLineInfoVO::getObjId).distinct().collect(Collectors.toList());
//            List<NewUserReportVO> userLedgerVOList = userLedgerFeignClient.getUserReportByIds(objIds).getData();
//            Map<String, NewUserReportVO> userMap = userLedgerVOList.stream().collect(Collectors.toMap(NewUserReportVO::getId, Function.identity()));
            List<PowerQualityIndicatorsVO> info = new ArrayList<>();
            for (AreaLineInfoVO lineDetail : areaLineInfoVOList) {
                if (areaMap.containsKey(lineDetail.getLineId())) {
                    PowerQualityIndicatorsVO it = areaMap.get(lineDetail.getLineId());
                    it.setMonitorName(lineDetail.getLineName());
                    it.setStationName(lineDetail.getSubName());
                    it.setDevName(lineDetail.getDeviceName());
                    if (deptName.containsKey(lineDetail.getLineId())) {
                        it.setCity(deptName.get(lineDetail.getLineId()));
                    }
                    it.setVoltageLevel(lineDetail.getVoltageScale());
                    it.setIp(Base64.encode(lineDetail.getIp()));
                    it.setManufacturer(lineDetail.getManufacturer());
                    it.setObjType(lineDetail.getLoadType());
                    it.setObjName(lineDetail.getObjName());
                    info.add(it);
//                    if (sysDicTreePOMap.containsKey(areaLineInfoVO.getSmallObjType())) {
//                        it.setObjType(sysDicTreePOMap.get(areaLineInfoVO.getSmallObjType()).getName());
//                    }
//                    if (userMap.containsKey(areaLineInfoVO.getObjId())) {
//                        it.setObjName(userMap.get(areaLineInfoVO.getObjId()).getProjectName());
//                    }
                }
            }
            result.clear();
            result.addAll(info);
        }
    }

    @Override
    public List<PowerQualityIndicatorsVO> getMonitorVerifyDay(MonitorBaseParam monitorBaseParam) {
        List<String> monitorIds = commTerminalService.getRunMonitorByDept(monitorBaseParam);
        if (CollectionUtils.isEmpty(monitorIds)) {
            return new ArrayList<>();
        }
        List<PqDataVerifyBak> dataVerifyList = baseDataVerifyQuery(monitorIds, monitorBaseParam);
        return getAbnormalTable(dataVerifyList, monitorBaseParam);
    }

    @Override
    public List<DetailAbnormalVO> monitorAbnormalTable(MonitorBaseParam monitorBaseParam) {
        if (Objects.isNull(monitorBaseParam.getErrorTimeCount())) {
            monitorBaseParam.setErrorTimeCount(720);
        }
        List<DetailAbnormalVO> result = new ArrayList<>();
        //参数校验
        Map<String, PqReasonableRangeDto> rangeMap = getStandRange();
        if (StrUtil.isNotBlank(monitorBaseParam.getTargetKey())) {
            if (!rangeMap.containsKey(monitorBaseParam.getTargetKey())) {
                throw new BusinessException("targetKey异常参数!");
            }
        }
        //查询台账
        List<LineDetailDataVO> lineDetailList = lineMapper.getLineDetailDataVO(monitorBaseParam.getMonitorIds());
        Map<String, LineDetailDataVO> lineDetailMap = lineDetailList.stream().collect(Collectors.toMap(LineDetailDataVO::getLineId, Function.identity()));

        LambdaQueryWrapper<PqDataVerifyBak> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.between(PqDataVerifyBak::getTimeId, DateUtil.beginOfDay(DateUtil.parse(monitorBaseParam.getSearchBeginTime())),
                        DateUtil.endOfDay(DateUtil.parse(monitorBaseParam.getSearchEndTime())))
                .in(PqDataVerifyBak::getLineId, monitorBaseParam.getMonitorIds())
                .eq(PqDataVerifyBak::getState, 1)
                .orderByAsc(PqDataVerifyBak::getTimeId);

        if (StrUtil.isNotBlank(monitorBaseParam.getTargetKey())) {
            switch (monitorBaseParam.getTargetKey()) {
                case Param.freq:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getFreqTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.freq_dev:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getFreqDevTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.rms_v:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getVRmsTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.rms_lvr:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getRmsLvrTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.vu_dev:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getVuDevTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.vl_dev:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getVlDevTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.rms_i:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getIRmsTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.v_thd:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getVThdTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.v_1_v:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getVTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.phasic_v_1:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getV1PhasicTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.phasic_rate_x:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getVPhasicTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.v_rate:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getVHarmTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.in_v_rate:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getVInharmTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.v_zero:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getVZeroTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.v_neg:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getVNegTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.v_pos:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getVPosTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.v_unbalance:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getVUnbalanceTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.fluc:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getFlucTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.pst:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getPstTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.plt:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getPltTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.pf:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getPfTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.Voltage_Dip:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getDipTime, monitorBaseParam.getErrorTimeCount());
                    break;
                case Param.Voltage_Rise:
                    lambdaQueryWrapper.ge(PqDataVerifyBak::getRiseTime, monitorBaseParam.getErrorTimeCount());
                    break;
                default:
                    log.error("方法{}未匹配到异常数据指标", "monitorAbnormalTable");
                    break;
            }
        }
        List<PqDataVerifyBak> dataVerifyList = this.list(lambdaQueryWrapper);
        Map<String, List<PqDataVerifyBak>> VerifyMap = dataVerifyList.stream().collect(Collectors.groupingBy(PqDataVerifyBak::getLineId));
        VerifyMap.forEach((key, value) -> {
            LineDetailDataVO lineDevGetDTO = lineDetailMap.get(key);
            String temBgName = "";
            if (lineDevGetDTO.getPowerFlag().equals(LineBaseEnum.POWER_FLAG.getCode())) {
                temBgName = lineDevGetDTO.getBdName();
            } else if (lineDevGetDTO.getPowerFlag().equals(LineBaseEnum.POWER_FLAG_NOT.getCode())) {
                temBgName = lineDevGetDTO.getObjName();
            }
            List<DetailAbnormalVO.TimeAndTargetKey> targetKeyList = new ArrayList<>();
            DetailAbnormalVO.TimeAndTargetKey targetKey;
            DetailAbnormalVO detailAbnormalVO = new DetailAbnormalVO();
            for (PqDataVerifyBak pqDataVerifyBak : value) {
                targetKey = new DetailAbnormalVO.TimeAndTargetKey();
                targetKey.setDate(pqDataVerifyBak.getTimeId().format(DatePattern.NORM_DATE_FORMATTER));
                targetKey.setTargetKeys(getAbnormalTarget(Arrays.asList(pqDataVerifyBak), monitorBaseParam.getErrorTimeCount()).stream().filter(x -> x.getIds().size() > 0).collect(Collectors.toList()));
                targetKeyList.add(targetKey);
            }
            detailAbnormalVO.setDateTargetList(targetKeyList);
            detailAbnormalVO.setMonitorName(lineDevGetDTO.getLineName());
            detailAbnormalVO.setBdName(temBgName);
            detailAbnormalVO.setMonitorId(lineDevGetDTO.getLineId());
            detailAbnormalVO.setTargetKey(monitorBaseParam.getTargetKey());
            result.add(detailAbnormalVO);
        });
        return result;
    }


    @Override
    public DetailAbnormalVO.DetailAbnormalCountVO monitorAbnormalTableDetail(MonitorBaseParam monitorBaseParam) {
        Map<String, PqReasonableRangeDto> dtoMap = getStandRange();
        if (StrUtil.isNotBlank(monitorBaseParam.getTargetKey())) {
            if (!dtoMap.containsKey(monitorBaseParam.getTargetKey())) {
                throw new BusinessException("targetKey异常参数!");
            }
        }
        List<DetailAbnormalVO.DetailAbnormalInnerVO> result = new ArrayList<>();
        LambdaQueryWrapper<PqDataVerifyBak> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(PqDataVerifyBak::getTimeId, monitorBaseParam.getTime())
                .in(PqDataVerifyBak::getLineId, monitorBaseParam.getMonitorIds());

        List<PqDataVerifyBak> pqDataVerifyBak = this.list(lambdaQueryWrapper);
        //index[0] 总异常时间 index[1]指标异常时间
        Integer[] errorTimeCount = {0, 0};
        Integer[] errAllCount = {0};
        for (PqDataVerifyBak dataVerifyBak : pqDataVerifyBak) {
            try (InputStream fileStream = fileStorageUtil.getFileStream(dataVerifyBak.getPath())) {
                JSONArray jsonArray = new JSONArray(new JSONTokener(fileStream, new JSONConfig()));
                jsonArray.forEach(it -> {
                    JSONObject targetJson = (JSONObject) it;
                    if (targetJson.containsKey("lineErrorTimes")) {
                        errorTimeCount[0] += Integer.valueOf(targetJson.get("lineErrorTimes").toString());
                    } else {
                        if (StrUtil.isNotBlank(monitorBaseParam.getTargetKey())) {
                            if (targetJson.containsKey(monitorBaseParam.getTargetKey())) {
                                resultDeal(dtoMap, monitorBaseParam.getTargetKey(), targetJson, result, dataVerifyBak.getTimeId().toString(), errorTimeCount, errAllCount);
                            }
                        } else {
                            for (Map.Entry<String, PqReasonableRangeDto> entry : dtoMap.entrySet()) {
                                String key = entry.getKey();
                                if (targetJson.containsKey(key)) {
                                    resultDeal(dtoMap, key, targetJson, result, dataVerifyBak.getTimeId().toString(), errorTimeCount, errAllCount);
                                }
                            }

                        }
                    }
                });
            } catch (Exception e) {
                throw new BusinessException("数据异常" + e);
            }
        }
        // 排序实现
        DetailAbnormalVO.DetailAbnormalCountVO info = new DetailAbnormalVO.DetailAbnormalCountVO();
        info.setTimeSum(StrUtil.isBlank(monitorBaseParam.getTargetKey()) ? errorTimeCount[0] : errorTimeCount[1]);
        info.setErrCount(errAllCount[0]);
        info.setTime(result.stream()
                .sorted(Comparator.comparing(DetailAbnormalVO.DetailAbnormalInnerVO::getTargetName)
                        .thenComparing(vo -> extractHarmonicOrder(vo.getTargetName()))
                        .thenComparing(DetailAbnormalVO.DetailAbnormalInnerVO::getTime))
                .collect(Collectors.toList()));
        return info;
    }

    @Override
    public void dataVerifyExcel(HttpServletResponse response, MonitorBaseParam monitorBaseParam) throws IOException {
        if (StrUtil.isNotBlank(monitorBaseParam.getDeptId())) {
            DeviceInfoParam param = new DeviceInfoParam();
            param.setDeptIndex(monitorBaseParam.getDeptId());
            param.setStatisticalType(new SimpleDTO());
            //获取终端台账类信息
            List<GeneralDeviceDTO> deviceInfo = deviceService.getDeviceInfo(param, Arrays.asList(0), Arrays.asList(1));
            List<String> lineIds = deviceInfo.stream().flatMap(x -> x.getLineIndexes().stream()).distinct().collect(Collectors.toList());
            List<String> devIds = deviceInfo.stream().flatMap(x -> x.getDeviceIndexes().stream()).distinct().collect(Collectors.toList());
            List<LineInfoMonitorIdVO> areaLineInfoVOList = lineMapper.getBaseLineInfoMonitorIdInfo(lineIds);
            List<LineInfoMonitorIdVO> monitorLine = areaLineInfoVOList.stream().filter(x -> StrUtil.isNotBlank(x.getMonitorId())).collect(Collectors.toList());
            List<LineInfoMonitorIdVO> gridSide = areaLineInfoVOList.stream().filter(x -> x.getPowerFlag() == 0).collect(Collectors.toList());
            List<LineInfoMonitorIdVO> nonGridSide = areaLineInfoVOList.stream().filter(x -> x.getPowerFlag() == 1).collect(Collectors.toList());

            OnlineRateParam onlineRateParam = new OnlineRateParam();
            onlineRateParam.setIds(devIds);
            onlineRateParam.setStartTime(monitorBaseParam.getSearchBeginTime());
            onlineRateParam.setEndTime(monitorBaseParam.getSearchEndTime());
            //完整率
            List<RStatIntegrityVO> integrityList = integrityDMapper.getLineIntegrityRateInfo(lineIds,
                    monitorBaseParam.getSearchBeginTime(),
                    monitorBaseParam.getSearchEndTime());
            //获取所有终端在线率
            List<RStatOnlineRateVO> onlineRateByDev = onLineRateMapper.getOnlineRateByDevIds(onlineRateParam);
            List<CityDataExcel> cityData1 = new ArrayList<>();
            List<CityDataExcel> cityData2 = new ArrayList<>();
            List<CityDataExcel> cityData3 = new ArrayList<>();
            for (GeneralDeviceDTO dto : deviceInfo) {
                cityData1.add(addCityDataExcel(dto, monitorLine, onlineRateByDev, integrityList));
                cityData2.add(addCityDataExcel(dto, gridSide, onlineRateByDev, integrityList));
                cityData3.add(addCityDataExcel(dto, nonGridSide, onlineRateByDev, integrityList));
            }
            monitorBaseParam.setMonitorIds(lineIds);
            List<DataVerifyExcel> dataVerifyExcels = this.baseMapper.selectDataVerifySum(monitorBaseParam);
            Map<String, LineInfoMonitorIdVO> lineInfomap = areaLineInfoVOList.stream().collect(Collectors.toMap(LineInfoMonitorIdVO::getLineId, Function.identity()));
            for (DataVerifyExcel dataVerifyExcel : dataVerifyExcels) {
                if (lineInfomap.containsKey(dataVerifyExcel.getLineId())) {
                    LineInfoMonitorIdVO areaLineInfoVO = lineInfomap.get(dataVerifyExcel.getLineId());
                    dataVerifyExcel.setCity(areaLineInfoVO.getGdName());
                    dataVerifyExcel.setLineName(areaLineInfoVO.getLineName());
                    dataVerifyExcel.setLoadType(areaLineInfoVO.getLoadType());
                    dataVerifyExcel.setObjName(areaLineInfoVO.getObjName());
                    dataVerifyExcel.setStationName(areaLineInfoVO.getSubName());
                    dataVerifyExcel.setPowerSubstationName(areaLineInfoVO.getPowerSubstationName());
                    dataVerifyExcel.setDevName(areaLineInfoVO.getDeviceName());
                    dataVerifyExcel.setIp(areaLineInfoVO.getIp());
                    dataVerifyExcel.setPowerFlag(areaLineInfoVO.getPowerFlag() == 0 ? "电网侧" : "非电网侧");
                    dataVerifyExcel.setManufacturer(areaLineInfoVO.getManufacturer());
                }
            }
            dataVerifyExcels.sort(Comparator
                    .comparing(DataVerifyExcel::getAllTime, Comparator.reverseOrder())
                    .thenComparing((DataVerifyExcel item) -> item.getCity() + "_" + item.getStationName() + "_" + item.getDevName())
            );
            Map<String, List> map = exportExcelByDataSize(dataVerifyExcels);
            // 步骤5：导出Excel（含列宽设置）
            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
                    .registerWriteHandler(new SimpleColumnWidthStyleStrategy(20))
                    .registerWriteHandler(writeCenterStyle()).build();
            WriteSheet writeSheet0 = EasyExcel.writerSheet("监测点异常指标统计").build();
            WriteTable writeTable0 = EasyExcel.writerTable(0).needHead(Boolean.TRUE).head(map.get("head")).build();
            excelWriter.write(map.get("data"), writeSheet0, writeTable0);

            Map<Integer, List<CityDataExcel>> cityData = new HashMap<>();
            cityData.put(1, cityData1);
            cityData.put(2, cityData2);
            cityData.put(3, cityData3);

            Map<Integer, List<LineDataExcel>> lineData = new HashMap<>();
            addLineDataExcel(cityData1, areaLineInfoVOList, onlineRateByDev, integrityList, lineData, 1);
            addLineDataExcel(cityData2, areaLineInfoVOList, onlineRateByDev, integrityList, lineData, 2);
            addLineDataExcel(cityData3, areaLineInfoVOList, onlineRateByDev, integrityList, lineData, 3);

            excelExtracted(excelWriter, "数据质量", new HashMap<>(), cityData, true);
            excelExtracted(excelWriter, "数据质量表格", lineData, new HashMap<>(), false);
            excelWriter.finish();
        }
    }

    private void addLineDataExcel(List<CityDataExcel> cityData1, List<LineInfoMonitorIdVO> areaLineInfoVOList, List<RStatOnlineRateVO> onlineRateByDev, List<RStatIntegrityVO> integrityList, Map<Integer, List<LineDataExcel>> lineData, Integer num) {
        List<LineDataExcel> lineDataExcels1 = new ArrayList<>();
        List<String> lineData1 = cityData1.stream().flatMap(x -> x.getAbnormalList().stream()).collect(Collectors.toList());
        List<LineInfoMonitorIdVO> lineInfoData1 = areaLineInfoVOList.stream().filter(x -> lineData1.contains(x.getLineId())).collect(Collectors.toList());
        LineDataExcel lineDataExcel;
        for (int i = 0; i < lineInfoData1.size(); i++) {
            LineInfoMonitorIdVO dto = lineInfoData1.get(i);
            lineDataExcel = new LineDataExcel();
            lineDataExcel.setLineNum(BigDecimal.valueOf((i + 1)));
            lineDataExcel.setLineName(dto.getLineName());
            lineDataExcel.setDeptName(dto.getDeviceName());
            lineDataExcel.setPowerSubstationName(dto.getPowerSubstationName());
            lineDataExcel.setObjName(dto.getObjName());
            lineDataExcel.setDeviceName(dto.getDeviceName());
            lineDataExcel.setIp(dto.getIp());
            lineDataExcel.setManufacturer(dto.getManufacturer());
            lineDataExcel.setOnlineRate(onLineRate(onlineRateByDev, Arrays.asList(dto.getDeviceId())));
            lineDataExcel.setIntegrity(integrity(integrityList, Arrays.asList(dto.getLineId())));
            if (num == 1) {
                lineDataExcel.setMonitorId(dto.getMonitorId());
            }
            lineDataExcels1.add(lineDataExcel);
        }
        lineData.put(num, lineDataExcels1);
    }


    private CityDataExcel addCityDataExcel(GeneralDeviceDTO dto, List<LineInfoMonitorIdVO> monitorLine, List<RStatOnlineRateVO> onlineRateByDev, List<RStatIntegrityVO> integrityList) {
        CityDataExcel data = new CityDataExcel();
        List<String> monitorLineList = monitorLine.stream()
                .filter(x -> dto.getLineIndexes().contains(x.getLineId()))
                .map(LineInfoMonitorIdVO::getLineId)
                .distinct()
                .collect(Collectors.toList());
        List<String> devList = monitorLine.stream()
                .filter(x -> dto.getDeviceIndexes().contains(x.getDeviceId()))
                .map(LineInfoMonitorIdVO::getDeviceId)
                .distinct()
                .collect(Collectors.toList());
        data.setDeptName(dto.getName());
        data.setDeviceNum(BigDecimal.valueOf(devList.size()));
        data.setLineNum(BigDecimal.valueOf(monitorLineList.size()));
        data.setOnlineRate(onLineRate(onlineRateByDev, devList));
        data.setIntegrity(integrity(integrityList, monitorLineList));
        List<String> abnormalList = integrityList.stream()
                .filter(x -> ObjUtil.isNotNull(x.getIntegrityRate()))
                .filter(x -> x.getIntegrityRate().compareTo(BigDecimal.ZERO) == 0)
                .filter(x -> monitorLineList.contains(x.getLineIndex()))
                .map(RStatIntegrityVO::getLineIndex)
                .distinct()
                .collect(Collectors.toList());
        data.setAbnormalNum(BigDecimal.valueOf(abnormalList.size()));
        data.setAbnormalList(abnormalList);
        return data;
    }

    private BigDecimal integrity(List<RStatIntegrityVO> integrityList, List<String> lineIds) {
        //监测完整率
        List<RStatIntegrityVO> integrityDS = integrityList.stream().filter(x -> lineIds.contains(x.getLineIndex())).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(integrityDS)) {
            double realTime = integrityDS.stream().mapToDouble(RStatIntegrityVO::getRealTime).sum();
            double dueTime = integrityDS.stream().mapToDouble(RStatIntegrityVO::getDueTime).sum();
            if (dueTime == 0) {
                return new BigDecimal(0);
            }
            return NumberUtil.round(Math.min(realTime * 100.0 / dueTime, 100), 2);
        } else {
            return new BigDecimal(0);
        }
    }

    private BigDecimal onLineRate(List<RStatOnlineRateVO> onlineRateByDev, List<String> devIds) {
        //终端在线率
        List<RStatOnlineRateVO> onlineRateDS = onlineRateByDev.stream().filter(x -> devIds.contains(x.getDevIndex())).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(onlineRateDS)) {
            double onlineTime = onlineRateDS.stream().mapToDouble(RStatOnlineRateVO::getOnlineMin).sum();
            double offlineTime = onlineRateDS.stream().mapToDouble(RStatOnlineRateVO::getOfflineMin).sum();
            if ((onlineTime + offlineTime) == 0) {
                return new BigDecimal(0);
            }
            return NumberUtil.round(Math.min(onlineTime * 100.0 / (onlineTime + offlineTime), 100), 2);
        } else {
            return new BigDecimal(0);
        }
    }

    private void excelExtracted(ExcelWriter excelWriter, String sheetName, Map<Integer, List<LineDataExcel>> lineData, Map<Integer, List<CityDataExcel>> cityData, Boolean fly) {
        // 构建sheet页--表示不加表头
        WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).needHead(Boolean.FALSE).build();
        // 表头的数量
        int num = 0;
        Boolean first = false;
        // 模拟写5张表
        for (int i = 1; i < 4; i++) {
            String name = "";
            if (i == 1) {
                if (fly) {
                    name = "一类监测点";
                } else {
                    name = "冀北公司一类监测点数据质量问题";
                }
                first = true;
            }
            if (i == 2) {
                if (fly) {
                    name = "电网侧";
                } else {
                    name = "冀北公司电网侧监测点数据质量问题（不包含一类监测点）";
                }
                first = false;
            }
            if (i == 3) {
                if (fly) {
                    name = "非电网侧";
                } else {
                    name = "冀北公司非电网侧问题监测点（不包含一类监测点）";
                }
                first = false;
            }
            String finalName = name;
            List<List<String>> contentHeader;
            if (fly) {
                contentHeader = DataLineExcelUtil.cityHeader();
            } else {
                contentHeader = DataLineExcelUtil.lineHeader(first);
            }
            List<List<String>> nameHeader = contentHeader.stream().map(item -> Collections.singletonList(finalName)).collect(Collectors.toList());
            // 这里必须指定需要头，table 会继承sheet的配置，sheet配置了不需要，table 默认也是不需要
            // 创建一个表头
            WriteTable writeTable1 = EasyExcel.writerTable(num).needHead(Boolean.TRUE).head(nameHeader).build();
            excelWriter.write(new ArrayList<>(), writeSheet, writeTable1);
            //创建数据
            WriteTable writeTable2 = EasyExcel.writerTable(num + 1).needHead(Boolean.TRUE).head(contentHeader).build();
            List<List<Object>> body;
            if (fly) {
                body = DataLineExcelUtil.cityBody(cityData.get(i));
            } else {
                body = DataLineExcelUtil.lineBody(lineData.get(i));
            }
            excelWriter.write(body, writeSheet, writeTable2);
            // 插入两次表头加2
            num = num + 2;
        }
    }


    private static final Pattern HARMONIC_PATTERN = Pattern.compile("(\\d+)次谐波");

    // 提取谐波次数的方法
    private static int extractHarmonicOrder(String targetName) {
        if (StrUtil.isBlank(targetName)) {
            return 0;
        }

        Matcher matcher = HARMONIC_PATTERN.matcher(targetName);
        if (matcher.find()) {
            try {
                return Integer.parseInt(matcher.group(1));
            } catch (NumberFormatException e) {
                log.warn("无法从targetName中提取谐波次数: {}", targetName);
            }
        }
        return 0;
    }


    /**
     * 处理json对象中list数组
     */

    private void resultDeal(Map<String, PqReasonableRangeDto> dtoMap, String targetKey, JSONObject targetJson, List<DetailAbnormalVO.DetailAbnormalInnerVO> result, String date, Integer[] errorTimeCount, Integer[] errorCounts) {
        PqReasonableRangeDto pqReasonableRangeDto = dtoMap.get(targetKey);
        JSONArray innerJson = targetJson.getJSONArray(targetKey);
        innerJson.forEach(oo -> {
            JSONObject jsonObjectTem = (JSONObject) oo;
            String targetName = jsonObjectTem.get("targetName").toString();
            errorCounts[0] += Integer.valueOf(jsonObjectTem.get("errorCounts").toString());
            errorTimeCount[1] += Integer.valueOf(jsonObjectTem.get("errorTimes").toString());
            JSONArray list = jsonObjectTem.getJSONArray("list");
            List<DetailAbnormalVO.DetailLimitInnerVO> temList = new ArrayList<>();
            list.forEach(listItem -> {
                JSONObject object = (JSONObject) listItem;
                JSONArray timeArr = object.getJSONArray("time");
                JSONArray valueArr = object.getJSONArray("value");
                String phase = object.get("phasic").toString();
                String valueType = object.get("valueType").toString();
                for (int i = 0; i < timeArr.size(); i++) {
                    DetailAbnormalVO.DetailLimitInnerVO temData = new DetailAbnormalVO.DetailLimitInnerVO();
                    temData.setTime(date + " " + timeArr.get(i).toString());
                    temData.setVal(valueArr.get(i).toString());
                    temData.setTargetName(targetName);
                    temData.setTargetKey(targetKey);
                    temData.setPhaseType(phase);
                    temData.setType(valueType);
                    temList.add(temData);
                }
            });
            Map<String, List<DetailAbnormalVO.DetailLimitInnerVO>> listMap = temList.stream().collect(Collectors.groupingBy(DetailAbnormalVO.DetailLimitInnerVO::getTime, TreeMap::new, Collectors.toList()));
            listMap.forEach((time, timeList) -> {
                Map<String, List<DetailAbnormalVO.DetailLimitInnerVO>> indexMap = timeList.stream().collect(Collectors.groupingBy(DetailAbnormalVO.DetailLimitInnerVO::getTargetKey));
                indexMap.forEach((target, targetList) -> {
                    String unit;
                    if (StrUtil.isNotBlank(pqReasonableRangeDto.getUnit())) {
                        unit = pqReasonableRangeDto.getUnit();
                    } else {
                        unit = "";
                    }

                    Map<String, List<DetailAbnormalVO.DetailLimitInnerVO>> phaseMap = timeList.stream().collect(Collectors.groupingBy(DetailAbnormalVO.DetailLimitInnerVO::getPhaseType));
                    phaseMap.forEach((phaseKey, valueList) -> {
                        DetailAbnormalVO.DetailAbnormalInnerVO vo = new DetailAbnormalVO.DetailAbnormalInnerVO();
                        vo.setTime(time);
                        vo.setTargetName(timeList.get(0).getTargetName());
                        vo.setTargetKey(targetKey);
                        vo.setPhaseType(phaseKey);

//                        if ((DataCleanEnum.DataI.getCode() + SEPARATOR + DataCleanEnum.RmsI.getCode() ).equals(targetKey)) {
//                            vo.setRangeDesc(pqReasonableRangeDto.getMinValue() + unit + " ~ " + pqReasonableRangeDto.getMaxValue() + "*CT1" + unit);
//                        } else {
                        vo.setRangeDesc(pqReasonableRangeDto.getMinValue() + unit + " ~ " + pqReasonableRangeDto.getMaxValue() + unit);
//                        }
                        valueList.forEach(ites -> {
                            switch (ites.getType()) {
                                case "AVG":
                                    vo.setAvg(ites.getVal());
                                    break;
                                case "MAX":
                                    vo.setMax(ites.getVal());
                                    break;
                                case "MIN":
                                    vo.setMin(ites.getVal());
                                    break;
                                case "CP95":
                                    vo.setCp95(ites.getVal());
                                    break;
                                case "Feature_Amplitude":
                                    vo.setFeatureAmplitude(ites.getVal());
                                    break;
                            }
                        });
                        result.add(vo);
                    });
                });
            });
        });
    }


    private List<PqDataVerifyBak> baseDataVerifyQuery(List<String> monitorIds, MonitorBaseParam monitorBaseParam) {
        if (Objects.isNull(monitorBaseParam.getErrorTimeCount())) {
            monitorBaseParam.setErrorTimeCount(720);
        }
        LambdaQueryWrapper<PqDataVerifyBak> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.between(PqDataVerifyBak::getTimeId, DateUtil.beginOfDay(DateUtil.parse(monitorBaseParam.getSearchBeginTime())), DateUtil.endOfDay(DateUtil.parse(monitorBaseParam.getSearchEndTime())))
                .in(PqDataVerifyBak::getLineId, monitorIds)
                .eq(PqDataVerifyBak::getState, 1)
                .and(x -> x.ge(PqDataVerifyBak::getFreqTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getFreqDevTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVRmsTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVPosTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVNegTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVZeroTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVUnbalanceTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getRmsLvrTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVuDevTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVlDevTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVThdTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getIRmsTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getPltTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVInharmTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVHarmTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getPfTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVPhasicTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getV1PhasicTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getFlucTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getPstTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getDipTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getRiseTime, monitorBaseParam.getErrorTimeCount())
                )
                .orderByAsc(PqDataVerifyBak::getTimeId);
        return this.list(lambdaQueryWrapper);
    }

    private List<PqDataVerifyBak> baseDataSumVerifyQuery(List<String> monitorIds, MonitorBaseParam monitorBaseParam) {
        QueryWrapper<PqDataVerifyBak> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("line_id as lineId,sum(freq) as freq,sum(freq_Dev) as freqDev,sum(v_Rms) as vRms,sum(v_Pos) as vPos,sum(v_Neg) as vNeg," +
                        "sum(v_Zero) as vZero,sum(v_Unbalance) as vUnbalance,sum(rms_Lvr) as rmsLvr,sum(vu_Dev) as vuDev,sum(vl_Dev) as vlDev," +
                        "sum(v_Thd) as vThd,sum(v) as v,sum(i_Rms) as iRms,sum(plt) as plt,sum(v_Inharm) as vInharm,sum(v_Harm) as vHarm,sum(pf) as pf," +
                        "sum(v_Phasic) as vPhasic,sum(v1_Phasic) as v1Phasic,sum(fluc) as fluc,sum(pst) as pst,sum(dip) as dip,sum(rise) as rise,sum(state) as state")
                .lambda().between(PqDataVerifyBak::getTimeId, DateUtil.beginOfDay(DateUtil.parse(monitorBaseParam.getSearchBeginTime())), DateUtil.endOfDay(DateUtil.parse(monitorBaseParam.getSearchEndTime())))
                .in(PqDataVerifyBak::getLineId, monitorIds)
                .eq(PqDataVerifyBak::getState, 1)
                .and(x -> x.ge(PqDataVerifyBak::getFreqTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getFreqDevTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVRmsTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVPosTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVNegTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVZeroTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVUnbalanceTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getRmsLvrTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVuDevTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVlDevTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVThdTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getIRmsTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getPltTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVInharmTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVHarmTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getPfTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getVPhasicTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getV1PhasicTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getFlucTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getPstTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getDipTime, monitorBaseParam.getErrorTimeCount())
                        .or()
                        .ge(PqDataVerifyBak::getRiseTime, monitorBaseParam.getErrorTimeCount())
                )
                .groupBy(PqDataVerifyBak::getLineId);
        return this.list(queryWrapper);
    }

    /**
     * 时间范围异常测点数量
     */
    private List<Map<String, Object>> getRangeAbnormalMonitor(MonitorBaseParam monitorBaseParam, List<PqDataVerifyBak> dataVerifyList) {
        List<Map<String, Object>> result = new ArrayList<>();
        DateTime startTime = DateUtil.parse(monitorBaseParam.getSearchBeginTime());
        DateTime endTime = DateUtil.parse(monitorBaseParam.getSearchEndTime());
        if (startTime.year() == endTime.year() && startTime.month() == endTime.month()) {
            DateRange ranged = DateUtil.range(DateUtil.parse(monitorBaseParam.getSearchBeginTime()), DateUtil.parse(monitorBaseParam.getSearchEndTime()), DateField.DAY_OF_MONTH);
            Map<String, Integer> map = dataVerifyList.stream().collect(Collectors.groupingBy(its -> its.getTimeId().format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN)),
                    Collectors.mapping(PqDataVerifyBak::getLineId, Collectors.collectingAndThen(Collectors.toSet(), Set::size))));
            setTime(result, ranged, map, DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN));
        } else {
            //不在一个月则展示月数据
            DateRange ranged = DateUtil.range(DateUtil.parse(monitorBaseParam.getSearchBeginTime()), DateUtil.parse(monitorBaseParam.getSearchEndTime()), DateField.MONTH);
            Map<String, Integer> map = dataVerifyList.stream().collect(Collectors.groupingBy(it -> it.getTimeId().format(DateTimeFormatter.ofPattern(DatePattern.NORM_MONTH_PATTERN)),
                    Collectors.mapping(PqDataVerifyBak::getLineId, Collectors.collectingAndThen(Collectors.toSet(), Set::size))));
            setTime(result, ranged, map, DateTimeFormatter.ofPattern(DatePattern.NORM_MONTH_PATTERN));
        }
        return result;
    }

    static void setTime(List<Map<String, Object>> result, DateRange ranged, Map<String, Integer> map, DateTimeFormatter dateFormat) {
        for (DateTime dateTime : ranged) {
            Map<String, Object> rangeMap = new HashMap<>();
            String time = DateUtil.format(dateTime, dateFormat);
            rangeMap.put("time", time);
            rangeMap.put("val", map.getOrDefault(time, 0));
            result.add(rangeMap);
        }
    }


    /**
     * 指标异常测点数量
     */
    public List<VerifyTargetVO> getAbnormalTarget(List<PqDataVerifyBak> dataVerifyList, Integer errorTimeCount) {
        List<VerifyTargetVO> result = new ArrayList<>();
        Map<String, PqReasonableRangeDto> rangeMap = getStandRange();
        rangeMap.forEach((key, dto) -> {
            Set<String> ids;
            switch (key) {
                case Param.freq:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getFreqTime())).filter(it -> it.getFreqTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.freq_dev:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getFreqDevTime())).filter(it -> it.getFreqDevTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.rms_v:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getVRmsTime())).filter(it -> it.getVRmsTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.rms_lvr:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getRmsLvrTime())).filter(it -> it.getRmsLvrTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.vu_dev:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getVuDevTime())).filter(it -> it.getVuDevTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.vl_dev:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getVlDevTime())).filter(it -> it.getVlDevTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.rms_i:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getIRmsTime())).filter(it -> it.getIRmsTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.v_thd:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getVThdTime())).filter(it -> it.getVThdTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.v_1_v:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getVTime())).filter(it -> it.getVTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.phasic_v_1:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getV1PhasicTime())).filter(it -> it.getV1PhasicTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.phasic_rate_x:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getVPhasicTime())).filter(it -> it.getVPhasicTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.v_rate:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getVHarmTime())).filter(it -> it.getVHarmTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.in_v_rate:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getVInharmTime())).filter(it -> it.getVInharmTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.v_zero:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getVZeroTime())).filter(it -> it.getVZeroTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.v_neg:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getVNegTime())).filter(it -> it.getVNegTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.v_pos:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getVPosTime())).filter(it -> it.getVPosTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.v_unbalance:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getVUnbalanceTime())).filter(it -> it.getVUnbalanceTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.fluc:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getFlucTime())).filter(it -> it.getFlucTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.pst:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getPstTime())).filter(it -> it.getPstTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.plt:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getPltTime())).filter(it -> it.getPltTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.pf:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getPfTime())).filter(it -> it.getPfTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.Voltage_Dip:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getDipTime())).filter(it -> it.getDipTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                case Param.Voltage_Rise:
                    ids = dataVerifyList.stream().filter(x -> ObjUtil.isNotNull(x.getRiseTime())).filter(it -> it.getRiseTime() > errorTimeCount).map(PqDataVerifyBak::getLineId).collect(Collectors.toSet());
                    assembleEntity(ids, dto, result);
                    break;
                default:
                    log.error("未匹配到异常数据指标");
                    break;
            }
        });
        return result.stream().sorted(Comparator.comparing(VerifyTargetVO::getSort)).collect(Collectors.toList());
    }

    /**
     * 组装实体
     */
    private void assembleEntity(Set<String> ids, PqReasonableRangeDto dto, List<VerifyTargetVO> result) {
        VerifyTargetVO verifyTargetVO = new VerifyTargetVO();
        String temStr = dto.getInfluxdbTableName() + SEPARATOR + dto.getIndexCode();
        verifyTargetVO.setKey(temStr);
        verifyTargetVO.setTargetName(dto.getIndexName().replace("次", ""));
        verifyTargetVO.setIds(ids);
        verifyTargetVO.setRemark(dto.getRemark());
        verifyTargetVO.setSort(dto.getSort());
        String unit = "";
        if (StrUtil.isNotBlank(dto.getUnit())) {
            unit = dto.getUnit();
        }
//        if (temStr.equals(Param.rms_i)) {
//            verifyTargetVO.setRangeDesc(dto.getMinValue() + unit + " ~ " + dto.getMaxValue() + "*CT1" + unit);
//        } else {
        verifyTargetVO.setRangeDesc(dto.getMinValue() + unit + " ~ " + dto.getMaxValue() + unit);
//        }
        result.add(verifyTargetVO);
    }

    /**
     * 获取异常范围标准
     */
    private Map<String, PqReasonableRangeDto> getStandRange() {
        DataCleanParam dataCleanParam = new DataCleanParam();
        dataCleanParam.setSystemType(DataCleanEnum.Pqs.getCode());
        List<PqReasonableRangeDto> pqReasonableRangeDtoList = pqReasonableRangeFeignClient.getData(dataCleanParam).getData();
        return pqReasonableRangeDtoList.stream().collect(Collectors.toMap(it -> it.getInfluxdbTableName() + SEPARATOR + it.getIndexCode(), Function.identity()));
    }

    /**
     * 检查日期列表中是否存在连续指定天数的异常
     *
     * @param dateList 日期列表（已排序）
     * @param day      连续天数阈值
     * @return 是否存在连续异常
     */
    private Boolean verData(List<LocalDate> dateList, Integer day) {
        for (int i = 0; i <= dateList.size() - day; i++) {
            boolean isConsecutive = true;
            for (int j = 1; j < day; j++) {
                if (!dateList.get(i).plusDays(j).equals(dateList.get(i + j))) {
                    isConsecutive = false;
                    break;
                }
            }
            if (isConsecutive) {
                return true;
            }
        }
        return false;
    }

}
