yuan
3 天以前 d2ab6f7153e604bac7bc4ad58f27f368b65d8a1e
src/main/java/com/ruoyi/http/service/impl/StatisticEleServiceImpl.java
@@ -15,6 +15,9 @@
import com.ruoyi.http.service.StatisticEleService;
import com.ruoyi.http.util.StatisticEleAggregateUtil;
import com.ruoyi.http.util.StatisticEleAggregateUtil.HourRange;
import com.ruoyi.http.util.StatisticEleAnalyticsUtil;
import com.ruoyi.http.util.StatisticEleAnalyticsUtil.DateBounds;
import com.ruoyi.http.vo.StatisticEleAnalyticsVo;
import com.ruoyi.http.vo.StatisticEleRecordVo;
import com.ruoyi.http.vo.StatisticEleSummaryVo;
import com.ruoyi.http.vo.StatisticEleSyncStatusVo;
@@ -24,22 +27,24 @@
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@Service
@Slf4j
@RequiredArgsConstructor
public class StatisticEleServiceImpl implements StatisticEleService {
    private static final Set<String> STAT_DIMENSIONS = Set.of("day", "month", "quarter", "year");
    private static final Set<String> STAT_DIMENSIONS = Set.of("day", "week", "month", "quarter", "year");
    private static final List<String> DATA_DIMENSIONS = List.of("hour", "manual");
    private static final DateTimeFormatter LOG_TIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final DateTimeFormatter DAY_FMT = DateTimeFormatter.ofPattern("yyyyMMdd");
    private final TqdianbiaoConfig config;
    private final TqdianbiaoEleRecordMapper eleRecordMapper;
@@ -75,24 +80,116 @@
            throw new ServiceException("开始时间和结束时间不能为空");
        }
        if ("hour".equals(dimension)) {
            List<StatisticEleRecordVo> detailRecords = queryHourRecords(startTime, endTime);
            List<StatisticEleRecordVo> hourRecords = queryHourRecords(startTime, endTime);
            List<StatisticEleRecordVo> chartRecords = StatisticEleAggregateUtil.aggregateHourToBuckets(
                    detailRecords, StatisticEleAggregateUtil.HOUR_TO_HOUR);
            return buildSummary(detailRecords, chartRecords);
                    hourRecords, StatisticEleAggregateUtil.HOUR_TO_HOUR);
            return buildSummary(hourRecords, chartRecords, hourRecords);
        }
        if (!STAT_DIMENSIONS.contains(dimension)) {
            throw new ServiceException("统计维度无效,支持 hour/day/month/quarter/year");
            throw new ServiceException("统计维度无效,支持 hour/day/week/month/quarter/year");
        }
        if ("day".equals(dimension)) {
            return getDayDimensionSummary(startTime, endTime);
        }
        HourRange range = StatisticEleAggregateUtil.toHourQueryRange(dimension, startTime, endTime);
        List<StatisticEleRecordVo> hourRecords = queryHourRecords(range.startTime(), range.endTime());
        List<StatisticEleRecordVo> detailRecords = aggregateFromHour(dimension, startTime, endTime, true);
        List<StatisticEleRecordVo> chartRecords = aggregateFromHour(dimension, startTime, endTime, false);
        return buildSummary(detailRecords, chartRecords);
        return buildSummary(detailRecords, chartRecords, hourRecords);
    }
    @Override
    public StatisticEleAnalyticsVo getAnalytics(String dimension, String startTime, String endTime, String trendGranularity) {
        if (!StringUtils.hasText(startTime) || !StringUtils.hasText(endTime)) {
            throw new ServiceException("开始时间和结束时间不能为空");
        }
        StatisticEleSummaryVo summary = getSummary(dimension, startTime, endTime);
        HourRange range = StatisticEleAggregateUtil.toHourQueryRange(
                normalizeAnalyticsDimension(dimension), startTime, endTime);
        List<StatisticEleRecordVo> hourRecords = queryHourRecords(range.startTime(), range.endTime());
        List<StatisticEleRecordVo> hourlyMerged = StatisticEleAggregateUtil.aggregateHourToBuckets(
                hourRecords, StatisticEleAggregateUtil.HOUR_TO_HOUR);
        boolean singleDay = "day".equals(dimension) && startTime.equals(endTime);
        String trend = StringUtils.hasText(trendGranularity)
                ? trendGranularity
                : StatisticEleAnalyticsUtil.defaultTrendGranularity(dimension, singleDay);
        StatisticEleAnalyticsVo analytics = copyToAnalytics(summary);
        analytics.setLoadRate(StatisticEleAnalyticsUtil.calcLoadRate(
                summary.getAvgConsumption(), summary.getMaxConsumption()));
        analytics.setPeriodSplits(StatisticEleAnalyticsUtil.calcPeriodSplits(hourlyMerged));
        analytics.setShiftSplits(StatisticEleAnalyticsUtil.calcShiftSplits(hourlyMerged));
        analytics.setDayTypeSplits(StatisticEleAnalyticsUtil.calcDayTypeSplits(hourlyMerged));
        analytics.setTrendGranularity(trend);
        analytics.setTrendRecords(StatisticEleAggregateUtil.aggregateHourToBuckets(
                hourRecords, StatisticEleAnalyticsUtil.trendBucketFn(trend)));
        DateBounds bounds = StatisticEleAnalyticsUtil.resolveBounds(dimension, startTime, endTime);
        Double chainTotal = queryTotalByDayBounds(StatisticEleAnalyticsUtil.shiftChain(bounds));
        Double yoyTotal = queryTotalByDayBounds(StatisticEleAnalyticsUtil.shiftYoy(bounds));
        analytics.setChainComparison(StatisticEleAnalyticsUtil.buildComparison(
                "chain", "环比上期", summary.getTotalConsumption(), chainTotal));
        analytics.setYoyComparison(StatisticEleAnalyticsUtil.buildComparison(
                "yoy", "同比去年同期", summary.getTotalConsumption(), yoyTotal));
        return analytics;
    }
    private String normalizeAnalyticsDimension(String dimension) {
        return STAT_DIMENSIONS.contains(dimension) ? dimension : "day";
    }
    private Double queryTotalByDayBounds(DateBounds bounds) {
        String start = bounds.start().format(DAY_FMT);
        String end = bounds.end().format(DAY_FMT);
        HourRange range = StatisticEleAggregateUtil.toHourQueryRange("day", start, end);
        List<StatisticEleRecordVo> hourRecords = queryHourRecords(range.startTime(), range.endTime());
        if (hourRecords.isEmpty()) {
            return 0.0;
        }
        return StatisticEleAnalyticsUtil.calcCombinedMetrics(
                hourRecords,
                StatisticEleAggregateUtil.aggregateHourToBuckets(hourRecords, StatisticEleAggregateUtil.HOUR_TO_DAY)
        ).getTotalConsumption();
    }
    private StatisticEleAnalyticsVo copyToAnalytics(StatisticEleSummaryVo summary) {
        StatisticEleAnalyticsVo analytics = new StatisticEleAnalyticsVo();
        analytics.setTotalConsumption(summary.getTotalConsumption());
        analytics.setAvgConsumption(summary.getAvgConsumption());
        analytics.setMaxConsumption(summary.getMaxConsumption());
        analytics.setMinConsumption(summary.getMinConsumption());
        analytics.setRecordCount(summary.getRecordCount());
        analytics.setChartRecords(summary.getChartRecords());
        analytics.setRecords(summary.getRecords());
        return analytics;
    }
    /**
     * 天维度汇总:单日用小时级数据计算均值/极值并展示 24 小时趋势;多日按日桶对比。
     */
    private StatisticEleSummaryVo getDayDimensionSummary(String startTime, String endTime) {
        HourRange range = StatisticEleAggregateUtil.toHourQueryRange("day", startTime, endTime);
        List<StatisticEleRecordVo> hourRecords = queryHourRecords(range.startTime(), range.endTime());
        List<StatisticEleRecordVo> detailRecords = StatisticEleAggregateUtil.aggregateHourPerMeter(
                hourRecords, StatisticEleAggregateUtil.HOUR_TO_DAY);
        boolean singleDay = startTime.equals(endTime);
        List<StatisticEleRecordVo> chartRecords = singleDay
                ? StatisticEleAggregateUtil.aggregateHourToBuckets(hourRecords, StatisticEleAggregateUtil.HOUR_TO_HOUR)
                : StatisticEleAggregateUtil.aggregateHourToBuckets(hourRecords, StatisticEleAggregateUtil.HOUR_TO_DAY);
        return buildSummary(detailRecords, chartRecords, hourRecords);
    }
    private StatisticEleSummaryVo buildSummary(
            List<StatisticEleRecordVo> detailRecords, List<StatisticEleRecordVo> chartRecords) {
            List<StatisticEleRecordVo> detailRecords,
            List<StatisticEleRecordVo> chartRecords,
            List<StatisticEleRecordVo> hourRecords) {
        StatisticEleAggregateUtil.StatisticEleSummaryMetrics metrics =
                StatisticEleAggregateUtil.calcMetrics(chartRecords);
                StatisticEleAnalyticsUtil.calcCombinedMetrics(hourRecords, chartRecords);
        StatisticEleSummaryVo summary = new StatisticEleSummaryVo();
        summary.setRecords(detailRecords);
        summary.setChartRecords(chartRecords);
@@ -106,17 +203,8 @@
    @Override
    public StatisticEleSummaryVo getYesterdaySummary() {
        HourRange range = StatisticEleAggregateUtil.yesterdayHourRange();
        List<StatisticEleRecordVo> records = queryHourRecords(range.startTime(), range.endTime());
        List<StatisticEleRecordVo> chartRecords = StatisticEleAggregateUtil.aggregateHourToBuckets(
                records, StatisticEleAggregateUtil.HOUR_TO_HOUR);
        StatisticEleSummaryVo summary = buildSummary(records, chartRecords);
        summary.setTotalConsumption(round(StatisticEleAggregateUtil.sumRecordsTotal(records)));
        return summary;
    }
    private static double round(double value) {
        return Math.round(value * 100.0) / 100.0;
        String day = LocalDate.now().minusDays(1).format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        return getDayDimensionSummary(day, day);
    }
    @Override
@@ -163,7 +251,9 @@
    private List<StatisticEleRecordVo> queryHourRecords(String startTime, String endTime) {
        String normalizedStart = StatisticEleAggregateUtil.normalizeQueryStartTimeKey(startTime);
        String normalizedEnd = StatisticEleAggregateUtil.normalizeQueryEndTimeKey(endTime);
        return eleRecordMapper.selectRecordList(DATA_DIMENSIONS, normalizedStart, normalizedEnd);
        List<StatisticEleRecordVo> records = eleRecordMapper.selectRecordList(DATA_DIMENSIONS, normalizedStart, normalizedEnd);
        StatisticEleAggregateUtil.normalizeConsumptions(records);
        return records;
    }
    private List<StatisticEleRecordVo> aggregateFromHour(