resolve manual DTOs

This commit is contained in:
2025-12-18 22:19:40 +01:00
parent 4a3087ae35
commit d617654928
179 changed files with 3716 additions and 1257 deletions

View File

@@ -1,4 +1,4 @@
import { Controller, Get, Post, Body, HttpCode, HttpStatus, Param } from '@nestjs/common';
import { Controller, Get, Post, Body, HttpCode, HttpStatus, Param, Query } from '@nestjs/common';
import { ApiTags, ApiResponse, ApiOperation } from '@nestjs/swagger';
import { SponsorService } from './SponsorService';
import { GetEntitySponsorshipPricingResultDTO } from './dtos/GetEntitySponsorshipPricingResultDTO';
@@ -9,6 +9,10 @@ import { GetSponsorDashboardQueryParamsDTO } from './dtos/GetSponsorDashboardQue
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';
@ApiTags('sponsors')
@Controller('sponsors')
@@ -52,4 +56,39 @@ export class SponsorController {
async getSponsorSponsorships(@Param('sponsorId') sponsorId: string): Promise<SponsorSponsorshipsDTO | null> {
return this.sponsorService.getSponsorSponsorships({ sponsorId } as GetSponsorSponsorshipsQueryParamsDTO);
}
@Get(':sponsorId')
@ApiOperation({ summary: 'Get a sponsor by ID' })
@ApiResponse({ status: 200, description: 'Sponsor data', type: GetSponsorOutputDTO })
@ApiResponse({ status: 404, description: 'Sponsor not found' })
async getSponsor(@Param('sponsorId') sponsorId: string): Promise<GetSponsorOutputDTO | null> {
return this.sponsorService.getSponsor(sponsorId);
}
@Get('requests')
@ApiOperation({ summary: 'Get pending sponsorship requests' })
@ApiResponse({ status: 200, description: 'List of pending sponsorship requests', type: GetPendingSponsorshipRequestsOutputDTO })
async getPendingSponsorshipRequests(@Query() query: { entityType: string; entityId: string }): Promise<GetPendingSponsorshipRequestsOutputDTO> {
return this.sponsorService.getPendingSponsorshipRequests(query);
}
@Post('requests/:requestId/accept')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Accept a sponsorship request' })
@ApiResponse({ status: 200, description: 'Sponsorship request accepted' })
@ApiResponse({ status: 400, description: 'Invalid request' })
@ApiResponse({ status: 404, description: 'Request not found' })
async acceptSponsorshipRequest(@Param('requestId') requestId: string, @Body() input: AcceptSponsorshipRequestInputDTO): Promise<any> {
return this.sponsorService.acceptSponsorshipRequest(requestId, input.respondedBy);
}
@Post('requests/:requestId/reject')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Reject a sponsorship request' })
@ApiResponse({ status: 200, description: 'Sponsorship request rejected' })
@ApiResponse({ status: 400, description: 'Invalid request' })
@ApiResponse({ status: 404, description: 'Request not found' })
async rejectSponsorshipRequest(@Param('requestId') requestId: string, @Body() input: RejectSponsorshipRequestInputDTO): Promise<any> {
return this.sponsorService.rejectSponsorshipRequest(requestId, input.respondedBy, input.reason);
}
}

View File

