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

feat(api): 肠脑轴指标过滤+异常指标告警-认知/情绪分类+身体异常指标端点

- HealthReportService 新增 emotion/cognition 关键词过滤,getEmotionIndicators/getCognitionIndicators/getBodyAbnormalIndicators
- MindController: /api/mind/neurotransmitters 只返回情绪相关指标(血清素/多巴胺/GABA/色氨酸)
- WisdomGutController(新): /api/wisdom/gut-cognition 认知指标(谷氨酸/多巴胺/色氨酸/丁酸等)
- HealthReportController: /api/health/report/body-abnormal 疾病风险+营养/氨基酸异常(仅异常项)

Ultraworked with [Sisyphus](https://github.com/OhMyOpenCode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
E2E Test Bot 1 месяц назад
Родитель
Сommit
64650775be

+ 13 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/HealthReportController.java

@@ -2073,4 +2073,17 @@ public class HealthReportController {
             log.warn("AI解读触发失败: {}", e.getMessage());
             log.warn("AI解读触发失败: {}", e.getMessage());
         }
         }
     }
     }
+
+    /**
+     * 获取身体维度异常指标(身体页面"指标告警")
+     * 疾病风险:只返回非低风险项目
+     * 营养评估+氨基酸评估:只返回异常状态(偏高/偏低/缺乏等)
+     */
+    @PostMapping("/report/body-abnormal")
+    public Result<?> bodyAbnormal(@RequestBody Map<String, Object> params,
+                                  @RequestAttribute(value = "currentMemberId", required = false) Long currentMemberId) {
+        Long memberId = ParamUtils.getLong(params.get("memberId"), currentMemberId);
+        if (memberId == null) return Result.success(new java.util.HashMap<>());
+        return Result.success(healthReportService.getBodyAbnormalIndicators(memberId));
+    }
 }
 }

+ 3 - 3
cfc-backend/src/main/java/com/etotem/cfc/controller/mind/MindController.java

