package com.njcn.advance.service.responsibility.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.advance.enums.AdvanceResponseEnum;
import com.njcn.advance.mapper.responsibility.RespDataMapper;
import com.njcn.advance.model.responsibility.*;
import com.njcn.advance.pojo.bo.responsibility.*;
import com.njcn.advance.pojo.dto.responsibility.CustomerData;
import com.njcn.advance.pojo.dto.responsibility.CustomerResponsibility;
import com.njcn.advance.pojo.dto.responsibility.RespDataDTO;
import com.njcn.advance.pojo.dto.responsibility.ResponsibilityResult;
import com.njcn.advance.pojo.param.ResponsibilityCalculateParam;
import com.njcn.advance.pojo.param.ResponsibilitySecondCalParam;
import com.njcn.advance.pojo.po.responsibility.RespData;
import com.njcn.advance.pojo.po.responsibility.RespDataResult;
import com.njcn.advance.responsibility.constant.CalculationStatus;
import com.njcn.advance.responsibility.model.HarmonicData;
import com.njcn.advance.responsibility.service.IHarmonicResponsibilityService;
import com.njcn.advance.service.responsibility.IRespDataResultService;
import com.njcn.advance.service.responsibility.IRespDataService;
import com.njcn.advance.service.responsibility.IRespUserDataService;
import com.njcn.advance.utils.ResponsibilityAlgorithm;
import com.njcn.common.pojo.constant.ServerInfo;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.utils.FileUtil;
import com.njcn.common.utils.PubUtils;
import com.njcn.db.constant.DbConstant;
import com.njcn.device.biz.commApi.CommTerminalGeneralClient;
import com.njcn.device.biz.pojo.dto.DeptGetChildrenMoreDTO;
import com.njcn.device.biz.pojo.dto.LineDevGetDTO;
import com.njcn.device.biz.pojo.param.DeptGetLineParam;
import com.njcn.device.biz.pojo.po.Overlimit;
import com.njcn.device.pq.api.LineFeignClient;
import com.njcn.device.pq.pojo.vo.LineDetailDataVO;
import com.njcn.device.pq.pojo.vo.LineDetailVO;
import com.njcn.harmonic.api.HarmDataFeignClient;
import com.njcn.harmonic.pojo.param.HistoryHarmParam;
import com.njcn.influx.pojo.dto.HarmData;
import com.njcn.influx.pojo.dto.HarmHistoryDataDTO;
import com.njcn.oss.constant.OssPath;
import com.njcn.oss.utils.FileStorageUtil;
import com.njcn.web.factory.PageFactory;
import com.njcn.web.pojo.param.BaseParam;
import com.njcn.web.utils.RequestUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author hongawen
 * @since 2023-07-21
 */
@Service
@RequiredArgsConstructor
public class RespDataServiceImpl extends ServiceImpl<RespDataMapper, RespData> implements IRespDataService {


    private final FileStorageUtil fileStorageUtil;

    private final LineFeignClient lineFeignClient;

    private final HarmDataFeignClient harmDataFeignClient;

    private final IRespDataResultService respDataResultService;

    private final IRespUserDataService respUserDataService;

    private final CommTerminalGeneralClient commTerminalGeneralClient;

    private final IHarmonicResponsibilityService harmonicResponsibilityService;

    public final static int SORT_10 = 10;
    public final static int INTERVAL_TIME_1 = 1;
    public final static int INTERVAL_TIME_3 = 3;
    public final static int INTERVAL_TIME_5 = 5;
    public final static int INTERVAL_TIME_15 = 15;
    public final static int INTERVAL_TIME_30 = 30;
    public final static int WINDOW_96 = 96;
    public final static int WINDOW_48 = 48;
    public final static int WINDOW_4 = 4;

    public final static int MINUS_2 = 2;
    public final static int MINUS_3 = 3;
    public final static int MINUS_4 = 4;
    public final static int MINUS_5 = 5;


    @Override
    public Page<RespDataDTO> responsibilityList(BaseParam queryParam) {
        QueryWrapper<RespDataDTO> queryWrapper = new QueryWrapper<>();
        //获取当前用户部门，根据部门获取监测点
        DeptGetLineParam deptGetLineParam = new DeptGetLineParam();
        deptGetLineParam.setDeptId(RequestUtil.getDeptIndex());
        deptGetLineParam.setServerName(ServerInfo.ADVANCE_BOOT);
        List<DeptGetChildrenMoreDTO> list = commTerminalGeneralClient.deptGetLine(deptGetLineParam).getData();
        DeptGetChildrenMoreDTO dto = list.stream().collect(Collectors.toMap(DeptGetChildrenMoreDTO::getUnitId, Function.identity())).get(RequestUtil.getDeptIndex());
        if (CollUtil.isNotEmpty(dto.getLineBaseList())) {
            List<String> line = dto.getLineBaseList().stream().map(LineDevGetDTO::getPointId).collect(Collectors.toList());
            queryWrapper.in("pqs_resp_data.line_id", line);
        }
        if (ObjectUtil.isNotNull(queryParam)) {
            //查询参数不为空，进行条件填充
            if (StrUtil.isNotBlank(queryParam.getSearchValue())) {
                //仅提供用采名称
                queryWrapper.and(param -> param.like("pqs_resp_user_data.name", queryParam.getSearchValue()));
            }
            //排序
            if (ObjectUtil.isAllNotEmpty(queryParam.getSortBy(), queryParam.getOrderBy())) {
                queryWrapper.orderBy(true, queryParam.getOrderBy().equals(DbConstant.ASC), StrUtil.toUnderlineCase(queryParam.getSortBy()));
            } else {
                //没有排序参数，默认根据sort字段排序，没有排序字段的，根据updateTime更新时间排序
                queryWrapper.orderBy(true, false, "pqs_resp_data.create_time");
            }
            queryWrapper.between("pqs_resp_data.create_time", DateUtil.beginOfDay(DateUtil.parse(queryParam.getSearchBeginTime())), DateUtil.endOfDay(DateUtil.parse(queryParam.getSearchEndTime())));
        }
        queryWrapper.eq("pqs_resp_data.state", DataStateEnum.ENABLE.getCode());
        Page<RespDataDTO> page = this.baseMapper.page(new Page<>(PageFactory.getPageNum(queryParam), PageFactory.getPageSize(queryParam)), queryWrapper);
        List<RespDataDTO> records = page.getRecords();
        if (CollectionUtil.isNotEmpty(records)) {
            //获取该监测点的详细信息
            for (RespDataDTO respDataDTO : records) {
                LineDetailVO lineSubGdDetail = lineFeignClient.getLineSubGdDetail(respDataDTO.getLineId()).getData();
                BeanUtil.copyProperties(lineSubGdDetail, respDataDTO);
            }
        }
        return page.setRecords(records);
    }


    /***
     * 批量逻辑删除责任划分数据
     * @author hongawen
     * @date 2023/7/24 19:16
     */
    @Override
    public void deleteByIds(List<String> ids) {
        this.baseMapper.deleteByIds(ids);
    }


