Prechádzať zdrojové kódy

fix(indicator): 修复报告日期回填无限循环(keyset游标+去除no-op写)+ 补回填测试

iwt 2 dní pred
rodič
commit
2298606093

+ 6 - 5
cfc-backend/src/main/java/com/etotem/cfc/service/ReportDateExtractor.java

@@ -105,11 +105,14 @@ public class ReportDateExtractor {
      */
     public int backfillHistoricalReports() {
         int total = 0;
+        long lastId = 0L;
         while (true) {
             List<HealthReport> batch = healthReportMapper.selectList(
                     new LambdaQueryWrapper<HealthReport>()
                             .isNull(HealthReport::getReportDate)
                             .like(HealthReport::getPayloadJson, "reportDate")
+                            .gt(HealthReport::getId, lastId)
+                            .orderByAsc(HealthReport::getId)
                             .last("LIMIT 500"));
             if (batch.isEmpty()) {
                 break;
@@ -121,17 +124,15 @@ public class ReportDateExtractor {
                         report.setReportDate(realDate);
                         healthReportMapper.updateById(report);
                         log.info("回填报告真实日期 reportId={}, reportDate={}", report.getId(), realDate);
-                    } else {
-                        // 置 NULL 并清 payload 中可提取性:无法提取的不再反复处理
-                        report.setReportDate(null);
-                        healthReportMapper.updateById(report);
-                        log.info("报告日期无法提取,置 NULL reportId={}", report.getId());
                     }
+                    // 无法提取:保持 report_date 为 NULL(不伪装成上传时间);
+                    // 由 keyset(id) 游标跳过,本轮不再反复处理
                     total++;
                 } catch (Exception e) {
                     log.warn("回填报告日期失败 reportId={}: {}", report.getId(), e.getMessage());
                 }
             }
+            lastId = batch.get(batch.size() - 1).getId();
         }
         log.info("ReportDateExtractor 历史回填完成,共处理 {} 行", total);
         return total;

+ 40 - 0
cfc-backend/src/test/java/com/etotem/cfc/service/ReportDateExtractorTest.java

@@ -1,9 +1,14 @@
 package com.etotem.cfc.service;
 
+import com.etotem.cfc.entity.HealthReport;
+import com.etotem.cfc.mapper.HealthReportMapper;
 import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
 import org.springframework.test.util.ReflectionTestUtils;
 
 import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collections;
 
 import static org.junit.jupiter.api.Assertions.*;
 
@@ -49,4 +54,39 @@ class ReportDateExtractorTest {
         assertNotNull(d);
         assertEquals("2026-09-01", new SimpleDateFormat("yyyy-MM-dd").format(d));
     }
+
+    @Test
+    void backfillFillsExtractableDateAndTerminates() {
+        ReportDateExtractor e = new ReportDateExtractor();
+        HealthReportMapper mapper = Mockito.mock(HealthReportMapper.class);
+        ReflectionTestUtils.setField(e, "healthReportMapper", mapper);
+        HealthReport r = new HealthReport();
+        r.setId(1L);
+        r.setPayloadJson("{\"reportDate\":\"2026-08-15\"}");
+        Mockito.when(mapper.selectList(Mockito.any()))
+                .thenReturn(Arrays.asList(r), Collections.emptyList());
+        int n = e.backfillHistoricalReports();
+        assertEquals(1, n);
+        Mockito.verify(mapper, Mockito.times(1))
+                .updateById(Mockito.any(HealthReport.class));
+        assertEquals("2026-08-15",
+                new SimpleDateFormat("yyyy-MM-dd").format(r.getReportDate()));
+    }
+
+    @Test
+    void backfillSkipsUnextractableWithoutWrite() {
+        ReportDateExtractor e = new ReportDateExtractor();
+        HealthReportMapper mapper = Mockito.mock(HealthReportMapper.class);
+        ReflectionTestUtils.setField(e, "healthReportMapper", mapper);
+        HealthReport r = new HealthReport();
+        r.setId(7L);
+        r.setPayloadJson("{\"reportDate\":\"\"}"); // 含字段但不可提取
+        Mockito.when(mapper.selectList(Mockito.any()))
+                .thenReturn(Arrays.asList(r), Collections.emptyList());
+        int n = e.backfillHistoricalReports();
+        assertEquals(1, n);
+        Mockito.verify(mapper, Mockito.never())
+                .updateById(Mockito.any(HealthReport.class));
+        assertNull(r.getReportDate());
+    }
 }