diff --git a/dongjian-dashboard-back-service/src/main/java/com/dongjian/dashboard/back/service/common/CommonOpt.java b/dongjian-dashboard-back-service/src/main/java/com/dongjian/dashboard/back/service/common/CommonOpt.java index 3c9143b..92d030a 100644 --- a/dongjian-dashboard-back-service/src/main/java/com/dongjian/dashboard/back/service/common/CommonOpt.java +++ b/dongjian-dashboard-back-service/src/main/java/com/dongjian/dashboard/back/service/common/CommonOpt.java @@ -302,7 +302,7 @@ public class CommonOpt { if (searchType == 1) { processAccumulateDay(conn, yesterday, yesterdayBase, "yesterday", acc, deviceInfo); processAccumulateDay(conn, lastYear, lastYearBase, "lastYear", acc, deviceInfo); - LineDataAggregator.convertToLineDataHour(acc, lineData); + LineDataAggregator.convertToAccumulateLineData(acc, lineData); } else { LineDataAggregator.convertToLineDataAll(acc, lineData); } @@ -471,39 +471,77 @@ public class CommonOpt { List dateList = getPreDay(1); for (String date : dateList) { - LocalDate localDate = LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy_MM_dd")); - - String sql = "SELECT upload_value, upload_at FROM dashboard_record_measure " + - "WHERE device_id = ? AND attr_code = ? AND date_year = ? AND date_month = ? AND date_day = ? ORDER BY id"; - - try (PreparedStatement ps = conn.prepareStatement(sql)) { - ps.setString(1, lineDataSearchParams.getDeviceId()); - ps.setString(2, lineData.getAttrCode()); - ps.setInt(3, localDate.getYear()); - ps.setInt(4, localDate.getMonthValue()); - ps.setInt(5, localDate.getDayOfMonth()); - - try (ResultSet rs = ps.executeQuery()) { - if (rs.next()) { - processResult(rs, lineData, deviceInfo); - } - } - } catch (Exception e) { - logger.error("process measure data error", e); + LocalDate today = LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy_MM_dd")); + LocalDate yesterday = today.minusDays(1); + LocalDate lastYear = DateUtil.getLastYearSameIsoWeekDay(today); + + SubLineData sub = new SubLineData(); + sub.setAttrCode(lineData.getAttrCode()); + + processMeasureDay(conn, today, "today", sub, deviceInfo); + if (searchType == 1) { + processMeasureDay(conn, yesterday, "yesterday", sub, deviceInfo); + processMeasureDay(conn, lastYear, "lastYear", sub, deviceInfo); + LineDataAggregator.convertToMeasureLineDataAvg(sub, lineData); + } else { + LineDataAggregator.convertToLineDataAll(sub, lineData); } } }); - if (searchType == 1) { - list.forEach(ld -> { - LineDataAggregator.aggregateAverageByHalfHour(ld); - LineDataAggregator.fillTo48HalfHour(ld); - }); + return list; + } + + private void processMeasureDay( + Connection conn, + LocalDate date, + String yKey, + SubLineData sub, + DeviceInfo deviceInfo + ) { + SubLineData.childLineData child = new SubLineData.childLineData(); + + String sql = "SELECT upload_value, upload_at FROM dashboard_record_measure " + + "WHERE device_id = ? AND attr_code = ? " + + "AND date_year = ? AND date_month = ? AND date_day = ? ORDER BY id"; + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setString(1, deviceInfo.getDeviceId()); + ps.setString(2, sub.getAttrCode()); + ps.setInt(3, date.getYear()); + ps.setInt(4, date.getMonthValue()); + ps.setInt(5, date.getDayOfMonth()); + + try (ResultSet rs = ps.executeQuery()) { + DateTimeFormatter formatter = Constants.FORMATTER_YMDHMS; + + while (rs.next()) { + long ts = rs.getLong("upload_at"); + String value = rs.getString("upload_value"); + if (ts == 0) continue; + + String time = Instant.ofEpochMilli(ts) + .atZone(Constants.ZONE_TOKYO) + .toLocalDateTime() + .format(formatter); + + child.getXData().add(time); + child.getYData().add( + CommonUtil.formatDecimal( + StringUtils.isBlank(value) ? "0" : value, + deviceInfo.getDashboardDecimalPlaces() + ) + ); + } + } + } catch (Exception e) { + logger.error("processMeasureDay error", e); } - return list; + sub.getSubData().put(yKey, child); } + private void processResult(ResultSet result, LineData lineData, DeviceInfo deviceInfo) { try { // 用于存储 xData 和 yData diff --git a/dongjian-dashboard-back-service/src/main/java/com/dongjian/dashboard/back/service/common/LineDataAggregator.java b/dongjian-dashboard-back-service/src/main/java/com/dongjian/dashboard/back/service/common/LineDataAggregator.java index db4961d..4ea82a7 100644 --- a/dongjian-dashboard-back-service/src/main/java/com/dongjian/dashboard/back/service/common/LineDataAggregator.java +++ b/dongjian-dashboard-back-service/src/main/java/com/dongjian/dashboard/back/service/common/LineDataAggregator.java @@ -149,80 +149,100 @@ public class LineDataAggregator { return LocalDateTime.parse(String.valueOf(x), TimeAxisUtil.DATETIME_FMT); } - /* =============================== - * 按小时聚合(平均值) - * =============================== */ - public static void aggregateByHour(LineData lineData) { - aggregateByHourOrHalfHour(lineData, new AvgCollector(), true); - } + public static void convertToMeasureLineDataAvg(SubLineData accumulate, LineData lineData) { + if (accumulate == null || accumulate.getSubData() == null) return; - /* =============================== - * 按半小时聚合(平均值) - * =============================== */ - public static void aggregateAverageByHalfHour(LineData lineData) { - aggregateByHourOrHalfHour(lineData, new AvgCollector(), false); + // 1. 每个 series 做「半小时平均」 + Map seriesMap = new LinkedHashMap<>(); + for (Map.Entry entry : accumulate.getSubData().entrySet()) { + seriesMap.put(entry.getKey(), aggregateAvgByHalfHour(entry.getValue())); + } + + // 2. today 作为 X 轴基准 + HalfHourSeries today = seriesMap.get("today"); + if (today == null) { + throw new IllegalStateException("subData 必须包含 today"); + } + + // 3. 构建最终 X(yyyy-MM-dd HH:mm:00) + List finalX = new ArrayList<>(); + for (String hm : today.xList) { + finalX.add(today.date + " " + hm + ":00"); + } + + // 4. 构建临时 map,用 today.xList 对齐 + Map> tmpMap = new HashMap<>(); + for (Map.Entry entry : seriesMap.entrySet()) { + tmpMap.put(entry.getKey(), entry.getValue().toMap()); + } + + // 5. 写入 LineData + lineData.setXData(finalX); + lineData.setYData(LineDataAligner.alignY(today.xList, tmpMap)); + lineData.setAttrCode(accumulate.getAttrCode()); + + // 6. 补齐 48 半小时 + fillTo48HalfHour(lineData); } - /* 公共聚合方法 */ - private static void aggregateByHourOrHalfHour( - LineData lineData, - HalfHourCollector collector, - boolean byHour - ) { - if (lineData == null) return; + private static HalfHourSeries aggregateAvgByHalfHour(SubLineData.childLineData child) { + if (child == null || child.getXData() == null || child.getYData() == null) { + return new HalfHourSeries(null); + } - List xData = lineData.getXData(); - Map> yDataMap = lineData.getYData(); - if (xData == null || xData.isEmpty() || yDataMap == null || yDataMap.isEmpty()) return; + Map> bucket = new HashMap<>(); + String date = null; - Set allSeriesKeys = yDataMap.keySet(); + int size = Math.min(child.getXData().size(), child.getYData().size()); - Map> bucket = new TreeMap<>(); - for (int i = 0; i < xData.size(); i++) { - LocalDateTime time = parseTime(xData.get(i)); - String key = byHour ? TimeAxisUtil.hourKey(time) : TimeAxisUtil.halfHourKey(time); + for (int i = 0; i < size; i++) { + Object x = child.getXData().get(i); + Object y = child.getYData().get(i); + if (x == null || y == null) continue; + + LocalDateTime time; + try { + time = LocalDateTime.parse(x.toString(), TimeAxisUtil.DATETIME_FMT); + } catch (Exception e) { + continue; + } - for (String series : allSeriesKeys) { - Object yVal = safeGet(yDataMap.get(series), i); - if (yVal == null) continue; + if (date == null) { + date = time.toLocalDate().toString(); + } - bucket.computeIfAbsent(key, k -> new HashMap<>()); - collector.collect(bucket.get(key), series, time, yVal); + double num; + try { + num = Double.parseDouble(y.toString()); + } catch (Exception e) { + // 非数字直接丢 + continue; } + + String hm = TimeAxisUtil.alignToHalfHour(time.toLocalTime()); // HH:mm + bucket.computeIfAbsent(hm, k -> new ArrayList<>()).add(num); } - // 构建临时 Map 用于统一对齐 - Map> tmpMap = new HashMap<>(); - boolean isAvg = collector instanceof AvgCollector; - - for (Map.Entry> entry : bucket.entrySet()) { - String xKey = entry.getKey(); - Map seriesMap = entry.getValue(); - for (String series : allSeriesKeys) { - Object val; - T tVal = seriesMap.get(series); - if (isAvg && tVal instanceof List) { - List list = (List) tVal; - val = list.stream().mapToDouble(Double::doubleValue).average().orElse(0); - } else if (!isAvg && tVal instanceof TimeValue) { - val = ((TimeValue) tVal).value; - } else { - val = tVal; - } - tmpMap.computeIfAbsent(series, k -> new HashMap<>()).put(xKey, val); - } + List sortedHM = new ArrayList<>(bucket.keySet()); + Collections.sort(sortedHM); + + List values = new ArrayList<>(); + for (String hm : sortedHM) { + List list = bucket.get(hm); + double avg = list.stream().mapToDouble(Double::doubleValue).average().orElse(0D); + values.add(avg); } - List newX = new ArrayList<>(bucket.keySet()); - lineData.setXData(newX); - lineData.setYData(LineDataAligner.alignY(newX, tmpMap)); + return new HalfHourSeries(date, sortedHM, values); } + + /* =============================== * SubLineData -> LineData * 半小时取最后一条 * =============================== */ - public static void convertToLineDataHour(SubLineData accumulate, LineData lineData) { + public static void convertToAccumulateLineData(SubLineData accumulate, LineData lineData) { if (accumulate == null || accumulate.getSubData() == null) return; Map seriesMap = new LinkedHashMap<>();