From d2ab6f7153e604bac7bc4ad58f27f368b65d8a1e Mon Sep 17 00:00:00 2001
From: yuan <123@>
Date: 星期二, 16 六月 2026 13:54:58 +0800
Subject: [PATCH] feat: 添加能耗数据综合分析功能,支持按天和周维度的趋势分析
---
src/main/java/com/ruoyi/http/util/StatisticEleAggregateUtil.java | 151 +++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 137 insertions(+), 14 deletions(-)
diff --git a/src/main/java/com/ruoyi/http/util/StatisticEleAggregateUtil.java b/src/main/java/com/ruoyi/http/util/StatisticEleAggregateUtil.java
index c57f8bd..4ff014f 100644
--- a/src/main/java/com/ruoyi/http/util/StatisticEleAggregateUtil.java
+++ b/src/main/java/com/ruoyi/http/util/StatisticEleAggregateUtil.java
@@ -2,6 +2,8 @@
import com.ruoyi.http.vo.StatisticEleRecordVo;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
@@ -18,6 +20,8 @@
public final class StatisticEleAggregateUtil {
private static final DateTimeFormatter DAY_FMT = DateTimeFormatter.ofPattern("yyyyMMdd");
+ private static final int CONSUMPTION_SCALE = StatisticEleReadingUtil.CONSUMPTION_SCALE;
+ private static final int SUMMARY_SCALE = 2;
private StatisticEleAggregateUtil() {
}
@@ -30,6 +34,7 @@
String monthKey = HOUR_TO_MONTH.apply(tk);
return monthKey != null ? toQuarterKey(monthKey) : null;
};
+ public static final Function<String, String> HOUR_TO_WEEK = StatisticEleAnalyticsUtil.HOUR_TO_WEEK;
/**
* 鎸夋椂闂存《姹囨�伙紙澶氱數琛ㄥ悎骞讹紝鐢ㄤ簬鍥捐〃锛�
@@ -62,10 +67,84 @@
mergeInto(map, key, bucket, record);
StatisticEleRecordVo agg = map.get(key);
agg.setMeterId(record.getMeterId());
- agg.setAddress(record.getAddress());
- agg.setCollectorNo(record.getCollectorNo());
+ mergeMeterInfo(agg, record);
}
return sorted(map);
+ }
+
+ private static void mergeMeterInfo(StatisticEleRecordVo agg, StatisticEleRecordVo record) {
+ if (hasText(record.getMeterName())) {
+ agg.setMeterName(record.getMeterName());
+ }
+ if (hasText(record.getAddress())) {
+ agg.setAddress(record.getAddress());
+ }
+ mergeTimeRange(agg, record);
+ }
+
+ private static void mergeTimeRange(StatisticEleRecordVo agg, StatisticEleRecordVo record) {
+ if (hasText(record.getStartTime())) {
+ if (!hasText(agg.getStartTime()) || record.getStartTime().compareTo(agg.getStartTime()) < 0) {
+ agg.setStartTime(record.getStartTime());
+ }
+ }
+ if (hasText(record.getEndTime())) {
+ if (!hasText(agg.getEndTime()) || record.getEndTime().compareTo(agg.getEndTime()) > 0) {
+ agg.setEndTime(record.getEndTime());
+ }
+ }
+ }
+
+ /** 姹囨�诲悗鑻ヨ捣姝㈡椂闂翠负绌猴紝鎸� timeKey 鎺ㄥ */
+ private static void fillTimeRangeIfEmpty(StatisticEleRecordVo vo) {
+ if (hasText(vo.getStartTime()) && hasText(vo.getEndTime())) {
+ return;
+ }
+ String timeKey = vo.getTimeKey();
+ if (!hasText(timeKey)) {
+ return;
+ }
+ if (timeKey.contains("Q")) {
+ String[] parts = timeKey.split("Q");
+ if (parts.length != 2) {
+ return;
+ }
+ int year = Integer.parseInt(parts[0]);
+ int quarter = Integer.parseInt(parts[1]);
+ int startMonth = (quarter - 1) * 3 + 1;
+ int endMonth = startMonth + 2;
+ YearMonth endYm = YearMonth.of(year, endMonth);
+ vo.setStartTime(String.format("%04d-%02d-01 00:00:00", year, startMonth));
+ vo.setEndTime(String.format("%04d-%02d-%02d 23:59:59", year, endMonth, endYm.lengthOfMonth()));
+ return;
+ }
+ if (timeKey.length() == 4) {
+ vo.setStartTime(timeKey + "-01-01 00:00:00");
+ vo.setEndTime(timeKey + "-12-31 23:59:59");
+ return;
+ }
+ if (timeKey.length() == 6) {
+ YearMonth ym = YearMonth.parse(timeKey, DateTimeFormatter.ofPattern("yyyyMM"));
+ vo.setStartTime(String.format("%04d-%02d-01 00:00:00", ym.getYear(), ym.getMonthValue()));
+ vo.setEndTime(String.format("%04d-%02d-%02d 23:59:59",
+ ym.getYear(), ym.getMonthValue(), ym.lengthOfMonth()));
+ return;
+ }
+ if (timeKey.length() >= 8) {
+ String day = timeKey.substring(0, 8);
+ vo.setStartTime(toDateTime(day, "00:00:00"));
+ vo.setEndTime(toDateTime(day, "23:59:59"));
+ }
+ }
+
+ private static String toDateTime(String yyyyMMdd, String time) {
+ return yyyyMMdd.substring(0, 4) + "-"
+ + yyyyMMdd.substring(4, 6) + "-"
+ + yyyyMMdd.substring(6, 8) + " " + time;
+ }
+
+ private static boolean hasText(String value) {
+ return value != null && !value.isBlank();
}
/**
@@ -100,6 +179,7 @@
return switch (dimension) {
case "hour" -> new HourRange(startTime, endTime);
case "day" -> new HourRange(startTime + "00", endTime + "23");
+ case "week" -> new HourRange(startTime + "00", endTime + "23");
case "month" -> new HourRange(startTime + "0100", endTime + lastDayOfMonth(endTime) + "23");
case "year" -> new HourRange(startTime + "010100", endTime + "123123");
case "quarter" -> new HourRange(
@@ -144,17 +224,19 @@
/** 鏄庣粏璁板綍鎬荤敤鐢甸噺锛堜笌鏁版嵁閲囬泦椤垫眰鍜屾柟寮忎竴鑷达級 */
public static double sumRecordsTotal(List<StatisticEleRecordVo> records) {
- return records.stream()
+ BigDecimal total = records.stream()
.map(StatisticEleRecordVo::getTotalConsumption)
.filter(v -> v != null)
- .mapToDouble(Double::doubleValue)
- .sum();
+ .map(BigDecimal::valueOf)
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
+ return roundSummary(total);
}
public static Function<String, String> bucketFn(String dimension) {
return switch (dimension) {
case "hour" -> HOUR_TO_HOUR;
case "day" -> HOUR_TO_DAY;
+ case "week" -> HOUR_TO_WEEK;
case "month" -> HOUR_TO_MONTH;
case "quarter" -> HOUR_TO_QUARTER;
case "year" -> HOUR_TO_YEAR;
@@ -179,15 +261,17 @@
metrics.setMinConsumption(0.0);
return metrics;
}
- List<Double> values = buckets.stream()
+ List<BigDecimal> values = buckets.stream()
.map(StatisticEleRecordVo::getTotalConsumption)
.filter(v -> v != null)
+ .map(BigDecimal::valueOf)
.collect(Collectors.toList());
- double total = values.stream().mapToDouble(Double::doubleValue).sum();
- metrics.setTotalConsumption(round(total));
- metrics.setAvgConsumption(round(total / values.size()));
- metrics.setMaxConsumption(round(values.stream().mapToDouble(Double::doubleValue).max().orElse(0)));
- metrics.setMinConsumption(round(values.stream().mapToDouble(Double::doubleValue).min().orElse(0)));
+ BigDecimal total = values.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
+ metrics.setTotalConsumption(roundSummary(total));
+ metrics.setAvgConsumption(roundSummary(total.divide(
+ BigDecimal.valueOf(values.size()), CONSUMPTION_SCALE, RoundingMode.HALF_UP)));
+ metrics.setMaxConsumption(roundSummary(values.stream().max(BigDecimal::compareTo).orElse(BigDecimal.ZERO)));
+ metrics.setMinConsumption(roundSummary(values.stream().min(BigDecimal::compareTo).orElse(BigDecimal.ZERO)));
return metrics;
}
@@ -212,6 +296,10 @@
private static List<StatisticEleRecordVo> sorted(Map<String, StatisticEleRecordVo> map) {
return map.values().stream()
+ .peek(vo -> {
+ normalizeConsumptions(vo);
+ fillTimeRangeIfEmpty(vo);
+ })
.sorted(Comparator.comparing(StatisticEleRecordVo::getTimeKey))
.collect(Collectors.toList());
}
@@ -222,11 +310,46 @@
}
private static Double add(Double a, Double b) {
- return (a == null ? 0.0 : a) + (b == null ? 0.0 : b);
+ BigDecimal sum = BigDecimal.valueOf(a == null ? 0.0 : a)
+ .add(BigDecimal.valueOf(b == null ? 0.0 : b));
+ return roundConsumption(sum);
}
- private static double round(double value) {
- return Math.round(value * 100.0) / 100.0;
+ /** 缁熶竴鐢甸噺瀛楁绮惧害锛堟槑缁嗗睍绀猴級 */
+ public static void normalizeConsumptions(StatisticEleRecordVo vo) {
+ if (vo == null) {
+ return;
+ }
+ vo.setTotalConsumption(roundConsumption(vo.getTotalConsumption()));
+ vo.setSharpConsumption(roundConsumption(vo.getSharpConsumption()));
+ vo.setPeakConsumption(roundConsumption(vo.getPeakConsumption()));
+ vo.setFlatConsumption(roundConsumption(vo.getFlatConsumption()));
+ vo.setValleyConsumption(roundConsumption(vo.getValleyConsumption()));
+ }
+
+ public static void normalizeConsumptions(List<StatisticEleRecordVo> records) {
+ if (records == null) {
+ return;
+ }
+ records.forEach(vo -> normalizeConsumptions(vo));
+ }
+
+ private static Double roundConsumption(Double value) {
+ if (value == null) {
+ return null;
+ }
+ return roundConsumption(BigDecimal.valueOf(value));
+ }
+
+ private static Double roundConsumption(BigDecimal value) {
+ if (value == null) {
+ return null;
+ }
+ return value.setScale(CONSUMPTION_SCALE, RoundingMode.HALF_UP).doubleValue();
+ }
+
+ private static double roundSummary(BigDecimal value) {
+ return value.setScale(SUMMARY_SCALE, RoundingMode.HALF_UP).doubleValue();
}
public record HourRange(String startTime, String endTime) {}
--
Gitblit v1.9.3