96 lines
3.1 KiB
TypeScript
96 lines
3.1 KiB
TypeScript
/**
|
|
* Get Global Leaderboards Use Case
|
|
*
|
|
* Orchestrates the retrieval of global leaderboards data.
|
|
* Aggregates data from repositories and returns top drivers and teams.
|
|
*/
|
|
|
|
import { LeaderboardsRepository } from '../ports/LeaderboardsRepository';
|
|
import { LeaderboardsEventPublisher } from '../ports/LeaderboardsEventPublisher';
|
|
import {
|
|
GlobalLeaderboardsQuery,
|
|
GlobalLeaderboardsResult,
|
|
GlobalLeaderboardDriverEntry,
|
|
GlobalLeaderboardTeamEntry,
|
|
} from '../ports/GlobalLeaderboardsQuery';
|
|
|
|
export interface GetGlobalLeaderboardsUseCasePorts {
|
|
leaderboardsRepository: LeaderboardsRepository;
|
|
eventPublisher: LeaderboardsEventPublisher;
|
|
}
|
|
|
|
export class GetGlobalLeaderboardsUseCase {
|
|
constructor(private readonly ports: GetGlobalLeaderboardsUseCasePorts) {}
|
|
|
|
async execute(query: GlobalLeaderboardsQuery = {}): Promise<GlobalLeaderboardsResult> {
|
|
try {
|
|
const driverLimit = query.driverLimit ?? 10;
|
|
const teamLimit = query.teamLimit ?? 10;
|
|
|
|
// Fetch all drivers and teams in parallel
|
|
const [allDrivers, allTeams] = await Promise.all([
|
|
this.ports.leaderboardsRepository.findAllDrivers(),
|
|
this.ports.leaderboardsRepository.findAllTeams(),
|
|
]);
|
|
|
|
// Sort drivers by rating (highest first) and take top N
|
|
const topDrivers = allDrivers
|
|
.sort((a, b) => {
|
|
const ratingComparison = b.rating - a.rating;
|
|
if (ratingComparison === 0) {
|
|
return a.name.localeCompare(b.name);
|
|
}
|
|
return ratingComparison;
|
|
})
|
|
.slice(0, driverLimit)
|
|
.map((driver, index): GlobalLeaderboardDriverEntry => ({
|
|
rank: index + 1,
|
|
id: driver.id,
|
|
name: driver.name,
|
|
rating: driver.rating,
|
|
...(driver.teamId !== undefined && { teamId: driver.teamId }),
|
|
...(driver.teamName !== undefined && { teamName: driver.teamName }),
|
|
raceCount: driver.raceCount,
|
|
}));
|
|
|
|
// Sort teams by rating (highest first) and take top N
|
|
const topTeams = allTeams
|
|
.sort((a, b) => {
|
|
const ratingComparison = b.rating - a.rating;
|
|
if (ratingComparison === 0) {
|
|
return a.name.localeCompare(b.name);
|
|
}
|
|
return ratingComparison;
|
|
})
|
|
.slice(0, teamLimit)
|
|
.map((team, index): GlobalLeaderboardTeamEntry => ({
|
|
rank: index + 1,
|
|
id: team.id,
|
|
name: team.name,
|
|
rating: team.rating,
|
|
memberCount: team.memberCount,
|
|
raceCount: team.raceCount,
|
|
}));
|
|
|
|
// Publish event
|
|
await this.ports.eventPublisher.publishGlobalLeaderboardsAccessed({
|
|
type: 'global_leaderboards_accessed',
|
|
timestamp: new Date(),
|
|
});
|
|
|
|
return {
|
|
drivers: topDrivers,
|
|
teams: topTeams,
|
|
};
|
|
} catch (error) {
|
|
// Publish error event
|
|
await this.ports.eventPublisher.publishLeaderboardsError({
|
|
type: 'leaderboards_error',
|
|
error: error instanceof Error ? error.message : String(error),
|
|
timestamp: new Date(),
|
|
});
|
|
throw error;
|
|
}
|
|
}
|
|
}
|