presenter refactoring

This commit is contained in:
2025-12-20 17:06:11 +01:00
parent 92be9d2e1b
commit e9d6f90bb2
109 changed files with 4159 additions and 1283 deletions

View File

@@ -1,14 +1,7 @@
import { Injectable, Inject } from '@nestjs/common';
import { GetEntitySponsorshipPricingResultDTO } from './dtos/GetEntitySponsorshipPricingResultDTO';
import { GetSponsorsOutputDTO } from './dtos/GetSponsorsOutputDTO';
import { CreateSponsorInputDTO } from './dtos/CreateSponsorInputDTO';
import { CreateSponsorOutputDTO } from './dtos/CreateSponsorOutputDTO';
import { GetSponsorDashboardQueryParamsDTO } from './dtos/GetSponsorDashboardQueryParamsDTO';
import { SponsorDashboardDTO } from './dtos/SponsorDashboardDTO';
import { GetSponsorSponsorshipsQueryParamsDTO } from './dtos/GetSponsorSponsorshipsQueryParamsDTO';
import { SponsorSponsorshipsDTO } from './dtos/SponsorSponsorshipsDTO';
import { GetSponsorOutputDTO } from './dtos/GetSponsorOutputDTO';
import { GetPendingSponsorshipRequestsOutputDTO } from './dtos/GetPendingSponsorshipRequestsOutputDTO';
import { AcceptSponsorshipRequestInputDTO } from './dtos/AcceptSponsorshipRequestInputDTO';
import { RejectSponsorshipRequestInputDTO } from './dtos/RejectSponsorshipRequestInputDTO';
import { PaymentMethodDTO } from './dtos/PaymentMethodDTO';
@@ -29,139 +22,253 @@ import { CreateSponsorUseCase } from '@core/racing/application/use-cases/CreateS
import { GetSponsorDashboardUseCase } from '@core/racing/application/use-cases/GetSponsorDashboardUseCase';
import { GetSponsorSponsorshipsUseCase } from '@core/racing/application/use-cases/GetSponsorSponsorshipsUseCase';
import { GetSponsorUseCase } from '@core/racing/application/use-cases/GetSponsorUseCase';
import { GetPendingSponsorshipRequestsUseCase, GetPendingSponsorshipRequestsDTO } from '@core/racing/application/use-cases/GetPendingSponsorshipRequestsUseCase';
import {
GetPendingSponsorshipRequestsUseCase,
GetPendingSponsorshipRequestsDTO,
} from '@core/racing/application/use-cases/GetPendingSponsorshipRequestsUseCase';
import { AcceptSponsorshipRequestUseCase } from '@core/racing/application/use-cases/AcceptSponsorshipRequestUseCase';
import { RejectSponsorshipRequestUseCase } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
import type { SponsorableEntityType } from '@core/racing/domain/entities/SponsorshipRequest';
import type { AcceptSponsorshipRequestResultPort } from '@core/racing/application/ports/output/AcceptSponsorshipRequestResultPort';
import type { RejectSponsorshipRequestResultDTO } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
import type { Logger } from '@core/shared/application';
// Presenters
import { GetEntitySponsorshipPricingPresenter } from './presenters/GetEntitySponsorshipPricingPresenter';
import { GetSponsorsPresenter } from './presenters/GetSponsorsPresenter';
import { CreateSponsorPresenter } from './presenters/CreateSponsorPresenter';
import { GetSponsorDashboardPresenter } from './presenters/GetSponsorDashboardPresenter';
import { GetSponsorSponsorshipsPresenter } from './presenters/GetSponsorSponsorshipsPresenter';
import { GetSponsorPresenter } from './presenters/GetSponsorPresenter';
import { GetPendingSponsorshipRequestsPresenter } from './presenters/GetPendingSponsorshipRequestsPresenter';
import { AcceptSponsorshipRequestPresenter } from './presenters/AcceptSponsorshipRequestPresenter';
import { RejectSponsorshipRequestPresenter } from './presenters/RejectSponsorshipRequestPresenter';
import { SponsorBillingPresenter } from './presenters/SponsorBillingPresenter';
import { AvailableLeaguesPresenter } from './presenters/AvailableLeaguesPresenter';
import { LeagueDetailPresenter } from './presenters/LeagueDetailPresenter';
import { SponsorSettingsPresenter } from './presenters/SponsorSettingsPresenter';
import { SponsorSettingsUpdatePresenter } from './presenters/SponsorSettingsUpdatePresenter';
// Tokens
import { GET_SPONSORSHIP_PRICING_USE_CASE_TOKEN, GET_SPONSORS_USE_CASE_TOKEN, CREATE_SPONSOR_USE_CASE_TOKEN, GET_SPONSOR_DASHBOARD_USE_CASE_TOKEN, GET_SPONSOR_SPONSORSHIPS_USE_CASE_TOKEN, GET_SPONSOR_USE_CASE_TOKEN, GET_PENDING_SPONSORSHIP_REQUESTS_USE_CASE_TOKEN, ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN, REJECT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN, LOGGER_TOKEN } from './SponsorProviders';
import type { Logger } from '@core/shared/application';
import {
GET_SPONSORSHIP_PRICING_USE_CASE_TOKEN,
GET_SPONSORS_USE_CASE_TOKEN,
CREATE_SPONSOR_USE_CASE_TOKEN,
GET_SPONSOR_DASHBOARD_USE_CASE_TOKEN,
GET_SPONSOR_SPONSORSHIPS_USE_CASE_TOKEN,
GET_SPONSOR_USE_CASE_TOKEN,
GET_PENDING_SPONSORSHIP_REQUESTS_USE_CASE_TOKEN,
ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN,
REJECT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN,
LOGGER_TOKEN,
} from './SponsorProviders';
@Injectable()
export class SponsorService {
constructor(
@Inject(GET_SPONSORSHIP_PRICING_USE_CASE_TOKEN) private readonly getSponsorshipPricingUseCase: GetSponsorshipPricingUseCase,
@Inject(GET_SPONSORS_USE_CASE_TOKEN) private readonly getSponsorsUseCase: GetSponsorsUseCase,
@Inject(CREATE_SPONSOR_USE_CASE_TOKEN) private readonly createSponsorUseCase: CreateSponsorUseCase,
@Inject(GET_SPONSOR_DASHBOARD_USE_CASE_TOKEN) private readonly getSponsorDashboardUseCase: GetSponsorDashboardUseCase,
@Inject(GET_SPONSOR_SPONSORSHIPS_USE_CASE_TOKEN) private readonly getSponsorSponsorshipsUseCase: GetSponsorSponsorshipsUseCase,
@Inject(GET_SPONSOR_USE_CASE_TOKEN) private readonly getSponsorUseCase: GetSponsorUseCase,
@Inject(GET_PENDING_SPONSORSHIP_REQUESTS_USE_CASE_TOKEN) private readonly getPendingSponsorshipRequestsUseCase: GetPendingSponsorshipRequestsUseCase,
@Inject(ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN) private readonly acceptSponsorshipRequestUseCase: AcceptSponsorshipRequestUseCase,
@Inject(REJECT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN) private readonly rejectSponsorshipRequestUseCase: RejectSponsorshipRequestUseCase,
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
@Inject(GET_SPONSORSHIP_PRICING_USE_CASE_TOKEN)
private readonly getSponsorshipPricingUseCase: GetSponsorshipPricingUseCase,
@Inject(GET_SPONSORS_USE_CASE_TOKEN)
private readonly getSponsorsUseCase: GetSponsorsUseCase,
@Inject(CREATE_SPONSOR_USE_CASE_TOKEN)
private readonly createSponsorUseCase: CreateSponsorUseCase,
@Inject(GET_SPONSOR_DASHBOARD_USE_CASE_TOKEN)
private readonly getSponsorDashboardUseCase: GetSponsorDashboardUseCase,
@Inject(GET_SPONSOR_SPONSORSHIPS_USE_CASE_TOKEN)
private readonly getSponsorSponsorshipsUseCase: GetSponsorSponsorshipsUseCase,
@Inject(GET_SPONSOR_USE_CASE_TOKEN)
private readonly getSponsorUseCase: GetSponsorUseCase,
@Inject(GET_PENDING_SPONSORSHIP_REQUESTS_USE_CASE_TOKEN)
private readonly getPendingSponsorshipRequestsUseCase: GetPendingSponsorshipRequestsUseCase,
@Inject(ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN)
private readonly acceptSponsorshipRequestUseCase: AcceptSponsorshipRequestUseCase,
@Inject(REJECT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN)
private readonly rejectSponsorshipRequestUseCase: RejectSponsorshipRequestUseCase,
@Inject(LOGGER_TOKEN)
private readonly logger: Logger,
) {}
async getEntitySponsorshipPricing(): Promise<GetEntitySponsorshipPricingResultDTO> {
async getEntitySponsorshipPricing(): Promise<GetEntitySponsorshipPricingPresenter> {
this.logger.debug('[SponsorService] Fetching sponsorship pricing.');
const presenter = new GetEntitySponsorshipPricingPresenter();
const result = await this.getSponsorshipPricingUseCase.execute();
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch sponsorship pricing.', result.error);
return { entityType: 'season', entityId: '', pricing: [] };
presenter.present(null);
return presenter;
}
return result.value as GetEntitySponsorshipPricingResultDTO;
presenter.present(result.value);
return presenter;
}
async getSponsors(): Promise<GetSponsorsOutputDTO> {
async getSponsors(): Promise<GetSponsorsPresenter> {
this.logger.debug('[SponsorService] Fetching sponsors.');
const presenter = new GetSponsorsPresenter();
const result = await this.getSponsorsUseCase.execute();
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch sponsors.', result.error);
return { sponsors: [] };
presenter.present({ sponsors: [] });
return presenter;
}
return result.value as GetSponsorsOutputDTO;
presenter.present(result.value);
return presenter;
}
async createSponsor(input: CreateSponsorInputDTO): Promise<CreateSponsorOutputDTO> {
async createSponsor(input: CreateSponsorInputDTO): Promise<CreateSponsorPresenter> {
this.logger.debug('[SponsorService] Creating sponsor.', { input });
const presenter = new CreateSponsorPresenter();
const result = await this.createSponsorUseCase.execute(input);
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to create sponsor.', result.error);
throw new Error(result.error.details?.message || 'Failed to create sponsor');
}
return result.value as CreateSponsorOutputDTO;
presenter.present(result.value);
return presenter;
}
async getSponsorDashboard(params: GetSponsorDashboardQueryParamsDTO): Promise<SponsorDashboardDTO | null> {
async getSponsorDashboard(
params: GetSponsorDashboardQueryParamsDTO,
): Promise<GetSponsorDashboardPresenter> {
this.logger.debug('[SponsorService] Fetching sponsor dashboard.', { params });
const presenter = new GetSponsorDashboardPresenter();
const result = await this.getSponsorDashboardUseCase.execute(params);
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch sponsor dashboard.', result.error);
return null;
presenter.present(null);
return presenter;
}
return result.value as SponsorDashboardDTO | null;
presenter.present(result.value);
return presenter;
}
async getSponsorSponsorships(params: GetSponsorSponsorshipsQueryParamsDTO): Promise<SponsorSponsorshipsDTO | null> {
async getSponsorSponsorships(
params: GetSponsorSponsorshipsQueryParamsDTO,
): Promise<GetSponsorSponsorshipsPresenter> {
this.logger.debug('[SponsorService] Fetching sponsor sponsorships.', { params });
const presenter = new GetSponsorSponsorshipsPresenter();
const result = await this.getSponsorSponsorshipsUseCase.execute(params);
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch sponsor sponsorships.', result.error);
return null;
presenter.present(null);
return presenter;
}
return result.value as SponsorSponsorshipsDTO | null;
presenter.present(result.value);
return presenter;
}
async getSponsor(sponsorId: string): Promise<GetSponsorOutputDTO | null> {
async getSponsor(sponsorId: string): Promise<GetSponsorPresenter> {
this.logger.debug('[SponsorService] Fetching sponsor.', { sponsorId });
const presenter = new GetSponsorPresenter();
const result = await this.getSponsorUseCase.execute({ sponsorId });
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch sponsor.', result.error);
return null;
presenter.present(null);
return presenter;
}
return result.value as GetSponsorOutputDTO | null;
presenter.present(result.value);
return presenter;
}
async getPendingSponsorshipRequests(params: { entityType: SponsorableEntityType; entityId: string }): Promise<GetPendingSponsorshipRequestsOutputDTO> {
async getPendingSponsorshipRequests(params: {
entityType: SponsorableEntityType;
entityId: string;
}): Promise<GetPendingSponsorshipRequestsPresenter> {
this.logger.debug('[SponsorService] Fetching pending sponsorship requests.', { params });
const result = await this.getPendingSponsorshipRequestsUseCase.execute(params as GetPendingSponsorshipRequestsDTO);
const presenter = new GetPendingSponsorshipRequestsPresenter();
const result = await this.getPendingSponsorshipRequestsUseCase.execute(
params as GetPendingSponsorshipRequestsDTO,
);
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch pending sponsorship requests.', result.error);
return { entityType: params.entityType, entityId: params.entityId, requests: [], totalCount: 0 };
presenter.present({
entityType: params.entityType,
entityId: params.entityId,
requests: [],
totalCount: 0,
});
return presenter;
}
return result.value as GetPendingSponsorshipRequestsOutputDTO;
presenter.present(result.value);
return presenter;
}
async acceptSponsorshipRequest(requestId: string, respondedBy: string): Promise<AcceptSponsorshipRequestResultPort | null> {
this.logger.debug('[SponsorService] Accepting sponsorship request.', { requestId, respondedBy });
async acceptSponsorshipRequest(
requestId: string,
respondedBy: string,
): Promise<AcceptSponsorshipRequestPresenter> {
this.logger.debug('[SponsorService] Accepting sponsorship request.', {
requestId,
respondedBy,
});
const presenter = new AcceptSponsorshipRequestPresenter();
const result = await this.acceptSponsorshipRequestUseCase.execute({
requestId,
respondedBy,
} as AcceptSponsorshipRequestInputDTO);
const result = await this.acceptSponsorshipRequestUseCase.execute({ requestId, respondedBy });
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to accept sponsorship request.', result.error);
return null;
presenter.present(null);
return presenter;
}
return result.value;
presenter.present(result.value);
return presenter;
}
async rejectSponsorshipRequest(requestId: string, respondedBy: string, reason?: string): Promise<RejectSponsorshipRequestResultDTO | null> {
this.logger.debug('[SponsorService] Rejecting sponsorship request.', { requestId, respondedBy, reason });
async rejectSponsorshipRequest(
requestId: string,
respondedBy: string,
reason?: string,
): Promise<RejectSponsorshipRequestPresenter> {
this.logger.debug('[SponsorService] Rejecting sponsorship request.', {
requestId,
respondedBy,
reason,
});
const presenter = new RejectSponsorshipRequestPresenter();
const result = await this.rejectSponsorshipRequestUseCase.execute({
requestId,
respondedBy,
reason,
} as RejectSponsorshipRequestInputDTO);
const result = await this.rejectSponsorshipRequestUseCase.execute({ requestId, respondedBy, reason });
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to reject sponsorship request.', result.error);
return null;
presenter.present(null);
return presenter;
}
return result.value;
presenter.present(result.value);
return presenter;
}
async getSponsorBilling(sponsorId: string): Promise<{
paymentMethods: PaymentMethodDTO[];
invoices: InvoiceDTO[];
stats: BillingStatsDTO;
}> {
async getSponsorBilling(sponsorId: string): Promise<SponsorBillingPresenter> {
this.logger.debug('[SponsorService] Fetching sponsor billing.', { sponsorId });
const presenter = new SponsorBillingPresenter();
// Mock data - in real implementation, this would come from repositories
const paymentMethods: PaymentMethodDTO[] = [
{
@@ -242,14 +349,16 @@ export class SponsorService {
averageMonthlySpend: 2075,
};
return { paymentMethods, invoices, stats };
presenter.present({ paymentMethods, invoices, stats });
return presenter;
}
async getAvailableLeagues(): Promise<AvailableLeagueDTO[]> {
async getAvailableLeagues(): Promise<AvailableLeaguesPresenter> {
this.logger.debug('[SponsorService] Fetching available leagues.');
// Mock data
return [
const presenter = new AvailableLeaguesPresenter();
const leagues: AvailableLeagueDTO[] = [
{
id: 'league-1',
name: 'GT3 Masters Championship',
@@ -262,7 +371,8 @@ export class SponsorService {
tier: 'premium',
nextRace: '2025-12-20',
seasonStatus: 'active',
description: 'Premier GT3 racing with top-tier drivers. Weekly broadcasts and active community.',
description:
'Premier GT3 racing with top-tier drivers. Weekly broadcasts and active community.',
},
{
id: 'league-2',
@@ -276,18 +386,20 @@ export class SponsorService {
tier: 'premium',
nextRace: '2026-01-05',
seasonStatus: 'active',
description: 'Multi-class endurance racing. High engagement from dedicated endurance fans.',
description:
'Multi-class endurance racing. High engagement from dedicated endurance fans.',
},
];
presenter.present(leagues);
return presenter;
}
async getLeagueDetail(leagueId: string): Promise<{
league: LeagueDetailDTO;
drivers: DriverDTO[];
races: RaceDTO[];
}> {
async getLeagueDetail(leagueId: string): Promise<LeagueDetailPresenter> {
this.logger.debug('[SponsorService] Fetching league detail.', { leagueId });
const presenter = new LeagueDetailPresenter();
// Mock data
const league: LeagueDetailDTO = {
id: leagueId,
@@ -295,7 +407,8 @@ export class SponsorService {
game: 'iRacing',
tier: 'premium',
season: 'Season 3',
description: 'Premier GT3 racing with top-tier drivers competing across the world\'s most iconic circuits.',
description:
"Premier GT3 racing with top-tier drivers competing across the world's most iconic circuits.",
drivers: 48,
races: 12,
completedRaces: 8,
@@ -316,7 +429,7 @@ export class SponsorService {
'Race results page branding',
'Social media feature posts',
'Newsletter sponsor spot',
]
],
},
secondary: {
available: 1,
@@ -327,31 +440,58 @@ export class SponsorService {
'League page sidebar placement',
'Race results mention',
'Social media mentions',
]
],
},
},
};
const drivers: DriverDTO[] = [
{ id: 'd1', name: 'Max Verstappen', country: 'NL', position: 1, races: 8, impressions: 4200, team: 'Red Bull Racing' },
{ id: 'd2', name: 'Lewis Hamilton', country: 'GB', position: 2, races: 8, impressions: 3980, team: 'Mercedes AMG' },
{
id: 'd1',
name: 'Max Verstappen',
country: 'NL',
position: 1,
races: 8,
impressions: 4200,
team: 'Red Bull Racing',
},
{
id: 'd2',
name: 'Lewis Hamilton',
country: 'GB',
position: 2,
races: 8,
impressions: 3980,
team: 'Mercedes AMG',
},
];
const races: RaceDTO[] = [
{ id: 'r1', name: 'Spa-Francorchamps', date: '2025-12-20', views: 0, status: 'upcoming' },
{ id: 'r2', name: 'Monza', date: '2025-12-08', views: 5800, status: 'completed' },
{
id: 'r1',
name: 'Spa-Francorchamps',
date: '2025-12-20',
views: 0,
status: 'upcoming',
},
{
id: 'r2',
name: 'Monza',
date: '2025-12-08',
views: 5800,
status: 'completed',
},
];
return { league, drivers, races };
presenter.present({ league, drivers, races });
return presenter;
}
async getSponsorSettings(sponsorId: string): Promise<{
profile: SponsorProfileDTO;
notifications: NotificationSettingsDTO;
privacy: PrivacySettingsDTO;
}> {
async getSponsorSettings(sponsorId: string): Promise<SponsorSettingsPresenter> {
this.logger.debug('[SponsorService] Fetching sponsor settings.', { sponsorId });
const presenter = new SponsorSettingsPresenter();
// Mock data
const profile: SponsorProfileDTO = {
companyName: 'Acme Racing Co.',
@@ -359,7 +499,8 @@ export class SponsorService {
contactEmail: 'sponsor@acme-racing.com',
contactPhone: '+1 (555) 123-4567',
website: 'https://acme-racing.com',
description: 'Premium sim racing equipment and accessories for competitive drivers.',
description:
'Premium sim racing equipment and accessories for competitive drivers.',
logoUrl: null,
industry: 'Racing Equipment',
address: {
@@ -392,7 +533,8 @@ export class SponsorService {
allowDirectContact: true,
};
return { profile, notifications, privacy };
presenter.present({ profile, notifications, privacy });
return presenter;
}
async updateSponsorSettings(
@@ -401,12 +543,15 @@ export class SponsorService {
profile?: Partial<SponsorProfileDTO>;
notifications?: Partial<NotificationSettingsDTO>;
privacy?: Partial<PrivacySettingsDTO>;
}
): Promise<void> {
},
): Promise<SponsorSettingsUpdatePresenter> {
this.logger.debug('[SponsorService] Updating sponsor settings.', { sponsorId, input });
// Mock implementation - in real app, this would persist to database
// For now, just log the update
this.logger.info('[SponsorService] Settings updated successfully.', { sponsorId });
const presenter = new SponsorSettingsUpdatePresenter();
presenter.present({ success: true });
return presenter;
}
}