feat: 优化文件上传模块,添加批内去重和错误处理

- 提取 fetchSingleUrlItem 函数,复用 URL 获取逻辑
- 添加 mergeSameNameFiles 函数合并同名文件列表
- 批内按内容哈希去重,避免同一批次重复入库
- 优化内容重复错误提示
This commit is contained in:
Wenjie Zhang 2026-03-18 02:19:29 +08:00
parent 42aa4f4771
commit c980dedd6b

View File

@ -453,6 +453,7 @@ const fileList = ref([])
const urlList = ref([])
const newUrl = ref('')
const fetchingUrls = ref(false)
const CONTENT_EXISTS_ERROR_TEXT = '内容已存在于知识库中'
//
const sameNameFiles = ref([])
@ -467,6 +468,38 @@ const isValidUrl = (string) => {
}
}
const mergeSameNameFiles = (sameNameList = []) => {
if (!Array.isArray(sameNameList) || sameNameList.length === 0) {
return
}
const existingIds = new Set(sameNameFiles.value.map((f) => f.file_id))
const newConflicts = sameNameList.filter((f) => !existingIds.has(f.file_id))
sameNameFiles.value.push(...newConflicts)
}
const fetchSingleUrlItem = async (item) => {
item.status = 'fetching'
try {
const res = await fileApi.fetchUrl(item.url, databaseId.value)
item.status = 'success'
item.data = res
mergeSameNameFiles(res.same_name_files)
} catch (error) {
console.error('Failed to fetch URL:', error)
item.status = 'error'
const detailData = error.response?.data?.detail
const detailMessage =
(typeof detailData === 'string' ? detailData : detailData?.message) || error.message || ''
if (detailMessage.includes('same content') || detailMessage.includes('相同内容')) {
item.error = CONTENT_EXISTS_ERROR_TEXT
mergeSameNameFiles(detailData?.same_name_files)
} else {
item.error = detailMessage || '加载失败'
}
}
}
const handleFetchUrls = async () => {
const text = newUrl.value
if (!text) return
@ -500,37 +533,7 @@ const handleFetchUrls = async () => {
newUrl.value = '' //
fetchingUrls.value = true
// 2.
// 使 p-limit
const processItem = async (item) => {
item.status = 'fetching'
try {
const res = await fileApi.fetchUrl(item.url, databaseId.value)
item.status = 'success'
item.data = res
//
if (res.has_same_name && res.same_name_files && res.same_name_files.length > 0) {
//
const existingIds = new Set(sameNameFiles.value.map((f) => f.file_id))
const newConflicts = res.same_name_files.filter((f) => !existingIds.has(f.file_id))
sameNameFiles.value.push(...newConflicts)
}
} catch (error) {
console.error('Failed to fetch URL:', error)
item.status = 'error'
// (409)
const detail = error.response?.data?.detail || error.message || ''
if (detail.includes('same content') || detail.includes('相同内容')) {
item.error = '内容已存在于知识库中'
} else {
item.error = detail || '加载失败'
}
}
}
await Promise.all(newItems.map(processItem))
await Promise.all(newItems.map(fetchSingleUrlItem))
fetchingUrls.value = false
}
@ -1006,6 +1009,29 @@ const chunkData = async () => {
return
}
//
const deduplicatedItems = []
const seenKeys = new Set()
let skippedDuplicates = 0
for (const item of successfulItems) {
const dedupKey = item.data?.content_hash || item.data?.file_path || item.url
if (seenKeys.has(dedupKey)) {
skippedDuplicates += 1
continue
}
seenKeys.add(dedupKey)
deduplicatedItems.push(item)
}
if (deduplicatedItems.length === 0) {
message.error('URL 内容均为重复项,请更换后重试')
return
}
if (skippedDuplicates > 0) {
message.warning(`检测到 ${skippedDuplicates} 个重复 URL 内容,已保留首个并跳过其余项`)
}
try {
store.state.chunkLoading = true
const params = { ...chunkParams.value }
@ -1017,7 +1043,7 @@ const chunkData = async () => {
// _preprocessed_map items (minio urls)
const items = []
const preprocessedMap = {}
for (const item of successfulItems) {
for (const item of deduplicatedItems) {
// item.data = { file_path: "http://minio...", content_hash: "...", filename: "...", ... }
// fetch-url file_path MinIO URL
// MinIO URL addDocuments