Explorar o código

feate:增加短信预警

jackzhou hai 1 semana
pai
achega
7c5e29b6d4

+ 5 - 0
snowy-plugin/snowy-plugin-coldchain/pom.xml

@@ -113,5 +113,10 @@
             <artifactId>weixin-java-miniapp</artifactId>
             <version>4.6.0</version>
         </dependency>
+        <dependency>
+            <groupId>com.github.jfcloud</groupId>
+            <artifactId>jfcloud-boot-starter-sms-aliyun</artifactId>
+            <version>K7.5.4</version>
+        </dependency>
     </dependencies>
 </project>

+ 10 - 6
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/offline/DeviceOfflineDetectionService.java

@@ -6,22 +6,20 @@ import org.jetbrains.annotations.NotNull;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
+import vip.xiaonuo.coldchain.core.alarm.bean.NotificationChannel;
 import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarm;
 import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarmUser;
 import vip.xiaonuo.coldchain.core.alarm.config.ColdChainAlarmMessageProperties;
 import vip.xiaonuo.coldchain.core.alarm.enums.SensorAlarmType;
-import vip.xiaonuo.coldchain.core.alarm.service.messagepush.LocalSensorAlarmMessagePushService;
 import vip.xiaonuo.coldchain.core.alarm.service.messagepush.RedisSensorAlarmMessagePushService;
 import vip.xiaonuo.coldchain.core.event.SensorAlarmEvent;
 import vip.xiaonuo.coldchain.core.renke.config.JfcloudColdChainServerProperties;
-import vip.xiaonuo.coldchain.modular.monitordevice.entity.MonitorDevice;
 import vip.xiaonuo.coldchain.modular.monitordevice.service.MonitorDeviceService;
 import vip.xiaonuo.coldchain.modular.monitortarget.enums.MonitorStatusEnum;
 import vip.xiaonuo.coldchain.modular.monitortarget.service.MonitorTargetService;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.entity.MonitorTargetRegion;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.service.MonitorTargetRegionService;
 import vip.xiaonuo.common.cache.CommonCacheOperator;