    @Override
    @Deprecated
    public ResponsibilityResult getDynamicDataOld(ResponsibilityCalculateParam responsibilityCalculateParam) {
        ResponsibilityResult result = new ResponsibilityResult();
        //调用c++依赖需要待初始化的参数
        int pNode, pNum, win, harmNum;
        float harmMk;
        List<UserDataExcel> userDataExcels = respUserDataService.getUserDataExcelList(responsibilityCalculateParam.getUserDataId());
        //开始处理，根据接口参数需求，需要节点数（用户数，用户名+监测点号为一个用户），时间范围内功率数据
        DealDataResult dealDataResult = RespUserDataServiceImpl.getStanderData(userDataExcels, 1);
        List<String> dateStr = PubUtils.getTimes(DateUtil.parse(responsibilityCalculateParam.getSearchBeginTime(), DatePattern.NORM_DATE_PATTERN), DateUtil.parse(responsibilityCalculateParam.getSearchEndTime(), DatePattern.NORM_DATE_PATTERN));
        Map<String/*户号@监测点号@户名*/, Map<String/*yyyy-MM-dd天日期*/, List<UserDataExcel>>> finalData = getFinalUserData(dealDataResult, dateStr);
        //至此，finalData便是我们最终获得的用于计算责任数据,第一个参数节点数值pNode获取到
        //第一个参数pNode
        pNode = finalData.size();
        if (pNode < 1) {
            //没有合理的用采数据直接返回
            throw new BusinessException(AdvanceResponseEnum.USER_DATA_P_NODE_PARAMETER_ERROR);
        }
        //第二个参数pNum，根据起始时间和截止时间以及监测点测量间隔计算数量
        RespCommon pNumAndInterval = getPNumAndInterval(finalData, responsibilityCalculateParam.getLineId(), dateStr);
        pNum = pNumAndInterval.getPNum();
        int userIntervalTime = pNumAndInterval.getUserIntervalTime();
        int lineInterval = pNumAndInterval.getLineInterval();
        //第三个参数win,根据起始时间和截止时间的间隔
        if (dateStr.size() > 1) {
            if (userIntervalTime == INTERVAL_TIME_15) {
                win = WINDOW_96;
            } else {
                win = WINDOW_48;
            }
        } else {
            win = WINDOW_4;
        }
        //第四个参数harmMk，默认为0f
        harmMk = 0f;
        //第五个参数harmNum，与功率数据保持一致
        harmNum = pNum;
        //至此基础数据组装完毕，开始组装功率数据和谐波数据
        //先做谐波数据，理论上到这步的时候，谐波数据是满足完整性并已经补充完整性到100%，此处需要将谐波数据与功率数据长度匹配上
        RespHarmData respHarmData = getRespHarmData(responsibilityCalculateParam, lineInterval);
        //harmData填充完毕后，开始组装功率数据
        //首先获取当前时间内的各个用户的数据
        Map<String/*用户名*/, List<UserDataExcel>> originalPData = new HashMap<>(16);
        List<String> names = new ArrayList<>();
        Set<String> userNamesFinal = finalData.keySet();
        for (String userName : userNamesFinal) {
            List<UserDataExcel> tempData = new ArrayList<>();
            //根据日期将日期数据全部获取出来z
            Map<String, List<UserDataExcel>> tempResult = finalData.get(userName);
            for (String date : dateStr) {
                tempData.addAll(tempResult.get(date));
            }
            //按日期排序
            Collections.sort(tempData);
            originalPData.put(userName, tempData);
            names.add(userName);
        }
        //然后开始组装数据
        float[][] pData = new float[QvvrDataEntity.MAX_P_NUM][QvvrDataEntity.MAX_P_NODE];
        for (int i = 0; i < names.size(); i++) {
            //当前某用户测量节点的所有数据
            List<UserDataExcel> userDataExcelBodies1 = originalPData.get(names.get(i));
            for (int k = 0; k < userDataExcelBodies1.size(); k++) {
                float[] pDataStruct = pData[k];
                if (pDataStruct == null) {
                    pDataStruct = new float[QvvrDataEntity.MAX_P_NODE];
                }
                float[] p = pDataStruct;
                p[i] = userDataExcelBodies1.get(k).getWork().floatValue();
                pData[k] = pDataStruct;
            }
        }
        //至此功率数据也组装完毕，调用友谊提供的接口
        QvvrDataEntity qvvrDataEntity = new QvvrDataEntity();
        qvvrDataEntity.calFlag = 0;
        qvvrDataEntity.pNode = pNode;
        qvvrDataEntity.pNum = pNum;
        qvvrDataEntity.win = win;
        qvvrDataEntity.harmNum = harmNum;
        qvvrDataEntity.harmMk = harmMk;
        qvvrDataEntity.pData = pData;
        qvvrDataEntity.harmData = respHarmData.getHarmData();
        ResponsibilityAlgorithm responsibilityAlgorithm = new ResponsibilityAlgorithm();
        qvvrDataEntity = responsibilityAlgorithm.getResponsibilityResult(qvvrDataEntity);
        //至此接口调用结束，开始组装动态责任数据和用户责任量化结果
        //首先判断cal_ok的标识位是否为1，为0表示程序没有计算出结果
        if (qvvrDataEntity.calOk == 0) {
            throw new BusinessException(AdvanceResponseEnum.RESPONSIBILITY_PARAMETER_ERROR);
        }
        //没问题后，先玩动态责任数据
        CustomerData[] customerDatas = new CustomerData[qvvrDataEntity.pNode];
        float[][] fKdata/*无背景的动态责任数据*/ = qvvrDataEntity.getFKData();
        //第一个时间节点是起始时间+win窗口得到的时间
        Date sTime = DateUtil.parse(dateStr.get(0).concat(" 00:00:00"), DatePattern.NORM_DATETIME_PATTERN);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(sTime);
        calendar.add(Calendar.MINUTE, (win - 1) * userIntervalTime);
        List<Long> timeDatas = new ArrayList<>();
        for (int i = 0; i < qvvrDataEntity.pNum - qvvrDataEntity.win; i++) {
            calendar.add(Calendar.MINUTE, userIntervalTime);
            //一个时间点所有的用户数据
            float[] fKdatum = fKdata[i];
            for (int k = 0; k < qvvrDataEntity.pNode; k++) {
                CustomerData customerData = customerDatas[k];
                if (null == customerData) {
                    customerData = new CustomerData();
                    customerData.setCustomerName(names.get(k));
                }
                List<Float> valueDatas = customerData.getValueDatas();
                Float valueTemp = fKdatum[k];
                if (valueTemp.isNaN()) {
                    valueTemp = 0.0f;
                }
                valueDatas.add(valueTemp);
                customerData.setValueDatas(valueDatas);
                customerDatas[k] = customerData;
            }
            timeDatas.add(calendar.getTimeInMillis());
        }
        //OK拿到所有测量点的数据了，现在就是看如何将相同户号的动态数据进行算术和求值，之前的用户name为：户号@测量点号@用户名
        Map<String/*用户名（户号）*/, List<CustomerData>> customerDataTemp = new HashMap<>(16);
        for (CustomerData data : customerDatas) {
            String customerName = data.getCustomerName();
            String[] customerInfo = customerName.split("@");
            String name = customerInfo[2] + "(" + customerInfo[0] + ")";
            List<CustomerData> customerData = customerDataTemp.get(name);
            CustomerData temp = data;
            temp.setCustomerName(name);
            if (CollectionUtils.isEmpty(customerData)) {
                customerData = new ArrayList<>();
            }
            customerData.add(temp);
            customerDataTemp.put(name, customerData);
        }
        //动态数据组装完成后，开始组装责任数据
        List<CustomerResponsibility> customerResponsibilities = getCustomerResponsibilityData(names, qvvrDataEntity.sumFKdata, qvvrDataEntity.pNode);
        //根据前十的用户数据，获取这些用户的动态责任数据
        List<CustomerData> customerData = new ArrayList<>();
        for (CustomerResponsibility customerResponsibility : customerResponsibilities) {
            String cusName = customerResponsibility.getCustomerName();
            List<CustomerData> customerData1 = customerDataTemp.get(cusName);
            if (CollectionUtils.isEmpty(customerData1)) {
                continue;
            }
            if (customerData1.size() == 1) {
                //表示用户唯一的
                customerData.add(customerData1.get(0));
            } else {
                // 表示用户可能包含多个监测点号，需要进行数据累加
                CustomerData customerDataT = new CustomerData();
                customerDataT.setCustomerName(cusName);
                //进行数值累加
                List<Float> valueDatas = new ArrayList<>();
                for (int i = 0; i < customerData1.get(0).getValueDatas().size(); i++) {
                    float original = 0.0f;
                    for (CustomerData data : customerData1) {
                        original = original + data.getValueDatas().get(i);
                    }
                    valueDatas.add(original);
                }
                customerDataT.setValueDatas(valueDatas);
                customerData.add(customerDataT);
            }
        }
        result.setDatas(customerData);
        result.setTimeDatas(timeDatas);
        result.setResponsibilities(customerResponsibilities);
        //此次的操作进行入库操作responsibilityData表数据
        //根据监测点名称+谐波框选的时间来查询，是否做过责任量化
        String timeWin = responsibilityCalculateParam.getSearchBeginTime().replaceAll(StrPool.DASHED, "").concat(StrPool.DASHED).concat(responsibilityCalculateParam.getSearchEndTime().replaceAll(StrPool.DASHED, ""));
        String type = responsibilityCalculateParam.getType() == 0 ? "谐波电流" : "谐波电压";
        //为了避免有监测点名称重复的，最终还是选择使用监测点索引来判断唯一性
        LambdaQueryWrapper<RespData> respDataLambdaQueryWrapper = new LambdaQueryWrapper<>();
        respDataLambdaQueryWrapper.eq(RespData::getLineId, responsibilityCalculateParam.getLineId())
                .eq(RespData::getUserDataId, responsibilityCalculateParam.getUserDataId())
                .eq(RespData::getTimeWindow, timeWin)
                .eq(RespData::getDataType, type)
                .eq(RespData::getState, DataStateEnum.ENABLE.getCode());
        List<RespData> responsibilityDataTemp = this.baseMapper.selectList(respDataLambdaQueryWrapper);
        RespData responsibilityData;
        if (CollectionUtils.isEmpty(responsibilityDataTemp)) {
            responsibilityData = new RespData();
            //库中没有记录则可以新建数据进行插入
            responsibilityData.setLineId(responsibilityCalculateParam.getLineId());
            responsibilityData.setUserDataId(responsibilityCalculateParam.getUserDataId());
            responsibilityData.setDataType(type);
            responsibilityData.setDataTimes(responsibilityCalculateParam.getTime().toString());
            responsibilityData.setTimeWindow(timeWin);
            responsibilityData.setState(DataStateEnum.ENABLE.getCode());
            //进行插入操作
            this.baseMapper.insert(responsibilityData);
        } else {
            //库中存在记录只需要判断次数进行数据更新
            responsibilityData = responsibilityDataTemp.get(0);
            String times = responsibilityData.getDataTimes();
            List<String> timesList = Stream.of(times.split(StrPool.COMMA)).collect(Collectors.toList());
            Integer time = responsibilityCalculateParam.getTime();
            if (!timesList.contains(time.toString())) {
                timesList.add(time.toString());
                timesList = timesList.stream().sorted().collect(Collectors.toList());
                responsibilityData.setDataTimes(String.join(StrPool.COMMA, timesList));
            }
            //执行更新操作
            this.baseMapper.updateById(responsibilityData);
        }
        //入库完毕之后，需要将必要数据进行序列化存储，方便后期的重复利用
        /*
         * 需要序列化三种数据结构 1 cal_flag置为1时需要的一些列参数的CacheQvvrData 2 cal_flag为0时的，动态结果。3 用户责任量化结果
         * 其中1/2都只需要一个文件即可
         * 3因为用户限值的变化调整，可能存在很多个文件，具体根据用户的选择而定
         *
         * 路径的结构为，temPath+userData+excelName+type+timeWin+lineIndex+time+文件名
         *  用户责任量化结果，需要再细化到限值
         */
        //首先判断有没有存储记录，没有则存储，有就略过 指定测点、时间窗口、谐波类型、谐波次数判断唯一性
        LambdaQueryWrapper<RespDataResult> respDataResultLambdaQueryWrapper = new LambdaQueryWrapper<>();
        respDataResultLambdaQueryWrapper.eq(RespDataResult::getResDataId, responsibilityData.getId())
                .eq(RespDataResult::getTime, responsibilityCalculateParam.getTime())
                .eq(RespDataResult::getStartTime, DateUtil.parse(responsibilityCalculateParam.getSearchBeginTime() + " 00:00:00", DatePattern.NORM_DATETIME_PATTERN))
                .eq(RespDataResult::getEndTime, DateUtil.parse(responsibilityCalculateParam.getSearchEndTime() + " 23:59:59", DatePattern.NORM_DATETIME_PATTERN))
                .eq(RespDataResult::getLimitValue, respHarmData.getOverLimit());
        RespDataResult respDataResult = respDataResultService.getOne(respDataResultLambdaQueryWrapper);
        if (Objects.isNull(respDataResult)) {
            respDataResult = new RespDataResult();
            respDataResult.setResDataId(responsibilityData.getId());
            respDataResult.setTime(responsibilityCalculateParam.getTime());
            respDataResult.setStartTime(DateUtil.parse(responsibilityCalculateParam.getSearchBeginTime() + " 00:00:00", DatePattern.NORM_DATETIME_PATTERN));
            respDataResult.setEndTime(DateUtil.parse(responsibilityCalculateParam.getSearchEndTime() + " 23:59:59", DatePattern.NORM_DATETIME_PATTERN));
            respDataResult.setLimitValue(respHarmData.getOverLimit());
            //时间横轴数据 timeDatas
            JSONArray timeDataJson = JSONArray.parseArray(JSON.toJSONString(timeDatas));
            InputStream timeDataStream = IoUtil.toUtf8Stream(timeDataJson.toString());
            String timeDataPath = fileStorageUtil.uploadStream(timeDataStream, OssPath.RESPONSIBILITY_USER_RESULT_DATA, FileUtil.generateFileName("json"));
            respDataResult.setTimeData(timeDataPath);
            //用户每时刻对应的责任数据
            JSONArray customerDataJson = JSONArray.parseArray(JSON.toJSONString(customerData));
            InputStream customerStream = IoUtil.toUtf8Stream(customerDataJson.toString());
            String customerPath = fileStorageUtil.uploadStream(customerStream, OssPath.RESPONSIBILITY_USER_RESULT_DATA, FileUtil.generateFileName("json"));
            respDataResult.setUserDetailData(customerPath);
            //调用qvvr生成的中间数据
            CacheQvvrData cacheQvvrData = new CacheQvvrData(qvvrDataEntity.getPNode(), qvvrDataEntity.getHarmNum(), qvvrDataEntity.getHarmData(), qvvrDataEntity.fKData, qvvrDataEntity.hKData, names, userIntervalTime, qvvrDataEntity.win, userIntervalTime, respHarmData.getHarmTime());
            String cacheJson = PubUtils.obj2json(cacheQvvrData);
            InputStream cacheQvvrDataStream = IoUtil.toUtf8Stream(cacheJson);
            String cacheQvvrDataPath = fileStorageUtil.uploadStream(cacheQvvrDataStream, OssPath.RESPONSIBILITY_USER_RESULT_DATA, FileUtil.generateFileName("json"));
            respDataResult.setQvvrData(cacheQvvrDataPath);
            //用户前10数据存储
            JSONArray customerResJson = JSONArray.parseArray(JSON.toJSONString(customerResponsibilities));
            InputStream customerResStream = IoUtil.toUtf8Stream(customerResJson.toString());
            String customerResPath = fileStorageUtil.uploadStream(customerResStream, OssPath.RESPONSIBILITY_USER_RESULT_DATA, FileUtil.generateFileName("json"));
            respDataResult.setUserResponsibility(customerResPath);
            respDataResultService.save(respDataResult);
        }
        //防止过程中创建了大量的对象，主动调用下GC处理
        System.gc();
        result.setResponsibilityDataIndex(responsibilityData.getId());
        return result;
    }

