From 65a34b97e2026d2c55effbe6f84c04457a38385a Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Thu, 18 Jun 2026 23:01:44 +0330 Subject: [PATCH] optimize performance --- src/modules/users/providers/user.service.ts | 155 ++++++++++++-------- 1 file changed, 95 insertions(+), 60 deletions(-) diff --git a/src/modules/users/providers/user.service.ts b/src/modules/users/providers/user.service.ts index 84a4c4a..ee878c7 100644 --- a/src/modules/users/providers/user.service.ts +++ b/src/modules/users/providers/user.service.ts @@ -445,73 +445,108 @@ export class UserService { errors: [], }; + const validRows: Array<{ row: (typeof rows)[number]; phone: string }> = []; + for (const row of rows) { - try { - if (!row.phone) { - result.errors.push({ - row: row.rowNumber, - message: UserImportMessage.PHONE_REQUIRED, - }); - continue; - } + if (!row.phone) { + result.errors.push({ + row: row.rowNumber, + message: UserImportMessage.PHONE_REQUIRED, + }); + continue; + } - const normalizedPhone = normalizePhone(row.phone); - if (!normalizedPhone) { - result.errors.push({ - row: row.rowNumber, - phone: row.phone, - message: UserImportMessage.INVALID_PHONE, - }); - continue; - } - - let user = await this.userRepository.findOne({ phone: normalizedPhone }); - - if (!user) { - const createData = { - phone: normalizedPhone, - firstName: row.firstName?.trim() || '[نام]', - lastName: row.lastName?.trim() || undefined, - gender: row.gender, - birthDate: undefined, - marriageDate: undefined, - } as unknown as RequiredEntityData; - - user = this.userRepository.create(createData); - await this.em.persistAndFlush(user); - result.usersCreated++; - } else { - const updates: Partial = {}; - if (row.firstName?.trim()) { - updates.firstName = row.firstName.trim(); - } - if (row.lastName?.trim()) { - updates.lastName = row.lastName.trim(); - } - if (row.gender !== undefined) { - updates.gender = row.gender; - } - if (Object.keys(updates).length > 0) { - this.em.assign(user, updates); - await this.em.flush(); - } - } - - const existingLink = await this.userRestaurantRepository.findOne({ user, restaurant }); - if (existingLink) { - result.usersAlreadyLinked++; - continue; - } - - await this.addUserToRestaurant(user, restaurant); - result.usersLinked++; - } catch (error) { + const normalizedPhone = normalizePhone(row.phone); + if (!normalizedPhone) { result.errors.push({ row: row.rowNumber, phone: row.phone, - message: error instanceof Error ? error.message : 'Unknown error', + message: UserImportMessage.INVALID_PHONE, }); + continue; } + + validRows.push({ row, phone: normalizedPhone }); + } + + if (validRows.length === 0) { + return result; + } + + const phones = [...new Set(validRows.map(entry => entry.phone))]; + const existingUsers = await this.userRepository.find({ phone: { $in: phones } }); + const userByPhone = new Map(existingUsers.map(user => [user.phone, user])); + + for (const { row, phone } of validRows) { + let user = userByPhone.get(phone); + + if (!user) { + const createData = { + phone, + firstName: row.firstName?.trim() || '[نام]', + lastName: row.lastName?.trim() || undefined, + gender: row.gender, + birthDate: undefined, + marriageDate: undefined, + } as unknown as RequiredEntityData; + + user = this.userRepository.create(createData); + userByPhone.set(phone, user); + result.usersCreated++; + } else { + const updates: Partial = {}; + if (row.firstName?.trim()) { + updates.firstName = row.firstName.trim(); + } + if (row.lastName?.trim()) { + updates.lastName = row.lastName.trim(); + } + if (row.gender !== undefined) { + updates.gender = row.gender; + } + if (Object.keys(updates).length > 0) { + this.em.assign(user, updates); + } + } + } + + const userIds = [...userByPhone.values()].map(user => user.id); + const existingLinks = await this.userRestaurantRepository.find( + { restaurant, user: { $in: userIds } }, + { populate: ['user'] }, + ); + const linkedUserIds = new Set(existingLinks.map(link => link.user.id)); + const linkedPhonesInBatch = new Set(); + + for (const { phone } of validRows) { + const user = userByPhone.get(phone); + if (!user) { + continue; + } + + if (linkedUserIds.has(user.id) || linkedPhonesInBatch.has(phone)) { + result.usersAlreadyLinked++; + continue; + } + + this.userRestaurantRepository.create({ + user, + restaurant, + orderCount: 0, + totalOrderAmount: 0, + }); + linkedUserIds.add(user.id); + linkedPhonesInBatch.add(phone); + result.usersLinked++; + } + + try { + await this.em.flush(); + } catch (error) { + result.errors.push({ + row: 0, + message: error instanceof Error ? error.message : 'Unknown error', + }); } return result;