From 03e1ff455f71330e6d1adaaa46c6613238f2b1bd Mon Sep 17 00:00:00 2001
From: yuan <123@>
Date: 星期二, 16 六月 2026 10:58:16 +0800
Subject: [PATCH] feat: 添加电量记录同步功能及相关数据处理优化

---
 src/main/java/com/ruoyi/http/controller/TqdianbiaoEleRecordController.java |   23 +++++++
 src/main/java/com/ruoyi/http/util/StatisticEleParseUtil.java               |   30 ++++++---
 src/main/resources/application-zqsy.yml                                    |    8 ++
 src/main/java/com/ruoyi/http/task/TqdianbiaoSyncTask.java                  |    2 
 src/main/java/com/ruoyi/http/util/StatisticEleAggregateUtil.java           |   67 ++++++++++++++++++----
 src/main/java/com/ruoyi/http/util/StatisticEleReadingUtil.java             |    4 +
 src/main/java/com/ruoyi/http/service/impl/StatisticEleServiceImpl.java     |    4 +
 7 files changed, 113 insertions(+), 25 deletions(-)

diff --git a/src/main/java/com/ruoyi/http/controller/TqdianbiaoEleRecordController.java b/src/main/java/com/ruoyi/http/controller/TqdianbiaoEleRecordController.java
index a165b8a..4870802 100644
--- a/src/main/java/com/ruoyi/http/controller/TqdianbiaoEleRecordController.java
+++ b/src/main/java/com/ruoyi/http/controller/TqdianbiaoEleRecordController.java
@@ -7,6 +7,7 @@
 import com.ruoyi.framework.web.domain.AjaxResult;
 import com.ruoyi.http.pojo.TqdianbiaoEleRecord;
 import com.ruoyi.http.service.TqdianbiaoEleRecordManageService;
+import com.ruoyi.http.service.TqdianbiaoEleSyncService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import lombok.RequiredArgsConstructor;
@@ -17,7 +18,12 @@
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 @RestController
 @Tag(name = "鐢甸噺璁板綍绠$悊")
@@ -25,7 +31,10 @@
 @RequiredArgsConstructor
 public class TqdianbiaoEleRecordController extends BaseController {
 
+    private static final DateTimeFormatter HOUR_FMT = DateTimeFormatter.ofPattern("yyyyMMddHH");
+
     private final TqdianbiaoEleRecordManageService eleRecordManageService;
+    private final TqdianbiaoEleSyncService eleSyncService;
 
     @GetMapping("/listPage")
     @Operation(summary = "鐢甸噺璁板綍-鍒嗛〉鏌ヨ")
@@ -56,4 +65,18 @@
     public AjaxResult prevReading(Long meterId, String timeKey) {
         return AjaxResult.success(eleRecordManageService.getPrevReading(meterId, timeKey));
     }
+
+    @PostMapping("/syncRecentOnce")
+    @Log(title = "鐢甸噺璁板綍-杩戜笁澶╄ˉ鏁�", businessType = BusinessType.OTHER)
+    @Operation(summary = "鍚屾鍓嶄笁澶╄嚦褰撳墠灏忔椂鐢甸噺锛堜竴娆℃�цˉ鏁帮級")
+    public AjaxResult syncRecentOnce() {
+        LocalDateTime start = LocalDate.now().minusDays(3).atStartOfDay();
+        LocalDateTime end = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0);
+        int count = eleSyncService.syncLast3DaysHourData();
+        Map<String, Object> data = new HashMap<>();
+        data.put("count", count);
+        data.put("startTime", start.format(HOUR_FMT));
+        data.put("endTime", end.format(HOUR_FMT));
+        return AjaxResult.success("鍚屾瀹屾垚锛�" + data.get("startTime") + " ~ " + data.get("endTime") + " 鍏� " + count + " 鏉�", data);
+    }
 }
