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

166 lines
5.7 KiB
TypeScript

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<ConversationEntity, ConversationORM>
implements IConversationRepository
{
constructor(
@InjectRepository(ConversationORM) repo: Repository<ConversationORM>,
tenantContext: TenantContextService,
) {
super(repo, tenantContext);
}
async save(conversation: ConversationEntity): Promise<ConversationEntity> {
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<ConversationEntity | null> {
const orm = await this.findOneWithTenant({ id } as any);
return orm ? this.toEntity(orm) : null;
}
async findByUserId(
userId: string,
options?: { status?: ConversationStatusType; limit?: number },
): Promise<ConversationEntity[]> {
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<ConversationEntity[]> {
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<ConversationEntity> {
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<number> {
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,
});
}
}