Explorar o código

feate: 消息推送

jackzhou hai 6 meses
pai
achega
6668f19710
Modificáronse 29 ficheiros con 719 adicións e 35 borrados
  1. 82 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/bean/NotificationChannel.java
  2. 136 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/bean/SensorAlarm.java
  3. 54 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/bean/SensorAlarmUser.java
  4. 1 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/bean/SensorThreshold.java
  5. 1 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/config/ColdChainAlarmMessageProperties.java
  6. 14 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/mapper/SensorAlarmMapper.java
  7. 10 7
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/check/DefaultSensorAlarmChecker.java
  8. 1 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/check/SensorAlarmChecker.java
  9. 31 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/MessagePushService.java
  10. 60 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/SensorAlarmMessagePushService.java
  11. 23 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/impl/DingTalkMessagePushService.java
  12. 29 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/impl/EmailPushService.java
  13. 29 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/impl/PushNotificationService.java
  14. 29 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/impl/SMSPushService.java
  15. 35 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/impl/WechatMessagePushService.java
  16. 21 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/impl/WechatMiniMessagePushService.java
  17. 2 2
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/threshold/SensorThresholdService.java
  18. 4 3
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/threshold/ThresholdService.java
  19. 6 5
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/SensorAlarmEvent.java
  20. 10 2
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/SensorAlarmEventListener.java
  21. 1 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/SensorDataEventListener2.java
  22. 22 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/handler/NotificationChannelListTypeHandler.java
  23. 21 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/handler/SensorAlarmUserTypeHandler.java
  24. 71 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/handler/abs/GenericTypeHandler.java
  25. 17 3
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/handler/abs/ListTypeHandler.java
  26. 0 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/push/param/PushParam.java
  27. 0 4
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/push/utils/PushUtil.java
  28. 8 3
      snowy-web-app/src/main/java/vip/xiaonuo/Application.java
  29. 1 1
      snowy-web-app/src/main/resources/application.properties

+ 82 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/bean/NotificationChannel.java

@@ -0,0 +1,82 @@
+package vip.xiaonuo.coldchain.core.alarm.bean;
+
+import lombok.Getter;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description 告警通知渠道的枚举类型,定义了用户可以选择的通知方式
+ * @date 2024/11/26 11:30:00
+ */
+@Getter
+public enum NotificationChannel {
+
+    /**
+     * 短信通知
+     */
+    SMS("短信"),
+
+    /**
+     * 邮件通知
+     */
+    EMAIL("电子邮件"),
+
+    /**
+     * 推送通知(例如APP通知)
+     */
+    PUSH_NOTIFICATION("推送通知"),
+
+    /**
+     * 微信通知
+     */
+    WECHAT("微信公众号"),
+    /**
+     * 微信通知
+     */
+    WECHAT_MINI("微信小程序"),
+
+    /**
+     * 钉钉通知
+     */
+    DINGTALK("钉钉"),
+
+    /**
+     * 系统内部通知
+     */
+    SYSTEM("系统通知");
+
+    /**
+     * -- GETTER --
+     * 获取通知渠道的中文名称
+     *
+     * @return 通知渠道的中文名称
+     */
+    // 通道名称
+    private final String channelName;
+
+    /**
+     * 构造函数
+     *
+     * @param channelName 通道的中文名称
+     */
+    NotificationChannel(String channelName) {
+        this.channelName = channelName;
+    }
+
+    /**
+     * 根据通道名称返回对应的枚举类型
+     *
+     * @param channelName 通道名称
+     * @return 对应的枚举类型
+     */
+    public static NotificationChannel fromString(String channelName) {
+        for (NotificationChannel channel : NotificationChannel.values()) {
+            if (channel.channelName.equals(channelName)) {
+                return channel;
+            }
+        }
+        // 默认返回系统通知
+        return SYSTEM;
+    }
+}

+ 136 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/bean/SensorAlarm.java

