소스 검색

Merge remote-tracking branch 'origin/cfclub' into cfclub

liaoxg 1 개월 전
부모
커밋
f7c81f5f93

+ 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());
         }
     }
+
+    /**
+     * 获取身体维度异常指标(身体页面"指标告警")
+     * 疾病风险:只返回非低风险项目
+     * 营养评估+氨基酸评估:只返回异常状态(偏高/偏低/缺乏等)
+     */
+    @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;
 
     /**
-     * Get neurotransmitter indicators from latest gut microbiome report.
-     * Returns indicators categorized as "神经递质与激素" and "短链脂肪酸".
+     * Get emotion-related indicators from latest gut microbiome report.
+     * 只返回与情绪状态直接相关的神经递质指标(体现"心生身"概念)。
      */
     @PostMapping("/neurotransmitters")
     public Result<?> neurotransmitters(@RequestBody Map<String, Object> params,
                                        @RequestAttribute(value = "currentMemberId", required = false) Long currentMemberId) {
         try {
             Long memberId = ParamUtils.getLong(params.get("memberId"), currentMemberId);
-            Object result = healthReportService.getMindRelatedIndicators(memberId);
+            Object result = healthReportService.getEmotionIndicators(memberId);
             return Result.success(result);
         } catch (Exception 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 java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 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);
     }
 
+    /**
+     * 情绪相关指标关键词(知识库"神经递质及激素"分类中与情绪状态直接相关的指标)
+     * 体现"心生身"概念:情绪状态通过神经递质反映在身体上
+     */
+    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.
      *