    @Override
    public ResponsibilityResult getDynamicData(ResponsibilityCalculateParam responsibilityCalculateParam) {
        ResponsibilityResult result = new ResponsibilityResult();
        //调用c++依赖需要待初始化的参数
        int pNode, pNum, win, harmNum;
        float harmMk;
        List<UserDataExcel> userDataExcels = respUserDataService.getUserDataExcelList(responsibilityCalculateParam.getUserDataId());
        //开始处理，根据接口参数需求，需要节点数（用户数，用户名+监测点号为一个用户），时间范围内功率数据
        DealDataResult dealDataResult = RespUserDataServiceImpl.getStanderData(userDataExcels, 1);
        List<String> dateStr = PubUtils.getTimes(DateUtil.parse(responsibilityCalculateParam.getSearchBeginTime(), DatePattern.NORM_DATE_PATTERN), DateUtil.parse(responsibilityCalculateParam.getSearchEndTime(), DatePattern.NORM_DATE_PATTERN));
        Map<String/*户号@监测点号@户名*/, Map<String/*yyyy-MM-dd天日期*/, List<UserDataExcel>>> finalData = getFinalUserData(dealDataResult, dateStr);
        //至此，finalData便是我们最终获得的用于计算责任数据,第一个参数节点数值pNode获取到
        //第一个参数pNode
        pNode = finalData.size();
        if (pNode < 1) {
            //没有合理的用采数据直接返回
            throw new BusinessException(AdvanceResponseEnum.USER_DATA_P_NODE_PARAMETER_ERROR);
        }
        //第二个参数pNum，根据起始时间和截止时间以及监测点测量间隔计算数量
        RespCommon pNumAndInterval = getPNumAndInterval(finalData, responsibilityCalculateParam.getLineId(), dateStr);
        pNum = pNumAndInterval.getPNum();
        int userIntervalTime = pNumAndInterval.getUserIntervalTime();
        int lineInterval = pNumAndInterval.getLineInterval();
        //第三个参数win,根据起始时间和截止时间的间隔
        if (dateStr.size() > 1) {
            if (userIntervalTime == INTERVAL_TIME_15) {
                win = WINDOW_96;
            } else {
                win = WINDOW_48;
            }
        } else {
            win = WINDOW_4;
        }
        //第四个参数harmMk，默认为0f
        harmMk = 0f;
        //第五个参数harmNum，与功率数据保持一致
        harmNum = pNum;
        //至此基础数据组装完毕，开始组装功率数据和谐波数据
        //先做谐波数据，理论上到这步的时候，谐波数据是满足完整性并已经补充完整性到100%，此处需要将谐波数据与功率数据长度匹配上
        RespHarmData respHarmData = getRespHarmData(responsibilityCalculateParam, lineInterval);
        //harmData填充完毕后，开始组装功率数据
        //首先获取当前时间内的各个用户的数据
        Map<String/*用户名*/, List<UserDataExcel>> originalPData = new HashMap<>(16);
        List<String> names = new ArrayList<>();
        Set<String> userNamesFinal = finalData.keySet();
        for (String userName : userNamesFinal) {
            List<UserDataExcel> tempData = new ArrayList<>();
            //根据日期将日期数据全部获取出来z
            Map<String, List<UserDataExcel>> tempResult = finalData.get(userName);
            for (String date : dateStr) {
                tempData.addAll(tempResult.get(date));
            }
            //按日期排序
            Collections.sort(tempData);
            originalPData.put(userName, tempData);
            names.add(userName);
        }
        //然后开始组装数据
        float[][] pData = new float[QvvrDataEntity.MAX_P_NUM][QvvrDataEntity.MAX_P_NODE];
        for (int i = 0; i < names.size(); i++) {
            //当前某用户测量节点的所有数据
            List<UserDataExcel> userDataExcelBodies1 = originalPData.get(names.get(i));
            for (int k = 0; k < userDataExcelBodies1.size(); k++) {
                float[] pDataStruct = pData[k];
                if (pDataStruct == null) {
                    pDataStruct = new float[QvvrDataEntity.MAX_P_NODE];
                }
                float[] p = pDataStruct;
                p[i] = userDataExcelBodies1.get(k).getWork().floatValue();
                pData[k] = pDataStruct;
            }
        }
        //至此功率数据也组装完毕，调用友谊提供的接口
//        QvvrDataEntity qvvrDataEntity = new QvvrDataEntity();
//        qvvrDataEntity.calFlag = 0;
//        qvvrDataEntity.pNode = pNode;
//        qvvrDataEntity.pNum = pNum;
//        qvvrDataEntity.win = win;
//        qvvrDataEntity.harmNum = harmNum;
//        qvvrDataEntity.harmMk = harmMk;
//        qvvrDataEntity.pData = pData;
//        qvvrDataEntity.harmData = respHarmData.getHarmData();
//        ResponsibilityAlgorithm responsibilityAlgorithm = new ResponsibilityAlgorithm();
//        qvvrDataEntity = responsibilityAlgorithm.getResponsibilityResult(qvvrDataEntity);

        HarmonicData harmonicData = harmonicResponsibilityService.fullCalculation(respHarmData.getHarmData(), pData, harmNum, pNum, pNode, win, harmMk);
        //至此接口调用结束，开始组装动态责任数据和用户责任量化结果
        //首先判断cal_ok的标识位是否为1，为0表示程序没有计算出结果
        if (harmonicData.getCalculationStatus() == CalculationStatus.FAILED) {
            throw new BusinessException(AdvanceResponseEnum.RESPONSIBILITY_PARAMETER_ERROR);
        }
        //没问题后，先玩动态责任数据
        CustomerData[] customerDatas = new CustomerData[harmonicData.getPowerNodeCount()];
        float[][] fKdata/*无背景的动态责任数据*/ = harmonicData.getFkData();
        //第一个时间节点是起始时间+win窗口得到的时间
        Date sTime = DateUtil.parse(dateStr.get(0).concat(" 00:00:00"), DatePattern.NORM_DATETIME_PATTERN);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(sTime);
        calendar.add(Calendar.MINUTE, (win - 1) * userIntervalTime);
        List<Long> timeDatas = new ArrayList<>();
        for (int i = 0; i < harmonicData.getPowerCount() - harmonicData.getWindowSize(); i++) {
            calendar.add(Calendar.MINUTE, userIntervalTime);
            //一个时间点所有的用户数据
            float[] fKdatum = fKdata[i];
            for (int k = 0; k < harmonicData.getPowerNodeCount(); k++) {
                CustomerData customerData = customerDatas[k];
                if (null == customerData) {
                    customerData = new CustomerData();
                    customerData.setCustomerName(names.get(k));
                }
                List<Float> valueDatas = customerData.getValueDatas();
                Float valueTemp = fKdatum[k];
                if (valueTemp.isNaN()) {
                    valueTemp = 0.0f;
                }
                valueDatas.add(valueTemp);
                customerData.setValueDatas(valueDatas);
                customerDatas[k] = customerData;
            }
            timeDatas.add(calendar.getTimeInMillis());
        }
        //OK拿到所有测量点的数据了，现在就是看如何将相同户号的动态数据进行算术和求值，之前的用户name为：户号@测量点号@用户名
        Map<String/*用户名（户号）*/, List<CustomerData>> customerDataTemp = new HashMap<>(16);
        for (CustomerData data : customerDatas) {
            String customerName = data.getCustomerName();
            String[] customerInfo = customerName.split("@");
            String name = customerInfo[2] + "(" + customerInfo[0] + ")";
            List<CustomerData> customerData = customerDataTemp.get(name);
            CustomerData temp = data;
            temp.setCustomerName(name);
            if (CollectionUtils.isEmpty(customerData)) {
                customerData = new ArrayList<>();
            }
            customerData.add(temp);
            customerDataTemp.put(name, customerData);
        }
        //动态数据组装完成后，开始组装责任数据
        List<CustomerResponsibility> customerResponsibilities = getCustomerResponsibilityData(names, harmonicData.getSumFKData(), harmonicData.getPowerNodeCount());
        //根据前十的用户数据，获取这些用户的动态责任数据
        List<CustomerData> customerData = new ArrayList<>();
        for (CustomerResponsibility customerResponsibility : customerResponsibilities) {
            String cusName = customerResponsibility.getCustomerName();
            List<CustomerData> customerData1 = customerDataTemp.get(cusName);
            if (CollectionUtils.isEmpty(customerData1)) {
                continue;
            }
            if (customerData1.size() == 1) {
                //表示用户唯一的
                customerData.add(customerData1.get(0));
            } else {
                // 表示用户可能包含多个监测点号，需要进行数据累加
                CustomerData customerDataT = new CustomerData();
                customerDataT.setCustomerName(cusName);
                //进行数值累加
                List<Float> valueDatas = new ArrayList<>();
                for (int i = 0; i < customerData1.get(0).getValueDatas().size(); i++) {
                    float original = 0.0f;
                    for (CustomerData data : customerData1) {
                        original = original + data.getValueDatas().get(i);
                    }
                    valueDatas.add(original);
                }
                customerDataT.setValueDatas(valueDatas);
                customerData.add(customerDataT);
            }
        }
        result.setDatas(customerData);
        result.setTimeDatas(timeDatas);
        result.setResponsibilities(customerResponsibilities);
        //此次的操作进行入库操作responsibilityData表数据
        //根据监测点名称+谐波框选的时间来查询，是否做过责任量化
        String timeWin = responsibilityCalculateParam.getSearchBeginTime().replaceAll(StrPool.DASHED, "").concat(StrPool.DASHED).concat(responsibilityCalculateParam.getSearchEndTime().replaceAll(StrPool.DASHED, ""));
        String type = responsibilityCalculateParam.getType() == 0 ? "谐波电流" : "谐波电压";
        //为了避免有监测点名称重复的，最终还是选择使用监测点索引来判断唯一性
        LambdaQueryWrapper<RespData> respDataLambdaQueryWrapper = new LambdaQueryWrapper<>();
        respDataLambdaQueryWrapper.eq(RespData::getLineId, responsibilityCalculateParam.getLineId())
                .eq(RespData::getUserDataId, responsibilityCalculateParam.getUserDataId())
                .eq(RespData::getTimeWindow, timeWin)
                .eq(RespData::getDataType, type)
                .eq(RespData::getState, DataStateEnum.ENABLE.getCode());
        List<RespData> responsibilityDataTemp = this.baseMapper.selectList(respDataLambdaQueryWrapper);
        RespData responsibilityData;
        if (CollectionUtils.isEmpty(responsibilityDataTemp)) {
            responsibilityData = new RespData();
            //库中没有记录则可以新建数据进行插入
            responsibilityData.setLineId(responsibilityCalculateParam.getLineId());
            responsibilityData.setUserDataId(responsibilityCalculateParam.getUserDataId());
            responsibilityData.setDataType(type);
            responsibilityData.setDataTimes(responsibilityCalculateParam.getTime().toString());
            responsibilityData.setTimeWindow(timeWin);
            responsibilityData.setState(DataStateEnum.ENABLE.getCode());
            //进行插入操作
            this.baseMapper.insert(responsibilityData);
        } else {
            //库中存在记录只需要判断次数进行数据更新
            responsibilityData = responsibilityDataTemp.get(0);
            String times = responsibilityData.getDataTimes();
            List<String> timesList = Stream.of(times.split(StrPool.COMMA)).collect(Collectors.toList());
            Integer time = responsibilityCalculateParam.getTime();
            if (!timesList.contains(time.toString())) {
                timesList.add(time.toString());
                timesList = timesList.stream().sorted().collect(Collectors.toList());
                responsibilityData.setDataTimes(String.join(StrPool.COMMA, timesList));
            }
            //执行更新操作
            this.baseMapper.updateById(responsibilityData);
        }
        //入库完毕之后，需要将必要数据进行序列化存储，方便后期的重复利用
        /*
         * 需要序列化三种数据结构 1 cal_flag置为1时需要的一些列参数的CacheQvvrData 2 cal_flag为0时的，动态结果。3 用户责任量化结果
         * 其中1/2都只需要一个文件即可
         * 3因为用户限值的变化调整，可能存在很多个文件，具体根据用户的选择而定
         *
         * 路径的结构为，temPath+userData+excelName+type+timeWin+lineIndex+time+文件名
         *  用户责任量化结果，需要再细化到限值
         */
        //首先判断有没有存储记录，没有则存储，有就略过 指定测点、时间窗口、谐波类型、谐波次数判断唯一性
        LambdaQueryWrapper<RespDataResult> respDataResultLambdaQueryWrapper = new LambdaQueryWrapper<>();
        respDataResultLambdaQueryWrapper.eq(RespDataResult::getResDataId, responsibilityData.getId())
                .eq(RespDataResult::getTime, responsibilityCalculateParam.getTime())
                .eq(RespDataResult::getStartTime, DateUtil.parse(responsibilityCalculateParam.getSearchBeginTime() + " 00:00:00", DatePattern.NORM_DATETIME_PATTERN))
                .eq(RespDataResult::getEndTime, DateUtil.parse(responsibilityCalculateParam.getSearchEndTime() + " 23:59:59", DatePattern.NORM_DATETIME_PATTERN))
                .eq(RespDataResult::getLimitValue, respHarmData.getOverLimit());
        RespDataResult respDataResult = respDataResultService.getOne(respDataResultLambdaQueryWrapper);
        if (Objects.isNull(respDataResult)) {
            respDataResult = new RespDataResult();
            respDataResult.setResDataId(responsibilityData.getId());
            respDataResult.setTime(responsibilityCalculateParam.getTime());
            respDataResult.setStartTime(DateUtil.parse(responsibilityCalculateParam.getSearchBeginTime() + " 00:00:00", DatePattern.NORM_DATETIME_PATTERN));
            respDataResult.setEndTime(DateUtil.parse(responsibilityCalculateParam.getSearchEndTime() + " 23:59:59", DatePattern.NORM_DATETIME_PATTERN));
            respDataResult.setLimitValue(respHarmData.getOverLimit());
            //时间横轴数据 timeDatas
            JSONArray timeDataJson = JSONArray.parseArray(JSON.toJSONString(timeDatas));
            InputStream timeDataStream = IoUtil.toUtf8Stream(timeDataJson.toString());
            String timeDataPath = fileStorageUtil.uploadStream(timeDataStream, OssPath.RESPONSIBILITY_USER_RESULT_DATA, FileUtil.generateFileName("json"));
            respDataResult.setTimeData(timeDataPath);
            //用户每时刻对应的责任数据
            JSONArray customerDataJson = JSONArray.parseArray(JSON.toJSONString(customerData));
            InputStream customerStream = IoUtil.toUtf8Stream(customerDataJson.toString());
            String customerPath = fileStorageUtil.uploadStream(customerStream, OssPath.RESPONSIBILITY_USER_RESULT_DATA, FileUtil.generateFileName("json"));
            respDataResult.setUserDetailData(customerPath);
            //调用qvvr生成的中间数据
            CacheQvvrData cacheQvvrData = new CacheQvvrData(harmonicData.getPowerNodeCount(), harmonicData.getHarmonicCount(), harmonicData.getHarmonicData(), harmonicData.getFkData(), harmonicData.getHkData(), names, userIntervalTime, harmonicData.getWindowSize(), userIntervalTime, respHarmData.getHarmTime());
            String cacheJson = PubUtils.obj2json(cacheQvvrData);
            InputStream cacheQvvrDataStream = IoUtil.toUtf8Stream(cacheJson);
            String cacheQvvrDataPath = fileStorageUtil.uploadStream(cacheQvvrDataStream, OssPath.RESPONSIBILITY_USER_RESULT_DATA, FileUtil.generateFileName("json"));
            respDataResult.setQvvrData(cacheQvvrDataPath);
            //用户前10数据存储
            JSONArray customerResJson = JSONArray.parseArray(JSON.toJSONString(customerResponsibilities));
            InputStream customerResStream = IoUtil.toUtf8Stream(customerResJson.toString());
            String customerResPath = fileStorageUtil.uploadStream(customerResStream, OssPath.RESPONSIBILITY_USER_RESULT_DATA, FileUtil.generateFileName("json"));
            respDataResult.setUserResponsibility(customerResPath);
            respDataResultService.save(respDataResult);
        }
        //防止过程中创建了大量的对象，主动调用下GC处理
        System.gc();
        result.setResponsibilityDataIndex(responsibilityData.getId());
        return result;
    }

