import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { BaseTenantRepository, TenantContextService } from '@iconsulting/shared'; import { IUserContactRepository } from '../../../domain/repositories/user-contact.repository.interface'; import { UserContactEntity, ContactType, NotificationType, } from '../../../domain/entities/user-contact.entity'; import { UserContactORM } from '../../../infrastructure/database/postgres/entities/user-contact.orm'; @Injectable() export class UserContactPostgresRepository extends BaseTenantRepository implements IUserContactRepository { constructor( @InjectRepository(UserContactORM) repo: Repository, tenantContext: TenantContextService, ) { super(repo, tenantContext); } async save(contact: UserContactEntity): Promise { const orm = this.toORM(contact); orm.tenantId = this.getTenantId(); await this.repo.save(orm); } async findById(id: string): Promise { const orm = await this.findOneWithTenant({ id } as any); return orm ? this.toEntity(orm) : null; } async findByUserIdAndType(userId: string, type: ContactType): Promise { const orm = await this.findOneWithTenant({ userId, type } as any); return orm ? this.toEntity(orm) : null; } async findByUserId(userId: string): Promise { const orms = await this.repo.find({ where: { userId, tenantId: this.getTenantId() } as any, order: { createdAt: 'ASC' }, }); return orms.map(orm => this.toEntity(orm)); } async findVerifiedByUserId(userId: string): Promise { const orms = await this.repo.find({ where: { userId, tenantId: this.getTenantId(), isVerified: true } as any, order: { createdAt: 'ASC' }, }); return orms.map(orm => this.toEntity(orm)); } async update(contact: UserContactEntity): Promise { const orm = this.toORM(contact); orm.tenantId = this.getTenantId(); await this.repo.save(orm); } async delete(id: string): Promise { await this.repo.delete({ id, tenantId: this.getTenantId() } as any); } async findByNotificationType( notificationType: NotificationType, options?: { limit?: number; offset?: number }, ): Promise { const query = this.createTenantQueryBuilder('contact') .andWhere('contact.notification_enabled = true') .andWhere('contact.is_verified = true') .andWhere(':type = ANY(contact.enabled_notification_types)', { type: notificationType }) .orderBy('contact.created_at', 'ASC'); if (options?.limit) { query.take(options.limit); } if (options?.offset) { query.skip(options.offset); } const orms = await query.getMany(); return orms.map(orm => this.toEntity(orm)); } async countVerifiedByType(): Promise> { const tenantId = this.getTenantId(); const result = await this.repo .createQueryBuilder('contact') .select('contact.type', 'type') .addSelect('COUNT(*)', 'count') .where('contact.tenant_id = :tenantId', { tenantId }) .andWhere('contact.is_verified = true') .groupBy('contact.type') .getRawMany(); const counts: Record = { [ContactType.EMAIL]: 0, [ContactType.WECHAT]: 0, [ContactType.WHATSAPP]: 0, [ContactType.TELEGRAM]: 0, [ContactType.LINE]: 0, }; for (const row of result) { counts[row.type as ContactType] = parseInt(row.count, 10); } return counts; } private toORM(entity: UserContactEntity): UserContactORM { const orm = new UserContactORM(); orm.id = entity.id; orm.tenantId = this.getTenantId(); orm.userId = entity.userId; orm.type = entity.type; orm.value = entity.value; orm.displayName = entity.displayName; orm.isVerified = entity.isVerified; orm.verifiedAt = entity.verifiedAt; orm.notificationEnabled = entity.notificationEnabled; orm.enabledNotificationTypes = entity.enabledNotificationTypes; orm.verificationCode = entity.verificationCode; orm.verificationExpiresAt = entity.verificationExpiresAt; orm.createdAt = entity.createdAt; orm.updatedAt = entity.updatedAt; return orm; } private toEntity(orm: UserContactORM): UserContactEntity { return UserContactEntity.fromPersistence({ id: orm.id, userId: orm.userId, type: orm.type, value: orm.value, displayName: orm.displayName, isVerified: orm.isVerified, verifiedAt: orm.verifiedAt, notificationEnabled: orm.notificationEnabled, enabledNotificationTypes: orm.enabledNotificationTypes, verificationCode: orm.verificationCode, verificationExpiresAt: orm.verificationExpiresAt, createdAt: orm.createdAt, updatedAt: orm.updatedAt, }); } }