@@ -0,0 +1,136 @@
+package vip.xiaonuo.coldchain.core.alarm.bean;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.google.common.collect.Lists;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import vip.xiaonuo.coldchain.core.handler.NotificationChannelListTypeHandler;
+import vip.xiaonuo.coldchain.core.handler.SensorAlarmUserTypeHandler;
+import vip.xiaonuo.common.pojo.CommonEntity;
+
+import java.util.List;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description 告警信息类,包含告警内容、告警值、告警接收人等信息。
+ * @date 2024/11/26
+ */
+@Data
+@TableName("sensor_alarm")
+public class SensorAlarm extends CommonEntity {
+    @TableId(type = IdType.ASSIGN_UUID)
+    private String id;
+    /**
+     * 告警内容,通常为告警的详细描述,例如温度、湿度等超标的具体信息
+     */
+    @Schema(description = "告警内容,通常为告警的详细描述,例如温度、湿度等超标的具体信息")
+    private String message;
+
+    /**
+     * 告警值,表示触发告警的实际数值,例如超标的温度值
+     */
+    @Schema(description = "告警值,表示触发告警的实际数值,例如超标的温度值")
+    private float value;
+
+    /**
+     * 告警接收人,存储告警通知的接收用户信息
+     */
+    /**
+     * 告警接收人,存储告警通知的接收用户信息
+     */
+    @TableField(value = "alarm_users", typeHandler = SensorAlarmUserTypeHandler.class)
+    @Schema(description = "告警接收人,存储告警通知的接收用户信息")
+    private List<SensorAlarmUser> alarmUsers = Lists.newArrayList();
+
+    /**
+     * 告警类型,例如温度过高、湿度过低等
+     */
+    @Schema(description = "告警类型,例如温度过高、湿度过低等")
+    private String alarmType;
+
+    /**
+     * 告警时间,记录触发告警的时间
+     */
+    @Schema(description = "告警时间,记录触发告警的时间")
+    private String alarmTime;
+
+    /**
+     * 告警优先级,用于区分告警的紧急程度,例如高、低、紧急等
+     */
+    @Schema(description = "告警优先级,用于区分告警的紧急程度,例如高、低、紧急等")
+    private String priority;
+
+    /**
+     * 告警来源,记录告警是来自于哪个设备、传感器或系统
+     */
+    @Schema(description = "告警来源,记录告警是来自于哪个设备、传感器或系统")
+    private String source;
+
+    /**
+     * 设备ID,指示告警是由哪个设备触发的
+     */
+    @Schema(description = "设备ID,指示告警是由哪个设备触发的")
+    private String deviceId;
+
+    /**
+     * 设备名称,给出触发告警的设备的名称(可选)
+     */
+    @Schema(description = "设备名称,给出触发告警的设备的名称(可选)")
+    private String deviceName;
+
+    /**
+     * 告警处理状态,用于表示告警是否已经被处理,未处理、处理中、已处理等
+     */
+    @Schema(description = "告警处理状态,用于表示告警是否已经被处理,未处理、处理中、已处理等")
+    private String status;
+
+    /**
+     * 设备所在位置,指明告警设备的物理位置,便于定位
+     */
+    @Schema(description = "设备所在位置,指明告警设备的物理位置,便于定位")
+    private String location;
+
+    /**
+     * 是否已通知告警接收人,标识告警是否已成功发送通知
+     */
+    @Schema(description = "是否已通知告警接收人,标识告警是否已成功发送通知")
+    @TableField("is_notified")
+    private boolean isNotified;
+
+    /**
+     * 用户的通知渠道设置,支持选择接收告警的多个渠道,如短信、邮件、APP通知等
+     */
+    @Schema(description = "用户的通知渠道设置,支持选择接收告警的多个渠道,如短信、邮件、APP通知等")
+    @TableField(value = "notification_channel", typeHandler = NotificationChannelListTypeHandler.class)
+    private List<NotificationChannel> notificationChannel = List.of(NotificationChannel.WECHAT);
+
+    /**
+     * 用户的通知时间段,限制用户接收告警的时间范围(如仅在工作时间接收告警)
+     */
+    @Schema(description = "用户的通知时间段,限制用户接收告警的时间范围(如仅在工作时间接收告警)")
+    private String notificationTimeFrame;
+    /**
+     * 其他附加信息,例如报警的原因、建议措施等
+     */
+    @Schema(description = "其他附加信息,例如报警的原因、建议措施等")
+    private String additionalInfo;
+
+    /**
+     * 告警触发阈值,记录触发告警的具体阈值
+     */
+    @Schema(description = "告警触发阈值,记录触发告警的具体阈值")
+    private float threshold;
+
+
+    @Schema(description = "微信请求状态码")
+    private String weixinRequestCode;
+
+
+    public SensorAlarm() {
+    }
+}

