DYL0109
2025-04-16 75f043dfa6660716364e66ee0b3cf99f44255686
zhitan-system/src/main/java/com/zhitan/statisticalAnalysis/service/impl/EnergyConsumeDataServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,498 @@
package com.zhitan.statisticalAnalysis.service.impl;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zhitan.basicdata.domain.SysEnergy;
import com.zhitan.basicdata.mapper.SysEnergyMapper;
import com.zhitan.carbonemission.domain.CarbonEmission;
import com.zhitan.common.constant.CommonConst;
import com.zhitan.common.constant.TimeTypeConst;
import com.zhitan.common.utils.StringUtils;
import com.zhitan.dataitem.mapper.DataItemMapper;
import com.zhitan.model.domain.ModelNode;
import com.zhitan.model.domain.NodeIndex;
import com.zhitan.model.mapper.ModelNodeMapper;
import com.zhitan.model.mapper.NodeIndexMapper;
import com.zhitan.peakvalley.domain.ElectricityDataItem;
import com.zhitan.peakvalley.mapper.PeakValleyMapper;
import com.zhitan.statisticalAnalysis.common.DateTimeUtil;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import com.zhitan.statisticalAnalysis.domain.vo.*;
import com.zhitan.statisticalAnalysis.service.IEnergyConsumeDataService;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
/**
 * @Description: TODO
 * @author: yxw
 * @date: 2022å¹´04月12日 14:15
 */