@@ -28,15 +28,15 @@ public class MindController {
     private EmiReportService emiReportService;
     private EmiReportService emiReportService;
 
 
     /**
     /**
-     * Get neurotransmitter indicators from latest gut microbiome report.
-     * Returns indicators categorized as "神经递质与激素" and "短链脂肪酸".
+     * Get emotion-related indicators from latest gut microbiome report.
+     * 只返回与情绪状态直接相关的神经递质指标(体现"心生身"概念)。
      */
      */
     @PostMapping("/neurotransmitters")
     @PostMapping("/neurotransmitters")
     public Result<?> neurotransmitters(@RequestBody Map<String, Object> params,
     public Result<?> neurotransmitters(@RequestBody Map<String, Object> params,
                                        @RequestAttribute(value = "currentMemberId", required = false) Long currentMemberId) {
                                        @RequestAttribute(value = "currentMemberId", required = false) Long currentMemberId) {
         try {
         try {
             Long memberId = ParamUtils.getLong(params.get("memberId"), currentMemberId);
             Long memberId = ParamUtils.getLong(params.get("memberId"), currentMemberId);
-            Object result = healthReportService.getMindRelatedIndicators(memberId);
+            Object result = healthReportService.getEmotionIndicators(memberId);
             return Result.success(result);
             return Result.success(result);
         } catch (Exception e) {
         } catch (Exception e) {
             log.error("获取神经递质指标失败", e);
             log.error("获取神经递质指标失败", e);

+ 45 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/wisdom/WisdomGutController.java

@@ -0,0 +1,45 @@
+package com.etotem.cfc.controller.wisdom;
+
+import com.etotem.cfc.common.Result;
+import com.etotem.cfc.service.HealthReportService;
+import com.etotem.cfc.util.ParamUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestAttribute;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * 智页面"肠胃与认知" — 肠脑轴认知指标
+ * 从菌群报告提取与认知/智力相关的指标(谷氨酸、丁酸等),体现"肠脑轴"概念。
+ */
+@Slf4j
+@RestController
+@RequestMapping("/api/wisdom")
+public class WisdomGutController {
+
+    @Resource
+    private HealthReportService healthReportService;
+
+    /**
+     * 获取与认知/智力相关的菌群报告指标(智页面"肠胃与认知")
+     * 无菌群报告时返回空列表,前端据此引导用户上传。
+     */
+    @PostMapping("/gut-cognition")
+    public Result<?> gutCognition(@RequestBody Map<String, Object> params,
+                                  @RequestAttribute(value = "currentMemberId", required = false) Long currentMemberId) {
+        try {
+            Long memberId = ParamUtils.getLong(params.get("memberId"), currentMemberId);
+            Object result = healthReportService.getCognitionIndicators(memberId);
+            return Result.success(result);
+        } catch (Exception e) {
+            log.error("获取肠胃认知指标失败", e);
+            return Result.success(Collections.emptyList());
+        }
+    }
+}

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

@@ -25,12 +25,15 @@ import org.springframework.transaction.annotation.Transactional;
 
 
 import javax.annotation.Resource;
 import javax.annotation.Resource;
 import java.util.ArrayList;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Comparator;
 import java.util.Date;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
+import java.util.Set;
 import com.etotem.cfc.util.SortUtil;
 import com.etotem.cfc.util.SortUtil;
 
 
 /**
 /**
@@ -362,6 +365,60 @@ public class HealthReportService {
         }
         }
     }
     }
 
 
+    /**
+     * 获取身体维度异常指标(身体页面"指标告警"专供)
+     * 返回:疾病风险(非低风险) + 主要营养评估异常 + 氨基酸评估异常
+     * 正常指标不显示,减少信息噪音
+     */
+    public Map<String, Object> getBodyAbnormalIndicators(Long memberId) {
+        HealthReport latest = getLatestReport(memberId);
+        Map<String, Object> result = new HashMap<>();
+        result.put("diseaseRisks", Collections.emptyList());
+        result.put("nutritionAbnormal", Collections.emptyList());
+        result.put("aminoAcidAbnormal", Collections.emptyList());
+        result.put("hasReport", latest != null);
+        if (latest == null) return result;
+
+        // 疾病风险:排除低风险
+        List<HealthDiseaseRisk> allRisks = getDiseaseRisksByReportId(latest.getId());
+        List<Map<String, Object>> abnormalRisks = new ArrayList<>();
+        for (HealthDiseaseRisk r : allRisks) {
+            if (r.getRiskLevel() != null && !"低风险".equals(r.getRiskLevel())) {
+                Map<String, Object> item = new HashMap<>();
+                item.put("name", r.getDiseaseName());
+                item.put("value", r.getRiskValue());
+                item.put("level", r.getRiskLevel());
+                abnormalRisks.add(item);
+            }
+        }
+        result.put("diseaseRisks", abnormalRisks);
+
+        // 主要营养评估 + 氨基酸评估:只保留异常状态
+        List<HealthIndicator> allIndicators = getReportIndicators(latest.getId());
+        List<Map<String, Object>> nutritionAbnormal = new ArrayList<>();
+        List<Map<String, Object>> aminoAcidAbnormal = new ArrayList<>();
+        for (HealthIndicator ind : allIndicators) {
+            String category = ind.getCategory();
+            String status = ind.getStatus();
+            if (status == null || "正常".equals(status) || "normal".equalsIgnoreCase(status)
+                    || "低风险".equals(status)) continue;
+            Map<String, Object> item = new HashMap<>();
+            item.put("name", ind.getIndicatorName());
+            item.put("value", ind.getIndicatorValue());
+            item.put("unit", ind.getUnit());
+            item.put("status", status);
+            item.put("refRange", ind.getRefRange());
+            if ("主要营养评估".equals(category)) {
+                nutritionAbnormal.add(item);
+            } else if ("氨基酸评估".equals(category)) {
+                aminoAcidAbnormal.add(item);
+            }
+        }
+        result.put("nutritionAbnormal", nutritionAbnormal);
+        result.put("aminoAcidAbnormal", aminoAcidAbnormal);
+        return result;
+    }
+
     /**
     /**
      * 获取报告完整详情(含指标明细 + 菌群数据 + 疾病风险评估)
      * 获取报告完整详情(含指标明细 + 菌群数据 + 疾病风险评估)
      */
      */
@@ -790,6 +847,81 @@ public class HealthReportService {
         return healthIndicatorMapper.selectList(indicatorWrapper);
         return healthIndicatorMapper.selectList(indicatorWrapper);
     }
     }
 
 
+    /**
+     * 情绪相关指标关键词(知识库"神经递质及激素"分类中与情绪状态直接相关的指标)
+     * 体现"心生身"概念:情绪状态通过神经递质反映在身体上
+     */
+    private static final Set<String> EMOTION_INDICATOR_KEYWORDS = new HashSet<>(Arrays.asList(
+            "血清素", "5-HT", "多巴胺", "GABA", "γ-氨基丁酸", "色氨酸", "褪黑素", "皮质醇"
+    ));
+
+    /**
+     * 认知相关指标关键词(与学习记忆、智力、注意力相关的肠脑轴指标)
+     * 含谷氨酸(学习记忆)、多巴胺(专注力,用户确认)、色氨酸(褪黑素前体→睡眠→记忆力)
+     */
+    private static final Set<String> COGNITION_INDICATOR_KEYWORDS = new HashSet<>(Arrays.asList(
+            "谷氨酸", "Glutamate", "乙酰胆碱", "多巴胺", "色氨酸", "丁酸", "丙酸", "乙酸", "组胺", "短链脂肪酸"
+    ));
+
+    /**
+     * 根据关键词过滤指标列表
+     */
+    private List<HealthIndicator> filterIndicatorsByKeywords(List<HealthIndicator> indicators, Set<String> keywords) {
+        if (indicators == null || indicators.isEmpty()) return Collections.emptyList();
+        List<HealthIndicator> result = new ArrayList<>();
+        for (HealthIndicator ind : indicators) {
+            if (ind.getIndicatorName() == null) continue;
+            String name = ind.getIndicatorName();
+            for (String kw : keywords) {
+                if (name.contains(kw)) {
+                    result.add(ind);
+                    break;
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 获取与情绪状态相关的菌群报告指标(心页面"情绪与肠胃")
+     * 体现"心生身":从菌群报告提取与情绪调节直接相关的神经递质指标
+     *
+     * @param childId the child's ID
+     * @return list of emotion-related HealthIndicator
+     */
+    public List<HealthIndicator> getEmotionIndicators(Long childId) {
+        List<HealthIndicator> all = getMindRelatedIndicators(childId);
+        return filterIndicatorsByKeywords(all, EMOTION_INDICATOR_KEYWORDS);
+    }
+
+    /**
+     * 获取与认知/智力相关的菌群报告指标(智页面"肠胃与认知")
+     * 体现"肠脑轴":肠道菌群代谢产物影响认知功能
+     *
+     * @param childId the child's ID
+     * @return list of cognition-related HealthIndicator
+     */
+    public List<HealthIndicator> getCognitionIndicators(Long childId) {
+        // Get latest gut flora report for this child
+        LambdaQueryWrapper<HealthReport> reportWrapper = new LambdaQueryWrapper<>();
+        reportWrapper.eq(HealthReport::getUserId, childId);
+        reportWrapper.eq(HealthReport::getReportType, "gut_flora");
+        reportWrapper.orderByDesc(HealthReport::getCreatedAt);
+        reportWrapper.last("LIMIT 1");
+        SortUtil.applySort(reportWrapper);
+        HealthReport report = healthReportMapper.selectOne(reportWrapper);
+        if (report == null) {
+            return Collections.emptyList();
+        }
+        LambdaQueryWrapper<HealthIndicator> indicatorWrapper = new LambdaQueryWrapper<>();
+        indicatorWrapper.eq(HealthIndicator::getReportId, report.getId());
+        indicatorWrapper.in(HealthIndicator::getCategory, "神经递质与激素", "短链脂肪酸");
+        indicatorWrapper.orderByAsc(HealthIndicator::getSortOrder);
+        SortUtil.applySort(indicatorWrapper);
+        List<HealthIndicator> all = healthIndicatorMapper.selectList(indicatorWrapper);
+        return filterIndicatorsByKeywords(all, COGNITION_INDICATOR_KEYWORDS);
+    }
+
     /**
     /**
      * Get gut-emotion insight summary based on neurotransmitter and SCFA indicators.
      * Get gut-emotion insight summary based on neurotransmitter and SCFA indicators.
      *
      *
@@ -797,7 +929,7 @@ public class HealthReportService {
      * @return map with summary text and indicator highlights
      * @return map with summary text and indicator highlights
      */
      */
     public Map<String, Object> getGutEmotionInsight(Long childId) {
     public Map<String, Object> getGutEmotionInsight(Long childId) {
-        List<HealthIndicator> indicators = getMindRelatedIndicators(childId);
+        List<HealthIndicator> indicators = getEmotionIndicators(childId);
         if (indicators.isEmpty()) {
         if (indicators.isEmpty()) {
             Map<String, Object> empty = new HashMap<>();
             Map<String, Object> empty = new HashMap<>();
             empty.put("summary", "暂无菌群报告数据");
             empty.put("summary", "暂无菌群报告数据");