    @Override
    @Deprecated
    public ResponsibilityResult getResponsibilityDataOld(ResponsibilitySecondCalParam responsibilitySecondCalParam) {
        ResponsibilityResult result = new ResponsibilityResult();
        //根据时间天数，获取理论上多少次用采数据
        RespData responsibilityData = this.baseMapper.selectById(responsibilitySecondCalParam.getResDataId());
        if (Objects.isNull(responsibilityData)) {
            throw new BusinessException(AdvanceResponseEnum.RESP_DATA_NOT_FOUND);
        }
        Overlimit overlimit = lineFeignClient.getOverLimitData(responsibilityData.getLineId()).getData();
        //获取总数据
        LambdaQueryWrapper<RespDataResult> respDataResultLambdaQueryWrapper = new LambdaQueryWrapper<>();
        respDataResultLambdaQueryWrapper.eq(RespDataResult::getResDataId, responsibilityData.getId())
                .eq(RespDataResult::getTime, responsibilitySecondCalParam.getTime());
        if (responsibilitySecondCalParam.getType() == 0) {
            respDataResultLambdaQueryWrapper.eq(RespDataResult::getLimitValue, PubUtils.getValueByMethod(overlimit, "getIharm", responsibilitySecondCalParam.getTime()));
        } else {
            respDataResultLambdaQueryWrapper.eq(RespDataResult::getLimitValue, PubUtils.getValueByMethod(overlimit, "getUharm", responsibilitySecondCalParam.getTime()));
        }
        RespDataResult respDataResultTemp = respDataResultService.getOne(respDataResultLambdaQueryWrapper);
        if (Objects.isNull(respDataResultTemp)) {
            throw new BusinessException(AdvanceResponseEnum.RESP_DATA_NOT_FOUND);
        }
        CacheQvvrData cacheQvvrData;
        try {
            InputStream fileStream = fileStorageUtil.getFileStream(respDataResultTemp.getQvvrData());
            String qvvrDataStr = IoUtil.readUtf8(fileStream);
            cacheQvvrData = PubUtils.json2obj(qvvrDataStr, CacheQvvrData.class);

        } catch (Exception exception) {
            throw new BusinessException(AdvanceResponseEnum.RESP_RESULT_DATA_NOT_FOUND);
        }
        //获取成功后，延长该缓存的生命周期为初始生命时长
        int win = cacheQvvrData.getWin();
        //不管窗口为4或者96，都需要考虑最小公倍数
        //最小公倍数根据监测点测量间隔来获取，可以考虑也由第一步操作缓存起来
        int minMultiple = cacheQvvrData.getMinMultiple();
        //谐波横轴所有的时间
        List<Long> times = cacheQvvrData.getTimes();
        //首先根据窗口判断限值时间范围是否满足最小窗口
        Long limitSL = DateUtil.parse(responsibilitySecondCalParam.getLimitStartTime(), DatePattern.NORM_DATETIME_PATTERN).getTime();
        Long limitEL = DateUtil.parse(responsibilitySecondCalParam.getLimitEndTime(), DatePattern.NORM_DATETIME_PATTERN).getTime();
        List<Integer> temp = getTimes(times, limitSL, limitEL);
        //在动态责任数据中，时间的起始索引位置和截止索引位置
        Integer timeStartIndex = temp.get(0);
        Integer timeEndIndex = temp.get(1);
        //间隔中的时间长度
        int minus = timeEndIndex - timeStartIndex + 1;
        //组装参数
        QvvrDataEntity qvvrDataEntity = new QvvrDataEntity();
        qvvrDataEntity.calFlag = 1;
        qvvrDataEntity.pNode = cacheQvvrData.getPNode();
        qvvrDataEntity.harmMk = responsibilitySecondCalParam.getLimitValue();
        qvvrDataEntity.win = win;
        int resNum;
        float[][] FKdata = new float[9600][QvvrDataEntity.MAX_P_NODE];
        float[][] HKdata = new float[9600][QvvrDataEntity.MAX_P_NODE + 1];
        float[] harmData = new float[1440 * 100];
        float[][] fKdataOriginal = cacheQvvrData.getFKData();
        float[][] hKdataOriginal = cacheQvvrData.getHKData();
        float[] harmDataOriginal = cacheQvvrData.getHarmData();
        //如果起始索引与截止索引的差值等于时间轴的长度，则说明用户没有选择限值时间，直接带入全部的原始数据，参与计算即可
        if (minus == times.size()) {
            qvvrDataEntity.harmNum = cacheQvvrData.getHarmNum();
            qvvrDataEntity.resNum = cacheQvvrData.getHarmNum() - cacheQvvrData.getWin();
            qvvrDataEntity.setFKData(cacheQvvrData.getFKData());
            qvvrDataEntity.setHKData(cacheQvvrData.getHKData());
            qvvrDataEntity.harmData = cacheQvvrData.getHarmData();
        } else {
            if (win == WINDOW_4) {
                //当窗口为4时,两个时间限制范围在最小公倍数为15时，最起码有5个有效时间点，在最小公倍数为30时，最起码有3个有效时间点
                if (minMultiple == INTERVAL_TIME_15) {
                    if (minus < MINUS_5) {
                        throw new BusinessException(AdvanceResponseEnum.WIN_TIME_ERROR);
                    }
                    resNum = minus - MINUS_4;

                } else if (minMultiple == INTERVAL_TIME_30) {
                    if (minus < MINUS_3) {
                        throw new BusinessException(AdvanceResponseEnum.WIN_TIME_ERROR);
                    }
                    resNum = minus - MINUS_2;
                } else {
                    throw new BusinessException(AdvanceResponseEnum.CALCULATE_INTERVAL_ERROR);
                }
            } else if (win == WINDOW_96) {
                //当窗口为96时,两个时间限值范围在最小公倍数为15时，最起码有97个有效时间点，在最小公倍数为30时，最起码有49个有效时间点
                if (minMultiple == INTERVAL_TIME_15) {
                    if (minus <= WINDOW_96) {
                        throw new BusinessException(AdvanceResponseEnum.WIN_TIME_ERROR);
                    }
                    resNum = minus - WINDOW_96;
                } else if (minMultiple == INTERVAL_TIME_30) {
                    if (minus <= WINDOW_48) {
                        throw new BusinessException(AdvanceResponseEnum.WIN_TIME_ERROR);
                    }
                    resNum = minus - WINDOW_48;
                } else {
                    throw new BusinessException(AdvanceResponseEnum.CALCULATE_INTERVAL_ERROR);
                }
            } else {
                throw new BusinessException(AdvanceResponseEnum.CALCULATE_INTERVAL_ERROR);
            }
            qvvrDataEntity.resNum = resNum;
            qvvrDataEntity.harmNum = minus;
            //因为限值时间实际是含头含尾的，所以harmNum需要索引差值+1
            for (int i = timeStartIndex; i <= timeEndIndex; i++) {
                harmData[i - timeStartIndex] = harmDataOriginal[i];
            }
            qvvrDataEntity.harmData = harmData;
            //FKData与HKData的值则等于resNum
            for (int i = timeStartIndex; i < timeStartIndex + resNum; i++) {
                FKdata[i - timeStartIndex] = fKdataOriginal[i];
                HKdata[i - timeStartIndex] = hKdataOriginal[i];
            }

            qvvrDataEntity.setFKData(FKdata);
            qvvrDataEntity.setHKData(HKdata);
        }
        ResponsibilityAlgorithm responsibilityAlgorithm = new ResponsibilityAlgorithm();
        qvvrDataEntity = responsibilityAlgorithm.getResponsibilityResult(qvvrDataEntity);
        if (qvvrDataEntity.calOk == 0) {
            throw new BusinessException(AdvanceResponseEnum.RESPONSIBILITY_PARAMETER_ERROR);
        }

        //没问题后，先玩动态责任数据
        List<String> names = cacheQvvrData.getNames();
        CustomerData[] customerDatas = new CustomerData[qvvrDataEntity.pNode];
        float[][] fKdata/*无背景的动态责任数据*/ = qvvrDataEntity.getFKData();
        //第一个时间节点是起始时间+win窗口得到的时间
        Date sTime = new Date();
        sTime.setTime(times.get(timeStartIndex));
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(sTime);
        calendar.add(Calendar.MINUTE, (win - 1) * minMultiple);
        List<Long> timeDatas = new ArrayList<>();
        for (int i = 0; i < qvvrDataEntity.harmNum - qvvrDataEntity.win; i++) {
            calendar.add(Calendar.MINUTE, minMultiple);
            //一个时间点所有的用户数据
            float[] fKdatum = fKdata[i];
            for (int k = 0; k < qvvrDataEntity.pNode; k++) {
                CustomerData customerData = customerDatas[k];
                if (null == customerData) {
                    customerData = new CustomerData();
                    customerData.setCustomerName(names.get(k));
                }
                List<Float> valueDatas = customerData.getValueDatas();
                Float valueTemp = fKdatum[k];
                if (valueTemp.isNaN()) {
                    valueTemp = 0.0f;
                }
                valueDatas.add(valueTemp);
                customerData.setValueDatas(valueDatas);
                customerDatas[k] = customerData;
            }
            timeDatas.add(calendar.getTimeInMillis());
        }
        //OK拿到所有测量点的数据了，现在就是看如何将相同户号的动态数据进行算术和求值，之前的用户name为：户号@测量点号@用户名
        Map<String/*用户名（户号）*/, List<CustomerData>> customerDataTemp = new HashMap<>(32);
        for (CustomerData data : customerDatas) {
            String customerName = data.getCustomerName();
            String[] customerInfo = customerName.split("@");
            String name = customerInfo[2] + "(" + customerInfo[0] + ")";
            List<CustomerData> customerData = customerDataTemp.get(name);
            CustomerData customerTemp = data;
            customerTemp.setCustomerName(name);
            if (CollectionUtils.isEmpty(customerData)) {
                customerData = new ArrayList<>();
            }
            customerData.add(customerTemp);
            customerDataTemp.put(name, customerData);
        }
        //调用程序接口后，首先组装责任量化结果
        float[] sumFKdata = qvvrDataEntity.sumFKdata;
        List<CustomerResponsibility> customerResponsibilities = getCustomerResponsibilityData(names, sumFKdata, qvvrDataEntity.pNode);
        //根据前十的用户数据，获取这些用户的动态责任数据
        List<CustomerData> customerData = new ArrayList<>();

        for (CustomerResponsibility customerResponsibility : customerResponsibilities) {
            String cusName = customerResponsibility.getCustomerName();
            List<CustomerData> customerData1 = customerDataTemp.get(cusName);
            if (CollectionUtils.isEmpty(customerData1)) {
                continue;
            }
            if (customerData1.size() == 1) {
                //表示用户唯一的
                customerData.add(customerData1.get(0));
            } else {
                // 表示用户可能包含多个监测点号，需要进行数据累加
                CustomerData customerDataT = new CustomerData();
                customerDataT.setCustomerName(cusName);
                //进行数值累加
                List<Float> valueDatas = new ArrayList<>();
                for (int i = 0; i < customerData1.get(0).getValueDatas().size(); i++) {
                    float original = 0.0f;
                    for (CustomerData data : customerData1) {
                        original = original + data.getValueDatas().get(i);
                    }
                    valueDatas.add(original);
                }
                customerDataT.setValueDatas(valueDatas);
                customerData.add(customerDataT);
            }
        }
        //接着组装动态数据结果
        result.setResponsibilities(customerResponsibilities);
        result.setDatas(customerData);
        result.setTimeDatas(timeDatas);

        //首先判断有没有存储记录，没有则存储，有就略过 指定测点、时间窗口、谐波类型、谐波次数判断唯一性
        LambdaQueryWrapper<RespDataResult> respDataResultLambdaQueryWrapper1 = new LambdaQueryWrapper<>();
        respDataResultLambdaQueryWrapper1.eq(RespDataResult::getResDataId, responsibilityData.getId())
                .eq(RespDataResult::getTime, responsibilitySecondCalParam.getTime())
                .eq(RespDataResult::getStartTime, DateUtil.parse(responsibilitySecondCalParam.getLimitStartTime(), DatePattern.NORM_DATETIME_PATTERN))
                .eq(RespDataResult::getEndTime, DateUtil.parse(responsibilitySecondCalParam.getLimitEndTime(), DatePattern.NORM_DATETIME_PATTERN))
                .eq(RespDataResult::getLimitValue, responsibilitySecondCalParam.getLimitValue());
        RespDataResult respDataResult = respDataResultService.getOne(respDataResultLambdaQueryWrapper1);
        if (Objects.isNull(respDataResult)) {
            respDataResult = new RespDataResult();
            respDataResult.setResDataId(responsibilityData.getId());
            respDataResult.setTime(responsibilitySecondCalParam.getTime());
            respDataResult.setStartTime(DateUtil.parse(responsibilitySecondCalParam.getLimitStartTime(), DatePattern.NORM_DATETIME_PATTERN));
            respDataResult.setEndTime(DateUtil.parse(responsibilitySecondCalParam.getLimitEndTime(), DatePattern.NORM_DATETIME_PATTERN));
            respDataResult.setLimitValue(responsibilitySecondCalParam.getLimitValue());
            //时间横轴数据 timeDatas
            JSONArray timeDataJson = JSONArray.parseArray(JSON.toJSONString(timeDatas));
            InputStream timeDataStream = IoUtil.toUtf8Stream(timeDataJson.toString());
            String timeDataPath = fileStorageUtil.uploadStream(timeDataStream, OssPath.RESPONSIBILITY_USER_RESULT_DATA, FileUtil.generateFileName("json"));
            respDataResult.setTimeData(timeDataPath);
            //用户每时刻对应的责任数据
            JSONArray customerDataJson = JSONArray.parseArray(JSON.toJSONString(customerData));
            InputStream customerStream = IoUtil.toUtf8Stream(customerDataJson.toString());
            String customerPath = fileStorageUtil.uploadStream(customerStream, OssPath.RESPONSIBILITY_USER_RESULT_DATA, FileUtil.generateFileName("json"));
            respDataResult.setUserDetailData(customerPath);
            //用户前10数据存储
            JSONArray customerResJson = JSONArray.parseArray(JSON.toJSONString(customerResponsibilities));
            InputStream customerResStream = IoUtil.toUtf8Stream(customerResJson.toString());
            String customerResPath = fileStorageUtil.uploadStream(customerResStream, OssPath.RESPONSIBILITY_USER_RESULT_DATA, FileUtil.generateFileName("json"));
            respDataResult.setUserResponsibility(customerResPath);
            respDataResultService.save(respDataResult);
        }
        //防止过程中创建了大量的对象，主动调用下GC处理
        System.gc();
        result.setResponsibilityDataIndex(responsibilityData.getId());
        return result;
    }

