Bladeren bron

feate: 温湿度查询

jackzhou 6 maanden geleden
bovenliggende
commit
aa2e2e18fb
16 gewijzigde bestanden met toevoegingen van 274 en 377 verwijderingen
  1. 1 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/SensorAlarmServiceImpl.java
  2. 1 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/LocalSensorAlarmMessagePushService.java
  3. 1 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/RedisSensorAlarmMessagePushService.java
  4. 21 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/bean/influxdb/DataType.java
  5. 12 161
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/bean/influxdb/SensorData.java
  6. 1 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/config/JfcloudColdChainConstants.java
  7. 45 77
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/SensorDataEventListener.java
  8. 26 22
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/SensorDataQueueEventListener.java
  9. 119 94
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/service/JfcloudSensorDataService.java
  10. 2 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/service/dataprocess/dataclean/impl/AbsRenkeMonitorDataProcessor.java
  11. 0 6
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/controller/AppController.java
  12. 1 2
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/param/AppDeviceData.java
  13. 28 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/param/FloatCo2ToDashSerializer.java
  14. 0 12
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/service/AppDeviceService.java
  15. 3 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/entity/MonitorTargetRegion.java
  16. 13 0
      snowy-web-app/src/main/java/vip/xiaonuo/weixin/miniapp/controller/WxPortalController.java

+ 1 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/SensorAlarmServiceImpl.java

