Ver código fonte

feat(增加统计设备在线时长,数据条数,预警条数数据接口,修复导出co2异常): 增加统计设备在线时长,数据条数,预警条数数据接口,修复导出co2异常

增加统计设备在线时长,数据条数,预警条数数据接口,修复导出co2异常
zjian 1 dia atrás
pai
commit
999e3f5151

+ 82 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/service/JfcloudSensorDataService.java

@@ -36,6 +36,87 @@ public class JfcloudSensorDataService extends JfcloudFluxDataService<SensorData>
         this.sensorAlarmService = sensorAlarmService;
     }
 
+    /**
+     * 根据 deviceId 和 roads 查询最旧的数据
+     *
+     * @param deviceId 设备 ID
+     * @param roads    路数
+     * @return 最新的传感器数据
+     */
+    public SensorData queryFirstDataTimeByDeviceIdAndRoads(String startDate, String endDate, String deviceId, Integer roads) {
+        QueryApi queryApi = jfcloudInfluxDBService.getInfluxDBClient().getQueryApi();
+        //按时间正序查询且只取一条
+        String query = String.format("""
+                from(bucket: "%s")
+                            |> range(start: %s, stop: %s)
+                            |> filter(fn: (r) => r._measurement == "%s")
+                            |> filter(fn: (r) => r["device_id"] == "%s" and r["roads"] == "%s")
+                            |> sort(columns: ["_time"], desc: false)
+                            |> limit(n: 1)
+                  """, getBucketName(), startDate, endDate, JfcloudColdChainConstants.INFLUXDB_DEFAULT_MEASUREMENT_NAME, deviceId, roads);
+        List<FluxTable> tables = queryApi.query(query);
+
+        // 将查询结果映射为 SensorData 实体
+        return tables.stream().flatMap(table -> table.getRecords().stream())  // 拉平所有记录
+                .map(fluxRecord -> mapRecordToEntity(fluxRecord, getEntityClass()))  // 将每个记录映射为 SensorData 实体
+                .findFirst()  // 只取第一条记录(即最新一条记录)
+                .orElse(null);  // 如果没有数据,返回 null
+    }
+
+    /**
+     * 获取传感器数据全部指标计数
+     *
+     * @param startDate 开始日期
+     * @param endDate   结束日期
+     * @param deviceId  设备标识符
+     * @param roads     道路
+     * @return long
+     */
+    public long getSensorDataAllCount(String startDate, String endDate, String deviceId, String roads) {
+        long count = 0;
+        try {
+            StringBuilder sb =new StringBuilder();
+            //需要的指标
+            String fields = "humidity, co2, pm25, pm10, no2, o3,  o2, so2, tvoc, co, h2, ch2o, h2s, db, pressure, illuminance, ch4, nh3, odor, tsp, smoke, n2, plugInStatus, battery";
+            for (String field : fields.split(", ")) {
+                sb.append(String.format("""
+                        or r["_field"] == "%s"
+                        """,field));
+            }
+            // 构建Flux查询,简化了不必要的group操作
+            String query = String.format("""
+                    from(bucket: "%s")
+                      |> range(start: %s, stop: %s)
+                      |> filter(fn: (r) => r["_measurement"] == "%s")
+                      |> filter(fn: (r) => r["device_id"] == "%s" and r["roads"] == "%s")
+                      |> filter(fn: (r) => r["_field"] == "humidity" %s)
+                      |> count()
+                    """, getBucketName(), startDate, endDate, JfcloudColdChainConstants.INFLUXDB_DEFAULT_MEASUREMENT_NAME, deviceId, roads,sb.toString());
+
+            // 获取QueryApi并执行查询
+            QueryApi queryApi = jfcloudInfluxDBService.getInfluxDBClient().getQueryApi();
+            List<FluxTable> result = queryApi.query(query);
+
+            // 处理查询结果
+            if (result != null && !result.isEmpty()) {
+                for (FluxTable fluxTable : result) {
+                    if (fluxTable.getRecords() != null && !fluxTable.getRecords().isEmpty()) {
+                        // 获取结果值
+                        Object countValue = fluxTable.getRecords().get(0).getValues().get("_value");
+                        if (countValue != null) {
+                            // 确保转换为正确的数字类型
+                            if (countValue instanceof Number) {
+                                count += ((Number) countValue).longValue();  // 类型安全转换
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace(); // 打印异常信息
+        }
+        return count; // 返回统计结果
+    }
 
     /**
      * 根据 deviceId 和 roads 查询最新的数据
@@ -324,7 +405,7 @@ public class JfcloudSensorDataService extends JfcloudFluxDataService<SensorData>
      * @param roads     道路字段
      * @return 数据总数
      */
-    private long getSensorDataCount(String startDate, String endDate, String deviceId, String roads) {
+    public long getSensorDataCount(String startDate, String endDate, String deviceId, String roads) {
         long count = 0;
         try {
             // 构建Flux查询,简化了不必要的group操作

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

@@ -3,6 +3,7 @@ package vip.xiaonuo.coldchain.modular.app.controller;
 import cn.hutool.core.io.resource.InputStreamResource;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.github.jfcloud.influxdb.flux.AggregationWindow;
 import io.swagger.v3.oas.annotations.Operation;
@@ -190,4 +191,10 @@ public class AppController {
                 .collect(Collectors.toList());
         return CommonResult.data(datas);
     }
+
+    @Operation(summary = "设备指标-数据统计")
+    @GetMapping("/device/getRecordCount")
+    public CommonResult<JSONObject> getRecordCount(@RequestParam Long deviceId, @RequestParam String roads) {
+        return CommonResult.data(appDeviceService.getRecordCount(deviceId,roads));
+    }
 }

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

@@ -10,9 +10,12 @@ package vip.xiaonuo.coldchain.modular.app.service;
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.date.LocalDateTimeUtil;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.github.jfcloud.influxdb.flux.AggregationWindow;
@@ -37,6 +40,9 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
 import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarm;
+import vip.xiaonuo.coldchain.core.alarm.enums.SensorAlarmType;
+import vip.xiaonuo.coldchain.core.alarm.service.SensorAlarmService;
 import vip.xiaonuo.coldchain.core.alarm.service.delay.DeviceAlertDelayService;
 import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
 import vip.xiaonuo.coldchain.core.service.JfcloudSensorDataService;
@@ -44,6 +50,7 @@ import vip.xiaonuo.coldchain.modular.app.dto.AppInfo;
 import vip.xiaonuo.coldchain.modular.app.param.*;
 import vip.xiaonuo.coldchain.modular.app.param.export.AppTrendParam;
 import vip.xiaonuo.coldchain.modular.app.param.export.ExportParam;
+import vip.xiaonuo.coldchain.modular.monitordevice.entity.MonitorDevice;
 import vip.xiaonuo.coldchain.modular.monitordevice.service.MonitorDeviceService;
 import vip.xiaonuo.coldchain.modular.monitortarget.entity.MonitorTarget;
 import vip.xiaonuo.coldchain.modular.monitortarget.param.MonitorTargetPageParam;
@@ -63,6 +70,11 @@ import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.text.DecimalFormat;
 import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
 import java.util.List;
 import java.util.*;
 import java.util.concurrent.CompletableFuture;
@@ -82,6 +94,8 @@ public class AppDeviceService {
     private final JfcloudSensorDataService jfcloudSensorDataService;
     private final MonitorDeviceService monitorDeviceService;
     private final DeviceAlertDelayService delayService;
+
+    private final SensorAlarmService sensorAlarmService;
     @Resource
     DevConfigService devConfigService;
 
@@ -1233,9 +1247,9 @@ public class AppDeviceService {
                 String avgCo2 = (co2Stats != null && co2Stats.length > 2 && co2Stats[2] != null)
                         ? TEMP_FORMAT.format(co2Stats[2]) + "ppm" : "";
 
-                addTableRow(recordTable, "开始时间:", trendParam.getStartTime(), "最高CO₂浓度:", maxCo2);
-                addTableRow(recordTable, "结束时间:", trendParam.getEndTime(), "最低CO₂浓度:", minCo2);
-                addTableRow(recordTable, "", "", "平均CO₂浓度:", avgCo2);
+                addTableRow(recordTable, "开始时间:", trendParam.getStartTime(), "最高co2浓度:", maxCo2);
+                addTableRow(recordTable, "结束时间:", trendParam.getEndTime(), "最低co2浓度:", minCo2);
+                addTableRow(recordTable, "", "", "平均co2浓度:", avgCo2);
             }
             if (sensorType.contains("D")) {
                 // 温度处理
@@ -1437,9 +1451,9 @@ public class AppDeviceService {
                 }
 
                 if (co2Stats != null && !Arrays.stream(co2Stats).allMatch(Objects::isNull)) {
-                    addTableRow(recordTable, "开始时间:", trendParam.getStartTime(), "最高CO2:", maxCo2);
-                    addTableRow(recordTable, "结束时间:", trendParam.getEndTime(), "最低CO2:", minCo2);
-                    addTableRow(recordTable, "", "", "平均CO2:", avgCo2);
+                    addTableRow(recordTable, "开始时间:", trendParam.getStartTime(), "最高co2:", maxCo2);
+                    addTableRow(recordTable, "结束时间:", trendParam.getEndTime(), "最低co2:", minCo2);
+                    addTableRow(recordTable, "", "", "平均co2:", avgCo2);
                 }
 
                 if (pm25Stats != null && !Arrays.stream(pm25Stats).allMatch(Objects::isNull)) {
@@ -1906,4 +1920,27 @@ public class AppDeviceService {
         }
         return appInfo;
     }
+
+
+
+    public JSONObject getRecordCount(Long deviceId, String roads) {
+        MonitorDevice monitorDevice = monitorDeviceService.getById(deviceId);
+        //统计时间范围
+        String startTime ="1997-01-01T00:00:00Z";
+        String endTime= LocalDate.now().format(DateTimeFormatter.ISO_DATE) + "T23:59:59Z";
+        //记录总数
+        long sensorDataCount = jfcloudSensorDataService.getSensorDataAllCount(startTime, endTime, monitorDevice.getDeviceCode(), roads);
+        //在线时长
+        SensorData startData = jfcloudSensorDataService.queryFirstDataTimeByDeviceIdAndRoads(startTime, endTime,monitorDevice.getDeviceCode(), Integer.valueOf(roads));
+        SensorData endData = jfcloudSensorDataService.queryLatestDataByDeviceIdAndRoads(monitorDevice.getDeviceCode(), Integer.valueOf(roads));
+        //预警次数
+        long alarmCount = 0;
+        LocalDateTime startUtc = startData.getTime().atZone(ZoneId.of("UTC+8")).toLocalDateTime();
+        LocalDateTime endUtc = endData.getTime().atZone(ZoneId.of("UTC+8")).toLocalDateTime();
+        JSONObject obj = new JSONObject()
+                .fluentPut("recordCount", sensorDataCount)
+                .fluentPut("onlineDay", LocalDateTimeUtil.between(startUtc,endUtc, ChronoUnit.DAYS))
+                .fluentPut("alarmCount", alarmCount);
+        return obj;
+    }
 }

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

@@ -27,6 +27,7 @@ import vip.xiaonuo.coldchain.core.handler.NotificationChannelListTypeHandler;
 import vip.xiaonuo.coldchain.core.handler.SensorAlarmUserTypeHandler;
 import vip.xiaonuo.common.pojo.OrgEntity;
 
+import java.io.Serial;
 import java.util.List;
 
 /**
@@ -39,6 +40,8 @@ import java.util.List;
 @Setter
 @TableName(value = "monitor_target_region", autoResultMap = true)
 public class MonitorTargetRegion extends OrgEntity {
+    @Serial
+    private static final long serialVersionUID = -7159395446785296905L;
     /**
      * 区域唯一标识符,UUID
      */