Allow large activity Excel imports (40k+ rows) with bigger chunks and caches.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-03 17:42:11 +03:30
co-authored by Cursor
parent beae16437b
commit c629729a9a
4 changed files with 96 additions and 22 deletions
@@ -18,6 +18,8 @@ import {
} from '../common/excel';
import {
chunkImportMeta,
destroyExcelImportSession,
getExcelImportSessionCaches,
resolveExcelImportChunk,
} from '../common/excel-import-session';
@@ -184,22 +186,49 @@ export class ActivitiesExcelService {
return result;
}
const MAX_ROWS = 25000;
const MAX_ROWS = 100_000;
if (ctx.total > MAX_ROWS) {
destroyExcelImportSession(ctx.sessionId);
result.errors.push({
row: 1,
message: `فایل بیش از ${MAX_ROWS} ردیف دارد.`,
message: `فایل بیش از ${MAX_ROWS} ردیف دارد. فایل را تقسیم کنید.`,
});
return result;
return { ...result, hasMore: false, total: ctx.total, processed: 0 };
}
const userCache = new Map<string, string | null>();
const caches = getExcelImportSessionCaches(ctx.sessionId, actor.organizationId);
const resolveUser = async (name?: string) => {
const key = (name ?? '').trim().toLowerCase();
if (!key) return null;
if (userCache.has(key)) return userCache.get(key) ?? null;
if (caches.user.has(key)) return caches.user.get(key) ?? null;
const id = await this.findUserId(actor.organizationId, name);
userCache.set(key, id);
caches.user.set(key, id);
return id;
};
const resolvePerson = async (row: Record<string, string>) => {
const mobile = normalizePhone(row.personMobile);
const name = (row.personName ?? '').trim().toLowerCase();
const key = mobile ? `m:${mobile}` : name ? `n:${name}` : '';
if (!key) return null;
if (caches.person.has(key)) return caches.person.get(key) ?? null;
const id = await this.findPersonId(actor.organizationId, row);
caches.person.set(key, id);
return id;
};
const resolveCompany = async (name?: string) => {
const key = (name ?? '').trim().toLowerCase();
if (!key) return null;
if (caches.company.has(key)) return caches.company.get(key) ?? null;
const id = await this.findCompanyId(actor.organizationId, name);
caches.company.set(key, id);
return id;
};
const resolveDeal = async (title?: string) => {
const key = (title ?? '').trim().toLowerCase();
if (!key) return null;
if (caches.deal.has(key)) return caches.deal.get(key) ?? null;
const id = await this.findDealId(actor.organizationId, title);
caches.deal.set(key, id);
return id;
};
@@ -223,9 +252,9 @@ export class ActivitiesExcelService {
const status = parseStatus(row.status);
try {
const personId = await this.findPersonId(actor.organizationId, row);
const companyId = await this.findCompanyId(actor.organizationId, row.companyName);
const dealId = await this.findDealId(actor.organizationId, row.dealTitle);
const personId = await resolvePerson(row);
const companyId = await resolveCompany(row.companyName);
const dealId = await resolveDeal(row.dealTitle);
const assigneeNames = String(row.assignee ?? '')
.split(/[,،]/)
.map((s) => s.trim())