|
|
@@ -8,6 +8,8 @@ import com.hdkj.lt.bf.entity.FhzgSeMonitorCurrent;
|
|
|
import com.hdkj.lt.bf.entity.FhzgSeMonitorVoltage;
|
|
|
import com.hdkj.lt.bf.entity.FhzgSeSnapshotDetail;
|
|
|
import com.hdkj.lt.bf.entity.FhzgSeVoltageEvent;
|
|
|
+import com.hdkj.lt.bf.entity.FhzgSeBlockVoltageEvent;
|
|
|
+import com.hdkj.lt.bf.entity.FhzgSeBlockReconLog;
|
|
|
import com.hdkj.lt.bf.entity.dto.SeOverloadAlarmListReq;
|
|
|
import com.hdkj.lt.bf.entity.dto.SeVoltageAlarmListReq;
|
|
|
import com.hdkj.lt.bf.application.FeederGroupApplication;
|
|
|
@@ -17,6 +19,8 @@ import com.hdkj.lt.bf.mapper.FhzgSeMonitorCurrentMapper;
|
|
|
import com.hdkj.lt.bf.mapper.FhzgSeMonitorVoltageMapper;
|
|
|
import com.hdkj.lt.bf.mapper.FhzgSeSnapshotDetailMapper;
|
|
|
import com.hdkj.lt.bf.mapper.FhzgSeVoltageEventMapper;
|
|
|
+import com.hdkj.lt.bf.mapper.FhzgSeBlockVoltageEventMapper;
|
|
|
+import com.hdkj.lt.bf.mapper.FhzgSeBlockReconLogMapper;
|
|
|
import com.hdkj.lt.bf.service.SeAlarmService;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
@@ -45,6 +49,8 @@ public class SeAlarmServiceImpl implements SeAlarmService {
|
|
|
|
|
|
private final FhzgSeCurrentEventMapper currentEventMapper;
|
|
|
private final FhzgSeVoltageEventMapper voltageEventMapper;
|
|
|
+ private final FhzgSeBlockVoltageEventMapper blockVoltageEventMapper;
|
|
|
+ private final FhzgSeBlockReconLogMapper blockReconLogMapper;
|
|
|
private final FeederGroupApplication feederGroupApplication;
|
|
|
private final FhzgSeMonitorCurrentMapper monitorCurrentMapper;
|
|
|
private final FhzgSeMonitorVoltageMapper monitorVoltageMapper;
|
|
|
@@ -124,23 +130,57 @@ public class SeAlarmServiceImpl implements SeAlarmService {
|
|
|
LocalDateTime rangeStart = start.atStartOfDay();
|
|
|
LocalDateTime rangeEnd = end.plusDays(1).atStartOfDay();
|
|
|
|
|
|
- Page<FhzgSeVoltageEvent> mpPage = new Page<>(req.getPage(), req.getPageSize());
|
|
|
- LambdaQueryWrapper<FhzgSeVoltageEvent> wrapper = new LambdaQueryWrapper<FhzgSeVoltageEvent>()
|
|
|
- .ge(FhzgSeVoltageEvent::getFirstOverTime, rangeStart)
|
|
|
- .lt(FhzgSeVoltageEvent::getFirstOverTime, rangeEnd)
|
|
|
- .eq(FhzgSeVoltageEvent::getAlarmLevel, "feeder".equals(req.getAlarmLevel()) ? "feeder" : "mvtrans");
|
|
|
+ String alarmLevel = "feeder".equals(req.getAlarmLevel()) ? "feeder" : "mvtrans";
|
|
|
|
|
|
- applyVoltageFilter(wrapper, req.getId(), req.getType());
|
|
|
- if (StringUtils.isNotBlank(req.getFeederName())) {
|
|
|
- wrapper.like(FhzgSeVoltageEvent::getFeederName, req.getFeederName());
|
|
|
- }
|
|
|
- applyOrderBy(wrapper, req.getOrderByTime(), FhzgSeVoltageEvent::getFirstOverTime);
|
|
|
-
|
|
|
- Page<FhzgSeVoltageEvent> result = voltageEventMapper.selectPage(mpPage, wrapper);
|
|
|
- List<SeAlarmListVO> voList = result.getRecords().stream()
|
|
|
+ // 1. 实时电压事件(事件表,不分页全量拉取时间窗)
|
|
|
+ List<FhzgSeVoltageEvent> volEvents = voltageEventMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<FhzgSeVoltageEvent>()
|
|
|
+ .ge(FhzgSeVoltageEvent::getFirstOverTime, rangeStart)
|
|
|
+ .lt(FhzgSeVoltageEvent::getFirstOverTime, rangeEnd)
|
|
|
+ .eq(FhzgSeVoltageEvent::getAlarmLevel, alarmLevel)
|
|
|
+ .and(w -> applyVoltageFilter(w, req.getId(), req.getType()))
|
|
|
+ .like(StringUtils.isNotBlank(req.getFeederName()),
|
|
|
+ FhzgSeVoltageEvent::getFeederName, req.getFeederName()));
|
|
|
+ List<SeAlarmListVO> voList = volEvents.stream()
|
|
|
.map(this::toVoltageVO)
|
|
|
.collect(Collectors.toList());
|
|
|
- return PageResult.of(result, voList);
|
|
|
+
|
|
|
+ // 2. 成片电压事件(仅 feeder 级时合并,中台T-2聚合结果)
|
|
|
+ if ("feeder".equals(alarmLevel)) {
|
|
|
+ List<FhzgSeBlockVoltageEvent> blockEvents = blockVoltageEventMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<FhzgSeBlockVoltageEvent>()
|
|
|
+ .ge(FhzgSeBlockVoltageEvent::getEventStartTime, rangeStart)
|
|
|
+ .lt(FhzgSeBlockVoltageEvent::getEventStartTime, rangeEnd)
|
|
|
+ .and(w -> applyBlockVoltageFilter(w, req.getId(), req.getType()))
|
|
|
+ .like(StringUtils.isNotBlank(req.getFeederName()),
|
|
|
+ FhzgSeBlockVoltageEvent::getFeederName, req.getFeederName()));
|
|
|
+ // 批量关联 recon_log:alarmEventId 用稳定 log id(业务表 id 随中台重推变化,不能作跳转键)
|
|
|
+ Map<String, Long> logIdByEventId = queryBlockReconLogIds(blockEvents);
|
|
|
+ voList.addAll(blockEvents.stream()
|
|
|
+ .map(e -> toBlockVoltageVO(e, logIdByEventId))
|
|
|
+ .collect(Collectors.toList()));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 合并后统一排序(时间字段均为 yyyy-MM-dd HH:mm:ss,字典序=时间序)
|
|
|
+ Comparator<SeAlarmListVO> byTime = Comparator.comparing(SeAlarmListVO::getAlarmTime,
|
|
|
+ Comparator.nullsLast(String::compareTo));
|
|
|
+ voList.sort("asc".equalsIgnoreCase(req.getOrderByTime()) ? byTime : byTime.reversed());
|
|
|
+
|
|
|
+ // 4. 手工分页
|
|
|
+ long total = voList.size();
|
|
|
+ int fromIndex = (int) ((req.getPage() - 1) * req.getPageSize());
|
|
|
+ int toIndex = (int) Math.min(total, (long) req.getPage() * req.getPageSize());
|
|
|
+ List<SeAlarmListVO> pageList = fromIndex >= total
|
|
|
+ ? Collections.emptyList()
|
|
|
+ : new ArrayList<>(voList.subList(fromIndex, toIndex));
|
|
|
+
|
|
|
+ return PageResult.<SeAlarmListVO>builder()
|
|
|
+ .records(pageList)
|
|
|
+ .total(total)
|
|
|
+ .pages((total + req.getPageSize() - 1) / req.getPageSize())
|
|
|
+ .current(req.getPage())
|
|
|
+ .pageSize(req.getPageSize())
|
|
|
+ .build();
|
|
|
}
|
|
|
|
|
|
private SeAlarmListVO toVoltageVO(FhzgSeVoltageEvent e) {
|
|
|
@@ -219,9 +259,49 @@ public class SeAlarmServiceImpl implements SeAlarmService {
|
|
|
return buildFeederDetail(voltageEvent);
|
|
|
}
|
|
|
|
|
|
+ // 成片电压事件(alarmEventId 为 recon_log.id——中台重推业务表 id 会变,前端跳转用稳定 log id)
|
|
|
+ FhzgSeBlockReconLog blockLog = blockReconLogMapper.selectById(alarmEventId);
|
|
|
+ if (blockLog != null) {
|
|
|
+ FhzgSeBlockVoltageEvent blockEvent = blockVoltageEventMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<FhzgSeBlockVoltageEvent>()
|
|
|
+ .eq(FhzgSeBlockVoltageEvent::getEventId, blockLog.getEventId())
|
|
|
+ .last("LIMIT 1"));
|
|
|
+ if (blockEvent != null) {
|
|
|
+ return buildBlockDetail(blockEvent, blockLog);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 成片电压事件详情:直接使用中台聚合结果(T-2 历史,无断面快照)
|
|
|
+ * reconStatus 从传入的 recon_log 取(中台快照表无此字段)
|
|
|
+ * abnormalIndicator/alarmType 按 over_limit_type 映射成与实时电压事件一致(不做特化)
|
|
|
+ */
|
|
|
+ private SeAlarmDetailVO buildBlockDetail(FhzgSeBlockVoltageEvent e, FhzgSeBlockReconLog logRecord) {
|
|
|
+ Integer reconStatus = logRecord != null ? logRecord.getReconStatus() : 0;
|
|
|
+ String alarmType = resolveBlockVoltageAlarmType(e);
|
|
|
+ return SeAlarmDetailVO.builder()
|
|
|
+ .alarmEventId(logRecord != null ? logRecord.getId() : e.getId())
|
|
|
+ .alarmType(alarmType)
|
|
|
+ .alarmCategory("voltage")
|
|
|
+ .countyName(e.getMaintainerName())
|
|
|
+ .feederName(e.getFeederName())
|
|
|
+ .alarmStartTime(e.getEventStartTime())
|
|
|
+ .alarmEndTime(e.getEventEndTime())
|
|
|
+ .abnormalIndicator(resolveVoltageDisplay(alarmType, "feeder"))
|
|
|
+ .overLowerMvtransCount(e.getMemberTransCount())
|
|
|
+ .reconStatus(reconStatus)
|
|
|
+ .memberTransCount(e.getMemberTransCount())
|
|
|
+ .memberTransformerList(e.getMemberTransformerList())
|
|
|
+ .memberTimeRanges(e.getMemberTimeRanges())
|
|
|
+ .effectCustNum(e.getEffectCustNum())
|
|
|
+ .overLimitCause(e.getOverLimitCause())
|
|
|
+ .controlMeasures(e.getControlMeasures())
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 线路事件详情:展示电压范围 + 窗口内配变统计
|
|
|
*/
|
|
|
@@ -346,6 +426,77 @@ public class SeAlarmServiceImpl implements SeAlarmService {
|
|
|
else if (type == 4) wrapper.eq(FhzgSeVoltageEvent::getFeederId, id);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 成片事件节点过滤(与事件表对应:county=maintainer, subs=substation, feeder=feeder)
|
|
|
+ */
|
|
|
+ private void applyBlockVoltageFilter(LambdaQueryWrapper<FhzgSeBlockVoltageEvent> wrapper,
|
|
|
+ String id, Integer type) {
|
|
|
+ if (type == 2) wrapper.eq(FhzgSeBlockVoltageEvent::getMaintainerId, id);
|
|
|
+ else if (type == 3) wrapper.eq(FhzgSeBlockVoltageEvent::getSubstationId, id);
|
|
|
+ else if (type == 4) wrapper.eq(FhzgSeBlockVoltageEvent::getFeederId, id);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 成片事件列表行:展示方向(越上限/越下限)+ 影响用户数
|
|
|
+ * 实体字段无 alarmType/alarmLevel/status(中台快照表),按 over_limit_type 映射成
|
|
|
+ * 与实时电压事件一致的 alarmType(voltage_under/voltage_over),前端无需特化处理。
|
|
|
+ * alarmEventId 用 recon_log.id(稳定,中台重推业务表 id 会变,不能用)
|
|
|
+ */
|
|
|
+ private SeAlarmListVO toBlockVoltageVO(FhzgSeBlockVoltageEvent e, Map<String, Long> logIdByEventId) {
|
|
|
+ Long stableId = e.getEventId() != null ? logIdByEventId.get(e.getEventId()) : null;
|
|
|
+ String alarmType = resolveBlockVoltageAlarmType(e);
|
|
|
+ return SeAlarmListVO.builder()
|
|
|
+ .alarmEventId(stableId != null ? stableId : e.getId())
|
|
|
+ .deviceName(e.getFeederName())
|
|
|
+ .feederName(e.getFeederName())
|
|
|
+ .feederId(e.getFeederId())
|
|
|
+ .alarmDisplayValue(resolveVoltageDirection(alarmType))
|
|
|
+ .alarmTime(e.getEventStartTime() != null ? e.getEventStartTime().format(DT_FMT) : null)
|
|
|
+ .alarmDuration(calcDuration(e.getEventStartTime(), e.getEventEndTime(), STATUS_RECOVERED))
|
|
|
+ .alarmType(alarmType)
|
|
|
+ .alarmLevel("feeder")
|
|
|
+ .affectedConsumerCount(e.getEffectCustNum())
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 成片事件 alarmType:按 over_limit_type 映射成与实时电压事件一致的值
|
|
|
+ * (01=越下限 → voltage_under,02=越上限 → voltage_over),方向展示直接复用
|
|
|
+ * resolveVoltageDirection/resolveVoltageDisplay,不做特化。
|
|
|
+ */
|
|
|
+ private String resolveBlockVoltageAlarmType(FhzgSeBlockVoltageEvent e) {
|
|
|
+ if (e != null && "02".equals(e.getOverLimitType())) {
|
|
|
+ return "voltage_over";
|
|
|
+ }
|
|
|
+ return "voltage_under";
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 批量查成片 recon_log id 映射(eventId → log.id),未登记(未触发)的成片不在映射中
|
|
|
+ */
|
|
|
+ private Map<String, Long> queryBlockReconLogIds(List<FhzgSeBlockVoltageEvent> blockEvents) {
|
|
|
+ Map<String, Long> map = new HashMap<>();
|
|
|
+ if (blockEvents == null || blockEvents.isEmpty()) return map;
|
|
|
+ List<String> eventIds = blockEvents.stream()
|
|
|
+ .map(FhzgSeBlockVoltageEvent::getEventId)
|
|
|
+ .filter(Objects::nonNull)
|
|
|
+ .distinct()
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ if (eventIds.isEmpty()) return map;
|
|
|
+ List<FhzgSeBlockReconLog> logs = blockReconLogMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<FhzgSeBlockReconLog>()
|
|
|
+ .in(FhzgSeBlockReconLog::getEventId, eventIds));
|
|
|
+ if (logs != null) {
|
|
|
+ for (FhzgSeBlockReconLog logRecord : logs) {
|
|
|
+ map.put(logRecord.getEventId(), logRecord.getId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 成片事件状态:中台快照均为已恢复历史数据 */
|
|
|
+ private static final int STATUS_RECOVERED = 2;
|
|
|
+
|
|
|
// ============================================================
|
|
|
// 线路下拉列表
|
|
|
// ============================================================
|