-import vip.xiaonuo.common.exception.CommonException;
 
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
@@ -138,8 +136,8 @@ public class DeviceOfflineDetectionService {
                 log.debug("设备 {}-{} 在线,最后上报时间:{}。", deviceCode, route, lastReportTimeStr);
                 MonitorTargetRegion monitorTargetRegion = monitorTargetRegionService.findOneByDeviceCodeAndSensorNo(deviceCode, route);
                 if (Objects.nonNull(monitorTargetRegion)) {
-                    if (redisSensorAlarmMessagePushService.hasExceededPushLimitLike("*", monitorTargetRegion.getId(), route,"设备离线")) {
-                        publishAlarm(deviceCode, route, lastReportTimeStr, "设备上线",SensorAlarmType.SENSOR_ON_LINE.getDeviceCode());
+                    if (redisSensorAlarmMessagePushService.hasExceededPushLimitLike("*", monitorTargetRegion.getId(), route, "设备离线")) {
+                        publishAlarm(deviceCode, route, lastReportTimeStr, "设备上线", SensorAlarmType.SENSOR_ON_LINE.getDeviceCode());
                     }
                 } else {
                     log.error("未找到设备编号 [{}] 和传感器编号 [{}] 对应的监控目标区域", deviceCode, route);
@@ -162,7 +160,7 @@ public class DeviceOfflineDetectionService {
          * jackzhou add 设备下线预警标识
          */
         if (Objects.isNull(monitorTargetRegion.getDeviceOfflineAlarm()) || !monitorTargetRegion.getDeviceOfflineAlarm()) {
-            log.error("***********************设备未启动设备下线预警设置");
+            log.warn("设备未启动设备下线预警设置");
             return;
         }
         String monitorTargetId = monitorTargetRegion.getMonitorTargetId();
@@ -197,6 +195,12 @@ public class DeviceOfflineDetectionService {
         sensorAlarm.setRoomName(monitorTargetRegion.getRoomName());
         sensorAlarm.setType(type);
         log.warn("设备断电报警: 类型: {},详细报警内容 : {}", alarmType, alarmMessage);
+        // jackzhou 2025.10.21  添加短信预警通知类型
+        if (Boolean.TRUE.equals(monitorTargetRegion.getSmgOfflineAlarm())) {
+            List<NotificationChannel> channels = new ArrayList<>(sensorAlarm.getNotificationChannel());
+            channels.add(NotificationChannel.SMS);
+            sensorAlarm.setNotificationChannel(channels);
+        }
         // 发布报警事件
         applicationEventPublisher.publishEvent(new SensorAlarmEvent(this, sensorAlarm));
     }

+ 8 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/check/DefaultSensorAlarmChecker.java

@@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.stereotype.Component;
+import vip.xiaonuo.coldchain.core.alarm.bean.NotificationChannel;
 import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarm;
 import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarmUser;
 import vip.xiaonuo.coldchain.core.alarm.bean.SensorThreshold;
@@ -18,6 +19,7 @@ import vip.xiaonuo.coldchain.core.event.SensorAlarmEvent;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.entity.MonitorTargetRegion;
 
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
@@ -146,6 +148,12 @@ public class DefaultSensorAlarmChecker implements SensorAlarmChecker {
         // 添加房间字段
         sensorAlarm.setRoomName(monitorTargetRegion.getRoomName());
         log.warn("触发报警: 类型: {},详细报警内容 : {}", alarmType, alarmMessage);
+        // jackzhou 2025.10.21  添加短信预警通知类型
+        if (Boolean.TRUE.equals(monitorTargetRegion.getSmgOfflineAlarm())) {
+            List<NotificationChannel> channels = new ArrayList<>(sensorAlarm.getNotificationChannel());
+            channels.add(NotificationChannel.SMS);
+            sensorAlarm.setNotificationChannel(channels);
+        }
         // 发布报警事件
         applicationEventPublisher.publishEvent(new SensorAlarmEvent(this, sensorAlarm));
     }

+ 55 - 4
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/impl/SMSPushService.java

@@ -1,9 +1,17 @@
 package vip.xiaonuo.coldchain.core.alarm.service.messagepush.impl;
 
+import com.github.jfcloud.sms.aliyun.service.JfcloudAliyunSmsService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarm;
+import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarmUser;
 import vip.xiaonuo.coldchain.core.alarm.service.messagepush.MessagePushService;
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 /**
  * @author jackzhou
  * @version 1.0
@@ -12,16 +20,59 @@ import vip.xiaonuo.coldchain.core.alarm.service.messagepush.MessagePushService;
  * @date 2024/11/26
  */
 @Service("SMS")
+@Slf4j
+@RequiredArgsConstructor
 public class SMSPushService implements MessagePushService {
+    private final JfcloudAliyunSmsService jfcloudAliyunSmsService;
 
     @Override
-    public boolean sendAlarmMessage(SensorAlarm alarm/*, SensorAlarmUser user*/) {
-        // 这里是模拟发送短信的实现,具体可以集成第三方短信服务
+    public boolean sendAlarmMessage(SensorAlarm alarm) {
+        if (alarm == null) {
+            log.warn("⚠️ 传入的告警对象为空,取消发送短信");
+            return false;
+        }
         String message = buildMessage(alarm);
-        System.out.println("发送短信到:  内容: " + message);
-        return false;
+        log.info("准备发送短信告警: {}", message);
+        // 构造短信参数
+        Map<String, String> params = new HashMap<>();
+        params.put("name", alarm.getDeviceName() != null ? alarm.getDeviceName() : "未知设备");
+        params.put("type", alarm.getAlarmType() != null ? alarm.getAlarmType() : "未知类型");
+        params.put("value", String.valueOf(alarm.getValue()));
+        params.put("settingValue", String.valueOf(alarm.getThreshold()));
+
+        // 获取告警用户列表
+        List<SensorAlarmUser> alarmUsers = alarm.getAlarmUsers();
+        if (alarmUsers == null || alarmUsers.isEmpty()) {
+            log.warn("⚠️ 未找到告警接收用户,取消发送短信");
+            return false;
+        }
+
+        boolean success = true;
+        for (SensorAlarmUser alarmUser : alarmUsers) {
+            try {
+                String phone = alarmUser.getPhone();
+//                String phone="13627258973";
+                if (phone == null || phone.isEmpty()) {
+
+                    log.warn("跳过用户 [{}]:手机号为空", alarmUser.getUserName());
+                    continue;
+                }
+
+                boolean result = jfcloudAliyunSmsService.sendSms(phone, "预警通知", params);
+                log.info("短信发送至 [{}] - 结果: {}", phone, result ? "成功" : "失败");
+                if (!result) {
+                    success = false;
+                }
+            } catch (Exception e) {
+                log.error("短信发送失败 -> 用户: {}, 错误: {}", alarmUser.getUserName(), e.getMessage());
+                success = false;
+            }
+        }
+
+        return success;
     }
 
+
     @Override
     public boolean sendAlarmRecoverMessage(SensorAlarm alarm, Long firstAlarmTime) {
         return false;

+ 6 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/entity/MonitorTargetRegion.java

@@ -195,6 +195,12 @@ public class MonitorTargetRegion extends OrgEntity {
     @Schema(description = "勿扰结束时间")
     private String ignoreEndTime;
 
+    /**
+     * 短信预警,默认关闭
+     */
+    @Schema(description = "短信预警")
+    private Boolean smgOfflineAlarm = false;
+
     /**
      * 房间名
      */

+ 6 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/param/MonitorTargetRegionAddParam.java

@@ -166,4 +166,10 @@ public class MonitorTargetRegionAddParam {
      */
     @Schema(description = "设备下线预警")
     private Boolean deviceOfflineAlarm;
+
+    /**
+     * 短信预警,默认关闭
+     */
+    @Schema(description = "短信预警")
+    private Boolean smgOfflineAlarm = false;
 }

+ 6 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/param/MonitorTargetRegionEditParam.java

@@ -169,4 +169,10 @@ public class MonitorTargetRegionEditParam {
      */
     @Schema(description = "设备下线预警")
     private Boolean deviceOfflineAlarm;
+
+    /**
+     * 短信预警,默认关闭
+     */
+    @Schema(description = "短信预警")
+    private Boolean smgOfflineAlarm = false;
 }

+ 0 - 2
snowy-web-app/src/main/java/vip/xiaonuo/Application.java

@@ -31,8 +31,6 @@ import vip.xiaonuo.core.config.JfcloudApplication;
  */
 @Slf4j
 @JfcloudApplication
-@EnableScheduling
-@EnableAsync
 public class Application {
     /* 解决druid 日志报错:discard long time none received connection:xxx */
     static {

+ 1 - 1
snowy-web-app/src/main/resources/application-master.properties

@@ -1,2 +1,2 @@
 server.port=58888
-coldchain.port=0
+coldchain.port=40404

+ 1 - 0
snowy-web-app/src/main/resources/application.properties

@@ -2,6 +2,7 @@
 # spring allow-circular-references
 #########################################
 spring.main.allow-circular-references=true
+spring.main.allow-bean-definition-overriding=true
 
 #########################################
 # spring profiles configuration

+ 103 - 0
snowy-web-app/src/main/resources/sms_aliyun_templates.xml

@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<templates>
+  <template>
+    <key>SMS_495940909</key>
+    <id>SMS_495940909</id>
+    <name>预警通知</name>
+    <content>您的设备 ${name} 触发 ${type} 报警,当前值 ${value},阈值 ${settingValue},请立即处理!</content>
+    <params>
+      <param>name</param>
+      <param>type</param>
+      <param>value</param>
+      <param>settingValue</param>
+    </params>
+  </template>
+  <template>
+    <key>SMS_495970827</key>
+    <id>SMS_495970827</id>
+    <name>测试模板</name>
+    <content>验证码:${code},有效时间5分钟,您正在测试短信服务功能。如非本人操作,请忽略该短信</content>
+    <params>
+      <param>code</param>
+    </params>
+  </template>
+  <template>
+    <key>SMS_495900866</key>
+    <id>SMS_495900866</id>
+    <name>设备上线</name>
+    <content>您的设备:${name}, 已恢复在线状态。上线时间:${time}</content>
+    <params>
+      <param>name</param>
+      <param>time</param>
+    </params>
+  </template>
+  <template>
+    <key>SMS_495945875</key>
+    <id>SMS_495945875</id>
+    <name>设备离线</name>
+    <content>您的设备:${name} ,处于离线或者网络不稳定状态。离线时间:${time},请及时处理!</content>
+    <params>
+      <param>name</param>
+      <param>time</param>
+    </params>
+  </template>
+  <template>
+    <key>SMS_496005635</key>
+    <id>SMS_496005635</id>
+    <name>预约申请</name>
+    <content>您的预约申请提交成功,申请编号为${code},请等待管理员审核。</content>
+    <params>
+      <param>code</param>
+    </params>
+  </template>
+  <template>
+    <key>SMS_495900673</key>
+    <id>SMS_495900673</id>
+    <name>修改密码</name>
+    <content>尊敬的${name}用户,您的密码已经重置为${password},请及时登录并修改密码。</content>
+    <params>
+      <param>name</param>
+      <param>password</param>
+    </params>
+  </template>
+  <template>
+    <key>SMS_495985644</key>
+    <id>SMS_495985644</id>
+    <name>动态码</name>
+    <content>您的动态码为:${code},5分钟内有效,请勿泄露!</content>
+    <params>
+      <param>code</param>
+    </params>
+  </template>
+  <template>
+    <key>SMS_495970567</key>
+    <id>SMS_495970567</id>
+    <name>找回密码</name>
+    <content>您正在进行找回密码,验证码为:${code},5分钟内有效,请勿泄露于他人!</content>
+    <params>
+      <param>code</param>
+    </params>
+  </template>
+  <template>
+    <key>SMS_496080531</key>
+    <id>SMS_496080531</id>
+    <name>验证码</name>
+    <content>您的验证码:${code},您正在进行身份验证,请勿泄露于他人!</content>
+    <params>
+      <param>code</param>
+    </params>
+  </template>
+  <template>
+    <key>SMS_464390810</key>
+    <id>SMS_464390810</id>
+    <name>TemperatureHumidityWarni</name>
+    <content>您的设备${c} 中的传感器${s},在${t}时 ${type}为${content}出现异常</content>
+    <params>
+      <param>c</param>
+      <param>s</param>
+      <param>t</param>
+      <param>type</param>
+      <param>content</param>
+    </params>
+  </template>
+</templates>