+ 54 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/bean/SensorAlarmUser.java

@@ -0,0 +1,54 @@
+package vip.xiaonuo.coldchain.core.alarm.bean;
+
+import lombok.Data;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description 告警接收人信息,包含用户的联系方式,支持多渠道接收报警信息
+ * @date 2024/11/26 11:05:33
+ */
+@Data
+public class SensorAlarmUser {
+    /**
+     * 系统用户ID,唯一标识一个用户
+     */
+    private String userId;
+
+    /**
+     * 用户的OpenID,通常用于与外部系统(如微信、钉钉等)进行集成,便于推送消息
+     */
+    private String openId;
+
+    /**
+     * 用户的手机号,作为接收告警的联系方式之一
+     */
+    private String phone;
+
+    /**
+     * 用户的电子邮件地址,用于发送告警邮件
+     */
+    private String email;
+
+    /**
+     * 用户的昵称或姓名,用于告警信息中显示用户的身份
+     */
+    private String userName;
+
+    /**
+     * 用户所属部门,便于将告警信息按部门划分,支持定向告警
+     */
+    private String department;
+
+    /**
+     * 用户的接收告警的偏好设置,例如是否接受短信、邮件、推送等
+     */
+    private String alarmPreferences;
+
+    /**
+     * 用户的接收告警的优先级设置,控制不同类型告警的发送优先级(如高优先级告警、低优先级告警等)
+     */
+    private String alarmPriority;
+
+}

+ 1 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/alarm/bean/SensorThreshold.java → snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/bean/SensorThreshold.java

@@ -1,4 +1,4 @@
-package vip.xiaonuo.coldchain.core.event.alarm.bean;
+package vip.xiaonuo.coldchain.core.alarm.bean;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;

+ 1 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/alarm/config/ColdChainAlarmMessageProperties.java → snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/config/ColdChainAlarmMessageProperties.java

@@ -1,4 +1,4 @@
-package vip.xiaonuo.coldchain.core.event.alarm.config;
+package vip.xiaonuo.coldchain.core.alarm.config;
 
 import lombok.Data;
 import org.springframework.boot.context.properties.ConfigurationProperties;

+ 14 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/mapper/SensorAlarmMapper.java

@@ -0,0 +1,14 @@
+package vip.xiaonuo.coldchain.core.alarm.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarm;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description SensorAlarm Mapper接口
+ * @date 2024/11/26
+ */
+public interface SensorAlarmMapper extends BaseMapper<SensorAlarm> {
+}

+ 10 - 7
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/alarm/service/DefaultSensorAlarmChecker.java → snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/check/DefaultSensorAlarmChecker.java

@@ -1,13 +1,15 @@
-package vip.xiaonuo.coldchain.core.event.alarm.service;
+package vip.xiaonuo.coldchain.core.alarm.service.check;
 
 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.SensorAlarm;
+import vip.xiaonuo.coldchain.core.alarm.bean.SensorThreshold;
+import vip.xiaonuo.coldchain.core.alarm.config.ColdChainAlarmMessageProperties;
+import vip.xiaonuo.coldchain.core.alarm.service.threshold.SensorThresholdService;
 import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
 import vip.xiaonuo.coldchain.core.event.SensorAlarmEvent;
-import vip.xiaonuo.coldchain.core.event.alarm.bean.SensorThreshold;
-import vip.xiaonuo.coldchain.core.event.alarm.config.ColdChainAlarmMessageProperties;
 
 import java.text.SimpleDateFormat;
 import java.util.Date;
