rating
This commit is contained in:
123
core/identity/application/use-cases/AppendRatingEventsUseCase.ts
Normal file
123
core/identity/application/use-cases/AppendRatingEventsUseCase.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { IRatingEventRepository } from '../../domain/repositories/IRatingEventRepository';
|
||||
import { IUserRatingRepository } from '../../domain/repositories/IUserRatingRepository';
|
||||
import { RatingEventFactory } from '../../domain/services/RatingEventFactory';
|
||||
import { RatingSnapshotCalculator } from '../../domain/services/RatingSnapshotCalculator';
|
||||
import { RatingEvent } from '../../domain/entities/RatingEvent';
|
||||
import { RatingEventId } from '../../domain/value-objects/RatingEventId';
|
||||
import { RatingDimensionKey } from '../../domain/value-objects/RatingDimensionKey';
|
||||
import { RatingDelta } from '../../domain/value-objects/RatingDelta';
|
||||
import { CreateRatingEventDto } from '../dtos/CreateRatingEventDto';
|
||||
|
||||
/**
|
||||
* Input for AppendRatingEventsUseCase
|
||||
*/
|
||||
export interface AppendRatingEventsInput {
|
||||
userId: string;
|
||||
events?: CreateRatingEventDto[]; // Optional: direct event creation
|
||||
// Alternative: raceId, penaltyId, etc. for factory-based creation
|
||||
raceId?: string;
|
||||
raceResults?: Array<{
|
||||
position: number;
|
||||
totalDrivers: number;
|
||||
startPosition: number;
|
||||
incidents: number;
|
||||
fieldStrength: number;
|
||||
status: 'finished' | 'dnf' | 'dns' | 'dsq' | 'afk';
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output for AppendRatingEventsUseCase
|
||||
*/
|
||||
export interface AppendRatingEventsOutput {
|
||||
events: string[]; // Event IDs
|
||||
snapshotUpdated: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Case: AppendRatingEventsUseCase
|
||||
*
|
||||
* Appends rating events to the ledger and recomputes the snapshot.
|
||||
* Follows CQRS Light: command side operation.
|
||||
*/
|
||||
export class AppendRatingEventsUseCase {
|
||||
constructor(
|
||||
private readonly ratingEventRepository: IRatingEventRepository,
|
||||
private readonly userRatingRepository: IUserRatingRepository,
|
||||
) {}
|
||||
|
||||
async execute(input: AppendRatingEventsInput): Promise<AppendRatingEventsOutput> {
|
||||
const eventsToSave = [];
|
||||
|
||||
// 1. Create events from direct input
|
||||
if (input.events) {
|
||||
for (const eventDto of input.events) {
|
||||
const event = this.createEventFromDto(eventDto);
|
||||
eventsToSave.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Create events from race results (using factory)
|
||||
if (input.raceId && input.raceResults) {
|
||||
for (const result of input.raceResults) {
|
||||
const raceEvents = RatingEventFactory.createFromRaceFinish({
|
||||
userId: input.userId,
|
||||
raceId: input.raceId,
|
||||
position: result.position,
|
||||
totalDrivers: result.totalDrivers,
|
||||
startPosition: result.startPosition,
|
||||
incidents: result.incidents,
|
||||
fieldStrength: result.fieldStrength,
|
||||
status: result.status,
|
||||
});
|
||||
eventsToSave.push(...raceEvents);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Save all events to ledger
|
||||
const savedEventIds: string[] = [];
|
||||
for (const event of eventsToSave) {
|
||||
await this.ratingEventRepository.save(event);
|
||||
savedEventIds.push(event.id.value);
|
||||
}
|
||||
|
||||
// 4. Recompute snapshot if events were saved
|
||||
let snapshotUpdated = false;
|
||||
if (savedEventIds.length > 0) {
|
||||
const allEvents = await this.ratingEventRepository.getAllByUserId(input.userId);
|
||||
const snapshot = RatingSnapshotCalculator.calculate(input.userId, allEvents);
|
||||
await this.userRatingRepository.save(snapshot);
|
||||
snapshotUpdated = true;
|
||||
}
|
||||
|
||||
return {
|
||||
events: savedEventIds,
|
||||
snapshotUpdated,
|
||||
};
|
||||
}
|
||||
|
||||
private createEventFromDto(dto: CreateRatingEventDto) {
|
||||
const props: any = {
|
||||
id: RatingEventId.generate(),
|
||||
userId: dto.userId,
|
||||
dimension: RatingDimensionKey.create(dto.dimension),
|
||||
delta: RatingDelta.create(dto.delta),
|
||||
occurredAt: dto.occurredAt ? new Date(dto.occurredAt) : new Date(),
|
||||
createdAt: new Date(),
|
||||
source: { type: dto.sourceType, id: dto.sourceId },
|
||||
reason: {
|
||||
code: dto.reasonCode,
|
||||
summary: dto.reasonSummary,
|
||||
details: dto.reasonDetails || {},
|
||||
},
|
||||
visibility: { public: true, redactedFields: [] },
|
||||
version: 1,
|
||||
};
|
||||
|
||||
if (dto.weight !== undefined) {
|
||||
props.weight = dto.weight;
|
||||
}
|
||||
|
||||
return RatingEvent.create(props);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user