From c73d493efe3ed4d4f9a3317ca729bc6b5920a1c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=AB=9E=E7=94=B1?= <1193626695@qq.com> Date: Mon, 1 Dec 2025 12:54:03 +0800 Subject: [PATCH] =?UTF-8?q?=E5=91=8A=E8=AD=A6=E9=80=BB=E8=BE=911.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 1 + .../campus/water/mapper/AlertRepository.java | 8 + .../water/service/AlertTriggerService.java | 230 ++++++++++++++++++ .../water/service/MqttSensorReceiver.java | 10 +- 4 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/campus/water/service/AlertTriggerService.java diff --git a/pom.xml b/pom.xml index 246cfe8..0e80add 100644 --- a/pom.xml +++ b/pom.xml @@ -86,6 +86,7 @@ true + org.springframework.boot diff --git a/src/main/java/com/campus/water/mapper/AlertRepository.java b/src/main/java/com/campus/water/mapper/AlertRepository.java index 63cf7aa..1ce22ea 100644 --- a/src/main/java/com/campus/water/mapper/AlertRepository.java +++ b/src/main/java/com/campus/water/mapper/AlertRepository.java @@ -34,4 +34,12 @@ public interface AlertRepository extends JpaRepository { // 根据处理人查询告警 List findByResolvedBy(String resolvedBy); + + // 新增:检查重复未处理告警 + List findByDeviceIdAndAlertTypeAndStatusAndTimestampAfter( + String deviceId, + String alertType, + List activeStatus, + LocalDateTime timestamp + ); } \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/AlertTriggerService.java b/src/main/java/com/campus/water/service/AlertTriggerService.java new file mode 100644 index 0000000..817146c --- /dev/null +++ b/src/main/java/com/campus/water/service/AlertTriggerService.java @@ -0,0 +1,230 @@ +package com.campus.water.service; + +import com.campus.water.entity.Alert; +import com.campus.water.entity.Device; +import com.campus.water.entity.WorkOrder; +import com.campus.water.mapper.AlertRepository; +import com.campus.water.mapper.DeviceRepository; +import com.campus.water.mapper.WorkOrderRepository; +import com.campus.water.model.WaterMakerSensorData; +import com.campus.water.model.WaterSupplySensorData; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +/** + * 告警触发与工单创建服务 + * 监听传感器数据,满足异常条件时自动创建告警和工单 + * 工单类型映射规则: + * - repair:故障维修(设备已故障,如漏水、TDS异常、水压/水位异常等) + * - maintenance:保养(设备未故障但需预防性维护,如滤芯寿命不足) + * - inspection:巡检(主动发起,不通过告警触发) + */ +@Service +@RequiredArgsConstructor +@Slf4j +public class AlertTriggerService { + + // 异常阈值配置(可根据实际需求调整或移至配置文件) + private static final double WATER_MAKER_TDS_THRESHOLD = 100.0; // 制水机TDS值异常阈值 + private static final double WATER_MAKER_PRESS_MIN = 0.2; // 最小水压 + private static final int FILTER_LIFE_THRESHOLD = 20; // 滤芯寿命阈值(%) + private static final double WATER_SUPPLY_LEVEL_MIN = 20.0; // 供水机最低水位(%) + private static final double WATER_SUPPLY_PRESS_MIN = 0.1; // 供水机最小水压 + private static final double WATER_SUPPLY_TEMP_MAX = 25.0; // 供水机最高水温(℃) + private static final int ALERT_DUPLICATE_INTERVAL = 30; // 重复告警间隔(分钟) + + private final AlertRepository alertRepository; + private final WorkOrderRepository workOrderRepository; + private final DeviceRepository deviceRepository; + + /** + * 检查制水机数据异常并触发告警 + * 区分故障类(repair)和保养类(maintenance)工单 + */ + @Transactional + public void checkWaterMakerAbnormal(WaterMakerSensorData data) { + // 1. 拆分故障类和保养类异常 + boolean isFaultAbnormal = false; // 故障类(需repair) + boolean isMaintainAbnormal = false; // 保养类(需maintenance) + StringBuilder faultAlertMsg = new StringBuilder("制水机故障:"); + StringBuilder maintainAlertMsg = new StringBuilder("制水机保养提醒:"); + + // 故障类异常判断(触发repair工单) + if (data.getTdsValue1() > WATER_MAKER_TDS_THRESHOLD) { + faultAlertMsg.append(String.format("原水TDS值过高(%.2f);", data.getTdsValue1())); + isFaultAbnormal = true; + } + if (data.getTdsValue2() > WATER_MAKER_TDS_THRESHOLD) { + faultAlertMsg.append(String.format("纯水TDS值过高(%.2f);", data.getTdsValue2())); + isFaultAbnormal = true; + } + if (data.getWaterPress() < WATER_MAKER_PRESS_MIN) { + faultAlertMsg.append(String.format("水压过低(%.2fMPa);", data.getWaterPress())); + isFaultAbnormal = true; + } + if (data.getLeakage()) { + faultAlertMsg.append("设备漏水;"); + isFaultAbnormal = true; + } + + // 保养类异常判断(触发maintenance工单) + if (data.getFilterLife() < FILTER_LIFE_THRESHOLD) { + maintainAlertMsg.append(String.format("滤芯寿命不足(%d%%),需更换;", data.getFilterLife())); + isMaintainAbnormal = true; + } + + // 2. 处理故障类告警(repair工单) + if (isFaultAbnormal) { + if (isDuplicateAlert(data.getDeviceId(), "WATER_MAKER_FAULT")) { + log.info("制水机存在未处理故障告警,跳过重复触发 | 设备ID:{}", data.getDeviceId()); + } else { + log.warn("制水机故障条件满足 | 设备ID:{} | 原因:{}", data.getDeviceId(), faultAlertMsg); + createAlertAndWorkOrder( + data.getDeviceId(), + "WATER_MAKER_FAULT", + Alert.AlertLevel.critical, + faultAlertMsg.toString(), + WorkOrder.OrderType.repair // 故障→维修工单 + ); + } + } + + // 3. 处理保养类告警(maintenance工单) + if (isMaintainAbnormal) { + if (isDuplicateAlert(data.getDeviceId(), "WATER_MAKER_MAINTENANCE")) { + log.info("制水机存在未处理保养告警,跳过重复触发 | 设备ID:{}", data.getDeviceId()); + } else { + log.warn("制水机保养条件满足 | 设备ID:{} | 原因:{}", data.getDeviceId(), maintainAlertMsg); + createAlertAndWorkOrder( + data.getDeviceId(), + "WATER_MAKER_MAINTENANCE", + Alert.AlertLevel.warning, + maintainAlertMsg.toString(), + WorkOrder.OrderType.maintenance // 保养→保养工单 + ); + } + } + } + + /** + * 检查供水机数据异常并触发告警 + * 均为故障类异常,触发repair工单 + */ + @Transactional + public void checkWaterSupplyAbnormal(WaterSupplySensorData data) { + // 1. 检查重复告警 + if (isDuplicateAlert(data.getDeviceId(), "WATER_SUPPLY_FAULT")) { + log.info("供水机存在未处理故障告警,跳过重复触发 | 设备ID:{}", data.getDeviceId()); + return; + } + + // 2. 故障类异常判断(仅repair工单) + boolean isAbnormal = false; + StringBuilder alertMsg = new StringBuilder("供水机故障:"); + + if (data.getWaterLevel() < WATER_SUPPLY_LEVEL_MIN) { + alertMsg.append(String.format("水位过低(%.2f%%);", data.getWaterLevel())); + isAbnormal = true; + } + if (data.getWaterPress() < WATER_SUPPLY_PRESS_MIN) { + alertMsg.append(String.format("水压过低(%.2fMPa);", data.getWaterPress())); + isAbnormal = true; + } + if (data.getTemperature() > WATER_SUPPLY_TEMP_MAX) { + alertMsg.append(String.format("水温过高(%.2f℃);", data.getTemperature())); + isAbnormal = true; + } + + // 3. 触发告警并创建维修工单 + if (isAbnormal) { + log.warn("供水机故障条件满足 | 设备ID:{} | 原因:{}", data.getDeviceId(), alertMsg); + createAlertAndWorkOrder( + data.getDeviceId(), + "WATER_SUPPLY_FAULT", + Alert.AlertLevel.error, + alertMsg.toString(), + WorkOrder.OrderType.repair // 供水机异常均为故障→维修工单 + ); + } + } + + /** + * 创建告警记录和对应的工单(支持repair/maintenance类型) + * @param orderType 工单类型(repair:维修,maintenance:保养,inspection:巡检<告警不触发>) + */ + private void createAlertAndWorkOrder(String deviceId, String alertType, + Alert.AlertLevel level, String message, + WorkOrder.OrderType orderType) { + // 获取设备所在区域(用于工单分配) + String areaId = getDeviceAreaId(deviceId); + + // 1. 创建告警记录 + Alert alert = new Alert(); + alert.setDeviceId(deviceId); + alert.setAlertType(alertType); + alert.setAlertLevel(level); + alert.setAlertMessage(message); + alert.setAreaId(areaId); + alert.setStatus(Alert.AlertStatus.pending); + alert.setTimestamp(LocalDateTime.now()); + alertRepository.save(alert); + log.info("创建告警记录成功 | 告警ID:{} | 设备ID:{} | 告警类型:{}", + alert.getAlertId(), deviceId, alertType); + + // 2. 创建对应类型的工单(inspection类型不通过告警触发) + WorkOrder workOrder = new WorkOrder(); + workOrder.setOrderId(generateOrderId()); + workOrder.setDeviceId(deviceId); + workOrder.setAreaId(areaId); + workOrder.setOrderType(orderType); // 动态设置工单类型 + workOrder.setDescription(message); + workOrder.setStatus(WorkOrder.OrderStatus.pending); + workOrder.setCreatedTime(LocalDateTime.now()); + workOrderRepository.save(workOrder); + log.info("创建工单成功 | 工单ID:{} | 设备ID:{} | 工单类型:{}", + workOrder.getOrderId(), deviceId, orderType.name()); + } + + /** + * 检查是否存在重复告警(同设备同类型未处理告警,且在间隔时间内) + * 覆盖pending/processing两种未处理状态 + */ + private boolean isDuplicateAlert(String deviceId, String alertType) { + LocalDateTime before = LocalDateTime.now().minusMinutes(ALERT_DUPLICATE_INTERVAL); + // 检查未处理的告警状态(pending/processing) + List activeStatus = Arrays.asList( + Alert.AlertStatus.pending, + Alert.AlertStatus.processing + ); + return !alertRepository.findByDeviceIdAndAlertTypeAndStatusAndTimestampAfter( + deviceId, + alertType, + activeStatus, + before + ).isEmpty(); + } + + /** + * 获取设备所在区域ID + */ + private String getDeviceAreaId(String deviceId) { + Optional deviceOpt = deviceRepository.findById(deviceId); + return deviceOpt.map(Device::getAreaId).orElse("unknown"); + } + + /** + * 生成唯一工单ID(WO+时间戳+随机数) + */ + private String generateOrderId() { + return String.format("WO%s%03d", + System.currentTimeMillis(), + (int)(Math.random() * 1000)); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/MqttSensorReceiver.java b/src/main/java/com/campus/water/service/MqttSensorReceiver.java index 92f358d..5220504 100644 --- a/src/main/java/com/campus/water/service/MqttSensorReceiver.java +++ b/src/main/java/com/campus/water/service/MqttSensorReceiver.java @@ -31,6 +31,8 @@ public class MqttSensorReceiver { private final AlertRepository alertRepo; private final ObjectMapper objectMapper; private final MqttPahoMessageDrivenChannelAdapter mqttAdapter; + // 新增告警触发服务依赖 + private final AlertTriggerService alertTriggerService; /** * 项目启动后初始化:订阅所有需要的MQTT主题 @@ -102,6 +104,9 @@ public class MqttSensorReceiver { // 3. 持久化到数据库(JPA save() 自动实现CRUD) waterMakerRepo.save(entity); log.info("制水机状态数据持久化成功 | 设备ID:{}", sensorData.getDeviceId()); + + // 新增:调用告警检查逻辑 + alertTriggerService.checkWaterMakerAbnormal(sensorData); } /** @@ -119,8 +124,6 @@ public class MqttSensorReceiver { "制水机异常 - 设备ID:%s,TDS值:%.2f,滤芯寿命:%d%%,漏水状态:%s", sensorData.getDeviceId(), sensorData.getTdsValue1(), - sensorData.getTdsValue2(), - sensorData.getTdsValue3(), sensorData.getFilterLife(), sensorData.getLeakage() ? "是" : "否" )); @@ -153,6 +156,9 @@ public class MqttSensorReceiver { waterSupplyRepo.save(entity); log.info("供水机状态数据持久化成功 | 设备ID:{}", sensorData.getDeviceId()); + + // 新增:调用告警检查逻辑 + alertTriggerService.checkWaterSupplyAbnormal(sensorData); } /** -- 2.34.1