@@ -41,7 +41,7 @@ public class SensorAlarmServiceImpl extends ServiceImpl<SensorAlarmMapper, Senso
     @Override
     public Page<Message> getMessages(MessagePageParam messagePageParam) {
         if (messagePageParam.getType().equalsIgnoreCase("system")) {
-            return null;
+            return new Page<Message>();
         } else {
             // 创建分页对象
             Page<Message> page = new Page<>(messagePageParam.getCurrent(), messagePageParam.getSize());

+ 1 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/LocalSensorAlarmMessagePushService.java

@@ -43,7 +43,7 @@ public class LocalSensorAlarmMessagePushService {
         for (String openid : openids) {
             // 判断该用户对该设备和告警类型的推送是否超过限制
             if (hasExceededPushLimit(openid, deviceID, alarmType)) {
-                log.info("用户 {} 对设备 {} 的告警类型 {} \n在过去{}分钟内推送次数超过限制,跳过推送", openid, deviceID, alarmType, JfcloudColdChainConstants.MESS_PUSH_TIME_WINDOW / 60 / 1000);
+//                log.info("用户 {} 对设备 {} 的告警类型 {} \n在过去{}分钟内推送次数超过限制,跳过推送", openid, deviceID, alarmType, JfcloudColdChainConstants.MESS_PUSH_TIME_WINDOW / 60 / 1000);
                 continue;
             }
             // 遍历每个接收人和通知渠道,发送告警信息

+ 1 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/messagepush/RedisSensorAlarmMessagePushService.java

@@ -38,7 +38,7 @@ public class RedisSensorAlarmMessagePushService {
 
         for (String openid : openids) {
             if (hasExceededPushLimit(openid, deviceID, alarmType)) {
-                log.info("用户 {} 对设备 {} 的告警类型 {} \n在过去{}分钟内推送次数超过限制,跳过推送", openid, deviceID, alarmType, JfcloudColdChainConstants.MESS_PUSH_TIME_WINDOW / 60 / 1000);
+//                log.info("用户 {} 对设备 {} 的告警类型 {} \n在过去{}分钟内推送次数超过限制,跳过推送", openid, deviceID, alarmType, JfcloudColdChainConstants.MESS_PUSH_TIME_WINDOW / 60 / 1000);
                 continue;
             }
             for (NotificationChannel channel : alarm.getNotificationChannel()) {

+ 21 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/bean/influxdb/DataType.java

@@ -0,0 +1,21 @@
+package vip.xiaonuo.coldchain.core.bean.influxdb;
+
+import lombok.Getter;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description
+ * @date 2024/11/27 20:19:25
+ */
+public enum DataType {
+    HISTORY(1),
+    NEW(0);
+    @Getter
+    private final int code;
+
+    DataType(int code) {
+        this.code = code;
+    }
+}

+ 12 - 161
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/bean/influxdb/SensorData.java

@@ -41,23 +41,23 @@ public class SensorData extends JfcloudInFluxEntity {
     @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "#.00")
     private Float co2;
 
-    @Column(name = "battery", tag = true)
+    @Column(name = "battery")
     @JsonSerialize(using = FloatNullToDashSerializer.class)  // 使用自定义序列化器
     @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "#.00")
     private Float battery;
 
-    @Column(name = "plugInStatus", tag = true)
+    @Column(name = "plugInStatus")
     private String plugInStatus = PowerEnum.AC.getCode();
 
-    @Column(name = "location", tag = true)
+    @Column(name = "location")
     private String location;
 
-    @Column(name = "longitude", tag = true)
+    @Column(name = "longitude")
     @JsonSerialize(using = DoubleNullToDashSerializer.class)  // 使用自定义序列化器
     @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "#.00")
     private Double lng;
 
-    @Column(name = "latitude", tag = true)
+    @Column(name = "latitude")
     @JsonSerialize(using = DoubleNullToDashSerializer.class)  // 使用自定义序列化器
     @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "#.00")
     private Double lat;
@@ -71,162 +71,13 @@ public class SensorData extends JfcloudInFluxEntity {
     @Column(name = "model_name", tag = true)
     private String modelName;
 
-    @Column(name = "create_time", tag = true)
+    @Column(name = "create_time")
     private String createTime;
 
-//    /**
-//     * 使用反射和FluxRecord动态映射字段值到SensorData对象
-//     *
-//     * @param record FluxRecord数据记录
-//     * @return 映射后的SensorData对象
-//     */
-//    public static SensorData mapToSensorData(FluxRecord record) {
-//        SensorData sensorData = new SensorData();
-//        try {
-//            // 获取SensorData类的所有字段
-//            Field[] fields = SensorData.class.getDeclaredFields();
-//            for (Field field : fields) {
-//                field.setAccessible(true);  // 设置字段可访问
-//                // 如果字段没有@Column注解,则跳过
-//                if (field.getAnnotation(Column.class) == null) {
-//                    continue;
-//                }
-//
-//                String fieldName = field.getName(); // 获取字段名
-//                Object value = record.getValueByKey(fieldName); // 从FluxRecord中根据字段名获取值
-//                if (value == null) {
-//                    continue; // 如果FluxRecord中没有该字段的值,则跳过
-//                }
-//
-//                // 根据字段类型进行相应的值转换
-//                if (field.getType().equals(String.class)) {
-//                    field.set(sensorData, value.toString());
-//                } else if (field.getType().equals(Integer.class) || field.getType().equals(int.class)) {
-//                    field.set(sensorData, convertToInteger(value));
-//                } else if (field.getType().equals(Float.class) || field.getType().equals(float.class)) {
-//                    field.set(sensorData, convertToFloat(value));
-//                } else if (field.getType().equals(Double.class) || field.getType().equals(double.class)) {
-//                    field.set(sensorData, convertToDouble(value));
-//                } else if (field.getType().equals(Instant.class)) {
-//                    field.set(sensorData, convertToInstant(value));
-//                } else if (field.getType().equals(Date.class)) {
-//                    field.set(sensorData, convertToDate(value));
-//                } else if (field.getType().equals(LocalDate.class)) {
-//                    field.set(sensorData, convertToLocalDate(value));
-//                } else {
-//                    log.warn("无法处理字段类型: " + field.getName() + ",字段值: " + value);
-//                }
-//            }
-//        } catch (Exception e) {
-//            log.error("从FluxRecord映射到SensorData时出错", e);
-//        }
-//        return sensorData;
-//    }
-//
-//    // 类型转换方法
-//
-//    /**
-//     * 将值转换为Integer类型
-//     *
-//     * @param value 要转换的值
-//     * @return 转换后的Integer值
-//     */
-//    private static Integer convertToInteger(Object value) {
-//        try {
-//            return (value instanceof Integer) ? (Integer) value : Integer.valueOf(value.toString());
-//        } catch (NumberFormatException e) {
-//            log.error("将值转换为Integer时出错: " + value, e);
-//            return null;
-//        }
-//    }
-//
-//    /**
-//     * 将值转换为Float类型
-//     *
-//     * @param value 要转换的值
-//     * @return 转换后的Float值
-//     */
-//    private static Float convertToFloat(Object value) {
-//        try {
-//            if (value instanceof Double) {
-//                return ((Double) value).floatValue();  // 将Double 转换为Float
-//            }
-//            return (value instanceof Float) ? (Float) value : Float.valueOf(value.toString());
-//        } catch (NumberFormatException e) {
-//            log.error("将值转换为Float时出错: " + value, e);
-//            return null;
-//        }
-//    }
-//
-//    /**
-//     * 将值转换为Double类型
-//     *
-//     * @param value 要转换的值
-//     * @return 转换后的Double值
-//     */
-//    private static Double convertToDouble(Object value) {
-//        try {
-//            return (value instanceof Double) ? (Double) value : Double.valueOf(value.toString());
-//        } catch (NumberFormatException e) {
-//            log.error("将值转换为Double时出错: " + value, e);
-//            return null;
-//        }
-//    }
-//
-//    /**
-//     * 将值转换为Instant类型
-//     *
-//     * @param value 要转换的值
-//     * @return 转换后的Instant值
-//     */
-//    private static Instant convertToInstant(Object value) {
-//        try {
-//            return (value instanceof Instant) ? (Instant) value : Instant.parse(value.toString());
-//        } catch (Exception e) {
-//            log.error("将值转换为Instant时出错: " + value, e);
-//            return null;
-//        }
-//    }
-//
-//    /**
-//     * 将值转换为Date类型
-//     *
-//     * @param value 要转换的值
-//     * @return 转换后的Date值
-//     */
-//    private static Date convertToDate(Object value) {
-//        try {
-//            if (value instanceof Instant) {
-//                return Date.from((Instant) value);
-//            } else if (value instanceof String) {
-//                return Date.from(Instant.parse((String) value));
-//            } else {
-//                return (Date) value;
-//            }
-//        } catch (Exception e) {
-//            log.error("将值转换为Date时出错: " + value, e);
-//            return null;
-//        }
-//    }
-//
-//    /**
-//     * 将值转换为LocalDate类型
-//     *
-//     * @param value 要转换的值
-//     * @return 转换后的LocalDate值
-//     */
-//    private static LocalDate convertToLocalDate(Object value) {
-//        try {
-//            if (value instanceof Instant) {
-//                return ((Instant) value).atZone(java.time.ZoneId.systemDefault()).toLocalDate();
-//            } else if (value instanceof String) {
-//                return LocalDate.parse((String) value);
-//            } else {
-//                return (LocalDate) value;
-//            }
-//        } catch (Exception e) {
-//            log.error("将值转换为LocalDate时出错: " + value, e);
-//            return null;
-//        }
-//    }
+    @Column(name = "type")
+    /**
+     * 实时数据0  历史数据1
+     */
+    private Integer type = DataType.NEW.getCode();
+
 }

+ 1 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/config/JfcloudColdChainConstants.java

@@ -41,6 +41,7 @@ public interface JfcloudColdChainConstants {
 
     /**
      * 队列最大容量和写入数据库的时间间隔
+     * 1分钟写入一次
      */
     long CHECK_INTERVAL_MS = 1 * 60 * 1000L;
 

+ 45 - 77
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/SensorDataEventListener.java

@@ -1,77 +1,45 @@
-//package vip.xiaonuo.coldchain.core.event;
-//
-///**
-// * @author jackzhou
-// * @version 1.0
-// * @project jfcloud-coldchain
-// * @description 监听传感器数据事件并异步写入 InfluxDB
-// * @date 2024/11/12 15:31:43
-// */
-//
-//import com.github.jfcloud.influxdb.service.JfcloudInfluxDBService;
-//import lombok.RequiredArgsConstructor;
-//import lombok.extern.slf4j.Slf4j;
-//import org.springframework.context.ApplicationEventPublisher;
-//import org.springframework.context.event.EventListener;
-//import org.springframework.scheduling.annotation.Async;
-//import org.springframework.stereotype.Component;
-//import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
-//
-//@Slf4j
-//@RequiredArgsConstructor
-//@Component
-//public class SensorDataEventListener {
-//    private final JfcloudInfluxDBService jfcloudInfluxDBService;
-//    private final ApplicationEventPublisher eventPublisher;
-//
-//    @Async
-//    @EventListener
-//    public void handleSensorDataEvent(SensorDataEvent event) {
-//        SensorData sensorData = event.getSensorData();
-//        try {
-//            log.info("开始写入数据到 InfluxDB: {}", sensorData);
-//            jfcloudInfluxDBService.writePojo(sensorData);
-//            log.info("成功写入数据到 InfluxDB: {}", sensorData);
-//
-//            // 数据写入成功后检查是否需要报警
-//            checkForAlarm(sensorData);
-//
-//        } catch (Exception e) {
-//            log.error("写入数据到 InfluxDB 时出错: {}", e.getMessage(), e);
-//            // 可以根据需要选择是否抛出新的异常或进行其他处理
-//        }
-//    }
-//
-//
-//    // 检查是否需要触发报警
-//    private void checkForAlarm(SensorData sensorData) {
-//        // 设置阈值(可以根据实际情况调整这些阈值)
-//        float temperatureThreshold = 50.0f;  // 温度阈值
-//        float humidityThreshold = 90.0f;     // 湿度阈值
-//        float co2Threshold = 1000.0f;        // 二氧化碳阈值
-//        StringBuilder alarmMessage = new StringBuilder();
-//
-//        // 检查温度是否超标
-//        if (sensorData.getTemperature() != null && sensorData.getTemperature() > temperatureThreshold) {
-//            alarmMessage.append("温度超标: ").append(sensorData.getTemperature()).append("°C; ");
-//        }
-//
-//        // 检查湿度是否超标
-//        if (sensorData.getHumidity() != null && sensorData.getHumidity() > humidityThreshold) {
-//            alarmMessage.append("湿度超标: ").append(sensorData.getHumidity()).append("%; ");
-//        }
-//
-//        // 检查二氧化碳是否超标
-//        if (sensorData.getCo2() != null && sensorData.getCo2() > co2Threshold) {
-//            alarmMessage.append("二氧化碳超标: ").append(sensorData.getCo2()).append("ppm; ");
-//        }
-//        // 如果发现超标数据,发布报警事件
-//        if (!alarmMessage.isEmpty()) {
-//            // 构造报警事件
-//            SensorAlarmEvent alarmEvent = new SensorAlarmEvent(this);
-//            log.warn("数据异常,发布报警: {}", alarmMessage.toString());
-//            // 发布报警事件
-//            eventPublisher.publishEvent(alarmEvent);
-//        }
-//    }
-//}
+package vip.xiaonuo.coldchain.core.event;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description 监听传感器数据事件并异步写入 InfluxDB
+ * @date 2024/11/12 15:31:43
+ */
+
+import com.github.jfcloud.influxdb.service.JfcloudInfluxDBService;
+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.check.SensorAlarmChecker;
+import vip.xiaonuo.coldchain.core.bean.influxdb.DataType;
+import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
+
+import java.util.Objects;
+
+@Slf4j
+@RequiredArgsConstructor
+@Component
+public class SensorDataEventListener {
+    private final JfcloudInfluxDBService jfcloudInfluxDBService;
+    private final SensorAlarmChecker sensorAlarmChecker;
+
+    @Async
+    @EventListener
+    public void handleSensorDataEvent(SensorDataEvent event) {
+        SensorData sensorData = event.getSensorData();
+        try {
+            // 实时数据才预警提醒
+            if (Objects.nonNull(sensorData.getType()) && sensorData.getType() == DataType.NEW.getCode()) {
+                sensorAlarmChecker.checkAlarm(sensorData);
+            }
+            jfcloudInfluxDBService.writePojo(sensorData);
+            log.info("成功写入数据到 InfluxDB: {}", sensorData);
+        } catch (Exception e) {
+            log.error("写入数据到 InfluxDB 时出错: {}", e.getMessage(), e);
+        }
+    }
+}

+ 26 - 22
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/SensorDataEventListener2.java → snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/event/SensorDataQueueEventListener.java

@@ -6,32 +6,35 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.event.EventListener;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
+import vip.xiaonuo.coldchain.core.alarm.service.check.SensorAlarmChecker;
 import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
 import vip.xiaonuo.coldchain.core.config.JfcloudColdChainConstants;
-import vip.xiaonuo.coldchain.core.alarm.service.check.SensorAlarmChecker;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Objects;
 import java.util.Queue;
 import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 @Slf4j
 @RequiredArgsConstructor
-@Component
-public class SensorDataEventListener2 {
+@Deprecated
+public class SensorDataQueueEventListener {
+
     private final JfcloudInfluxDBService jfcloudInfluxDBService;
     private final SensorAlarmChecker sensorAlarmChecker;
-    // 使用双缓冲机制:一个队列缓存数据,另一个队列进行批量写入
+
     private final Queue<SensorData> bufferQueue = new ConcurrentLinkedQueue<>();
     private final Queue<SensorData> writeQueue = new ConcurrentLinkedQueue<>();
 
+    // 使用 AtomicBoolean 来标记数据是否需要写入
+    private final AtomicBoolean isWriteRequired = new AtomicBoolean(false);
+
     // 事件监听,处理传感器数据
     @EventListener
     public void handleSensorDataEvent(SensorDataEvent event) {
         SensorData sensorData = event.getSensorData();
-        if (Objects.nonNull(sensorData)) {
+        if (sensorData != null) {
             // 将数据添加到缓存队列
             addSensorDataToBufferQueue(sensorData);
             // 获取传感器的阈值范围并检查报警
@@ -41,26 +44,17 @@ public class SensorDataEventListener2 {
 
     // 将数据添加到缓存队列中
     public void addSensorDataToBufferQueue(SensorData sensorData) {
-        bufferQueue.offer(sensorData);
-        // 如果缓存队列的数据超过设定阈值,转移数据到写入队列
-        if (bufferQueue.size() >= JfcloudColdChainConstants.MAX_QUEUE_SIZE) {
-            transferDataToWriteQueue();
-        }
-    }
+        bufferQueue.offer(sensorData);  // 将数据放入缓存队列
 
-    // 定期检查并批量写入数据
-    @Scheduled(fixedRate = JfcloudColdChainConstants.CHECK_INTERVAL_MS)
-    public void checkAndWriteData() {
-        // 如果缓存队列达到阈值,则将数据转移到写入队列并异步写入
+        // 如果缓存队列的大小超过了设定的阈值,立即进行数据转移和写入操作
         if (bufferQueue.size() >= JfcloudColdChainConstants.MAX_QUEUE_SIZE) {
-            transferDataToWriteQueue();
-            // 异步写入数据到数据库
-            writeDataToDatabaseAsync();
+            transferDataToWriteQueue();  // 将数据转移到写入队列
+            writeDataToDatabaseAsync();  // 触发异步写入数据库
         }
     }
 
     // 将缓存队列的数据转移到写入队列
-    private void transferDataToWriteQueue() {
+    private synchronized void transferDataToWriteQueue() {
         if (!bufferQueue.isEmpty()) {
             writeQueue.addAll(bufferQueue);
             bufferQueue.clear();  // 清空缓存队列
@@ -112,4 +106,14 @@ public class SensorDataEventListener2 {
             }
         }
     }
-}
+
+    // 定期检查并批量写入数据,作为备用机制
+    @Scheduled(fixedRate = JfcloudColdChainConstants.CHECK_INTERVAL_MS)
+    public void checkAndWriteData() {
+        // 如果写入队列有数据,进行定期检查并触发异步写入
+        if (!writeQueue.isEmpty()) {
+            log.info("定期检查发现有待写入数据,执行批量写入操作");
+            writeDataToDatabaseAsync();
+        }
+    }
+}

+ 119 - 94
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/service/JfcloudSensorDataService.java

@@ -3,15 +3,16 @@ package vip.xiaonuo.coldchain.core.service;
 import cn.hutool.core.lang.Assert;
 import com.github.jfcloud.influxdb.config.JfcloudInfluxDB2Properties;
 import com.github.jfcloud.influxdb.flux.JfcloudFluxDataService;
-import com.github.jfcloud.influxdb.flux.QueryCondition;
 import com.github.jfcloud.influxdb.service.JfcloudInfluxDBService;
 import com.influxdb.client.QueryApi;
 import com.influxdb.query.FluxTable;
 import org.springframework.stereotype.Service;
 import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
 
+import java.time.Duration;
 import java.time.Instant;
-import java.time.ZoneId;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -38,30 +39,22 @@ public class JfcloudSensorDataService extends JfcloudFluxDataService<SensorData>
      * @return 最新的传感器数据
      */
     public SensorData queryLatestDataByDeviceIdAndRoads(String deviceId, Integer roads) {
-        String fluxQuery = String.format(
-                "from(bucket: \"%s\") " +
-                        "|> range(start: -1d) " +
-                        "|> filter(fn: (r) => r._measurement == \"%s\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%s\") " +
-                        "|> sort(columns: [\"_time\"], desc: true) " +
-                        "|> limit(n: 1) " +
-                        "|> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\") " +
-                        "|> keep(columns: [\"_time\", \"temperature\", \"humidity\", \"co2\", \"battery\", \"plugInStatus\", \"location\", \"longitude\", \"latitude\", \"device_id\", \"roads\", \"modelName\", \"create_time\"])",
-                getBucketName(),
-                getMeasurement(SensorData.class),
-                deviceId,
-                roads
-        );
+        String fluxQuery = buildLatestSensorDataQuery(getBucketName(), deviceId, roads + "");
         QueryApi queryApi = jfcloudInfluxDBService.getInfluxDBClient().getQueryApi();
         List<FluxTable> tables = queryApi.query(fluxQuery);
 
         // 将查询结果映射为 SensorData 实体
-        return tables.stream()
-                .flatMap(table -> table.getRecords().stream())  // 拉平所有记录
-                .map(fluxRecord -> mapRecordToEntity(fluxRecord,getEntityClass()))  // 将每个记录映射为 SensorData 实体
+        return tables.stream().flatMap(table -> table.getRecords().stream())  // 拉平所有记录
+                .map(fluxRecord -> mapRecordToEntity(fluxRecord, getEntityClass()))  // 将每个记录映射为 SensorData 实体
                 .findFirst()  // 只取第一条记录(即最新一条记录)
                 .orElse(null);  // 如果没有数据,返回 null
     }
 
+    public String buildLatestSensorDataQuery(String bucketName, String deviceId, String roads) {
+        return String.format("temperature = from(bucket: \"%s\")\n" + "  |> range(start: -1h)  // Hardcoded time range for the last hour\n" + "  |> filter(fn: (r) => r[\"_measurement\"] == \"sensor_data\" and r[\"_field\"] == \"temperature\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%s\")\n" + "  |> last()\n" + "humidity = from(bucket: \"%s\")\n" + "  |> range(start: -1h)\n" + "  |> filter(fn: (r) => r[\"_measurement\"] == \"sensor_data\" and r[\"_field\"] == \"humidity\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%s\")\n" + "  |> last()\n" + "co2 = from(bucket: \"%s\")\n" + "  |> range(start: -1h)\n" + "  |> filter(fn: (r) => r[\"_measurement\"] == \"sensor_data\" and r[\"_field\"] == \"co2\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%s\")\n" + "  |> last()\n" + "plugInStatus = from(bucket: \"%s\")\n" + "  |> range(start: -1h)\n" + "  |> filter(fn: (r) => r[\"_measurement\"] == \"sensor_data\" and r[\"_field\"] == \"plugInStatus\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%s\")\n" + "  |> last()\n" + "battery = from(bucket: \"%s\")\n" + "  |> range(start: -1h)\n" + "  |> filter(fn: (r) => r[\"_measurement\"] == \"sensor_data\" and r[\"_field\"] == \"battery\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%s\")\n" + "  |> last()\n" + "union(tables: [temperature, humidity, co2, plugInStatus, battery])\n" + "  |> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n" + "  |> yield(name: \"latest_data\")", bucketName, deviceId, roads, bucketName, deviceId, roads, bucketName, deviceId, roads, bucketName, deviceId, roads, bucketName, deviceId, roads);
+    }
+
+
     /**
      * 根据 deviceId、roads 和时间范围查询数据
      *
@@ -84,99 +77,131 @@ public class JfcloudSensorDataService extends JfcloudFluxDataService<SensorData>
         if (endTimeStr.length() == 10) {
             endTimeStr += " 23:59:59";
         }
-        // 日期时间字符串解析格式
-        DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
-                .withZone(ZoneId.of("UTC"));
-
-        // 将传入的日期时间字符串解析为 Instant 对象
-        Instant startTime = Instant.from(inputFormatter.parse(startTimeStr));
-        Instant endTime = Instant.from(inputFormatter.parse(endTimeStr));
-
-        // 格式化 Instant 为 InfluxDB 可以接受的字符串格式
-        DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.of("UTC"));
-        String startTimeFormatted = formatter.format(startTime);
-        String endTimeFormatted = formatter.format(endTime);
-
-        // 构建Flux查询
-//        String query = String.format(
-//                "from(bucket: \"%s\") |> range(start: %s, stop: %s) " +
-//                        "|> filter(fn: (r) => r._measurement == \"sensor_data\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%d\") " +
-//                        "|> sort(columns: [\"_time\"], desc: true) " +
-//                        "|> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\") " +
-//                        "|> keep(columns: [\"_time\", \"temperature\", \"humidity\", \"co2\", \"battery\", \"plugInStatus\", \"location\", \"longitude\", \"latitude\", \"device_id\", \"roads\", \"modelName\", \"create_time\"])",
-//                getBucketName(), startTimeFormatted, endTimeFormatted, deviceId, roads
-//        );
-        String query = String.format(
-                "from(bucket: \"%s\") " +
-                        "|> range(start: %s, stop: %s) " +
-                        "|> filter(fn: (r) => r._measurement == \"sensor_data\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%d\") " +
-                        "|> aggregateWindow(every: 1h, fn: mean, createEmpty: false) " +  // 按小时聚合,使用平均值
-                        "|> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\") " +
-                        "|> keep(columns: [\"_time\", \"temperature\", \"humidity\", \"co2\", \"battery\", \"plugInStatus\", \"location\", \"longitude\", \"latitude\", \"device_id\", \"roads\", \"modelName\", \"create_time\"])",
-                getBucketName(), startTimeFormatted, endTimeFormatted, deviceId, roads
-        );
-
+        String startTimeFormatted = convertToISO8601(startTimeStr);
+        String endTimeFormatted = convertToISO8601(endTimeStr);
+        // Determine the aggregation window (by day or by hour)
+        String aggregationWindow = determineAggregationWindow(startTimeFormatted, endTimeFormatted);
+        String query = buildRangeTimeFluxQuery(getBucketName(), deviceId, roads + "", startTimeFormatted, endTimeFormatted, aggregationWindow);
         // 执行查询
         QueryApi queryApi = jfcloudInfluxDBService.getInfluxDBClient().getQueryApi();
         List<FluxTable> results = queryApi.query(query);
 
         // 将查询结果转换为 SensorData 实体并返回
-        return results.stream()
-                .flatMap(table -> table.getRecords().stream())
-                .map(fluxRecord -> mapRecordToEntity(fluxRecord,getEntityClass()))  // 转换为 SensorData 实体
+        return results.stream().flatMap(table -> table.getRecords().stream()).map(fluxRecord -> mapRecordToEntity(fluxRecord, getEntityClass()))  // 转换为 SensorData 实体
                 .collect(Collectors.toList());  // 返回多条数据
     }
 
-
     /**
-     * 根据时间范围、设备ID、路数和 bucket 查询传感器数据,并筛选需要的字段
+     * Builds the Flux query dynamically based on the provided parameters.
      *
-     * @param startTime 起始时间
-     * @param endTime   结束时间
-     * @param deviceId  设备ID
-     * @param roads     路数
-     * @param fields    查询的字段列表,可以包含 "temperature", "humidity", "co2", "battery", "plugInStatus", etc.
-     * @return 查询到的传感器数据列表
+     * @param bucketName The name of the InfluxDB bucket.
+     * @param deviceId   The device ID to filter by.
+     * @param roads      The roads field to filter by.
+     * @param startTime  The start time of the query (ISO format).
+     * @param stopTime   The end time of the query (ISO format).
+     * @return The Flux query as a string.
      */
-    public List<SensorData> queryDataByTimeAndDevice(String startTime, String endTime, String deviceId, Integer roads, List<String> fields) {
-        // 构建查询字段的条件
-        String fieldsToSelect = fields.isEmpty() ? "*" : String.join(",", fields);
-        // 构建Flux查询语句
-        String query = String.format(
-                "from(bucket: \"%s\") |> range(start: %s, stop: %s) " +
-                        "|> filter(fn: (r) => r._measurement == \"sensor_data\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%d\") " +
-                        "|> sort(columns: [\"_time\"], desc: true) " +
-                        "|> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\") " +
-                        "|> keep(columns: [\"_time\", %s])",
-                getBucketName(), startTime, endTime, deviceId, roads, fieldsToSelect
+    public static String buildRangeTimeFluxQuery(String bucketName, String deviceId, String roads, String startTime, String stopTime, String aggregationWindow) {
+        return String.format(
+                "temperature = from(bucket: \"%s\")\n" +
+                        "  |> range(start: time(v: \"%s\"), stop: time(v: \"%s\"))\n" +
+                        "  |> filter(fn: (r) => r[\"_measurement\"] == \"sensor_data\" and r[\"_field\"] == \"temperature\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%s\")\n" +
+                        "  |> aggregateWindow(every: %s, fn: mean, createEmpty: false)\n" +
+                        "humidity = from(bucket: \"%s\")\n" +
+                        "  |> range(start: time(v: \"%s\"), stop: time(v: \"%s\"))\n" +
+                        "  |> filter(fn: (r) => r[\"_measurement\"] == \"sensor_data\" and r[\"_field\"] == \"humidity\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%s\")\n" +
+                        "  |> aggregateWindow(every: %s, fn: mean, createEmpty: false)\n" +
+                        "co2 = from(bucket: \"%s\")\n" +
+                        "  |> range(start: time(v: \"%s\"), stop: time(v: \"%s\"))\n" +
+                        "  |> filter(fn: (r) => r[\"_measurement\"] == \"sensor_data\" and r[\"_field\"] == \"co2\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%s\")\n" +
+                        "  |> aggregateWindow(every: %s, fn: mean, createEmpty: false)\n" +
+                        "union(tables: [temperature, humidity, co2])\n" +
+                        "  |> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n" +
+                        "  |> yield(name: \"latest_data\")",
+                bucketName, startTime, stopTime, deviceId, roads, aggregationWindow,
+                bucketName, startTime, stopTime, deviceId, roads, aggregationWindow,
+                bucketName, startTime, stopTime, deviceId, roads, aggregationWindow
         );
+    }
 
-        // 查询InfluxDB并返回结果
-        QueryApi queryApi = jfcloudInfluxDBService.getInfluxDBClient().getQueryApi();
-        List<FluxTable> results = queryApi.query(query);
-
-        // 将查询结果转换为 SensorData 实体
-        return results.stream()
-                .flatMap(table -> table.getRecords().stream())
-                .map(fluxRecord -> mapRecordToEntity(fluxRecord,getEntityClass()))  // 转换为 SensorData 实体
-                .collect(Collectors.toList());  // 返回多条数据
+    public static String determineAggregationWindow(String startTime, String stopTime) {
+        Instant start = Instant.parse(startTime);
+        Instant stop = Instant.parse(stopTime);
+        // Calculate the duration between start and stop
+        Duration duration = Duration.between(start, stop);
+        // If the duration is more than 30 days, return "1w" (weekly aggregation)
+        if (duration.toDays() > 30) {
+            return "1w"; // Weekly aggregation
+        }
+        // If the duration is more than 1 day but less than or equal to 30 days, return "1d" (daily aggregation)
+        else if (duration.toDays() > 1) {
+            return "1d"; // Daily aggregation
+        } else {
+            // If the duration is less than or equal to 1 day, return "1h" (hourly aggregation)
+            return "1h"; // Hourly aggregation
+        }
     }
 
     /**
-     * 根据 deviceId、roads、时间范围和查询条件查询数据
+     * Converts a time string in "yyyy-MM-dd HH:mm:ss" format to ISO 8601 format.
      *
-     * @param deviceId        设备 ID
-     * @param roads           路数
-     * @param startTime       查询开始时间
-     * @param endTime         查询结束时间
-     * @param queryConditions 查询条件列表
-     * @return 查询到的传感器数据列表
+     * @param time The time string in "yyyy-MM-dd HH:mm:ss" format.
+     * @return The ISO 8601 formatted time string.
      */
-    public List<SensorData> querySensorData(String deviceId, int roads, Instant startTime, Instant endTime, List<QueryCondition> queryConditions) {
-        String fluxQuery = buildFluxQuery(deviceId, roads, startTime, endTime, queryConditions, SensorData.class);
-        QueryApi queryApi = jfcloudInfluxDBService.getInfluxDBClient().getQueryApi();
-        List<FluxTable> tables = queryApi.query(fluxQuery);
-        // 将查询结果映射为 SensorData 实体
-        return tables.stream().flatMap(table -> table.getRecords().stream()).map(fluxRecord -> mapRecordToEntity(fluxRecord,getEntityClass())).collect(Collectors.toList());
+    public static String convertToISO8601(String time) {
+        // Define the input and output formats
+        DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        DateTimeFormatter outputFormatter = DateTimeFormatter.ISO_DATE_TIME;
+        // Parse the input time string to LocalDateTime (in system default zone)
+        LocalDateTime localDateTime = LocalDateTime.parse(time, inputFormatter);
+
+        // Convert the LocalDateTime to Instant (UTC time zone)
+        Instant instant = localDateTime.atZone(ZoneOffset.UTC).toInstant();
+
+        // Format the Instant to ISO 8601
+        return outputFormatter.format(instant.atZone(ZoneOffset.UTC));
     }
+
+//    /**
+//     * 根据时间范围、设备ID、路数和 bucket 查询传感器数据,并筛选需要的字段
+//     *
+//     * @param startTime 起始时间
+//     * @param endTime   结束时间
+//     * @param deviceId  设备ID
+//     * @param roads     路数
+//     * @param fields    查询的字段列表,可以包含 "temperature", "humidity", "co2", "battery", "plugInStatus", etc.
+//     * @return 查询到的传感器数据列表
+//     */
+//    public List<SensorData> queryDataByTimeAndDevice(String startTime, String endTime, String deviceId, Integer roads, List<String> fields) {
+//        // 构建查询字段的条件
+//        String fieldsToSelect = fields.isEmpty() ? "*" : String.join(",", fields);
+//        // 构建Flux查询语句
+//        String query = String.format("from(bucket: \"%s\") |> range(start: %s, stop: %s) " + "|> filter(fn: (r) => r._measurement == \"sensor_data\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%d\") " + "|> sort(columns: [\"_time\"], desc: true) " + "|> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\") " + "|> keep(columns: [\"_time\", %s])", getBucketName(), startTime, endTime, deviceId, roads, fieldsToSelect);
+//
+//        // 查询InfluxDB并返回结果
+//        QueryApi queryApi = jfcloudInfluxDBService.getInfluxDBClient().getQueryApi();
+//        List<FluxTable> results = queryApi.query(query);
+//
+//        // 将查询结果转换为 SensorData 实体
+//        return results.stream().flatMap(table -> table.getRecords().stream()).map(fluxRecord -> mapRecordToEntity(fluxRecord, getEntityClass()))  // 转换为 SensorData 实体
+//                .collect(Collectors.toList());  // 返回多条数据
+//    }
+//
+//    /**
+//     * 根据 deviceId、roads、时间范围和查询条件查询数据
+//     *
+//     * @param deviceId        设备 ID
+//     * @param roads           路数
+//     * @param startTime       查询开始时间
+//     * @param endTime         查询结束时间
+//     * @param queryConditions 查询条件列表
+//     * @return 查询到的传感器数据列表
+//     */
+//    public List<SensorData> querySensorData(String deviceId, int roads, Instant startTime, Instant endTime, List<QueryCondition> queryConditions) {
+//        String fluxQuery = buildFluxQuery(deviceId, roads, startTime, endTime, queryConditions, SensorData.class);
+//        QueryApi queryApi = jfcloudInfluxDBService.getInfluxDBClient().getQueryApi();
+//        List<FluxTable> tables = queryApi.query(fluxQuery);
+//        // 将查询结果映射为 SensorData 实体
+//        return tables.stream().flatMap(table -> table.getRecords().stream()).map(fluxRecord -> mapRecordToEntity(fluxRecord, getEntityClass())).collect(Collectors.toList());
+//    }
 }

+ 2 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/service/dataprocess/dataclean/impl/AbsRenkeMonitorDataProcessor.java

@@ -8,6 +8,7 @@ import org.springframework.context.ApplicationEventPublisher;
 import rk.netDevice.sdk.p2.NodeData;
 import rk.netDevice.sdk.p2.RealTimeData;
 import rk.netDevice.sdk.p2.StoreData;
+import vip.xiaonuo.coldchain.core.bean.influxdb.DataType;
 import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
 import vip.xiaonuo.coldchain.core.event.MonitorDeviceHeartBeatEvent;
 import vip.xiaonuo.coldchain.core.event.MonitorDeviceLoginEvent;
@@ -129,6 +130,7 @@ public abstract class AbsRenkeMonitorDataProcessor implements MonitorDataProcess
             int nodeId = nodeData.getNodeId();
             if (recordTime != null && nodeData.getCoordinateType() == 2) {
                 SensorData sensorData = new SensorData();
+                sensorData.setType(DataType.HISTORY.getCode());
                 sensorData.setDeviceId(String.valueOf(deviceId));
                 sensorData.setRoads(nodeId);
                 sensorData.setModelName(modelName);

+ 0 - 6
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/controller/AppController.java

@@ -37,12 +37,6 @@ public class AppController {
         return CommonResult.data(deviceStatus);
     }
 
-    @GetMapping("/devices")
-    @Operation(summary = "设备列表")
-    public CommonResult<Page<AppDevice>> devices(AppDevicePageParam appDevicePageParam) {
-        return CommonResult.data(null);
-    }
-
     @Operation(summary = "分页设备")
     @GetMapping("/device/page")
     public CommonResult<Page<AppDevice>> devicePage(AppDevicePageParam appDevicePageParam) {

+ 1 - 2
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/param/AppDeviceData.java

@@ -34,8 +34,7 @@ public class AppDeviceData extends MonitorTargetRegion {
     private Float humidity;  // 湿度
 
     @Schema(description = "二氧化碳浓度 (ppm)")
-    @JsonSerialize(using = FloatNullToDashSerializer.class)  // 使用自定义序列化器
-    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "#.00")
+    @JsonSerialize(using = FloatCo2ToDashSerializer.class)  // 使用自定义序列化器
     private Float co2Level;  // 二氧化碳浓度
 
     @Schema(description = "电池电量 (%)")

+ 28 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/param/FloatCo2ToDashSerializer.java

@@ -0,0 +1,28 @@
+package vip.xiaonuo.coldchain.modular.app.param;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description
+ * @date 2024/11/23 19:01:49
+ */
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import java.io.IOException;
+
+public class FloatCo2ToDashSerializer extends JsonSerializer<Float> {
+    @Override
+    public void serialize(Float value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+        if (value == null) {
+            gen.writeString("-");  // 如果值为 null,返回 "-"
+        } else {
+            // 四舍五入到整数
+            int roundedValue = Math.round(value);  // 四舍五入为整数
+            gen.writeNumber(roundedValue);  // 输出整数值
+        }
+    }
+}

+ 0 - 12
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/service/AppDeviceService.java

@@ -42,18 +42,6 @@ public class AppDeviceService {
         System.out.println("设备已添加:" + device);
         return true; // 假设设备保存成功
     }
-
-    /**
-     * 监控对象列表包含温湿度监控指标--全部
-     *
-     * @return
-     */
-    public List<AppDevice> appDevices(AppDevicePageParam appDevicePageParam) {
-        List<AppDevice> rlt = Lists.newArrayList();
-
-        return rlt;
-    }
-
     /**
      * 监控对象列表包含温湿度监控指标--分页
      *

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

@@ -16,6 +16,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.google.common.collect.Lists;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -167,6 +168,7 @@ public class MonitorTargetRegion extends CommonEntity {
      */
     @Schema(description = "告警接收人,存储告警通知的接收用户信息")
     @TableField(value = "alarm_users", typeHandler = SensorAlarmUserTypeHandler.class)
+    @JsonIgnore
     private List<SensorAlarmUser> alarmUsers = Lists.newArrayList();
 
     /**
@@ -174,6 +176,7 @@ public class MonitorTargetRegion extends CommonEntity {
      */
     @Schema(description = "用户的通知渠道设置,支持选择接收告警的多个渠道,如短信、邮件、APP通知等")
     @TableField(value = "notification_channel", typeHandler = NotificationChannelListTypeHandler.class)
+    @JsonIgnore
     private List<NotificationChannel> notificationChannel = List.of(NotificationChannel.WECHAT);
 
 

+ 13 - 0
snowy-web-app/src/main/java/vip/xiaonuo/weixin/miniapp/controller/WxPortalController.java

@@ -98,6 +98,19 @@ public class WxPortalController {
         throw new RuntimeException("不可识别的加密类型:" + encryptType);
     }
 
+    @GetMapping("/mini")
+    public String getMini(@PathVariable String appid,
+                          @RequestParam(name = "signature", required = false) String signature,
+                          @RequestParam(name = "timestamp", required = false) String timestamp,
+                          @RequestParam(name = "nonce", required = false) String nonce,
+                          @RequestParam(name = "code", required = false) String code,
+                          @RequestParam(name = "state", required = false) String state,
+                          @RequestParam(name = "echostr", required = false) String echostr) {
+        log.info("\n接收到来自微信服务器的认证消息:signature = [{}], timestamp = [{}], nonce = [{}], echostr = [{}], code = [{}], state = [{}]",
+            signature, timestamp, nonce, echostr,code,state);
+        return code;
+    }
+
     private void route(WxMaMessage message) {
         try {
             wxMaMessageRouter.route(message);