From 31ac13d308df83ab23304b7f65953f8dea53da81 Mon Sep 17 00:00:00 2001 From: mohammad sedighi Date: Mon, 29 Jun 2026 01:31:09 +0330 Subject: [PATCH] work on moving customers from tahesab 7 --- .../components/TahesabCustomerSync.vue | 24 +++++--- src/store/customerImportStore.ts | 57 ++++++++++++------- 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/src/pages/menuPages/accounting/components/TahesabCustomerSync.vue b/src/pages/menuPages/accounting/components/TahesabCustomerSync.vue index 2bef580..88b35a1 100644 --- a/src/pages/menuPages/accounting/components/TahesabCustomerSync.vue +++ b/src/pages/menuPages/accounting/components/TahesabCustomerSync.vue @@ -384,17 +384,21 @@ async function load(panelPage = 1) { search: panelSearch.value || undefined, page: panelPage, }) - status.value = res + // این درخواست داده‌های ته‌حساب را شامل نمی‌شود؛ پس فیلدهای ته‌حساب را دست‌نخورده نگه می‌داریم + if (status.value) { + status.value.panel_total = res.panel_total + status.value.panel_registered = res.panel_registered + status.value.panel_only = res.panel_only + status.value.panel_only_list = res.panel_only_list + } else { + status.value = res + } panelList.value = res.panel_only_list.data panelPagination.value = { page: res.panel_only_list.current_page, lastPage: res.panel_only_list.last_page, total: res.panel_only_list.total, } - // داده‌های ته‌حساب فقط اگر قبلاً fetch شده باشه بروز کن - if (tahesabFetched.value) { - tahesabOnlyList.value = res.tahesab_only_list.data ?? [] - } } catch (e) { console.error(e) } finally { loadingPanel.value = false } } @@ -500,9 +504,12 @@ async function doImport(customers: TahesabOnlyCustomer[]) { if (res.success) { message.success(`${res.success} مشتری وارد پنل شد`) customers.forEach(c => { if (!res.errors.find((e: any) => e.code === c.code)) tahesabDone.add(c.code) }) + if (status.value?.tahesab_only_count != null) { + status.value.tahesab_only_count = Math.max(0, status.value.tahesab_only_count - res.success) + } } if (res.failed) message.warning(`${res.failed} مشتری وارد نشد`) - await load(panelPagination.value.page) + await loadPanel(panelPagination.value.page) selectedTahesab.clear() } catch { message.error('خطا در ارتباط با سرور') } finally { syncingTahesab.value = false } @@ -531,7 +538,10 @@ watch(() => importStore.doneCodes.length, () => { watch(() => importStore.status, async (s, prev) => { if (prev === 'running' && (s === 'done' || s === 'canceled')) { selectedTahesab.clear() - await load(panelPagination.value.page) + if (status.value?.tahesab_only_count != null) { + status.value.tahesab_only_count = Math.max(0, status.value.tahesab_only_count - importStore.success) + } + await loadPanel(panelPagination.value.page) } }) diff --git a/src/store/customerImportStore.ts b/src/store/customerImportStore.ts index 8d7426b..5490463 100644 --- a/src/store/customerImportStore.ts +++ b/src/store/customerImportStore.ts @@ -13,6 +13,11 @@ export type ImportStatus = 'idle' | 'running' | 'done' | 'canceled' /** نتیجهٔ نهایی برای نمایش آیکن/متن مناسب */ export type ImportOutcome = 'running' | 'success' | 'error' | 'partial' | 'canceled' +/** تعداد مشتری در هر درخواست (برای جلوگیری از محدودیت نرخ سرور) */ +const CHUNK_SIZE = 50 + +const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)) + /** * فروشگاه سراسری برای فرایند «وارد کردن همه» مشتریان ته‌حساب. * چون به‌صورت singleton است، با جابه‌جایی بین صفحات از بین نمی‌رود و @@ -100,6 +105,19 @@ export const useCustomerImportStore = defineStore('customerImport', () => { await runLoop() } + // ارسال یک دسته با تلاش مجدد در صورت خطا (مثلاً محدودیت نرخ موقتی) + async function importChunkWithRetry(chunk: TahesabOnlyCustomer[], attempt = 0): Promise { + try { + return await importFromTahesab(chunk) + } catch (e) { + if (attempt < 2 && !cancelRequested) { + await sleep(1500 * (attempt + 1)) + return importChunkWithRetry(chunk, attempt + 1) + } + throw e + } + } + async function runLoop() { while (processed.value < queue.value.length) { if (cancelRequested) { @@ -107,29 +125,30 @@ export const useCustomerImportStore = defineStore('customerImport', () => { return } - const customer = queue.value[processed.value] + const chunk = queue.value.slice(processed.value, processed.value + CHUNK_SIZE) try { - const res = await importFromTahesab([customer]) - if (res.success) { - success.value++ - doneCodes.value.push(customer.code) - } else { - failed.value++ - errors.value.push({ - code: customer.code, - name: customer.name, - error: res.errors?.[0]?.error || 'ثبت ناموفق بود', - }) + const res = await importChunkWithRetry(chunk) + const errCodes = new Set((res.errors ?? []).map((e: any) => e.code)) + for (const c of chunk) { + if (errCodes.has(c.code)) { + const err = (res.errors ?? []).find((e: any) => e.code === c.code) + failed.value++ + errors.value.push({ code: c.code, name: c.name, error: err?.error || 'ثبت ناموفق بود' }) + } else { + success.value++ + doneCodes.value.push(c.code) + } } } catch (e) { - failed.value++ - errors.value.push({ - code: customer.code, - name: customer.name, - error: 'خطا در ارتباط با سرور', - }) + // کل دسته ناموفق شد (مثلاً محدودیت نرخ یا قطع ارتباط) + for (const c of chunk) { + failed.value++ + errors.value.push({ code: c.code, name: c.name, error: 'خطا در ارتباط با سرور' }) + } } - processed.value++ + processed.value += chunk.length + // مکث کوتاه بین دسته‌ها برای جلوگیری از محدودیت نرخ سرور + if (processed.value < queue.value.length) await sleep(250) } status.value = 'done'