This commit is contained in:
2025-12-29 22:27:33 +01:00
parent 3f610c1cb6
commit 7a853d4e43
96 changed files with 14790 additions and 111 deletions

View File

@@ -9,6 +9,7 @@ import { RatingUpdateService } from '@core/identity/domain/services/RatingUpdate
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { IRaceResultsProvider } from '@core/identity/application/ports/IRaceResultsProvider';
export interface CompleteRaceWithRatingsInput {
raceId: string;
@@ -32,6 +33,7 @@ interface DriverRatingProvider {
/**
* Enhanced CompleteRaceUseCase that includes rating updates.
* EVOLVED (Slice 7): Now uses ledger-based rating updates for transparency and auditability.
*/
export class CompleteRaceUseCaseWithRatings {
constructor(
@@ -42,6 +44,7 @@ export class CompleteRaceUseCaseWithRatings {
private readonly driverRatingProvider: DriverRatingProvider,
private readonly ratingUpdateService: RatingUpdateService,
private readonly output: UseCaseOutputPort<CompleteRaceWithRatingsResult>,
private readonly raceResultsProvider?: IRaceResultsProvider, // Optional: for new ledger flow
) {}
async execute(command: CompleteRaceWithRatingsInput): Promise<
@@ -93,8 +96,41 @@ export class CompleteRaceUseCaseWithRatings {
await this.updateStandings(race.leagueId, results);
// SLICE 7: Use new ledger-based approach if raceResultsProvider is available
// This provides backward compatibility while evolving to event-driven architecture
try {
await this.updateDriverRatings(results, registeredDriverIds.length);
if (this.raceResultsProvider) {
// NEW LEDGER APPROACH: Use RecordRaceRatingEventsUseCase via RatingUpdateService
const raceResultsData = {
raceId,
results: results.map(result => ({
userId: result.driverId.toString(),
startPos: result.startPosition.toNumber(),
finishPos: result.position.toNumber(),
incidents: result.incidents.toNumber(),
status: 'finished' as const, // RaceResultGenerator only generates finished results
})),
};
try {
const ratingResult = await this.ratingUpdateService.recordRaceRatingEvents(
raceId,
raceResultsData.results
);
if (!ratingResult.success) {
console.warn(`[Slice 7] Ledger-based rating update failed for race ${raceId}, falling back to legacy method`);
await this.updateDriverRatingsLegacy(results, registeredDriverIds.length);
}
} catch (error) {
// If ledger approach throws error, fall back to legacy method
console.warn(`[Slice 7] Ledger-based rating update threw error for race ${raceId}, falling back to legacy method: ${error instanceof Error ? error.message : 'Unknown error'}`);
await this.updateDriverRatingsLegacy(results, registeredDriverIds.length);
}
} else {
// BACKWARD COMPATIBLE: Use legacy direct update approach
await this.updateDriverRatingsLegacy(results, registeredDriverIds.length);
}
} catch (error) {
return Result.err({
code: 'RATING_UPDATE_FAILED',
@@ -161,7 +197,11 @@ export class CompleteRaceUseCaseWithRatings {
}
}
private async updateDriverRatings(results: RaceResult[], totalDrivers: number): Promise<void> {
/**
* Legacy rating update method (BACKWARD COMPATIBLE)
* Uses direct updates via RatingUpdateService
*/
private async updateDriverRatingsLegacy(results: RaceResult[], totalDrivers: number): Promise<void> {
const driverResults = results.map((result) => ({
driverId: result.driverId.toString(),
position: result.position.toNumber(),