134 lines
5.2 KiB
TypeScript
134 lines
5.2 KiB
TypeScript
import type { ISeasonRepository } from '@core/racing/domain/repositories/ISeasonRepository';
|
|
import type { ILeagueScoringConfigRepository } from '@core/racing/domain/repositories/ILeagueScoringConfigRepository';
|
|
import type { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository';
|
|
import type { IResultRepository } from '@core/racing/domain/repositories/IResultRepository';
|
|
import type { IPenaltyRepository } from '@core/racing/domain/repositories/IPenaltyRepository';
|
|
import type { IChampionshipStandingRepository } from '@core/racing/domain/repositories/IChampionshipStandingRepository';
|
|
|
|
import type { ChampionshipConfig } from '@core/racing/domain/types/ChampionshipConfig';
|
|
import type { SessionType } from '@core/racing/domain/types/SessionType';
|
|
import type { ChampionshipStanding } from '@core/racing/domain/entities/championship/ChampionshipStanding';
|
|
import { EventScoringService } from '@core/racing/domain/services/EventScoringService';
|
|
import { ChampionshipAggregator } from '@core/racing/domain/services/ChampionshipAggregator';
|
|
|
|
import type {
|
|
ChampionshipStandingsDTO,
|
|
ChampionshipStandingsRowDTO,
|
|
} from '../dto/ChampionshipStandingsDTO';
|
|
|
|
import type { AsyncUseCase } from '@core/shared/application/AsyncUseCase';
|
|
import { Result } from '@core/shared/application/Result';
|
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
|
|
|
export interface RecalculateChampionshipStandingsParams {
|
|
seasonId: string;
|
|
championshipId: string;
|
|
}
|
|
|
|
type RecalculateChampionshipStandingsErrorCode =
|
|
| 'SEASON_NOT_FOUND'
|
|
| 'LEAGUE_SCORING_CONFIG_NOT_FOUND'
|
|
| 'CHAMPIONSHIP_CONFIG_NOT_FOUND';
|
|
|
|
export class RecalculateChampionshipStandingsUseCase
|
|
implements AsyncUseCase<RecalculateChampionshipStandingsParams, ChampionshipStandingsDTO, RecalculateChampionshipStandingsErrorCode>
|
|
{
|
|
constructor(
|
|
private readonly seasonRepository: ISeasonRepository,
|
|
private readonly leagueScoringConfigRepository: ILeagueScoringConfigRepository,
|
|
private readonly raceRepository: IRaceRepository,
|
|
private readonly resultRepository: IResultRepository,
|
|
private readonly penaltyRepository: IPenaltyRepository,
|
|
private readonly championshipStandingRepository: IChampionshipStandingRepository,
|
|
private readonly eventScoringService: EventScoringService,
|
|
private readonly championshipAggregator: ChampionshipAggregator,
|
|
) {}
|
|
|
|
async execute(params: RecalculateChampionshipStandingsParams): Promise<Result<ChampionshipStandingsDTO, ApplicationErrorCode<RecalculateChampionshipStandingsErrorCode>>> {
|
|
const { seasonId, championshipId } = params;
|
|
|
|
const season = await this.seasonRepository.findById(seasonId);
|
|
if (!season) {
|
|
return Result.err({ code: 'SEASON_NOT_FOUND', details: { message: `Season not found: ${seasonId}` } });
|
|
}
|
|
|
|
const leagueScoringConfig =
|
|
await this.leagueScoringConfigRepository.findBySeasonId(seasonId);
|
|
if (!leagueScoringConfig) {
|
|
return Result.err({ code: 'LEAGUE_SCORING_CONFIG_NOT_FOUND', details: { message: `League scoring config not found for season: ${seasonId}` } });
|
|
}
|
|
|
|
const championship = leagueScoringConfig.championships.find((c) => c.id === championshipId);
|
|
if (!championship) {
|
|
return Result.err({ code: 'CHAMPIONSHIP_CONFIG_NOT_FOUND', details: { message: `Championship config not found: ${championshipId}` } });
|
|
}
|
|
|
|
const races = await this.raceRepository.findByLeagueId(season.leagueId);
|
|
|
|
const eventPointsByEventId: Record<string, ReturnType<EventScoringService['scoreSession']>> =
|
|
{};
|
|
|
|
for (const race of races) {
|
|
// Map existing Race.sessionType into scoring SessionType where possible.
|
|
const sessionType = this.mapRaceSessionType(race.sessionType);
|
|
if (!championship.sessionTypes.includes(sessionType)) {
|
|
continue;
|
|
}
|
|
|
|
const results = await this.resultRepository.findByRaceId(race.id);
|
|
|
|
// Fetch penalties for this specific race
|
|
const penalties = await this.penaltyRepository.findByRaceId(race.id);
|
|
|
|
const participantPoints = this.eventScoringService.scoreSession({
|
|
seasonId,
|
|
championship,
|
|
sessionType,
|
|
results,
|
|
penalties,
|
|
});
|
|
|
|
eventPointsByEventId[race.id] = participantPoints;
|
|
}
|
|
|
|
const standings: ChampionshipStanding[] = this.championshipAggregator.aggregate({
|
|
seasonId,
|
|
championship,
|
|
eventPointsByEventId,
|
|
});
|
|
|
|
await this.championshipStandingRepository.saveAll(standings);
|
|
|
|
const rows: ChampionshipStandingsRowDTO[] = standings.map((s) => ({
|
|
participant: s.participant,
|
|
position: s.position.toNumber(),
|
|
totalPoints: s.totalPoints.toNumber(),
|
|
resultsCounted: s.resultsCounted.toNumber(),
|
|
resultsDropped: s.resultsDropped.toNumber(),
|
|
}));
|
|
|
|
const dto: ChampionshipStandingsDTO = {
|
|
seasonId,
|
|
championshipId: championship.id,
|
|
championshipName: championship.name,
|
|
rows,
|
|
};
|
|
|
|
return Result.ok(dto);
|
|
}
|
|
|
|
|
|
private mapRaceSessionType(sessionType: string): SessionType {
|
|
if (sessionType === 'race') {
|
|
return 'main';
|
|
}
|
|
if (
|
|
sessionType === 'practice' ||
|
|
sessionType === 'qualifying' ||
|
|
sessionType === 'timeTrial'
|
|
) {
|
|
return sessionType;
|
|
}
|
|
return 'main';
|
|
}
|
|
} |