Fixing the error when importing a large Excel file

This commit is contained in:
2026-10-03 15:01:01 +03:30
parent f8313c0ea5
commit 2e315c9cfd
15 changed files with 503 additions and 86 deletions
@@ -78,11 +78,18 @@ export class CompaniesController {
@CurrentUser() user: Actor,
@UploadedFile() file?: Express.Multer.File,
@Body('mapping') mapping?: string,
@Body('sessionId') sessionId?: string,
@Body('offset') offset?: string,
@Body('limit') limit?: string,
) {
if (!file?.buffer?.length) {
if (!file?.buffer?.length && !sessionId) {
throw new BadRequestException('فایل ارسال نشده');
}
return this.excelService.importCompanies(user, file, mapping);
return this.excelService.importCompanies(user, file, mapping, {
sessionId,
offset,
limit,
});
}
@Get(':id')
+40 -12
View File
@@ -16,9 +16,19 @@ import {
writeXlsx,
type ExcelPreviewResult,
} from '../common/excel';
import {
chunkImportMeta,
resolveExcelImportChunk,
} from '../common/excel-import-session';
import { CompaniesService } from './companies.service';
import { PeopleService } from './people.service';
type ImportChunkOpts = {
sessionId?: string;
offset?: string | number;
limit?: string | number;
};
function parseMood(raw?: string): PersonMood | undefined {
const value = raw?.trim();
if (!value) return undefined;
@@ -363,16 +373,25 @@ export class ContactsExcelService {
async importPeople(
actor: Actor,
file: Express.Multer.File,
file: Express.Multer.File | undefined,
mappingRaw?: string,
chunk?: ImportChunkOpts,
): Promise<ImportContactsResult> {
const rows = await readMappedRows(file, PERSON_ALIASES, parseExcelMapping(mappingRaw));
const ctx = await resolveExcelImportChunk({
organizationId: actor.organizationId,
file,
sessionId: chunk?.sessionId,
offset: chunk?.offset,
limit: chunk?.limit,
loadRows: () =>
readMappedRows(file!, PERSON_ALIASES, parseExcelMapping(mappingRaw)),
});
const result: ImportContactsResult = { created: 0, skipped: 0, errors: [] };
const ownerCache = new Map<string, string | null>();
for (let i = 0; i < rows.length; i++) {
const row = rows[i]!;
const rowNum = i + 2;
for (let i = 0; i < ctx.slice.length; i++) {
const row = ctx.slice[i]!;
const rowNum = ctx.offset + i + 2;
if ((row._type || '').trim() && row._type.trim().toLowerCase() !== 'contact') {
continue;
}
@@ -476,20 +495,29 @@ export class ContactsExcelService {
}
}
return result;
return { ...result, ...chunkImportMeta(ctx) };
}
async importCompanies(
actor: Actor,
file: Express.Multer.File,
file: Express.Multer.File | undefined,
mappingRaw?: string,
chunk?: ImportChunkOpts,
): Promise<ImportContactsResult> {
const rows = await readMappedRows(file, COMPANY_ALIASES, parseExcelMapping(mappingRaw));
const ctx = await resolveExcelImportChunk({
organizationId: actor.organizationId,
file,
sessionId: chunk?.sessionId,
offset: chunk?.offset,
limit: chunk?.limit,
loadRows: () =>
readMappedRows(file!, COMPANY_ALIASES, parseExcelMapping(mappingRaw)),
});
const result: ImportContactsResult = { created: 0, skipped: 0, errors: [] };
for (let i = 0; i < rows.length; i++) {
const row = rows[i]!;
const rowNum = i + 2;
for (let i = 0; i < ctx.slice.length; i++) {
const row = ctx.slice[i]!;
const rowNum = ctx.offset + i + 2;
const name = row.name?.trim() ?? '';
if (!name) {
result.errors.push({ row: rowNum, message: 'نام شرکت الزامی است' });
@@ -518,7 +546,7 @@ export class ContactsExcelService {
}
}
return result;
return { ...result, ...chunkImportMeta(ctx) };
}
private async findOrCreateCompanyId(actor: Actor, name: string): Promise<string> {
+9 -2
View File
@@ -82,11 +82,18 @@ export class PeopleController {
@CurrentUser() user: Actor,
@UploadedFile() file?: Express.Multer.File,
@Body('mapping') mapping?: string,
@Body('sessionId') sessionId?: string,
@Body('offset') offset?: string,
@Body('limit') limit?: string,
) {
if (!file?.buffer?.length) {
if (!file?.buffer?.length && !sessionId) {
throw new BadRequestException('فایل ارسال نشده');
}
return this.excelService.importPeople(user, file, mapping);
return this.excelService.importPeople(user, file, mapping, {
sessionId,
offset,
limit,
});
}
@Get(':id')