From c6c6a92645ccc1b883ebd4ce886fb922151e9f33 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sun, 28 Jun 2026 20:20:37 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BA=8C=E7=BB=B4=E7=A0=81?= =?UTF-8?q?=20OCR=20=E4=B8=8A=E4=BC=A0=E4=BD=93=E9=AA=8C=EF=BC=9A=E5=B9=B6?= =?UTF-8?q?=E5=8F=91=E6=8E=A7=E5=88=B6=E3=80=81=E7=8A=B6=E6=80=81=E6=B8=85?= =?UTF-8?q?=E7=90=86=E3=80=81=E9=A2=84=E8=A7=88=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/views/AdminQrCodePoolView.vue | 50 +++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) 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)
-
+