refactor use cases
This commit is contained in:
@@ -1,150 +1,96 @@
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import type { ITeamRepository } from '../../domain/repositories/ITeamRepository';
|
||||
import type { ITeamMembershipRepository } from '../../domain/repositories/ITeamMembershipRepository';
|
||||
import type { ITeamStatsRepository } from '../../domain/repositories/ITeamStatsRepository';
|
||||
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
|
||||
import { MediaReference } from '@core/domain/media/MediaReference';
|
||||
import type { Team } from '../../domain/entities/Team';
|
||||
|
||||
export type GetAllTeamsInput = {};
|
||||
export interface GetAllTeamsInput {}
|
||||
|
||||
export type GetAllTeamsErrorCode = 'REPOSITORY_ERROR';
|
||||
|
||||
export interface TeamSummary {
|
||||
id: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
leagues: string[];
|
||||
createdAt: Date;
|
||||
export interface EnrichedTeam {
|
||||
team: Team;
|
||||
memberCount: number;
|
||||
totalWins?: number;
|
||||
totalRaces?: number;
|
||||
performanceLevel?: string;
|
||||
specialization?: string;
|
||||
region?: string;
|
||||
languages?: string[];
|
||||
logoRef?: MediaReference;
|
||||
logoUrl?: string | null;
|
||||
rating?: number;
|
||||
category?: string | undefined;
|
||||
totalWins: number;
|
||||
totalRaces: number;
|
||||
performanceLevel: 'beginner' | 'intermediate' | 'advanced' | 'pro';
|
||||
specialization: 'endurance' | 'sprint' | 'mixed';
|
||||
region: string;
|
||||
languages: string[];
|
||||
rating: number;
|
||||
logoUrl: string | null;
|
||||
description: string;
|
||||
leagues: string[];
|
||||
isRecruiting: boolean;
|
||||
}
|
||||
|
||||
export interface GetAllTeamsResult {
|
||||
teams: TeamSummary[];
|
||||
teams: EnrichedTeam[];
|
||||
totalCount: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Case for retrieving all teams.
|
||||
*/
|
||||
export class GetAllTeamsUseCase {
|
||||
constructor(
|
||||
private readonly teamRepository: ITeamRepository,
|
||||
private readonly teamMembershipRepository: ITeamMembershipRepository,
|
||||
private readonly teamStatsRepository: ITeamStatsRepository,
|
||||
private readonly resultRepository: IResultRepository,
|
||||
private readonly membershipRepository: ITeamMembershipRepository,
|
||||
private readonly statsRepository: ITeamStatsRepository,
|
||||
private readonly logger: Logger,
|
||||
private readonly output: UseCaseOutputPort<GetAllTeamsResult>,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
_input: GetAllTeamsInput = {},
|
||||
): Promise<Result<void, ApplicationErrorCode<GetAllTeamsErrorCode, { message: string }>>> {
|
||||
void _input;
|
||||
this.logger.debug('Executing GetAllTeamsUseCase');
|
||||
_input: GetAllTeamsInput,
|
||||
): Promise<Result<GetAllTeamsResult, ApplicationErrorCode<GetAllTeamsErrorCode, { message: string }>>> {
|
||||
this.logger.debug('GetAllTeamsUseCase: Fetching all teams');
|
||||
|
||||
try {
|
||||
const teams = await this.teamRepository.findAll();
|
||||
const enrichedTeams: EnrichedTeam[] = [];
|
||||
|
||||
const enrichedTeams: TeamSummary[] = await Promise.all(
|
||||
teams.map(async (team) => {
|
||||
const memberCount = await this.teamMembershipRepository.countByTeamId(team.id);
|
||||
|
||||
// Get logo reference from team entity
|
||||
const logoRef = team.logoRef;
|
||||
|
||||
// Try to get pre-computed stats first
|
||||
let stats = await this.teamStatsRepository.getTeamStats(team.id);
|
||||
|
||||
// If no pre-computed stats, compute them on-the-fly from results
|
||||
if (!stats) {
|
||||
this.logger.debug(`Computing stats for team ${team.id} on-the-fly`);
|
||||
const teamMemberships = await this.teamMembershipRepository.getTeamMembers(team.id);
|
||||
const teamMemberIds = teamMemberships.map(m => m.driverId.toString());
|
||||
|
||||
const allResults = await this.resultRepository.findAll();
|
||||
const teamResults = allResults.filter(r => teamMemberIds.includes(r.driverId.toString()));
|
||||
|
||||
const wins = teamResults.filter(r => r.position.toNumber() === 1).length;
|
||||
const totalRaces = teamResults.length;
|
||||
|
||||
// Calculate rating
|
||||
const baseRating = 1000;
|
||||
const winBonus = wins * 50;
|
||||
const raceBonus = Math.min(totalRaces * 5, 200);
|
||||
const rating = Math.round(baseRating + winBonus + raceBonus);
|
||||
|
||||
// Determine performance level
|
||||
let performanceLevel: 'beginner' | 'intermediate' | 'advanced' | 'pro';
|
||||
if (wins >= 20) performanceLevel = 'pro';
|
||||
else if (wins >= 10) performanceLevel = 'advanced';
|
||||
else if (wins >= 5) performanceLevel = 'intermediate';
|
||||
else performanceLevel = 'beginner';
|
||||
|
||||
stats = {
|
||||
performanceLevel,
|
||||
specialization: 'mixed',
|
||||
region: 'International',
|
||||
languages: ['en'],
|
||||
totalWins: wins,
|
||||
totalRaces,
|
||||
rating,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
id: team.id,
|
||||
name: team.name.props,
|
||||
tag: team.tag.props,
|
||||
description: team.description.props,
|
||||
ownerId: team.ownerId.toString(),
|
||||
leagues: team.leagues.map(l => l.toString()),
|
||||
createdAt: team.createdAt.toDate(),
|
||||
memberCount,
|
||||
totalWins: stats!.totalWins,
|
||||
totalRaces: stats!.totalRaces,
|
||||
performanceLevel: stats!.performanceLevel,
|
||||
specialization: stats!.specialization,
|
||||
region: stats!.region,
|
||||
languages: stats!.languages,
|
||||
logoRef: logoRef,
|
||||
logoUrl: null, // Will be resolved by presenter
|
||||
rating: stats!.rating,
|
||||
category: team.category,
|
||||
isRecruiting: team.isRecruiting,
|
||||
};
|
||||
}),
|
||||
);
|
||||
for (const team of teams) {
|
||||
// Get member count
|
||||
const memberCount = await this.membershipRepository.countByTeamId(team.id.toString());
|
||||
|
||||
// Get team stats
|
||||
const stats = await this.statsRepository.getTeamStats(team.id.toString());
|
||||
|
||||
// Resolve logo URL
|
||||
let logoUrl: string | undefined;
|
||||
if (team.logoRef) {
|
||||
// For now, use a placeholder - in real implementation, MediaResolver would be used
|
||||
logoUrl = `/media/teams/${team.id}/logo`;
|
||||
}
|
||||
|
||||
const result: GetAllTeamsResult = {
|
||||
enrichedTeams.push({
|
||||
team,
|
||||
memberCount,
|
||||
totalWins: stats?.totalWins ?? 0,
|
||||
totalRaces: stats?.totalRaces ?? 0,
|
||||
performanceLevel: stats?.performanceLevel ?? 'intermediate',
|
||||
specialization: stats?.specialization ?? 'mixed',
|
||||
region: stats?.region ?? '',
|
||||
languages: stats?.languages ?? [],
|
||||
rating: stats?.rating ?? 0,
|
||||
logoUrl: logoUrl ?? null,
|
||||
description: team.description.toString(),
|
||||
leagues: team.leagues.map(l => l.toString()),
|
||||
isRecruiting: team.isRecruiting,
|
||||
});
|
||||
}
|
||||
|
||||
this.logger.debug('Successfully retrieved and enriched all teams.');
|
||||
return Result.ok({
|
||||
teams: enrichedTeams,
|
||||
totalCount: enrichedTeams.length,
|
||||
};
|
||||
totalCount: enrichedTeams.length
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : 'Failed to fetch all teams';
|
||||
this.logger.error('GetAllTeamsUseCase: Error fetching teams', error instanceof Error ? error : new Error(message));
|
||||
|
||||
this.logger.debug('Successfully retrieved all teams.');
|
||||
this.output.present(result);
|
||||
|
||||
return Result.ok(undefined);
|
||||
} catch (error) {
|
||||
this.logger.error('Error retrieving all teams', error instanceof Error ? error : new Error(String(error)));
|
||||
return Result.err({
|
||||
code: 'REPOSITORY_ERROR',
|
||||
details: { message: error instanceof Error ? error.message : 'Failed to load teams' },
|
||||
details: { message },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user