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

feat: implement taxonomy level support, pathogen tabs, report interpretation, DB migration 98

asus 2 месяцев назад
Родитель
Сommit
b27a72e289

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

@@ -5291,6 +5291,7 @@ try {
         runMigration93();
         runMigration94();
         runMigration95();
+            runMigration96();
     }
 
     private void runMigration81() {
@@ -6135,5 +6136,14 @@ try {
     ensureColumn("growth_task", "dimension", "VARCHAR(20) NULL COMMENT '五维维度(body/mind/wisdom/action/wealth)'");
     ensureColumn("growth_task", "source_conversation_id", "VARCHAR(100) NULL COMMENT '来源AI会话ID'");
     log.info("迁移97: growth_task 表扩展完成");
+
+    // 迁移98: health_gut_flora 表扩展 level 字段长度(支持 CLASS/ORDER/FAMILY)
+    try {
+        jdbcTemplate.execute("ALTER TABLE health_gut_flora MODIFY COLUMN level VARCHAR(20) COMMENT '级别: CLASS/ORDER/FAMILY/GENUS/SPECIES'");
+        log.info("已扩展health_gut_flora.level字段长度");
+    } catch (Exception e) {
+        log.warn("扩展health_gut_flora.level失败: {}", e.getMessage());
+    }
+    log.info("迁移98: health_gut_flora 表扩展完成");
 }
 }

+ 21 - 0
cfc-backend/src/main/java/com/etotem/cfc/dto/ParsedReportPayload.java

