diff --git a/frontend/src/features/admin/views/AdminQrCodePoolView.vue b/frontend/src/features/admin/views/AdminQrCodePoolView.vue index 2a104aa..00701ca 100644 --- a/frontend/src/features/admin/views/AdminQrCodePoolView.vue +++ b/frontend/src/features/admin/views/AdminQrCodePoolView.vue @@ -67,6 +67,7 @@ const uploadVisible = ref(false) const uploadPendingCount = ref(0) const uploadSaving = ref(false) type UploadedQrImage = { + _id: string url: string previewUrl: string file: File @@ -230,6 +231,37 @@ function parseGroupNameFromOcrText(text: string) { return candidates.find(line => line.includes('群')) || candidates.find(line => /[-—]/.test(line)) || candidates[0] || '' } +const ocrConcurrencyLimit = 4 +let ocrRunningCount = 0 +const ocrWaitQueue: (() => void)[] = [] + +function ocrAcquireSlot(): Promise { + if (ocrRunningCount < ocrConcurrencyLimit) { + ocrRunningCount++ + return Promise.resolve() + } + return new Promise(resolve => { + ocrWaitQueue.push(() => { + ocrRunningCount++ + resolve() + }) + }) +} + +function ocrReleaseSlot() { + ocrRunningCount = Math.max(0, ocrRunningCount - 1) + const next = ocrWaitQueue.shift() + if (next) next() +} + +function ocrResetSlots() { + ocrRunningCount = 0 + while (ocrWaitQueue.length > 0) { + const next = ocrWaitQueue.shift() + next?.() + } +} + async function recognizeGroupName(file: File) { const { job_id } = await submitOCRJob(file) const delays = [1000, 2000, 3000, 3000, 3000] @@ -246,6 +278,15 @@ async function recognizeGroupName(file: File) { throw new Error('OCR 识别超时') } +async function recognizeGroupNameWithLimit(file: File) { + await ocrAcquireSlot() + try { + return await recognizeGroupName(file) + } finally { + ocrReleaseSlot() + } +} + // 上传:逐个上传图片拿 URL async function handleFileUpload(options: { file: File }) { if (uploadedImages.value.length + uploadPendingCount.value >= maxBatchUploadCount) { @@ -260,6 +301,7 @@ async function handleFileUpload(options: { file: File }) { uploadPendingCount.value += 1 try { const item: UploadedQrImage = { + _id: `${Date.now()}_${Math.random().toString(36).slice(2)}`, url: '', previewUrl: URL.createObjectURL(options.file), file: options.file, @@ -270,7 +312,7 @@ async function handleFileUpload(options: { file: File }) { const [uploaded, groupName] = await Promise.all([ uploadAdminFile(options.file, 'qrcode'), - recognizeGroupName(options.file).catch(error => { + recognizeGroupNameWithLimit(options.file).catch(error => { console.warn('二维码群名 OCR 失败', error) ElMessage.warning(readError(error, '二维码群名 OCR 失败')) return '' @@ -307,6 +349,8 @@ function clearUploadedImages() { if (item.previewUrl) URL.revokeObjectURL(item.previewUrl) }) uploadedImages.value = [] + uploadPendingCount.value = 0 + ocrResetSlots() } function handleUploadExceed() { @@ -778,7 +822,7 @@ onMounted(reloadAll)
-
+