diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java index eb4430162f..77feb66b4e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java @@ -183,11 +183,13 @@ public class DeviceController { @GetMapping("/historyRecord") @Operation(summary = "历史记录查询") // @PreAuthorize("@ss.hasPermission('iot:device:query')") - public CommonResult>> historyRecord(@RequestParam("deviceId") Long deviceId, + public CommonResult>> historyRecord(@RequestParam("deviceId") Long deviceId, @RequestParam(name = "collectionStartTime", required = false) String collectionStartTime, - @RequestParam(name = "collectionEndTime", required = false) String collectionEndTime + @RequestParam(name = "collectionEndTime", required = false) String collectionEndTime, + @RequestParam(name = "pageNo", required = false, defaultValue = "1") Integer page, + @RequestParam(name = "pageSize", required = false, defaultValue = "10") Integer pageSize ) throws JsonProcessingException { - List> deviceContactModelDO=deviceService.historyRecord(deviceId,collectionStartTime,collectionEndTime); + PageResult> deviceContactModelDO=deviceService.historyRecord(deviceId,collectionStartTime,collectionEndTime,page,pageSize); return success(deviceContactModelDO); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java index 02e04e3f58..7589d79b4e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java @@ -123,7 +123,7 @@ public interface DeviceService { Map>> singleDevice(Long deviceId) throws JsonProcessingException; - List> historyRecord(Long deviceId,String collectionStartTime, String collectionEndTime); + PageResult> historyRecord(Long deviceId,String collectionStartTime, String collectionEndTime,Integer page,Integer size); Map> createDeviceDataMap(Long deviceId); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java index 88994ab243..9efc6a5529 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java @@ -108,6 +108,8 @@ public class DeviceServiceImpl implements DeviceService { @Resource private DeviceOperationRecordMapper deviceOperationRecordMapper; + + @Override @Transactional(rollbackFor = Exception.class) public DeviceDO createDevice(DeviceSaveReqVO createReqVO) { @@ -689,78 +691,107 @@ public class DeviceServiceImpl implements DeviceService { } @Override - public List> historyRecord(Long deviceId,String collectionStartTime, String collectionEndTime) { - + public PageResult> historyRecord(Long deviceId, String collectionStartTime, String collectionEndTime,Integer page, + Integer pageSize) { List> resultList = new ArrayList<>(); + if (deviceId == null) { + return null; + } try { // 1. 获取设备数据列表 - List> deviceDataList = tdengineService.getstDeviceDataOrderByTimeDesc(deviceId,collectionStartTime,collectionEndTime,null); + List> deviceDataList = tdengineService.getstDeviceDataOrderByTimeDescPage( + deviceId, collectionStartTime, collectionEndTime, page, pageSize + ); - // 2. 获取属性类型映射 + long total = tdengineService.queryDeviceDataTotal(deviceId, collectionStartTime, collectionEndTime); + + + if (deviceDataList.isEmpty()) { + return null; + } + + // 2. 获取属性类型映射 (ID -> Name) 并构建缓存,避免重复数据库查询 Map idToNameMap = deviceAttributeTypeMapper.selectList() .stream() - .collect(Collectors.toMap( - DeviceAttributeTypeDO::getId, - DeviceAttributeTypeDO::getName - )); + .collect(Collectors.toMap(DeviceAttributeTypeDO::getId, DeviceAttributeTypeDO::getName)); + + // 额外缓存:数字ID -> 名称,防止同一个ID多次查询数据库 + Map attributeTypeCache = new HashMap<>(); - // 3. 遍历每个时间点的数据 + ObjectMapper objectMapper = new ObjectMapper(); + + // 3. 遍历每条设备数据 for (Map deviceData : deviceDataList) { String queryDataJson = (String) deviceData.get("queryData"); Timestamp timestamp = (Timestamp) deviceData.get("timestamp"); - if (StringUtils.isNotBlank(queryDataJson) && timestamp != null) { - List> dataList = new ObjectMapper().readValue( - queryDataJson, - new TypeReference>>() {} - ); - - // 按属性类型分组 - Map>> groupedData = new LinkedHashMap<>(); + if (StringUtils.isBlank(queryDataJson) || timestamp == null) { + continue; // 跳过无效数据 + } - for (Map data : dataList) { - String attributeTypeName = "其他"; - String typeStr = (String) data.get("attributeType"); - if (typeStr != null) { - try { - attributeTypeName = typeStr; - } catch (Exception e) { - attributeTypeName = "未知"; - } - } + List> dataList = objectMapper.readValue( + queryDataJson, new TypeReference>>() {} + ); - Map simplifiedData = new HashMap<>(); - simplifiedData.put("addressValue", data.get("addressValue")); - simplifiedData.put("attributeName", data.get("attributeName")); + // 4. 按属性类型分组 + Map>> groupedData = new LinkedHashMap<>(); - groupedData - .computeIfAbsent(attributeTypeName, k -> new ArrayList<>()) - .add(simplifiedData); - } + for (Map data : dataList) { + String attributeTypeName = "其他"; + Object typeObj = data.get("attributeType"); - // 创建当前时间点的Map - Map timePointData = new LinkedHashMap<>(); + if (typeObj != null) { + String typeStr = typeObj.toString(); - // 添加属性分组 - for (Map.Entry>> entry : groupedData.entrySet()) { - timePointData.put(entry.getKey(), entry.getValue()); + try { + // 尝试当作数字 ID + Long typeId = Long.parseLong(typeStr); + + // 先从缓存查 + if (attributeTypeCache.containsKey(typeId)) { + attributeTypeName = attributeTypeCache.get(typeId); + } else { + // 查数据库 + String nameFromDb = idToNameMap.get(typeId); + if (nameFromDb != null) { + attributeTypeName = nameFromDb; + } else { + attributeTypeName = typeStr; // 查不到就用原值 + } + attributeTypeCache.put(typeId, attributeTypeName); + } + } catch (NumberFormatException e) { + // 不是数字,直接用原字符串 + attributeTypeName = typeStr; + } } - // 添加收集时间 - timePointData.put("collectTime", timestamp.toString()); + // 构建属性数据 Map + Map simplifiedData = new HashMap<>(); + simplifiedData.put("addressValue", data.get("addressValue")); + simplifiedData.put("attributeName", data.get("attributeName")); - resultList.add(timePointData); + groupedData.computeIfAbsent(attributeTypeName, k -> new ArrayList<>()) + .add(simplifiedData); } + + // 5. 构建时间点 Map + Map timePointData = new LinkedHashMap<>(groupedData); + timePointData.put("collectTime", timestamp.toString()); + + resultList.add(timePointData); } + return new PageResult<>(resultList, total); } catch (Exception e) { - System.out.println("处理设备数据时发生异常: " + e.getMessage()); + log.error("处理设备数据时发生异常", e); + return PageResult.empty(); + } + } - return resultList; - } private void validateDeviceAttributeExists(Long id) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/TDengineService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/TDengineService.java index 5f05f7ba92..588df44bed 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/TDengineService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/TDengineService.java @@ -266,6 +266,96 @@ public class TDengineService { } } + + /** + * 查询指定设备表的全部数据并按时间倒序排序 + * @param id 设备ID + * @return 设备数据列表,按时间戳倒序排列 + */ + @DS("tdengine") + public List> getstDeviceDataOrderByTimeDescPage(Long id,String StartTime, String EndTime,Integer page, + Integer pageSize) { + String tableName = "d_" + id; + StringBuilder sqlBuilder = new StringBuilder(); + List params = new ArrayList<>(); + + sqlBuilder.append("SELECT ts, query_data FROM besure.").append(tableName).append(" WHERE 1=1"); + + if (StartTime != null) { + // 直接将时间字符串拼接到SQL中 + sqlBuilder.append(" AND ts >= '").append(StartTime).append("'"); + } + + if (EndTime != null) { + sqlBuilder.append(" AND ts <= '").append(EndTime).append("'"); + } + + sqlBuilder.append(" ORDER BY ts DESC"); + + // 分页处理 + if (page != null && pageSize != null && page > 0 && pageSize > 0) { + int offset = (page - 1) * pageSize; + sqlBuilder.append(" LIMIT ").append(offset).append(",").append(pageSize); + } + + try { + return jdbcTemplate.query(sqlBuilder.toString(), new RowMapper>() { + @Override + public Map mapRow(ResultSet rs, int rowNum) throws SQLException { + Map result = new HashMap<>(); + result.put("timestamp", rs.getTimestamp("ts")); + result.put("deviceId", id); + + // 读取 query_data(二进制字段) + byte[] blob = rs.getBytes("query_data"); + result.put("queryData", decodeQueryData(blob)); + + return result; + } + }); + } catch (Exception e) { +// log.error("查询设备" + id + "的最新数据时发生异常", e); +// e.printStackTrace(); + return Collections.singletonList(createEmptyResult(id)); + } + } + + + /** + * 查询指定设备历史数据的总条数 + * + * @param deviceId 设备ID + * @param startTime 开始时间,可为空 + * @param endTime 结束时间,可为空 + * @return 总记录数 + */ + @DS("tdengine") + public long queryDeviceDataTotal(Long deviceId, String startTime, String endTime) { + if (deviceId == null) { + return 0L; + } + + String tableName = "d_" + deviceId; + StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM besure." + tableName + " WHERE 1=1"); + + if (startTime != null) { + countSql.append(" AND ts >= '").append(startTime).append("'"); + } + + if (endTime != null) { + countSql.append(" AND ts <= '").append(endTime).append("'"); + } + + try { + Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class); + return total != null ? total : 0L; + } catch (Exception e) { + log.error("查询设备 {} 总条数异常", deviceId, e); + return 0L; + } + } + + /** * 从 TDengine 中查询设备边缘数据 *