Files
gridpilot.gg/core/identity/application/queries/GetUserRatingsSummaryQuery.ts
2026-01-16 19:46:49 +01:00

119 lines
4.2 KiB
TypeScript

/**
* Query: GetUserRatingsSummaryQuery
*
* Fast read query for user rating summary.
* Combines platform snapshots and external game ratings.
*/
import { ExternalGameRatingRepository } from '../../domain/repositories/ExternalGameRatingRepository';
import { RatingEventRepository } from '../../domain/repositories/RatingEventRepository';
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
import { RatingSummaryDto } from '../dtos/RatingSummaryDto';
export interface GetUserRatingsSummaryQuery {
userId: string;
}
export class GetUserRatingsSummaryQueryHandler {
constructor(
private readonly userRatingRepo: UserRatingRepository,
private readonly externalRatingRepo: ExternalGameRatingRepository,
private readonly ratingEventRepo: RatingEventRepository
) {}
async execute(query: GetUserRatingsSummaryQuery): Promise<RatingSummaryDto> {
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;
}
}