Преглед изворни кода

feat(health): 健康报告增加 report_format 字段区分北京菌群/谷禾报告格式,前端报告详情适配

liaoxg пре 2 месеци
родитељ
комит
8f489e82fd

+ 2 - 0
cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java

@@ -8492,6 +8492,8 @@ public class DatabaseInitializer implements CommandLineRunner {
         ensureColumn("health_reports", "proxy_uploader_id", "BIGINT COMMENT '代上传人ID(规划师/营养师)'");
         ensureColumn("health_reports", "proxy_upload_note", "TEXT COMMENT '代上传备注'");
         ensureColumn("health_reports", "ai_analysis", "TEXT COMMENT 'AI解读结果JSON'");
+        // 迁移180b: health_reports 添加报告来源格式(beijing/guhe,两种菌群报告数据结构不兼容,需区分)
+        ensureColumn("health_reports", "report_format", "VARCHAR(32) COMMENT '报告来源格式: beijing(北京菌群)/guhe(谷禾)'");
 
         // 迁移181: health_report_drafts 添加备注字段(代上传说明文字)
         ensureColumn("health_report_drafts", "remark", "VARCHAR(500) DEFAULT NULL COMMENT '备注(如代上传时的说明文字)'");

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

@@ -77,6 +77,9 @@ public class ParsedReportResult {
     /** 食材适宜度 */
     private List<ParsedFoodSuitability> foodSuitability;
 
+    /** 报告格式: beijing(北京菌群), guhe(谷禾/募极生物) */
+    private String reportFormat;
+
     // ==================== getters/setters ====================
 
     public Integer getOverallScore() {
@@ -254,4 +257,12 @@ public class ParsedReportResult {
     public void setFoodSuitability(List<ParsedFoodSuitability> foodSuitability) {
         this.foodSuitability = foodSuitability;
     }
+
+    public String getReportFormat() {
+        return reportFormat;
+    }
+
+    public void setReportFormat(String reportFormat) {
+        this.reportFormat = reportFormat;
+    }
 }

+ 3 - 0
cfc-backend/src/main/java/com/etotem/cfc/entity/HealthReport.java

@@ -32,6 +32,9 @@ public class HealthReport implements Serializable {
     /** 报告类型: physical_exam(体检报告), gut_flora(菌群检测报告) */
     private String reportType;
 
+    /** 报告格式: beijing(北京菌群), guhe(谷禾/募极生物), null(未知/体检报告) */
+    private String reportFormat;
+
     /** 报告编号 */
     private String reportNumber;
 

+ 4 - 1
cfc-backend/src/main/java/com/etotem/cfc/service/PdfParseService.java

@@ -143,7 +143,9 @@ public ParsedReportResult parseText(String fullText) {
         // 2. 检测是否为北京菌群报告格式
         if (isBeijingFormat(normalized)) {
             log.debug("PDF 检测格式: beijing");
-            return parseBeijingReport(normalized);
+            ParsedReportResult beijingResult = parseBeijingReport(normalized);
+            beijingResult.setReportFormat("beijing");
+            return beijingResult;
         }
 
         // 3. 按行分割
@@ -170,6 +172,7 @@ public ParsedReportResult parseText(String fullText) {
         } else {
             parseTripletReport(lineList, result);
         }
+        result.setReportFormat("guhe");
 
         // 6. 解析食材推荐(格式无关)
         List<ParsedFoodSuitability> foodSuitability = parseFoodSuitability(normalized);

+ 1 - 0
cfc-backend/src/main/resources/schema.sql

@@ -1738,6 +1738,7 @@ CREATE TABLE IF NOT EXISTS health_reports (
     subject_id BIGINT COMMENT '报告主体用户ID(关联users表,为null表示未绑定)',
     family_id BIGINT COMMENT '家庭ID',
     report_type VARCHAR(32) NOT NULL COMMENT '报告类型: physical_exam(体检)/gut_flora(菌群检测)',
+    report_format VARCHAR(32) COMMENT '报告来源格式: beijing(北京菌群)/guhe(谷禾)',
     report_date DATE COMMENT '报告日期',
     overall_score INT COMMENT '健康总分(0-100)',
     gut_health_score INT COMMENT '菌群健康分',

+ 31 - 13
cfc-frontend/pages/health/report-detail.vue

@@ -414,6 +414,7 @@ export default {
       confirming: false,
       payload: {
         type: 'gut_flora',
+        reportFormat: '',
         summary: {},
         indicators: [],
         gutFlora: [],
@@ -595,6 +596,10 @@ export default {
           if (res.data.payload) {
             self.payload = Object.assign({}, self.defaultPayload(), res.data.payload)
           }
+          // 报告来源格式优先取 payload,兜底取 report 实体字段
+          if (!self.payload.reportFormat && report.reportFormat) {
+            self.payload.reportFormat = report.reportFormat
+          }
           // 后端无 blocks 时从 payload 自动组装
           var cleanBlocks = self.stripFoodBlocks(backendBlocks)
           if (cleanBlocks.length > 0) {
@@ -620,6 +625,7 @@ export default {
     defaultPayload() {
       return {
         type: 'gut_flora',
+        reportFormat: '',
         summary: {},
         indicators: [],
         gutFlora: [],
@@ -1072,25 +1078,37 @@ export default {
       var p = this.payload
       if (!p) return blocks
 
-      // 1. 评分圆环块
+      // 1. 评分圆环块(北京/谷禾报告评分体系不同:北京用 GMHI 指数,谷禾用健康总分)
       var scoreItems = []
+      var isBeijing = p.reportFormat === 'beijing'
       if (p.summary) {
-        this._addScoreItem(scoreItems, '综合', p.summary.overallScore)
-        this._addScoreItem(scoreItems, '菌群健康', p.summary.gutHealthScore)
-        this._addScoreItem(scoreItems, '慢病控制', p.summary.chronicDiseaseScore)
-        // 子项
-        if (p.summary.nutritionScore != null) { this._addSubScore(scoreItems, '综合', '营养均衡', p.summary.nutritionScore) }
-        if (p.summary.gutBalanceScore != null) { this._addSubScore(scoreItems, '菌群健康', '平衡', p.summary.gutBalanceScore) }
-        if (p.summary.gutDiversityScore != null) { this._addSubScore(scoreItems, '菌群健康', '多样性', p.summary.gutDiversityScore) }
-        if (p.summary.beneficialBacteriaScore != null) { this._addSubScore(scoreItems, '菌群健康', '有益菌', p.summary.beneficialBacteriaScore) }
-        if (p.summary.harmfulBacteriaScore != null) { this._addSubScore(scoreItems, '菌群健康', '有害菌', p.summary.harmfulBacteriaScore) }
-        if (p.summary.coreSpeciesScore != null) { this._addSubScore(scoreItems, '菌群健康', '核心菌属', p.summary.coreSpeciesScore) }
-        // 肠道年龄/肠型
+        if (isBeijing) {
+          // 北京菌群报告:GMHI 肠道微生物健康指数为主评分
+          this._addScoreItem(scoreItems, 'GMHI 肠道微生物健康指数', p.summary.overallScore != null ? p.summary.overallScore : p.summary.gmhiIndex)
+          this._addScoreItem(scoreItems, '菌群健康', p.summary.gutHealthScore)
+          if (p.summary.gutBalanceScore != null) { this._addSubScore(scoreItems, '菌群健康', '平衡', p.summary.gutBalanceScore) }
+          if (p.summary.gutDiversityScore != null) { this._addSubScore(scoreItems, '菌群健康', '多样性', p.summary.gutDiversityScore) }
+          if (p.summary.beneficialBacteriaScore != null) { this._addSubScore(scoreItems, '菌群健康', '有益菌', p.summary.beneficialBacteriaScore) }
+          if (p.summary.coreSpeciesScore != null) { this._addSubScore(scoreItems, '菌群健康', '核心菌属', p.summary.coreSpeciesScore) }
+        } else {
+          // 谷禾/其它报告:健康总分 + 细分维度
+          this._addScoreItem(scoreItems, '综合', p.summary.overallScore)
+          this._addScoreItem(scoreItems, '菌群健康', p.summary.gutHealthScore)
+          this._addScoreItem(scoreItems, '慢病控制', p.summary.chronicDiseaseScore)
+          // 子项
+          if (p.summary.nutritionScore != null) { this._addSubScore(scoreItems, '综合', '营养均衡', p.summary.nutritionScore) }
+          if (p.summary.gutBalanceScore != null) { this._addSubScore(scoreItems, '菌群健康', '平衡', p.summary.gutBalanceScore) }
+          if (p.summary.gutDiversityScore != null) { this._addSubScore(scoreItems, '菌群健康', '多样性', p.summary.gutDiversityScore) }
+          if (p.summary.beneficialBacteriaScore != null) { this._addSubScore(scoreItems, '菌群健康', '有益菌', p.summary.beneficialBacteriaScore) }
+          if (p.summary.harmfulBacteriaScore != null) { this._addSubScore(scoreItems, '菌群健康', '有害菌', p.summary.harmfulBacteriaScore) }
+          if (p.summary.coreSpeciesScore != null) { this._addSubScore(scoreItems, '菌群健康', '核心菌属', p.summary.coreSpeciesScore) }
+        }
+        // 肠道年龄/肠型(两种格式均有或部分有)
         if (p.summary.gutAge) { this._addScoreItem(scoreItems, '肠道年龄', p.summary.gutAge) }
         if (p.summary.gutType) { this._addScoreItem(scoreItems, '肠型', p.summary.gutType) }
       }
       if (scoreItems.length > 0) {
-        blocks.push({ type: 'score', title: '健康评分', items: scoreItems })
+        blocks.push({ type: 'score', title: isBeijing ? '肠道微生物健康指数' : '健康评分', items: scoreItems })
       }
 
       // 2. 疾病风险分组块