121 lines
3.8 KiB
TypeScript
121 lines
3.8 KiB
TypeScript
/**
|
|
* Application Use Case: TeamRatingFactoryUseCase
|
|
*
|
|
* Factory use case for creating team rating events from race results.
|
|
* This replaces direct team rating calculations with event-based approach.
|
|
* Mirrors the user rating factory pattern.
|
|
*/
|
|
|
|
import { TeamRatingEvent } from '@core/racing/domain/entities/TeamRatingEvent';
|
|
import { TeamRatingDelta } from '@core/racing/domain/value-objects/TeamRatingDelta';
|
|
import { TeamRatingDimensionKey } from '@core/racing/domain/value-objects/TeamRatingDimensionKey';
|
|
import { TeamRatingEventId } from '@core/racing/domain/value-objects/TeamRatingEventId';
|
|
import type { Logger } from '@core/shared/domain/Logger';
|
|
import { TeamDrivingRatingEventFactory } from '../../domain/services/TeamDrivingRatingEventFactory';
|
|
import { TeamRaceResultsProvider } from '../ports/TeamRaceResultsProvider';
|
|
|
|
export interface TeamRatingFactoryInput {
|
|
raceId: string;
|
|
}
|
|
|
|
export interface TeamRatingFactoryOutput {
|
|
success: boolean;
|
|
raceId: string;
|
|
events: TeamRatingEvent[];
|
|
errors: string[];
|
|
}
|
|
|
|
export class TeamRatingFactoryUseCase {
|
|
constructor(
|
|
private readonly teamRaceResultsProvider: TeamRaceResultsProvider,
|
|
private readonly logger: Logger
|
|
) {
|
|
this.logger.info('[TeamRatingFactoryUseCase] Initialized');
|
|
}
|
|
|
|
async execute(input: TeamRatingFactoryInput): Promise<TeamRatingFactoryOutput> {
|
|
this.logger.debug(`[TeamRatingFactoryUseCase] Creating rating events for race ${input.raceId}`);
|
|
|
|
try {
|
|
// Load team race results
|
|
const teamRaceResults = await this.teamRaceResultsProvider.getTeamRaceResults(input.raceId);
|
|
|
|
if (!teamRaceResults) {
|
|
return {
|
|
success: false,
|
|
raceId: input.raceId,
|
|
events: [],
|
|
errors: ['Team race results not found'],
|
|
};
|
|
}
|
|
|
|
// Use factory to create events
|
|
const eventsByTeam = TeamDrivingRatingEventFactory.createDrivingEventsFromRace(teamRaceResults);
|
|
|
|
// Flatten events from all teams
|
|
const allEvents: TeamRatingEvent[] = [];
|
|
for (const [, events] of eventsByTeam) {
|
|
allEvents.push(...events);
|
|
}
|
|
|
|
if (allEvents.length === 0) {
|
|
this.logger.info(`[TeamRatingFactoryUseCase] No events generated for race ${input.raceId}`);
|
|
return {
|
|
success: true,
|
|
raceId: input.raceId,
|
|
events: [],
|
|
errors: [],
|
|
};
|
|
}
|
|
|
|
this.logger.info(`[TeamRatingFactoryUseCase] Generated ${allEvents.length} events for race ${input.raceId}`);
|
|
|
|
return {
|
|
success: true,
|
|
raceId: input.raceId,
|
|
events: allEvents,
|
|
errors: [],
|
|
};
|
|
|
|
} catch (error) {
|
|
const errorMsg = `Failed to create rating events: ${error instanceof Error ? error.message : 'Unknown error'}`;
|
|
this.logger.error('[TeamRatingFactoryUseCase] Error:', error instanceof Error ? error : new Error(String(error)));
|
|
|
|
return {
|
|
success: false,
|
|
raceId: input.raceId,
|
|
events: [],
|
|
errors: [errorMsg],
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create team rating events manually (for testing or manual adjustments)
|
|
*/
|
|
createManualEvents(
|
|
teamId: string,
|
|
dimension: string,
|
|
delta: number,
|
|
reason: string,
|
|
sourceType: 'race' | 'penalty' | 'vote' | 'adminAction' | 'manualAdjustment',
|
|
sourceId?: string
|
|
): TeamRatingEvent[] {
|
|
const source = sourceId ? { type: sourceType, id: sourceId } : { type: sourceType };
|
|
|
|
const event = TeamRatingEvent.create({
|
|
id: TeamRatingEventId.generate(),
|
|
teamId,
|
|
dimension: TeamRatingDimensionKey.create(dimension),
|
|
delta: TeamRatingDelta.create(delta),
|
|
occurredAt: new Date(),
|
|
createdAt: new Date(),
|
|
source: source,
|
|
reason: { code: reason },
|
|
visibility: { public: true },
|
|
version: 1,
|
|
});
|
|
|
|
return [event];
|
|
}
|
|
} |