yuan
3 天以前 03e1ff455f71330e6d1adaaa46c6613238f2b1bd
feat: 添加电量记录同步功能及相关数据处理优化
已修改7个文件
138 ■■■■ 文件已修改
src/main/java/com/ruoyi/http/controller/TqdianbiaoEleRecordController.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/http/service/impl/StatisticEleServiceImpl.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/http/task/TqdianbiaoSyncTask.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/http/util/StatisticEleAggregateUtil.java 67 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/http/util/StatisticEleParseUtil.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/http/util/StatisticEleReadingUtil.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-zqsy.yml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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);
    }
}
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(
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 {
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) {}
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();
    }
}
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) {
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