Browse Source

feat:新增适用于sims的获取采集设备历史数据接口
feat:新增导出传感器数据接口
feat:新增获取传感器数据方法
pref:优化获取报警次数最多的十条记录接口为获取近一个月报警次数最多的十条记录

黄渊昊 1 month ago
parent
commit
61088dbb75
15 changed files with 217 additions and 10 deletions
  1. 1 1
      snowy-plugin/snowy-plugin-coldchain/pom.xml
  2. 2 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/mapper/SensorAlarmMapper.java
  3. 2 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/mapper/mapping/SensorAlarmMapper.xml
  4. 4 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/SensorAlarmServiceImpl.java
  5. 8 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/controller/SimsController.java
  6. 13 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/dto/DeviceDataDto.java
  7. 1 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/param/MessagePageParam.java
  8. 22 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/param/SensorDataTransformer.java
  9. 57 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/service/AppDeviceService.java
  10. 41 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/service/SimsService.java
  11. 9 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitordevice/controller/MonitorDeviceController.java
  12. 11 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitordevice/mapper/MonitorDeviceMapper.java
  13. 0 4
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitordevice/mapper/mapping/MonitorDeviceMapper.xml
  14. 5 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitordevice/service/MonitorDeviceService.java
  15. 41 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitordevice/service/impl/MonitorDeviceServiceImpl.java

+ 1 - 1
snowy-plugin/snowy-plugin-coldchain/pom.xml

@@ -59,7 +59,7 @@
         <dependency>
             <groupId>com.github.jfcloud</groupId>
             <artifactId>jfcloud-boot-starter-influxdb</artifactId>
-            <version>K6.6.8</version>
+            <version>K7.0.0</version>
         </dependency>
         <dependency>
             <groupId>cn.dev33</groupId>

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

@@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Param;
 import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarm;
 import vip.xiaonuo.coldchain.modular.monitorsearchhistory.dto.TopWarningDto;
 
+import java.util.Date;
 import java.util.List;
 
 /**
@@ -19,5 +20,5 @@ public interface SensorAlarmMapper extends BaseMapper<SensorAlarm> {
     /**
      * 获取报警次数最多的十条记录
      */
-    List<TopWarningDto> getTop10Warning(@Param("orgId") String orgId, @Param("month") String month, @Param("types") List<String> types);
+    List<TopWarningDto> getTop10Warning(@Param("orgId") String orgId, @Param("month") String month, @Param("types") List<String> types, @Param("time") Date time);
 }

+ 2 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/mapper/mapping/SensorAlarmMapper.xml

@@ -15,7 +15,8 @@
       FROM sensor_alarm s
       left join monitor_target_region r
       on s.device_id = r.id
-      WHERE r.delete_flag = 'NOT_DELETE'
+      WHERE s.create_time > #{time}
+      AND r.delete_flag = 'NOT_DELETE'
       AND s.type in
       <foreach collection="types" item="type" open="(" separator="," close=")">
         #{type}

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

@@ -127,7 +127,10 @@ public class SensorAlarmServiceImpl extends ServiceImpl<SensorAlarmMapper, Senso
     @Override
     public List<TopWarningDto> topWarning(String month, List<String> types) {
         SaBaseLoginUser loginUser = StpLoginUserUtil.getLoginUser();
-        return getBaseMapper().getTop10Warning(loginUser.getOrgId(), month, types);
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(new Date());
+        calendar.add(Calendar.MONTH, -1);
+        return getBaseMapper().getTop10Warning(loginUser.getOrgId(), month, types, calendar.getTime());
     }
 
     public Page<SensorAlarm> getSensorAlarmPage(MessagePageParam messagePageParam) {

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

@@ -7,11 +7,14 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
+import vip.xiaonuo.coldchain.modular.app.dto.DeviceDataDto;
 import vip.xiaonuo.coldchain.modular.app.param.AppDeviceQueryParams;
 import vip.xiaonuo.coldchain.modular.app.param.SensorEchartDataResult;
 import vip.xiaonuo.coldchain.modular.app.service.SimsService;
 import vip.xiaonuo.common.pojo.CommonResult;
 
+import java.util.List;
+
 @Tag(name = "APP移动端控制器")
 @RestController
 @RequestMapping("/coldchain/api/sims")
@@ -33,6 +36,11 @@ public class SimsController {
         return CommonResult.data(simsService.queryDataByDeviceIdAndRoads(appDeviceQueryParams));
     }
 
+    @Operation(summary = "获取采集设备历史数据")
+    @PostMapping("/view/{deviceCode}/{rodas}")
+    public CommonResult<List<DeviceDataDto>> queryDeviceDataList(@RequestBody @Valid AppDeviceQueryParams appDeviceQueryParams) {
+        return CommonResult.data(simsService.queryDeviceDataList(appDeviceQueryParams));
+    }
 
 
 }