diff --git a/src/main/java/com/ruoyi/http/service/impl/StatisticEleServiceImpl.java b/src/main/java/com/ruoyi/http/service/impl/StatisticEleServiceImpl.java
index 878e927..357fdcd 100644
--- a/src/main/java/com/ruoyi/http/service/impl/StatisticEleServiceImpl.java
+++ b/src/main/java/com/ruoyi/http/service/impl/StatisticEleServiceImpl.java
@@ -163,7 +163,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(
diff --git a/src/main/java/com/ruoyi/http/task/TqdianbiaoSyncTask.java b/src/main/java/com/ruoyi/http/task/TqdianbiaoSyncTask.java
index ba390a9..f01c9cd 100644
--- a/src/main/java/com/ruoyi/http/task/TqdianbiaoSyncTask.java
+++ b/src/main/java/com/ruoyi/http/task/TqdianbiaoSyncTask.java
@@ -22,7 +22,7 @@
     private final TqdianbiaoMeterSyncService meterSyncService;
     private final TqdianbiaoEleSyncService eleSyncService;
 
-    @Scheduled(cron = "30 5 * * * ?")
+    //@Scheduled(cron = "30 5 * * * ?")
     public void syncCollectors() {
         if (!isEnabled()) return;
         try {
diff --git a/src/main/java/com/ruoyi/http/util/StatisticEleAggregateUtil.java b/src/main/java/com/ruoyi/http/util/StatisticEleAggregateUtil.java
index c57f8bd..4759781 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() {
     }
@@ -144,11 +148,12 @@
 
     /** 鏄庣粏璁板綍鎬荤敤鐢甸噺锛堜笌鏁版嵁閲囬泦椤垫眰鍜屾柟寮忎竴鑷达級 */
     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) {
@@ -179,15 +184,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 +219,7 @@
 
     private static List<StatisticEleRecordVo> sorted(Map<String, StatisticEleRecordVo> map) {
         return map.values().stream()
+                .peek(vo -> normalizeConsumptions(vo))
                 .sorted(Comparator.comparing(StatisticEleRecordVo::getTimeKey))
                 .collect(Collectors.toList());
     }
@@ -222,11 +230,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) {}
diff --git a/src/main/java/com/ruoyi/http/util/StatisticEleParseUtil.java b/src/main/java/com/ruoyi/http/util/StatisticEleParseUtil.java
index 5177dd7..bb682de 100644
--- a/src/main/java/com/ruoyi/http/util/StatisticEleParseUtil.java
+++ b/src/main/java/com/ruoyi/http/util/StatisticEleParseUtil.java
@@ -8,6 +8,7 @@
 import com.ruoyi.http.vo.StatisticEleRecordVo;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -99,24 +100,33 @@
 
     private static void fillConsumption(TqdianbiaoEleRecord entity, JSONArray d, Integer ratio,
                                         JSONArray sArr, JSONArray eArr) {
-        BigDecimal rawTotal = null;
         if (d != null && !d.isEmpty()) {
-            rawTotal = d.getBigDecimal(0);
-            entity.setSharpConsumption(d.size() >= 2 ? d.getBigDecimal(1) : null);
-            entity.setPeakConsumption(d.size() >= 3 ? d.getBigDecimal(2) : null);
-            entity.setFlatConsumption(d.size() >= 4 ? d.getBigDecimal(3) : null);
-            entity.setValleyConsumption(d.size() >= 5 ? d.getBigDecimal(4) : null);
-            entity.setDeepValleyConsumption(d.size() >= 6 ? d.getBigDecimal(5) : null);
+            entity.setTotalConsumption(StatisticEleReadingUtil.calcConsumptionFromRaw(d.getBigDecimal(0), ratio));
+            entity.setSharpConsumption(consumptionAt(d, 1, ratio));
+            entity.setPeakConsumption(consumptionAt(d, 2, ratio));
+            entity.setFlatConsumption(consumptionAt(d, 3, ratio));
+            entity.setValleyConsumption(consumptionAt(d, 4, ratio));
+            entity.setDeepValleyConsumption(consumptionAt(d, 5, ratio));
         } else {
-            rawTotal = StatisticEleReadingUtil.calcConsumption(
+            BigDecimal rawTotal = StatisticEleReadingUtil.calcConsumption(
                     StatisticEleReadingUtil.firstReading(sArr),
                     StatisticEleReadingUtil.firstReading(eArr),
                     1);
+            entity.setTotalConsumption(StatisticEleReadingUtil.calcConsumptionFromRaw(rawTotal, ratio));
         }
-        entity.setTotalConsumption(StatisticEleReadingUtil.calcConsumptionFromRaw(rawTotal, ratio));
+    }
+
+    private static BigDecimal consumptionAt(JSONArray d, int index, Integer ratio) {
+        if (d.size() <= index) {
+            return null;
+        }
+        return StatisticEleReadingUtil.calcConsumptionFromRaw(d.getBigDecimal(index), ratio);
     }
 
     private static Double toDouble(BigDecimal value) {
-        return value == null ? null : value.doubleValue();
+        if (value == null) {
+            return null;
+        }
+        return value.setScale(StatisticEleReadingUtil.CONSUMPTION_SCALE, RoundingMode.HALF_UP).doubleValue();
     }
 }
diff --git a/src/main/java/com/ruoyi/http/util/StatisticEleReadingUtil.java b/src/main/java/com/ruoyi/http/util/StatisticEleReadingUtil.java
index aacf4c4..b1b21e8 100644
--- a/src/main/java/com/ruoyi/http/util/StatisticEleReadingUtil.java
+++ b/src/main/java/com/ruoyi/http/util/StatisticEleReadingUtil.java
@@ -10,6 +10,8 @@
  */
 public final class StatisticEleReadingUtil {
 
+    public static final int CONSUMPTION_SCALE = 4;
+
     private StatisticEleReadingUtil() {
     }
 
@@ -40,7 +42,7 @@
             return null;
         }
         int r = ratio == null || ratio <= 0 ? 1 : ratio;
-        return value.multiply(BigDecimal.valueOf(r)).setScale(4, RoundingMode.HALF_UP);
+        return value.multiply(BigDecimal.valueOf(r)).setScale(CONSUMPTION_SCALE, RoundingMode.HALF_UP);
     }
 
     public static String formatReadingArray(JSONArray arr) {
diff --git a/src/main/resources/application-zqsy.yml b/src/main/resources/application-zqsy.yml
index 56786e6..8a76bb6 100644
--- a/src/main/resources/application-zqsy.yml
+++ b/src/main/resources/application-zqsy.yml
@@ -267,3 +267,11 @@
   compressQuality: 0.5 # 鍘嬬缉璐ㄩ噺(0.0-1.0)
 knowledge:
   one: D:\鏂扮枂澶х綏绱犱紒涓氫骇鍝佷綋绯昏鏄庢枃妗�.md
+
+tqdianbiao:
+  base-url: https://168.tqdianbiao.com
+  auth: b6229401590539d5def7f2bf897b33de
+  ignore-radio: 1
+  sync:
+    enabled: true
+    hour-window: 2

--
Gitblit v1.9.3