From cadf44e0b82980923d5ea6491091730ff742041e Mon Sep 17 00:00:00 2001 From: Nelson <1475262689@qq.com> Date: Fri, 17 Jul 2026 15:34:23 +0800 Subject: [PATCH] fix: allow direct knowledge deletion --- .../components/KnowledgeManagementView.vue | 8 +++---- .../apps/backend/app/api/admin_knowledge.py | 24 ++++++++++++------- .../backend/tests/test_knowledge_admin.py | 15 ++++++++++++ 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/ai_knowledge_base_v2/apps/admin-web/src/components/KnowledgeManagementView.vue b/ai_knowledge_base_v2/apps/admin-web/src/components/KnowledgeManagementView.vue index c22cab4..06a670c 100644 --- a/ai_knowledge_base_v2/apps/admin-web/src/components/KnowledgeManagementView.vue +++ b/ai_knowledge_base_v2/apps/admin-web/src/components/KnowledgeManagementView.vue @@ -310,9 +310,9 @@ async function archive(item: KnowledgeItem) { async function remove(item: KnowledgeItem) { if (!await confirmAction( - `将物理删除“${item.name}”及关联数据。该操作不可恢复,并可能影响历史检索记录定位。`, - "物理删除知识库", - { confirmButtonText: "确认删除", cancelButtonText: "取消", type: "error" }, + `将永久删除“${item.name}”的知识正文、版本、章节、切片和同步记录,并立即停止 Agent 使用。审计日志仍会保留,该操作不可恢复。`, + "永久删除知识库", + { confirmButtonText: "永久删除", cancelButtonText: "取消", type: "error" }, )) return; await api.deleteKnowledge(item.id); ElMessage.success("知识库已删除"); @@ -402,7 +402,7 @@ async function confirmAction( - + 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 8280fa0..6cc241c 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 @@ -192,12 +192,13 @@ def delete_knowledge( ) -> dict: _require_super_admin(current_admin) knowledge = _get_knowledge(db, knowledge_id) - if knowledge.lifecycle_status != "archived" or knowledge.status != 0: - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, - detail="物理删除前必须先关闭并归档知识库", - ) - # 清理早期按用户授权口径留下的历史关联,避免外键阻止知识库删除。 + # 删除操作本身会立即停止开放并清理正式内容,不再要求管理员先执行“关闭、归档”两步。 + knowledge.status = 0 + knowledge.lifecycle_status = "archived" + db.add(knowledge) + db.flush() + + # 清理按用户授权口径留下的历史关联,避免外键阻止知识库删除。 permissions = db.scalars( select(UserKnowledgePermission).where(UserKnowledgePermission.knowledge_id == knowledge.id) ).all() @@ -212,9 +213,16 @@ def delete_knowledge( db.flush() for row in db.scalars(select(KnowledgeSourceSnapshot).where(KnowledgeSourceSnapshot.knowledge_id == knowledge.id)).all(): db.delete(row) - db.delete(knowledge) OperationLogService.write(db, admin_id=current_admin.id, module="knowledge", action="delete", target_id=knowledge.id) - db.commit() + db.delete(knowledge) + try: + db.commit() + except IntegrityError as exc: + db.rollback() + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail="知识库仍有关联数据,删除未执行,请刷新后重试", + ) from exc return api_success() diff --git a/ai_knowledge_base_v2/apps/backend/tests/test_knowledge_admin.py b/ai_knowledge_base_v2/apps/backend/tests/test_knowledge_admin.py index ed35262..7a3694b 100644 --- a/ai_knowledge_base_v2/apps/backend/tests/test_knowledge_admin.py +++ b/ai_knowledge_base_v2/apps/backend/tests/test_knowledge_admin.py @@ -10,6 +10,7 @@ from sqlalchemy import create_engine, select from sqlalchemy.orm import Session from sqlalchemy.pool import StaticPool +from app.api.admin_knowledge import delete_knowledge from app.api.admin_knowledge_lifecycle import batch_sync, batch_update_metadata, update_metadata from app.models import Base from app.models.admin import Admin, Role @@ -134,6 +135,20 @@ def test_batch_metadata_update_reports_missing_items(): assert db.scalar(select(Knowledge).where(Knowledge.id == item.id)).knowledge_type == "qa" +def test_active_knowledge_can_be_deleted_without_manual_archive_steps(): + with _database() as db: + item = _knowledge(db) + knowledge_id = item.id + + response = delete_knowledge(knowledge_id, db, _admin()) + + assert response["code"] == 0 + assert db.get(Knowledge, knowledge_id) is None + assert db.scalars(select(KnowledgeVersion).where(KnowledgeVersion.knowledge_id == knowledge_id)).all() == [] + assert db.scalars(select(KnowledgeManifest).where(KnowledgeManifest.knowledge_id == knowledge_id)).all() == [] + assert db.scalars(select(KnowledgeSourceSnapshot).where(KnowledgeSourceSnapshot.knowledge_id == knowledge_id)).all() == [] + + def test_batch_sync_continues_after_an_item_fails_and_reports_each_result(): with _database() as db: first = _knowledge(db, 1)