@@ -169,6 +169,13 @@ public class ParsedReportPayload {
         private List<Indicator> indicators;
         private List<Flora> gutFlora;
         private List<Flora> probioticSpecies;
+        private List<Flora> taxonomyClass;       // 菌纲构成
+        private List<Flora> taxonomyOrder;       // 菌目构成
+        private List<Flora> taxonomyFamily;      // 菌科构成
+        private List<Flora> taxonomyGenus;       // 菌属构成
+        private List<Flora> taxonomySpecies;     // 菌种构成
+        private List<Flora> pathogenGenus;       // 病原菌属
+        private List<Flora> pathogenDetection;   // 病原菌检出
         private List<FoodItem> foods;
         private List<DiseaseRisk> diseaseRisks;
 
@@ -180,6 +187,20 @@ public class ParsedReportPayload {
         public void setGutFlora(List<Flora> gutFlora) { this.gutFlora = gutFlora; }
         public List<Flora> getProbioticSpecies() { return probioticSpecies; }
         public void setProbioticSpecies(List<Flora> probioticSpecies) { this.probioticSpecies = probioticSpecies; }
+        public List<Flora> getTaxonomyClass() { return taxonomyClass; }
+        public void setTaxonomyClass(List<Flora> taxonomyClass) { this.taxonomyClass = taxonomyClass; }
+        public List<Flora> getTaxonomyOrder() { return taxonomyOrder; }
+        public void setTaxonomyOrder(List<Flora> taxonomyOrder) { this.taxonomyOrder = taxonomyOrder; }
+        public List<Flora> getTaxonomyFamily() { return taxonomyFamily; }
+        public void setTaxonomyFamily(List<Flora> taxonomyFamily) { this.taxonomyFamily = taxonomyFamily; }
+        public List<Flora> getTaxonomyGenus() { return taxonomyGenus; }
+        public void setTaxonomyGenus(List<Flora> taxonomyGenus) { this.taxonomyGenus = taxonomyGenus; }
+        public List<Flora> getTaxonomySpecies() { return taxonomySpecies; }
+        public void setTaxonomySpecies(List<Flora> taxonomySpecies) { this.taxonomySpecies = taxonomySpecies; }
+        public List<Flora> getPathogenGenus() { return pathogenGenus; }
+        public void setPathogenGenus(List<Flora> pathogenGenus) { this.pathogenGenus = pathogenGenus; }
+        public List<Flora> getPathogenDetection() { return pathogenDetection; }
+        public void setPathogenDetection(List<Flora> pathogenDetection) { this.pathogenDetection = pathogenDetection; }
         public List<FoodItem> getFoods() { return foods; }
         public void setFoods(List<FoodItem> foods) { this.foods = foods; }
         public List<DiseaseRisk> getDiseaseRisks() { return diseaseRisks; }

+ 16 - 0
cfc-backend/src/main/java/com/etotem/cfc/entity/HealthGutFlora.java

@@ -16,6 +16,22 @@ import java.util.Date;
 @TableName("health_gut_flora")
 public class HealthGutFlora implements Serializable {
 
+    // ===== Level 常量 =====
+    public static final String LEVEL_CLASS = "CLASS";      // 纲
+    public static final String LEVEL_ORDER = "ORDER";      // 目
+    public static final String LEVEL_FAMILY = "FAMILY";    // 科
+    public static final String LEVEL_GENUS = "GENUS";      // 属
+    public static final String LEVEL_SPECIES = "SPECIES";  // 种
+
+    // ===== Category 常量 =====
+    public static final String CATEGORY_CORE = "core";                 // 核心菌属
+    public static final String CATEGORY_PROBIOTIC = "probiotic";       // 益生菌
+    public static final String CATEGORY_HARMFUL = "harmful";           // 有害菌
+    public static final String CATEGORY_OTHER = "other";               // 其它重要菌属
+    public static final String CATEGORY_PATHOGEN_GENUS = "pathogen_genus";        // 病原菌属
+    public static final String CATEGORY_PATHOGEN_DETECTION = "pathogen_detection"; // 病原菌检出
+    public static final String CATEGORY_TAXONOMY = "taxonomy";         // 菌群层级
+
     @TableId(type = IdType.AUTO)
     private Long id;
 

+ 20 - 9
cfc-backend/src/main/java/com/etotem/cfc/service/HealthReportService.java

@@ -1048,17 +1048,28 @@ public class HealthReportService {
 
     List<HealthGutFlora> buildGutFloraFromPayload(ParsedReportPayload.Payload payload) {
         List<HealthGutFlora> result = new ArrayList<>();
-        if (payload.getGutFlora() != null) {
-            for (ParsedReportPayload.Flora pf : payload.getGutFlora()) {
-                result.add(convertToGutFlora(pf));
-            }
-        }
-        if (payload.getProbioticSpecies() != null) {
-            for (ParsedReportPayload.Flora pf : payload.getProbioticSpecies()) {
-                result.add(convertToGutFlora(pf));
+        // 核心菌属 + 有害菌属等
+        addFloraList(result, payload.getGutFlora());
+        // 益生菌种
+        addFloraList(result, payload.getProbioticSpecies());
+        // Taxonomy 层级
+        addFloraList(result, payload.getTaxonomyClass());
+        addFloraList(result, payload.getTaxonomyOrder());
+        addFloraList(result, payload.getTaxonomyFamily());
+        addFloraList(result, payload.getTaxonomyGenus());
+        addFloraList(result, payload.getTaxonomySpecies());
+        // 病原菌
+        addFloraList(result, payload.getPathogenGenus());
+        addFloraList(result, payload.getPathogenDetection());
+        return result;
+    }
+
+    private void addFloraList(List<HealthGutFlora> target, List<ParsedReportPayload.Flora> source) {
+        if (source != null) {
+            for (ParsedReportPayload.Flora pf : source) {
+                target.add(convertToGutFlora(pf));
             }
         }
-        return result;
     }
 
     private HealthGutFlora convertToGutFlora(ParsedReportPayload.Flora pf) {

+ 42 - 0
cfc-frontend/pages/health/gut-flora-detail.vue

@@ -24,6 +24,10 @@
           <text class="badge" v-if="report.gutAge">肠龄: {{ report.gutAge }}</text>
           <text class="badge" v-if="report.gutType">肠型: {{ report.gutType }}</text>
         </view>
+        <!-- 报告解读 -->
+        <view class="interpretation-box" v-if="reportInterpretation && reportInterpretation.gutAgeAssessment">
+          <text class="interp-text">{{ reportInterpretation.gutAgeAssessment }}</text>
+        </view>
       </view>
 
       <!-- 评分圆环 -->
@@ -43,6 +47,16 @@
           <text class="nav-label">菌群详情</text>
           <text class="nav-count">{{ floraCount }}种</text>
         </view>
+        <view class="nav-card" @tap="goToPathogen">
+          <text class="nav-icon">🔬</text>
+          <text class="nav-label">病原菌</text>
+          <text class="nav-count">{{ pathogenCount }}项</text>
+        </view>
+        <view class="nav-card" @tap="goToTaxonomy">
+          <text class="nav-icon">📊</text>
+          <text class="nav-label">菌群层级</text>
+          <text class="nav-count">5级</text>
+        </view>
         <view class="nav-card" @tap="goToRisks">
           <text class="nav-icon">⚠️</text>
           <text class="nav-label">疾病风险</text>
@@ -170,6 +184,7 @@ export default {
       reportId: null,
       reportData: null,
       report: {},
+      reportInterpretation: null,
       isEditing: false,
       accordionOpen: { gut: false, nutri: false },
       scoreItems: [],
@@ -180,6 +195,7 @@ export default {
       riskCount: 0,
       foodCount: 0,
       indicatorCount: 0,
+      pathogenCount: 0,
       knowledgeVisible: false,
       knowledgeData: {},
       feedbackVisible: false,
@@ -200,12 +216,18 @@ export default {
         if (res.code === 200 && res.data) {
           self.reportData = res.data
           self.report = res.data.report || res.data
+          self.reportInterpretation = res.data.reportInterpretation || null
           self.buildScores(res.data)
           self.buildIndicators(res.data)
           self.floraCount = (res.data.gutFlora || []).length
           self.riskCount = (res.data.diseaseRisks || []).length
           self.foodCount = (res.data.foods || []).length
           self.indicatorCount = (res.data.indicators || []).length
+          // 计算病原菌总数
+          var flora = res.data.gutFlora || []
+          self.pathogenCount = flora.filter(function(i) {
+            return i.category === 'pathogen_genus' || i.category === 'pathogen_detection'
+          }).length
         }
       })
     },
@@ -319,6 +341,12 @@ export default {
     goToSpecies: function() {
       uni.navigateTo({ url: '/pages/health/gut-flora-species-detail?reportId=' + this.reportId })
     },
+    goToPathogen: function() {
+      uni.navigateTo({ url: '/pages/health/gut-flora-species-detail?reportId=' + this.reportId + '&tab=pathogen_genus' })
+    },
+    goToTaxonomy: function() {
+      uni.navigateTo({ url: '/pages/health/gut-flora-species-detail?reportId=' + this.reportId + '&tab=taxonomy_class' })
+    },
     goToRisks: function() {
       uni.navigateTo({ url: '/pages/health/gut-flora-risks-detail?reportId=' + this.reportId })
     },
@@ -409,4 +437,18 @@ export default {
 .feedback-arrow { font-size: 28rpx; color: #ccc; }
 
 .bottom-spacer { height: 40rpx; }
+
+/* 报告解读 */
+.interpretation-box {
+  background: #FFF8E1;
+  border-radius: 12rpx;
+  padding: 16rpx 20rpx;
+  margin: 16rpx 0 0;
+  border-left: 6rpx solid #FFB300;
+}
+.interp-text {
+  font-size: 24rpx;
+  color: #795548;
+  line-height: 1.6;
+}
 </style>

+ 39 - 10
cfc-frontend/pages/health/gut-flora-species-detail.vue

@@ -76,13 +76,13 @@ export default {
         { key: 'probiotic', label: '益生菌' },
         { key: 'harmful', label: '有害菌' },
         { key: 'other', label: '其它菌属' },
-        { key: 'pathogen', label: '病原菌' },
-        { key: 'obesity', label: '肥胖相关' },
-        { key: 'constipation', label: '便秘相关' },
-        { key: 'depression', label: '抑郁相关' },
-        { key: 'allergy', label: '过敏相关' },
-        { key: 'bloating', label: '腹胀相关' },
-        { key: 'insomnia', label: '失眠相关' },
+        { key: 'pathogen_genus', label: '病原菌' },
+        { key: 'pathogen_detection', label: '病原菌检出' },
+        { key: 'taxonomy_class', label: '菌纲' },
+        { key: 'taxonomy_order', label: '菌目' },
+        { key: 'taxonomy_family', label: '菌科' },
+        { key: 'taxonomy_genus', label: '菌属' },
+        { key: 'taxonomy_species', label: '菌种' },
         { key: 'all', label: '全部' }
       ]
     }
@@ -96,13 +96,42 @@ export default {
           ? this.editFlora.filter(function(i) { return i.category === 'probiotic' })
           : this.probioticSpecies
       }
-      return list.filter(function(item) {
-        return item.category === this.selectedTab
-      }.bind(this))
+      // Taxonomy tabs: filter by category='taxonomy' + level
+      if (this.selectedTab.indexOf('taxonomy_') === 0) {
+        var levelMap = {
+          'taxonomy_class': 'CLASS',
+          'taxonomy_order': 'ORDER',
+          'taxonomy_family': 'FAMILY',
+          'taxonomy_genus': 'GENUS',
+          'taxonomy_species': 'SPECIES'
+        }
+        var targetLevel = levelMap[this.selectedTab]
+        return list.filter(function(item) {
+          return item.category === 'taxonomy' && item.level === targetLevel
+        })
+      }
+      // Other category tabs: filter by category field
+      var categoryMap = {
+        'core': 'core',
+        'harmful': 'harmful',
+        'other': 'other',
+        'pathogen_genus': 'pathogen_genus',
+        'pathogen_detection': 'pathogen_detection'
+      }
+      var targetCategory = categoryMap[this.selectedTab]
+      if (targetCategory) {
+        return list.filter(function(item) {
+          return item.category === targetCategory
+        })
+      }
+      return []
     }
   },
   onLoad: function(options) {
     this.reportId = options.reportId ? parseInt(options.reportId) : null
+    if (options.tab) {
+      this.selectedTab = options.tab
+    }
     if (this.reportId) {
       this.loadData()
     }