Browse Source

docs: 五维自检 AI 结合 P0+P1 实现计划

iwt 2 tuần trước cách đây
mục cha
commit
6bcfd8619e
1 tập tin đã thay đổi với 1303 bổ sung0 xóa
  1. 1303 0
      docs/superpowers/plans/2026-08-31-self-check-ai-integration.md

+ 1303 - 0
docs/superpowers/plans/2026-08-31-self-check-ai-integration.md

@@ -0,0 +1,1303 @@
+# 五维家庭自检 AI 结合(P0 + P1)实现计划
+
+> **面向 AI 代理的工作者:** 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。
+
+**目标:** 将五维自检结果从静态五行寻源表(`WuxingSourcingService`)替换为 LangGraph AI 生成建议,并叠加健康计划生成、趋势分析、Chat 上下文注入三个能力。
+
+**架构:** LangGraph 新增 2 个 graph(`self_check_analysis_graph`、`self_check_trend_graph`)+ 1 个 api 模块;Java 新增 `SelfCheckAnalysisService` + 2 个 AiGateway 方法 + 3 个 Controller 接口;前端新增 API 方法 + 改造结果页/中间页/Chat。
+
+**技术栈:** Java 8 / Spring Boot 2.7.18 / MyBatis-Plus / Python 3.11 + FastAPI + LangGraph + LangChain / uni-app Vue 2
+
+**规格文档:** `docs/superpowers/specs/2026-08-31-self-check-ai-integration-design.md`
+
+---
+
+## 文件结构
+
+| 文件 | 职责 | 变更 |
+|------|------|------|
+| `cfc-langgraph/app/graphs/self_check_analysis_graph.py` | P0-1:自检建议 AI 生成 graph | 新建 |
+| `cfc-langgraph/app/graphs/self_check_trend_graph.py` | P1-1:趋势分析 AI 生成 graph | 新建 |
+| `cfc-langgraph/app/api/self_check.py` | 注册 P0-1 + P1-1 路由 | 新建 |
+| `cfc-langgraph/app/main.py` | 注册 self_check router | 修改 |
+| `cfc-backend/src/main/java/com/etotem/cfc/dto/WuxingSourcingAdviceVO.java` | VO 改造(去 upstream/restrainer/action,加 AI 字段) | 修改 |
+| `cfc-backend/src/main/java/com/etotem/cfc/service/SelfCheckAnalysisService.java` | P0-1:调 AiGateway 生成建议 | 新建 |
+| `cfc-backend/src/main/java/com/etotem/cfc/service/WuxingSourcingService.java` | 删除 SOURCING_TABLE/getAdvice/getAdvicesForLowScores | 修改 |
+| `cfc-backend/src/main/java/com/etotem/cfc/service/FiveDimensionSelfCheckService.java` | submitSelfCheck 调新 Service;新增 trend 方法 | 修改 |
+| `cfc-backend/src/main/java/com/etotem/cfc/service/AiGateway.java` | 新增 generateSelfCheckAdvice/generateSelfCheckTrend | 修改 |
+| `cfc-backend/src/main/java/com/etotem/cfc/controller/family/FiveDimensionSelfCheckController.java` | 新增 generatePlan、trendAnalysis 接口;submit 改造 | 修改 |
+| `cfc-backend/src/main/java/com/etotem/cfc/service/HealthPlanService.java` | 新增 generateFromSelfCheck 接口 | 修改 |
+| `cfc-backend/src/main/java/com/etotem/cfc/service/impl/HealthPlanServiceImpl.java` | 实现 generateFromSelfCheck | 修改 |
+| `cfc-backend/src/main/java/com/etotem/cfc/controller/ai/AIChatController.java` | sendMessage 支持 selfCheckId 参数 | 修改 |
+| `cfc-frontend/utils/api.js` | 新增 3 个 API 方法 | 修改 |
+| `cfc-frontend/pages/family/self-check-result.vue` | P0-1/P0-2/P1-2 展示改造 | 修改 |
+| `cfc-frontend/pages/family/self-check-entry.vue` | P1-1 趋势展示 | 修改 |
+
+---
+
+## 任务 1:LangGraph — self_check_analysis_graph(P0-1 核心)
+
+**文件:**
+- 创建:`cfc-langgraph/app/graphs/self_check_analysis_graph.py`
+- 创建:`cfc-langgraph/app/api/self_check.py`
+
+- [ ] **步骤 1:创建 self_check_analysis_graph.py**
+
+创建文件 `cfc-langgraph/app/graphs/self_check_analysis_graph.py`:
+
+```python
+import json
+import logging
+from typing import TypedDict, Optional
+from langgraph.graph import StateGraph, START, END
+from langchain_core.messages import SystemMessage, HumanMessage
+from app.llm.client import get_llm
+from app.monitoring import monitor_agent
+
+logger = logging.getLogger(__name__)
+
+SYSTEM_PROMPT = """你是一位家庭健康顾问,基于五维自检结果(身·智·富·行·心,每维0-9分,满分45)给出个性化建议。
+
+要求:
+1. 对每个低分维度(≤6分)给出1-2句解读和2-3个具体可执行的微行动
+2. 如有历史数据,简要对比趋势(改善/下滑)
+3. 语气温暖口语化,每条解读不超过80字
+4. 最后给出1句家庭整体洞察(30字以内)
+
+返回 JSON(严格格式,不要额外文字):
+{
+  "advice": [
+    {
+      "dimension": "mind",
+      "dimensionName": "心",
+      "interpretation": "你的情绪能量偏低,可能最近压力较大,建议...",
+      "microActions": ["今晚睡前做10分钟深呼吸", "和伴侣约定每周一次夜谈"],
+      "fallbackUsed": false
+    }
+  ],
+  "familyInsight": "建议从行动维度入手,关系顺畅了内心才能安定"
+}
+"""
+
+
+class GraphState(TypedDict):
+    scores: dict
+    question_ids: list
+    user_id: int
+    recent_history: list
+    advice: Optional[dict]
+    error: Optional[str]
+
+
+class SelfCheckAnalysisAgent:
+    def __init__(self):
+        self.llm = get_llm()
+
+    @monitor_agent("self_check_analysis")
+    async def run(self, scores: dict, question_ids: list, user_id: int, recent_history: list) -> dict:
+        try:
+            score_summary = "\n".join(
+                f"{v.get('dimensionName', k)}({k}): {v.get('score', 0)}分"
+                for k, v in scores.items()
+            )
+            history_summary = ""
+            if recent_history:
+                history_summary = "历史趋势:\n" + "\n".join(
+                    f"- {h.get('createdAt', '')}: 总分{h.get('totalScore', 0)}分"
+                    for h in recent_history[:3]
+                )
+            messages = [
+                SystemMessage(content=SYSTEM_PROMPT),
+                HumanMessage(content=f"用户ID: {user_id}\n当前自检得分:\n{score_summary}\n{history_summary}"),
+            ]
+            response = await self.llm.ainvoke(messages)
+            text = response.content.strip()
+            if "```json" in text:
+                text = text.split("```json")[1].split("```")[0].strip()
+            elif "```" in text:
+                text = text.split("```")[1].split("```")[0].strip()
+            data = json.loads(text)
+            advice_list = data.get("advice", [])
+            for item in advice_list:
+                item.setdefault("fallbackUsed", False)
+            if advice_list:
+                advice_list[0]["familyInsight"] = data.get("familyInsight", "")
+            return {"advice": {"advice_json": json.dumps(advice_list, ensure_ascii=False), "fallback_used": False}}
+        except Exception as e:
+            logger.warning("自检建议生成失败: %s", e)
+            return {"advice": {"advice_json": None, "fallback_used": True}}
+
+
+def build_graph():
+    agent = SelfCheckAnalysisAgent()
+
+    def parse_input(state: GraphState) -> GraphState:
+        return state
+
+    async def call_llm(state: GraphState) -> dict:
+        return await agent.run(state["scores"], state["question_ids"], state["user_id"], state["recent_history"])
+
+    def validate(state: GraphState) -> GraphState:
+        adv = state.get("advice")
+        if adv is None or adv.get("advice_json") is None:
+            return {**state, "error": "AI 建议生成失败"}
+        return state
+
+    graph = StateGraph(GraphState)
+    graph.add_node("parse", parse_input)
+    graph.add_node("llm", call_llm)
+    graph.add_node("validate", validate)
+    graph.add_edge(START, "parse")
+    graph.add_edge("parse", "llm")
+    graph.add_edge("llm", "validate")
+    graph.add_edge("validate", END)
+    return graph.compile()
+
+
+_graph = None
+
+
+def get_graph():
+    global _graph
+    if _graph is None:
+        _graph = build_graph()
+    return _graph
+```
+
+- [ ] **步骤 2:创建 self_check.py API 模块**
+
+创建文件 `cfc-langgraph/app/api/self_check.py`:
+
+```python
+import logging
+from fastapi import APIRouter
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from app.graphs.self_check_analysis_graph import get_graph
+from app.graphs.self_check_trend_graph import get_trend_graph
+
+logger = logging.getLogger(__name__)
+router = APIRouter(prefix="/api/v1", tags=["self-check"])
+
+
+class SelfCheckAnalysisRequest(BaseModel):
+    scores: Dict[str, Any]
+    question_ids: list
+    user_id: int
+    recent_history: Optional[list] = None
+
+
+class SelfCheckTrendRequest(BaseModel):
+    history: list
+    user_id: int
+
+
+@router.post("/self-check/analysis")
+async def self_check_analysis(req: SelfCheckAnalysisRequest):
+    graph = get_graph()
+    state = {
+        "scores": req.scores,
+        "question_ids": req.question_ids,
+        "user_id": req.user_id,
+        "recent_history": req.recent_history or [],
+        "advice": None,
+        "error": None,
+    }
+    result = await graph.ainvoke(state)
+    advice = result.get("advice") or {}
+    return {
+        "advice_json": advice.get("advice_json"),
+        "fallback_used": advice.get("fallback_used", True),
+        "error": result.get("error"),
+    }
+
+
+@router.post("/self-check/trend")
+async def self_check_trend(req: SelfCheckTrendRequest):
+    graph = get_trend_graph()
+    state = {"history": req.history, "user_id": req.user_id, "insight": None, "error": None}
+    result = await graph.ainvoke(state)
+    insight = result.get("insight") or {}
+    return {
+        "aiInsight": insight.get("aiInsight", ""),
+        "trendSummary": insight.get("trendSummary", ""),
+        "error": result.get("error"),
+    }
+```
+
+- [ ] **步骤 3:注册路由**
+
+在 `cfc-langgraph/app/main.py` 中,于 `app.include_router(innate_portrait.router)` 之后追加:
+
+```python
+from app.api import self_check
+app.include_router(self_check.router)
+```
+
+- [ ] **步骤 4:语法校验**
+
+```bash
+cd cfc-langgraph && python -m py_compile app/graphs/self_check_analysis_graph.py app/graphs/self_check_trend_graph.py app/api/self_check.py && echo "OK"
+```
+
+- [ ] **步骤 5:Commit**
+
+```bash
+git add cfc-langgraph/app/graphs/self_check_analysis_graph.py \
+        cfc-langgraph/app/graphs/self_check_trend_graph.py \
+        cfc-langgraph/app/api/self_check.py \
+        cfc-langgraph/app/main.py
+git commit -m "feat(langgraph): 新增自检建议+trend graph + self_check 路由"
+```
+
+---
+
+## 任务 2:LangGraph — self_check_trend_graph(P1-1 核心)
+
+**文件:**
+- 创建:`cfc-langgraph/app/graphs/self_check_trend_graph.py`
+
+- [ ] **步骤 1:创建 self_check_trend_graph.py**
+
+创建文件 `cfc-langgraph/app/graphs/self_check_trend_graph.py`:
+
+```python
+import json
+import logging
+from typing import TypedDict, Optional
+from langgraph.graph import StateGraph, START, END
+from langchain_core.messages import SystemMessage, HumanMessage
+from app.llm.client import get_llm
+from app.monitoring import monitor_agent
+
+logger = logging.getLogger(__name__)
+
+SYSTEM_PROMPT = """分析用户近3次五维自检趋势,输出:
+- aiInsight: 趋势解读(2-3句,指出最大变化维度和可能原因,口语化)
+- trendSummary: 各维度 delta 简写(如"身-2 智+1 富0 行-1 心+2")
+返回 JSON:{"aiInsight": "...", "trendSummary": "..."}
+"""
+
+
+class TrendState(TypedDict):
+    history: list
+    user_id: int
+    insight: Optional[dict]
+    error: Optional[str]
+
+
+class SelfCheckTrendAgent:
+    def __init__(self):
+        self.llm = get_llm()
+
+    @monitor_agent("self_check_trend")
+    async def run(self, history: list, user_id: int) -> dict:
+        try:
+            if len(history) < 2:
+                return {"insight": {"aiInsight": "自检次数不足,建议完成至少2次自检后查看趋势", "trendSummary": ""}, "error": None}
+            history_text = "\n".join(
+                f"{h.get('createdAt', '')}: 总分{h.get('totalScore', 0)}," +
+                " ".join(f"{d.get('name','')}{d.get('score',0)}分" for d in h.get('dimensions', []))
+                for h in history[-3:]
+            )
+            messages = [
+                SystemMessage(content=SYSTEM_PROMPT),
+                HumanMessage(content=f"用户{user_id}的自检历史:\n{history_text}"),
+            ]
+            response = await self.llm.ainvoke(messages)
+            text = response.content.strip()
+            if "```json" in text:
+                text = text.split("```json")[1].split("```")[0].strip()
+            elif "```" in text:
+                text = text.split("```")[1].split("```")[0].strip()
+            data = json.loads(text)
+            return {"insight": data, "error": None}
+        except Exception as e:
+            logger.warning("趋势分析失败: %s", e)
+            return {"insight": {"aiInsight": "", "trendSummary": ""}, "error": str(e)}
+
+
+def build_trend_graph():
+    agent = SelfCheckTrendAgent()
+
+    async def call_llm(state: TrendState) -> dict:
+        return await agent.run(state["history"], state["user_id"])
+
+    graph = StateGraph(TrendState)
+    graph.add_node("llm", call_llm)
+    graph.add_edge(START, "llm")
+    graph.add_edge("llm", END)
+    return graph.compile()
+
+
+_trend_graph = None
+
+
+def get_trend_graph():
+    global _trend_graph
+    if _trend_graph is None:
+        _trend_graph = build_trend_graph()
+    return _trend_graph
+```
+
+- [ ] **步骤 2:语法校验**
+
+```bash
+cd cfc-langgraph && python -m py_compile app/graphs/self_check_trend_graph.py && echo "OK"
+```
+
+- [ ] **步骤 3:Commit**
+
+```bash
+git add cfc-langgraph/app/graphs/self_check_trend_graph.py
+git commit -m "feat(langgraph): 新增 self_check_trend_graph 趋势分析实现"
+```
+
+---
+
+## 任务 3:Java — WuxingSourcingAdviceVO 改造 + SelfCheckAnalysisService
+
+**文件:**
+- 修改:`cfc-backend/src/main/java/com/etotem/cfc/dto/WuxingSourcingAdviceVO.java`
+- 新建:`cfc-backend/src/main/java/com/etotem/cfc/service/SelfCheckAnalysisService.java`
+
+- [ ] **步骤 1:改造 WuxingSourcingAdviceVO**
+
+将 `cfc-backend/src/main/java/com/etotem/cfc/dto/WuxingSourcingAdviceVO.java` 的字段替换为:
+
+```java
+package com.etotem.cfc.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import java.util.List;
+
+@Data
+public class WuxingSourcingAdviceVO {
+
+    private String dimension;
+    private String dimensionName;
+    private String element;
+    private String color;
+    private Integer score;
+    private String level;
+    private String levelName;
+
+    /** AI 解读(替换原 upstreamReason) */
+    private String interpretation;
+
+    /** AI 微行动列表 */
+    private List<String> microActions;
+
+    /** AI 补充洞察 */
+    private String aiInsight;
+
+    /** 家庭整体洞察(仅第一条携带) */
+    private String familyInsight;
+
+    /** 是否使用降级静态建议 */
+    @JsonProperty("fallbackUsed")
+    private Boolean fallbackUsed;
+}
+```
+
+- [ ] **步骤 2:创建 SelfCheckAnalysisService**
+
+创建文件 `cfc-backend/src/main/java/com/etotem/cfc/service/SelfCheckAnalysisService.java`:
+
+```java
+package com.etotem.cfc.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.etotem.cfc.entity.FiveDimensionSelfCheck;
+import com.etotem.cfc.mapper.FiveDimensionSelfCheckMapper;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.*;
+
+@Service
+public class SelfCheckAnalysisService {
+
+    private static final Logger log = LoggerFactory.getLogger(SelfCheckAnalysisService.class);
+
+    @Resource
+    private AiGateway aiGateway;
+
+    @Resource
+    private FiveDimensionSelfCheckMapper selfCheckMapper;
+
+    private final ObjectMapper objectMapper = new ObjectMapper();
+
+    /**
+     * 生成自检建议(AI 优先,失败返回 adviceJson=null + fallbackUsed=true)
+     */
+    public Map<String, Object> generateAdvice(Long userId, Map<String, Integer> scoreMap, List<Integer> questionIds) {
+        try {
+            List<Map<String, Object>> recentHistory = getRecentHistory(userId, 3);
+            Map<String, Object> scoresWithMeta = new LinkedHashMap<>();
+            for (Map.Entry<String, Integer> entry : scoreMap.entrySet()) {
+                Map<String, Object> m = new LinkedHashMap<>();
+                m.put("dimension", entry.getKey());
+                m.put("dimensionName", getDimensionName(entry.getKey()));
+                m.put("score", entry.getValue());
+                scoresWithMeta.put(entry.getKey(), m);
+            }
+            Map<String, Object> inputs = new LinkedHashMap<>();
+            inputs.put("scores", scoresWithMeta);
+            inputs.put("questionIds", questionIds);
+            inputs.put("userId", userId);
+            inputs.put("recentHistory", recentHistory);
+            Map<String, Object> result = aiGateway.generateSelfCheckAdvice(inputs);
+            if (result == null) {
+                log.info("AI 自检建议生成失败,fallback");
+                return Map.of("adviceJson", null, "fallbackUsed", true);
+            }
+            String adviceJson = (String) result.get("advice_json");
+            Boolean fallbackUsed = (Boolean) result.getOrDefault("fallback_used", false);
+            return Map.of("adviceJson", adviceJson, "fallbackUsed", fallbackUsed);
+        } catch (Exception e) {
+            log.warn("自检建议生成异常: {}", e.getMessage());
+            return Map.of("adviceJson", null, "fallbackUsed", true);
+        }
+    }
+
+    /**
+     * 生成趋势分析(AI 优先,失败返回空 insight)
+     */
+    public Map<String, Object> generateTrend(Long userId) {
+        try {
+            List<Map<String, Object>> history = getRecentHistory(userId, 3);
+            Map<String, Object> inputs = Map.of("history", history, "userId", userId);
+            Map<String, Object> result = aiGateway.generateSelfCheckTrend(inputs);
+            if (result == null) {
+                return Map.of("aiInsight", "", "trendSummary", "");
+            }
+            return Map.of(
+                    "aiInsight", result.getOrDefault("aiInsight", ""),
+                    "trendSummary", result.getOrDefault("trendSummary", "")
+            );
+        } catch (Exception e) {
+            log.warn("趋势分析异常: {}", e.getMessage());
+            return Map.of("aiInsight", "", "trendSummary", "");
+        }
+    }
+
+    /** 获取最近 N 次自检历史(供 AI 和前端使用) */
+    public List<Map<String, Object>> getRecentHistory(Long userId, int limit) {
+        try {
+            List<FiveDimensionSelfCheck> records = selfCheckMapper.selectList(
+                    new LambdaQueryWrapper<FiveDimensionSelfCheck>()
+                            .eq(FiveDimensionSelfCheck::getUserId, userId)
+                            .orderByDesc(FiveDimensionSelfCheck::getCreatedAt)
+                            .last("LIMIT " + limit)
+            );
+            List<Map<String, Object>> result = new ArrayList<>();
+            for (FiveDimensionSelfCheck r : records) {
+                Map<String, Object> m = new LinkedHashMap<>();
+                m.put("createdAt", r.getCreatedAt() != null ? r.getCreatedAt().toString() : "");
+                m.put("totalScore", r.getTotalScore());
+                if (r.getScoresJson() != null) {
+                    try {
+                        Map<String, Integer> scores = objectMapper.readValue(r.getScoresJson(),
+                                new TypeReference<Map<String, Integer>>() {});
+                        List<Map<String, Object>> dims = new ArrayList<>();
+                        for (Map.Entry<String, Integer> e : scores.entrySet()) {
+                            Map<String, Object> d = new LinkedHashMap<>();
+                            d.put("dimension", e.getKey());
+                            d.put("name", getDimensionName(e.getKey()));
+                            d.put("score", e.getValue());
+                            dims.add(d);
+                        }
+                        m.put("dimensions", dims);
+                    } catch (Exception ignored) {}
+                }
+                result.add(m);
+            }
+            return result;
+        } catch (Exception e) {
+            log.warn("获取自检历史失败: {}", e.getMessage());
+            return Collections.emptyList();
+        }
+    }
+
+    private String getDimensionName(String dim) {
+        switch (dim) {
+            case "body": return "身";
+            case "wisdom": return "智";
+            case "wealth": return "富";
+            case "action": return "行";
+            case "mind": return "心";
+            default: return dim;
+        }
+    }
+}
+```
+
+- [ ] **步骤 3:编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+
+预期:BUILD SUCCESS
+
+- [ ] **步骤 4:Commit**
+
+```bash
+git add cfc-backend/src/main/java/com/etotem/cfc/dto/WuxingSourcingAdviceVO.java \
+       cfc-backend/src/main/java/com/etotem/cfc/service/SelfCheckAnalysisService.java
+git commit -m "feat(self-check): WuxingSourcingAdviceVO 改造为 AI 建议 VO + SelfCheckAnalysisService"
+```
+
+---
+
+## 任务 4:Java — AiGateway 新增方法
+
+**文件:**
+- 修改:`cfc-backend/src/main/java/com/etotem/cfc/service/AiGateway.java`
+
+- [ ] **步骤 1:新增 generateSelfCheckAdvice 方法**
+
+在 `AiGateway.java` 中,于 `generateInnateReading` 方法之后追加:
+
+```java
+/**
+ * 生成自检 AI 建议(调用 LangGraph self_check_analysis_graph)
+ * @return 含 "advice_json" 和 "fallback_used" 的 Map;失败返回 null
+ */
+public Map<String, Object> generateSelfCheckAdvice(Map<String, Object> inputs) {
+    if (!enabled || isCircuitOpen()) return null;
+    try {
+        ObjectNode body = objectMapper.valueToTree(inputs);
+        HttpEntity<String> entity = new HttpEntity<>(body.toString(), createJsonHeaders());
+        String url = baseUrl + "/api/v1/self-check/analysis";
+        ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);
+        if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
+            JsonNode root = objectMapper.readTree(response.getBody());
+            Map<String, Object> result = new LinkedHashMap<>();
+            result.put("advice_json", root.has("advice_json") ? root.get("advice_json").asText() : null);
+            result.put("fallback_used", root.has("fallback_used") ? root.get("fallback_used").asBoolean() : true);
+            consecutiveFailures.set(0);
+            return result;
+        }
+        return null;
+    } catch (Exception e) {
+        log.warn("AiGateway generateSelfCheckAdvice 调用失败: {}", e.getMessage());
+        recordFailure();
+        return null;
+    }
+}
+
+/**
+ * 生成自检趋势分析(调用 LangGraph self_check_trend_graph)
+ * @return 含 "aiInsight" 和 "trendSummary" 的 Map;失败返回 null
+ */
+public Map<String, Object> generateSelfCheckTrend(Map<String, Object> inputs) {
+    if (!enabled || isCircuitOpen()) return null;
+    try {
+        ObjectNode body = objectMapper.valueToTree(inputs);
+        HttpEntity<String> entity = new HttpEntity<>(body.toString(), createJsonHeaders());
+        String url = baseUrl + "/api/v1/self-check/trend";
+        ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);
+        if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
+            JsonNode root = objectMapper.readTree(response.getBody());
+            Map<String, Object> result = new LinkedHashMap<>();
+            result.put("aiInsight", root.has("aiInsight") ? root.get("aiInsight").asText() : "");
+            result.put("trendSummary", root.has("trendSummary") ? root.get("trendSummary").asText() : "");
+            consecutiveFailures.set(0);
+            return result;
+        }
+        return null;
+    } catch (Exception e) {
+        log.warn("AiGateway generateSelfCheckTrend 调用失败: {}", e.getMessage());
+        recordFailure();
+        return null;
+    }
+}
+```
+
+- [ ] **步骤 2:编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+
+预期:BUILD SUCCESS
+
+- [ ] **步骤 3:Commit**
+
+```bash
+git add cfc-backend/src/main/java/com/etotem/cfc/service/AiGateway.java
+git commit -m "feat(self-check): AiGateway 新增 generateSelfCheckAdvice/generateSelfCheckTrend"
+```
+
+---
+
+## 任务 5:Java — WuxingSourcingService 清理 + FiveDimensionSelfCheckService 改造
+
+**文件:**
+- 修改:`cfc-backend/src/main/java/com/etotem/cfc/service/WuxingSourcingService.java`
+- 修改:`cfc-backend/src/main/java/com/etotem/cfc/service/FiveDimensionSelfCheckService.java`
+
+- [ ] **步骤 1:清理 WuxingSourcingService**
+
+删除 `WuxingSourcingService.java` 中的:
+- `SOURCING_TABLE` 静态字段(第 41-68 行)
+- `getAdvice()` 方法(第 78-109 行)
+- `getAdvicesForLowScores()` 方法(第 117-132 行)
+
+保留:`DIMENSION_META`、`levelOf()`、`levelName()`、`dimensionName()`
+
+- [ ] **步骤 2:FiveDimensionSelfCheckService 改造 submitSelfCheck**
+
+在 `submitSelfCheck` 方法中,将第 472-477 行(`wuxingSourcingService.getAdvicesForLowScores` + 落库 adviceJson)替换为:
+
+```java
+        // 生成 AI 建议(AI 优先,失败降级为空建议)
+        String adviceJson = null;
+        try {
+            Map<String, Object> adviceResult = selfCheckAnalysisService.generateAdvice(userId, scoreMap, submittedQuestionIds);
+            adviceJson = (String) adviceResult.get("adviceJson");
+        } catch (Exception e) {
+            log.warn("AI 建议生成异常,使用空建议: {}", e.getMessage());
+        }
+        record.setAdviceJson(adviceJson != null ? adviceJson : "[]");
+```
+
+同时新增注入:
+
+```java
+    @Resource
+    private SelfCheckAnalysisService selfCheckAnalysisService;
+```
+
+并将 `vo.setAdvices(...)` 改为根据 adviceJson 解析(若无 AI 结果则空列表):
+
+```java
+        vo.setAdvices(parseAdvices(adviceJson));
+```
+
+- [ ] **步骤 3:新增 parseAdvices + trend 方法**
+
+在 Service 中新增:
+
+```java
+    private List<WuxingSourcingAdviceVO> parseAdvices(String adviceJson) {
+        if (adviceJson == null || adviceJson.isEmpty() || "[]".equals(adviceJson)) {
+            return new ArrayList<>();
+        }
+        try {
+            return objectMapper.readValue(adviceJson,
+                    objectMapper.getTypeFactory().constructCollectionType(List.class, WuxingSourcingAdviceVO.class));
+        } catch (Exception e) {
+            log.warn("解析 AI 建议 JSON 失败: {}", e.getMessage());
+            return new ArrayList<>();
+        }
+    }
+
+    /** 获取自检趋势分析结果(含历史列表) */
+    public Map<String, Object> getTrendAnalysis(Long userId) {
+        Map<String, Object> trend = selfCheckAnalysisService.generateTrend(userId);
+        Map<String, Object> resp = new HashMap<>();
+        resp.put("history", selfCheckAnalysisService.getRecentHistory(userId, 3));
+        resp.put("aiInsight", trend.getOrDefault("aiInsight", ""));
+        resp.put("trendSummary", trend.getOrDefault("trendSummary", ""));
+        return resp;
+    }
+```
+
+- [ ] **步骤 4:编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+
+预期:BUILD SUCCESS
+
+- [ ] **步骤 5:Commit**
+
+```bash
+git add cfc-backend/src/main/java/com/etotem/cfc/service/WuxingSourcingService.java \
+       cfc-backend/src/main/java/com/etotem/cfc/service/FiveDimensionSelfCheckService.java
+git commit -m "feat(self-check): 清理 WuxingSourcingService 静态表 + submitSelfCheck 接 AI"
+```
+
+---
+
+## 任务 6:Java — Controller 新增接口 + HealthPlanService
+
+**文件:**
+- 修改:`cfc-backend/src/main/java/com/etotem/cfc/controller/family/FiveDimensionSelfCheckController.java`
+- 修改:`cfc-backend/src/main/java/com/etotem/cfc/service/HealthPlanService.java`
+- 修改:`cfc-backend/src/main/java/com/etotem/cfc/service/impl/HealthPlanServiceImpl.java`
+
+- [ ] **步骤 1:Controller 新增 generatePlan 接口**
+
+在 `FiveDimensionSelfCheckController.java` 中追加:
+
+```java
+    @Resource
+    private com.etotem.cfc.service.HealthPlanService healthPlanService;
+
+    @Operation(summary = "用户点击生成健康计划(基于自检低分维度)")
+    @PostMapping("/generate-plan")
+    public Result<Map<String, Object>> generatePlan(
+            @RequestBody(required = false) Map<String, Object> body,
+            @RequestAttribute("userId") Long userId) {
+        if (userId == null) return Result.error("请先登录");
+        Long checkId = body != null ? com.etotem.cfc.util.ParamUtils.getLong(body.get("checkId")) : null;
+        try {
+            Long planId = healthPlanService.generateFromSelfCheck(userId, checkId);
+            if (planId == null) return Result.error("计划生成失败,请稍后重试");
+            Map<String, Object> resp = new HashMap<>();
+            resp.put("planId", planId);
+            resp.put("status", "draft");
+            return Result.success(resp);
+        } catch (Exception e) {
+            return Result.error("计划生成失败");
+        }
+    }
+
+    @Operation(summary = "获取自检历史趋势分析")
+    @PostMapping("/trend-analysis")
+    public Result<Map<String, Object>> getTrendAnalysis(@RequestAttribute("userId") Long userId) {
+        if (userId == null) return Result.error("请先登录");
+        try {
+            return Result.success(selfCheckService.getTrendAnalysis(userId));
+        } catch (Exception e) {
+            return Result.error("趋势分析失败");
+        }
+    }
+```
+
+- [ ] **步骤 2:HealthPlanService 接口新增 generateFromSelfCheck**
+
+在 `HealthPlanService.java` 中追加:
+
+```java
+    /** 基于自检低分维度自动生成 draft 计划 */
+    Long generateFromSelfCheck(Long userId, Long checkId);
+```
+
+- [ ] **步骤 3:HealthPlanServiceImpl 实现 generateFromSelfCheck**
+
+在 `HealthPlanServiceImpl.java` 中追加(需新增 `@Resource private com.etotem.cfc.mapper.FiveDimensionSelfCheckMapper selfCheckMapper;`):
+
+```java
+    @Override
+    public Long generateFromSelfCheck(Long userId, Long checkId) {
+        try {
+            com.etotem.cfc.entity.FiveDimensionSelfCheck check;
+            if (checkId != null) {
+                check = selfCheckMapper.selectById(checkId);
+            } else {
+                check = selfCheckMapper.selectOne(
+                        new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<com.etotem.cfc.entity.FiveDimensionSelfCheck>()
+                                .eq(com.etotem.cfc.entity.FiveDimensionSelfCheck::getUserId, userId)
+                                .orderByDesc(com.etotem.cfc.entity.FiveDimensionSelfCheck::getCreatedAt)
+                                .last("LIMIT 1"));
+            }
+            if (check == null) return null;
+            Map<String, Integer> scores = objectMapper.readValue(check.getScoresJson(),
+                    new com.fasterxml.jackson.core.type.TypeReference<Map<String, Integer>>() {});
+            List<String> lowDims = new ArrayList<>();
+            for (Map.Entry<String, Integer> e : scores.entrySet()) {
+                if (e.getValue() != null && e.getValue() <= 6) lowDims.add(e.getKey());
+            }
+            if (lowDims.isEmpty()) return null;
+            com.etotem.cfc.entity.User user = userMapper.selectById(userId);
+            if (user == null || user.getFamilyId() == null) return null;
+            Map<String, Object> inputs = new LinkedHashMap<>();
+            inputs.put("familyId", user.getFamilyId());
+            inputs.put("dimensions", String.join(",", lowDims));
+            inputs.put("goal", "基于五维自检结果,针对低分维度制定改善计划");
+            String planContent = aiGateway.generateHealthPlan(inputs);
+            if (planContent == null) return null;
+            com.etotem.cfc.entity.HealthPlan plan = new com.etotem.cfc.entity.HealthPlan();
+            plan.setFamilyId(user.getFamilyId());
+            plan.setDimensions(String.join(",", lowDims));
+            plan.setGoal("五维自检自动生成");
+            plan.setPlanContent(planContent);
+            plan.setStatus("draft");
+            plan.setCreatedAt(new Date());
+            healthPlanMapper.insert(plan);
+            return plan.getId();
+        } catch (Exception e) {
+            log.warn("generateFromSelfCheck 失败: {}", e.getMessage());
+            return null;
+        }
+    }
+```
+
+- [ ] **步骤 4:编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+
+预期:BUILD SUCCESS
+
+- [ ] **步骤 5:Commit**
+
+```bash
+git add cfc-backend/src/main/java/com/etotem/cfc/controller/family/FiveDimensionSelfCheckController.java \
+       cfc-backend/src/main/java/com/etotem/cfc/service/HealthPlanService.java \
+       cfc-backend/src/main/java/com/etotem/cfc/service/impl/HealthPlanServiceImpl.java
+git commit -m "feat(self-check): 新增 /generate-plan + /trend-analysis 接口 + HealthPlanService.generateFromSelfCheck"
+```
+
+---
+
+## 任务 7:Java — AIChatController P1-2 context 注入
+
+**文件:**
+- 修改:`cfc-backend/src/main/java/com/etotem/cfc/controller/ai/AIChatController.java`
+
+- [ ] **步骤 1:sendMessage 支持 selfCheckId**
+
+在 `AIChatController.java` 的 `sendMessage` 方法中,于第 72 行(`String surveyIdStr = params.get("surveyId");`)之后追加:
+
+```java
+        String selfCheckIdStr = params.get("selfCheckId");
+```
+
+在第 122 行(`inputs.put("portrait_prompt", chatPortrait);`)之后追加:
+
+```java
+        // 注入自检上下文(P1-2)
+        if (selfCheckIdStr != null && !selfCheckIdStr.trim().isEmpty()) {
+            try {
+                Long selfCheckId = Long.valueOf(selfCheckIdStr);
+                com.etotem.cfc.entity.FiveDimensionSelfCheck selfCheck = selfCheckMapper.selectById(selfCheckId);
+                if (selfCheck != null) {
+                    inputs.put("self_check_result", objectMapper.writeValueAsString(selfCheck));
+                }
+            } catch (Exception e) {
+                log.warn("注入自检上下文失败: {}", e.getMessage());
+            }
+        }
+```
+
+需要新增注入(如未已有):
+
+```java
+    @Resource
+    private com.etotem.cfc.mapper.FiveDimensionSelfCheckMapper selfCheckMapper;
+
+    @Resource
+    private com.fasterxml.jackson.databind.ObjectMapper objectMapper;
+```
+
+- [ ] **步骤 2:编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+
+预期:BUILD SUCCESS
+
+- [ ] **步骤 3:Commit**
+
+```bash
+git add cfc-backend/src/main/java/com/etotem/cfc/controller/ai/AIChatController.java
+git commit -m "feat(self-check): AIChat sendMessage 支持 selfCheckId 注入上下文(P1-2)"
+```
+
+---
+
+## 任务 8:前端 — utils/api.js 新增方法
+
+**文件:**
+- 修改:`cfc-frontend/utils/api.js`
+
+- [ ] **步骤 1:追加 2 个 API 方法**
+
+在 `cfc-frontend/utils/api.js` 中,于 `ignoreSelfCheck` 方法之后追加:
+
+```js
+export const generateSelfCheckPlan = (data) => {
+  return request('/api/family/self-check/generate-plan', 'POST', data || {})
+}
+
+export const getSelfCheckTrendAnalysis = () => {
+  return request('/api/family/self-check/trend-analysis', 'POST', {})
+}
+```
+
+- [ ] **步骤 2:语法校验**
+
+```bash
+node --check cfc-frontend/utils/api.js && echo "api.js OK"
+```
+
+- [ ] **步骤 3:Commit**
+
+```bash
+git add cfc-frontend/utils/api.js
+git commit -m "feat(self-check): 新增 generateSelfCheckPlan/getSelfCheckTrendAnalysis API"
+```
+
+---
+
+## 任务 9:前端 — self-check-result.vue 改造(P0-1/P0-2/P1-2)
+
+**文件:**
+- 修改:`cfc-frontend/pages/family/self-check-result.vue`
+
+- [ ] **步骤 1:替换寻源建议区块(P0-1)**
+
+将第 70-131 行的 `<view class="scr-card" v-if="result.advices...">` 区块(五行相生寻源建议 + 全部健康提示)整体替换为:
+
+```html
+      <!-- AI 健康解读(P0-1) -->
+      <view class="scr-card" v-if="result.advices && result.advices.length > 0">
+        <view class="scr-card-title">
+          <text class="scr-card-title-text">AI 健康解读</text>
+          <text class="scr-card-sub">基于你的五维自检结果</text>
+        </view>
+        <view v-for="adv in result.advices" :key="adv.dimension" class="scr-advice-item">
+          <view class="scr-advice-dim-row">
+            <view class="scr-advice-dim-badge" :style="{ background: adv.color }">
+              <text class="scr-advice-dim-name">{{ adv.dimensionName }}</text>
+              <text class="scr-advice-dim-elem">{{ adv.score }}分</text>
+            </view>
+          </view>
+          <text class="scr-advice-interpretation" v-if="adv.interpretation">{{ adv.interpretation }}</text>
+          <view class="scr-advice-actions" v-if="adv.microActions && adv.microActions.length > 0">
+            <text class="scr-advice-action-label">本周行动:</text>
+            <text v-for="(action, i) in adv.microActions" :key="'ma'+i" class="scr-advice-action">{{ i+1 }}. {{ action }}</text>
+          </view>
+        </view>
+        <view class="scr-family-insight" v-if="result.advices[0] && result.advices[0].familyInsight">
+          <text class="scr-family-insight-label">💡 家庭整体洞察</text>
+          <text class="scr-family-insight-text">{{ result.advices[0].familyInsight }}</text>
+        </view>
+        <view class="scr-fallback" v-if="result.advices[0] && result.advices[0].fallbackUsed">
+          <text>AI 分析暂时不可用,请稍后再试</text>
+        </view>
+      </view>
+      <!-- 全部健康提示 -->
+      <view class="scr-card" v-else-if="result.totalScore >= 35">
+        <view class="scr-all-healthy">
+          <text class="scr-all-healthy-icon">🌿</text>
+          <text class="scr-all-healthy-text">五维状态均健康,请继续保持这份平衡!</text>
+        </view>
+      </view>
+```
+
+- [ ] **步骤 2:底部按钮区改造(P0-2 + P1-2)**
+
+将第 134-137 行的底部按钮区替换为:
+
+```html
+      <!-- 历史与操作 -->
+      <view class="scr-footer">
+        <button class="scr-btn scr-btn-primary" @click="goHistory">历史记录</button>
+        <button class="scr-btn scr-btn-outline" @click="retake">重新自检</button>
+        <button class="scr-btn scr-btn-secondary" v-if="hasLowScore" :loading="planGenerating" @click="generatePlan">
+          生成健康计划
+        </button>
+        <button class="scr-btn scr-btn-ghost" @click="askAI">问问 AI</button>
+      </view>
+```
+
+- [ ] **步骤 3:script 改造**
+
+在 `data()` 中追加 `planGenerating: false`;在 `computed` 中追加 `hasLowScore`;在 `methods` 中追加 `generatePlan` / `askAI`:
+
+```javascript
+      planGenerating: false,   // data() 内
+    // computed 内
+    hasLowScore: function() {
+      return this.result && this.result.advices && this.result.advices.some(function(a) {
+        return a.score != null && a.score <= 6
+      })
+    },
+    // methods 内
+    generatePlan: function() {
+      var self = this
+      if (this.planGenerating) return
+      this.planGenerating = true
+      generateSelfCheckPlan({ checkId: this.result && this.result.id }).then(function(res) {
+        self.planGenerating = false
+        if (res.code === 200 && res.data && res.data.planId) {
+          uni.showToast({ title: '计划已生成,可在健康计划页查看', icon: 'success' })
+          setTimeout(function() {
+            uni.navigateTo({ url: '/pages/health-main/index?planId=' + res.data.planId })
+          }, 1000)
+        } else {
+          uni.showToast({ title: (res && res.message) || '计划生成失败', icon: 'none' })
+        }
+      }).catch(function() {
+        self.planGenerating = false
+        uni.showToast({ title: '计划生成失败', icon: 'none' })
+      })
+    },
+    askAI: function() {
+      var selfCheckId = this.result && this.result.id
+      if (!selfCheckId) {
+        uni.showToast({ title: '暂无自检记录', icon: 'none' })
+        return
+      }
+      uni.navigateTo({ url: '/pages/ai/chat?selfCheckId=' + selfCheckId })
+    }
+```
+
+- [ ] **步骤 4:import 追加**
+
+在文件头部 import 区追加:
+
+```javascript
+import { generateSelfCheckPlan } from '@/utils/api'
+```
+
+- [ ] **步骤 5:样式追加**
+
+在 `<style>` 块末尾追加(复用 `.scr-btn` 模式):
+
+```css
+.scr-btn-secondary {
+  background: linear-gradient(135deg, #10B981, #34D399);
+  color: #fff;
+  font-weight: 600;
+}
+.scr-btn-ghost {
+  background: #fff;
+  color: #666;
+  border: 2rpx solid #D1D5DB;
+}
+.scr-advice-interpretation {
+  display: block;
+  font-size: 26rpx;
+  color: #555;
+  line-height: 1.6;
+  margin: 16rpx 0;
+}
+.scr-advice-actions {
+  display: flex;
+  flex-direction: column;
+  gap: 8rpx;
+  margin-top: 12rpx;
+}
+.scr-advice-action-label {
+  font-size: 24rpx;
+  font-weight: 600;
+  color: #F97316;
+}
+.scr-advice-action {
+  font-size: 24rpx;
+  color: #555;
+  line-height: 1.5;
+}
+.scr-family-insight {
+  background: #F5FAFE;
+  border-radius: 16rpx;
+  padding: 20rpx 24rpx;
+  margin-top: 24rpx;
+}
+.scr-family-insight-label {
+  display: block;
+  font-size: 24rpx;
+  font-weight: 600;
+  color: #F97316;
+  margin-bottom: 8rpx;
+}
+.scr-family-insight-text {
+  display: block;
+  font-size: 26rpx;
+  color: #555;
+  line-height: 1.6;
+}
+.scr-fallback {
+  background: #FFF7ED;
+  border-radius: 12rpx;
+  padding: 16rpx 20rpx;
+  margin-top: 16rpx;
+  text-align: center;
+}
+.scr-fallback text {
+  font-size: 24rpx;
+  color: #999;
+}
+```
+
+- [ ] **步骤 6:语法校验**
+
+提取 script 块语法校验:
+
+```bash
+node -e "
+var fs = require('fs');
+var content = fs.readFileSync('cfc-frontend/pages/family/self-check-result.vue', 'utf8');
+var m = content.match(/<script>([\s\S]*?)<\/script>/);
+if (m) { new Function(m[1]); console.log('script OK'); } else { console.log('no script'); }
+"
+```
+
+- [ ] **步骤 7:Commit**
+
+```bash
+git add cfc-frontend/pages/family/self-check-result.vue
+git commit -m "feat(self-check): 结果页改 AI 解读 + 生成计划按钮 + 问问AI(P0-1/P0-2/P1-2)"
+```
+
+---
+
+## 任务 10:前端 — self-check-entry.vue 趋势展示(P1-1)
+
+**文件:**
+- 修改:`cfc-frontend/pages/family/self-check-entry.vue`
+
+- [ ] **步骤 1:追加趋势区块**
+
+在 `self-check-entry.vue` 的状态提示(`sce-status`)之后、按钮区(`sce-footer`)之前,插入:
+
+```html
+      <!-- 趋势分析(P1-1) -->
+      <view class="sce-trend" v-if="trendLoaded">
+        <view class="sce-trend-header">
+          <text class="sce-trend-title">📈 自检趋势</text>
+          <text class="sce-trend-summary" v-if="trendSummary">{{ trendSummary }}</text>
+        </view>
+        <text class="sce-trend-insight" v-if="aiInsight">{{ aiInsight }}</text>
+        <view class="sce-trend-loading" v-if="trendLoading">
+          <text>AI 趋势分析中...</text>
+        </view>
+      </view>
+```
+
+- [ ] **步骤 2:script 追加**
+
+在 `data()` 追加 `trendLoaded: false, trendLoading: false, aiInsight: '', trendSummary: ''`;在 `onLoad` 中追加 `this.loadTrend()`;在 `methods` 追加:
+
+```javascript
+    loadTrend: function() {
+      var self = this
+      this.trendLoading = true
+      getSelfCheckTrendAnalysis().then(function(res) {
+        self.trendLoading = false
+        self.trendLoaded = true
+        if (res.code === 200 && res.data) {
+          self.aiInsight = res.data.aiInsight || ''
+          self.trendSummary = res.data.trendSummary || ''
+        }
+      }).catch(function() {
+        self.trendLoading = false
+        self.trendLoaded = true
+      })
+    }
+```
+
+- [ ] **步骤 3:import 追加**
+
+```javascript
+import { getSelfCheckTrendAnalysis } from '@/utils/api'
+```
+
+- [ ] **步骤 4:样式追加**
+
+```css
+.sce-trend {
+  background: #fff;
+  border-radius: 24rpx;
+  padding: 30rpx 26rpx;
+  margin-bottom: 24rpx;
+  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+}
+.sce-trend-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 12rpx;
+}
+.sce-trend-title {
+  font-size: 30rpx;
+  font-weight: 600;
+  color: #333;
+}
+.sce-trend-summary {
+  font-size: 24rpx;
+  color: #F97316;
+  font-weight: 600;
+}
+.sce-trend-insight {
+  display: block;
+  font-size: 26rpx;
+  color: #555;
+  line-height: 1.6;
+}
+.sce-trend-loading {
+  padding: 10rpx 0;
+}
+.sce-trend-loading text {
+  font-size: 24rpx;
+  color: #999;
+}
+```
+
+- [ ] **步骤 5:语法校验**
+
+提取 script 块校验(同任务 9 步骤 6 方法)。
+
+- [ ] **步骤 6:Commit**
+
+```bash
+git add cfc-frontend/pages/family/self-check-entry.vue
+git commit -m "feat(self-check): 中间页展示自检趋势 AI 解读(P1-1)"
+```
+
+---
+
+## 任务 11:最终验证
+
+- [ ] **步骤 1:后端编译**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+
+预期:BUILD SUCCESS
+
+- [ ] **步骤 2:LangGraph 语法校验**
+
+```bash
+cd cfc-langgraph && python -m py_compile app/graphs/self_check_analysis_graph.py app/graphs/self_check_trend_graph.py app/api/self_check.py app/main.py && echo "OK"
+```
+
+- [ ] **步骤 3:前端语法校验**
+
+```bash
+node --check cfc-frontend/utils/api.js && echo "api.js OK"
+```
+
+- [ ] **步骤 4:提交最终 commit(如有遗漏)**
+
+```bash
+git add -A
+git diff --cached --stat
+git commit -m "feat(self-check): 五维自检 AI 结合 P0+P1 全栈实现"
+```
+
+---
+
+## 自检清单
+
+**规格覆盖度:**
+- [x] P0-1 纯 AI 替换 → 任务 1(LangGraph graph)+ 任务 3/4/5(Java Service/AiGateway/submit)
+- [x] P0-2 用户点击生成计划 → 任务 6(Controller + HealthPlanService)+ 任务 9(前端按钮)
+- [x] P1-1 历史趋势 + AI 解读 → 任务 2(trend graph)+ 任务 6(trend-analysis)+ 任务 10(前端展示)
+- [x] P1-2 自检注入 Chat → 任务 7(AIChatController)+ 任务 9(askAI 按钮)
+
+**占位符扫描:** 无"待定"/"TODO"/"后续实现"
+
+**类型一致性:**
+- `WuxingSourcingAdviceVO` 新字段(interpretation/microActions/aiInsight/familyInsight/fallbackUsed)— 任务 3 定义,任务 5 解析、任务 9 渲染
+- `SelfCheckAnalysisService.generateAdvice/generateTrend/getRecentHistory` — 任务 3 定义,任务 5/6 调用
+- `AiGateway.generateSelfCheckAdvice/generateSelfCheckTrend` — 任务 4 定义,任务 3 调用
+- `HealthPlanService.generateFromSelfCheck` — 任务 6 接口+实现,Controller 调用
+- `generateSelfCheckPlan/getSelfCheckTrendAnalysis` — 任务 8 前端定义,任务 9/10 调用