@@ -797,7 +929,7 @@ public class HealthReportService {
      * @return map with summary text and indicator highlights
      */
     public Map<String, Object> getGutEmotionInsight(Long childId) {
-        List<HealthIndicator> indicators = getMindRelatedIndicators(childId);
+        List<HealthIndicator> indicators = getEmotionIndicators(childId);
         if (indicators.isEmpty()) {
             Map<String, Object> empty = new HashMap<>();
             empty.put("summary", "暂无菌群报告数据");

+ 149 - 1
cfc-frontend/pages/body-detail/index.vue

@@ -55,6 +55,49 @@
       accent-color="#FF8C42"
       :dims="healthSubDims" />
 
+    <!-- 指标告警:疾病风险+营养/氨基酸异常(仅显示异常项) -->
+    <view class="section alert-card" v-if="isLoggedIn && bodyAbnormalData">
+      <view class="section-header">
+        <text class="section-title">⚠️ 指标告警</text>
+        <text class="section-subtitle">仅显示异常指标</text>
+      </view>
+      <template v-if="!bodyAbnormalData.hasReport">
+        <view class="alert-empty">
+          <text class="alert-empty-text">上传菌群报告后查看异常指标</text>
+          <button class="alert-upload-btn" @click="goToUploadReport">去上传</button>
+        </view>
+      </template>
+      <template v-else>
+        <!-- 疾病风险 -->
+        <view class="alert-group" v-if="bodyAbnormalData.diseaseRisks && bodyAbnormalData.diseaseRisks.length > 0">
+          <text class="alert-group-title">🩺 疾病风险</text>
+          <view class="alert-item" v-for="(risk, idx) in bodyAbnormalData.diseaseRisks" :key="'dr' + idx">
+            <text class="alert-item-name">{{ risk.name }}</text>
+            <text :class="['alert-item-level', 'level-' + (risk.level === '高风险' ? 'high' : 'warn')]">{{ risk.level }}</text>
+          </view>
+        </view>
+        <!-- 主要营养素异常 -->
+        <view class="alert-group" v-if="bodyAbnormalData.nutritionAbnormal && bodyAbnormalData.nutritionAbnormal.length > 0">
+          <text class="alert-group-title">🥗 主要营养素</text>
+          <view class="alert-item" v-for="(item, idx) in bodyAbnormalData.nutritionAbnormal" :key="'nt' + idx">
+            <text class="alert-item-name">{{ item.name }}</text>
+            <text class="alert-item-status">{{ item.status }}</text>
+          </view>
+        </view>
+        <!-- 氨基酸异常 -->
+        <view class="alert-group" v-if="bodyAbnormalData.aminoAcidAbnormal && bodyAbnormalData.aminoAcidAbnormal.length > 0">
+          <text class="alert-group-title">🧬 氨基酸</text>
+          <view class="alert-item" v-for="(item, idx) in bodyAbnormalData.aminoAcidAbnormal" :key="'aa' + idx">
+            <text class="alert-item-name">{{ item.name }}</text>
+            <text class="alert-item-status">{{ item.status }}</text>
+          </view>
+        </view>
+        <view class="alert-no-data" v-if="(!bodyAbnormalData.diseaseRisks || bodyAbnormalData.diseaseRisks.length === 0) && (!bodyAbnormalData.nutritionAbnormal || bodyAbnormalData.nutritionAbnormal.length === 0) && (!bodyAbnormalData.aminoAcidAbnormal || bodyAbnormalData.aminoAcidAbnormal.length === 0)">
+          <text class="alert-no-data-text">暂无异常指标,保持健康状态</text>
+        </view>
+      </template>
+    </view>
+
     <!-- 功能入口 -->
     <view class="func-section" v-if="sectionVisible('func_entries')">
       <view class="func-grid">
@@ -109,7 +152,7 @@ import DimensionSubDims from '../../components/DimensionSubDims.vue'
 import FloatingAvatar from '../../components/AIFloatingAvatar.vue'
 import DimensionIntroCard from '../../components/DimensionIntroCard.vue'
 import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
-import { getVisibleSections, getEnergyOverview, getChildren, getFamilyEnergySandbox, getEnergySandbox, getFamilyMemberList, getDimensionOverview } from '../../utils/api.js'
+import { getVisibleSections, getEnergyOverview, getChildren, getFamilyEnergySandbox, getEnergySandbox, getFamilyMemberList, getDimensionOverview, getBodyAbnormalIndicators } from '../../utils/api.js'
 
 export default {
   components: { TabTransition, FamilyEnergyBar, RadarChart, DimensionSubDims, FloatingAvatar, FamilyMemberStrip, DimensionIntroCard },
@@ -133,6 +176,7 @@ export default {
       dualDimension: null,
       visibleSections: [],
       dimensionData: null,
+      bodyAbnormalData: null,
       /** Mock 平均值(7维):生长发育/睡眠质量/视力健康/免疫力/营养均衡/肠胃健康/运动活力 */
       radarAvgScores: [72, 68, 75, 70, 73, 69, 71],
       funcList: [
@@ -217,6 +261,7 @@ export default {
     if (this.isLoggedIn) {
       this.loadChildren()
       this.loadFamilyMembersVisible()
+      this.loadBodyAbnormalData()
     }
     // 游客也能浏览活动和商品
     // 游客也能浏览文章
@@ -408,6 +453,20 @@ export default {
       var id = this.activeChildId || uni.getStorageSync('currentChildId') || ''
       uni.navigateTo({ url: '/pages/health/report-upload?memberId=' + id + '&from=body' })
     },
+    loadBodyAbnormalData: function() {
+      var self = this
+      var memberId = self.activeChildId || uni.getStorageSync('currentChildId') || ''
+      if (!memberId) return
+      getBodyAbnormalIndicators(memberId).then(function(res) {
+        if (res.code === 200 && res.data) {
+          self.bodyAbnormalData = res.data
+        } else {
+          self.bodyAbnormalData = null
+        }
+      }).catch(function() {
+        self.bodyAbnormalData = null
+      })
+    },
     _watchPageReady() {
       var self = this
       var unwatch = this.$watch(function() {
@@ -857,4 +916,93 @@ export default {
   color: #9A3412;
   font-weight: 500;
 }
+
+/* ===== 指标告警卡片 ===== */
+.alert-card {
+  background: #fff;
+  border-radius: 20rpx;
+  padding: 24rpx;
+  margin: 16rpx 24rpx;
+  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
+}
+.section-subtitle {
+  font-size: 22rpx;
+  color: #999;
+  margin-left: 12rpx;
+}
+.alert-group {
+  margin-bottom: 16rpx;
+}
+.alert-group-title {
+  font-size: 26rpx;
+  font-weight: 600;
+  color: #333;
+  margin-bottom: 10rpx;
+  display: block;
+}
+.alert-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 10rpx 0;
+  border-bottom: 1rpx solid #f5f5f5;
+}
+.alert-item:last-child {
+  border-bottom: none;
+}
+.alert-item-name {
+  font-size: 24rpx;
+  color: #555;
+}
+.alert-item-level {
+  font-size: 22rpx;
+  padding: 4rpx 16rpx;
+  border-radius: 8rpx;
+  font-weight: 500;
+}
+.alert-item-level.level-high {
+  background: #FEE2E2;
+  color: #DC2626;
+}
+.alert-item-level.level-warn {
+  background: #FEF3C7;
+  color: #D97706;
+}
+.alert-item-status {
+  font-size: 22rpx;
+  padding: 4rpx 16rpx;
+  border-radius: 8rpx;
+  background: #FEE2E2;
+  color: #DC2626;
+}
+.alert-empty {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 30rpx 0;
+}
+.alert-empty-text {
+  font-size: 24rpx;
+  color: #999;
+  margin-bottom: 20rpx;
+}
+.alert-upload-btn {
+  background: linear-gradient(135deg, #FF8C42, #FFA366);
+  color: #fff;
+  font-size: 26rpx;
+  font-weight: 600;
+  border: none;
+  border-radius: 40rpx;
+  padding: 14rpx 60rpx;
+  line-height: 1.6;
+  margin: 0;
+}
+.alert-no-data {
+  text-align: center;
+  padding: 30rpx 0;
+}
+.alert-no-data-text {
+  font-size: 24rpx;
+  color: #999;
+}
 </style>

+ 69 - 27
cfc-frontend/pages/mind-detail/index.vue

@@ -78,25 +78,33 @@
           </view>
         </view>
 
-        <!-- 肠脑轴情绪卡片 -->
-        <view class="section" v-if="gutIndicators && gutIndicators.length > 0">
+        <!-- 肠脑轴情绪卡片(有菌群报告显示指标,无报告引导上传) -->
+        <view class="section gut-card" v-if="isLoggedIn">
           <view class="section-header">
             <text class="section-title">🌱 情绪与肠胃</text>
           </view>
-          <view class="gut-insight-text" v-if="gutInsight">
-            <text>{{ gutInsight }}</text>
-          </view>
-          <view class="gut-indicator-list">
-            <view class="gut-indicator-item" v-for="ind in gutIndicators" :key="ind.id">
-              <view class="gut-indicator-left">
-                <text class="gut-indicator-name">{{ ind.indicatorName || ind.name }}</text>
-              </view>
-              <view class="gut-indicator-bar-track">
-                <view class="gut-indicator-bar-fill" :style="{ width: (ind.value || 0) + '%', background: indicatorColor(ind.status) }"></view>
+          <text class="gut-card-subtitle">心生身 · 情绪状态,透过肠道菌群看见</text>
+          <template v-if="gutIndicators && gutIndicators.length > 0">
+            <view class="gut-insight-text" v-if="gutInsight">
+              <text>{{ gutInsight }}</text>
+            </view>
+            <view class="gut-indicator-list">
+              <view class="gut-indicator-item" v-for="ind in gutIndicators" :key="ind.id">
+                <view class="gut-indicator-left">
+                  <text class="gut-indicator-name">{{ ind.indicatorName || ind.name }}</text>
+                </view>
+                <view class="gut-indicator-bar-track">
+                  <view class="gut-indicator-bar-fill" :style="{ width: (ind.value || 0) + '%', background: indicatorColor(ind.status) }"></view>
+                </view>
+                <text class="gut-indicator-value">{{ ind.value }}{{ ind.unit || '%' }}</text>
+                <text :class="['gut-indicator-status', 'status-' + (ind.status || 'normal')]">{{ statusLabel(ind.status) }}</text>
               </view>
-              <text class="gut-indicator-value">{{ ind.value }}{{ ind.unit || '%' }}</text>
-              <text :class="['gut-indicator-status', 'status-' + (ind.status || 'normal')]">{{ statusLabel(ind.status) }}</text>
             </view>
+          </template>
+          <view class="gut-empty" v-else>
+            <text class="gut-empty-icon">📄</text>
+            <text class="gut-empty-text">上传菌群报告,查看情绪相关指标</text>
+            <button class="gut-upload-btn" @click="goReportUpload">去上传</button>
           </view>
         </view>
 
@@ -166,7 +174,7 @@ import PsychCrisisBanner from '../../components/PsychCrisisBanner.vue'
 import FloatingAvatar from '../../components/AIFloatingAvatar.vue'
 import FamilyTianpanCard from '../../components/FamilyTianpanCard.vue'
 import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
-import { getVisibleSections, getEmiReport, getFamilyEnergySandbox, getEnergySandbox, getChildren, getFamilyMemberList, getEnergyOverview, getContactList, getHealthAlerts, getMilestones, getDanReportByChild, getFamilyFortune } from '../../utils/api.js'
+import { getVisibleSections, getEmiReport, getFamilyEnergySandbox, getEnergySandbox, getChildren, getFamilyMemberList, getEnergyOverview, getContactList, getHealthAlerts, getMilestones, getDanReportByChild, getFamilyFortune, getEmotionIndicators } from '../../utils/api.js'
 import nav from '../../utils/nav.js'
 import config from '../../config.js'
 import { parseDate } from '../../utils/format.js'
@@ -625,20 +633,17 @@ var descList = descMap[this.dominantElement] || descMap.wood
       var memberId = self.currentChildId
       if (!memberId) return
       var token = uni.getStorageSync('token')
-      // 获取神经递质指标
-      uni.request({
-        url: config.API_BASE_URL + '/api/mind/neurotransmitters',
-        method: 'POST',
-        header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
-        data: { memberId: memberId },
-        success: function(r) {
-          if (r.statusCode === 200 && r.data && r.data.code === 200) {
-            var list = r.data.data
-            if (Array.isArray(list) && list.length > 0) {
-              self.gutIndicators = list.slice(0, 4)
-            }
+      getEmotionIndicators(memberId).then(function(res) {
+        if (res.code === 200 && res.data) {
+          var list = res.data
+          if (Array.isArray(list) && list.length > 0) {
+            self.gutIndicators = list.slice(0, 6)
+          } else {
+            self.gutIndicators = []
           }
         }
+      }).catch(function() {
+        self.gutIndicators = []
       })
       // 获取情绪洞察摘要
       uni.request({
@@ -906,6 +911,9 @@ var descList = descMap[this.dominantElement] || descMap.wood
     goDanUpload: function(dimension) {
       uni.navigateTo({ url: '/pages/dan-assessment/report-upload' })
     },
+    goReportUpload: function() {
+      uni.navigateTo({ url: '/pages/health/report-upload' })
+    },
     viewDanReport: function(report) {
       var reportId = report.sourceReportId || report.id
       uni.navigateTo({ url: '/pages/health/report-detail?reportType=dan&reportId=' + reportId })
@@ -1955,6 +1963,40 @@ var descList = descMap[this.dominantElement] || descMap.wood
   color: #D97706;
 }
 
+/* ===== 肠脑轴卡片 - 新生身副标题/空态引导 ===== */
+.gut-card-subtitle {
+  font-size: 24rpx;
+  color: #FF6B9D;
+  margin-bottom: 16rpx;
+  display: block;
+}
+.gut-empty {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 40rpx 0;
+}
+.gut-empty-icon {
+  font-size: 56rpx;
+  margin-bottom: 16rpx;
+}
+.gut-empty-text {
+  font-size: 26rpx;
+  color: #999;
+  margin-bottom: 24rpx;
+}
+.gut-upload-btn {
+  background: linear-gradient(135deg, #FF6B9D, #FF9EC4);
+  color: #fff;
+  font-size: 26rpx;
+  font-weight: 600;
+  border: none;
+  border-radius: 40rpx;
+  padding: 16rpx 64rpx;
+  line-height: 1.6;
+  margin: 0;
+}
+
 /* ===== 关键信息(家庭+个人) ===== */
 .key-info-card {
   background: #fff;

+ 151 - 1
cfc-frontend/pages/wisdom-detail/index.vue

@@ -83,6 +83,33 @@
       </view>
     </view>
 
+    <!-- 肠胃与认知卡片(有菌群报告显示认知指标,无报告引导上传) -->
+    <view class="section gut-cognition-card" v-if="isLoggedIn">
+      <view class="section-header">
+        <text class="section-title">🦠 肠胃与认知</text>
+      </view>
+      <text class="gut-cog-subtitle">肠脑轴 · 肠道菌群,影响学习与思维</text>
+      <template v-if="cognitionGutIndicators && cognitionGutIndicators.length > 0">
+        <view class="gut-indicator-list">
+          <view class="gut-indicator-item" v-for="ind in cognitionGutIndicators" :key="ind.id">
+            <view class="gut-indicator-left">
+              <text class="gut-indicator-name">{{ ind.indicatorName || ind.name }}</text>
+            </view>
+            <view class="gut-indicator-bar-track">
+              <view class="gut-indicator-bar-fill" :style="{ width: (ind.value || 0) + '%', background: indicatorColor(ind.status) }"></view>
+            </view>
+            <text class="gut-indicator-value">{{ ind.value }}{{ ind.unit || '%' }}</text>
+            <text :class="['gut-indicator-status', 'status-' + (ind.status || 'normal')]">{{ statusLabel(ind.status) }}</text>
+          </view>
+        </view>
+      </template>
+      <view class="gut-empty" v-else>
+        <text class="gut-empty-icon">📄</text>
+        <text class="gut-empty-text">上传菌群报告,查看认知相关指标</text>
+        <button class="gut-upload-btn" @click="goReportUpload">去上传</button>
+      </view>
+    </view>
+
     <!-- 6. 功能入口 -->
     <view class="func-section" v-if="sectionVisible('func_entries')">
       <view class="func-grid">
@@ -133,7 +160,7 @@ import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
 import FloatingAvatar from '../../components/AIFloatingAvatar.vue'
 import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
 import DimensionIntroCard from '../../components/DimensionIntroCard.vue'
-import { getVisibleSections, getChildren, getFamilyEnergySandbox, getEnergySandbox, getAssessmentLatestResult, getDanReportByChild } from '../../utils/api.js'
+import { getVisibleSections, getChildren, getFamilyEnergySandbox, getEnergySandbox, getAssessmentLatestResult, getDanReportByChild, getGutCognitionIndicators } from '../../utils/api.js'
 import { parseDate } from '../../utils/format.js'
 
 export default {
@@ -159,6 +186,7 @@ export default {
       /** Mock 平均值(7维认知):感知/专注/记忆/逻辑/空间/加工速度/成长型思维 */
       cognitiveAvgScores: [70, 65, 72, 68, 66, 71, 70],
       danReports: [],
+      cognitionGutIndicators: [],
       funcList: [
         { icon: '\u{1F4CA}', label: '认知报告', needLogin: true, page: '/pages/wisdom-detail/cognitive-report' },
         { icon: '\u{1F9E0}', label: '训练中心', needLogin: true, page: '/pages/wisdom-detail/training-hub' },
@@ -252,6 +280,7 @@ export default {
       this.loadFamilyMembersVisible()
       this.loadCognitiveReport()
       this.loadDanReports()
+      this.loadGutCognitionData()
     }
     // 游客也能浏览文章
     this._watchPageReady()
@@ -396,6 +425,38 @@ export default {
         }
       }).catch(function() {})
     },
+    // ===== 肠胃与认知(肠脑轴认知指标) =====
+    loadGutCognitionData: function() {
+      var self = this
+      var memberId = self.activeChildId
+      if (!memberId) return
+      getGutCognitionIndicators(memberId).then(function(res) {
+        if (res.code === 200 && res.data) {
+          var list = res.data
+          self.cognitionGutIndicators = Array.isArray(list) ? list.slice(0, 6) : []
+        } else {
+          self.cognitionGutIndicators = []
+        }
+      }).catch(function() {
+        self.cognitionGutIndicators = []
+      })
+    },
+    indicatorColor: function(status) {
+      if (status === 'abnormal' || status === '偏高' || status === '偏低' || status === '过高' || status === '缺乏') return '#EF4444'
+      if (status === 'warning' || status === '注意' || status === '不足') return '#F59E0B'
+      return '#10B981'
+    },
+    statusLabel: function(status) {
+      if (status === 'normal') return '正常'
+      if (status === 'abnormal') return '异常'
+      if (status === 'warning') return '警讯'
+      if (status === '过高' || status === '过多') return '偏高'
+      if (status === '缺乏' || status === '不足') return '偏低'
+      return status || '正常'
+    },
+    goReportUpload: function() {
+      uni.navigateTo({ url: '/pages/health/report-upload' })
+    },
     goDanUpload: function() {
       uni.navigateTo({ url: '/pages/dan-assessment/report-upload' })
     },
@@ -724,4 +785,93 @@ export default {
   color: #3730A3;
   font-weight: 500;
 }
+
+/* ===== 肠胃与认知卡片 ===== */
+.gut-cog-subtitle {
+  font-size: 24rpx;
+  color: #6366F1;
+  margin-bottom: 16rpx;
+  display: block;
+}
+.gut-indicator-list {
+  display: flex;
+  flex-direction: column;
+  gap: 12rpx;
+}
+.gut-indicator-item {
+  display: flex;
+  align-items: center;
+  gap: 12rpx;
+}
+.gut-indicator-left {
+  width: 80rpx;
+  flex-shrink: 0;
+}
+.gut-indicator-name {
+  font-size: 22rpx;
+  color: #666;
+}
+.gut-indicator-bar-track {
+  flex: 1;
+  height: 16rpx;
+  background: #F0F0F0;
+  border-radius: 8rpx;
+  overflow: hidden;
+}
+.gut-indicator-bar-fill {
+  height: 100%;
+  border-radius: 8rpx;
+  transition: width 0.3s;
+}
+.gut-indicator-value {
+  font-size: 22rpx;
+  color: #666;
+  width: 60rpx;
+  text-align: right;
+}
+.gut-indicator-status {
+  font-size: 20rpx;
+  padding: 2rpx 12rpx;
+  border-radius: 10rpx;
+  width: 60rpx;
+  text-align: center;
+}
+.status-normal {
+  background: #D1FAE5;
+  color: #059669;
+}
+.status-abnormal {
+  background: #FEE2E2;
+  color: #DC2626;
+}
+.status-warning {
+  background: #FEF3C7;
+  color: #D97706;
+}
+.gut-empty {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 40rpx 0;
+}
+.gut-empty-icon {
+  font-size: 56rpx;
+  margin-bottom: 16rpx;
+}
+.gut-empty-text {
+  font-size: 26rpx;
+  color: #999;
+  margin-bottom: 24rpx;
+}
+.gut-upload-btn {
+  background: linear-gradient(135deg, #6366F1, #818CF8);
+  color: #fff;
+  font-size: 26rpx;
+  font-weight: 600;
+  border: none;
+  border-radius: 40rpx;
+  padding: 16rpx 64rpx;
+  line-height: 1.6;
+  margin: 0;
+}
 </style>

+ 8 - 0
cfc-frontend/utils/api.js

@@ -1783,6 +1783,14 @@ export const getExerciseList = (data) => request('/api/health/exercise/list', 'P
 export const getMindCheckinList = (data) => request('/api/mind/checkin/list', 'POST', data)
 export const getHealthMealList = (data) => request('/api/health/meal/list', 'POST', data)
 
+// ===== 菌群报告 · 肠脑轴 =====
+// 心页面"情绪与肠胃":只返回与情绪状态直接相关的菌群报告指标
+export const getEmotionIndicators = (memberId) => request('/api/mind/neurotransmitters', 'POST', { memberId })
+// 智页面"肠胃与认知":返回与认知/智力相关的菌群报告指标
+export const getGutCognitionIndicators = (memberId) => request('/api/wisdom/gut-cognition', 'POST', { memberId })
+// 身体页面"指标告警":疾病风险(非低风险) + 营养/氨基酸异常指标
+export const getBodyAbnormalIndicators = (memberId) => request('/api/health/report/body-abnormal', 'POST', { memberId })
+
 // ===== 定期调研 =====
 export const getSurveyStatus = (data) => request('/api/survey/status', 'POST', data)
 export const getActiveTemplates = () => request('/api/survey/template/active', 'POST', {})

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-7e488ad84e181923be8ddfbfc8998fa0b122e1e9
+dda4ce4ae856900b772b9f50e85ae9f64b8830f3

+ 2 - 2
cfc-web/package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "cfc-web",
-  "version": "1.0.1119",
+  "version": "1.0.1121",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "cfc-web",
-      "version": "1.0.1119",
+      "version": "1.0.1121",
       "dependencies": {
         "@wangeditor/editor": "^5.1.23",
         "@wangeditor/editor-for-vue": "^1.0.2",

+ 1 - 1
cfc-web/package.json

@@ -1,6 +1,6 @@
 {
   "name": "cfc-web",
-  "version": "1.0.1120",
+  "version": "1.0.1122",
   "private": true,
   "scripts": {
     "dev": "vue-cli-service serve",

+ 27 - 0
cfc-web/public/CHANGELOG-v1.0.md

@@ -4,6 +4,33 @@
 
 ---
 
+## v1.0.1122 (2026-08-18)
+
+### 新功能
+- 心身智三页肠脑轴卡片-情绪/认知指标过滤+身体异常告警
+- 肠脑轴指标过滤+异常指标告警-认知/情绪分类+身体异常指标端点
+
+### 其他
+- - wisdom-detail: 新增肠胃与认知卡片,含认知指标(谷氨酸/多巴胺/色氨酸等)+引导上传
+- - body-detail: 新增指标告警卡片,疾病风险(非低风险)+营养素/氨基酸异常(仅异常项)
+- - api.js: 新增 getGutCognitionIndicators/getBodyAbnormalIndicators
+- Ultraworked with [Sisyphus](https://github.com/OhMyOpenCode)
+- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
+- 
+- - 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>
+- 
+
+
+## v1.0.1121 (2026-08-18)
+
+### Bug 修复
+- pages.json 删除多余右花括号修复 JSON 语法
+
+
 ## v1.0.1120 (2026-08-18)
 
 ### Bug 修复

+ 28 - 1
cfc-web/public/CHANGELOG.md

@@ -1,6 +1,6 @@
 # 更新日志
 
-> 当前版本: v1.0.1120
+> 当前版本: v1.0.1122
 
 ## 历史版本
 
@@ -8,6 +8,33 @@
 
 ---
 
+## v1.0.1122 (2026-08-18)
+
+### 新功能
+- 心身智三页肠脑轴卡片-情绪/认知指标过滤+身体异常告警
+- 肠脑轴指标过滤+异常指标告警-认知/情绪分类+身体异常指标端点
+
+### 其他
+- - wisdom-detail: 新增肠胃与认知卡片,含认知指标(谷氨酸/多巴胺/色氨酸等)+引导上传
+- - body-detail: 新增指标告警卡片,疾病风险(非低风险)+营养素/氨基酸异常(仅异常项)
+- - api.js: 新增 getGutCognitionIndicators/getBodyAbnormalIndicators
+- Ultraworked with [Sisyphus](https://github.com/OhMyOpenCode)
+- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
+- 
+- - 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>
+- 
+
+
+## v1.0.1121 (2026-08-18)
+
+### Bug 修复
+- pages.json 删除多余右花括号修复 JSON 语法
+
+
 ## v1.0.1120 (2026-08-18)
 
 ### Bug 修复