@Service
@AllArgsConstructor
public class EnergyConsumeDataServiceImpl implements IEnergyConsumeDataService {
    private DataItemMapper dataItemMapper;
    private ModelNodeMapper modelNodeMapper;
    private NodeIndexMapper nodeIndexMapper;
    private PeakValleyMapper peakValleyMapper;
    private SysEnergyMapper sysEnergyMapper;
    /**
     * æˆæœ¬è¶‹åŠ¿åˆ†æžï¼ˆèƒ½æºæ¶ˆè€—æˆæœ¬ï¼‰- èŽ·å–è¡¨æ ¼åˆ—è¡¨æ•°æ®
     *
     * @param pageNo     é¡µç æ•°
     * @param pageSize   æ¯é¡µæ•°æ®å¤šå°‘
     * @param timeCode   æ—¶é—´å€¼   ä¸Žæ—¶é—´ç±»åž‹å¯¹åº”:2022-03-21/2022-03/2022
     * @param timeType   æ—¶é—´ç±»åž‹ DAY/MONTH/YEAR
     * @param energyType èƒ½æºç±»åž‹
     * @param modelCode  æ¨¡åž‹Code
     * @return
     */
    @Override
    public EnergyCostTrendPage listEnergyCostTrend(int pageNo, int pageSize, String timeCode, String timeType, String energyType,
                                                   String modelCode) {
        //能源类型信息
        SysEnergy sysEnergy = new SysEnergy();
        if (StringUtils.isNotEmpty(energyType)) {
            sysEnergy.setEnersno(energyType);
        }
        List<SysEnergy> sysEnergies = sysEnergyMapper.selectSysEnergyList(sysEnergy);
        if (sysEnergies.isEmpty()) {
            throw new RuntimeException("未查询到能源信息");
        }
        //节点信息
        List<ModelNode> modelNodes = modelNodeMapper.selectList(Wrappers.<ModelNode>lambdaQuery().eq(ModelNode::getModelCode, modelCode)
                .isNull(ModelNode::getParentId));
        if (ObjectUtils.isEmpty(modelNodes)) {
            throw new RuntimeException("未查询到节点信息");
        }
        ModelNode modelNodeInfo = modelNodes.stream().findFirst().get();
        //点位信息
        List<NodeIndex> nodeIndices = nodeIndexMapper.selectList(Wrappers.<NodeIndex>lambdaQuery()
                .eq(NodeIndex::getNodeId, modelNodeInfo.getNodeId()));
        if (nodeIndices.isEmpty()) {
            throw new RuntimeException("未查询到点位信息");
        }
        // æ€»è´¹ç”¨
        BigDecimal totalCost = BigDecimal.ZERO;
        // éåŽ†èƒ½æºç±»åž‹
        List<CostTrendEnergyTypeItem> itemList = new ArrayList<>();
        for (SysEnergy sysEnergyInfo : sysEnergies) {
            CostTrendEnergyTypeItem item = new CostTrendEnergyTypeItem();
            item.setEnergyType(sysEnergyInfo.getEnersno());
            item.setEnergyName(sysEnergyInfo.getEnername());
            // å¤„理时间
            Date bsTime = DateTimeUtil.getTime(timeType, timeCode);
            Date endTime = DateTimeUtil.getEndTimeByType(timeType, bsTime);
            totalCost = getEnergyUnitCostTrendAnalysisValueInfo(timeType, bsTime, endTime, totalCost, nodeIndices, modelNodeInfo.getNodeId(), sysEnergyInfo, item);
            itemList.add(item);
        }
        // éåŽ†ç”¨èƒ½å•å…ƒèŽ·å–è¡¨æ ¼ä¸­çš„æ•°æ®
        List<EnergyCostTrendItem> trendItemList = new ArrayList<>();
        EnergyCostTrendItem energyCostTrendItem = new EnergyCostTrendItem();
        energyCostTrendItem.setDateCode(timeCode);
        energyCostTrendItem.setTotal(totalCost.setScale(CommonConst.DIGIT_2, RoundingMode.HALF_UP));
        energyCostTrendItem.setItemList(itemList);
        trendItemList.add(energyCostTrendItem);
        EnergyCostTrendPage energyCostTrendPage = new EnergyCostTrendPage();
        energyCostTrendPage.setTotal(1);
        energyCostTrendPage.setItemList(trendItemList);
        return energyCostTrendPage;
    }
    /**
     * èŽ·å–ç”¨èƒ½å•å…ƒæˆæœ¬è¶‹åŠ¿åˆ†æžç´¯ç§¯é‡ã€è´¹ç”¨ä¿¡æ¯
     *
     * @param timeType      æ—¶é—´ç±»åž‹
     * @param bsTime        å¼€å§‹æ—¶é—´
     * @param endTime       ç»“束时间
     * @param totalCost     æ€»è´¹ç”¨
     * @param nodeIndices   èŠ‚ç‚¹ç‚¹ä½é›†åˆ
     * @param nodeId        èŠ‚ç‚¹id
     * @param sysEnergyInfo èƒ½æºç±»åž‹ä¿¡æ¯
     * @param item          è¿”回对象
     * @return
     */
    private BigDecimal getEnergyUnitCostTrendAnalysisValueInfo(String timeType, Date bsTime, Date endTime, BigDecimal totalCost,
                                                               List<NodeIndex> nodeIndices, String nodeId, SysEnergy sysEnergyInfo,
                                                               CostTrendEnergyTypeItem item) {
        BigDecimal costValue = BigDecimal.ZERO;
        BigDecimal accumulationValue = BigDecimal.ZERO;
        //电:只有HOUR数据有效;其他能源类型:HOUR、DAY有数据
        switch (sysEnergyInfo.getEnersno()) {
            case "electric":
                List<ElectricityDataItem> electricityDataItems = peakValleyMapper.getDataStatistics(nodeIndices.stream().map(NodeIndex::getIndexId).collect(Collectors.toSet()), bsTime, endTime, TimeTypeConst.TIME_TYPE_HOUR);
                costValue = electricityDataItems.stream().map(ElectricityDataItem::getCost).reduce(BigDecimal.ZERO, BigDecimal::add);
                accumulationValue = electricityDataItems.stream().map(ElectricityDataItem::getElectricity).reduce(BigDecimal.ZERO, BigDecimal::add);
                break;
            default:
                accumulationValue = dataItemMapper.getDataItemTimeRangeValueByNodeId(bsTime, endTime, TimeTypeConst.TIME_TYPE_DAY, nodeId, sysEnergyInfo.getEnersno());
                costValue = accumulationValue.multiply(sysEnergyInfo.getPrice());
                break;
        }
        costValue = costValue.setScale(CommonConst.DIGIT_2, RoundingMode.HALF_UP);
        totalCost = totalCost.add(costValue);
        item.setCost(costValue);
        item.setAccumulation(accumulationValue.setScale(CommonConst.DIGIT_2, RoundingMode.HALF_UP));
        return totalCost;
    }
    /**
     * æˆæœ¬è¶‹åŠ¿åˆ†æžï¼ˆèƒ½æºæ¶ˆè€—æˆæœ¬ï¼‰
     *
     * @param timeCode   æ—¶é—´å€¼   ä¸Žæ—¶é—´ç±»åž‹å¯¹åº”:2022-03-21/2022-03/2022
     * @param timeType   æ—¶é—´ç±»åž‹ DAY/MONTH/YEAR
     * @param modelCode  æ¨¡åž‹Code
     * @param energyType èƒ½æºç±»åž‹
     * @return
     */
    @Override
    public List<EnergyConsumeTrendDetailItem> listEnergyCostTrendDetail(String timeCode, String timeType, String modelCode, String energyType) {
        //能源类型信息
        SysEnergy sysEnergy = new SysEnergy();
        if (StringUtils.isNotEmpty(energyType)) {
            sysEnergy.setEnersno(energyType);
        }
        List<SysEnergy> sysEnergies = sysEnergyMapper.selectSysEnergyList(sysEnergy);
        if (sysEnergies.isEmpty()) {
            throw new RuntimeException("未查询到能源信息");
        }
        //节点信息
        List<ModelNode> modelNodes = modelNodeMapper.selectList(Wrappers.<ModelNode>lambdaQuery().eq(ModelNode::getModelCode, modelCode)
                .isNull(ModelNode::getParentId));
        if (modelNodes.isEmpty()) {
            throw new RuntimeException("未查询到节点信息");
        }
        String nodeId = modelNodes.stream().findFirst().get().getNodeId();
        // èƒ½è€—信息
        List<EnergyConsumeTrendDetailItem> itemList = new ArrayList<>();
        Date startTime = DateTimeUtil.getTime(timeType, timeCode);
        Date endTime = DateTimeUtil.getEndTimeByType(timeType, startTime);
        //电:只有HOUR数据有效;其他能源类型:HOUR、DAY有数据
        String queryTimeType = TimeTypeConst.TIME_TYPE_HOUR;
        for (SysEnergy sysEnergyInfo : sysEnergies) {
            List<EnergyConsumeVO> energyConsumeVOList = new ArrayList<>();
            switch (sysEnergyInfo.getEnersno()) {
                case "electric":
                    List<ElectricityDataItem> electricityDataItems = peakValleyMapper.getCostTrends(startTime, endTime, queryTimeType, nodeId, sysEnergyInfo.getEnersno());
                    if (!electricityDataItems.isEmpty()) {
                        electricityDataItems.forEach(electricityDataItem -> {
                            EnergyConsumeVO temp = new EnergyConsumeVO();
                            temp.setDataTime(electricityDataItem.getDataTime());
                            temp.setCostValue(electricityDataItem.getCost());
                            temp.setAccumulationValue(electricityDataItem.getElectricity());
                            energyConsumeVOList.add(temp);
                        });
                    }
                    break;
                default:
                    if (timeType.equals(TimeTypeConst.TIME_TYPE_MONTH) || timeType.equals(TimeTypeConst.TIME_TYPE_YEAR)) {
                        queryTimeType = TimeTypeConst.TIME_TYPE_DAY;
                    }
                    List<CarbonEmission> dataItems = dataItemMapper.getMiddleCarbonEmission(startTime, endTime, queryTimeType, nodeId, sysEnergyInfo.getEnersno());
                    if (!dataItems.isEmpty()) {
                        dataItems.forEach(electricityDataItem -> {
                            EnergyConsumeVO temp = new EnergyConsumeVO();
                            temp.setDataTime(electricityDataItem.getDataTime());
                            temp.setCostValue(new BigDecimal(electricityDataItem.getValue()));
                            temp.setAccumulationValue(new BigDecimal(electricityDataItem.getValue()).multiply(sysEnergyInfo.getPrice()));
                            energyConsumeVOList.add(temp);
                        });
                    }
                    break;
            }
            BigDecimal cost = energyConsumeVOList.stream().map(EnergyConsumeVO::getCostValue)
                    .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(CommonConst.DIGIT_2, RoundingMode.HALF_UP);
            BigDecimal accumulation = energyConsumeVOList.stream().map(EnergyConsumeVO::getAccumulationValue)
                    .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(CommonConst.DIGIT_2, RoundingMode.HALF_UP);
            // ç»„装统计图信息
            EnergyConsumeTrendDetailItem item = new EnergyConsumeTrendDetailItem();
            item.setEnergyType(sysEnergyInfo.getEnersno());
            item.setEnergyUnit(sysEnergyInfo.getMuid());
            item.setCostLabel(sysEnergyInfo.getEnername() + "è´¹");
            item.setAccumulationLabel(sysEnergyInfo.getEnername() + "用量");
            item.setCost(cost);
            item.setAccumulation(accumulation);
            // ç»„装图表信息
            getTrendAnalysisCharInfoByEnergyType(startTime, timeType, energyConsumeVOList, item);
            itemList.add(item);
        }
        return itemList;
    }
    /**
     * ç»„装成本趋势分析-统计图信息
     *
     * @param bsTime    æ—¶é—´
     * @param timeType  æ—¶é—´ç±»åž‹
     * @param dataItems èƒ½è€—
     * @param item      è¿”回对象
     */
    private void getTrendAnalysisCharInfoByEnergyType(Date bsTime, String timeType,
                                                      List<EnergyConsumeVO> dataItems, EnergyConsumeTrendDetailItem item) {
        List<String> costKeyList = new ArrayList<>();
        List<String> accumulationKeyList = new ArrayList<>();
        List<BigDecimal> costValueList = new ArrayList<>();
        List<BigDecimal> accumulationValueList = new ArrayList<>();
        Map<String, List<EnergyConsumeVO>> energyConsumeVOMap;
        //按时间类型组织返回数据
        switch (timeType) {
            case TimeTypeConst.TIME_TYPE_DAY:
                energyConsumeVOMap = dataItems.stream().collect(Collectors.groupingBy(li -> DateUtil.formatDateTime(li.getDataTime())));
                for (int i = 0; i < CommonConst.DIGIT_24; i++) {
                    String formatDate = i + CommonConst.TIME_UNIT_SHOW_HOUR;
                    costKeyList.add(formatDate);
                    accumulationKeyList.add(formatDate);
                    String key = DateUtil.formatDateTime(DateUtil.offsetHour(bsTime, i));
                    calculateCostAndAccumulation(energyConsumeVOMap, key, costValueList, accumulationValueList);
                }
                break;
            case TimeTypeConst.TIME_TYPE_MONTH:
                energyConsumeVOMap = dataItems.stream().collect(Collectors.groupingBy(li -> DateUtil.formatDate(li.getDataTime())));
                Date endTime = DateTimeUtil.getEndTimeByType(timeType, bsTime);
                while (bsTime.before(endTime)) {
                    String formatDate = DateUtil.formatDate(bsTime);
                    costKeyList.add(formatDate);
                    accumulationKeyList.add(formatDate);
                    calculateCostAndAccumulation(energyConsumeVOMap, formatDate, costValueList, accumulationValueList);
                    bsTime = DateUtil.offsetDay(bsTime, CommonConst.DIGIT_1);
                }
                break;
            case TimeTypeConst.TIME_TYPE_YEAR:
                SimpleDateFormat formatter = new SimpleDateFormat(DateTimeUtil.COMMON_PATTERN_TO_MONTH_ZH);
                energyConsumeVOMap = dataItems.stream().collect(Collectors.groupingBy(li -> formatter.format(li.getDataTime())));
                for (int i = 0; i < CommonConst.DIGIT_12; i++) {
                    Date newDate = DateUtil.offsetMonth(bsTime, i);
                    String formatDate = DateUtil.format(newDate, DateTimeUtil.COMMON_PATTERN_TO_MONTH_ZH);
                    costKeyList.add(formatDate);
                    accumulationKeyList.add(formatDate);
                    calculateCostAndAccumulation(energyConsumeVOMap, formatDate, costValueList, accumulationValueList);
                }
                break;
            default:
                break;
        }
        item.setCostKeyList(costKeyList);
        item.setCostValueList(costValueList);
        item.setAccumulationKeyList(accumulationKeyList);
        item.setAccumulationValueList(accumulationValueList);
    }
    /**
     * è®¡ç®—费用和用量
     *
     * @param energyConsumeVOMap
     * @param formatDate
     * @param costValueList
     * @param accumulationValueList
     */
    private static void calculateCostAndAccumulation(Map<String, List<EnergyConsumeVO>> energyConsumeVOMap, String formatDate, List<BigDecimal> costValueList, List<BigDecimal> accumulationValueList) {
        List<EnergyConsumeVO> energyConsumeList = Optional.ofNullable(energyConsumeVOMap.get(formatDate))
                .orElse(Collections.emptyList());
        BigDecimal totalCost = energyConsumeList.stream()
                .map(EnergyConsumeVO::getCostValue)
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .setScale(CommonConst.DIGIT_2, RoundingMode.HALF_UP);
        BigDecimal totalAccumulation = energyConsumeList.stream()
                .map(EnergyConsumeVO::getAccumulationValue)
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .setScale(CommonConst.DIGIT_2, RoundingMode.HALF_UP);
        costValueList.add(totalCost);
        accumulationValueList.add(totalAccumulation);
    }
    /**
     * åŒæ­¥çŽ¯æ¯”åˆ†æž
     *
     * @param req            è¯·æ±‚参数
     * @param comparisonType å¯¹æ¯”类型
     * @return
     */
    @Override
    public List<EnergyTypeValueContrastedVO> listEnergyTypeYoyInfo(QueryCompareRequest req, String comparisonType) {
        String energyType = req.getEnergyType();
        String timeType = req.getTimeType();
        String timeCode = req.getTimeCode();
        String nodeId = req.getNodeId();
        //能源类型信息
        SysEnergy sysEnergy = new SysEnergy();
        sysEnergy.setEnersno(energyType);
        List<SysEnergy> sysEnergies = sysEnergyMapper.selectSysEnergyList(sysEnergy);
        if (sysEnergies.isEmpty()) {
            throw new RuntimeException("未查询到能源信息");
        }
        SysEnergy sysEnergyInfo = sysEnergies.get(0);
        // èƒ½è€—信息
        Date startTime = DateTimeUtil.getTime(timeType, timeCode);
        Date endTime = DateTimeUtil.getEndTimeByType(timeType, startTime);
        //是否同比
        boolean isYoy = comparisonType.equals(CommonConst.ENERGY_COMPARISON_YOY);
        // è®¡ç®—上一年的同期时间
        Date lastBeginTime = DateUtil.offset(startTime, DateField.YEAR, CommonConst.DIGIT_MINUS_1);
        Date lastEndTime = DateUtil.offset(endTime, DateField.YEAR, CommonConst.DIGIT_MINUS_1);
        if (!isYoy) {
            switch (timeType) {
                case TimeTypeConst.TIME_TYPE_DAY:
                    lastBeginTime = DateUtil.offsetDay(startTime, CommonConst.DIGIT_MINUS_1);
                    lastEndTime = DateUtil.offsetDay(endTime, CommonConst.DIGIT_MINUS_1);
                    break;
                case TimeTypeConst.TIME_TYPE_MONTH:
                    lastBeginTime = DateUtil.offsetMonth(startTime, CommonConst.DIGIT_MINUS_1);
                    lastEndTime = DateUtil.offsetMonth(endTime, CommonConst.DIGIT_MINUS_1);
                    break;
            }
        }
        //电:只有HOUR数据有效;其他能源类型:HOUR、DAY有数据
        String queryTimeType = TimeTypeConst.TIME_TYPE_HOUR;
        List<EnergyConsumeVO> energyConsumeVOList = new ArrayList<>();
        switch (sysEnergyInfo.getEnersno()) {
            case "electric":
                List<ElectricityDataItem> electricityDataItems = peakValleyMapper.getCostTrends(startTime, endTime, queryTimeType, nodeId, sysEnergyInfo.getEnersno());
                List<ElectricityDataItem> lastDataItemList = peakValleyMapper.getCostTrends(lastBeginTime, lastEndTime, queryTimeType, nodeId, sysEnergyInfo.getEnersno());
                if (!lastDataItemList.isEmpty()) {
                    electricityDataItems.addAll(lastDataItemList);
                }
                if (!electricityDataItems.isEmpty()) {
                    electricityDataItems.forEach(electricityDataItem -> {
                        EnergyConsumeVO temp = new EnergyConsumeVO();
                        temp.setDataTime(electricityDataItem.getDataTime());
                        temp.setAccumulationValue(electricityDataItem.getElectricity());
                        energyConsumeVOList.add(temp);
                    });
                }
                break;
            default:
                if (timeType.equals(TimeTypeConst.TIME_TYPE_MONTH) || timeType.equals(TimeTypeConst.TIME_TYPE_YEAR)) {
                    queryTimeType = TimeTypeConst.TIME_TYPE_DAY;
                }
                List<CarbonEmission> dataItems = dataItemMapper.getMiddleCarbonEmission(startTime, endTime, queryTimeType, nodeId, sysEnergyInfo.getEnersno());
                List<CarbonEmission> lastDataItems = dataItemMapper.getMiddleCarbonEmission(lastBeginTime, lastEndTime, queryTimeType, nodeId, sysEnergyInfo.getEnersno());
                if (!lastDataItems.isEmpty()) {
                    dataItems.addAll(lastDataItems);
                }
                dataItems.addAll(lastDataItems);
                if (!dataItems.isEmpty()) {
                    dataItems.forEach(dataItem -> {
                        EnergyConsumeVO temp = new EnergyConsumeVO();
                        temp.setDataTime(dataItem.getDataTime());
                        temp.setAccumulationValue(new BigDecimal(dataItem.getValue()));
                        energyConsumeVOList.add(temp);
                    });
                }
                break;
        }
        // ç»„装统计图信息
        return getEnergyTypeValueContrastedVOList(startTime, timeType, energyConsumeVOList, sysEnergyInfo.getEnersno(), isYoy);
    }
    /**
     * ç»„装成本趋势分析-统计图信息
     *
     * @param bsTime     æ—¶é—´
     * @param timeType   æ—¶é—´ç±»åž‹
     * @param dataItems  èƒ½è€—
     * @param energyType èƒ½æºç±»åž‹
     * @param isYoy      æ˜¯å¦åŒæ¯”
     */
    private List<EnergyTypeValueContrastedVO> getEnergyTypeValueContrastedVOList(Date bsTime, String timeType,
                                                                                 List<EnergyConsumeVO> dataItems, String energyType, boolean isYoy) {
        Map<String, List<EnergyConsumeVO>> energyConsumeVOMap;
        Map<String, List<EnergyConsumeVO>> lastEnergyConsumeVOMap;
        List<EnergyTypeValueContrastedVO> itemList = new ArrayList<>();
        //按时间类型组织返回数据
        switch (timeType) {
            case TimeTypeConst.TIME_TYPE_DAY:
                energyConsumeVOMap = dataItems.stream().collect(Collectors.groupingBy(li -> DateUtil.formatDateTime(li.getDataTime())));
                for (int i = 0; i < CommonConst.DIGIT_24; i++) {
                    Date currentTime = DateUtil.offsetHour(bsTime, i);
                    Date compareTime = isYoy ? DateUtil.offset(currentTime, DateField.YEAR, CommonConst.DIGIT_MINUS_1) : DateUtil.offsetDay(currentTime, CommonConst.DIGIT_MINUS_1);
                    String keyCurrentTime = DateUtil.formatDateTime(currentTime);
                    String keyCompareTime = DateUtil.formatDateTime(compareTime);
                    EnergyTypeValueContrastedVO item = getEnergyTypeValueContrastedVO(energyType, energyConsumeVOMap, keyCurrentTime, keyCompareTime, currentTime, compareTime);
                    itemList.add(item);
                }
                break;
            case TimeTypeConst.TIME_TYPE_MONTH:
                energyConsumeVOMap = dataItems.stream().collect(Collectors.groupingBy(li -> DateUtil.formatDate(li.getDataTime())));
                Date endTime = DateTimeUtil.getEndTimeByType(timeType, bsTime);
                while (bsTime.before(endTime)) {
                    Date currentTime = bsTime;
                    Date compareTime = isYoy ? DateUtil.offset(currentTime, DateField.YEAR, CommonConst.DIGIT_MINUS_1) : DateUtil.offsetMonth(currentTime, CommonConst.DIGIT_MINUS_1);
                    String keyCurrentTime = DateUtil.formatDate(currentTime);
                    String keyCompareTime = DateUtil.formatDate(compareTime);
                    EnergyTypeValueContrastedVO item = getEnergyTypeValueContrastedVO(energyType, energyConsumeVOMap, keyCurrentTime, keyCompareTime, currentTime, compareTime);
                    itemList.add(item);
                    bsTime = DateUtil.offsetDay(bsTime, CommonConst.DIGIT_1);
                }
                break;
            case TimeTypeConst.TIME_TYPE_YEAR:
                SimpleDateFormat formatter = new SimpleDateFormat(DateTimeUtil.COMMON_PATTERN_TO_MONTH_ZH);
                energyConsumeVOMap = dataItems.stream().collect(Collectors.groupingBy(li -> formatter.format(li.getDataTime())));
                for (int i = 0; i < CommonConst.DIGIT_12; i++) {
                    Date currentTime = DateUtil.offsetMonth(bsTime, i);
                    Date compareTime = DateUtil.offset(currentTime, DateField.YEAR, CommonConst.DIGIT_MINUS_1);
                    String keyCurrentTime = formatter.format(currentTime);
                    String keyCompareTime = formatter.format(compareTime);
                    EnergyTypeValueContrastedVO item = getEnergyTypeValueContrastedVO(energyType, energyConsumeVOMap, keyCurrentTime, keyCompareTime, currentTime, compareTime);
                    itemList.add(item);
                }
                break;
            default:
                break;
        }
        return itemList;
    }
    private @NotNull EnergyTypeValueContrastedVO getEnergyTypeValueContrastedVO(String energyType, Map<String, List<EnergyConsumeVO>> energyConsumeVOMap,
                                                                                String keyCurrentTime, String keyCompareTime, Date currentTime, Date compareTime) {
        List<EnergyConsumeVO> energyConsumeList = Optional.ofNullable(energyConsumeVOMap.get(keyCurrentTime))
                .orElse(Collections.emptyList());
        BigDecimal currentValue = calculateSum(energyConsumeList);
        List<EnergyConsumeVO> lastEnergyConsumeList = Optional.ofNullable(energyConsumeVOMap.get(keyCompareTime))
                .orElse(Collections.emptyList());
        BigDecimal contrastValues = calculateSum(lastEnergyConsumeList);
        BigDecimal multiple = BigDecimal.valueOf(CommonConst.DIGIT_100);
        BigDecimal ratio = calculateRatio(currentValue, contrastValues, multiple);
        EnergyTypeValueContrastedVO item = new EnergyTypeValueContrastedVO();
        item.setEnergyType(energyType);
        item.setCurrentTime(DateUtil.formatDateTime(currentTime));
        item.setCompareTime(DateUtil.formatDateTime(compareTime));
        item.setCurrentValue(currentValue);
        item.setContrastValues(contrastValues);
        item.setRatio(ratio);
        return item;
    }
    private BigDecimal calculateSum(List<EnergyConsumeVO> dataItemList) {
        return dataItemList.stream()
                .map(EnergyConsumeVO::getAccumulationValue)
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .setScale(CommonConst.DIGIT_2, RoundingMode.HALF_UP);
    }
    private BigDecimal calculateRatio(BigDecimal currentSum, BigDecimal lastSum, BigDecimal multiple) {
        if (lastSum.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        return currentSum.subtract(lastSum)
                .divide(lastSum, CommonConst.DIGIT_2, RoundingMode.HALF_UP)
                .multiply(multiple)
                .setScale(CommonConst.DIGIT_2, RoundingMode.HALF_UP);
    }
}