iconsulting/packages/services/user-service/src/adapters/outbound/persistence/user-contact-postgres.repos...

152 lines
4.9 KiB
TypeScript

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<UserContactEntity, UserContactORM>
implements IUserContactRepository
{
constructor(
@InjectRepository(UserContactORM) repo: Repository<UserContactORM>,
tenantContext: TenantContextService,
) {
super(repo, tenantContext);
}
async save(contact: UserContactEntity): Promise<void> {
const orm = this.toORM(contact);
orm.tenantId = this.getTenantId();
await this.repo.save(orm);
}
async findById(id: string): Promise<UserContactEntity | null> {
const orm = await this.findOneWithTenant({ id } as any);
return orm ? this.toEntity(orm) : null;
}
async findByUserIdAndType(userId: string, type: ContactType): Promise<UserContactEntity | null> {
const orm = await this.findOneWithTenant({ userId, type } as any);
return orm ? this.toEntity(orm) : null;
}
async findByUserId(userId: string): Promise<UserContactEntity[]> {
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<UserContactEntity[]> {
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<void> {
const orm = this.toORM(contact);
orm.tenantId = this.getTenantId();
await this.repo.save(orm);
}
async delete(id: string): Promise<void> {
await this.repo.delete({ id, tenantId: this.getTenantId() } as any);
}
async findByNotificationType(
notificationType: NotificationType,
options?: { limit?: number; offset?: number },
): Promise<UserContactEntity[]> {
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<Record<ContactType, number>> {
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, number> = {
[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,
});
}
}