|
|
@@ -0,0 +1,283 @@
|
|
|
+package com.zksy.cockpit.overview;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
+import com.zksy.base.alarm.domain.AlarmData;
|
|
|
+import com.zksy.base.alarm.service.AlarmDataService;
|
|
|
+import com.zksy.base.warning.domain.AlarmWarningRel;
|
|
|
+import com.zksy.base.warning.domain.EarlyWarning;
|
|
|
+import com.zksy.base.warning.mapper.AlarmWarningRelMapper;
|
|
|
+import com.zksy.base.warning.service.EarlyWarningService;
|
|
|
+import com.zksy.ops.domain.WorkOrder;
|
|
|
+import com.zksy.ops.service.WorkOrderService;
|
|
|
+import org.springframework.context.annotation.Primary;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.ZoneId;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.LinkedHashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 总体概览-预警设置 真实数据适配器(BT-5)。
|
|
|
+ *
|
|
|
+ * <p>报警事件来自 {@code alarm_data}(经 {@code work_order_id → work_order.order_id} 关联工单);
|
|
|
+ * 预警事件来自 {@code early_warning},工单经
|
|
|
+ * {@code alarm_warning_rel.warning_id → alarm_data.work_order_id → work_order} 桥接(README 冻结关联)。
|
|
|
+ * 趋势桶按业务时区日期({@code yyyy-MM-dd})聚合,已处理 = 关联工单已结案(状态 5)。
|
|
|
+ * {@code updatedAt} 取事件与工单的最新时间。
|
|
|
+ */
|
|
|
+@Component
|
|
|
+@Primary
|
|
|
+public class BusinessCockpitOverviewWarningSettingsAdapter
|
|
|
+ implements CockpitOverviewWarningSettingsAdapter {
|
|
|
+
|
|
|
+ private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
|
|
|
+ private static final DateTimeFormatter BUCKET_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
+ private static final ZoneId BUSINESS_ZONE = ZoneId.of("Asia/Shanghai");
|
|
|
+ private static final int WORK_ORDER_HANDLED = 5;
|
|
|
+
|
|
|
+ private final EarlyWarningService earlyWarningService;
|
|
|
+ private final AlarmDataService alarmDataService;
|
|
|
+ private final AlarmWarningRelMapper alarmWarningRelMapper;
|
|
|
+ private final WorkOrderService workOrderService;
|
|
|
+
|
|
|
+ public BusinessCockpitOverviewWarningSettingsAdapter(
|
|
|
+ EarlyWarningService earlyWarningService,
|
|
|
+ AlarmDataService alarmDataService,
|
|
|
+ AlarmWarningRelMapper alarmWarningRelMapper,
|
|
|
+ WorkOrderService workOrderService) {
|
|
|
+ this.earlyWarningService = earlyWarningService;
|
|
|
+ this.alarmDataService = alarmDataService;
|
|
|
+ this.alarmWarningRelMapper = alarmWarningRelMapper;
|
|
|
+ this.workOrderService = workOrderService;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public CockpitOverviewWarningSettingsSnapshot load() {
|
|
|
+ List<EarlyWarning> warnings = safeList(earlyWarningService.list());
|
|
|
+ List<AlarmData> alarms = safeList(alarmDataService.list());
|
|
|
+ List<AlarmWarningRel> relations = safeList(alarmWarningRelMapper.selectList(new QueryWrapper<AlarmWarningRel>()));
|
|
|
+ List<WorkOrder> orders = safeList(workOrderService.list());
|
|
|
+
|
|
|
+ Map<Long, WorkOrder> orderByOrderId = new HashMap<>();
|
|
|
+ for (WorkOrder order : orders) {
|
|
|
+ if (order != null && order.getOrderId() != null) {
|
|
|
+ orderByOrderId.put(order.getOrderId(), order);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, List<String>> warningNosByAlarmId = new HashMap<>();
|
|
|
+ Map<String, String> warningNoById = new HashMap<>();
|
|
|
+ for (EarlyWarning warning : warnings) {
|
|
|
+ if (warning == null || isBlank(warning.getWarningId()) || isBlank(warning.getWarningNo())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ warningNoById.put(warning.getWarningId(), warning.getWarningNo().trim());
|
|
|
+ }
|
|
|
+ for (AlarmWarningRel relation : relations) {
|
|
|
+ if (relation == null || isBlank(relation.getAlarmId())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ String warningNo = warningNoById.get(relation.getWarningId());
|
|
|
+ if (warningNo != null) {
|
|
|
+ warningNosByAlarmId.computeIfAbsent(relation.getAlarmId(), key -> new ArrayList<>()).add(warningNo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ List<CockpitOverviewWarningSettingsSnapshot.SourceEvent> events = new ArrayList<>();
|
|
|
+ LocalDateTime updatedAt = null;
|
|
|
+
|
|
|
+ for (AlarmData alarm : alarms) {
|
|
|
+ if (alarm == null || isBlank(alarm.getId())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ updatedAt = latest(updatedAt, alarm.getUpdateTime());
|
|
|
+ updatedAt = latest(updatedAt, alarm.getCreateTime());
|
|
|
+ WorkOrder order = alarm.getWorkOrderId() == null ? null : orderByOrderId.get(alarm.getWorkOrderId());
|
|
|
+ if (order != null) {
|
|
|
+ updatedAt = latest(updatedAt, toLocal(order.getUpdateTime()));
|
|
|
+ updatedAt = latest(updatedAt, toLocal(order.getCreateTime()));
|
|
|
+ }
|
|
|
+ events.add(new CockpitOverviewWarningSettingsSnapshot.SourceEvent(
|
|
|
+ "ALARM",
|
|
|
+ alarm.getId().trim(),
|
|
|
+ trimToNull(alarm.getDeviceCode()),
|
|
|
+ order == null ? null : order.getOrderNo(),
|
|
|
+ order == null || order.getOrderStatus() == null ? null : order.getOrderStatus()));
|
|
|
+ }
|
|
|
+
|
|
|
+ for (EarlyWarning warning : warnings) {
|
|
|
+ if (warning == null || isBlank(warning.getWarningNo())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ updatedAt = latest(updatedAt, warning.getUpdateTime());
|
|
|
+ updatedAt = latest(updatedAt, warning.getPublishTime());
|
|
|
+ List<String> linkedAlarmIds = new ArrayList<>();
|
|
|
+ for (AlarmWarningRel relation : relations) {
|
|
|
+ if (relation != null && warning.getWarningId() != null
|
|
|
+ && warning.getWarningId().equals(relation.getWarningId())) {
|
|
|
+ linkedAlarmIds.add(relation.getAlarmId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ WorkOrder order = null;
|
|
|
+ for (String alarmId : linkedAlarmIds) {
|
|
|
+ AlarmData alarm = alarms.stream()
|
|
|
+ .filter(candidate -> candidate != null && alarmId != null && alarmId.equals(candidate.getId()))
|
|
|
+ .findFirst().orElse(null);
|
|
|
+ if (alarm != null && alarm.getWorkOrderId() != null) {
|
|
|
+ order = orderByOrderId.get(alarm.getWorkOrderId());
|
|
|
+ if (order != null) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (order != null) {
|
|
|
+ updatedAt = latest(updatedAt, toLocal(order.getUpdateTime()));
|
|
|
+ updatedAt = latest(updatedAt, toLocal(order.getCreateTime()));
|
|
|
+ }
|
|
|
+ events.add(new CockpitOverviewWarningSettingsSnapshot.SourceEvent(
|
|
|
+ "WARNING",
|
|
|
+ warning.getWarningNo().trim(),
|
|
|
+ trimToNull(warning.getDeviceCode()),
|
|
|
+ order == null ? null : order.getOrderNo(),
|
|
|
+ order == null || order.getOrderStatus() == null ? null : order.getOrderStatus()));
|
|
|
+ }
|
|
|
+
|
|
|
+ List<CockpitOverviewWarningSettingsSnapshot.SourceTrendPoint> trendPoints = new ArrayList<>();
|
|
|
+ addBucket(trendPoints, "WARNING", bucketFor(warnings, alarms, warningNosByAlarmId, orderByOrderId));
|
|
|
+ addBucket(trendPoints, "ALARM", bucketFor(alarms, orderByOrderId));
|
|
|
+
|
|
|
+ return new CockpitOverviewWarningSettingsSnapshot(offsetTime(updatedAt), events, trendPoints);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<String, Counter> bucketFor(
|
|
|
+ List<EarlyWarning> warnings,
|
|
|
+ List<AlarmData> alarms,
|
|
|
+ Map<String, List<String>> warningNosByAlarmId,
|
|
|
+ Map<Long, WorkOrder> orderByOrderId) {
|
|
|
+ Map<String, Counter> buckets = new LinkedHashMap<>();
|
|
|
+ for (EarlyWarning warning : warnings) {
|
|
|
+ if (warning == null || isBlank(warning.getWarningNo())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ LocalDateTime time = firstNonNull(warning.getPublishTime(), warning.getCreateTime());
|
|
|
+ if (time == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ Counter counter = buckets.computeIfAbsent(bucket(time), key -> new Counter());
|
|
|
+ counter.total++;
|
|
|
+ if (isHandledWarning(warning, warningNosByAlarmId, alarms, orderByOrderId)) {
|
|
|
+ counter.handled++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return buckets;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<String, Counter> bucketFor(List<AlarmData> alarms, Map<Long, WorkOrder> orderByOrderId) {
|
|
|
+ Map<String, Counter> buckets = new LinkedHashMap<>();
|
|
|
+ for (AlarmData alarm : alarms) {
|
|
|
+ if (alarm == null || isBlank(alarm.getId())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ LocalDateTime time = firstNonNull(alarm.getUpdateTime(), alarm.getCreateTime());
|
|
|
+ if (time == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ Counter counter = buckets.computeIfAbsent(bucket(time), key -> new Counter());
|
|
|
+ counter.total++;
|
|
|
+ WorkOrder order = alarm.getWorkOrderId() == null ? null : orderByOrderId.get(alarm.getWorkOrderId());
|
|
|
+ if (order != null && WORK_ORDER_HANDLED == order.getOrderStatus()) {
|
|
|
+ counter.handled++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return buckets;
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean isHandledWarning(
|
|
|
+ EarlyWarning warning,
|
|
|
+ Map<String, List<String>> warningNosByAlarmId,
|
|
|
+ List<AlarmData> alarms,
|
|
|
+ Map<Long, WorkOrder> orderByOrderId) {
|
|
|
+ if (warning.getWarningId() == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ for (AlarmData alarm : alarms) {
|
|
|
+ if (alarm == null || alarm.getWorkOrderId() == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ List<String> linked = warningNosByAlarmId.get(alarm.getId());
|
|
|
+ if (linked != null && linked.contains(warning.getWarningNo().trim())) {
|
|
|
+ WorkOrder order = orderByOrderId.get(alarm.getWorkOrderId());
|
|
|
+ if (order != null && WORK_ORDER_HANDLED == order.getOrderStatus()) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void addBucket(
|
|
|
+ List<CockpitOverviewWarningSettingsSnapshot.SourceTrendPoint> trendPoints,
|
|
|
+ String family,
|
|
|
+ Map<String, Counter> buckets) {
|
|
|
+ for (Map.Entry<String, Counter> entry : buckets.entrySet()) {
|
|
|
+ trendPoints.add(new CockpitOverviewWarningSettingsSnapshot.SourceTrendPoint(
|
|
|
+ family, entry.getKey(), entry.getValue().total, entry.getValue().handled));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String bucket(LocalDateTime time) {
|
|
|
+ return BUCKET_FORMATTER.format(time.atZone(BUSINESS_ZONE).toLocalDate());
|
|
|
+ }
|
|
|
+
|
|
|
+ private static LocalDateTime firstNonNull(LocalDateTime primary, LocalDateTime fallback) {
|
|
|
+ return primary != null ? primary : fallback;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static LocalDateTime toLocal(java.util.Date value) {
|
|
|
+ return value == null ? null
|
|
|
+ : LocalDateTime.ofInstant(value.toInstant(), BUSINESS_ZONE);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static LocalDateTime latest(LocalDateTime current, LocalDateTime candidate) {
|
|
|
+ if (candidate == null) {
|
|
|
+ return current;
|
|
|
+ }
|
|
|
+ if (current == null || candidate.isAfter(current)) {
|
|
|
+ return candidate;
|
|
|
+ }
|
|
|
+ return current;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String offsetTime(LocalDateTime value) {
|
|
|
+ return value == null ? null : TIME_FORMATTER.format(value.atZone(BUSINESS_ZONE).toOffsetDateTime());
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String trimToNull(String value) {
|
|
|
+ if (value == null || value.trim().isEmpty()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return value.trim();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static <T> List<T> safeList(List<T> value) {
|
|
|
+ return value == null ? Collections.emptyList() : value.stream().filter(Objects::nonNull).collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ private static boolean isBlank(String value) {
|
|
|
+ return value == null || value.trim().isEmpty();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class Counter {
|
|
|
+ private int total;
|
|
|
+ private int handled;
|
|
|
+ }
|
|
|
+}
|