+ 13 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/app/dto/DeviceDataDto.java

@@ -0,0 +1,13 @@
+package vip.xiaonuo.coldchain.modular.app.dto;
+
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class DeviceDataDto {
+    private String temperature;
+    private String humidity;
+    private String co2;
+    private String time;
+}

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

@@ -50,7 +50,7 @@ public class MessagePageParam {
     private String sortOrder;
 
     /**
-     * 账号、姓名关键词
+     * 预警类型
      */
     @Schema(description = "system、alert")
     private String type = "alert";

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

@@ -5,6 +5,7 @@ import com.github.jfcloud.influxdb.util.InfluxDBDateFormatter;
 import lombok.experimental.UtilityClass;
 import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
 
+import java.text.SimpleDateFormat;
 import java.util.*;
 
 @UtilityClass
@@ -75,6 +76,27 @@ public class SensorDataTransformer {
         return new SensorEchartData(times, values);
     }
 
+    public SensorEchartData processSensorDataFormatTime(List<SensorData> sensorDataList, String dataType, AggregationWindow window) {
+        // 按时间升序排序
+        List<SensorData> sortedData = sensorDataList.stream().sorted(Comparator.comparing(SensorData::getTime)).toList();
+        // 提取时间和数值
+        LinkedHashSet<String> times = new LinkedHashSet<>();
+        List<Float> values = new ArrayList<>();
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        sortedData.stream().filter(data -> isValidData(data, dataType)).forEach(data -> {
+//            Instant plus = data.getTime().plus(8, ChronoUnit.HOURS);
+//            String format = sdf.format(plus.toEpochMilli());
+            String format = sdf.format(data.getTime().toEpochMilli());
+            String x = InfluxDBDateFormatter.formatTime(data.getTime(), window);
+            Float y = SensorDataTransformer.formatFloat(getDataValue(data, dataType));
+            if (!times.contains(x)) {
+                times.add(format);
+                values.add(y);
+            }
+        });
+        return new SensorEchartData(times, values);
+    }
+
     // 工具方法:判断是否为有效数据
     private boolean isValidData(SensorData data, String dataType) {
         if ("temperature".equals(dataType)) return data.getTemperature() != null;

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

@@ -361,6 +361,63 @@ public class AppDeviceService {
         return result;
     }
 
+    public SensorEchartDataResult queryDataByDeviceIdAndRoadsFormatTime(AppDeviceQueryParams appDeviceQueryParams) {
+        Assert.notNull(appDeviceQueryParams, "appDeviceQueryParams cannot be null");
+        // 获取用户选择的聚合维度,优先使用用户选择的聚合维度,如果没有选择则根据时间范围计算
+        AggregationWindow window = null;
+        String aggregationWindow = appDeviceQueryParams.getAggregationWindow();
+        if (StrUtil.isBlank(aggregationWindow)) {
+            window = AggregationWindow.fromString("1h");
+        } else {
+            // 如果用户选择了聚合维度,但不在枚举中,使用默认的聚合窗口
+            window = AggregationWindow.fromString(aggregationWindow);
+        }
+        // 查找监控目标区域
+        MonitorTargetRegion monitorTarget = monitorTargetRegionService.findOneByDeviceCodeAndSensorNo(appDeviceQueryParams.getSensorCode(), appDeviceQueryParams.getSensorRoute());
+        // 如果监控目标区域为空,直接返回空的结果
+        if (Objects.isNull(monitorTarget)) {
+            return getEmptyResult();
+        }
+        String sensorType = monitorTarget.getSensorType();
+        // 如果传感器类型为空,直接返回空的结果
+        if (StrUtil.isBlank(sensorType)) {
+            return getEmptyResult();
+        }
+        // 定义传感器类型与数据类型的映射关系
+        Map<String, String> sensorMapping = Map.of("w", "temperature", // 温度
+                "s", "humidity",    // 湿度
+                "c", "co2"          // 二氧化碳
+        );
+        // 创建结果对象
+        SensorEchartDataResult result = new SensorEchartDataResult();
+        // 遍历传感器类型映射
+        AggregationWindow finalWindow1 = window;
+        sensorMapping.forEach((key, dataType) -> {
+            // 如果传感器类型包含对应的字符
+            if (sensorType.toLowerCase().contains(key)) {
+                // 获取相应的数据
+                List<SensorData> sensorData = monitorDeviceService.queryDataByDeviceIdAndRoads(appDeviceQueryParams.getSensorCode(), appDeviceQueryParams.getSensorRoute(), appDeviceQueryParams.getStartTime(), appDeviceQueryParams.getEndTime(), dataType, finalWindow1);
+                // 处理数据并设置到对应的字段
+                SensorEchartData echartData = SensorDataTransformer.processSensorDataFormatTime(sensorData, dataType, finalWindow1);
+                if ("temperature".equals(dataType)) {
+                    echartData.setUp(monitorTarget.getTemperatureUp());
+                    echartData.setDown(monitorTarget.getTemperatureDown());
+                    result.setTemperature(echartData);
+
+                } else if ("humidity".equals(dataType)) {
+                    echartData.setUp(monitorTarget.getHumidityUp());
+                    echartData.setDown(monitorTarget.getHumidityDown());
+                    result.setHumidity(echartData);
+                } else if ("co2".equals(dataType)) {
+                    echartData.setUp(monitorTarget.getCo2Up());
+                    echartData.setDown(monitorTarget.getCo2Down());
+                    result.setCo2(echartData);
+                }
+            }
+        });
+        return result;
+    }
+
     // 辅助方法:返回空的结果对象
     private SensorEchartDataResult getEmptyResult() {
         return new SensorEchartDataResult(); // 默认构造一个空的结果对象

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

@@ -11,10 +11,17 @@ package vip.xiaonuo.coldchain.modular.app.service;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
+import vip.xiaonuo.coldchain.modular.app.dto.DeviceDataDto;
 import vip.xiaonuo.coldchain.modular.app.param.AppDeviceQueryParams;
+import vip.xiaonuo.coldchain.modular.app.param.SensorEchartData;
 import vip.xiaonuo.coldchain.modular.app.param.SensorEchartDataResult;
 import vip.xiaonuo.coldchain.modular.monitordevice.service.MonitorDeviceService;
 
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
 @Service
 @RequiredArgsConstructor
 public class SimsService {
@@ -28,4 +35,38 @@ public class SimsService {
     public SensorEchartDataResult queryDataByDeviceIdAndRoads(AppDeviceQueryParams appDeviceQueryParams) {
         return appDeviceService.queryDataByDeviceIdAndRoads(appDeviceQueryParams);
     }
+
+    public List<DeviceDataDto> queryDeviceDataList(AppDeviceQueryParams appDeviceQueryParams) {
+        SensorEchartDataResult sensorEchartDataResult = appDeviceService.queryDataByDeviceIdAndRoadsFormatTime(appDeviceQueryParams);
+        SensorEchartData temperature = sensorEchartDataResult.getTemperature();
+        SensorEchartData humidity = sensorEchartDataResult.getHumidity();
+        SensorEchartData co2 = sensorEchartDataResult.getCo2();
+        List<DeviceDataDto> deviceDataDtoList = new ArrayList<>();
+        Iterator<String> temperatureIterator = temperature.getX().iterator();
+        Iterator<String> humidityIterator = humidity.getX().iterator();
+        Iterator<String> co2Iterator = co2.getX().iterator();
+        DecimalFormat df = new DecimalFormat("#.00");
+        for (int i = 0; i < temperature.getY().size(); i++) {
+            DeviceDataDto deviceDataDto = new DeviceDataDto();
+            deviceDataDto.setTime(temperatureIterator.next());
+            temperatureIterator.remove();
+            deviceDataDto.setTemperature(df.format(temperature.getY().get(i)));
+            deviceDataDtoList.add(deviceDataDto);
+        }
+        for (int i = 0; i < humidity.getY().size(); i++) {
+            DeviceDataDto deviceDataDto = new DeviceDataDto();
+            deviceDataDto.setTime(humidityIterator.next());
+            humidityIterator.remove();
+            deviceDataDto.setHumidity(df.format(humidity.getY().get(i)));
+            deviceDataDtoList.add(deviceDataDto);
+        }
+        for (int i = 0; i < co2.getY().size(); i++) {
+            DeviceDataDto deviceDataDto = new DeviceDataDto();
+            deviceDataDto.setTime(co2Iterator.next());
+            co2Iterator.remove();
+            deviceDataDto.setCo2(df.format(co2.getY().get(i)));
+            deviceDataDtoList.add(deviceDataDto);
+        }
+        return deviceDataDtoList;
+    }
 }

+ 9 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitordevice/controller/MonitorDeviceController.java

@@ -206,4 +206,13 @@ public class MonitorDeviceController {
         return CommonResult.ok();
     }
 
+    /**
+     * 导出传感器数据
+     */
+    @Operation(summary = "导出冷链设备对应清单")
+    @GetMapping("/coldchain/monitordevice/export")
+    public void export(HttpServletResponse response) {
+        monitorDeviceService.export(response);
+    }
+
 }

+ 11 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitordevice/mapper/MonitorDeviceMapper.java

@@ -13,8 +13,12 @@
 package vip.xiaonuo.coldchain.modular.monitordevice.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Select;
 import vip.xiaonuo.coldchain.modular.monitordevice.entity.MonitorDevice;
 
+import java.util.List;
+import java.util.Map;
+
 /**
  * 采集器管理Mapper接口
  *
@@ -22,4 +26,11 @@ import vip.xiaonuo.coldchain.modular.monitordevice.entity.MonitorDevice;
  * @date 2024/11/13 16:55
  **/
 public interface MonitorDeviceMapper extends BaseMapper<MonitorDevice> {
+    @Select("SELECT d.device_code as '设备终端地址', d.mac as 'MAC地址', d.model_name as '设备型号', r.name as '设备名称' " +
+            "FROM monitor_target_region r " +
+            "LEFT JOIN monitor_device d ON r.device_code = d.device_code " +
+            "WHERE d.CREATE_ORG = '1863397928059453442' " +
+            "AND r.DELETE_FLAG = 'NOT_DELETE' " +
+            "AND d.DELETE_FLAG = 'NOT_DELETE'")
+    List<Map<String, Object>> selectDeviceWithRegion();
 }

+ 0 - 4
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitordevice/mapper/mapping/MonitorDeviceMapper.xml

@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="vip.xiaonuo.coldchain.modular.monitordevice.mapper.MonitorDeviceMapper">
-</mapper>

+ 5 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitordevice/service/MonitorDeviceService.java

@@ -141,4 +141,9 @@ public interface MonitorDeviceService extends IService<MonitorDevice> {
     List<MonitorDevice> getMonitorDeviceByPowerCode(String powerCode);
 
     AppParametersParam getParameters(String deviceCode, Integer sensorRoute);
+
+    /**
+     * 导出传感器数据
+     */
+    void export(HttpServletResponse response);
 }

+ 41 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitordevice/service/impl/MonitorDeviceServiceImpl.java

@@ -19,6 +19,7 @@ import cn.hutool.core.util.ReUtil;
 import cn.hutool.core.util.StrUtil;
 import com.alibaba.excel.EasyExcel;
 import com.alibaba.excel.support.ExcelTypeEnum;
+import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -26,6 +27,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.github.jfcloud.influxdb.flux.AggregationWindow;
 import com.github.jfcloud.influxdb.flux.QueryCondition;
 import jakarta.annotation.Resource;
+import jakarta.servlet.ServletOutputStream;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -45,7 +47,6 @@ import vip.xiaonuo.coldchain.core.config.JfcloudRedisCacheService;
 import vip.xiaonuo.coldchain.core.renke.RenKeService;
 import vip.xiaonuo.coldchain.core.service.JfcloudSensorDataService;
 import vip.xiaonuo.coldchain.modular.app.param.AppParametersParam;
-import vip.xiaonuo.coldchain.modular.app.param.MessagePageParam;
 import vip.xiaonuo.coldchain.modular.monitordevice.config.CopyOptionsConfig;
 import vip.xiaonuo.coldchain.modular.monitordevice.dto.MonitorDevicePageDto;
 import vip.xiaonuo.coldchain.modular.monitordevice.entity.MonitorDevice;
@@ -103,6 +104,9 @@ public class MonitorDeviceServiceImpl extends ServiceImpl<MonitorDeviceMapper, M
     private MonitorTargetService monitorTargetService;
     @Resource
     JfcloudRedisCacheService jfcloudRedisCacheService;
+    @Resource
+    MonitorDeviceMapper monitorDeviceMapper;
+
     private String bucketName;
 
     @Override
@@ -570,4 +574,40 @@ public class MonitorDeviceServiceImpl extends ServiceImpl<MonitorDeviceMapper, M
         return appParametersParam;
     }
 
+    @Override
+    public void export(HttpServletResponse response) {
+        List<Map<String, Object>> data = getExportData();
+
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setHeader("Content-Disposition", "attachment;filename=device.xlsx");
+
+        try (ServletOutputStream out = response.getOutputStream()) {
+            // 动态生成表头
+            List<List<String>> head = data.isEmpty() ? Collections.emptyList() :
+                    data.get(0).keySet().stream()
+                            .map(Collections::singletonList)
+                            .collect(Collectors.toList());
+
+            // 写入数据
+            EasyExcel.write(out)
+                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
+                    .head(head)
+                    .sheet("设备列表")
+                    .doWrite(data.stream()
+                            .map(map -> new ArrayList<>(map.values()))
+                            .collect(Collectors.toList()));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private List<Map<String, Object>> getExportData() {
+        List<Map<String, Object>> dataList = monitorDeviceMapper.selectDeviceWithRegion();
+        // 动态处理空值(参考网页1[1](@ref))
+        return dataList.stream().map(map -> {
+            map.replaceAll((k, v) -> v == null ? "" : v);
+            return map;
+        }).collect(Collectors.toList());
+    }
+
 }