import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { BaseTenantRepository, TenantContextService } from '@iconsulting/shared'; import { ConversationORM } from '../../../infrastructure/database/postgres/entities/conversation.orm'; import { IConversationRepository } from '../../../domain/repositories/conversation.repository.interface'; import { ConversationEntity, ConversationStatusType, } from '../../../domain/entities/conversation.entity'; @Injectable() export class ConversationPostgresRepository extends BaseTenantRepository implements IConversationRepository { constructor( @InjectRepository(ConversationORM) repo: Repository, tenantContext: TenantContextService, ) { super(repo, tenantContext); } async save(conversation: ConversationEntity): Promise { const orm = this.toORM(conversation); orm.tenantId = this.getTenantId(); const saved = await this.repo.save(orm); return this.toEntity(saved); } async findById(id: string): Promise { const orm = await this.findOneWithTenant({ id } as any); return orm ? this.toEntity(orm) : null; } async findByUserId( userId: string, options?: { status?: ConversationStatusType; limit?: number }, ): Promise { const queryBuilder = this.createTenantQueryBuilder('conversation') .andWhere('conversation.user_id = :userId', { userId }) .andWhere('conversation.status != :deletedStatus', { deletedStatus: 'DELETED' }); if (options?.status) { queryBuilder.andWhere('conversation.status = :status', { status: options.status }); } queryBuilder.orderBy('conversation.created_at', 'DESC'); if (options?.limit) { queryBuilder.limit(options.limit); } const orms = await queryBuilder.getMany(); return orms.map((orm) => this.toEntity(orm)); } async findForEvolution(options: { status?: ConversationStatusType; hoursBack?: number; minMessageCount?: number; }): Promise { const queryBuilder = this.createTenantQueryBuilder('conversation') .andWhere('conversation.status != :deletedStatus', { deletedStatus: 'DELETED' }); if (options.status) { queryBuilder.andWhere('conversation.status = :status', { status: options.status }); } if (options.hoursBack) { const cutoffDate = new Date(); cutoffDate.setHours(cutoffDate.getHours() - options.hoursBack); queryBuilder.andWhere('conversation.created_at >= :cutoffDate', { cutoffDate }); } if (options.minMessageCount) { queryBuilder.andWhere('conversation.message_count >= :minCount', { minCount: options.minMessageCount, }); } const orms = await queryBuilder.getMany(); return orms.map((orm) => this.toEntity(orm)); } async update(conversation: ConversationEntity): Promise { const orm = this.toORM(conversation); orm.tenantId = this.getTenantId(); const updated = await this.repo.save(orm); return this.toEntity(updated); } async count(options?: { status?: ConversationStatusType; daysBack?: number }): Promise { const queryBuilder = this.createTenantQueryBuilder('conversation'); if (options?.status) { queryBuilder.andWhere('conversation.status = :status', { status: options.status }); } if (options?.daysBack) { const cutoffDate = new Date(); cutoffDate.setDate(cutoffDate.getDate() - options.daysBack); queryBuilder.andWhere('conversation.created_at >= :cutoffDate', { cutoffDate }); } return queryBuilder.getCount(); } private toORM(entity: ConversationEntity): ConversationORM { const orm = new ConversationORM(); orm.id = entity.id; orm.tenantId = this.getTenantId(); orm.userId = entity.userId; orm.status = entity.status; orm.title = entity.title; orm.summary = entity.summary; orm.category = entity.category; orm.messageCount = entity.messageCount; orm.userMessageCount = entity.userMessageCount; orm.assistantMessageCount = entity.assistantMessageCount; orm.totalInputTokens = entity.totalInputTokens; orm.totalOutputTokens = entity.totalOutputTokens; orm.rating = entity.rating; orm.feedback = entity.feedback; orm.hasConverted = entity.hasConverted; orm.consultingStage = entity.consultingStage; orm.consultingState = entity.consultingState; orm.collectedInfo = entity.collectedInfo; orm.recommendedPrograms = entity.recommendedPrograms; orm.conversionPath = entity.conversionPath; orm.deviceInfo = entity.deviceInfo; orm.createdAt = entity.createdAt; orm.updatedAt = entity.updatedAt; orm.endedAt = entity.endedAt; return orm; } private toEntity(orm: ConversationORM): ConversationEntity { return ConversationEntity.fromPersistence({ id: orm.id, userId: orm.userId, status: orm.status, title: orm.title, summary: orm.summary, category: orm.category, messageCount: orm.messageCount, userMessageCount: orm.userMessageCount, assistantMessageCount: orm.assistantMessageCount, totalInputTokens: orm.totalInputTokens, totalOutputTokens: orm.totalOutputTokens, rating: orm.rating, feedback: orm.feedback, hasConverted: orm.hasConverted, consultingStage: orm.consultingStage, consultingState: orm.consultingState, collectedInfo: orm.collectedInfo, recommendedPrograms: orm.recommendedPrograms, conversionPath: orm.conversionPath, deviceInfo: orm.deviceInfo, createdAt: orm.createdAt, updatedAt: orm.updatedAt, endedAt: orm.endedAt, }); } }