Procházet zdrojové kódy

T12 分页残段清洗下沉(guhe.l1.pageTailClean)

- PdfParseService 新增分页残段清洗执行入口 stripTableTail()
  (Python _strip_table_tail() 的 Java 下沉):剥离含 \d{1,3}/16\d
  分页页码拼接残段(如 34/167)的残段行
- parseText() 对 guhe 报告解析前自动调用 stripTableTail(),
  Adapter 调用解析即自动获得清洗(个人层丰度不混入知识层)
- 新增校验断言入口 assertTailClean()(断言清洗后残段 0 命中)
- 声明残段标记常量 PAGE_TAIL_SAMPLE(34/167)与残段断言正则
  PAGE_TAIL_ASSERT_REGEX(\d{1,3}/16\d)
- 契约 GuhePageTailCleanContractTest 6/6 转绿;
  同批 T13/T14 契约保持绿,T8 3 红为未实现(非本任务)
E2E Test Bot před 3 dny
rodič
revize
4a6d6c42a6

+ 69 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/PdfParseService.java

@@ -117,6 +117,21 @@ public class PdfParseService {
     private static final List<String> KNOWN_PATHOGENS = Arrays.asList(
             "幽门螺杆菌", "艰难梭菌", "沙门氏菌", "志贺氏菌", "弯曲杆菌");
 
+    // ======================== 分页残段清洗(T12/L1-2 分页残段清洗下沉) ========================
+
+    /**
+     * 分页残段标记样例(计划 §2.1 / T12)。
+     * 「与健康相关菌评估」分页表拼接残段样例 {@code 34/167 0.0033 参考 0-0.1746% ND},
+     * 其中 {@code 34/167} 为分页页码拼接残段;个人层丰度不再混入知识层。
+     */
+    private static final String PAGE_TAIL_SAMPLE = "34/167";
+
+    /**
+     * 分页残段断言正则(T12/L1-2 校验层断言:物种库条目不含 {@code \d{1,3}/16\d})。
+     * 匹配分页页码拼接残段(如 {@code 34/167});Java 字符串中 {@code \d} 转义为 {@code \\d}。
+     */
+    private static final String PAGE_TAIL_ASSERT_REGEX = "\\d{1,3}/16\\d";
+
     // ======================== 主入口 ========================
 
     /**
@@ -148,6 +163,10 @@ public ParsedReportResult parseText(String fullText) {
             return beijingResult;
         }
 
+        // T12/L1-2 分页残段清洗下沉:guhe 报告解析前自动剥离分页表拼接残段,
+        // Adapter 调用 parseText() 即自动获得清洗(无需单独调用),个人层丰度不再混入知识层。
+        normalized = stripTableTail(normalized);
+
         // 3. 按行分割
         String[] lines = normalized.split("\r?\n");
         List<String> lineList = new ArrayList<>();
@@ -181,6 +200,56 @@ public ParsedReportResult parseText(String fullText) {
         return result;
     }
 
+    // ======================== 分页残段清洗入口(T12/L1-2,_strip_table_tail 的 Java 下沉) ========================
+
+    /**
+     * 分页残段清洗执行入口(Python {@code _strip_table_tail()} 的 Java 下沉)。
+     *
+     * <p>根治「与健康相关菌评估」分页表拼接残段(如 {@code 34/167 0.0033 参考 0-0.1746% ND}):
+     * 剥离含分页页码拼接模式({@code \d{1,3}/16\d},如 {@code 34/167})的残段行,
+     * 使个人层丰度不再混入知识层。guhe 报告解析({@link #parseText})时自动调用,
+     * Adapter 无需单独触发即自动获得清洗。</p>
+     *
+     * @param text guhe 报告全文(已归一化 Kangxi 部首)
+     * @return 剥离分页残段行后的文本
+     */
+    public String stripTableTail(String text) {
+        if (text == null || text.isEmpty()) {
+            return text;
+        }
+        Pattern tailPattern = Pattern.compile(PAGE_TAIL_ASSERT_REGEX);
+        String[] lines = text.split("\r?\n", -1);
+        StringBuilder sb = new StringBuilder(text.length());
+        int removed = 0;
+        for (String line : lines) {
+            if (tailPattern.matcher(line).find()) {
+                removed++;
+                continue;
+            }
+            sb.append(line).append('\n');
+        }
+        if (removed > 0) {
+            log.debug("分页残段清洗: 剥离 {} 行分页表拼接残段(模式 {})", removed, PAGE_TAIL_ASSERT_REGEX);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 分页残段清洗校验断言入口(T12/L1-2 校验层:物种库条目不含 {@code \d{1,3}/16\d})。
+     *
+     * <p>断言清洗后产物「残段 0 命中」——文本中不存在分页页码拼接残段模式。
+     * 供解析管线在校验层调用,程序化校验清洗效果。</p>
+     *
+     * @param cleanedText 清洗后文本(或任一物种库条目字符串)
+     * @return true 表示残段 0 命中(校验通过);false 表示仍存在分页残段
+     */
+    public boolean assertTailClean(String cleanedText) {
+        if (cleanedText == null || cleanedText.isEmpty()) {
+            return true;
+        }
+        return !Pattern.compile(PAGE_TAIL_ASSERT_REGEX).matcher(cleanedText).find();
+    }
+
     /**
      * 归一化 Kangxi 部首到标准 CJK 汉字
      */