@@ -16,13 +18,12 @@ import java.util.Date;
 @RequiredArgsConstructor
 @Slf4j
 public class DefaultSensorAlarmChecker implements SensorAlarmChecker {
+    // 定义一个日期格式化器,用于将时间格式化成可读的字符串
+    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     private final SensorThresholdService thresholdService;
     private final ColdChainAlarmMessageProperties alarmMessageProperties;
     private final ApplicationEventPublisher applicationEventPublisher;
 
-    // 定义一个日期格式化器,用于将时间格式化成可读的字符串
-    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-
     @Override
     public boolean checkAlarm(SensorData sensorData) {
         if (sensorData == null) {
@@ -89,8 +90,10 @@ public class DefaultSensorAlarmChecker implements SensorAlarmChecker {
     private void publishAlarm(String alarmType, Float value, String unit, String deviceName, String time) {
         // 获取对应的报警消息模板
         String alarmMessage = getAlarmMessage(alarmType, value, unit, deviceName, time);
+        SensorAlarm sensorAlarm = new SensorAlarm();
+        sensorAlarm.setAlarmType(alarmType);
         // 发布报警事件(可以替换成实际的报警处理逻辑)
-        applicationEventPublisher.publishEvent(new SensorAlarmEvent(this, alarmMessage));
+        applicationEventPublisher.publishEvent(new SensorAlarmEvent(this, sensorAlarm));
     }
 
     /**

+ 1 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/alarm/service/SensorAlarmChecker.java → snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/check/SensorAlarmChecker.java

@@ -1,4 +1,4 @@
-package vip.xiaonuo.coldchain.core.event.alarm.service;
+package vip.xiaonuo.coldchain.core.alarm.service.check;
 
 import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
 

+ 31 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/MessagePushService.java

@@ -0,0 +1,31 @@
+package vip.xiaonuo.coldchain.core.alarm.service.messagepush;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description
+ * @date 2024/11/26 11:19:01
+ */
+
+import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarm;
+import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarmUser;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description 统一的推送消息接口,定义了推送消息的方法
+ * @date 2024/11/26
+ */
+
+public interface MessagePushService {
+
+    /**
+     * 发送告警消息
+     *
+     * @param alarm 告警信息
+     * @param user  告警接收人
+     */
+    boolean sendAlarmMessage(SensorAlarm alarm, SensorAlarmUser user);
+}

+ 60 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/SensorAlarmMessagePushService.java

@@ -0,0 +1,60 @@
+package vip.xiaonuo.coldchain.core.alarm.service.messagepush;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+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.mapper.SensorAlarmMapper;
+
+import java.util.Map;
+
+@Service
+@Slf4j
+public class SensorAlarmMessagePushService {
+
+    // 用一个 Map 来存储不同的推送渠道服务
+    private final Map<NotificationChannel, MessagePushService> pushServices;
+
+    @Autowired
+    private SensorAlarmMapper sensorAlarmMapper;  // 注入 MyBatis-Plus 的 Mapper
+
+    @Autowired
+    public SensorAlarmMessagePushService(Map<NotificationChannel, MessagePushService> pushServices) {
+        this.pushServices = pushServices;
+    }
+
+    /**
+     * 根据通知渠道推送消息,并更新通知状态
+     *
+     * @param alarm 告警信息
+     */
+    public void pushAlarmMessage(SensorAlarm alarm) {
+        // 遍历每个接收人和通知渠道,发送告警信息
+        for (SensorAlarmUser user : alarm.getAlarmUsers()) {
+            for (NotificationChannel channel : alarm.getNotificationChannel()) {
+                MessagePushService pushService = pushServices.get(channel);
+                if (pushService != null) {
+                    pushService.sendAlarmMessage(alarm, user);
+                } else {
+                    log.info("不支持的通知渠道:" + channel);
+                }
+            }
+        }
+        // 设置已通知状态
+        alarm.setNotified(true);
+        // 将告警信息存储到数据库
+        saveAlarmToDatabase(alarm);
+    }
+
+    /**
+     * 保存告警信息到数据库
+     *
+     * @param alarm 告警信息
+     */
+    private void saveAlarmToDatabase(SensorAlarm alarm) {
+        // 调用 MyBatis-Plus 的 saveOrUpdate 方法保存或更新告警数据
+        sensorAlarmMapper.insert(alarm); // 假设 mapper 有这个方法
+    }
+}

+ 23 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/impl/DingTalkMessagePushService.java

@@ -0,0 +1,23 @@
+package vip.xiaonuo.coldchain.core.alarm.service.messagepush.impl;
+
+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;
+
+/**
+ * 钉钉通知推送服务
+ *
+ * @author jackzhou
+ */
+@Service
+public class DingTalkMessagePushService implements MessagePushService {
+
+    @Override
+    public boolean sendAlarmMessage(SensorAlarm alarm, SensorAlarmUser user) {
+        System.out.println("发送钉钉通知:");
+        System.out.println("消息内容:" + alarm.getMessage());
+        System.out.println("发送渠道:" + alarm);
+        return false;
+    }
+}

+ 29 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/impl/EmailPushService.java

@@ -0,0 +1,29 @@
+package vip.xiaonuo.coldchain.core.alarm.service.messagepush.impl;
+
+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;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description 邮件推送实现类
+ * @date 2024/11/26
+ */
+@Service
+public class EmailPushService implements MessagePushService {
+
+    @Override
+    public boolean sendAlarmMessage(SensorAlarm alarm, SensorAlarmUser user) {
+        // 这里是模拟发送邮件的实现,具体可以集成第三方邮件服务
+        String message = buildMessage(alarm);
+        System.out.println("发送邮件到: " + user.getEmail() + " 内容: " + message);
+        return false;
+    }
+
+    private String buildMessage(SensorAlarm alarm) {
+        return "告警信息:设备 " + alarm.getDeviceName() + " " + alarm.getMessage();
+    }
+}

+ 29 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/impl/PushNotificationService.java

@@ -0,0 +1,29 @@
+package vip.xiaonuo.coldchain.core.alarm.service.messagepush.impl;
+
+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;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description 推送通知实现类
+ * @date 2024/11/26
+ */
+@Service
+public class PushNotificationService implements MessagePushService {
+
+    @Override
+    public boolean sendAlarmMessage(SensorAlarm alarm, SensorAlarmUser user) {
+        // 这里是模拟推送通知的实现,具体可以集成推送服务如 Firebase 或者其它
+        String message = buildMessage(alarm);
+        System.out.println("推送通知到: " + user.getOpenId() + " 内容: " + message);
+        return false;
+    }
+
+    private String buildMessage(SensorAlarm alarm) {
+        return "告警信息:设备 " + alarm.getDeviceName() + " " + alarm.getMessage();
+    }
+}

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

@@ -0,0 +1,29 @@
+package vip.xiaonuo.coldchain.core.alarm.service.messagepush.impl;
+
+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;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description 短信推送实现类
+ * @date 2024/11/26
+ */
+@Service
+public class SMSPushService implements MessagePushService {
+
+    @Override
+    public boolean sendAlarmMessage(SensorAlarm alarm, SensorAlarmUser user) {
+        // 这里是模拟发送短信的实现,具体可以集成第三方短信服务
+        String message = buildMessage(alarm);
+        System.out.println("发送短信到: " + user.getPhone() + " 内容: " + message);
+        return false;
+    }
+
+    private String buildMessage(SensorAlarm alarm) {
+        return "告警信息:设备 " + alarm.getDeviceName() + " " + alarm.getMessage();
+    }
+}

+ 35 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/impl/WechatMessagePushService.java

@@ -0,0 +1,35 @@
+package vip.xiaonuo.coldchain.core.alarm.service.messagepush.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.google.common.collect.Lists;
+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 vip.xiaonuo.coldchain.modular.push.param.PushParam;
+import vip.xiaonuo.coldchain.modular.push.utils.PushUtil;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 微信公众号通知推送服务
+ *
+ * @author jackzhou
+ */
+@Service
+public class WechatMessagePushService implements MessagePushService {
+
+    @Override
+    public boolean sendAlarmMessage(SensorAlarm alarm, SensorAlarmUser user) {
+        String message = alarm.getMessage();
+        List<String> myList = Lists.newArrayList("oltp26fXeljOKUxYGhAx_dRqVAak", "oltp26cDdkiAAsualbzKMyiZbJrU");
+        PushParam pushParam = new PushParam("1.0");
+        pushParam.setContext(message);
+        pushParam.setDeviceName(alarm.getSource());
+        pushParam.setNoticeTime(new Date());
+        String pushCode = PushUtil.push(pushParam);
+        alarm.setWeixinRequestCode(pushCode);
+        return !StrUtil.isBlank(pushCode);
+    }
+}

+ 21 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/impl/WechatMiniMessagePushService.java

@@ -0,0 +1,21 @@
+package vip.xiaonuo.coldchain.core.alarm.service.messagepush.impl;
+
+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;
+
+/**
+ * 微信小程序通知推送服务
+ *
+ * @author jackzhou
+ */
+@Service
+public class WechatMiniMessagePushService implements MessagePushService {
+
+    @Override
+    public boolean sendAlarmMessage(SensorAlarm alarm, SensorAlarmUser user) {
+        System.out.println("发送微信小程序通知:");
+        return false;
+    }
+}

+ 2 - 2
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/alarm/service/SensorThresholdService.java → snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/threshold/SensorThresholdService.java

@@ -1,4 +1,4 @@
-package vip.xiaonuo.coldchain.core.event.alarm.service;
+package vip.xiaonuo.coldchain.core.alarm.service.threshold;
 
 /**
  * @author jackzhou
@@ -11,7 +11,7 @@ package vip.xiaonuo.coldchain.core.event.alarm.service;
 import cn.hutool.core.bean.BeanUtil;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
-import vip.xiaonuo.coldchain.core.event.alarm.bean.SensorThreshold;
+import vip.xiaonuo.coldchain.core.alarm.bean.SensorThreshold;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.entity.MonitorTargetRegion;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.service.MonitorTargetRegionService;
 

+ 4 - 3
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/alarm/service/ThresholdService.java → snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/threshold/ThresholdService.java

@@ -1,4 +1,4 @@
-package vip.xiaonuo.coldchain.core.event.alarm.service;
+package vip.xiaonuo.coldchain.core.alarm.service.threshold;
 
 /**
  * @author jackzhou
@@ -8,7 +8,7 @@ package vip.xiaonuo.coldchain.core.event.alarm.service;
  * @date 2024/11/25 17:08:36
  */
 
-import vip.xiaonuo.coldchain.core.event.alarm.bean.SensorThreshold;
+import vip.xiaonuo.coldchain.core.alarm.bean.SensorThreshold;
 
 /**
  * 获取传感器阈值范围的服务接口
@@ -17,8 +17,9 @@ public interface ThresholdService {
 
     /**
      * 根据传感器 ID 获取对应的阈值范围
+     *
      * @param sensorCode 传感器 ID
-     * @param sensorNo 传感器 路数
+     * @param sensorNo   传感器 路数
      * @return 传感器的阈值范围
      */
     SensorThreshold getThresholdBySensorId(String sensorCode, Integer sensorNo);

+ 6 - 5
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/SensorAlarmEvent.java

@@ -3,6 +3,7 @@ package vip.xiaonuo.coldchain.core.event;
 import lombok.Getter;
 import lombok.Setter;
 import org.springframework.context.ApplicationEvent;
+import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarm;
 
 import java.time.Clock;
 
@@ -16,15 +17,15 @@ import java.time.Clock;
 @Getter
 @Setter
 public class SensorAlarmEvent extends ApplicationEvent {
-    private String message;
+    private SensorAlarm sensorAlarm;
 
-    public SensorAlarmEvent(Object source, String message) {
+    public SensorAlarmEvent(Object source, SensorAlarm sensorAlarm) {
         super(source);
-        this.message = message;
+        this.sensorAlarm = sensorAlarm;
     }
 
-    public SensorAlarmEvent(Object source, Clock clock, String message) {
+    public SensorAlarmEvent(Object source, Clock clock, SensorAlarm sensorAlarm) {
         super(source, clock);
-        this.message = message;
+        this.sensorAlarm = sensorAlarm;
     }
 }

+ 10 - 2
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/SensorAlarmEventListener.java

@@ -8,21 +8,29 @@ package vip.xiaonuo.coldchain.core.event;
  * @date 2024/11/12 15:31:43
  */
 
+import com.google.common.collect.Lists;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.event.EventListener;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
+import vip.xiaonuo.coldchain.core.alarm.service.messagepush.SensorAlarmMessagePushService;
+import vip.xiaonuo.coldchain.modular.push.param.PushParam;
+import vip.xiaonuo.coldchain.modular.push.utils.PushUtil;
+
+import java.util.Date;
+import java.util.List;
 
 @Slf4j
 @RequiredArgsConstructor
 @Component
 public class SensorAlarmEventListener {
+    private final SensorAlarmMessagePushService sensorAlarmMessagePushService;
 
     @Async
     @EventListener
     public void handleSensorAlarmEvent(SensorAlarmEvent event) {
-        log.warn(event.getMessage());
-        
+
+        sensorAlarmMessagePushService.pushAlarmMessage(event.getSensorAlarm());
     }
 }

+ 1 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/SensorDataEventListener2.java

@@ -9,7 +9,7 @@ import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
 import vip.xiaonuo.coldchain.core.config.JfcloudColdChainConstants;
-import vip.xiaonuo.coldchain.core.event.alarm.service.SensorAlarmChecker;
+import vip.xiaonuo.coldchain.core.alarm.service.check.SensorAlarmChecker;
 
 import java.util.ArrayList;
 import java.util.List;

+ 22 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/handler/NotificationChannelListTypeHandler.java

@@ -0,0 +1,22 @@
+package vip.xiaonuo.coldchain.core.handler;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import vip.xiaonuo.coldchain.core.alarm.bean.NotificationChannel;
+import vip.xiaonuo.coldchain.core.handler.abs.ListTypeHandler;
+
+import java.util.List;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description
+ * @date 2024/11/26 13:25:23
+ */
+public class NotificationChannelListTypeHandler extends ListTypeHandler<NotificationChannel> {
+
+    @Override
+    public TypeReference<List<NotificationChannel>> specificType() {
+        return new TypeReference<List<NotificationChannel>>() {};
+    }
+}

+ 21 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/handler/SensorAlarmUserTypeHandler.java

@@ -0,0 +1,21 @@
+package vip.xiaonuo.coldchain.core.handler;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarmUser;
+import vip.xiaonuo.coldchain.core.handler.abs.ListTypeHandler;
+
+import java.util.List;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description
+ * @date 2024/11/26 13:12:16
+ */
+public class SensorAlarmUserTypeHandler extends ListTypeHandler<SensorAlarmUser> {
+    @Override
+    public TypeReference<List<SensorAlarmUser>> specificType() {
+        return new TypeReference<List<SensorAlarmUser>>() {};
+    }
+}

+ 71 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/handler/abs/GenericTypeHandler.java

@@ -0,0 +1,71 @@
+package vip.xiaonuo.coldchain.core.handler.abs;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.ibatis.type.BaseTypeHandler;
+import org.apache.ibatis.type.JdbcType;
+
+import java.io.IOException;
+
+public abstract class GenericTypeHandler<T> extends BaseTypeHandler<T> {
+
+    public GenericTypeHandler() {
+    }
+
+    private static final ObjectMapper objectMapper = new ObjectMapper();
+
+    // 将对象转换为 JSON 字符串存入数据库
+    @Override
+    public void setNonNullParameter(java.sql.PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws java.sql.SQLException {
+        try {
+            String json = objectMapper.writeValueAsString(parameter);
+            ps.setString(i, json);
+        } catch (JsonProcessingException e) {
+            throw new java.sql.SQLException("Error serializing object to JSON", e);
+        }
+    }
+
+    // 从数据库中读取 JSON 字符串,并转换为对象
+    @Override
+    public T getNullableResult(java.sql.ResultSet rs, String columnName) throws java.sql.SQLException {
+        try {
+            String json = rs.getString(columnName);
+            if (json != null) {
+                return objectMapper.readValue(json, specificType());
+            }
+            return null;
+        } catch (IOException e) {
+            throw new java.sql.SQLException("Error deserializing JSON to object", e);
+        }
+    }
+
+    @Override
+    public T getNullableResult(java.sql.ResultSet rs, int columnIndex) throws java.sql.SQLException {
+        try {
+            String json = rs.getString(columnIndex);
+            if (json != null) {
+                return objectMapper.readValue(json, specificType());
+            }
+            return null;
+        } catch (IOException e) {
+            throw new java.sql.SQLException("Error deserializing JSON to object", e);
+        }
+    }
+
+    @Override
+    public T getNullableResult(java.sql.CallableStatement cs, int columnIndex) throws java.sql.SQLException {
+        try {
+            String json = cs.getString(columnIndex);
+            if (json != null) {
+                return objectMapper.readValue(json, specificType());
+            }
+            return null;
+        } catch (IOException e) {
+            throw new java.sql.SQLException("Error deserializing JSON to object", e);
+        }
+    }
+
+    protected abstract TypeReference<T> specificType();
+
+}

+ 17 - 3
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitordevice/entity/ListTypeHandler.java → snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/handler/abs/ListTypeHandler.java

@@ -1,4 +1,4 @@
-package vip.xiaonuo.coldchain.modular.monitordevice.entity;
+package vip.xiaonuo.coldchain.core.handler.abs;
 
 /**
  * @author jackzhou
@@ -30,7 +30,10 @@ import java.util.List;
 @Slf4j
 @MappedJdbcTypes(JdbcType.VARCHAR)
 @MappedTypes({List.class})
-public abstract class ListTypeHandler<T> extends BaseTypeHandler<List<T>> {
+public class ListTypeHandler<T> extends BaseTypeHandler<List<T>> {
+
+    public ListTypeHandler() {
+    }
 
     @Override
     public void setNonNullParameter(PreparedStatement preparedStatement, int i, List<T> ts, JdbcType jdbcType) throws SQLException {
@@ -69,7 +72,18 @@ public abstract class ListTypeHandler<T> extends BaseTypeHandler<List<T>> {
         return StringUtils.isEmpty(content) ? new ArrayList<>() : new ObjectMapper().readValue(content, this.specificType());
     }
 
-    protected abstract TypeReference<List<T>> specificType();
+    /**
+     * 默认的 TypeReference 提供了 List<T> 的类型信息。
+     * 子类可以覆盖此方法以提供更具体的类型信息。
+     */
+    /**
+     * 返回 List<T> 的 TypeReference 类型信息。
+     * 可以直接用于 JSON 反序列化。
+     */
+    public TypeReference<List<T>> specificType() {
+        return new TypeReference<List<T>>() {}; // 默认返回 List<T> 类型
+    }
+
 
     public String toJSONString(Object obj) {
         try {

+ 0 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/push/param/PushParam.java

@@ -5,7 +5,6 @@ import lombok.Setter;
 
 import java.util.Date;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 
 @Getter

+ 0 - 4
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/push/utils/PushUtil.java

@@ -2,11 +2,8 @@ package vip.xiaonuo.coldchain.modular.push.utils;
 
 
 import cn.hutool.core.date.DateUtil;
-import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.mp.api.WxMpService;
-import me.chanjar.weixin.mp.api.WxMpUserService;
 import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
-import me.chanjar.weixin.mp.api.impl.WxMpUserServiceImpl;
 import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
 import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
 import me.chanjar.weixin.mp.config.WxMpConfigStorage;
@@ -14,7 +11,6 @@ import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
 import vip.xiaonuo.coldchain.modular.push.config.PushConfigure;
 import vip.xiaonuo.coldchain.modular.push.param.PushParam;
 
-import java.util.Date;
 import java.util.Objects;
 
 /**

+ 8 - 3
snowy-web-app/src/main/java/vip/xiaonuo/Application.java

@@ -14,6 +14,7 @@ package vip.xiaonuo;
 
 import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.Banner;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -23,6 +24,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.coldchain.core.alarm.mapper.SensorAlarmMapper;
 
 /**
  * SpringBoot方式启动类
@@ -32,14 +34,17 @@ import org.springframework.web.bind.annotation.RestController;
  */
 @Slf4j
 @RestController
-@SpringBootApplication(scanBasePackages = {"vip.xiaonuo","com.github.jfcloud"})
+@SpringBootApplication(scanBasePackages = {"vip.xiaonuo", "com.github.jfcloud"})
 @EnableAsync
 @EnableScheduling
 public class Application {
 
+    @Autowired
+    SensorAlarmMapper sensorAlarmMapper;
+
     /* 解决druid 日志报错:discard long time none received connection:xxx */
     static {
-        System.setProperty("druid.mysql.usePingMethod","false");
+        System.setProperty("druid.mysql.usePingMethod", "false");
     }
 
     /**
@@ -55,7 +60,7 @@ public class Application {
         ConfigurableApplicationContext configurableApplicationContext = springApplication.run(args);
         Environment env = configurableApplicationContext.getEnvironment();
         log.info("""
-                        
+                                                
                         ----------------------------------------------------------
                         Application is running! Access URLs:
                         Local:    http://localhost:{}

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

@@ -142,7 +142,7 @@ mybatis-plus.global-config.db-config.logic-delete-field=DELETE_FLAG
 mybatis-plus.global-config.db-config.logic-delete-value=DELETED
 mybatis-plus.global-config.db-config.logic-not-delete-value=NOT_DELETE
 mybatis-plus.mapper-locations=classpath*:vip/xiaonuo/**/mapping/*.xml
-mybatis-plus.type-handlers-package=vip.xiaonuo.common.handler
+mybatis-plus.type-handlers-package=vip.xiaonuo.common.handler,vip.xiaonuo.coldchain.core.handler
 logging.level.mybatis=warn
 
 #########################################