优化知识库文件批量删除为50条分批请求
- 新增后端批量删除接口 /documents/batch - 前端批量删除改为每批50个文件请求 - 修复删除进度提示不更新问题(统一 key 更新 loading)
This commit is contained in:
parent
2b80e8c41f
commit
b2788fc173
@ -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)):
|
||||
"""删除文档或文件夹"""
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user