From 67048a14e87bbd6eeda8fe5ebdea4ce6d7aec341 Mon Sep 17 00:00:00 2001 From: Nelson <1475262689@qq.com> Date: Thu, 9 Jul 2026 10:13:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=AE=80=E5=8C=96=E7=9F=A5=E8=AF=86?= =?UTF-8?q?=E5=BA=93=E5=AF=BC=E5=85=A5=E2=80=94=E2=80=94=E5=8F=AA=E8=BE=93?= =?UTF-8?q?=E5=85=A5NodeToken=EF=BC=8C=E8=87=AA=E5=8A=A8=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E6=A0=87=E9=A2=98=E5=92=8CSpaceID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - admin_knowledge.py: /knowledge/import 改为单条导入,自动获取 title+space_id - feishu_service.py: 新增 get_node_info() 公开方法 - App.vue: 简化 UI,只保留 NodeToken 输入框和导入按钮 - api.ts: 更新类型定义 --- .../apps/admin-web/src/App.vue | 22 ++++--- .../apps/admin-web/src/services/api.ts | 4 +- .../apps/backend/app/api/admin_knowledge.py | 64 ++++++++----------- .../backend/app/services/feishu_service.py | 11 ++++ 4 files changed, 54 insertions(+), 47 deletions(-) diff --git a/ai_knowledge_base_v2/apps/admin-web/src/App.vue b/ai_knowledge_base_v2/apps/admin-web/src/App.vue index f3cd91f..29eaca6 100644 --- a/ai_knowledge_base_v2/apps/admin-web/src/App.vue +++ b/ai_knowledge_base_v2/apps/admin-web/src/App.vue @@ -37,7 +37,7 @@ const chatDetailOpen = ref(false); const recordTab = ref("chats"); const editingModelId = ref(null); const editingKnowledgeId = ref(null); -const batchParentToken = ref(""); +const batchNodeToken = ref(""); const batchImporting = ref(false); const agentDebugging = ref(false); const agentPreviewMessages = ref<{ role: "user" | "assistant" | "system"; content: string }[]>([ @@ -592,16 +592,20 @@ function resetKnowledgeForm() { } async function batchImportKnowledge() { - const parentToken = batchParentToken.value.trim(); - if (!parentToken) { - ElMessage.warning("请输入父节点 NodeToken"); + const token = batchNodeToken.value.trim(); + if (!token) { + ElMessage.warning("请输入 NodeToken"); return; } batchImporting.value = true; try { - const result = await api.importKnowledgeFromSpace({ parent_node_token: parentToken }); - ElMessage.success(`导入完成:新增 ${result.imported} 个,跳过 ${result.skipped} 个`); - batchParentToken.value = ""; + const result = await api.importKnowledgeFromSpace({ node_token: token }); + if (result.imported > 0) { + ElMessage.success(`导入成功:${result.name}(id=${result.id})`); + } else { + ElMessage.info(`该节点已存在:${result.name}(id=${result.id})`); + } + batchNodeToken.value = ""; await loadCurrentMenu(); } catch (e: any) { ElMessage.error(e.message || "导入失败"); @@ -1125,8 +1129,8 @@ function buildChatQuery() {
- - 递归导入 + + 导入
diff --git a/ai_knowledge_base_v2/apps/admin-web/src/services/api.ts b/ai_knowledge_base_v2/apps/admin-web/src/services/api.ts index 4c9cc16..ab61707 100644 --- a/ai_knowledge_base_v2/apps/admin-web/src/services/api.ts +++ b/ai_knowledge_base_v2/apps/admin-web/src/services/api.ts @@ -106,8 +106,8 @@ export const api = { updateKnowledge: (id: number, payload: Record) => request(`/admin/knowledge/${id}`, { method: "PUT", body: JSON.stringify(payload) }), deleteKnowledge: (id: number) => request(`/admin/knowledge/${id}`, { method: "DELETE" }), - importKnowledgeFromSpace: (body: Record) => - request<{ imported: number; skipped: number; nodes: { name: string; nodeToken: string; status: string; id?: number }[] }>( + importKnowledgeFromSpace: (body: { node_token: string }) => + request<{ imported: number; skipped: number; id: number; name: string }>( "/admin/knowledge/import", { method: "POST", body: JSON.stringify(body) }, ), diff --git a/ai_knowledge_base_v2/apps/backend/app/api/admin_knowledge.py b/ai_knowledge_base_v2/apps/backend/app/api/admin_knowledge.py index ee79844..b53d9ee 100644 --- a/ai_knowledge_base_v2/apps/backend/app/api/admin_knowledge.py +++ b/ai_knowledge_base_v2/apps/backend/app/api/admin_knowledge.py @@ -28,46 +28,38 @@ def import_knowledge_from_space( db: Session = Depends(get_db), current_admin: Admin = Depends(get_current_admin), ) -> dict: - parent_node_token = payload.get("parent_node_token", "").strip() - if not parent_node_token: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="父节点 NodeToken 不能为空") + node_token = payload.get("node_token", "").strip() + if not node_token: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="NodeToken 不能为空") - nodes = FeishuKnowledgeService.import_nodes_from_parent(parent_node_token, db) - if not nodes: - return api_success({"imported": 0, "skipped": 0, "nodes": []}) - - # 获取已存在的 node_id,避免重复导入 - existing_nodes = { - row.feishu_node_id for row in db.scalars(select(Knowledge)).all() if row.feishu_node_id - } - - imported = 0 - skipped = 0 - results = [] - for node in nodes: - node_token = node["node_token"] - if node_token in existing_nodes: - skipped += 1 - results.append({"name": node["name"], "nodeToken": node_token, "status": "skipped"}) - continue - - knowledge = Knowledge( - name=node["name"], - feishu_space_id=space_id, - feishu_node_id=node_token, - status=1, - remark=f"obj_type={node.get('obj_type', '')}", - ) - db.add(knowledge) - db.flush() - imported += 1 - results.append({"name": node["name"], "nodeToken": node_token, "status": "imported", "id": knowledge.id}) - OperationLogService.write( - db, admin_id=current_admin.id, module="knowledge", action="import", target_id=knowledge.id + info = FeishuKnowledgeService.get_node_info(node_token, db) + space_id = info.get("space_id", "") + if not space_id: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"无法获取节点 {node_token} 的 SpaceID", ) + # 检查是否已存在 + existing = db.scalar(select(Knowledge).where(Knowledge.feishu_node_id == node_token)) + if existing is not None: + return api_success({"imported": 0, "skipped": 1, "id": existing.id, "name": existing.name}) + + knowledge = Knowledge( + name=info.get("title", node_token), + feishu_space_id=space_id, + feishu_node_id=node_token, + status=1, + remark=f"obj_type={info.get('obj_type', '')}", + ) + db.add(knowledge) + db.flush() + OperationLogService.write( + db, admin_id=current_admin.id, module="knowledge", action="import", target_id=knowledge.id + ) db.commit() - return api_success({"imported": imported, "skipped": skipped, "nodes": results}) + db.refresh(knowledge) + return api_success({"imported": 1, "skipped": 0, "id": knowledge.id, "name": knowledge.name}) @router.post("/knowledge") diff --git a/ai_knowledge_base_v2/apps/backend/app/services/feishu_service.py b/ai_knowledge_base_v2/apps/backend/app/services/feishu_service.py index 632ad8d..d01cda5 100644 --- a/ai_knowledge_base_v2/apps/backend/app/services/feishu_service.py +++ b/ai_knowledge_base_v2/apps/backend/app/services/feishu_service.py @@ -330,6 +330,17 @@ class FeishuKnowledgeService: cls._content_cache.clear() return count + @classmethod + def get_node_info(cls, node_token: str, db: Session | None = None) -> dict: + """获取 Wiki 节点基本信息(space_id、标题、obj_type 等)""" + config = _feishu_retrieval_config(db) + if not config.app_id or not config.app_secret: + raise ExternalServiceError( + "未配置飞书应用凭证,无法读取飞书知识库。", + provider="feishu", + ) + return _get_wiki_node_info(node_token, config) + @classmethod def import_nodes_from_parent( cls,