优化知识库文件批量删除为50条分批请求

- 新增后端批量删除接口 /documents/batch

- 前端批量删除改为每批50个文件请求

- 修复删除进度提示不更新问题(统一 key 更新 loading)
This commit is contained in:
肖泽涛 2026-03-01 16:18:59 +08:00
parent 2b80e8c41f
commit b2788fc173
3 changed files with 93 additions and 15 deletions

View File

@ -659,6 +659,55 @@ async def get_document_content(db_id: str, doc_id: str, current_user: User = Dep
return {"message": "Failed to get file content", "status": "failed"}
@knowledge.delete("/databases/{db_id}/documents/batch")
async def batch_delete_documents(db_id: str, file_ids: list[str] = Body(...), current_user: User = Depends(get_admin_user)):
"""批量删除文档或文件夹"""
logger.debug(f"BATCH DELETE documents {file_ids} in {db_id}")
await _ensure_database_not_dify(db_id, "批量文档删除")
deleted_count = 0
failed_items = []
for doc_id in file_ids:
try:
file_meta_info = await knowledge_base.get_file_basic_info(db_id, doc_id)
# Check if it is a folder
is_folder = file_meta_info.get("meta", {}).get("is_folder", False)
if is_folder:
await knowledge_base.delete_folder(db_id, doc_id)
deleted_count += 1
continue
file_name = file_meta_info.get("meta", {}).get("filename")
# 尝试从MinIO删除文件如果失败例如旧知识库没有MinIO实例则忽略
try:
minio_client = get_minio_client()
await minio_client.adelete_file("ref-" + db_id.replace("_", "-"), file_name)
logger.debug(f"成功从MinIO删除文件: {file_name}")
except Exception as minio_error:
logger.warning(f"从MinIO删除文件失败可能是旧知识库: {minio_error}")
# 无论MinIO删除是否成功都继续从知识库删除
await knowledge_base.delete_file(db_id, doc_id)
deleted_count += 1
except Exception as e:
logger.error(f"批量删除过程中删除文档 {doc_id} 失败: {e}, {traceback.format_exc()}")
failed_items.append({"doc_id": doc_id, "error": str(e)})
if failed_items:
if deleted_count == 0:
raise HTTPException(status_code=400, detail=f"批量删除失败: 所有 {len(failed_items)} 个文件均未删除。")
return {
"message": f"部分删除成功: 已删除 {deleted_count} 个文件,失败 {len(failed_items)}",
"deleted_count": deleted_count,
"failed_items": failed_items
}
return {"message": f"批量删除成功: 已删除 {deleted_count} 个文件", "deleted_count": deleted_count}
@knowledge.delete("/databases/{db_id}/documents/{doc_id}")
async def delete_document(db_id: str, doc_id: str, current_user: User = Depends(get_admin_user)):
"""删除文档或文件夹"""

View File

@ -145,6 +145,22 @@ export const documentApi = {
return apiAdminDelete(`/api/knowledge/databases/${dbId}/documents/${docId}`)
},
/**
* 批量删除文档
* @param {string} dbId - 知识库ID
* @param {Array} fileIds - 文件ID列表
* @returns {Promise} - 批量删除结果
*/
batchDeleteDocuments: async (dbId, fileIds) => {
return apiRequest(`/api/knowledge/databases/${dbId}/documents/batch`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(fileIds)
}, true, 'json')
},
/**
* 下载文档
* @param {string} dbId - 知识库ID

View File

@ -208,24 +208,36 @@ export const useDatabaseStore = defineStore('database', () => {
state.batchDeleting = true
let successCount = 0
let failureCount = 0
let progressMessage = message.loading(`正在删除文件 0/${validFileIds.length}`, 0)
let processedCount = 0
const totalCount = validFileIds.length
const progressKey = `batch-delete-${Date.now()}`
message.loading({ content: `正在删除文件 0/${totalCount}`, key: progressKey, duration: 0 })
try {
for (let i = 0; i < validFileIds.length; i++) {
const fileId = validFileIds[i]
const CHUNK_SIZE = 50
for (let i = 0; i < totalCount; i += CHUNK_SIZE) {
const chunk = validFileIds.slice(i, i + CHUNK_SIZE)
try {
await deleteFile(fileId)
successCount++
} catch (error) {
console.error(`删除文件 ${fileId} 失败:`, error)
failureCount++
}
progressMessage?.()
if (i + 1 < validFileIds.length) {
progressMessage = message.loading(`正在删除文件 ${i + 1}/${validFileIds.length}`, 0)
const res = await documentApi.batchDeleteDocuments(databaseId.value, chunk)
successCount += res.deleted_count || 0
if (res.failed_items) {
failureCount += res.failed_items.length
}
} catch (err) {
console.error(`删除批次 ${i / CHUNK_SIZE + 1} 失败:`, err)
failureCount += chunk.length
} finally {
processedCount += chunk.length
message.loading({
content: `正在删除文件 ${processedCount}/${totalCount}`,
key: progressKey,
duration: 0
})
}
}
progressMessage?.()
message.destroy(progressKey)
if (successCount > 0 && failureCount === 0) {
message.success(`成功删除 ${successCount} 个文件`)
} else if (successCount > 0 && failureCount > 0) {
@ -233,12 +245,13 @@ export const useDatabaseStore = defineStore('database', () => {
} else if (failureCount > 0) {
message.error(`${failureCount} 个文件删除失败`)
}
selectedRowKeys.value = []
await getDatabaseInfo(undefined, true) // Skip query params for batch deletion
} catch (error) {
progressMessage?.()
message.destroy(progressKey)
console.error('批量删除出错:', error)
message.error('批量删除过程中发生错误')
message.error(error.message || '批量删除过程中发生错误')
} finally {
state.batchDeleting = false
}