/** * Query: GetUserRatingsSummaryQuery * * Fast read query for user rating summary. * Combines platform snapshots and external game ratings. */ import { RatingSummaryDto } from '../dtos/RatingSummaryDto'; import { IUserRatingRepository } from '../../domain/repositories/IUserRatingRepository'; import { IExternalGameRatingRepository } from '../../domain/repositories/IExternalGameRatingRepository'; import { IRatingEventRepository } from '../../domain/repositories/IRatingEventRepository'; export interface GetUserRatingsSummaryQuery { userId: string; } export class GetUserRatingsSummaryQueryHandler { constructor( private readonly userRatingRepo: IUserRatingRepository, private readonly externalRatingRepo: IExternalGameRatingRepository, private readonly ratingEventRepo: IRatingEventRepository ) {} async execute(query: GetUserRatingsSummaryQuery): Promise { const { userId } = query; // Fetch platform rating snapshot const userRating = await this.userRatingRepo.findByUserId(userId); // Fetch all external game ratings const externalProfiles = await this.externalRatingRepo.findByUserId(userId); // Get last event timestamp if available let lastRatingEventAt: string | undefined; if (userRating) { // Get all events to find the most recent one const events = await this.ratingEventRepo.getAllByUserId(userId); if (events.length > 0) { const lastEvent = events[events.length - 1]; if (lastEvent) { lastRatingEventAt = lastEvent.occurredAt.toISOString(); } } } // Build platform rating dimensions const platform = { driving: { value: userRating?.driver.value || 0, confidence: userRating?.driver.confidence || 0, sampleSize: userRating?.driver.sampleSize || 0, trend: userRating?.driver.trend || 'stable', lastUpdated: userRating?.driver.lastUpdated?.toISOString() || new Date(0).toISOString(), }, admin: { value: userRating?.admin.value || 0, confidence: userRating?.admin.confidence || 0, sampleSize: userRating?.admin.sampleSize || 0, trend: userRating?.admin.trend || 'stable', lastUpdated: userRating?.admin.lastUpdated?.toISOString() || new Date(0).toISOString(), }, steward: { value: userRating?.steward.value || 0, confidence: userRating?.steward.confidence || 0, sampleSize: userRating?.steward.sampleSize || 0, trend: userRating?.steward.trend || 'stable', lastUpdated: userRating?.steward.lastUpdated?.toISOString() || new Date(0).toISOString(), }, trust: { value: userRating?.trust.value || 0, confidence: userRating?.trust.confidence || 0, sampleSize: userRating?.trust.sampleSize || 0, trend: userRating?.trust.trend || 'stable', lastUpdated: userRating?.trust.lastUpdated?.toISOString() || new Date(0).toISOString(), }, fairness: { value: userRating?.fairness.value || 0, confidence: userRating?.fairness.confidence || 0, sampleSize: userRating?.fairness.sampleSize || 0, trend: userRating?.fairness.trend || 'stable', lastUpdated: userRating?.fairness.lastUpdated?.toISOString() || new Date(0).toISOString(), }, overallReputation: userRating?.overallReputation || 0, }; // Build external ratings map const external: { [gameKey: string]: { [type: string]: number } } = {}; for (const profile of externalProfiles) { const gameKey = profile.gameKey.toString(); external[gameKey] = {}; // Convert Map to array and iterate const ratingsArray = Array.from(profile.ratings.entries()); for (const [type, rating] of ratingsArray) { external[gameKey][type] = rating.value; } } // Get timestamps const createdAt = userRating?.createdAt?.toISOString() || new Date().toISOString(); const updatedAt = userRating?.updatedAt?.toISOString() || new Date().toISOString(); const result: RatingSummaryDto = { userId, platform, external, createdAt, updatedAt, }; if (lastRatingEventAt) { result.lastRatingEventAt = lastRatingEventAt; } return result; } }