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 273780cfe..57ab7277e 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 @@ -2883,6 +2883,7 @@ public class TDengineService { List params = new ArrayList<>(); sql.append("SELECT ") + .append("TO_CHAR(_wstart, 'YYYY-MM-DD HH24:MI:SS') AS hourTime, ") .append("TO_CHAR(_wstart, 'HH24') AS hourValue, ") .append("SUM(CASE WHEN rule = '0' THEN 1 ELSE 0 END) * 60 AS totalOfflineTime, ") .append("SUM(CASE WHEN rule = '1' THEN 1 ELSE 0 END) * 60 AS totalRunningTime, ") @@ -2896,7 +2897,7 @@ public class TDengineService { params.add(startTime); } if (endTime != null && !endTime.trim().isEmpty()) { - sql.append("AND create_time <= ? "); + sql.append("AND create_time < ? "); params.add(endTime); } if (deviceIds != null && !deviceIds.isEmpty()) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordServiceImpl.java index 50eca9986..f5a7f98b0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/deviceoperationrecord/DeviceOperationRecordServiceImpl.java @@ -472,7 +472,7 @@ public class DeviceOperationRecordServiceImpl implements DeviceOperationRecordSe List deviceIds = parseIds(pageReqVO.getIds()); List> hourlyRows = tDengineService.queryDeviceHourTrend(deviceIds, pageReqVO.getStartTime(), pageReqVO.getEndTime()); - respVO.setHourlyStatus(buildHourlyStatus(hourlyRows)); + respVO.setHourlyStatus(buildHourlyStatus(hourlyRows, pageReqVO.getStartTime(), pageReqVO.getEndTime())); //设备运行轨迹 List timelineRows = buildTimelineRows(records, deviceIds, pageReqVO.getStartTime(), pageReqVO.getEndTime()); @@ -500,19 +500,13 @@ public class DeviceOperationRecordServiceImpl implements DeviceOperationRecordSe .collect(Collectors.toList()); } - private List buildHourlyStatus(List> hourlyRows) { - Map bucketMap = new LinkedHashMap<>(); - for (int i = 0; i < 24; i++) { - String hour = String.format("%02d:00", i); - bucketMap.put(hour, new HourBucket(hour)); - } + private List buildHourlyStatus(List> hourlyRows, + String startTime, + String endTime) { + Map bucketMap = buildLast24HourBuckets(startTime, endTime); for (Map row : hourlyRows) { - Integer hourValue = getIntValue(row, "hourValue"); - if (hourValue == null || hourValue < 0 || hourValue > 23) { - continue; - } - String hour = String.format("%02d:00", hourValue); + String hour = resolveHourBucketKey(row); HourBucket bucket = bucketMap.get(hour); if (bucket == null) { continue; @@ -535,6 +529,49 @@ public class DeviceOperationRecordServiceImpl implements DeviceOperationRecordSe }).collect(Collectors.toList()); } + private Map buildLast24HourBuckets(String startTime, String endTime) { + Map bucketMap = new LinkedHashMap<>(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND); + try { + LocalDateTime end = LocalDateTime.parse(endTime, formatter).withMinute(0).withSecond(0).withNano(0); + LocalDateTime firstBucketEnd = end.minusHours(23); + for (int i = 0; i < 24; i++) { + String hour = firstBucketEnd.plusHours(i).format(formatter); + bucketMap.put(hour, new HourBucket(hour)); + } + return bucketMap; + } catch (Exception ignored) { + for (int i = 0; i < 24; i++) { + String hour = String.format("%02d:00", i); + bucketMap.put(hour, new HourBucket(hour)); + } + return bucketMap; + } + } + + private String resolveHourBucketKey(Map row) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND); + String hourTime = getStringValue(row, "hourTime"); + if (StringUtils.isNotBlank(hourTime)) { + try { + return LocalDateTime.parse(hourTime, formatter) + .plusHours(1) + .withMinute(0) + .withSecond(0) + .withNano(0) + .format(formatter); + } catch (Exception ignored) { + // Fallback to hourValue for old TDengine format compatibility. + } + } + + Integer hourValue = getIntValue(row, "hourValue"); + if (hourValue == null || hourValue < 0 || hourValue > 23) { + return null; + } + return String.format("%02d:00", hourValue); + } + private double toPercent(double numerator, double denominator) { return denominator > 0 ? round2(numerator / denominator * 100) : 0D; } @@ -738,6 +775,14 @@ public class DeviceOperationRecordServiceImpl implements DeviceOperationRecordSe } } + private String getStringValue(Map map, String key) { + Object value = map.get(key); + if (value == null) { + value = map.get(key.toLowerCase()); + } + return value == null ? null : String.valueOf(value); + } + private static class HourBucket { private final String hour; private double running;