Просмотр исходного кода

feat(guhe.l1.versionSplit): S-cfc-84 T8/L1-3 按报告格式版本分流 — 32键guhe_v2走专用提取器不落LLM兜底、旧21键保留原v5流程

实现(计划 2026-11-08-浠艾福平台程序修改计划 §三-T8 / §一-L1③):
- ParsedReportResult 暴露 guhe_v2 格式版本标记 FORMAT_GUHE_V2="guhe_v2" + 21/32 键数边界常量(LEGACY_KEYS_21=21 / GUHE_V2_KEYS_32=32)
- ReportParserDispatchService 新增 routeByFormatVersion 分流入口:guhe_v2(32键)→extract_guhe_v2 专用分支(禁误判路由错误→unknown/LLM兜底);旧21键/beijing/null→原 parseReport v5流程(禁强套guhe_v2)
- 契约测试 GuheVersionSplitContractTest 5/5 GREEN(含 32键不落LLM / 21键不强套 / 键数边界三断言)

C档平台现状探测(§四 待确认项,自主探测记录,不阻塞派发):
1. Adapter形态:无统一Adapter接口;L1=PdfParseService(文本,beijing/guhe)+ReportParserDispatchService(指纹→Python解析器/LLM)+ReportFingerprintService(DB指纹规则→Python引擎)。判定:新增guhe_v2分支,非重写路由。
2. KB加载方式:Java侧知识库走DB(HealthKnowledgeBase/KnowledgeBaseController);L4归因标准值取DB(AttributionNormValue/HealthNormReference/CognitiveNorm);Python侧另有rag/microbiome_loader文件加载。判定:L2改造走服务/DB接口新增,非纯文件升级。
3. L4引擎存在性:存在——service/attribution 已有 FiveDimAttributionEngine/MainCauseChainEngine/NormCompareEngine/IndicatorInterpretEngine/FalsificationEngine。判定:低侧规则补规则类,非新建规则表。
4. 合规门禁挂载点:已有门禁框架——AttributionComplianceGate(a13bb8192,G-1低侧红线组)+AttributionReportBuilder.sections.complianceGate。判定:G-1扩展挂 AttributionComplianceGate(L4出结论后/L5报告构建前)。

验证:mvn compile EXIT=0;mvn test -Dtest=GuheVersionSplitContractTest 5/5 GREEN;cfc-web npm run build EXIT=0(prebuild副作用 .last_build_commit 已还原)。
Sisyphus 4 дней назад
Родитель
Сommit
4ef901c484

+ 15 - 0
cfc-backend/src/main/java/com/etotem/cfc/dto/ParsedReportResult.java

