From c6202c6b72613e714a7b32717259b2f8ad532596 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期三, 02 九月 2026 20:38:57 +0800
Subject: [PATCH] 按设备时间归属采集器工步观测

---
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestDataQueryServiceImpl.java |  105 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 105 insertions(+), 0 deletions(-)

diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestDataQueryServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestDataQueryServiceImpl.java
index b8fcfd2..8f4b5dc 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestDataQueryServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestDataQueryServiceImpl.java
@@ -1,6 +1,13 @@
 package com.ruoyi.system.service.impl;
 
 import java.util.List;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.HashMap;
@@ -33,6 +40,7 @@
         List<Map<String, Object>> collector = mapper.selectPersistedCollectorRecords(testId);
         if (cabinet != null) source.addAll(cabinet);
         if (collector != null) source.addAll(collector);
+        attachCollectorStepContext(source);
         return applyEffectiveData(testId, "REALTIME", source);
     }
 
@@ -116,6 +124,103 @@
         return "cycle:" + channelIdentity + ":" + cycleNo;
     }
 
+    /** 按检测柜实际工步的设备时间边界给采集器样本补充筛选上下文,不配对采样点。 */
+    private void attachCollectorStepContext(List<Map<String, Object>> rows)
+    {
+        List<StepWindow> windows = new ArrayList<>();
+        Map<String, StepWindowBuilder> builders = new LinkedHashMap<>();
+        for (Map<String, Object> row : rows)
+        {
+            if ("COLLECTOR".equals(String.valueOf(row.get("sourceType")))) continue;
+            Instant time = toInstant(row.get("deviceTime"));
+            if (time == null || row.get("stepNo") == null) continue;
+            String key = String.join("|", text(row, "batteryNo"), text(row, "cycleNo"), text(row, "stepNo"));
+            StepWindowBuilder builder = builders.computeIfAbsent(key, ignored -> new StepWindowBuilder(row, time));
+            builder.start = builder.start.isAfter(time) ? time : builder.start;
+            builder.end = builder.end == null || builder.end.isBefore(time) ? time : builder.end;
+        }
+        for (StepWindowBuilder builder : builders.values()) windows.add(builder.build());
+        windows.sort(Comparator.comparing(StepWindow::start));
+        for (int index = 0; index < windows.size(); index++)
+        {
+            StepWindow current = windows.get(index);
+            Instant nextStart = windows.stream()
+                    .filter(window -> window.batteryNo().equals(current.batteryNo()) && window.start().isAfter(current.start()))
+                    .map(StepWindow::start).min(Comparator.naturalOrder()).orElse(null);
+            windows.set(index, current.withNextStart(nextStart));
+        }
+        for (Map<String, Object> row : rows)
+        {
+            if (!"COLLECTOR".equals(String.valueOf(row.get("sourceType")))) continue;
+            Instant time = toInstant(row.get("deviceTime"));
+            if (time == null) continue;
+            StepWindow match = windows.stream()
+                    .filter(window -> text(row, "batteryNo").equals(window.batteryNo()))
+                    .filter(window -> !time.isBefore(window.start())
+                            && (window.nextStart() == null || time.isBefore(window.nextStart())))
+                    .findFirst().orElse(null);
+            if (match == null) continue;
+            row.put("cycleNo", match.cycleNo());
+            row.put("stepNo", match.stepNo());
+            row.put("stepName", match.stepName());
+            row.put("actualStepLabel", match.stepName());
+        }
+    }
+
+    private String text(Map<String, Object> row, String key)
+    {
+        Object value = row.get(key);
+        return value == null ? "" : String.valueOf(value);
+    }
+
+    private Instant toInstant(Object value)
+    {
+        if (value instanceof Instant instant) return instant;
+        if (value instanceof Date date) return date.toInstant();
+        if (value == null) return null;
+        String text = String.valueOf(value);
+        try { return Instant.parse(text); }
+        catch (RuntimeException ignored) { }
+        try { return OffsetDateTime.parse(text).toInstant(); }
+        catch (RuntimeException ignored) { }
+        try { return LocalDateTime.parse(text).toInstant(ZoneOffset.UTC); }
+        catch (RuntimeException ignored) { return null; }
+    }
+
+    private static final class StepWindowBuilder
+    {
+        private final String batteryNo;
+        private final String cycleNo;
+        private final String stepNo;
+        private final String stepName;
+        private Instant start;
+        private Instant end;
+
+        private StepWindowBuilder(Map<String, Object> row, Instant time)
+        {
+            batteryNo = row.get("batteryNo") == null ? "" : String.valueOf(row.get("batteryNo"));
+            cycleNo = row.get("cycleNo") == null ? "" : String.valueOf(row.get("cycleNo"));
+            stepNo = row.get("stepNo") == null ? "" : String.valueOf(row.get("stepNo"));
+            stepName = row.get("stepName") == null ? "-" : String.valueOf(row.get("stepName"));
+            start = time;
+            end = time;
+        }
+
+        private StepWindow build()
+        {
+            return new StepWindow(batteryNo, cycleNo, stepNo, stepName, start, end, null);
+        }
+    }
+
+    private record StepWindow(String batteryNo, String cycleNo, String stepNo, String stepName,
+            Instant start, Instant end, Instant nextStart)
+    {
+        private StepWindow withNextStart(Instant value)
+        {
+            return new StepWindow(batteryNo, cycleNo, stepNo, stepName, start, end, value);
+        }
+    }
+
     private String identityPart(Map<String, Object> row, String key)
     {
         Object value = row == null ? null : row.get(key);

--
Gitblit v1.7.1