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

fix(report): T29-B1FIX 代传前置缺陷修复 — V-01 score 兜底 + V-02 report_date 提取

V-01: HealthReportService.saveFoodSuitability 中 FoodItem.score 为 null 时兜底为 0,
      修复 report_food_suitability.score (NOT NULL 无默认值) 导致的 proxyConfirm 全链路 500。
      不改数据库 schema,不新增迁移。
V-02: cfc-langgraph/pdf_parser 补 report_date 提取(报告/采样/检测日期及常见形态,归一 YYYY-MM-dd);
      report_parse_agent 补 _CN_TO_EN_OVERVIEW 中文键映射与 REPORT_PARSE_GUT_TEMPLATE overview
      schema 的 report_date 可选字段(不影响 brain/cognitive 的 reportDate 口径)。
      无法提取到日期时保持 null,前端预览页降级显示 '-'.

运行态自证(测试库 192.168.16.251:3306/zxyj,临时用例已删):
- 前置复现:score=null 插入被拒(Field 'score' doesn't have a default value)→ 500 根因确认;
- 修复后 auditConfirm 全链路不再抛异常,report_food_suitability 有行且 score 非 null(缺省 0、显式 88 保留);
- 代传报告 proxy_upload_note = 草稿 remark(非空)。

基线: 7680b21e7 (origin/feature/proxy-fix-b1-core)
E2E Test Bot 2 недель назад
Родитель
Сommit
c1df0f17de

+ 3 - 1
cfc-backend/src/main/java/com/etotem/cfc/service/HealthReportService.java

@@ -1873,7 +1873,9 @@ public class HealthReportService {
             suit.setReportId(reportId);
             suit.setFoodName(item.getName().trim());
             suit.setCategory(item.getCategory());
-            suit.setScore(item.getScore());
+            // V-01 fix: report_food_suitability.score 为 NOT NULL(无默认值),DTO 缺省时兜底为 0,
+            // 避免 proxyConfirm 全链路 500(不改数据库 schema)。
+            suit.setScore(item.getScore() == null ? 0 : item.getScore());
             suit.setCreatedAt(now);
 
             // 匹配 foodId

+ 5 - 1
cfc-langgraph/app/agents/report_parse_agent.py

@@ -49,7 +49,7 @@ REPORT_PARSE_GUT_TEMPLATE = """你是一个肠道菌群检测报告解析专家
 
 请按以下JSON Schema返回:
 {{
-    "overview": {{ "person_name": "", "report_number": "", "age": 0, "gender": "male/female",
+    "overview": {{ "person_name": "", "report_number": "", "report_date": "报告日期 yyyy-MM-dd(无法提取则为 null)", "age": 0, "gender": "male/female",
         "overallScore": 0, "gutHealthScore": 0, "chronicDiseaseScore": 0, "nutritionScore": 0,
         "gutAge": "", "gutType": "", "gutStatusLevel": "" }},
     "disease_risks": [{{"name": "", "value": "", "status": "",
@@ -217,6 +217,10 @@ _CN_TO_EN_OVERVIEW: dict[str, str] = {
     '有益菌': 'beneficialScore',
     '有害菌': 'harmfulScore',
     '核心菌属': 'coreGenusScore',
+    # V-02 fix: 报告日期相关中文键 → report_date(算法解析器直接产出英文键,此处兼容 LLM 兜底返回中文键)
+    '报告日期': 'report_date',
+    '采样日期': 'report_date',
+    '检测日期': 'report_date',
 }
 
 # 指标分区 → Java 消费端 indicators 的 category(与 Java PdfParseService 兜底路径保持一致)

+ 32 - 0
cfc-langgraph/app/parsers/pdf_parser.py

@@ -135,6 +135,30 @@ def extract_text(file_path):
     return lines
 
 
+def _normalize_report_date(raw):
+    """将常见中文/数字日期字符串归一为 YYYY-MM-DD;无法识别返回空串。"""
+    raw = (raw or '').strip()
+    m = re.search(r'(\d{4})\s*[-/年.]\s*(\d{1,2})\s*[-/月.]\s*(\d{1,2})', raw)
+    if m:
+        return f"{m.group(1)}-{int(m.group(2)):02d}-{int(m.group(3)):02d}"
+    return ''
+
+
+def _extract_report_date(text):
+    """从报告文本提取报告日期/采样日期/检测日期等,归一为 YYYY-MM-DD。
+
+    命中失败返回空串(保持 overview.report_date=null,不造假值)。
+    """
+    for kw in ('报告出具日期', '采样日期', '检测日期', '报告日期',
+               '采样时间', '检测时间', '报告时间'):
+        m = re.search(rf'{kw}\s*[::]?\s*(\d{{4}}\s*[-/年.]\s*\d{{1,2}}\s*[-/月.]\s*\d{{1,2}})', text)
+        if m:
+            d = _normalize_report_date(m.group(1))
+            if d:
+                return d
+    return ''
+
+
 def parse_overview(lines):
     """提取报告概述"""
     text = '\n'.join(lines)
@@ -155,6 +179,10 @@ def parse_overview(lines):
     if m: r['gut_age'] = m.group(1)
     m = re.search(r'肠型[::\s]*(\S+)', text)
     if m: r['gut_type'] = m.group(1)
+    # V-02 fix: 补 report_date 提取(报告/采样/检测日期常见形态);无则保持 null
+    report_date = _extract_report_date(text)
+    if report_date:
+        r['report_date'] = report_date
     return r
 
 
@@ -1842,6 +1870,10 @@ def _parse_bj_overview(text):
     if m: r['age'] = m.group(1)
     m = re.search(r'性\s*别[::\s]*(\S+)', text)
     if m: r['gender'] = 'male' if m.group(1) in ('男', 'M') else 'female'
+    # V-02 fix: 补 report_date 提取;无则保持 null(不造假值)
+    report_date = _extract_report_date(text)
+    if report_date:
+        r['report_date'] = report_date
     # 肠型
     gut_type = _parse_bj_gut_type(text)
     if gut_type: r['gut_type'] = gut_type