import { BadRequestException, Injectable, Logger } from '@nestjs/common'; import { FindOptionsWhere } from 'typeorm'; import { Transactional } from 'typeorm-transactional'; import { Roles } from '~/auth/enums'; import { PageOptionsRequestDto } from '~/core/dtos'; import { CustomerService } from '~/customer/services'; import { DocumentService, OciService } from '~/document/services'; import { User } from '~/user/entities'; import { UserType } from '~/user/enums'; import { UserService } from '~/user/services'; import { UserTokenService } from '~/user/services/user-token.service'; import { CreateJuniorRequestDto, SetThemeRequestDto } from '../dtos/request'; import { Junior } from '../entities'; import { JuniorRepository } from '../repositories'; import { QrcodeService } from './qrcode.service'; @Injectable() export class JuniorService { private readonly logger = new Logger(JuniorService.name); constructor( private readonly juniorRepository: JuniorRepository, private readonly userService: UserService, private readonly userTokenService: UserTokenService, private readonly customerService: CustomerService, private readonly documentService: DocumentService, private readonly ociService: OciService, private readonly qrCodeService: QrcodeService, ) {} @Transactional() async createJuniors(body: CreateJuniorRequestDto, guardianId: string) { this.logger.log(`Creating junior for guardian ${guardianId}`); const searchConditions: FindOptionsWhere[] = [{ email: body.email }]; if (body.phoneNumber && body.countryCode) { searchConditions.push({ phoneNumber: body.phoneNumber, countryCode: body.countryCode, }); } const existingUser = await this.userService.findUser(searchConditions); if (existingUser) { this.logger.error(`User with email ${body.email} or phone number ${body.phoneNumber} already exists`); throw new BadRequestException('USER.ALREADY_EXISTS'); } const user = await this.userService.createUser({ email: body.email, countryCode: body.countryCode, phoneNumber: body.phoneNumber, roles: [Roles.JUNIOR], }); const customer = await this.customerService.createJuniorCustomer(guardianId, user.id, body); await this.juniorRepository.createJunior(user.id, { guardianId, relationship: body.relationship, customerId: customer.id, }); this.logger.log(`Junior ${user.id} created successfully`); return this.generateToken(user.id); } async findJuniorById(juniorId: string, withGuardianRelation = false, guardianId?: string) { this.logger.log(`Finding junior ${juniorId}`); const junior = await this.juniorRepository.findJuniorById(juniorId, withGuardianRelation, guardianId); if (!junior) { this.logger.error(`Junior ${juniorId} not found`); throw new BadRequestException('JUNIOR.NOT_FOUND'); } this.logger.log(`Junior ${juniorId} found successfully`); return junior; } @Transactional() async setTheme(body: SetThemeRequestDto, juniorId: string) { this.logger.log(`Setting theme for junior ${juniorId}`); const document = await this.documentService.findDocumentById(body.avatarId); if (!document || document.createdById !== juniorId) { this.logger.error(`Document ${body.avatarId} not found or not created by junior ${juniorId}`); throw new BadRequestException('DOCUMENT.NOT_FOUND'); } const junior = await this.findJuniorById(juniorId); if (junior.theme) { this.logger.log(`Removing existing theme for junior ${juniorId}`); await this.juniorRepository.removeTheme(junior.theme); } await this.juniorRepository.setTheme(body, junior); this.logger.log(`Theme set for junior ${juniorId}`); return this.juniorRepository.findThemeForJunior(juniorId); } async findJuniorsByGuardianId(guardianId: string, pageOptions: PageOptionsRequestDto): Promise<[Junior[], number]> { this.logger.log(`Finding juniors for guardian ${guardianId}`); const [juniors, itemCount] = await this.juniorRepository.findJuniorsByGuardianId(guardianId, pageOptions); this.logger.log(`Juniors found for guardian ${guardianId}`); await this.prepareJuniorImages(juniors); return [juniors, itemCount]; } async validateToken(token: string) { this.logger.log(`Validating token ${token}`); const juniorId = await this.userTokenService.validateToken(token, UserType.JUNIOR); return this.findJuniorById(juniorId!, true); } async generateToken(juniorId: string) { this.logger.log(`Generating token for junior ${juniorId}`); const token = await this.userTokenService.generateToken(juniorId); return this.qrCodeService.generateQrCode(token); } async doesJuniorBelongToGuardian(guardianId: string, juniorId: string) { this.logger.log(`Checking if junior ${juniorId} belongs to guardian ${guardianId}`); const junior = await this.findJuniorById(juniorId, false, guardianId); return !!junior; } private async prepareJuniorImages(juniors: Junior[]) { this.logger.log(`Preparing junior images`); await Promise.all( juniors.map(async (junior) => { const profilePicture = junior.customer.profilePicture; if (profilePicture) { profilePicture.url = await this.ociService.generatePreSignedUrl(profilePicture); } }), ); } }