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