rename to core
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
|
||||
import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository';
|
||||
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
|
||||
import type { IStandingRepository } from '../../domain/repositories/IStandingRepository';
|
||||
import type { DriverRatingProvider } from '../ports/DriverRatingProvider';
|
||||
import { Result } from '../../domain/entities/Result';
|
||||
import { Standing } from '../../domain/entities/Standing';
|
||||
import { RaceResultGenerator } from '../utils/RaceResultGenerator';
|
||||
import { RatingUpdateService } from '@gridpilot/identity/domain/services/RatingUpdateService';
|
||||
import type { AsyncUseCase } from '@gridpilot/shared/application';
|
||||
import type { ILogger } from '../../../shared/src/logging/ILogger';
|
||||
|
||||
/**
|
||||
* Enhanced CompleteRaceUseCase that includes rating updates
|
||||
*/
|
||||
export interface CompleteRaceCommandDTO {
|
||||
raceId: string;
|
||||
}
|
||||
|
||||
export class CompleteRaceUseCaseWithRatings
|
||||
implements AsyncUseCase<CompleteRaceCommandDTO, void> {
|
||||
constructor(
|
||||
private readonly raceRepository: IRaceRepository,
|
||||
private readonly raceRegistrationRepository: IRaceRegistrationRepository,
|
||||
private readonly resultRepository: IResultRepository,
|
||||
private readonly standingRepository: IStandingRepository,
|
||||
private readonly driverRatingProvider: DriverRatingProvider,
|
||||
private readonly ratingUpdateService: RatingUpdateService,
|
||||
private readonly logger: ILogger,
|
||||
) {}
|
||||
|
||||
async execute(command: CompleteRaceCommandDTO): Promise<void> {
|
||||
const { raceId } = command;
|
||||
this.logger.debug(`Attempting to complete race with ID: ${raceId}`);
|
||||
|
||||
try {
|
||||
const race = await this.raceRepository.findById(raceId);
|
||||
if (!race) {
|
||||
this.logger.error(`Race not found for ID: ${raceId}`);
|
||||
throw new Error('Race not found');
|
||||
}
|
||||
this.logger.debug(`Found race: ${race.id}`);
|
||||
|
||||
// Get registered drivers for this race
|
||||
const registeredDriverIds = await this.raceRegistrationRepository.getRegisteredDrivers(raceId);
|
||||
if (registeredDriverIds.length === 0) {
|
||||
this.logger.warn(`No registered drivers for race ID: ${raceId}. Cannot complete race.`);
|
||||
throw new Error('Cannot complete race with no registered drivers');
|
||||
}
|
||||
this.logger.debug(`Found ${registeredDriverIds.length} registered drivers for race ID: ${raceId}`);
|
||||
|
||||
// Get driver ratings
|
||||
this.logger.debug('Fetching driver ratings...');
|
||||
const driverRatings = this.driverRatingProvider.getRatings(registeredDriverIds);
|
||||
this.logger.debug('Driver ratings fetched.');
|
||||
|
||||
// Generate realistic race results
|
||||
this.logger.debug('Generating race results...');
|
||||
const results = RaceResultGenerator.generateRaceResults(raceId, registeredDriverIds, driverRatings);
|
||||
this.logger.info(`Generated ${results.length} race results for race ID: ${raceId}`);
|
||||
|
||||
// Save results
|
||||
this.logger.debug('Saving race results...');
|
||||
for (const result of results) {
|
||||
await this.resultRepository.create(result);
|
||||
}
|
||||
this.logger.info('Race results saved successfully.');
|
||||
|
||||
// Update standings
|
||||
this.logger.debug(`Updating standings for league ID: ${race.leagueId}`);
|
||||
await this.updateStandings(race.leagueId, results);
|
||||
this.logger.info('Standings updated successfully.');
|
||||
|
||||
// Update driver ratings based on performance
|
||||
this.logger.debug('Updating driver ratings...');
|
||||
await this.updateDriverRatings(results, registeredDriverIds.length);
|
||||
this.logger.info('Driver ratings updated successfully.');
|
||||
|
||||
// Complete the race
|
||||
this.logger.debug(`Marking race ID: ${raceId} as complete...`);
|
||||
const completedRace = race.complete();
|
||||
await this.raceRepository.update(completedRace);
|
||||
this.logger.info(`Race ID: ${raceId} completed successfully.`);
|
||||
} catch (error: any) {
|
||||
this.logger.error(`Error completing race ${raceId}: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async updateStandings(leagueId: string, results: Result[]): Promise<void> {
|
||||
// Group results by driver
|
||||
const resultsByDriver = new Map<string, Result[]>();
|
||||
for (const result of results) {
|
||||
const existing = resultsByDriver.get(result.driverId) || [];
|
||||
existing.push(result);
|
||||
resultsByDriver.set(result.driverId, existing);
|
||||
}
|
||||
|
||||
// Update or create standings for each driver
|
||||
for (const [driverId, driverResults] of resultsByDriver) {
|
||||
let standing = await this.standingRepository.findByDriverIdAndLeagueId(driverId, leagueId);
|
||||
|
||||
if (!standing) {
|
||||
standing = Standing.create({
|
||||
leagueId,
|
||||
driverId,
|
||||
});
|
||||
}
|
||||
|
||||
// Add all results for this driver (should be just one for this race)
|
||||
for (const result of driverResults) {
|
||||
standing = standing.addRaceResult(result.position, {
|
||||
1: 25, 2: 18, 3: 15, 4: 12, 5: 10, 6: 8, 7: 6, 8: 4, 9: 2, 10: 1
|
||||
});
|
||||
}
|
||||
|
||||
await this.standingRepository.save(standing);
|
||||
}
|
||||
}
|
||||
|
||||
private async updateDriverRatings(results: Result[], totalDrivers: number): Promise<void> {
|
||||
const driverResults = results.map(result => ({
|
||||
driverId: result.driverId,
|
||||
position: result.position,
|
||||
totalDrivers,
|
||||
incidents: result.incidents,
|
||||
startPosition: result.startPosition,
|
||||
}));
|
||||
|
||||
await this.ratingUpdateService.updateDriverRatingsAfterRace(driverResults);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user