    @Override
    public ResponsibilityResult getResponsibilityData(ResponsibilitySecondCalParam responsibilitySecondCalParam) {
        ResponsibilityResult result = new ResponsibilityResult();
        //根据时间天数，获取理论上多少次用采数据
        RespData responsibilityData = this.baseMapper.selectById(responsibilitySecondCalParam.getResDataId());
        if (Objects.isNull(responsibilityData)) {
            throw new BusinessException(AdvanceResponseEnum.RESP_DATA_NOT_FOUND);
        }
        Overlimit overlimit = lineFeignClient.getOverLimitData(responsibilityData.getLineId()).getData();
        //获取总数据
        LambdaQueryWrapper<RespDataResult> respDataResultLambdaQueryWrapper = new LambdaQueryWrapper<>();
        respDataResultLambdaQueryWrapper.eq(RespDataResult::getResDataId, responsibilityData.getId())
                .eq(RespDataResult::getTime, responsibilitySecondCalParam.getTime());
        if (responsibilitySecondCalParam.getType() == 0) {
            respDataResultLambdaQueryWrapper.eq(RespDataResult::getLimitValue, PubUtils.getValueByMethod(overlimit, "getIharm", responsibilitySecondCalParam.getTime()));
        } else {
            respDataResultLambdaQueryWrapper.eq(RespDataResult::getLimitValue, PubUtils.getValueByMethod(overlimit, "getUharm", responsibilitySecondCalParam.getTime()));
        }
        RespDataResult respDataResultTemp = respDataResultService.getOne(respDataResultLambdaQueryWrapper);
        if (Objects.isNull(respDataResultTemp)) {
            throw new BusinessException(AdvanceResponseEnum.RESP_DATA_NOT_FOUND);
        }
        CacheQvvrData cacheQvvrData;
        try {
            InputStream fileStream = fileStorageUtil.getFileStream(respDataResultTemp.getQvvrData());
            String qvvrDataStr = IoUtil.readUtf8(fileStream);
            cacheQvvrData = PubUtils.json2obj(qvvrDataStr, CacheQvvrData.class);

        } catch (Exception exception) {
            throw new BusinessException(AdvanceResponseEnum.RESP_RESULT_DATA_NOT_FOUND);
        }
        //获取成功后，延长该缓存的生命周期为初始生命时长
        int win = cacheQvvrData.getWin();
        //不管窗口为4或者96，都需要考虑最小公倍数
        //最小公倍数根据监测点测量间隔来获取，可以考虑也由第一步操作缓存起来
        int minMultiple = cacheQvvrData.getMinMultiple();
        //谐波横轴所有的时间
        List<Long> times = cacheQvvrData.getTimes();
        //首先根据窗口判断限值时间范围是否满足最小窗口
        Long limitSL = DateUtil.parse(responsibilitySecondCalParam.getLimitStartTime(), DatePattern.NORM_DATETIME_PATTERN).getTime();
        Long limitEL = DateUtil.parse(responsibilitySecondCalParam.getLimitEndTime(), DatePattern.NORM_DATETIME_PATTERN).getTime();
        List<Integer> temp = getTimes(times, limitSL, limitEL);
        //在动态责任数据中，时间的起始索引位置和截止索引位置
        Integer timeStartIndex = temp.get(0);
        Integer timeEndIndex = temp.get(1);
        //间隔中的时间长度
        int minus = timeEndIndex - timeStartIndex + 1;
        //组装参数
        QvvrDataEntity qvvrDataEntity = new QvvrDataEntity();
        qvvrDataEntity.calFlag = 1;
        qvvrDataEntity.pNode = cacheQvvrData.getPNode();
        qvvrDataEntity.harmMk = responsibilitySecondCalParam.getLimitValue();
        qvvrDataEntity.win = win;
        int resNum;
        float[][] FKdata = new float[9600][QvvrDataEntity.MAX_P_NODE];
        float[][] HKdata = new float[9600][QvvrDataEntity.MAX_P_NODE + 1];
        float[] harmData = new float[1440 * 100];
        float[][] fKdataOriginal = cacheQvvrData.getFKData();
        float[][] hKdataOriginal = cacheQvvrData.getHKData();
        float[] harmDataOriginal = cacheQvvrData.getHarmData();
        //如果起始索引与截止索引的差值等于时间轴的长度，则说明用户没有选择限值时间，直接带入全部的原始数据，参与计算即可
        if (minus == times.size()) {
            qvvrDataEntity.harmNum = cacheQvvrData.getHarmNum();
            qvvrDataEntity.resNum = cacheQvvrData.getHarmNum() - cacheQvvrData.getWin();
            qvvrDataEntity.setFKData(cacheQvvrData.getFKData());
            qvvrDataEntity.setHKData(cacheQvvrData.getHKData());
            qvvrDataEntity.harmData = cacheQvvrData.getHarmData();
        } else {
            if (win == WINDOW_4) {
                //当窗口为4时,两个时间限制范围在最小公倍数为15时，最起码有5个有效时间点，在最小公倍数为30时，最起码有3个有效时间点
                if (minMultiple == INTERVAL_TIME_15) {
                    if (minus < MINUS_5) {
                        throw new BusinessException(AdvanceResponseEnum.WIN_TIME_ERROR);
                    }
                    resNum = minus - MINUS_4;

                } else if (minMultiple == INTERVAL_TIME_30) {
                    if (minus < MINUS_3) {
                        throw new BusinessException(AdvanceResponseEnum.WIN_TIME_ERROR);
                    }
                    resNum = minus - MINUS_2;
                } else {
                    throw new BusinessException(AdvanceResponseEnum.CALCULATE_INTERVAL_ERROR);
                }
            } else if (win == WINDOW_96) {
                //当窗口为96时,两个时间限值范围在最小公倍数为15时，最起码有97个有效时间点，在最小公倍数为30时，最起码有49个有效时间点
                if (minMultiple == INTERVAL_TIME_15) {
                    if (minus <= WINDOW_96) {
                        throw new BusinessException(AdvanceResponseEnum.WIN_TIME_ERROR);
                    }
                    resNum = minus - WINDOW_96;
                } else if (minMultiple == INTERVAL_TIME_30) {
                    if (minus <= WINDOW_48) {
                        throw new BusinessException(AdvanceResponseEnum.WIN_TIME_ERROR);
                    }
                    resNum = minus - WINDOW_48;
                } else {
                    throw new BusinessException(AdvanceResponseEnum.CALCULATE_INTERVAL_ERROR);
                }
            } else {
                throw new BusinessException(AdvanceResponseEnum.CALCULATE_INTERVAL_ERROR);
            }
            qvvrDataEntity.resNum = resNum;
            qvvrDataEntity.harmNum = minus;
            //因为限值时间实际是含头含尾的，所以harmNum需要索引差值+1
            for (int i = timeStartIndex; i <= timeEndIndex; i++) {
                harmData[i - timeStartIndex] = harmDataOriginal[i];
            }
            qvvrDataEntity.harmData = harmData;
            //FKData与HKData的值则等于resNum
            for (int i = timeStartIndex; i < timeStartIndex + resNum; i++) {
                FKdata[i - timeStartIndex] = fKdataOriginal[i];
                HKdata[i - timeStartIndex] = hKdataOriginal[i];
            }

            qvvrDataEntity.setFKData(FKdata);
            qvvrDataEntity.setHKData(HKdata);
        }
//        ResponsibilityAlgorithm responsibilityAlgorithm = new ResponsibilityAlgorithm();
//        qvvrDataEntity = responsibilityAlgorithm.getResponsibilityResult(qvvrDataEntity);
//
//        if (qvvrDataEntity.calOk == 0) {
//            throw new BusinessException(AdvanceResponseEnum.RESPONSIBILITY_PARAMETER_ERROR);
//        }
        HarmonicData harmonicData = harmonicResponsibilityService.partialCalculation(
                qvvrDataEntity.getHarmData(), qvvrDataEntity.getFKData(), qvvrDataEntity.getHKData(), qvvrDataEntity.getHarmNum(), qvvrDataEntity.getPNode(), qvvrDataEntity.getWin(), qvvrDataEntity.getResNum(), qvvrDataEntity.getHarmMk());
        if (harmonicData.getCalculationStatus() == CalculationStatus.FAILED) {
            throw new BusinessException(AdvanceResponseEnum.RESPONSIBILITY_PARAMETER_ERROR);
        }

        //没问题后，先玩动态责任数据
        List<String> names = cacheQvvrData.getNames();
        CustomerData[] customerDatas = new CustomerData[harmonicData.getPowerNodeCount()];
        float[][] fKdata/*无背景的动态责任数据*/ = harmonicData.getFkData();
        //第一个时间节点是起始时间+win窗口得到的时间
        Date sTime = new Date();
        sTime.setTime(times.get(timeStartIndex));
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(sTime);
        calendar.add(Calendar.MINUTE, (win - 1) * minMultiple);
        List<Long> timeDatas = new ArrayList<>();
        for (int i = 0; i < harmonicData.getHarmonicCount() - harmonicData.getWindowSize(); i++) {
            calendar.add(Calendar.MINUTE, minMultiple);
            //一个时间点所有的用户数据
            float[] fKdatum = fKdata[i];
            for (int k = 0; k < harmonicData.getPowerNodeCount(); k++) {
                CustomerData customerData = customerDatas[k];
                if (null == customerData) {
                    customerData = new CustomerData();
                    customerData.setCustomerName(names.get(k));
                }
                List<Float> valueDatas = customerData.getValueDatas();
                Float valueTemp = fKdatum[k];
                if (valueTemp.isNaN()) {
                    valueTemp = 0.0f;
                }
                valueDatas.add(valueTemp);
                customerData.setValueDatas(valueDatas);
                customerDatas[k] = customerData;
            }
            timeDatas.add(calendar.getTimeInMillis());
        }
        //OK拿到所有测量点的数据了，现在就是看如何将相同户号的动态数据进行算术和求值，之前的用户name为：户号@测量点号@用户名
        Map<String/*用户名（户号）*/, List<CustomerData>> customerDataTemp = new HashMap<>(32);
        for (CustomerData data : customerDatas) {
            String customerName = data.getCustomerName();
            String[] customerInfo = customerName.split("@");
            String name = customerInfo[2] + "(" + customerInfo[0] + ")";
            List<CustomerData> customerData = customerDataTemp.get(name);
            CustomerData customerTemp = data;
            customerTemp.setCustomerName(name);
            if (CollectionUtils.isEmpty(customerData)) {
                customerData = new ArrayList<>();
            }
            customerData.add(customerTemp);
            customerDataTemp.put(name, customerData);
        }
        //调用程序接口后，首先组装责任量化结果
        float[] sumFKdata = harmonicData.getSumFKData();
        List<CustomerResponsibility> customerResponsibilities = getCustomerResponsibilityData(names, sumFKdata, harmonicData.getPowerNodeCount());
        //根据前十的用户数据，获取这些用户的动态责任数据
        List<CustomerData> customerData = new ArrayList<>();

        for (CustomerResponsibility customerResponsibility : customerResponsibilities) {
            String cusName = customerResponsibility.getCustomerName();
            List<CustomerData> customerData1 = customerDataTemp.get(cusName);
            if (CollectionUtils.isEmpty(customerData1)) {
                continue;
            }
            if (customerData1.size() == 1) {
                //表示用户唯一的
                customerData.add(customerData1.get(0));
            } else {
                // 表示用户可能包含多个监测点号，需要进行数据累加
                CustomerData customerDataT = new CustomerData();
                customerDataT.setCustomerName(cusName);
                //进行数值累加
                List<Float> valueDatas = new ArrayList<>();
                for (int i = 0; i < customerData1.get(0).getValueDatas().size(); i++) {
                    float original = 0.0f;
                    for (CustomerData data : customerData1) {
                        original = original + data.getValueDatas().get(i);
                    }
                    valueDatas.add(original);
                }
                customerDataT.setValueDatas(valueDatas);
                customerData.add(customerDataT);
            }
        }
        //接着组装动态数据结果
        result.setResponsibilities(customerResponsibilities);
        result.setDatas(customerData);
        result.setTimeDatas(timeDatas);

        //首先判断有没有存储记录，没有则存储，有就略过 指定测点、时间窗口、谐波类型、谐波次数判断唯一性
        LambdaQueryWrapper<RespDataResult> respDataResultLambdaQueryWrapper1 = new LambdaQueryWrapper<>();
        respDataResultLambdaQueryWrapper1.eq(RespDataResult::getResDataId, responsibilityData.getId())
                .eq(RespDataResult::getTime, responsibilitySecondCalParam.getTime())
                .eq(RespDataResult::getStartTime, DateUtil.parse(responsibilitySecondCalParam.getLimitStartTime(), DatePattern.NORM_DATETIME_PATTERN))
                .eq(RespDataResult::getEndTime, DateUtil.parse(responsibilitySecondCalParam.getLimitEndTime(), DatePattern.NORM_DATETIME_PATTERN))
                .eq(RespDataResult::getLimitValue, responsibilitySecondCalParam.getLimitValue());
        RespDataResult respDataResult = respDataResultService.getOne(respDataResultLambdaQueryWrapper1);
        if (Objects.isNull(respDataResult)) {
            respDataResult = new RespDataResult();
            respDataResult.setResDataId(responsibilityData.getId());
            respDataResult.setTime(responsibilitySecondCalParam.getTime());
            respDataResult.setStartTime(DateUtil.parse(responsibilitySecondCalParam.getLimitStartTime(), DatePattern.NORM_DATETIME_PATTERN));
            respDataResult.setEndTime(DateUtil.parse(responsibilitySecondCalParam.getLimitEndTime(), DatePattern.NORM_DATETIME_PATTERN));
            respDataResult.setLimitValue(responsibilitySecondCalParam.getLimitValue());
            //时间横轴数据 timeDatas
            JSONArray timeDataJson = JSONArray.parseArray(JSON.toJSONString(timeDatas));
            InputStream timeDataStream = IoUtil.toUtf8Stream(timeDataJson.toString());
            String timeDataPath = fileStorageUtil.uploadStream(timeDataStream, OssPath.RESPONSIBILITY_USER_RESULT_DATA, FileUtil.generateFileName("json"));
            respDataResult.setTimeData(timeDataPath);
            //用户每时刻对应的责任数据
            JSONArray customerDataJson = JSONArray.parseArray(JSON.toJSONString(customerData));
            InputStream customerStream = IoUtil.toUtf8Stream(customerDataJson.toString());
            String customerPath = fileStorageUtil.uploadStream(customerStream, OssPath.RESPONSIBILITY_USER_RESULT_DATA, FileUtil.generateFileName("json"));
            respDataResult.setUserDetailData(customerPath);
            //用户前10数据存储
            JSONArray customerResJson = JSONArray.parseArray(JSON.toJSONString(customerResponsibilities));
            InputStream customerResStream = IoUtil.toUtf8Stream(customerResJson.toString());
            String customerResPath = fileStorageUtil.uploadStream(customerResStream, OssPath.RESPONSIBILITY_USER_RESULT_DATA, FileUtil.generateFileName("json"));
            respDataResult.setUserResponsibility(customerResPath);
            respDataResultService.save(respDataResult);
        }
        //防止过程中创建了大量的对象，主动调用下GC处理
        System.gc();
        result.setResponsibilityDataIndex(responsibilityData.getId());
        return result;
    }


