Преглед на файлове

feat(knowledge): 菌群知识统一到 health_knowledge_base,废弃 microbiome_article

- DB: microbiome_article 809 篇迁移至 health_knowledge_base(item_type=article,description=content 已扩 longtext,重复标题加序号后缀),原表数据保留于 microbiome_article_bak
- 后端: HealthKnowledgeBaseService 新增 pageArticles(item_type=article,ORDER BY id 稳定分页),Controller 新增 POST /api/health/knowledge/article/list 供 langgraph 同步
- langgraph: get_microbiome_articles 改从 health_knowledge_base 拉取(修复旧接口 LIMIT/OFFSET 无稳定排序导致跨页重复、向量库缺 30 篇的问题)
- 前端 health-knowledge.vue 无需改动,列表将自动显示 809 篇文章
Xiaogang Liao преди 4 дни
родител
ревизия
4e539c3275

+ 12 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/HealthKnowledgeBaseController.java

@@ -1,5 +1,6 @@
 package com.etotem.cfc.controller;
 
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.etotem.cfc.common.Result;
 import com.etotem.cfc.entity.HealthKnowledgeBase;
 import com.etotem.cfc.service.HealthKnowledgeBaseService;
@@ -66,6 +67,17 @@ public class HealthKnowledgeBaseController {
         return Result.success(healthKnowledgeBaseService.listAll());
     }
 
+    @Operation(summary = "菌群文章分页列表(item_type=article,供 langgraph 向量库同步,替代 microbiome_article)")
+    @PostMapping("/article/list")
+    public Result<Page<HealthKnowledgeBase>> articleList(@RequestBody(required = false) Map<String, Object> params) {
+        int page = (int) ParamUtils.getLong(params == null ? null : params.get("page"), 1L);
+        int size = (int) ParamUtils.getLong(params == null ? null : params.get("size"), 50L);
+        if (size <= 0 || size > 500) {
+            size = 50;
+        }
+        return Result.success(healthKnowledgeBaseService.pageArticles(page, size));
+    }
+
     @Operation(summary = "新增知识库条目")
     @PostMapping("/add")
     public Result add(@RequestBody HealthKnowledgeBase entity) {

+ 14 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/HealthKnowledgeBaseService.java

@@ -1,6 +1,7 @@
 package com.etotem.cfc.service;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.etotem.cfc.entity.HealthKnowledgeBase;
 import com.etotem.cfc.mapper.HealthKnowledgeBaseMapper;
 import lombok.extern.slf4j.Slf4j;
@@ -61,6 +62,19 @@ public class HealthKnowledgeBaseService {
         return healthKnowledgeBaseMapper.selectList(null);
     }
 
+    /**
+     * 菌群文章分页列表(item_type='article',按 id 升序保证分页稳定)。
+     * 作为 langgraph 向量库的统一数据源,替代已废弃的 microbiome_article 表。
+     */
+    public Page<HealthKnowledgeBase> pageArticles(int page, int size) {
+        return healthKnowledgeBaseMapper.selectPage(
+                new Page<>(page, size),
+                new LambdaQueryWrapper<HealthKnowledgeBase>()
+                        .eq(HealthKnowledgeBase::getItemType, "article")
+                        .orderByAsc(HealthKnowledgeBase::getId)
+        );
+    }
+
     public void save(HealthKnowledgeBase entry) {
         if (entry.getId() != null) {
             healthKnowledgeBaseMapper.updateById(entry);

+ 28 - 5
cfc-langgraph/app/tools/java_client.py

@@ -100,14 +100,20 @@ class JavaClient:
         return {}
 
     async def get_microbiome_articles(self) -> list[dict]:
-        """拉取菌群知识库文章(分页,避免单次响应过大导致 Broken pipe)"""
+        """拉取菌群知识库文章(统一数据源 health_knowledge_base,item_type='article')
+
+        2026-10-20 迁移:microbiome_article 表废弃,809 篇菌群知识统一到
+        health_knowledge_base 管理。分页按 id 升序稳定(ORDER BY id),
+        避免旧 /api/microbiome/article/list 接口 LIMIT/OFFSET 无稳定排序
+        导致的跨页重复(曾拉到 30 条重复 → 向量库缺内容)。
+        """
         client = await self._get_client()
         page = 1
-        size = 50
+        size = 100
         records = []
         while True:
-            resp = await client.post("/api/microbiome/article/list",
-                                     json={"page": page, "size": size, "keyword": "", "category": ""})
+            resp = await client.post("/api/health/knowledge/article/list",
+                                     json={"page": page, "size": size})
             data = resp.json()
             if data.get("code") != 200:
                 break
@@ -118,7 +124,24 @@ class JavaClient:
             if page * size >= int(total or 0) or not items:
                 break
             page += 1
-        return records
+        # health_knowledge_base 字段 → loader/_build_chunks 所需结构
+        mapped = []
+        for r in records:
+            if r.get("id") is None:
+                continue
+            title = r.get("itemName") or ""
+            if not title:
+                continue
+            mapped.append({
+                "id": r.get("id"),
+                "title": title,
+                "content": r.get("description") or "",
+                "category": r.get("category") or "",
+                "tags": "",
+                "source_url": r.get("source") or "",
+                "pub_date": str(r.get("updatedAt") or r.get("updated_at") or ""),
+            })
+        return mapped
 
     async def get_dan_knowledge_base(self) -> list[dict]:
         """拉取知识库内容(dan_knowledge_base 表,仅启用 status=1)"""