@@ -7,6 +7,21 @@ import java.util.List;
  */
  */
 public class ParsedReportResult {
 public class ParsedReportResult {
 
 
+    // ==================== 格式版本标记(guhe.l1.versionSplit / T8-L1-3) ====================
+
+    /**
+     * guhe_v2 新版标准报告格式标记(计划《2026-11-08-浠艾福平台程序修改计划》§2.1):
+     * 谷禾 2026-11 新版标准报告 = 旧 21 键打底 + 11 增量键 = 32 键;
+     * 判定链 {@code aidefansi → beijing → guhe_v2 → standard},旧三类路由不变。
+     */
+    public static final String FORMAT_GUHE_V2 = "guhe_v2";
+
+    /** 旧版谷禾报告键数(21 键):走原 v5 流程,禁止被 guhe_v2 提取器强套(T8/L1-3) */
+    public static final int LEGACY_KEYS_21 = 21;
+
+    /** guhe_v2 新版标准报告键数(32 键 = 21 键打底 + 11 增量键):走专用提取器,不落入 unknown→LLM 兜底(T8/L1-3) */
+    public static final int GUHE_V2_KEYS_32 = 32;
+
     // ==================== 汇总 ====================
     // ==================== 汇总 ====================
 
 
     /** 健康总分 */
     /** 健康总分 */

+ 51 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/ReportParserDispatchService.java

@@ -1,6 +1,7 @@
 package com.etotem.cfc.service;
 package com.etotem.cfc.service;
 
 
 import com.etotem.cfc.common.Result;
 import com.etotem.cfc.common.Result;
+import com.etotem.cfc.dto.ParsedReportResult;
 import com.etotem.cfc.entity.ReportUnknownCluster;
 import com.etotem.cfc.entity.ReportUnknownCluster;
 import com.etotem.cfc.entity.ReportUnknownUpload;
 import com.etotem.cfc.entity.ReportUnknownUpload;
 import com.etotem.cfc.mapper.ReportUnknownClusterMapper;
 import com.etotem.cfc.mapper.ReportUnknownClusterMapper;
@@ -49,6 +50,56 @@ public class ReportParserDispatchService {
 
 
     private static final int AUTO_LEARN_THRESHOLD = 3;
     private static final int AUTO_LEARN_THRESHOLD = 3;
 
 
+    // ==================== guhe_v2 格式版本分流(T8/L1-3) ====================
+
+    /** guhe_v2 专用解析器脚本名(32 键新版标准报告应走此提取器,不落入 unknown→LLM 兜底) */
+    private static final String GUHE_V2_PARSER_SCRIPT = "extract_guhe_v2";
+
+    /** guhe_v2 报告指纹类型标识(32 键新版标准报告) */
+    private static final String GUHE_V2_TYPE_ID = "gut_flora_guhe_v2";
+
+    /**
+     * 按《报告格式版本》分流(计划《2026-11-08-浠艾福平台程序修改计划》§三-T8 / L1-3):
+     * <ul>
+     *   <li><b>32 键 guhe_v2</b>({@link ParsedReportResult#FORMAT_GUHE_V2},
+     *       {@link ParsedReportResult#GUHE_V2_KEYS_32}):走 {@link #GUHE_V2_PARSER_SCRIPT} 专用提取分支,
+     *       <b>不落入</b> unknown→LLM 兜底(禁把 32 键误判成路由错误);</li>
+     *   <li>其余格式版本(旧 21 键 guhe {@link ParsedReportResult#LEGACY_KEYS_21} / beijing / null):
+     *       走原 v5 流程 {@link #parseReport},<b>禁止</b>被 guhe_v2 提取器强套。</li>
+     * </ul>
+     *
+     * @param reportFormatVersion 报告格式版本(guhe_v2 为 {@link ParsedReportResult#FORMAT_GUHE_V2};旧版传 null/其它)
+     * @return 解析结果(guhe_v2 走专用提取器;旧版走原 v5 流程)
+     */
+    public Result<Map<String, Object>> routeByFormatVersion(String reportFormatVersion, String pdfPath, String fileName) {
+        if (ParsedReportResult.FORMAT_GUHE_V2.equals(reportFormatVersion)) {
+            log.info("报告格式版本=guhe_v2(32 键新版),走专用提取器 extract_guhe_v2,不落 LLM 兜底");
+            return parseGuheV2(pdfPath, fileName);
+        }
+        log.info("报告格式版本={}(旧 21 键 / beijing / 未知),走原 v5 流程,不强套 guhe_v2",
+                reportFormatVersion == null ? "null(旧版)" : reportFormatVersion);
+        return parseReport(pdfPath, fileName);
+    }
+
+    /**
+     * 32 键 guhe_v2 专用解析分支:调用 {@link #GUHE_V2_PARSER_SCRIPT} 提取器。
+     * 提取器未就绪时返回明确错误,<b>绝不</b>回退到 unknown→LLM 兜底(T8 禁把 32 键误判成路由错误)。
+     */
+    private Result<Map<String, Object>> parseGuheV2(String pdfPath, String fileName) {
+        Map<String, Object> result = new LinkedHashMap<>();
+        Map<String, Object> parsed = fingerprintService.runParser(GUHE_V2_PARSER_SCRIPT, pdfPath);
+        if (parsed == null) {
+            log.warn("guhe_v2 提取器未就绪({} 缺失),32 键报告保持专用分支状态,不进入 LLM 兜底", GUHE_V2_PARSER_SCRIPT);
+            result.put("typeId", GUHE_V2_TYPE_ID);
+            result.put("parseMethod", "guhe_v2_pending_extractor");
+            return Result.error("guhe_v2 提取器未就绪,请平台装配 extract_guhe_v2 脚本后重试");
+        }
+        result.put("typeId", GUHE_V2_TYPE_ID);
+        result.put("parsedData", parsed);
+        result.put("parseMethod", "dedicated_parser_guhe_v2");
+        return Result.success(result);
+    }
+
     /**
     /**
      * 解析报告:指纹检测 → 专用解析器 或 LLM 兜底
      * 解析报告:指纹检测 → 专用解析器 或 LLM 兜底
      */
      */