@@ -19,6 +19,10 @@ 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 { GetEntitySponsorshipPricingUseCase } from '@core/racing/application/use-cases/GetEntitySponsorshipPricingUseCase';
import { GetSponsorUseCase } from '@core/racing/application/use-cases/GetSponsorUseCase';
import { GetPendingSponsorshipRequestsUseCase } 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 concrete in-memory implementations
import { InMemorySponsorRepository } from '@adapters/racing/persistence/inmemory/InMemorySponsorRepository';
@@ -49,6 +53,10 @@ export const CREATE_SPONSOR_USE_CASE_TOKEN = 'CreateSponsorUseCase';
export const GET_SPONSOR_DASHBOARD_USE_CASE_TOKEN = 'GetSponsorDashboardUseCase';
export const GET_SPONSOR_SPONSORSHIPS_USE_CASE_TOKEN = 'GetSponsorSponsorshipsUseCase';
export const GET_ENTITY_SPONSORSHIP_PRICING_USE_CASE_TOKEN = 'GetEntitySponsorshipPricingUseCase';
export const GET_SPONSOR_USE_CASE_TOKEN = 'GetSponsorUseCase';
export const GET_PENDING_SPONSORSHIP_REQUESTS_USE_CASE_TOKEN = 'GetPendingSponsorshipRequestsUseCase';
export const ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN = 'AcceptSponsorshipRequestUseCase';
export const REJECT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN = 'RejectSponsorshipRequestUseCase';
export const SponsorProviders: Provider[] = [
SponsorService,
@@ -131,4 +139,27 @@ export const SponsorProviders: Provider[] = [
new GetEntitySponsorshipPricingUseCase(sponsorshipPricingRepo, sponsorshipRequestRepo, seasonSponsorshipRepo, logger),
inject: [SPONSORSHIP_PRICING_REPOSITORY_TOKEN, SPONSORSHIP_REQUEST_REPOSITORY_TOKEN, SEASON_SPONSORSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
},
{
provide: GET_SPONSOR_USE_CASE_TOKEN,
useFactory: (sponsorRepo: ISponsorRepository) => new GetSponsorUseCase(sponsorRepo),
inject: [SPONSOR_REPOSITORY_TOKEN],
},
{
provide: GET_PENDING_SPONSORSHIP_REQUESTS_USE_CASE_TOKEN,
useFactory: (sponsorshipRequestRepo: ISponsorshipRequestRepository, sponsorRepo: ISponsorRepository) =>
new GetPendingSponsorshipRequestsUseCase(sponsorshipRequestRepo, sponsorRepo),
inject: [SPONSORSHIP_REQUEST_REPOSITORY_TOKEN, SPONSOR_REPOSITORY_TOKEN],
},
{
provide: ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN,
useFactory: (sponsorshipRequestRepo: ISponsorshipRequestRepository, seasonSponsorshipRepo: ISeasonSponsorshipRepository, seasonRepo: ISeasonRepository, notificationService: any, paymentGateway: any, walletRepository: any, leagueWalletRepository: any, logger: Logger) =>
new AcceptSponsorshipRequestUseCase(sponsorshipRequestRepo, seasonSponsorshipRepo, seasonRepo, notificationService, paymentGateway, walletRepository, leagueWalletRepository, logger),
inject: [SPONSORSHIP_REQUEST_REPOSITORY_TOKEN, SEASON_SPONSORSHIP_REPOSITORY_TOKEN, SEASON_REPOSITORY_TOKEN, 'INotificationService', 'IPaymentGateway', 'IWalletRepository', 'ILeagueWalletRepository', LOGGER_TOKEN],
},
{
provide: REJECT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN,
useFactory: (sponsorshipRequestRepo: ISponsorshipRequestRepository, logger: Logger) =>
new RejectSponsorshipRequestUseCase(sponsorshipRequestRepo, logger),
inject: [SPONSORSHIP_REQUEST_REPOSITORY_TOKEN, LOGGER_TOKEN],
},
];

View File

@@ -7,6 +7,10 @@ import { GetSponsorDashboardQueryParamsDTO } from './dtos/GetSponsorDashboardQue
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 { SponsorDTO } from './dtos/SponsorDTO';
import { SponsorDashboardMetricsDTO } from './dtos/SponsorDashboardMetricsDTO';
import { SponsoredLeagueDTO } from './dtos/SponsoredLeagueDTO';
@@ -19,6 +23,10 @@ import { GetSponsorsUseCase } from '@core/racing/application/use-cases/GetSponso
import { CreateSponsorUseCase } from '@core/racing/application/use-cases/CreateSponsorUseCase';
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 } 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';
// Presenters
import { GetSponsorshipPricingPresenter } from './presenters/GetSponsorshipPricingPresenter';
@@ -28,7 +36,7 @@ import { GetSponsorDashboardPresenter } from './presenters/GetSponsorDashboardPr
import { GetSponsorSponsorshipsPresenter } from './presenters/GetSponsorSponsorshipsPresenter';
// 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, LOGGER_TOKEN } from './SponsorProviders';
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';
@Injectable()
@@ -39,6 +47,10 @@ export class SponsorService {
@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,
) {}
@@ -81,4 +93,48 @@ export class SponsorService {
await this.getSponsorSponsorshipsUseCase.execute(params, presenter);
return presenter.viewModel as SponsorSponsorshipsDTO | null;
}
async getSponsor(sponsorId: string): Promise<GetSponsorOutputDTO | null> {
this.logger.debug('[SponsorService] Fetching sponsor.', { sponsorId });
const result = await this.getSponsorUseCase.execute({ sponsorId });
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch sponsor.', result.error);
return null;
}
return result.value as GetSponsorOutputDTO | null;
}
async getPendingSponsorshipRequests(params: { entityType: string; entityId: string }): Promise<GetPendingSponsorshipRequestsOutputDTO> {
this.logger.debug('[SponsorService] Fetching pending sponsorship requests.', { params });
const result = await this.getPendingSponsorshipRequestsUseCase.execute(params as any);
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch pending sponsorship requests.', result.error);
return { entityType: params.entityType as any, entityId: params.entityId, requests: [], totalCount: 0 };
}
return result.value as GetPendingSponsorshipRequestsOutputDTO;
}
async acceptSponsorshipRequest(requestId: string, respondedBy: string): Promise<{ requestId: string; sponsorshipId: string; status: string; acceptedAt: Date; platformFee: number; netAmount: number } | null> {
this.logger.debug('[SponsorService] Accepting sponsorship request.', { requestId, respondedBy });
const result = await this.acceptSponsorshipRequestUseCase.execute({ requestId, respondedBy });
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to accept sponsorship request.', result.error);
return null;
}
return result.value;
}
async rejectSponsorshipRequest(requestId: string, respondedBy: string, reason?: string): Promise<{ requestId: string; status: string; rejectedAt: Date } | null> {
this.logger.debug('[SponsorService] Rejecting sponsorship request.', { requestId, respondedBy, reason });
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;
}
return result.value;
}
}

