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';
const LIST_ALL_CHUNK = 2000;
const LIST_ALL_MAX_CODE = 50_000;
const LIST_ALL_EMPTY_STREAK = 3;
/** سقف «همه» در یک درخواست ByCode — تکه‌تکه باعث ۵۰۲ پروکسی می‌شود */
const LIST_ALL_MAX_CODE = 5_000;
const LIST_ALL_TIMEOUT_MS = 55_000;
function asMoshtariList(raw: unknown): Array<Record<string, unknown>> {
if (!raw) return [];
@@ -316,21 +317,26 @@ export class TahesabPersonLinkService {
return this.linkDto(link);
}
/**
* لیست همهٔ مشتریان در یک درخواست (بازه ۱…LIST_ALL_MAX_CODE).
* تکه‌تکه کردن پشت‌سرهم معمولاً از مهلت nginx بیشتر می‌شود و ۵۰۲ می‌دهد.
*/
private async listAllMoshtari(api: TahesabApi) {
const merged: Array<Record<string, unknown>> = [];
let emptyStreak = 0;
for (let from = 1; from <= LIST_ALL_MAX_CODE; from += LIST_ALL_CHUNK) {
const to = Math.min(from + LIST_ALL_CHUNK - 1, LIST_ALL_MAX_CODE);
const rows = asMoshtariList(await api.doListMoshtariByCode(from, to));
if (!rows.length) {
emptyStreak += 1;
if (emptyStreak >= LIST_ALL_EMPTY_STREAK) break;
continue;
}
emptyStreak = 0;
merged.push(...rows);
try {
return asMoshtariList(
await api.doListMoshtariByCode(1, LIST_ALL_MAX_CODE, {
timeoutMs: LIST_ALL_TIMEOUT_MS,
}),
);
} catch (err) {
// اگر بازهٔ بزرگ سمت ته‌حساب رد شد، همان سقف دستی ۲۰۰۰ را یک‌بار امتحان کن
if (LIST_ALL_MAX_CODE <= LIST_ALL_CHUNK) throw err;
return asMoshtariList(
await api.doListMoshtariByCode(1, LIST_ALL_CHUNK, {
timeoutMs: LIST_ALL_TIMEOUT_MS,
}),
);
}
return merged;
}
async search(organizationId: string, dto: SearchTahesabDto) {
@@ -366,7 +372,8 @@ export class TahesabPersonLinkService {
moshtariCode: pickCode(row),
name: pickName(row),
tel: pickTel(row),
raw: row,
// raw فقط برای جستجوی محدود؛ در listAll حجم پاسخ را باد می‌کند و پروکسی را می‌ترکاند
...(dto.listAll ? {} : { raw: row }),
}));
const codes = mapped
@@ -145,6 +145,7 @@ export class TahesabApi {
bodyKey?: string,
args?: unknown[],
method?: 'GET' | 'POST',
timeoutMs?: number,
) {
return this.http.call<T>({
...this.creds,
@@ -152,6 +153,7 @@ export class TahesabApi {
bodyKey,
args,
method,
timeoutMs,
});
}
@@ -182,12 +184,18 @@ export class TahesabApi {
return this.call('DoEditMoshtari', 'DoEditMoshtari', args);
}
doListMoshtariByCode(fromCode: number, toCode: number) {
doListMoshtariByCode(
fromCode: number,
toCode: number,
opts?: { timeoutMs?: number },
) {
// OpenAPI/Postman: کلید body برای ByCode دقیقاً «Dolistmoshtari» است (حساس به حروف)
return this.call(
'DoListMoshtari-ByCode',
'Dolistmoshtari',
[fromCode, toCode],
'POST',
opts?.timeoutMs,
);
}