From 29f1afa5fff5440ad5ce7dd40dee1053ea0ff7bf Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期三, 02 九月 2026 12:14:46 +0800
Subject: [PATCH] 彻底删除补采完整性与断线恢复遗留代码
---
/dev/null | 33 ----------------
ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TTestServiceImplTest.java | 2 -
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TTestMapper.java | 68 ----------------------------------
3 files changed, 0 insertions(+), 103 deletions(-)
diff --git a/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/CollectorDisconnectRecoveryServiceImplTest.java b/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/CollectorDisconnectRecoveryServiceImplTest.java
deleted file mode 100644
index 1c2c636..0000000
--- a/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/CollectorDisconnectRecoveryServiceImplTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.ruoyi.system.service.impl;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.time.Instant;
-import java.util.List;
-import java.util.Set;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import com.ruoyi.system.domain.TTestCollectorExceptionInterval;
-import com.ruoyi.system.domain.TTestCollectorSource;
-import com.ruoyi.system.mapper.TTestMapper;
-
-/** 采集器断线对账的来源级幂等与恢复边界测试。 */
-@ExtendWith(MockitoExtension.class)
-class CollectorDisconnectRecoveryServiceImplTest
-{
- @Mock private TTestMapper mapper;
- @InjectMocks private CollectorDisconnectRecoveryServiceImpl service;
-
- @Test
- void opensOnlyOfflineSourceAndKeepsOtherSourcesIndependent()
- {
- TTestCollectorSource offline = source(10L, 20L, 8L, "COL-1", "S-01");
- TTestCollectorSource online = source(10L, 20L, 9L, "COL-2", "S-01");
- when(mapper.selectReceivingCollectorSources()).thenReturn(List.of(offline, online));
- when(mapper.selectOpenCollectorException(10L, 20L, 8L, "S-01")).thenReturn(null);
- when(mapper.selectOpenCollectorException(10L, 20L, 9L, "S-01")).thenReturn(null);
-
- service.reconcile(Set.of("COL-2"), Instant.parse("2026-08-30T09:00:00Z"));
-
- verify(mapper).insertCollectorExceptionInterval(any(TTestCollectorExceptionInterval.class));
- verify(mapper, never()).closeCollectorExceptionInterval(any(), any(), any());
- }
-
- @Test
- void closesOpenIntervalAtRecoveryBoundary()
- {
- TTestCollectorSource source = source(10L, 20L, 8L, "COL-1", "S-01");
- TTestCollectorExceptionInterval open = new TTestCollectorExceptionInterval();
- open.setIntervalId(99L);
- open.setIntervalStatus("OPEN");
- when(mapper.selectReceivingCollectorSources()).thenReturn(List.of(source));
- when(mapper.selectOpenCollectorException(10L, 20L, 8L, "S-01")).thenReturn(open);
-
- service.reconcile(Set.of("COL-1"), Instant.parse("2026-08-30T09:01:00Z"));
-
- verify(mapper).closeCollectorExceptionInterval(eq(99L), any(), any());
- verify(mapper, never()).insertCollectorExceptionInterval(any());
- }
-
- @Test
- void allowsAnotherDisconnectCycleAfterPreviousIntervalWasClosed()
- {
- TTestCollectorSource source = source(10L, 20L, 8L, "COL-1", "S-01");
- TTestCollectorExceptionInterval first = openInterval(99L);
- TTestCollectorExceptionInterval second = openInterval(100L);
- when(mapper.selectReceivingCollectorSources()).thenReturn(List.of(source));
- when(mapper.selectOpenCollectorException(10L, 20L, 8L, "S-01"))
- .thenReturn(null, first, null, second);
-
- service.reconcile(Set.of(), Instant.parse("2026-08-30T09:00:00Z"));
- service.reconcile(Set.of("COL-1"), Instant.parse("2026-08-30T09:01:00Z"));
- service.reconcile(Set.of(), Instant.parse("2026-08-30T09:02:00Z"));
- service.reconcile(Set.of("COL-1"), Instant.parse("2026-08-30T09:03:00Z"));
-
- verify(mapper, org.mockito.Mockito.times(2)).insertCollectorExceptionInterval(any());
- verify(mapper).closeCollectorExceptionInterval(eq(99L), any(), any());
- verify(mapper).closeCollectorExceptionInterval(eq(100L), any(), any());
- }
-
- private TTestCollectorExceptionInterval openInterval(Long intervalId)
- {
- TTestCollectorExceptionInterval interval = new TTestCollectorExceptionInterval();
- interval.setIntervalId(intervalId);
- interval.setIntervalStatus("OPEN");
- return interval;
- }
-
- private TTestCollectorSource source(Long testId, Long relationId, Long collectorId,
- String collectorCode, String channelCode)
- {
- TTestCollectorSource source = new TTestCollectorSource();
- source.setTestId(testId); source.setBatteryRelationId(relationId); source.setCollectorId(collectorId);
- source.setCollectorCode(collectorCode); source.setBatteryNo("BAT-01"); source.setChannelCode(channelCode);
- return source;
- }
-}
diff --git a/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/IntervalIntegrityCheckerTest.java b/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/IntervalIntegrityCheckerTest.java
deleted file mode 100644
index fd8a10b..0000000
--- a/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/IntervalIntegrityCheckerTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.ruoyi.system.service.impl;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import java.util.Arrays;
-import java.util.List;
-import org.junit.jupiter.api.Test;
-
-/** 区间完整性序号检查的外部行为测试。 */
-class IntervalIntegrityCheckerTest
-{
- @Test
- void ignoresDuplicatesAndOutOfOrderSamples()
- {
- assertEquals(List.of(new IntervalIntegrityChecker.Gap(3, 3), new IntervalIntegrityChecker.Gap(5, 5)),
- IntervalIntegrityChecker.gaps(List.of(4L, 1L, 2L, 4L, 6L)));
- }
-
- @Test
- void treatsEachReceptionIntervalIndependently()
- {
- assertEquals(List.of(), IntervalIntegrityChecker.gaps(List.of(1L, 2L)));
- assertEquals(List.of(), IntervalIntegrityChecker.gaps(List.of(8L, 9L)));
- }
-
- @Test
- void reportsMultipleOpenGaps()
- {
- assertEquals(List.of(new IntervalIntegrityChecker.Gap(3, 4), new IntervalIntegrityChecker.Gap(7, 8)),
- IntervalIntegrityChecker.gaps(List.of(1L, 2L, 5L, 6L, 9L)));
- }
-
- @Test
- void doesNotTreatPauseOrHandoverAsAnIntraIntervalGap()
- {
- assertEquals(List.of(), IntervalIntegrityChecker.gaps(List.of(10L, 11L)));
- assertEquals(List.of(), IntervalIntegrityChecker.gaps(List.of(20L, 21L)));
- }
-
- @Test
- void emptyIntervalHasNoSyntheticSequenceGap()
- {
- assertEquals(List.of(), IntervalIntegrityChecker.gaps(List.of()));
- }
-
- @Test
- void filtersInvalidSequenceValuesWithoutInventingGaps()
- {
- assertEquals(List.of(), IntervalIntegrityChecker.gaps(Arrays.asList(null, 0L, -1L, 5L)));
- }
-
- @Test
- void remainsStableAcrossReplayPermutationsAndDuplicates()
- {
- List<IntervalIntegrityChecker.Gap> expected = List.of(
- new IntervalIntegrityChecker.Gap(3, 3), new IntervalIntegrityChecker.Gap(6, 7));
- List<List<Long>> replays = List.of(
- List.of(8L, 1L, 2L, 4L, 5L, 8L),
- List.of(1L, 8L, 5L, 4L, 2L, 5L),
- List.of(2L, 4L, 1L, 8L, 4L, 5L));
- for (List<Long> replay : replays) assertEquals(expected, IntervalIntegrityChecker.gaps(replay));
- assertEquals(expected, IntervalIntegrityChecker.gaps(replays.get(0)));
- }
-}
diff --git a/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TTestServiceImplTest.java b/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TTestServiceImplTest.java
index fea0c55..561b2de 100644
--- a/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TTestServiceImplTest.java
+++ b/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TTestServiceImplTest.java
@@ -535,8 +535,6 @@
assertEquals(1, testService.stopTest(10L, 7L, "operator"));
verify(testMapper).updateReceptionStopped(eq(10L), eq("operator"), any());
- verify(testMapper, never()).deleteChannelLeasesByTestId(anyLong());
- verify(testMapper, never()).deleteCollectorChannelLeasesByTestId(anyLong());
verify(testMapper).insertTestAudit(eq(10L), eq("TEST_STOPPED"), eq(7L), eq("operator"), isNull(),
org.mockito.ArgumentMatchers.argThat(value -> value != null && value.contains("COMPLETED")));
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestBackfillCursor.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestBackfillCursor.java
deleted file mode 100644
index 5d5f802..0000000
--- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestBackfillCursor.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.ruoyi.system.domain;
-
-import java.util.Date;
-
-/** 测试来源历史补采的可恢复游标。 */
-public class TTestBackfillCursor
-{
- private Long cursorId;
- private Long testId;
- private Long bindingId;
- private String sourceType;
- private Long sourceId;
- private String sourceChannelCode;
- private String protocolTestId;
- private Long nextPosition;
- private String sourceStatus;
- private String protocolMetadata;
- private String lastError;
- private Date updatedAtUtc;
-
- public Long getCursorId() { return cursorId; }
- public void setCursorId(Long value) { cursorId = value; }
- public Long getTestId() { return testId; }
- public void setTestId(Long value) { testId = value; }
- public Long getBindingId() { return bindingId; }
- public void setBindingId(Long value) { bindingId = value; }
- public String getSourceType() { return sourceType; }
- public void setSourceType(String value) { sourceType = value; }
- public Long getSourceId() { return sourceId; }
- public void setSourceId(Long value) { sourceId = value; }
- public String getSourceChannelCode() { return sourceChannelCode; }
- public void setSourceChannelCode(String value) { sourceChannelCode = value; }
- public String getProtocolTestId() { return protocolTestId; }
- public void setProtocolTestId(String value) { protocolTestId = value; }
- public Long getNextPosition() { return nextPosition; }
- public void setNextPosition(Long value) { nextPosition = value; }
- public String getSourceStatus() { return sourceStatus; }
- public void setSourceStatus(String value) { sourceStatus = value; }
- public String getProtocolMetadata() { return protocolMetadata; }
- public void setProtocolMetadata(String value) { protocolMetadata = value; }
- public String getLastError() { return lastError; }
- public void setLastError(String value) { lastError = value; }
- public Date getUpdatedAtUtc() { return updatedAtUtc; }
- public void setUpdatedAtUtc(Date value) { updatedAtUtc = value; }
-}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestCollectorExceptionInterval.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestCollectorExceptionInterval.java
deleted file mode 100644
index 798f595..0000000
--- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestCollectorExceptionInterval.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.ruoyi.system.domain;
-
-import java.util.Date;
-
-/** 采集器来源断线期间的只追加事实。关闭动作只补写恢复边界。 */
-public class TTestCollectorExceptionInterval
-{
- private Long intervalId;
- private Long testId;
- private Long batteryRelationId;
- private Long collectorId;
- private String collectorCode;
- private String collectorName;
- private String collectorChannelCode;
- private String measurementName;
- private String exceptionType;
- private Date openedAtUtc;
- private Date missingFromUtc;
- private Date recoveredAtUtc;
- private Date missingToUtc;
- private String intervalStatus;
- private String detail;
-
- public Long getIntervalId() { return intervalId; }
- public void setIntervalId(Long intervalId) { this.intervalId = intervalId; }
- public Long getTestId() { return testId; }
- public void setTestId(Long testId) { this.testId = testId; }
- public Long getBatteryRelationId() { return batteryRelationId; }
- public void setBatteryRelationId(Long batteryRelationId) { this.batteryRelationId = batteryRelationId; }
- public Long getCollectorId() { return collectorId; }
- public void setCollectorId(Long collectorId) { this.collectorId = collectorId; }
- public String getCollectorCode() { return collectorCode; }
- public void setCollectorCode(String collectorCode) { this.collectorCode = collectorCode; }
- public String getCollectorName() { return collectorName; }
- public void setCollectorName(String collectorName) { this.collectorName = collectorName; }
- public String getCollectorChannelCode() { return collectorChannelCode; }
- public void setCollectorChannelCode(String collectorChannelCode) { this.collectorChannelCode = collectorChannelCode; }
- public String getMeasurementName() { return measurementName; }
- public void setMeasurementName(String measurementName) { this.measurementName = measurementName; }
- public String getExceptionType() { return exceptionType; }
- public void setExceptionType(String exceptionType) { this.exceptionType = exceptionType; }
- public Date getOpenedAtUtc() { return openedAtUtc; }
- public void setOpenedAtUtc(Date openedAtUtc) { this.openedAtUtc = openedAtUtc; }
- public Date getMissingFromUtc() { return missingFromUtc; }
- public void setMissingFromUtc(Date missingFromUtc) { this.missingFromUtc = missingFromUtc; }
- public Date getRecoveredAtUtc() { return recoveredAtUtc; }
- public void setRecoveredAtUtc(Date recoveredAtUtc) { this.recoveredAtUtc = recoveredAtUtc; }
- public Date getMissingToUtc() { return missingToUtc; }
- public void setMissingToUtc(Date missingToUtc) { this.missingToUtc = missingToUtc; }
- public String getIntervalStatus() { return intervalStatus; }
- public void setIntervalStatus(String intervalStatus) { this.intervalStatus = intervalStatus; }
- public String getDetail() { return detail; }
- public void setDetail(String detail) { this.detail = detail; }
-}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestIntegrityIssue.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestIntegrityIssue.java
deleted file mode 100644
index c0646c3..0000000
--- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestIntegrityIssue.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.ruoyi.system.domain;
-
-import java.util.Date;
-import com.fasterxml.jackson.annotation.JsonFormat;
-
-/** 测试来源区间完整性问题事实。 */
-public class TTestIntegrityIssue
-{
- private Long issueId;
- private Long testId;
- private Long intervalId;
- private Long bindingId;
- private String sourceType;
- private Long sourceId;
- private String sourceChannelCode;
- private String issueType;
- private String issueStatus;
- private Long missingFromSequence;
- private Long missingToSequence;
- private Integer retryCount;
- private String detail;
- @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC")
- private Date firstDetectedAtUtc;
- @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC")
- private Date lastDetectedAtUtc;
- @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC")
- private Date resolvedAtUtc;
-
- public Long getIssueId() { return issueId; }
- public void setIssueId(Long value) { issueId = value; }
- public Long getTestId() { return testId; }
- public void setTestId(Long value) { testId = value; }
- public Long getIntervalId() { return intervalId; }
- public void setIntervalId(Long value) { intervalId = value; }
- public Long getBindingId() { return bindingId; }
- public void setBindingId(Long value) { bindingId = value; }
- public String getSourceType() { return sourceType; }
- public void setSourceType(String value) { sourceType = value; }
- public Long getSourceId() { return sourceId; }
- public void setSourceId(Long value) { sourceId = value; }
- public String getSourceChannelCode() { return sourceChannelCode; }
- public void setSourceChannelCode(String value) { sourceChannelCode = value; }
- public String getIssueType() { return issueType; }
- public void setIssueType(String value) { issueType = value; }
- public String getIssueStatus() { return issueStatus; }
- public void setIssueStatus(String value) { issueStatus = value; }
- public Long getMissingFromSequence() { return missingFromSequence; }
- public void setMissingFromSequence(Long value) { missingFromSequence = value; }
- public Long getMissingToSequence() { return missingToSequence; }
- public void setMissingToSequence(Long value) { missingToSequence = value; }
- public Integer getRetryCount() { return retryCount; }
- public void setRetryCount(Integer value) { retryCount = value; }
- public String getDetail() { return detail; }
- public void setDetail(String value) { detail = value; }
- public Date getFirstDetectedAtUtc() { return firstDetectedAtUtc; }
- public void setFirstDetectedAtUtc(Date value) { firstDetectedAtUtc = value; }
- public Date getLastDetectedAtUtc() { return lastDetectedAtUtc; }
- public void setLastDetectedAtUtc(Date value) { lastDetectedAtUtc = value; }
- public Date getResolvedAtUtc() { return resolvedAtUtc; }
- public void setResolvedAtUtc(Date value) { resolvedAtUtc = value; }
-}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestReceptionInterval.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestReceptionInterval.java
deleted file mode 100644
index 1ece2e3..0000000
--- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TTestReceptionInterval.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.ruoyi.system.domain;
-
-import java.util.Date;
-
-/** 测试数据来源的追加接收区间事实。 */
-public class TTestReceptionInterval
-{
- private Long intervalId;
- private Long testId;
- private Long bindingId;
- private String sourceType;
- private Long sourceId;
- private String sourceChannelCode;
- private Date actionAtUtc;
- private Date startBoundaryAtUtc;
- private Date endBoundaryAtUtc;
- private String boundaryNote;
- private String intervalStatus;
-
- public Long getIntervalId() { return intervalId; }
- public void setIntervalId(Long value) { intervalId = value; }
- public Long getTestId() { return testId; }
- public void setTestId(Long value) { testId = value; }
- public Long getBindingId() { return bindingId; }
- public void setBindingId(Long value) { bindingId = value; }
- public String getSourceType() { return sourceType; }
- public void setSourceType(String value) { sourceType = value; }
- public Long getSourceId() { return sourceId; }
- public void setSourceId(Long value) { sourceId = value; }
- public String getSourceChannelCode() { return sourceChannelCode; }
- public void setSourceChannelCode(String value) { sourceChannelCode = value; }
- public Date getActionAtUtc() { return actionAtUtc; }
- public void setActionAtUtc(Date value) { actionAtUtc = value; }
- public Date getStartBoundaryAtUtc() { return startBoundaryAtUtc; }
- public void setStartBoundaryAtUtc(Date value) { startBoundaryAtUtc = value; }
- public Date getEndBoundaryAtUtc() { return endBoundaryAtUtc; }
- public void setEndBoundaryAtUtc(Date value) { endBoundaryAtUtc = value; }
- public String getBoundaryNote() { return boundaryNote; }
- public void setBoundaryNote(String value) { boundaryNote = value; }
- public String getIntervalStatus() { return intervalStatus; }
- public void setIntervalStatus(String value) { intervalStatus = value; }
-}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TTestMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TTestMapper.java
index f610df5..0bd548a 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TTestMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TTestMapper.java
@@ -11,11 +11,6 @@
import com.ruoyi.system.domain.TTestPlanStep;
import com.ruoyi.system.domain.TTestResult;
import com.ruoyi.system.domain.TTestWarningThreshold;
-import com.ruoyi.system.domain.TTestCollectorExceptionInterval;
-import com.ruoyi.system.domain.TTestCollectorSource;
-import com.ruoyi.system.domain.TTestBackfillCursor;
-import com.ruoyi.system.domain.TTestIntegrityIssue;
-import com.ruoyi.system.domain.TTestReceptionInterval;
import com.ruoyi.system.domain.TTestLogicStepMapping;
import com.ruoyi.system.domain.TDetectionChannel;
import com.ruoyi.system.domain.TTestDataRevisionAction;
@@ -158,67 +153,4 @@
int insertSampleConflict(@Param("testId") Long testId, @Param("channelId") Long channelId,
@Param("detail") String detail);
int countTestBatteryNo(@Param("testId") Long testId, @Param("batteryNo") String batteryNo);
- /** 兼容已移除的历史实现源码;当前运行链路不再调用这些方法。 */
- TTestChannelBinding selectChannelLeaseForUpdate(Long channelId);
- int insertChannelLease(@Param("channelId") Long channelId, @Param("testId") Long testId,
- @Param("bindingId") Long bindingId, @Param("acquiredBy") Long acquiredBy,
- @Param("acquiredAtUtc") Date acquiredAtUtc);
- int insertCollectorChannelLease(@Param("collectorId") Long collectorId, @Param("channelCode") String channelCode,
- @Param("testId") Long testId, @Param("bindingId") Long bindingId,
- @Param("acquiredBy") Long acquiredBy, @Param("acquiredAtUtc") Date acquiredAtUtc);
- int deleteChannelLeasesByTestId(Long testId);
- int deleteCollectorChannelLeasesByTestId(Long testId);
- List<TTestCollectorSource> selectReceivingCollectorSources();
- TTestCollectorExceptionInterval selectOpenCollectorException(@Param("testId") Long testId,
- @Param("batteryRelationId") Long batteryRelationId, @Param("collectorId") Long collectorId,
- @Param("channelCode") String channelCode);
- int insertCollectorExceptionInterval(TTestCollectorExceptionInterval interval);
- int closeCollectorExceptionInterval(@Param("intervalId") Long intervalId,
- @Param("recoveredAtUtc") Date recoveredAtUtc, @Param("missingToUtc") Date missingToUtc);
- List<TTestCollectorExceptionInterval> selectCollectorExceptionIntervals(Long testId);
- List<TTest> selectTestsForNaturalEnd();
- List<TTest> selectTestsForBackfill();
- TTestBackfillCursor selectBackfillCursorForUpdate(@Param("testId") Long testId,
- @Param("bindingId") Long bindingId, @Param("sourceType") String sourceType,
- @Param("sourceId") Long sourceId, @Param("sourceChannelCode") String sourceChannelCode);
- int insertBackfillCursor(TTestBackfillCursor cursor);
- int updateBackfillCursor(TTestBackfillCursor cursor);
- int updateTestBackfillStarted(@Param("testId") Long testId, @Param("updatedAtUtc") Date updatedAtUtc);
- int updateTestBackfillWaiting(@Param("testId") Long testId, @Param("updatedAtUtc") Date updatedAtUtc,
- @Param("error") String error);
- int updateTestBackfillCompleted(@Param("testId") Long testId, @Param("updatedAtUtc") Date updatedAtUtc);
- int updateTestCompleted(@Param("testId") Long testId, @Param("updatedAtUtc") Date updatedAtUtc);
- int updateTestNaturallyEnded(@Param("testId") Long testId, @Param("endedAtUtc") Date endedAtUtc);
- String selectSampleContentHash(@Param("channelId") Long channelId, @Param("btsTestId") String btsTestId,
- @Param("sequenceId") Long sequenceId);
- int insertBackfillConflict(@Param("testId") Long testId, @Param("channelId") Long channelId,
- @Param("detail") String detail);
- List<TTestIntegrityIssue> selectIntegrityIssues(Long testId);
- List<TTestIntegrityIssue> selectTestExceptionFacts(Long testId);
- List<Long> selectBtsSequencesByInterval(Long intervalId);
- List<Long> selectCollectorSequencesByInterval(Long intervalId);
- TTestIntegrityIssue selectOpenIntegrityIssue(@Param("intervalId") Long intervalId,
- @Param("sourceType") String sourceType, @Param("sourceId") Long sourceId,
- @Param("sourceChannelCode") String sourceChannelCode, @Param("missingFrom") Long missingFrom,
- @Param("missingTo") Long missingTo);
- List<TTestIntegrityIssue> selectOpenIntegrityIssues(@Param("intervalId") Long intervalId,
- @Param("sourceType") String sourceType, @Param("sourceId") Long sourceId,
- @Param("sourceChannelCode") String sourceChannelCode);
- int insertIntegrityIssue(TTestIntegrityIssue issue);
- int touchIntegrityIssue(@Param("issueId") Long issueId, @Param("retryCount") Integer retryCount,
- @Param("detail") String detail, @Param("detectedAtUtc") Date detectedAtUtc);
- int rewindBackfillCursor(@Param("testId") Long testId, @Param("bindingId") Long bindingId,
- @Param("sourceType") String sourceType, @Param("sourceId") Long sourceId,
- @Param("sourceChannelCode") String sourceChannelCode, @Param("nextPosition") Long nextPosition);
- int resolveIntegrityIssues(@Param("testId") Long testId, @Param("intervalId") Long intervalId,
- @Param("sourceType") String sourceType, @Param("sourceId") Long sourceId,
- @Param("sourceChannelCode") String sourceChannelCode, @Param("resolvedAtUtc") Date resolvedAtUtc);
- int resolveIntegrityIssue(@Param("issueId") Long issueId, @Param("resolvedAtUtc") Date resolvedAtUtc);
- int countOpenIntegrityIssues(Long testId);
- int countOpenExceptions(Long testId);
- int closeOpenReceptionIntervals(@Param("testId") Long testId, @Param("endBoundaryAtUtc") Date endBoundaryAtUtc,
- @Param("boundaryNote") String boundaryNote);
- List<TTestReceptionInterval> selectTestReceptionIntervals(Long testId);
-
- /** ����兼容旧源码的声明;Ticket28 运行链路不再调用。 */
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ICollectorDisconnectRecoveryService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ICollectorDisconnectRecoveryService.java
deleted file mode 100644
index 8bbf49f..0000000
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ICollectorDisconnectRecoveryService.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.ruoyi.system.service;
-
-import java.time.Instant;
-import java.util.Set;
-
-
-/** 维护接收中采集器来源的断线异常与恢复边界。 */
-public interface ICollectorDisconnectRecoveryService
-{
- void reconcile(Set<String> onlineCollectorCodes, Instant observedAt);
- /** 根据当前模拟器在线设备编码对账所有接收中来源。 */
-}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ITestBackfillService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ITestBackfillService.java
deleted file mode 100644
index 3ae4fff..0000000
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ITestBackfillService.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.ruoyi.system.service;
-
-/** 设备自然结束后的区间历史补采与恢复协调接口。 */
-public interface ITestBackfillService
-{
- void reconcileNaturalEnd();
- void resumePendingBackfill();
- void endNaturally(Long testId, String reason);
- /** 对接收中的测试检查检测柜自然结束并关闭其接收边界。 */
-
- /** 从持久化游标继续所有处于完成确认中的测试补采。 */
-
- /** 立即结束指定测试的接收并进入完成确认中,供自然结束协调器使用。 */
-}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ITestIntegrityService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ITestIntegrityService.java
deleted file mode 100644
index a7b8fcc..0000000
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ITestIntegrityService.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.ruoyi.system.service;
-
-import java.util.List;
-import com.ruoyi.system.domain.TTestIntegrityIssue;
-
-
-/** 查询测试区间完整性事实。 */
-public interface ITestIntegrityService
-{
- List<TTestIntegrityIssue> selectIssues(Long testId);
-}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/CollectorDisconnectRecoveryServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/CollectorDisconnectRecoveryServiceImpl.java
deleted file mode 100644
index 4776ed4..0000000
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/CollectorDisconnectRecoveryServiceImpl.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.ruoyi.system.service.impl;
-
-import java.time.Instant;
-import java.util.Collections;
-import java.util.Set;
-import java.util.stream.Collectors;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import com.alibaba.fastjson2.JSON;
-import com.ruoyi.system.domain.TTestCollectorExceptionInterval;
-import com.ruoyi.system.domain.TTestCollectorSource;
-import com.ruoyi.system.mapper.TTestMapper;
-import com.ruoyi.system.service.ICollectorDisconnectRecoveryService;
-
-/** 将采集器在线事实对账为可恢复的来源级异常区间。 */
-public class CollectorDisconnectRecoveryServiceImpl implements ICollectorDisconnectRecoveryService
-{
- private final TTestMapper mapper;
-
- public CollectorDisconnectRecoveryServiceImpl(TTestMapper mapper) { this.mapper = mapper; }
-
- @Override
- @Transactional(rollbackFor = Exception.class)
- public void reconcile(Set<String> onlineCollectorCodes, Instant observedAt)
- {
- Set<String> online = onlineCollectorCodes == null ? Collections.emptySet()
- : onlineCollectorCodes.stream().filter(code -> code != null && !code.isBlank()).collect(Collectors.toSet());
- for (TTestCollectorSource source : mapper.selectReceivingCollectorSources())
- {
- TTestCollectorExceptionInterval open = mapper.selectOpenCollectorException(source.getTestId(),
- source.getBatteryRelationId(), source.getCollectorId(), source.getChannelCode());
- boolean isOnline = online.contains(source.getCollectorCode());
- if (!isOnline && open == null)
- {
- TTestCollectorExceptionInterval interval = new TTestCollectorExceptionInterval();
- interval.setTestId(source.getTestId());
- interval.setBatteryRelationId(source.getBatteryRelationId());
- interval.setCollectorId(source.getCollectorId());
- interval.setCollectorChannelCode(source.getChannelCode());
- interval.setExceptionType("COLLECTOR_DISCONNECTED");
- java.util.Date at = java.util.Date.from(observedAt);
- interval.setOpenedAtUtc(at);
- interval.setMissingFromUtc(at);
- interval.setDetail(JSON.toJSONString(java.util.Map.of("batteryNo", source.getBatteryNo(),
- "collectorCode", source.getCollectorCode(), "message", "采集器离线,检测柜及其他来源继续接收")));
- mapper.insertCollectorExceptionInterval(interval);
- mapper.insertTestAudit(source.getTestId(), "COLLECTOR_DISCONNECTED", null, "system", null,
- JSON.toJSONString(java.util.Map.of("collectorId", source.getCollectorId(),
- "channelCode", source.getChannelCode(), "missingFrom", observedAt.toString())));
- }
- else if (isOnline && open != null)
- {
- java.util.Date at = java.util.Date.from(observedAt);
- mapper.closeCollectorExceptionInterval(open.getIntervalId(), at, at);
- mapper.insertTestAudit(source.getTestId(), "COLLECTOR_RECONNECTED", null, "system", null,
- JSON.toJSONString(java.util.Map.of("collectorId", source.getCollectorId(),
- "channelCode", source.getChannelCode(), "recoveredAt", observedAt.toString())));
- }
- }
- }
-}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/IntervalIntegrityChecker.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/IntervalIntegrityChecker.java
deleted file mode 100644
index c2302f8..0000000
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/IntervalIntegrityChecker.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.ruoyi.system.service.impl;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-/** 按单个接收区间检查历史序号;暂停和区间边界由调用方分组后自然排除。 */
-final class IntervalIntegrityChecker
-{
- record Gap(long from, long to) { }
-
- private IntervalIntegrityChecker() { }
-
- static List<Gap> gaps(Collection<Long> sequenceIds)
- {
- if (sequenceIds == null || sequenceIds.isEmpty()) return List.of();
- Set<Long> unique = new LinkedHashSet<>();
- sequenceIds.stream().filter(value -> value != null && value > 0).forEach(unique::add);
- List<Long> ordered = new ArrayList<>(unique);
- ordered.sort(Comparator.naturalOrder());
- List<Gap> result = new ArrayList<>();
- for (int index = 1; index < ordered.size(); index++)
- {
- long previous = ordered.get(index - 1);
- long current = ordered.get(index);
- if (current > previous + 1) result.add(new Gap(previous + 1, current - 1));
- }
- return List.copyOf(result);
- }
-}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestBackfillServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestBackfillServiceImpl.java
deleted file mode 100644
index 5a92d54..0000000
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestBackfillServiceImpl.java
+++ /dev/null
@@ -1,423 +0,0 @@
-package com.ruoyi.system.service.impl;
-
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import java.time.Instant;
-import java.util.Date;
-import java.util.HexFormat;
-import java.util.List;
-import java.util.Objects;
-import com.alibaba.fastjson2.JSON;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.PlatformTransactionManager;
-import org.springframework.transaction.support.TransactionTemplate;
-import org.springframework.transaction.annotation.Transactional;
-import com.ruoyi.common.exception.ServiceException;
-import com.ruoyi.device.api.DeviceAdapter;
-import com.ruoyi.device.model.ChannelIdentity;
-import com.ruoyi.device.model.HistoryAvailability;
-import com.ruoyi.device.model.HistoryPage;
-import com.ruoyi.device.model.HistorySample;
-import com.ruoyi.device.simulator.BtsSimulatorStateStore;
-import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorMeasurementRecord;
-import com.ruoyi.device.registry.DeviceAdapterRegistry;
-import com.ruoyi.system.domain.TTest;
-import com.ruoyi.system.domain.TTestBackfillCursor;
-import com.ruoyi.system.domain.TTestBattery;
-import com.ruoyi.system.domain.TTestChannelBinding;
-import com.ruoyi.system.domain.TTestCollectorChannel;
-import com.ruoyi.system.domain.TTestReceptionInterval;
-import com.ruoyi.system.domain.TTestIntegrityIssue;
-import com.ruoyi.system.mapper.TTestMapper;
-import com.ruoyi.system.service.ITestBackfillService;
-import com.ruoyi.system.service.ITTestService;
-
-/** 以测试动作边界筛选来源历史,并以数据库游标保证补采可恢复。 */
-public class TestBackfillServiceImpl implements ITestBackfillService
-{
- private static final int PAGE_SIZE = 1000;
- private static final int MAX_INTEGRITY_RETRIES = 3;
- private final TTestMapper mapper;
- private final DeviceAdapterRegistry registry;
- private final BtsSimulatorStateStore collectorHistoryStore;
- private final ITTestService tests;
- private final TransactionTemplate transactionTemplate;
-
- public TestBackfillServiceImpl(TTestMapper mapper, DeviceAdapterRegistry registry,
- BtsSimulatorStateStore collectorHistoryStore, ITTestService tests,
- PlatformTransactionManager transactionManager)
- {
- this.mapper = mapper;
- this.registry = registry;
- this.collectorHistoryStore = collectorHistoryStore;
- this.tests = tests;
- this.transactionTemplate = new TransactionTemplate(transactionManager);
- }
-
- /** 将已自然结束的设备事实转为待补采测试,并关闭其开放接收区间。 */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public void reconcileNaturalEnd()
- {
- List<TTest> summaries = mapper.selectTestsForNaturalEnd();
- if (summaries == null) return;
- for (TTest summary : summaries)
- {
- TTest test = tests.selectTestById(summary.getTestId());
- if (test == null || test.getBatteries() == null || test.getBatteries().isEmpty()) continue;
- boolean allFinished = true;
- for (TTestBattery battery : test.getBatteries())
- {
- TTestChannelBinding binding = battery.getBinding();
- if (binding == null) { allFinished = false; break; }
- var channel = mapper.selectReceptionChannelForUpdate(binding.getChannelId());
- if (channel == null || !"FINISHED".equalsIgnoreCase(channel.getWorkStatus()))
- { allFinished = false; break; }
- }
- if (allFinished) endNaturally(test.getTestId(), "NATURAL_FINISH");
- }
- }
-
- /** 恢复等待中的历史补采;完整性未确认时继续保持完成确认中。 */
- @Override
- public void resumePendingBackfill()
- {
- List<TTest> pending = mapper.selectTestsForBackfill();
- if (pending == null) return;
- for (TTest test : pending)
- {
- try { transactionTemplate.executeWithoutResult(status -> backfill(test)); }
- catch (RuntimeException exception)
- {
- mapper.updateTestBackfillWaiting(test.getTestId(), new Date(), exception.getMessage());
- }
- }
- }
-
- /** 记录自然结束并释放租约;测试完成状态由后续补采完整性门禁确认。 */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public void endNaturally(Long testId, String reason)
- {
- if (testId == null) throw new ServiceException("测试ID不能为空");
- Date endedAt = new Date();
- if (mapper.updateTestNaturallyEnded(testId, endedAt) != 1) return;
- mapper.closeOpenReceptionIntervals(testId, endedAt, reason == null ? "NATURAL_FINISH" : reason);
- mapper.deleteChannelLeasesByTestId(testId);
- mapper.deleteCollectorChannelLeasesByTestId(testId);
- mapper.insertTestAudit(testId, "RECEPTION_NATURALLY_ENDED", null, "system", null,
- JSON.toJSONString(java.util.Map.of("testStatus", "COMPLETION_PENDING",
- "receptionStatus", "ENDED", "reason", reason == null ? "NATURAL_FINISH" : reason)));
- }
-
- /** 补采全部来源历史,只有完整性和异常检查通过后才写入检测完成。 */
- @Transactional(rollbackFor = Exception.class)
- protected void backfill(TTest test)
- {
- if (mapper.updateTestBackfillStarted(test.getTestId(), new Date()) == 0) return;
- // 来源按检测柜、采集器分别提交;任何一页失败都不推进该来源游标。
- for (TTestBattery battery : test.getBatteries() == null ? List.<TTestBattery>of() : test.getBatteries())
- {
- TTestChannelBinding binding = battery.getBinding();
- if (binding != null) backfillCabinet(test, battery, binding);
- for (TTestCollectorChannel mapping : battery.getCollectorChannels() == null
- ? List.<TTestCollectorChannel>of() : battery.getCollectorChannels())
- backfillCollector(test, battery, mapping);
- }
- Date checkedAt = new Date();
- boolean complete = checkIntegrity(test, checkedAt);
- if (complete)
- {
- mapper.updateTestCompleted(test.getTestId(), checkedAt);
- mapper.insertTestAudit(test.getTestId(), "TEST_COMPLETED", null, "system", null,
- JSON.toJSONString(java.util.Map.of("backfillStatus", "COMPLETED", "integrity", "PASSED")));
- }
- else
- {
- mapper.updateTestBackfillWaiting(test.getTestId(), checkedAt, "区间完整性检查未通过");
- mapper.insertTestAudit(test.getTestId(), "INTEGRITY_CHECK_BLOCKED", null, "system", null,
- JSON.toJSONString(java.util.Map.of("backfillStatus", "WAITING", "integrity", "OPEN")));
- }
- }
-
- private void backfillCabinet(TTest test, TTestBattery battery, TTestChannelBinding binding)
- {
- String adapterCode = binding.getAdapterCode();
- if (adapterCode == null || adapterCode.isBlank()) throw new ServiceException("检测柜适配器身份缺失");
- ChannelIdentity identity = new ChannelIdentity(adapterCode, binding.getDevtype(), binding.getDevid(),
- binding.getSubdevid(), binding.getChlid());
- List<TTestReceptionInterval> intervals = intervals(test.getTestId(), binding.getBindingId(), "CABINET_CHANNEL",
- binding.getChannelId(), null);
- if (intervals.isEmpty()) return;
- HistoryAvailability availability = registry.execute(adapterCode,
- adapter -> adapter.inquireHistory(java.util.Set.of(identity)).stream().findFirst()
- .orElseThrow(() -> new ServiceException("检测柜未返回历史清单")));
- TTestBackfillCursor cursor = cursor(test, binding.getBindingId(), "CABINET_CHANNEL", binding.getChannelId(), null,
- availability.btsTestId());
- cursor.setProtocolMetadata(JSON.toJSONString(java.util.Map.of("availableRecordCount",
- availability.availableRecordCount(), "uploadComplete", availability.uploadComplete(),
- "adapterCode", adapterCode)));
- cursor.setUpdatedAtUtc(new Date());
- mapper.updateBackfillCursor(cursor);
- long position = Math.max(1L, cursor.getNextPosition() == null ? 1L : cursor.getNextPosition());
- while (true)
- {
- final long requestPosition = position;
- HistoryPage page = registry.execute(adapterCode,
- adapter -> adapter.downloadHistory(identity, availability.btsTestId(), requestPosition, PAGE_SIZE));
- for (HistorySample sample : page.samples())
- {
- if (!within(sample.deviceTime(), intervals)) continue;
- saveBtsSample(test, binding, intervals, availability.btsTestId(), sample);
- }
- long next = page.nextPosition() > position ? page.nextPosition() : position + page.samples().size();
- cursor.setNextPosition(next);
- cursor.setSourceStatus(page.samples().isEmpty() && page.nextPosition() <= position ? "COMPLETED" : "RUNNING");
- cursor.setUpdatedAtUtc(new Date());
- mapper.updateBackfillCursor(cursor);
- if (page.samples().isEmpty() || page.nextPosition() <= position) break;
- position = next;
- }
- cursor.setSourceStatus("COMPLETED");
- cursor.setUpdatedAtUtc(new Date());
- mapper.updateBackfillCursor(cursor);
- }
-
- private void saveBtsSample(TTest test, TTestChannelBinding binding, List<TTestReceptionInterval> intervals,
- String btsTestId, HistorySample sample)
- {
- byte[] hash = hash(sample.sequenceId() + "|" + sample.stepNo() + "|" + sample.cycleNo() + "|"
- + sample.stepType() + "|" + sample.deviceTimeRaw() + "|" + sample.voltageV() + "|"
- + sample.currentA() + "|" + sample.capacityAh() + "|" + sample.energyWh());
- String testScoped = mapper.selectTestSampleContentHash(test.getTestId(), binding.getChannelId(),
- sample.sequenceId());
- if (testScoped != null)
- {
- if (!testScoped.equalsIgnoreCase(HexFormat.of().formatHex(hash)))
- mapper.insertSampleConflict(test.getTestId(), binding.getChannelId(),
- "测试内采样内容冲突 seqid=" + sample.sequenceId());
- return;
- }
- String old = mapper.selectSampleContentHash(binding.getChannelId(), btsTestId, sample.sequenceId());
- if (old != null && !old.equalsIgnoreCase(HexFormat.of().formatHex(hash)))
- {
- mapper.insertSampleConflict(test.getTestId(), binding.getChannelId(),
- "BTS testid=" + btsTestId + ", seqid=" + sample.sequenceId());
- return;
- }
- mapper.insertBtsSample(test.getTestId(), binding.getBindingId(), binding.getChannelId(),
- intervalId(intervals, sample.deviceTime()), btsTestId, sample.sequenceId(), Date.from(sample.deviceTime()),
- sample.deviceTimeRaw(), new Date(), sample.cycleNo(), sample.stepNo(), sample.stepType(),
- stepNameForType(sample.stepType()), sample.testTimeMs(),
- sample.voltageV(), sample.currentA(), sample.capacityAh(), sample.energyWh(), hash);
- Long sampleId = mapper.selectBtsSampleId(binding.getChannelId(), btsTestId, sample.sequenceId());
- if (sampleId != null)
- sample.measurements().forEach((code, measurement) -> mapper.insertBtsMeasurement(sampleId, code,
- measurement.value(), measurement.unit() == null ? "" : measurement.unit(), measurement.sourceType()));
- }
-
- private void backfillCollector(TTest test, TTestBattery battery, TTestCollectorChannel mapping)
- {
- if (battery.getCollectorId() == null) return;
- if (battery.getBinding() == null) throw new ServiceException("采集器绑定缺少检测柜通道");
- String collectorCode = mapper.selectCollectorCode(battery.getCollectorId());
- if (collectorCode == null || collectorCode.isBlank()) throw new ServiceException("采集器来源不存在");
- List<TTestReceptionInterval> intervals = intervals(test.getTestId(), battery.getBinding().getBindingId(),
- "COLLECTOR_CHANNEL", battery.getCollectorId(), mapping.getChannelCode());
- if (intervals.isEmpty()) return;
- TTestBackfillCursor cursor = cursor(test, battery.getBinding().getBindingId(), "COLLECTOR_CHANNEL",
- battery.getCollectorId(), mapping.getChannelCode(), collectorCode);
- long offset = Math.max(0L, (cursor.getNextPosition() == null ? 1L : cursor.getNextPosition()) - 1L);
- while (true)
- {
- var page = collectorHistoryStore.loadCollectorHistoryPage(collectorCode, offset, PAGE_SIZE, false);
- for (CollectorMeasurementRecord record : page.records())
- {
- if (!mapping.getMeasurementCode().equalsIgnoreCase(record.metricCode()) || !within(record.deviceTime(), intervals)) continue;
- saveCollectorSample(test, battery, mapping, intervals, record);
- }
- offset = page.nextOffset();
- cursor.setNextPosition(offset + 1L);
- cursor.setSourceStatus(page.hasMore() ? "RUNNING" : "COMPLETED");
- cursor.setUpdatedAtUtc(new Date());
- mapper.updateBackfillCursor(cursor);
- if (!page.hasMore()) break;
- }
- }
-
- private void saveCollectorSample(TTest test, TTestBattery battery, TTestCollectorChannel mapping,
- List<TTestReceptionInterval> intervals, CollectorMeasurementRecord record)
- {
- byte[] hash = hash(record.sequenceId() + "|" + record.deviceTimeRaw() + "|" + record.metricCode() + "|"
- + record.value() + "|" + record.unit());
- String old = mapper.selectCollectorSampleContentHash(battery.getCollectorId(), record.channelCode(),
- record.sequenceId(), record.metricCode());
- if (old != null && !old.equalsIgnoreCase(HexFormat.of().formatHex(hash)))
- {
- mapper.insertSampleConflict(test.getTestId(), null,
- "采集器=" + record.collectorCode() + ", seqid=" + record.sequenceId());
- return;
- }
- mapper.insertCollectorSample(test.getTestId(), battery.getBinding().getBindingId(),
- intervalId(intervals, record.deviceTime()), battery.getCollectorId(),
- record.channelCode(), record.sequenceId(), Date.from(record.deviceTime()), record.deviceTimeRaw(),
- Date.from(record.serverReceivedAt()), record.metricCode(), record.value(), record.unit(), hash);
- }
-
- /** BTS download 协议只提供类型码;演示协议按已确认的类型语义补出名称。 */
- private String stepNameForType(String stepType)
- {
- return switch (stepType == null ? "" : stepType.toLowerCase(java.util.Locale.ROOT))
- {
- case "cc" -> "恒流充电";
- case "dc" -> "恒流放电";
- case "rest" -> "搁置";
- default -> stepType;
- };
- }
-
- private boolean checkIntegrity(TTest test, Date checkedAt)
- {
- // 序号只在同一来源、同一接收区间内比较;区间边界和暂停间隔不构成缺口。
- boolean complete = true;
- for (TTestReceptionInterval interval : mapper.selectTestReceptionIntervals(test.getTestId()))
- {
- String sourceType = interval.getSourceType();
- List<Long> sequenceIds = "CABINET_CHANNEL".equals(sourceType)
- ? mapper.selectBtsSequencesByInterval(interval.getIntervalId())
- : mapper.selectCollectorSequencesByInterval(interval.getIntervalId());
- List<IntervalIntegrityChecker.Gap> gaps = IntervalIntegrityChecker.gaps(sequenceIds);
- if (sequenceIds == null || sequenceIds.isEmpty())
- {
- complete = false;
- TTestIntegrityIssue old = mapper.selectOpenIntegrityIssue(interval.getIntervalId(), sourceType,
- interval.getSourceId(), interval.getSourceChannelCode(), null, null);
- if (old == null)
- {
- TTestIntegrityIssue issue = new TTestIntegrityIssue();
- issue.setTestId(test.getTestId()); issue.setIntervalId(interval.getIntervalId());
- issue.setBindingId(interval.getBindingId()); issue.setSourceType(sourceType);
- issue.setSourceId(interval.getSourceId()); issue.setSourceChannelCode(interval.getSourceChannelCode());
- issue.setIssueType("NO_SAMPLES"); issue.setRetryCount(1);
- issue.setDetail("接收区间没有可确认的历史采样"); issue.setFirstDetectedAtUtc(checkedAt);
- issue.setLastDetectedAtUtc(checkedAt); mapper.insertIntegrityIssue(issue);
- mapper.insertTestAudit(test.getTestId(), "INTEGRITY_GAP_DETECTED", null, "system", null,
- JSON.toJSONString(java.util.Map.of("intervalId", interval.getIntervalId(), "sourceType", sourceType,
- "issueType", "NO_SAMPLES")));
- }
- else
- {
- int retry = old.getRetryCount() == null ? 1 : old.getRetryCount() + 1;
- int cappedRetry = Math.min(retry, MAX_INTEGRITY_RETRIES);
- mapper.touchIntegrityIssue(old.getIssueId(), cappedRetry,
- old.getDetail(), checkedAt);
- mapper.insertTestAudit(test.getTestId(), "INTEGRITY_GAP_RETRIED", null, "system", null,
- JSON.toJSONString(java.util.Map.of("issueId", old.getIssueId(), "retryCount", cappedRetry)));
- if (cappedRetry < MAX_INTEGRITY_RETRIES)
- mapper.rewindBackfillCursor(test.getTestId(), interval.getBindingId(), sourceType,
- interval.getSourceId(), interval.getSourceChannelCode(), 1L);
- }
- continue;
- }
- if (gaps.isEmpty())
- {
- int resolved = mapper.resolveIntegrityIssues(test.getTestId(), interval.getIntervalId(), sourceType,
- interval.getSourceId(), interval.getSourceChannelCode(), checkedAt);
- if (resolved > 0) mapper.insertTestAudit(test.getTestId(), "INTEGRITY_GAP_RESOLVED", null, "system", null,
- JSON.toJSONString(java.util.Map.of("intervalId", interval.getIntervalId())));
- continue;
- }
- complete = false;
- for (TTestIntegrityIssue open : mapper.selectOpenIntegrityIssues(interval.getIntervalId(), sourceType,
- interval.getSourceId(), interval.getSourceChannelCode()))
- {
- boolean stillOpen = gaps.stream().anyMatch(gap -> Objects.equals(gap.from(), open.getMissingFromSequence())
- && Objects.equals(gap.to(), open.getMissingToSequence()));
- if (!stillOpen && mapper.resolveIntegrityIssue(open.getIssueId(), checkedAt) > 0)
- mapper.insertTestAudit(test.getTestId(), "INTEGRITY_GAP_RESOLVED", null, "system", null,
- JSON.toJSONString(java.util.Map.of("issueId", open.getIssueId())));
- }
- for (IntervalIntegrityChecker.Gap gap : gaps)
- {
- TTestIntegrityIssue old = mapper.selectOpenIntegrityIssue(interval.getIntervalId(), sourceType,
- interval.getSourceId(), interval.getSourceChannelCode(), gap.from(), gap.to());
- if (old == null)
- {
- TTestIntegrityIssue issue = new TTestIntegrityIssue();
- issue.setTestId(test.getTestId()); issue.setIntervalId(interval.getIntervalId());
- issue.setBindingId(interval.getBindingId()); issue.setSourceType(sourceType);
- issue.setSourceId(interval.getSourceId()); issue.setSourceChannelCode(interval.getSourceChannelCode());
- issue.setIssueType("SEQUENCE_GAP"); issue.setMissingFromSequence(gap.from());
- issue.setMissingToSequence(gap.to()); issue.setRetryCount(1);
- issue.setDetail("接收区间内存在未解释序号缺口"); issue.setFirstDetectedAtUtc(checkedAt);
- issue.setLastDetectedAtUtc(checkedAt); mapper.insertIntegrityIssue(issue);
- mapper.insertTestAudit(test.getTestId(), "INTEGRITY_GAP_DETECTED", null, "system", null,
- JSON.toJSONString(java.util.Map.of("intervalId", interval.getIntervalId(), "sourceType", sourceType,
- "missingFrom", gap.from(), "missingTo", gap.to())));
- }
- else
- {
- int retry = old.getRetryCount() == null ? 1 : old.getRetryCount() + 1;
- int cappedRetry = Math.min(retry, MAX_INTEGRITY_RETRIES);
- mapper.touchIntegrityIssue(old.getIssueId(), cappedRetry,
- old.getDetail(), checkedAt);
- mapper.insertTestAudit(test.getTestId(), "INTEGRITY_GAP_RETRIED", null, "system", null,
- JSON.toJSONString(java.util.Map.of("issueId", old.getIssueId(), "retryCount", cappedRetry)));
- if (cappedRetry >= MAX_INTEGRITY_RETRIES) continue;
- }
- // next_position 是分页偏移而非 seqid;从安全起点重放,依靠幂等键避免覆盖原始事实。
- mapper.rewindBackfillCursor(test.getTestId(), interval.getBindingId(), sourceType,
- interval.getSourceId(), interval.getSourceChannelCode(), 1L);
- }
- }
- return complete && mapper.countOpenIntegrityIssues(test.getTestId()) == 0
- && mapper.countOpenExceptions(test.getTestId()) == 0;
- }
-
- private TTestBackfillCursor cursor(TTest test, Long bindingId, String sourceType, Long sourceId,
- String channelCode, String protocolTestId)
- {
- TTestBackfillCursor value = mapper.selectBackfillCursorForUpdate(test.getTestId(), bindingId, sourceType, sourceId, channelCode);
- if (value != null)
- {
- if (value.getProtocolTestId() != null && !Objects.equals(value.getProtocolTestId(), protocolTestId))
- throw new ServiceException("历史协议测试标识发生变化");
- return value;
- }
- value = new TTestBackfillCursor();
- value.setTestId(test.getTestId()); value.setBindingId(bindingId); value.setSourceType(sourceType);
- value.setSourceId(sourceId); value.setSourceChannelCode(channelCode); value.setProtocolTestId(protocolTestId);
- value.setNextPosition(1L); value.setSourceStatus("PENDING"); value.setProtocolMetadata("{}");
- value.setUpdatedAtUtc(new Date());
- mapper.insertBackfillCursor(value);
- return value;
- }
-
- private List<TTestReceptionInterval> intervals(Long testId, Long bindingId, String sourceType, Long sourceId,
- String channelCode)
- {
- return mapper.selectTestReceptionIntervals(testId).stream()
- .filter(item -> Objects.equals(item.getBindingId(), bindingId) && Objects.equals(item.getSourceType(), sourceType)
- && Objects.equals(item.getSourceId(), sourceId)
- && Objects.equals(item.getSourceChannelCode(), channelCode)).toList();
- }
-
- private boolean within(Instant time, List<TTestReceptionInterval> intervals)
- {
- return time != null && intervals.stream().anyMatch(item -> !time.isBefore(item.getStartBoundaryAtUtc().toInstant())
- && (item.getEndBoundaryAtUtc() == null || time.isBefore(item.getEndBoundaryAtUtc().toInstant())));
- }
-
- private Long intervalId(List<TTestReceptionInterval> intervals, Instant time)
- {
- return intervals.stream().filter(item -> !time.isBefore(item.getStartBoundaryAtUtc().toInstant())
- && (item.getEndBoundaryAtUtc() == null || time.isBefore(item.getEndBoundaryAtUtc().toInstant())))
- .map(TTestReceptionInterval::getIntervalId).findFirst().orElse(null);
- }
-
- private byte[] hash(String value)
- {
- try { return MessageDigest.getInstance("SHA-256").digest(value.getBytes(StandardCharsets.UTF_8)); }
- catch (Exception exception) { throw new IllegalStateException("SHA-256不可用", exception); }
- }
-}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestIntegrityServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestIntegrityServiceImpl.java
deleted file mode 100644
index ebb44b4..0000000
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestIntegrityServiceImpl.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.ruoyi.system.service.impl;
-
-import java.util.List;
-import org.springframework.stereotype.Service;
-import com.ruoyi.common.exception.ServiceException;
-import com.ruoyi.system.domain.TTestIntegrityIssue;
-import com.ruoyi.system.mapper.TTestMapper;
-import com.ruoyi.system.service.ITTestService;
-import com.ruoyi.system.service.ITestIntegrityService;
-
-/** 完整性查询只返回持久化问题事实,不把实时快照当作历史结果。 */
-public class TestIntegrityServiceImpl implements ITestIntegrityService
-{
- private final TTestMapper mapper;
- private final ITTestService tests;
-
- public TestIntegrityServiceImpl(TTestMapper mapper, ITTestService tests)
- { this.mapper = mapper; this.tests = tests; }
-
- /** 返回持久化完整性问题和历史异常事实,不把实时快照当作最终结果。 */
- @Override
- public List<TTestIntegrityIssue> selectIssues(Long testId)
- {
- if (testId == null) throw new ServiceException("测试ID不能为空");
- tests.selectTestById(testId);
- List<TTestIntegrityIssue> issues = new java.util.ArrayList<>();
- List<TTestIntegrityIssue> integrity = mapper.selectIntegrityIssues(testId);
- if (integrity != null) issues.addAll(integrity);
- List<TTestIntegrityIssue> exceptions = mapper.selectTestExceptionFacts(testId);
- if (exceptions != null) issues.addAll(exceptions);
- return issues;
- }
-}
--
Gitblit v1.7.1