Fix Tahesab empty-range 502 by using a single ByCode call.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-05 10:41:12 +03:30
co-authored by Cursor
parent 38f4b75a78
commit 43526cb980
3 changed files with 35 additions and 19 deletions
@@ -18,8 +18,9 @@ import {
import { normalizeIranMobile, SanadBuilders, type TahesabApi } from './tahesab-methods'; import { normalizeIranMobile, SanadBuilders, type TahesabApi } from './tahesab-methods';
const LIST_ALL_CHUNK = 2000; const LIST_ALL_CHUNK = 2000;
const LIST_ALL_MAX_CODE = 50_000; /** سقف «همه» در یک درخواست ByCode — تکه‌تکه باعث ۵۰۲ پروکسی می‌شود */
const LIST_ALL_EMPTY_STREAK = 3; const LIST_ALL_MAX_CODE = 5_000;
const LIST_ALL_TIMEOUT_MS = 55_000;
function asMoshtariList(raw: unknown): Array<Record<string, unknown>> { function asMoshtariList(raw: unknown): Array<Record<string, unknown>> {
if (!raw) return []; if (!raw) return [];
@@ -316,21 +317,26 @@ export class TahesabPersonLinkService {
return this.linkDto(link); return this.linkDto(link);
} }
/**
* لیست همهٔ مشتریان در یک درخواست (بازه ۱…LIST_ALL_MAX_CODE).
* تکه‌تکه کردن پشت‌سرهم معمولاً از مهلت nginx بیشتر می‌شود و ۵۰۲ می‌دهد.
*/
private async listAllMoshtari(api: TahesabApi) { private async listAllMoshtari(api: TahesabApi) {
const merged: Array<Record<string, unknown>> = []; try {
let emptyStreak = 0; return asMoshtariList(
for (let from = 1; from <= LIST_ALL_MAX_CODE; from += LIST_ALL_CHUNK) { await api.doListMoshtariByCode(1, LIST_ALL_MAX_CODE, {
const to = Math.min(from + LIST_ALL_CHUNK - 1, LIST_ALL_MAX_CODE); timeoutMs: LIST_ALL_TIMEOUT_MS,
const rows = asMoshtariList(await api.doListMoshtariByCode(from, to)); }),
if (!rows.length) { );
emptyStreak += 1; } catch (err) {
if (emptyStreak >= LIST_ALL_EMPTY_STREAK) break; // اگر بازهٔ بزرگ سمت ته‌حساب رد شد، همان سقف دستی ۲۰۰۰ را یک‌بار امتحان کن
continue; if (LIST_ALL_MAX_CODE <= LIST_ALL_CHUNK) throw err;
} return asMoshtariList(
emptyStreak = 0; await api.doListMoshtariByCode(1, LIST_ALL_CHUNK, {
merged.push(...rows); timeoutMs: LIST_ALL_TIMEOUT_MS,
}),
);
} }
return merged;
} }
async search(organizationId: string, dto: SearchTahesabDto) { async search(organizationId: string, dto: SearchTahesabDto) {
@@ -366,7 +372,8 @@ export class TahesabPersonLinkService {
moshtariCode: pickCode(row), moshtariCode: pickCode(row),
name: pickName(row), name: pickName(row),
tel: pickTel(row), tel: pickTel(row),
raw: row, // raw فقط برای جستجوی محدود؛ در listAll حجم پاسخ را باد می‌کند و پروکسی را می‌ترکاند
...(dto.listAll ? {} : { raw: row }),
})); }));
const codes = mapped const codes = mapped
@@ -145,6 +145,7 @@ export class TahesabApi {
bodyKey?: string, bodyKey?: string,
args?: unknown[], args?: unknown[],
method?: 'GET' | 'POST', method?: 'GET' | 'POST',
timeoutMs?: number,
) { ) {
return this.http.call<T>({ return this.http.call<T>({
...this.creds, ...this.creds,
@@ -152,6 +153,7 @@ export class TahesabApi {
bodyKey, bodyKey,
args, args,
method, method,
timeoutMs,
}); });
} }
@@ -182,12 +184,18 @@ export class TahesabApi {
return this.call('DoEditMoshtari', 'DoEditMoshtari', args); return this.call('DoEditMoshtari', 'DoEditMoshtari', args);
} }
doListMoshtariByCode(fromCode: number, toCode: number) { doListMoshtariByCode(
fromCode: number,
toCode: number,
opts?: { timeoutMs?: number },
) {
// OpenAPI/Postman: کلید body برای ByCode دقیقاً «Dolistmoshtari» است (حساس به حروف) // OpenAPI/Postman: کلید body برای ByCode دقیقاً «Dolistmoshtari» است (حساس به حروف)
return this.call( return this.call(
'DoListMoshtari-ByCode', 'DoListMoshtari-ByCode',
'Dolistmoshtari', 'Dolistmoshtari',
[fromCode, toCode], [fromCode, toCode],
'POST',
opts?.timeoutMs,
); );
} }
+3 -2
View File
@@ -261,6 +261,7 @@ async function searchTahesab(mode: 'filter' | 'range' = 'filter') {
const { data } = await api.post<{ results: SyncResult[] }>( const { data } = await api.post<{ results: SyncResult[] }>(
'/integrations/tahesab/sync/search', '/integrations/tahesab/sync/search',
body, body,
{ timeout: 60_000 },
); );
syncResults.value = data.results ?? []; syncResults.value = data.results ?? [];
if (!syncResults.value.length) { if (!syncResults.value.length) {
@@ -502,8 +503,8 @@ onMounted(load);
<h3 class="subhead">۱) وارد کردن دسته‌ای با بازهٔ کد مشتری</h3> <h3 class="subhead">۱) وارد کردن دسته‌ای با بازهٔ کد مشتری</h3>
<p class="muted small"> <p class="muted small">
اگر «از» و «تا» را خالی بگذارید و نمایش بازه را بزنید، همهٔ مشتریان ته‌حساب لیست می‌شوند. اگر «از» و «تا» خالی باشند، مشتریان با کد ۱ تا ۵۰۰۰ از ته‌حساب لیست می‌شوند.
برای محدود کردن، بازه بگذارید (حداکثر ۲۰۰۰ کد در هر درخواست محدود). برای کدهای بالاتر بازه را دستی بگذارید (حداکثر ۲۰۰۰ کد در هر درخواست محدود).
</p> </p>
<div class="sync-grid range"> <div class="sync-grid range">
<input v-model="sync.fromCode" type="number" min="1" dir="ltr" placeholder="از کد (خالی = همه)" /> <input v-model="sync.fromCode" type="number" min="1" dir="ltr" placeholder="از کد (خالی = همه)" />