    /**
     * 监测点测量间隔获取最后用于计算的功率数据
     *
     * @param finalData 参数计算的功率数据
     */
    private Map<String, Map<String, List<UserDataExcel>>> dealFinalDataByLineInterval(Map<String, Map<String, List<UserDataExcel>>> finalData) {
        DecimalFormat decimalFormat = new DecimalFormat("0.0000");
        Map<String, Map<String, List<UserDataExcel>>> result;
        //当监测点测量间隔为10分钟时，功率数据需要调整为30分钟数据
        result = new HashMap<>(32);
        Set<String> userNames = finalData.keySet();
        for (String userName : userNames) {
            Map<String, List<UserDataExcel>> temp = new HashMap<>(32);
            Map<String, List<UserDataExcel>> original = finalData.get(userName);
            Set<String> dates = original.keySet();
            for (String date : dates) {
                //某当天的数据
                List<UserDataExcel> single = original.get(date);
                //先根据事时间排序
                Collections.sort(single);
                //此时根据当天所有的数据，重新计算出所有时间点的数据，担心这个过程会消耗过长时间
                List<UserDataExcel> tempDatas = new ArrayList<>();
                for (int i = 0; i < WINDOW_96; i = i + 2) {
                    //30分钟内的2个15分钟功率数据相加作平均计算30分钟内的功率数据，最终的数据序列时间间隔30分钟。by 友谊文档
                    UserDataExcel tempData = new UserDataExcel();
                    tempData.setUserName(single.get(i).getUserName());
                    tempData.setUserId(single.get(i).getUserId());
                    tempData.setLine(single.get(i).getLine());
                    tempData.setTime(single.get(i).getTime());
                    //功率为 2个15分钟功率数据相加作平均
                    double work = single.get(i).getWork().doubleValue() + single.get(i + 1).getWork().doubleValue();
                    tempData.setWork(new BigDecimal(decimalFormat.format(work / 2.0)));
                    tempDatas.add(tempData);
                }
                temp.put(date, tempDatas);
            }
            result.put(userName, temp);
        }
        return result;
    }

