|
|
@@ -0,0 +1,277 @@
|
|
|
+package com.haha.service.impl;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.haha.common.constant.RedisConstants;
|
|
|
+import com.haha.common.enums.DeviceAlertType;
|
|
|
+import com.haha.entity.Device;
|
|
|
+import com.haha.entity.DeviceAlertRecord;
|
|
|
+import com.haha.entity.Shop;
|
|
|
+import com.haha.mapper.DeviceAlertRecordMapper;
|
|
|
+import com.haha.service.DeviceAlertService;
|
|
|
+import com.haha.service.DeviceService;
|
|
|
+import com.haha.service.ShopService;
|
|
|
+import com.haha.service.config.DeviceAlertProperties;
|
|
|
+import com.haha.service.notify.WeChatWorkNotifyService;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.data.redis.core.StringRedisTemplate;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 设备告警服务实现
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class DeviceAlertServiceImpl extends ServiceImpl<DeviceAlertRecordMapper, DeviceAlertRecord>
|
|
|
+ implements DeviceAlertService {
|
|
|
+
|
|
|
+ private static final String NOTIFY_CHANNEL = "WECHAT_WORK";
|
|
|
+ private static final int NOTIFY_SUCCESS = 1;
|
|
|
+ private static final int NOTIFY_FAIL = 2;
|
|
|
+ private static final int ALERT_LEVEL_NORMAL = 1;
|
|
|
+ private static final int ALERT_LEVEL_URGENT = 2;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private DeviceAlertRecordMapper deviceAlertRecordMapper;
|
|
|
+ @Autowired
|
|
|
+ private DeviceService deviceService;
|
|
|
+ @Autowired
|
|
|
+ private ShopService shopService;
|
|
|
+ @Autowired
|
|
|
+ private StringRedisTemplate stringRedisTemplate;
|
|
|
+ @Autowired
|
|
|
+ private WeChatWorkNotifyService weChatWorkNotifyService;
|
|
|
+ @Autowired
|
|
|
+ private DeviceAlertProperties properties;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void processOfflineAlert(String deviceId, String triggerSource) {
|
|
|
+ try {
|
|
|
+ if (!properties.isEnabled()) {
|
|
|
+ log.debug("[设备告警] 告警功能已禁用,跳过离线告警处理 - deviceId: {}", deviceId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isInCooldown(deviceId, DeviceAlertType.OFFLINE)) {
|
|
|
+ log.debug("[设备告警] 设备离线告警处于冷却期,跳过 - deviceId: {}", deviceId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询设备信息
|
|
|
+ Device device = deviceService.getDeviceBySn(deviceId);
|
|
|
+ if (device == null) {
|
|
|
+ log.warn("[设备告警] 设备不存在,跳过离线告警 - deviceId: {}", deviceId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ String deviceName = device.getName() != null ? device.getName() : deviceId;
|
|
|
+ String shopName = getShopName(device.getShopId());
|
|
|
+ LocalDateTime alertTime = LocalDateTime.now();
|
|
|
+
|
|
|
+ // 构建告警记录
|
|
|
+ DeviceAlertRecord record = new DeviceAlertRecord();
|
|
|
+ record.setDeviceId(deviceId);
|
|
|
+ record.setShopId(device.getShopId());
|
|
|
+ record.setDeviceName(deviceName);
|
|
|
+ record.setAlertType(DeviceAlertType.OFFLINE.getCode());
|
|
|
+ record.setAlertLevel(ALERT_LEVEL_URGENT);
|
|
|
+ record.setAlertContent(String.format("设备[%s](%s)离线,所属门店:%s,触发来源:%s",
|
|
|
+ deviceName, deviceId, shopName, triggerSource));
|
|
|
+ record.setTriggerSource(triggerSource);
|
|
|
+ record.setAlertTime(alertTime);
|
|
|
+ record.setNotifyChannel(NOTIFY_CHANNEL);
|
|
|
+
|
|
|
+ // 发送通知
|
|
|
+ int notifyStatus = NOTIFY_FAIL;
|
|
|
+ try {
|
|
|
+ weChatWorkNotifyService.sendOfflineAlert(deviceId, deviceName, shopName, triggerSource, alertTime);
|
|
|
+ notifyStatus = NOTIFY_SUCCESS;
|
|
|
+ log.info("[设备告警] 离线告警通知发送成功 - deviceId: {}", deviceId);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[设备告警] 离线告警通知发送失败 - deviceId: {}", deviceId, e);
|
|
|
+ }
|
|
|
+
|
|
|
+ record.setNotifyStatus(notifyStatus);
|
|
|
+ this.save(record);
|
|
|
+
|
|
|
+ // 设置冷却期
|
|
|
+ setCooldown(deviceId, DeviceAlertType.OFFLINE);
|
|
|
+
|
|
|
+ // 在Redis中标记设备为离线状态(用于恢复上线判断)
|
|
|
+ String offlineKey = String.format(RedisConstants.DEVICE_LAST_ONLINE_KEY, deviceId);
|
|
|
+ stringRedisTemplate.opsForValue().set(offlineKey, alertTime.toString());
|
|
|
+
|
|
|
+ log.info("[设备告警] 离线告警处理完成 - deviceId: {}, notifyStatus: {}", deviceId, notifyStatus);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[设备告警] 处理离线告警异常 - deviceId: {}", deviceId, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void processWeakSignalAlert(String deviceId, Integer signalValue, String triggerSource) {
|
|
|
+ try {
|
|
|
+ if (!properties.isEnabled()) {
|
|
|
+ log.debug("[设备告警] 告警功能已禁用,跳过信号弱告警处理 - deviceId: {}", deviceId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (signalValue == null || signalValue > properties.getWeakSignalThreshold()) {
|
|
|
+ log.debug("[设备告警] 信号值正常,跳过 - deviceId: {}, signalValue: {}, threshold: {}",
|
|
|
+ deviceId, signalValue, properties.getWeakSignalThreshold());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isInCooldown(deviceId, DeviceAlertType.WEAK_SIGNAL)) {
|
|
|
+ log.debug("[设备告警] 设备信号弱告警处于冷却期,跳过 - deviceId: {}", deviceId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询设备信息
|
|
|
+ Device device = deviceService.getDeviceBySn(deviceId);
|
|
|
+ if (device == null) {
|
|
|
+ log.warn("[设备告警] 设备不存在,跳过信号弱告警 - deviceId: {}", deviceId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ String deviceName = device.getName() != null ? device.getName() : deviceId;
|
|
|
+ String shopName = getShopName(device.getShopId());
|
|
|
+ LocalDateTime alertTime = LocalDateTime.now();
|
|
|
+
|
|
|
+ // 构建告警记录
|
|
|
+ DeviceAlertRecord record = new DeviceAlertRecord();
|
|
|
+ record.setDeviceId(deviceId);
|
|
|
+ record.setShopId(device.getShopId());
|
|
|
+ record.setDeviceName(deviceName);
|
|
|
+ record.setAlertType(DeviceAlertType.WEAK_SIGNAL.getCode());
|
|
|
+ record.setAlertLevel(ALERT_LEVEL_NORMAL);
|
|
|
+ record.setAlertContent(String.format("设备[%s](%s)信号弱,信号值:%d,所属门店:%s",
|
|
|
+ deviceName, deviceId, signalValue, shopName));
|
|
|
+ record.setSignalValue(signalValue);
|
|
|
+ record.setTriggerSource(triggerSource);
|
|
|
+ record.setAlertTime(alertTime);
|
|
|
+ record.setNotifyChannel(NOTIFY_CHANNEL);
|
|
|
+
|
|
|
+ // 发送通知
|
|
|
+ int notifyStatus = NOTIFY_FAIL;
|
|
|
+ try {
|
|
|
+ weChatWorkNotifyService.sendWeakSignalAlert(deviceId, deviceName, shopName, signalValue, alertTime);
|
|
|
+ notifyStatus = NOTIFY_SUCCESS;
|
|
|
+ log.info("[设备告警] 信号弱告警通知发送成功 - deviceId: {}, signalValue: {}", deviceId, signalValue);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[设备告警] 信号弱告警通知发送失败 - deviceId: {}", deviceId, e);
|
|
|
+ }
|
|
|
+
|
|
|
+ record.setNotifyStatus(notifyStatus);
|
|
|
+ this.save(record);
|
|
|
+
|
|
|
+ // 设置冷却期
|
|
|
+ setCooldown(deviceId, DeviceAlertType.WEAK_SIGNAL);
|
|
|
+
|
|
|
+ log.info("[设备告警] 信号弱告警处理完成 - deviceId: {}, signalValue: {}, notifyStatus: {}",
|
|
|
+ deviceId, signalValue, notifyStatus);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[设备告警] 处理信号弱告警异常 - deviceId: {}", deviceId, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void processBackOnlineNotify(String deviceId) {
|
|
|
+ try {
|
|
|
+ if (!properties.isEnabled()) {
|
|
|
+ log.debug("[设备告警] 告警功能已禁用,跳过恢复上线通知 - deviceId: {}", deviceId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查Redis中该设备是否有离线标记
|
|
|
+ String offlineKey = String.format(RedisConstants.DEVICE_LAST_ONLINE_KEY, deviceId);
|
|
|
+ Boolean hasOfflineMark = stringRedisTemplate.hasKey(offlineKey);
|
|
|
+ if (!Boolean.TRUE.equals(hasOfflineMark)) {
|
|
|
+ log.debug("[设备告警] 设备无离线标记,跳过恢复上线通知 - deviceId: {}", deviceId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询设备信息
|
|
|
+ Device device = deviceService.getDeviceBySn(deviceId);
|
|
|
+ if (device == null) {
|
|
|
+ log.warn("[设备告警] 设备不存在,跳过恢复上线通知 - deviceId: {}", deviceId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ String deviceName = device.getName() != null ? device.getName() : deviceId;
|
|
|
+ String shopName = getShopName(device.getShopId());
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+
|
|
|
+ // 构建告警记录
|
|
|
+ DeviceAlertRecord record = new DeviceAlertRecord();
|
|
|
+ record.setDeviceId(deviceId);
|
|
|
+ record.setShopId(device.getShopId());
|
|
|
+ record.setDeviceName(deviceName);
|
|
|
+ record.setAlertType(DeviceAlertType.BACK_ONLINE.getCode());
|
|
|
+ record.setAlertLevel(ALERT_LEVEL_NORMAL);
|
|
|
+ record.setAlertContent(String.format("设备[%s](%s)已恢复上线,所属门店:%s", deviceName, deviceId, shopName));
|
|
|
+ record.setTriggerSource("SYSTEM");
|
|
|
+ record.setAlertTime(now);
|
|
|
+ record.setNotifyChannel(NOTIFY_CHANNEL);
|
|
|
+
|
|
|
+ // 发送通知
|
|
|
+ int notifyStatus = NOTIFY_FAIL;
|
|
|
+ try {
|
|
|
+ weChatWorkNotifyService.sendBackOnlineNotify(deviceId, deviceName, shopName, now);
|
|
|
+ notifyStatus = NOTIFY_SUCCESS;
|
|
|
+ log.info("[设备告警] 恢复上线通知发送成功 - deviceId: {}", deviceId);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[设备告警] 恢复上线通知发送失败 - deviceId: {}", deviceId, e);
|
|
|
+ }
|
|
|
+
|
|
|
+ record.setNotifyStatus(notifyStatus);
|
|
|
+ this.save(record);
|
|
|
+
|
|
|
+ // 删除Redis中的离线标记
|
|
|
+ stringRedisTemplate.delete(offlineKey);
|
|
|
+
|
|
|
+ // 删除该设备的离线告警冷却key(设备已恢复,下次再离线应立即告警)
|
|
|
+ String cooldownKey = String.format(RedisConstants.DEVICE_ALERT_COOLDOWN_KEY,
|
|
|
+ deviceId, DeviceAlertType.OFFLINE.getCode());
|
|
|
+ stringRedisTemplate.delete(cooldownKey);
|
|
|
+
|
|
|
+ log.info("[设备告警] 恢复上线通知处理完成 - deviceId: {}, notifyStatus: {}", deviceId, notifyStatus);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[设备告警] 处理恢复上线通知异常 - deviceId: {}", deviceId, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断设备告警是否在冷却期内
|
|
|
+ */
|
|
|
+ private boolean isInCooldown(String deviceId, DeviceAlertType alertType) {
|
|
|
+ String key = String.format(RedisConstants.DEVICE_ALERT_COOLDOWN_KEY, deviceId, alertType.getCode());
|
|
|
+ return Boolean.TRUE.equals(stringRedisTemplate.hasKey(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置设备告警冷却期
|
|
|
+ */
|
|
|
+ private void setCooldown(String deviceId, DeviceAlertType alertType) {
|
|
|
+ String key = String.format(RedisConstants.DEVICE_ALERT_COOLDOWN_KEY, deviceId, alertType.getCode());
|
|
|
+ stringRedisTemplate.opsForValue().set(key, "1", properties.getCooldownMinutes(), TimeUnit.MINUTES);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取门店名称
|
|
|
+ */
|
|
|
+ private String getShopName(Long shopId) {
|
|
|
+ if (shopId == null) {
|
|
|
+ return "未关联门店";
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ Shop shop = shopService.getById(shopId);
|
|
|
+ return shop != null ? shop.getName() : "未知门店";
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("[设备告警] 查询门店信息失败 - shopId: {}", shopId, e);
|
|
|
+ return "未知门店";
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|