|
@@ -0,0 +1,165 @@
|
|
|
|
|
+package com.hdkj.lt.bf.scheduler;
|
|
|
|
|
+
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import com.hdkj.lt.bf.entity.FhzgSeCapacityDaily;
|
|
|
|
|
+import com.hdkj.lt.bf.entity.FhzgSeCurrentEvent;
|
|
|
|
|
+import com.hdkj.lt.bf.entity.FhzgSeSnapshotDetail;
|
|
|
|
|
+import com.hdkj.lt.bf.mapper.FhzgSeCapacityDailyMapper;
|
|
|
|
|
+import com.hdkj.lt.bf.mapper.FhzgSeCurrentEventMapper;
|
|
|
|
|
+import com.hdkj.lt.bf.mapper.FhzgSeSnapshotDetailMapper;
|
|
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
+
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
|
|
+import java.math.RoundingMode;
|
|
|
|
|
+import java.time.LocalDate;
|
|
|
|
|
+import java.time.LocalDateTime;
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.HashMap;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 供电能力日汇总逻辑(公共模块)
|
|
|
|
|
+ * <p>
|
|
|
|
|
+ * 定时调度由 pwfhzg-job 通过 sys_job 配置触发(凌晨1点聚合昨天),
|
|
|
|
|
+ * 也可由 bf 手动端点 /indicator/capacity/manual 触发。
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author lsl
|
|
|
|
|
+ * @since 2026-07-25
|
|
|
|
|
+ */
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+@Component
|
|
|
|
|
+@RequiredArgsConstructor
|
|
|
|
|
+public class SeCapacityDailyScheduler {
|
|
|
|
|
+
|
|
|
|
|
+ private final FhzgSeSnapshotDetailMapper snapshotDetailMapper;
|
|
|
|
|
+ private final FhzgSeCurrentEventMapper currentEventMapper;
|
|
|
|
|
+ private final FhzgSeCapacityDailyMapper capacityDailyMapper;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 聚合指定日期的断面/事件数据到日汇总表(手动触发时调用)
|
|
|
|
|
+ * <p>
|
|
|
|
|
+ * 负载率从断面明细聚合,重过载事件数从事件表统计(与事件表口径一致)
|
|
|
|
|
+ */
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public String aggregateForDate(LocalDate date) {
|
|
|
|
|
+ LocalDateTime startTime = date.atStartOfDay();
|
|
|
|
|
+ LocalDateTime endTime = date.atTime(23, 59, 59);
|
|
|
|
|
+
|
|
|
|
|
+ // 查当天所有馈线断面(负载率)
|
|
|
|
|
+ LambdaQueryWrapper<FhzgSeSnapshotDetail> wrapper = new LambdaQueryWrapper<FhzgSeSnapshotDetail>()
|
|
|
|
|
+ .eq(FhzgSeSnapshotDetail::getDeviceType, "feeder")
|
|
|
|
|
+ .isNotNull(FhzgSeSnapshotDetail::getLoadRate)
|
|
|
|
|
+ .ge(FhzgSeSnapshotDetail::getSnapshotTime, startTime)
|
|
|
|
|
+ .le(FhzgSeSnapshotDetail::getSnapshotTime, endTime);
|
|
|
|
|
+
|
|
|
|
|
+ List<FhzgSeSnapshotDetail> snapshots = snapshotDetailMapper.selectList(wrapper);
|
|
|
|
|
+
|
|
|
|
|
+ // 查当天重过载事件(事件数,口径与 fhzg_se_current_event 一致)
|
|
|
|
|
+ LambdaQueryWrapper<FhzgSeCurrentEvent> eventWrapper = new LambdaQueryWrapper<FhzgSeCurrentEvent>()
|
|
|
|
|
+ .ge(FhzgSeCurrentEvent::getFirstOverTime, startTime)
|
|
|
|
|
+ .lt(FhzgSeCurrentEvent::getFirstOverTime, date.plusDays(1).atStartOfDay())
|
|
|
|
|
+ .in(FhzgSeCurrentEvent::getAlarmType, "current_heavy", "current_overload");
|
|
|
|
|
+ List<FhzgSeCurrentEvent> events = currentEventMapper.selectList(eventWrapper);
|
|
|
|
|
+
|
|
|
|
|
+ if (snapshots.isEmpty() && events.isEmpty()) {
|
|
|
|
|
+ log.info("[供电能力日汇总] {} 无断面/事件数据,跳过", date);
|
|
|
|
|
+ return date + " 无断面/事件数据";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 按馈线分组聚合
|
|
|
|
|
+ Map<String, DailyAccumulator> accumulatorMap = new HashMap<>();
|
|
|
|
|
+ for (FhzgSeSnapshotDetail s : snapshots) {
|
|
|
|
|
+ String feederId = s.getFeederId();
|
|
|
|
|
+ if (feederId == null) continue;
|
|
|
|
|
+
|
|
|
|
|
+ DailyAccumulator acc = accumulatorMap.computeIfAbsent(feederId, k -> new DailyAccumulator());
|
|
|
|
|
+ acc.countyId = s.getCountyId();
|
|
|
|
|
+ acc.subsId = s.getSubsId();
|
|
|
|
|
+ if (acc.feederName == null) acc.feederName = s.getFeederName();
|
|
|
|
|
+
|
|
|
|
|
+ BigDecimal loadRate = s.getLoadRate();
|
|
|
|
|
+ if (loadRate != null) {
|
|
|
|
|
+ if (loadRate.compareTo(acc.maxLoadRate) > 0) {
|
|
|
|
|
+ acc.maxLoadRate = loadRate;
|
|
|
|
|
+ }
|
|
|
|
|
+ acc.sumLoadRate = acc.sumLoadRate.add(loadRate);
|
|
|
|
|
+ acc.totalSnapshots++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 事件数按馈线累加(同一条馈线当日多个事件算多次)
|
|
|
|
|
+ for (FhzgSeCurrentEvent e : events) {
|
|
|
|
|
+ String feederId = e.getFeederId();
|
|
|
|
|
+ if (feederId == null) continue;
|
|
|
|
|
+
|
|
|
|
|
+ DailyAccumulator acc = accumulatorMap.computeIfAbsent(feederId, k -> new DailyAccumulator());
|
|
|
|
|
+ if (acc.countyId == null) acc.countyId = e.getCountyId();
|
|
|
|
|
+ if (acc.subsId == null) acc.subsId = e.getSubsId();
|
|
|
|
|
+ if (acc.feederName == null) acc.feederName = e.getFeederName();
|
|
|
|
|
+
|
|
|
|
|
+ if ("current_heavy".equals(e.getAlarmType())) acc.heavyCount++;
|
|
|
|
|
+ else if ("current_overload".equals(e.getAlarmType())) acc.overloadCount++;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 先删当天已有数据(避免重复运行时叠加)
|
|
|
|
|
+ LambdaQueryWrapper<FhzgSeCapacityDaily> deleteWrapper = new LambdaQueryWrapper<FhzgSeCapacityDaily>()
|
|
|
|
|
+ .eq(FhzgSeCapacityDaily::getStatDate, date);
|
|
|
|
|
+ capacityDailyMapper.delete(deleteWrapper);
|
|
|
|
|
+
|
|
|
|
|
+ // 批量插入
|
|
|
|
|
+ List<FhzgSeCapacityDaily> dailyRecords = new ArrayList<>();
|
|
|
|
|
+ for (Map.Entry<String, DailyAccumulator> entry : accumulatorMap.entrySet()) {
|
|
|
|
|
+ String feederId = entry.getKey();
|
|
|
|
|
+ DailyAccumulator acc = entry.getValue();
|
|
|
|
|
+
|
|
|
|
|
+ BigDecimal avgLoadRate = acc.totalSnapshots > 0
|
|
|
|
|
+ ? acc.sumLoadRate.divide(BigDecimal.valueOf(acc.totalSnapshots), 2, RoundingMode.HALF_UP)
|
|
|
|
|
+ : BigDecimal.ZERO;
|
|
|
|
|
+
|
|
|
|
|
+ FhzgSeCapacityDaily daily = FhzgSeCapacityDaily.builder()
|
|
|
|
|
+ .statDate(date)
|
|
|
|
|
+ .feederId(feederId)
|
|
|
|
|
+ .feederName(acc.feederName)
|
|
|
|
|
+ .countyId(acc.countyId)
|
|
|
|
|
+ .subsId(acc.subsId)
|
|
|
|
|
+ .maxLoadRate(acc.maxLoadRate)
|
|
|
|
|
+ .avgLoadRate(avgLoadRate)
|
|
|
|
|
+ .heavyCount(acc.heavyCount)
|
|
|
|
|
+ .overloadCount(acc.overloadCount)
|
|
|
|
|
+ .totalSnapshots(acc.totalSnapshots)
|
|
|
|
|
+ .createTime(LocalDateTime.now())
|
|
|
|
|
+ .updateTime(LocalDateTime.now())
|
|
|
|
|
+ .build();
|
|
|
|
|
+ dailyRecords.add(daily);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 批量插入
|
|
|
|
|
+ for (FhzgSeCapacityDaily record : dailyRecords) {
|
|
|
|
|
+ capacityDailyMapper.insert(record);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String result = date + " 处理完成: " + dailyRecords.size() + " 条馈线日汇总";
|
|
|
|
|
+ log.info("[供电能力日汇总] {}", result);
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 日聚合累加器
|
|
|
|
|
+ */
|
|
|
|
|
+ private static class DailyAccumulator {
|
|
|
|
|
+ String countyId;
|
|
|
|
|
+ String subsId;
|
|
|
|
|
+ String feederName;
|
|
|
|
|
+ BigDecimal maxLoadRate = BigDecimal.ZERO;
|
|
|
|
|
+ BigDecimal sumLoadRate = BigDecimal.ZERO;
|
|
|
|
|
+ /** 当日重载事件数(从事件表统计) */
|
|
|
|
|
+ int heavyCount = 0;
|
|
|
|
|
+ /** 当日过载事件数(从事件表统计) */
|
|
|
|
|
+ int overloadCount = 0;
|
|
|
|
|
+ int totalSnapshots = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|