View File

@@ -0,0 +1,9 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsNotEmpty } from 'class-validator';
export class AcceptSponsorshipRequestInputDTO {
@ApiProperty()
@IsString()
@IsNotEmpty()
respondedBy: string;
}

View File

@@ -0,0 +1,16 @@
import { ApiProperty } from '@nestjs/swagger';
import { SponsorshipRequestDTO } from './SponsorshipRequestDTO';
export class GetPendingSponsorshipRequestsOutputDTO {
@ApiProperty()
entityType: string;
@ApiProperty()
entityId: string;
@ApiProperty({ type: [SponsorshipRequestDTO] })
requests: SponsorshipRequestDTO[];
@ApiProperty()
totalCount: number;
}

View File

@@ -0,0 +1,7 @@
import { ApiProperty } from '@nestjs/swagger';
import { SponsorDTO } from './SponsorDTO';
export class GetSponsorOutputDTO {
@ApiProperty({ type: SponsorDTO })
sponsor: SponsorDTO;
}

View File

@@ -0,0 +1,14 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsNotEmpty, IsOptional } from 'class-validator';
export class RejectSponsorshipRequestInputDTO {
@ApiProperty()
@IsString()
@IsNotEmpty()
respondedBy: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
reason?: string;
}

View File

@@ -0,0 +1,15 @@
import { ApiProperty } from '@nestjs/swagger';
export class SponsorDTO {
@ApiProperty()
id: string;
@ApiProperty()
name: string;
@ApiProperty({ required: false })
logoUrl?: string;
@ApiProperty({ required: false })
websiteUrl?: string;
}

View File

@@ -0,0 +1,39 @@
import { ApiProperty } from '@nestjs/swagger';
export class SponsorshipRequestDTO {
@ApiProperty()
id: string;
@ApiProperty()
sponsorId: string;
@ApiProperty()
sponsorName: string;
@ApiProperty({ required: false })
sponsorLogo?: string;
@ApiProperty()
tier: string;
@ApiProperty()
offeredAmount: number;
@ApiProperty()
currency: string;
@ApiProperty()
formattedAmount: string;
@ApiProperty({ required: false })
message?: string;
@ApiProperty()
createdAt: Date;
@ApiProperty()
platformFee: number;
@ApiProperty()
netAmount: number;
}