    /**
     * 通过监测点测量间隔计算对齐后的谐波数据
     * 暂且认为最小公倍数就15、30两种可能
     *
     * @param historyData  原始的谐波数据
     * @param lineInterval 测量间隔
     */
    private List<HarmData> getDataWithLineInterval(List<HarmData> historyData, int lineInterval) {
        List<HarmData> result;
        switch (lineInterval) {
            case 1:
                result = getHarmResultByTimes(historyData, 15);
                break;
            case 3:
                result = getHarmResultByTimes(historyData, 5);
                break;
            // 间隔为5、10时，直接返回即可
            default:
                result = getHarmResultByTimes(historyData, 3);
                break;
        }
        return result.stream().sorted(Comparator.comparing(HarmData::getTime)).collect(Collectors.toList());
    }

    /**
     * 通过监测点测量间隔计算对齐后的谐波数据
     *
     * @param historyData 原始的谐波数据
     */
    private List<HarmData> getHarmResultByTimes(List<HarmData> historyData, int times) {
        List<HarmData> result = new ArrayList<>();
        DecimalFormat decimalFormat = new DecimalFormat("0.0000");
        for (int i = 0; i < historyData.size(); i = i + times) {
            float temp = 0.0f;
            for (int j = 0; j < times; j++) {
                int index = i + j;
                temp = temp + historyData.get(index).getValue();
            }
            //求平均值
            temp = Float.parseFloat(decimalFormat.format(temp / (float) times));
            HarmData resTemp = new HarmData();
            resTemp.setTime(historyData.get(i).getTime());
            resTemp.setValue(temp);
            result.add(resTemp);
        }
        return result;
    }

