|
|
@@ -1,11 +1,14 @@
|
|
|
package com.hdkj.lt.bf.service.impl;
|
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
|
|
import com.hdkj.lt.bf.entity.FhzgSeCurrentEvent;
|
|
|
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.dto.SeOverloadAlarmListReq;
|
|
|
+import com.hdkj.lt.bf.entity.dto.SeVoltageAlarmListReq;
|
|
|
import com.hdkj.lt.bf.application.FeederGroupApplication;
|
|
|
import com.hdkj.lt.bf.entity.vo.*;
|
|
|
import com.hdkj.lt.bf.mapper.FhzgSeCurrentEventMapper;
|
|
|
@@ -21,6 +24,7 @@ import org.apache.commons.lang3.StringUtils;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
+import java.time.Duration;
|
|
|
import java.time.LocalDate;
|
|
|
import java.time.LocalDateTime;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
@@ -58,30 +62,31 @@ public class SeAlarmServiceImpl implements SeAlarmService {
|
|
|
// ============================================================
|
|
|
|
|
|
@Override
|
|
|
- public PageResult<SeAlarmListVO> queryOverloadAlarmList(String id, Integer type, String feederName,
|
|
|
- String startDate, String endDate, String orderDir,
|
|
|
- Integer page, Integer pageSize) {
|
|
|
- if (page == null || page < 1) page = 1;
|
|
|
- if (pageSize == null || pageSize < 1) pageSize = 20;
|
|
|
-
|
|
|
- LocalDate start = parseDate(startDate);
|
|
|
- LocalDate end = parseDate(endDate);
|
|
|
+ public PageResult<SeAlarmListVO> queryOverloadAlarmList(SeOverloadAlarmListReq req) {
|
|
|
+ if (req.getPage() == null || req.getPage() < 1) req.setPage(1);
|
|
|
+ if (req.getPageSize() == null || req.getPageSize() < 1) req.setPageSize(20);
|
|
|
+
|
|
|
+ LocalDate start = parseDate(req.getStartDate());
|
|
|
+ LocalDate end = parseDate(req.getEndDate());
|
|
|
if (end == null || end.isBefore(start)) end = start;
|
|
|
LocalDateTime rangeStart = start.atStartOfDay();
|
|
|
LocalDateTime rangeEnd = end.plusDays(1).atStartOfDay();
|
|
|
|
|
|
- Page<FhzgSeCurrentEvent> mpPage = new Page<>(page, pageSize);
|
|
|
+ Page<FhzgSeCurrentEvent> mpPage = new Page<>(req.getPage(), req.getPageSize());
|
|
|
|
|
|
LambdaQueryWrapper<FhzgSeCurrentEvent> wrapper = new LambdaQueryWrapper<FhzgSeCurrentEvent>()
|
|
|
.ge(FhzgSeCurrentEvent::getFirstOverTime, rangeStart)
|
|
|
.lt(FhzgSeCurrentEvent::getFirstOverTime, rangeEnd)
|
|
|
.in(FhzgSeCurrentEvent::getAlarmType, OVERLOAD_TYPES);
|
|
|
|
|
|
- applyCurrentFilter(wrapper, id, type);
|
|
|
- if (StringUtils.isNotBlank(feederName)) {
|
|
|
- wrapper.like(FhzgSeCurrentEvent::getFeederName, feederName);
|
|
|
+ applyCurrentFilter(wrapper, req.getId(), req.getType());
|
|
|
+ if (StringUtils.isNotBlank(req.getFeederName())) {
|
|
|
+ wrapper.like(FhzgSeCurrentEvent::getFeederName, req.getFeederName());
|
|
|
}
|
|
|
- applyOrderBy(wrapper, orderDir, FhzgSeCurrentEvent::getFirstOverTime);
|
|
|
+
|
|
|
+ // 负载率排序优先级高于时间排序
|
|
|
+ applyOrderBy(wrapper, req.getOrderByLoadRate(), FhzgSeCurrentEvent::getMaxLoadRate);
|
|
|
+ applyOrderBy(wrapper, req.getOrderByTime(), FhzgSeCurrentEvent::getFirstOverTime);
|
|
|
|
|
|
Page<FhzgSeCurrentEvent> result = currentEventMapper.selectPage(mpPage, wrapper);
|
|
|
List<SeAlarmListVO> voList = result.getRecords().stream().map(this::toOverloadVO).collect(Collectors.toList());
|
|
|
@@ -89,15 +94,16 @@ public class SeAlarmServiceImpl implements SeAlarmService {
|
|
|
}
|
|
|
|
|
|
private SeAlarmListVO toOverloadVO(FhzgSeCurrentEvent e) {
|
|
|
- String displayValue = e.getLastLoadRate() != null
|
|
|
- ? e.getLastLoadRate().setScale(0, java.math.RoundingMode.HALF_UP).toPlainString()
|
|
|
- : "80";
|
|
|
+ String displayValue = e.getMaxLoadRate() != null
|
|
|
+ ? e.getMaxLoadRate().setScale(0, java.math.RoundingMode.HALF_UP).toPlainString()
|
|
|
+ : null;
|
|
|
return SeAlarmListVO.builder()
|
|
|
.alarmEventId(e.getId())
|
|
|
.feederName(e.getFeederName())
|
|
|
.feederId(e.getFeederId())
|
|
|
.alarmDisplayValue(displayValue)
|
|
|
.alarmTime(e.getFirstOverTime() != null ? e.getFirstOverTime().format(DT_FMT) : null)
|
|
|
+ .alarmDuration(calcDuration(e.getFirstOverTime(), e.getRecoverTime(), e.getStatus()))
|
|
|
.alarmType(e.getAlarmType())
|
|
|
.alarmLevel("feeder")
|
|
|
.build();
|
|
|
@@ -108,29 +114,27 @@ public class SeAlarmServiceImpl implements SeAlarmService {
|
|
|
// ============================================================
|
|
|
|
|
|
@Override
|
|
|
- public PageResult<SeAlarmListVO> queryVoltageAlarmList(String id, Integer type, String feederName,
|
|
|
- String startDate, String endDate, String orderDir,
|
|
|
- Integer page, Integer pageSize, String alarmLevel) {
|
|
|
- if (page == null || page < 1) page = 1;
|
|
|
- if (pageSize == null || pageSize < 1) pageSize = 20;
|
|
|
-
|
|
|
- LocalDate start = parseDate(startDate);
|
|
|
- LocalDate end = parseDate(endDate);
|
|
|
+ public PageResult<SeAlarmListVO> queryVoltageAlarmList(SeVoltageAlarmListReq req) {
|
|
|
+ if (req.getPage() == null || req.getPage() < 1) req.setPage(1);
|
|
|
+ if (req.getPageSize() == null || req.getPageSize() < 1) req.setPageSize(20);
|
|
|
+
|
|
|
+ LocalDate start = parseDate(req.getStartDate());
|
|
|
+ LocalDate end = parseDate(req.getEndDate());
|
|
|
if (end == null || end.isBefore(start)) end = start;
|
|
|
LocalDateTime rangeStart = start.atStartOfDay();
|
|
|
LocalDateTime rangeEnd = end.plusDays(1).atStartOfDay();
|
|
|
|
|
|
- Page<FhzgSeVoltageEvent> mpPage = new Page<>(page, pageSize);
|
|
|
+ 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(alarmLevel) ? "feeder" : "mvtrans");
|
|
|
+ .eq(FhzgSeVoltageEvent::getAlarmLevel, "feeder".equals(req.getAlarmLevel()) ? "feeder" : "mvtrans");
|
|
|
|
|
|
- applyVoltageFilter(wrapper, id, type);
|
|
|
- if (StringUtils.isNotBlank(feederName)) {
|
|
|
- wrapper.like(FhzgSeVoltageEvent::getFeederName, feederName);
|
|
|
+ applyVoltageFilter(wrapper, req.getId(), req.getType());
|
|
|
+ if (StringUtils.isNotBlank(req.getFeederName())) {
|
|
|
+ wrapper.like(FhzgSeVoltageEvent::getFeederName, req.getFeederName());
|
|
|
}
|
|
|
- applyOrderBy(wrapper, orderDir, FhzgSeVoltageEvent::getFirstOverTime);
|
|
|
+ applyOrderBy(wrapper, req.getOrderByTime(), FhzgSeVoltageEvent::getFirstOverTime);
|
|
|
|
|
|
Page<FhzgSeVoltageEvent> result = voltageEventMapper.selectPage(mpPage, wrapper);
|
|
|
List<SeAlarmListVO> voList = result.getRecords().stream().map(this::toVoltageVO).collect(Collectors.toList());
|
|
|
@@ -138,20 +142,46 @@ public class SeAlarmServiceImpl implements SeAlarmService {
|
|
|
}
|
|
|
|
|
|
private SeAlarmListVO toVoltageVO(FhzgSeVoltageEvent e) {
|
|
|
- String displayValue = resolveVoltageDisplay(e.getAlarmType(), e.getAlarmLevel());
|
|
|
+ String displayValue = resolveVoltageDirection(e.getAlarmType());
|
|
|
+ // 台区事件:查窗口内该配变下用户越限事件数(嵌套在配变窗口内的,不去重)
|
|
|
+ Integer consumerCount = null;
|
|
|
+ if ("mvtrans".equals(e.getAlarmLevel()) && e.getMvtransId() != null) {
|
|
|
+ LocalDateTime windowEnd = (e.getStatus() != null && e.getStatus() == 2 && e.getRecoverTime() != null)
|
|
|
+ ? e.getRecoverTime() : LocalDateTime.now();
|
|
|
+ Long cnt = voltageEventMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<FhzgSeVoltageEvent>()
|
|
|
+ .eq(FhzgSeVoltageEvent::getAlarmLevel, "consumer")
|
|
|
+ .eq(FhzgSeVoltageEvent::getMvtransId, e.getMvtransId())
|
|
|
+ .ge(FhzgSeVoltageEvent::getFirstOverTime, e.getFirstOverTime())
|
|
|
+ .lt(FhzgSeVoltageEvent::getFirstOverTime, windowEnd));
|
|
|
+ consumerCount = cnt != null ? cnt.intValue() : 0;
|
|
|
+ }
|
|
|
return SeAlarmListVO.builder()
|
|
|
.alarmEventId(e.getId())
|
|
|
+ .deviceName(e.getDeviceName())
|
|
|
.feederName(e.getFeederName())
|
|
|
.feederId(e.getFeederId())
|
|
|
.alarmDisplayValue(displayValue)
|
|
|
.alarmTime(e.getFirstOverTime() != null ? e.getFirstOverTime().format(DT_FMT) : null)
|
|
|
+ .alarmDuration(calcDuration(e.getFirstOverTime(), e.getRecoverTime(), e.getStatus()))
|
|
|
.alarmType(e.getAlarmType())
|
|
|
.alarmLevel(e.getAlarmLevel())
|
|
|
+ .affectedConsumerCount(consumerCount)
|
|
|
.build();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 展示标签:按 alarmType + alarmLevel 拼线路/台区电压越限
|
|
|
+ * 电压越限方向(列表用):只返回越上限/越下限,不带设备类型前缀
|
|
|
+ */
|
|
|
+ private String resolveVoltageDirection(String alarmType) {
|
|
|
+ if (alarmType == null) return "电压越限";
|
|
|
+ if (alarmType.contains("over")) return "越上限";
|
|
|
+ if (alarmType.contains("under")) return "越下限";
|
|
|
+ return "电压越限";
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 展示标签(详情用):按 alarmType + alarmLevel 拼线路/台区电压越限
|
|
|
*/
|
|
|
private String resolveVoltageDisplay(String alarmType, String alarmLevel) {
|
|
|
boolean isOver = alarmType != null && alarmType.contains("over");
|
|
|
@@ -181,7 +211,7 @@ public class SeAlarmServiceImpl implements SeAlarmService {
|
|
|
return buildOverloadDetail(currentEvent);
|
|
|
}
|
|
|
|
|
|
- // 电压事件(仅馈线级有详情,配变越限无详情)
|
|
|
+ // 电压事件
|
|
|
FhzgSeVoltageEvent voltageEvent = voltageEventMapper.selectById(alarmEventId);
|
|
|
if (voltageEvent != null && "feeder".equals(voltageEvent.getAlarmLevel())) {
|
|
|
return buildFeederDetail(voltageEvent);
|
|
|
@@ -244,35 +274,12 @@ public class SeAlarmServiceImpl implements SeAlarmService {
|
|
|
.build();
|
|
|
}
|
|
|
|
|
|
-// /**
|
|
|
-// * 单配变事件详情:展示该配变自身信息
|
|
|
-// */
|
|
|
-// private SeAlarmDetailVO buildMvtransDetail(FhzgSeVoltageEvent event) {
|
|
|
-// LocalDateTime endTime = event.getRecoverTime() != null ? event.getRecoverTime()
|
|
|
-// : (event.getTriggerTime() != null ? event.getTriggerTime() : event.getFirstOverTime());
|
|
|
-// return SeAlarmDetailVO.builder()
|
|
|
-// .alarmEventId(event.getId())
|
|
|
-// .alarmType(event.getAlarmType())
|
|
|
-// .alarmCategory("voltage")
|
|
|
-// .countyName(event.getCountyName())
|
|
|
-// .feederName(event.getFeederName())
|
|
|
-// .alarmStartTime(event.getFirstOverTime())
|
|
|
-// .alarmEndTime(endTime)
|
|
|
-// .abnormalIndicator(resolveVoltageDisplay(event.getAlarmType(), event.getAlarmLevel()))
|
|
|
-// .build();
|
|
|
-// }
|
|
|
+ // ============================================================
|
|
|
+ // 4. 重过载事件详情
|
|
|
+ // ============================================================
|
|
|
|
|
|
private SeAlarmDetailVO buildOverloadDetail(FhzgSeCurrentEvent event) {
|
|
|
- LocalDateTime endTime = event.getRecoverTime();
|
|
|
- if (endTime == null && event.getStatus() == 1) {
|
|
|
- FhzgSeMonitorCurrent state = monitorCurrentMapper.selectOne(
|
|
|
- new LambdaQueryWrapper<FhzgSeMonitorCurrent>()
|
|
|
- .eq(FhzgSeMonitorCurrent::getFeederId, event.getFeederId())
|
|
|
- .last("LIMIT 1"));
|
|
|
- if (state != null) endTime = state.getLastSnapTime();
|
|
|
- }
|
|
|
- if (endTime == null) endTime = event.getTriggerTime();
|
|
|
-
|
|
|
+ String abnormalIndicator = resolveOverloadDisplay(event.getAlarmType());
|
|
|
return SeAlarmDetailVO.builder()
|
|
|
.alarmEventId(event.getId())
|
|
|
.alarmType(event.getAlarmType())
|
|
|
@@ -280,27 +287,41 @@ public class SeAlarmServiceImpl implements SeAlarmService {
|
|
|
.countyName(event.getCountyName())
|
|
|
.feederName(event.getFeederName())
|
|
|
.alarmStartTime(event.getFirstOverTime())
|
|
|
- .alarmEndTime(endTime)
|
|
|
- .maxLoadRate(event.getMaxLoadRate())
|
|
|
+ .alarmEndTime(event.getRecoverTime())
|
|
|
+ .maxLoadRate(
|
|
|
+ event.getMaxLoadRate() != null
|
|
|
+ ? event.getMaxLoadRate().setScale(2, java.math.RoundingMode.HALF_UP)
|
|
|
+ : null)
|
|
|
+ .abnormalIndicator(abnormalIndicator)
|
|
|
.reconStatus(event.getReconStatus())
|
|
|
.build();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 查 feeder 事件发生在窗口 [firstOverTime, endTime] 内的 mvtrans 事件数
|
|
|
+ * 解析重过载展示标签
|
|
|
*/
|
|
|
- private int[] countMvtransInWindow(String feederId, LocalDateTime firstOverTime, LocalDateTime endTime) {
|
|
|
- if (firstOverTime == null) return new int[]{0, 0};
|
|
|
- LocalDateTime rangeEnd = endTime != null ? endTime : LocalDateTime.now();
|
|
|
- List<FhzgSeVoltageEvent> list = voltageEventMapper.selectList(
|
|
|
+ private String resolveOverloadDisplay(String alarmType) {
|
|
|
+ if ("current_overload".equals(alarmType)) return "线路过载";
|
|
|
+ if ("current_heavy".equals(alarmType)) return "线路重载";
|
|
|
+ return alarmType;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ============================================================
|
|
|
+ // 辅助:窗口内配变统计
|
|
|
+ // ============================================================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 统计指定馈线在窗口内的配变越限数
|
|
|
+ */
|
|
|
+ private int[] countMvtransInWindow(String feederId, LocalDateTime start, LocalDateTime end) {
|
|
|
+ List<FhzgSeVoltageEvent> mvEvents = voltageEventMapper.selectList(
|
|
|
new LambdaQueryWrapper<FhzgSeVoltageEvent>()
|
|
|
.eq(FhzgSeVoltageEvent::getFeederId, feederId)
|
|
|
.eq(FhzgSeVoltageEvent::getAlarmLevel, "mvtrans")
|
|
|
- .eq(FhzgSeVoltageEvent::getStatus, 1)
|
|
|
- .ge(FhzgSeVoltageEvent::getFirstOverTime, firstOverTime)
|
|
|
- .le(FhzgSeVoltageEvent::getFirstOverTime, rangeEnd));
|
|
|
+ .ge(FhzgSeVoltageEvent::getFirstOverTime, start)
|
|
|
+ .lt(FhzgSeVoltageEvent::getFirstOverTime, end));
|
|
|
int up = 0, down = 0;
|
|
|
- for (FhzgSeVoltageEvent e : list) {
|
|
|
+ for (FhzgSeVoltageEvent e : mvEvents) {
|
|
|
if (e.getAlarmType() != null && e.getAlarmType().contains("over")) up++;
|
|
|
else if (e.getAlarmType() != null && e.getAlarmType().contains("under")) down++;
|
|
|
}
|
|
|
@@ -378,8 +399,13 @@ public class SeAlarmServiceImpl implements SeAlarmService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 追加排序条件,orderDir 为空时不追加任何排序
|
|
|
+ * 多个字段排序时按调用顺序决定优先级,先调用的优先
|
|
|
+ */
|
|
|
private <T> void applyOrderBy(LambdaQueryWrapper<T> wrapper, String orderDir,
|
|
|
- com.baomidou.mybatisplus.core.toolkit.support.SFunction<T, ?> column) {
|
|
|
+ SFunction<T, ?> column) {
|
|
|
+ if (StringUtils.isBlank(orderDir)) return;
|
|
|
if ("asc".equalsIgnoreCase(orderDir)) {
|
|
|
wrapper.orderByAsc(column);
|
|
|
} else {
|
|
|
@@ -387,6 +413,18 @@ public class SeAlarmServiceImpl implements SeAlarmService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 计算告警持续时长
|
|
|
+ * @return 如 "1.5h",已结束用 recoverTime,活跃用当前时间
|
|
|
+ */
|
|
|
+ private String calcDuration(LocalDateTime start, LocalDateTime end, Integer status) {
|
|
|
+ if (start == null) return null;
|
|
|
+ LocalDateTime actualEnd = (status != null && status == 2 && end != null) ? end : LocalDateTime.now();
|
|
|
+ long minutes = Duration.between(start, actualEnd).toMinutes();
|
|
|
+ if (minutes < 0) minutes = 0;
|
|
|
+ return String.format("%.1fh", minutes / 60.0);
|
|
|
+ }
|
|
|
+
|
|
|
private void collectFeeders(com.hdkj.fhzg.optimization.response.FeederGroupTreeNode node,
|
|
|
java.util.List<FeederSelectVO> result) {
|
|
|
if (node.getType() == 4) {
|
|
|
@@ -398,4 +436,4 @@ public class SeAlarmServiceImpl implements SeAlarmService {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-}
|
|
|
+}
|