refactor racing use cases

This commit is contained in:
2025-12-21 00:43:42 +01:00
parent e9d6f90bb2
commit c12656d671
308 changed files with 14401 additions and 7419 deletions

View File

@@ -1,11 +1,12 @@
import type { ITeamRepository } from '@core/racing/domain/repositories/ITeamRepository';
import type { ITeamMembershipRepository } from '@core/racing/domain/repositories/ITeamMembershipRepository';
import type { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository';
import type { TeamsLeaderboardOutputPort, SkillLevel } from '../ports/output/TeamsLeaderboardOutputPort';
import { SkillLevelService } from '@core/racing/domain/services/SkillLevelService';
import { SkillLevelService, type SkillLevel } from '@core/racing/domain/services/SkillLevelService';
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { AsyncUseCase , Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/application';
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { Team } from '@core/racing/domain/entities/Team';
interface DriverStatsAdapter {
rating: number | null;
@@ -13,36 +14,50 @@ interface DriverStatsAdapter {
totalRaces: number;
}
interface TeamLeaderboardItem {
id: string;
name: string;
export type GetTeamsLeaderboardInput = {
leagueId: string;
seasonId?: string;
};
export interface TeamLeaderboardItem {
team: Team;
memberCount: number;
rating: number | null;
totalWins: number;
totalRaces: number;
performanceLevel: string;
performanceLevel: SkillLevel;
isRecruiting: boolean;
createdAt: Date;
description: string;
}
export interface GetTeamsLeaderboardResult {
items: TeamLeaderboardItem[];
recruitingCount: number;
groupsBySkillLevel: Record<SkillLevel, TeamLeaderboardItem[]>;
topItems: TeamLeaderboardItem[];
}
export type GetTeamsLeaderboardErrorCode = 'LEAGUE_NOT_FOUND' | 'SEASON_NOT_FOUND' | 'REPOSITORY_ERROR';
/**
* Use case: GetTeamsLeaderboardUseCase
*/
export class GetTeamsLeaderboardUseCase implements AsyncUseCase<void, TeamsLeaderboardOutputPort, 'REPOSITORY_ERROR'>
{
export class GetTeamsLeaderboardUseCase {
constructor(
private readonly teamRepository: ITeamRepository,
private readonly teamMembershipRepository: ITeamMembershipRepository,
private readonly driverRepository: IDriverRepository,
private readonly getDriverStats: (driverId: string) => DriverStatsAdapter | null,
private readonly logger: Logger,
private readonly output: UseCaseOutputPort<GetTeamsLeaderboardResult>,
) {}
async execute(): Promise<Result<TeamsLeaderboardOutputPort, ApplicationErrorCode<'REPOSITORY_ERROR'>>> {
async execute(
_input: GetTeamsLeaderboardInput,
): Promise<Result<void, ApplicationErrorCode<GetTeamsLeaderboardErrorCode, { message: string }>>> {
try {
const allTeams = await this.teamRepository.findAll();
const teams: TeamLeaderboardItem[] = [];
const items: TeamLeaderboardItem[] = [];
await Promise.all(
allTeams.map(async (team) => {
@@ -70,9 +85,8 @@ export class GetTeamsLeaderboardUseCase implements AsyncUseCase<void, TeamsLeade
const averageRating = ratingCount > 0 ? ratingSum / ratingCount : null;
const performanceLevel = SkillLevelService.getTeamPerformanceLevel(averageRating);
teams.push({
id: team.id,
name: team.name,
items.push({
team,
memberCount,
rating: averageRating,
totalWins,
@@ -80,38 +94,46 @@ export class GetTeamsLeaderboardUseCase implements AsyncUseCase<void, TeamsLeade
performanceLevel,
isRecruiting: true,
createdAt: new Date(),
description: team.description,
});
})
}),
);
const recruitingCount = teams.filter((t) => t.isRecruiting).length;
const recruitingCount = items.filter((t) => t.isRecruiting).length;
const groupsBySkillLevel: Record<SkillLevel, typeof teams> = {
const groupsBySkillLevel: Record<SkillLevel, TeamLeaderboardItem[]> = {
beginner: [],
intermediate: [],
advanced: [],
pro: [],
};
teams.forEach(team => {
const level = team.performanceLevel as SkillLevel;
groupsBySkillLevel[level].push(team);
items.forEach((item) => {
const level = item.performanceLevel;
groupsBySkillLevel[level].push(item);
});
const topTeams = teams.slice(0, 10); // Assuming top 10
const topItems = items
.slice()
.sort((a, b) => (b.rating ?? 0) - (a.rating ?? 0))
.slice(0, 10);
const outputPort: TeamsLeaderboardOutputPort = {
teams,
this.output.present({
items,
recruitingCount,
groupsBySkillLevel,
topTeams,
};
topItems,
});
return Result.ok(outputPort);
} catch (error) {
this.logger.error('Error retrieving teams leaderboard', error as Error);
return Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'Failed to retrieve teams leaderboard' } });
return Result.ok(undefined);
} catch (err) {
const error = err as { message?: string } | undefined;
this.logger.error('Error retrieving teams leaderboard', err as Error);
return Result.err({
code: 'REPOSITORY_ERROR',
details: { message: error?.message ?? 'Failed to load teams leaderboard' },
});
}
}
}
}