    /**
     * 根据接口返回值组装需要显示的责任量化数据
     */
    private List<CustomerResponsibility> getCustomerResponsibilityData(List<String> names, float[] sumFKdata, int pNode) {
        Map<String/*用户名（户号）*/, CustomerResponsibility> customerResponsibilityMap = new HashMap<>(16);
        for (int i = 0; i < pNode; i++) {
            /*用户ID 测量点ID 用户名*/
            String[] customerInfo = names.get(i).split("@");
            String name = customerInfo[2] + "(" + customerInfo[0] + ")";
            CustomerResponsibility customerResponsibility;
            if (customerResponsibilityMap.containsKey(name)) {
                customerResponsibility = customerResponsibilityMap.get(name);
                customerResponsibility.setResponsibilityData(customerResponsibility.getResponsibilityData() + sumFKdata[i]);
            } else {
                customerResponsibility = new CustomerResponsibility();
                customerResponsibility.setCustomerName(name);
                customerResponsibility.setResponsibilityData(sumFKdata[i]);
            }
            customerResponsibilityMap.put(name, customerResponsibility);
        }
        //map转为list
        List<CustomerResponsibility> customerResponsibilities = new ArrayList<>();
        Set<String> cusNames = customerResponsibilityMap.keySet();
        for (String cusName : cusNames) {
            customerResponsibilities.add(customerResponsibilityMap.get(cusName));
        }
        //取出前十的用户责任数据
        customerResponsibilities = customerResponsibilities.stream().sorted(Comparator.comparing(CustomerResponsibility::getResponsibilityData).reversed()).collect(Collectors.toList());
        if (customerResponsibilities.size() > SORT_10) {
            //当用户超出10，将前十用户保留，然后剩余归类为其他用户
            float tenTotal = 0.0f;
            for (int i = 0; i < SORT_10; i++) {
                float temp = PubUtils.floatRound(3, customerResponsibilities.get(i).getResponsibilityData());
                tenTotal = tenTotal + temp;
            }
            int size = customerResponsibilities.size() - 10;
            customerResponsibilities = customerResponsibilities.subList(0, 10);
            CustomerResponsibility others = new CustomerResponsibility();
            others.setCustomerName("其他用户(" + size + ")");
            others.setResponsibilityData(PubUtils.floatRound(3, 100.0f - tenTotal));
            customerResponsibilities.add(others);
        }
        return customerResponsibilities;
    }

    /**
     * 根据起始时间获取在集合中最接近的起始和截止值
     *
     * @param times   时间集合
     * @param limitSL 起始值
     * @param limitEL 截止值
     */
    private List<Integer> getTimes(List<Long> times, Long limitSL, Long limitEL) {
        List<Integer> result = new ArrayList<>();
        Integer temps = null;
        Integer tempe = null;
        //因为可以知道times是为4的倍数，所以长度肯定是偶数，不会出现索引越界的异常
        if (limitSL < times.get(0)) {
            temps = 0;
        }
        if (limitEL > times.get(times.size() - 1)) {
            tempe = times.size() - 1;
        }
        for (int i = 0; i < times.size() - 1; i++) {
            if (temps != null & tempe != null) {
                //判断都已经赋值后，跳出循环
                break;
            }
            //锁定前值
            if (times.get(i).equals(limitSL)) {
                //相等则给起始时间赋值
                temps = i;
            } else if (times.get(i + 1).equals(limitSL)) {
                temps = i + 1;
            } else if (times.get(i) < limitSL & times.get(i + 1) > limitSL) {
                //当起始时间处于中间时，将后值赋值给temps
                temps = i + 1;
            }
            //锁定后值
            if (times.get(i).equals(limitEL)) {
                //相等则给起始时间赋值
                tempe = i;
            } else if (times.get(i + 1).equals(limitEL)) {
                tempe = i + 1;
            } else if (times.get(i) < limitEL & times.get(i + 1) > limitEL) {
                //当起始时间处于中间时，将前值赋值给temps
                tempe = i;
            }
        }
        if (temps == null) {
            temps = 0;
        }
        if (tempe == null) {
            tempe = times.size() - 1;
        }
        result.add(temps);
        result.add(tempe);
        return result;
    }


    /***
     * 用采数据，根据用户选择的时间窗口过滤出稍后用于计算的用采数据
     */
    private Map<String, Map<String, List<UserDataExcel>>> getFinalUserData(DealDataResult dealDataResult, List<String> dateStr) {
        Map<String/*户号@监测点号@户名*/, Map<String/*yyyy-MM-dd天日期*/, List<UserDataExcel>>> totalData = dealDataResult.getTotalListData();
        Map<String/*户号@监测点号@户名*/, Map<String/*yyyy-MM-dd天日期*/, List<UserDataExcel>>> finalData = new HashMap<>(16);
        /*第一个参数pNode 如果时间范围内完整性不足90%的节点，不参与责任量化统计,因为之前处理过用采数据，此时只需要判断是否满足100%就可以判断*/
        int dueCounts = dateStr.size() * 96;
        Set<String> userNames = totalData.keySet();
        for (String userName : userNames) {
            int realCounts = 0;
            Map<String, List<UserDataExcel>> temp = totalData.get(userName);
            for (String date : dateStr) {
                if (CollectionUtil.isNotEmpty(temp.get(date))) {
                    realCounts = realCounts + temp.get(date).size();
                }
            }
            if (realCounts == dueCounts) {
                //只有期望和实际数量一致的时候才作为计算用户
                finalData.put(userName, temp);
            }
        }
        return finalData;
    }

    /***
     * 处理获取pNum参数
     */
    private RespCommon getPNumAndInterval(Map<String, Map<String, List<UserDataExcel>>> finalData, String lineId, List<String> dateStr) {
        int pNum;
        LineDetailDataVO lineDetailData = lineFeignClient.getLineDetailData(lineId).getData();
        int lineInterval = lineDetailData.getTimeInterval();
        int userIntervalTime;
        if (lineInterval == INTERVAL_TIME_1 || lineInterval == INTERVAL_TIME_3 || lineInterval == INTERVAL_TIME_5) {
            userIntervalTime = INTERVAL_TIME_15;
            pNum = dateStr.size() * WINDOW_96;
        } else {
            userIntervalTime = INTERVAL_TIME_30;
            pNum = dateStr.size() * WINDOW_48;
            finalData = dealFinalDataByLineInterval(finalData);
        }
        return new RespCommon(pNum, userIntervalTime, lineInterval);
    }


    /***
     * 获取责任需要的谐波数据
     */
    private RespHarmData getRespHarmData(ResponsibilityCalculateParam responsibilityCalculateParam, int lineInterval) {
        HarmHistoryDataDTO data = harmDataFeignClient.getHistoryHarmData(new HistoryHarmParam(responsibilityCalculateParam.getSearchBeginTime(), responsibilityCalculateParam.getSearchEndTime(), responsibilityCalculateParam.getLineId(), responsibilityCalculateParam.getType(), responsibilityCalculateParam.getTime())).getData();
        List<HarmData> historyData = data.getHistoryData();
        historyData = getDataWithLineInterval(historyData, lineInterval);
        //理论上此处的historyData的长度等于pNum,开始填充harm_data
        float[] harmData = new float[144000];
        //谐波波形的横轴时间集合
        List<Long> harmTime = new ArrayList<>();
        for (int i = 0; i < historyData.size(); i++) {
            Float value = historyData.get(i).getValue();
//            if (value != null) {
//                value = value * 1000;
//            }
            harmData[i] = value;
            harmTime.add(PubUtils.instantToDate(historyData.get(i).getTime()).getTime());
        }
        return new RespHarmData(harmData, harmTime, data.getOverLimit());
    }

}
