fix data flow issues
This commit is contained in:
@@ -6,6 +6,7 @@ import { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepos
|
||||
import { IRankingService } from '@core/racing/domain/services/IRankingService';
|
||||
import { IDriverStatsService } from '@core/racing/domain/services/IDriverStatsService';
|
||||
import { DriverRatingProvider } from '@core/racing/application/ports/DriverRatingProvider';
|
||||
import { DriverExtendedProfileProvider } from '@core/racing/application/ports/DriverExtendedProfileProvider';
|
||||
import { IImageServicePort } from '@core/racing/application/ports/IImageServicePort';
|
||||
import { IRaceRegistrationRepository } from '@core/racing/domain/repositories/IRaceRegistrationRepository';
|
||||
import { INotificationPreferenceRepository } from '@core/notifications/domain/repositories/INotificationPreferenceRepository';
|
||||
@@ -24,6 +25,7 @@ import { InMemoryDriverRepository } from '@adapters/racing/persistence/inmemory/
|
||||
import { InMemoryRankingService } from '@adapters/racing/services/InMemoryRankingService';
|
||||
import { InMemoryDriverStatsService } from '@adapters/racing/services/InMemoryDriverStatsService';
|
||||
import { InMemoryDriverRatingProvider } from '@adapters/racing/ports/InMemoryDriverRatingProvider';
|
||||
import { InMemoryDriverExtendedProfileProvider } from '@adapters/racing/ports/InMemoryDriverExtendedProfileProvider';
|
||||
import { InMemoryImageServiceAdapter } from '@adapters/media/ports/InMemoryImageServiceAdapter';
|
||||
import { InMemoryRaceRegistrationRepository } from '@adapters/racing/persistence/inmemory/InMemoryRaceRegistrationRepository';
|
||||
import { InMemoryNotificationPreferenceRepository } from '@adapters/notifications/persistence/inmemory/InMemoryNotificationPreferenceRepository';
|
||||
@@ -34,6 +36,7 @@ export const DRIVER_REPOSITORY_TOKEN = 'IDriverRepository';
|
||||
export const RANKING_SERVICE_TOKEN = 'IRankingService';
|
||||
export const DRIVER_STATS_SERVICE_TOKEN = 'IDriverStatsService';
|
||||
export const DRIVER_RATING_PROVIDER_TOKEN = 'DriverRatingProvider';
|
||||
export const DRIVER_EXTENDED_PROFILE_PROVIDER_TOKEN = 'DriverExtendedProfileProvider';
|
||||
export const IMAGE_SERVICE_PORT_TOKEN = 'IImageServicePort';
|
||||
export const RACE_REGISTRATION_REPOSITORY_TOKEN = 'IRaceRegistrationRepository';
|
||||
export const NOTIFICATION_PREFERENCE_REPOSITORY_TOKEN = 'INotificationPreferenceRepository';
|
||||
@@ -69,6 +72,11 @@ export const DriverProviders: Provider[] = [
|
||||
useFactory: (logger: Logger) => new InMemoryDriverRatingProvider(logger),
|
||||
inject: [LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: DRIVER_EXTENDED_PROFILE_PROVIDER_TOKEN,
|
||||
useFactory: (logger: Logger) => new InMemoryDriverExtendedProfileProvider(logger),
|
||||
inject: [LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: IMAGE_SERVICE_PORT_TOKEN,
|
||||
useFactory: (logger: Logger) => new InMemoryImageServiceAdapter(logger),
|
||||
@@ -117,7 +125,7 @@ export const DriverProviders: Provider[] = [
|
||||
},
|
||||
{
|
||||
provide: GET_PROFILE_OVERVIEW_USE_CASE_TOKEN,
|
||||
useFactory: (driverRepo: IDriverRepository, imageService: IImageServicePort, logger: Logger) =>
|
||||
useFactory: (driverRepo: IDriverRepository, imageService: IImageServicePort, driverExtendedProfileProvider: DriverExtendedProfileProvider, logger: Logger) =>
|
||||
new GetProfileOverviewUseCase(
|
||||
driverRepo,
|
||||
// TODO: Add teamRepository, teamMembershipRepository, socialRepository, etc.
|
||||
@@ -125,9 +133,10 @@ export const DriverProviders: Provider[] = [
|
||||
null as any, // teamMembershipRepository
|
||||
null as any, // socialRepository
|
||||
imageService,
|
||||
driverExtendedProfileProvider,
|
||||
() => null, // getDriverStats
|
||||
() => [], // getAllDriverRankings
|
||||
),
|
||||
inject: [DRIVER_REPOSITORY_TOKEN, IMAGE_SERVICE_PORT_TOKEN, LOGGER_TOKEN],
|
||||
inject: [DRIVER_REPOSITORY_TOKEN, IMAGE_SERVICE_PORT_TOKEN, DRIVER_EXTENDED_PROFILE_PROVIDER_TOKEN, LOGGER_TOKEN],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -29,6 +29,9 @@ import { GetLeagueOwnerSummaryQueryDTO } from './dtos/GetLeagueOwnerSummaryQuery
|
||||
import { GetLeagueAdminConfigQueryDTO } from './dtos/GetLeagueAdminConfigQueryDTO';
|
||||
import { GetLeagueProtestsQueryDTO } from './dtos/GetLeagueProtestsQueryDTO';
|
||||
import { GetLeagueSeasonsQueryDTO } from './dtos/GetLeagueSeasonsQueryDTO';
|
||||
import { GetLeagueWalletOutputDTO } from './dtos/GetLeagueWalletOutputDTO';
|
||||
import { WithdrawFromLeagueWalletInputDTO } from './dtos/WithdrawFromLeagueWalletInputDTO';
|
||||
import { WithdrawFromLeagueWalletOutputDTO } from './dtos/WithdrawFromLeagueWalletOutputDTO';
|
||||
|
||||
@ApiTags('leagues')
|
||||
@Controller('leagues')
|
||||
@@ -274,4 +277,22 @@ export class LeagueController {
|
||||
async getRaces(@Param('leagueId') leagueId: string): Promise<GetLeagueRacesOutputDTO> {
|
||||
return this.leagueService.getRaces(leagueId);
|
||||
}
|
||||
|
||||
@Get(':leagueId/wallet')
|
||||
@ApiOperation({ summary: 'Get league wallet information' })
|
||||
@ApiResponse({ status: 200, description: 'League wallet data', type: GetLeagueWalletOutputDTO })
|
||||
async getLeagueWallet(@Param('leagueId') leagueId: string): Promise<GetLeagueWalletOutputDTO> {
|
||||
return this.leagueService.getLeagueWallet(leagueId);
|
||||
}
|
||||
|
||||
@Post(':leagueId/wallet/withdraw')
|
||||
@ApiOperation({ summary: 'Withdraw from league wallet' })
|
||||
@ApiBody({ type: WithdrawFromLeagueWalletInputDTO })
|
||||
@ApiResponse({ status: 200, description: 'Withdrawal processed', type: WithdrawFromLeagueWalletOutputDTO })
|
||||
async withdrawFromLeagueWallet(
|
||||
@Param('leagueId') leagueId: string,
|
||||
@Body() input: WithdrawFromLeagueWalletInputDTO,
|
||||
): Promise<WithdrawFromLeagueWalletOutputDTO> {
|
||||
return this.leagueService.withdrawFromLeagueWallet(leagueId, input);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ import { InMemoryProtestRepository } from '@adapters/racing/persistence/inmemory
|
||||
import { InMemoryRaceRepository } from '@adapters/racing/persistence/inmemory/InMemoryRaceRepository';
|
||||
import { InMemoryDriverRepository } from '@adapters/racing/persistence/inmemory/InMemoryDriverRepository';
|
||||
import { InMemoryStandingRepository } from '@adapters/racing/persistence/inmemory/InMemoryStandingRepository';
|
||||
import { InMemoryLeagueWalletRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueWalletRepository';
|
||||
import { InMemoryTransactionRepository } from '@adapters/racing/persistence/inmemory/InMemoryTransactionRepository';
|
||||
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
|
||||
import { listLeagueScoringPresets } from '@adapters/bootstrap/LeagueScoringPresets';
|
||||
|
||||
@@ -38,6 +40,8 @@ import { GetLeagueMembershipsUseCase } from '@core/racing/application/use-cases/
|
||||
import { GetLeagueScheduleUseCase } from '@core/racing/application/use-cases/GetLeagueScheduleUseCase';
|
||||
import { GetLeagueStatsUseCase } from '@core/racing/application/use-cases/GetLeagueStatsUseCase';
|
||||
import { GetLeagueAdminPermissionsUseCase } from '@core/racing/application/use-cases/GetLeagueAdminPermissionsUseCase';
|
||||
import { GetLeagueWalletUseCase } from '@core/racing/application/use-cases/GetLeagueWalletUseCase';
|
||||
import { WithdrawFromLeagueWalletUseCase } from '@core/racing/application/use-cases/WithdrawFromLeagueWalletUseCase';
|
||||
|
||||
// Define injection tokens
|
||||
export const LEAGUE_REPOSITORY_TOKEN = 'ILeagueRepository';
|
||||
@@ -50,6 +54,8 @@ export const GAME_REPOSITORY_TOKEN = 'IGameRepository';
|
||||
export const PROTEST_REPOSITORY_TOKEN = 'IProtestRepository';
|
||||
export const RACE_REPOSITORY_TOKEN = 'IRaceRepository';
|
||||
export const DRIVER_REPOSITORY_TOKEN = 'IDriverRepository';
|
||||
export const LEAGUE_WALLET_REPOSITORY_TOKEN = 'ILeagueWalletRepository';
|
||||
export const TRANSACTION_REPOSITORY_TOKEN = 'ITransactionRepository';
|
||||
export const LOGGER_TOKEN = 'Logger'; // Already defined in AuthProviders, but good to have here too
|
||||
export const GET_LEAGUE_STANDINGS_USE_CASE = 'GetLeagueStandingsUseCase';
|
||||
|
||||
@@ -105,6 +111,16 @@ export const LeagueProviders: Provider[] = [
|
||||
useFactory: (logger: Logger) => new InMemoryDriverRepository(logger),
|
||||
inject: [LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: LEAGUE_WALLET_REPOSITORY_TOKEN,
|
||||
useFactory: (logger: Logger) => new InMemoryLeagueWalletRepository(logger),
|
||||
inject: [LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: TRANSACTION_REPOSITORY_TOKEN,
|
||||
useFactory: (logger: Logger) => new InMemoryTransactionRepository(logger),
|
||||
inject: [LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: LOGGER_TOKEN,
|
||||
useClass: ConsoleLogger,
|
||||
@@ -132,6 +148,8 @@ export const LeagueProviders: Provider[] = [
|
||||
GetLeagueScheduleUseCase,
|
||||
GetLeagueStatsUseCase,
|
||||
GetLeagueAdminPermissionsUseCase,
|
||||
GetLeagueWalletUseCase,
|
||||
WithdrawFromLeagueWalletUseCase,
|
||||
{
|
||||
provide: ListLeagueScoringPresetsUseCase,
|
||||
useFactory: () => new ListLeagueScoringPresetsUseCase(listLeagueScoringPresets()),
|
||||
|
||||
@@ -58,6 +58,8 @@ import { RejectLeagueJoinRequestUseCase } from '@core/racing/application/use-cas
|
||||
import { RemoveLeagueMemberUseCase } from '@core/racing/application/use-cases/RemoveLeagueMemberUseCase';
|
||||
import { TransferLeagueOwnershipUseCase } from '@core/racing/application/use-cases/TransferLeagueOwnershipUseCase';
|
||||
import { UpdateLeagueMemberRoleUseCase } from '@core/racing/application/use-cases/UpdateLeagueMemberRoleUseCase';
|
||||
import { GetLeagueWalletUseCase } from '@core/racing/application/use-cases/GetLeagueWalletUseCase';
|
||||
import { WithdrawFromLeagueWalletUseCase } from '@core/racing/application/use-cases/WithdrawFromLeagueWalletUseCase';
|
||||
|
||||
// API Presenters
|
||||
import { AllLeaguesWithCapacityPresenter } from './presenters/AllLeaguesWithCapacityPresenter';
|
||||
@@ -108,6 +110,8 @@ export class LeagueService {
|
||||
private readonly getLeagueMembershipsUseCase: GetLeagueMembershipsUseCase,
|
||||
private readonly getLeagueScheduleUseCase: GetLeagueScheduleUseCase,
|
||||
private readonly getLeagueAdminPermissionsUseCase: GetLeagueAdminPermissionsUseCase,
|
||||
private readonly getLeagueWalletUseCase: GetLeagueWalletUseCase,
|
||||
private readonly withdrawFromLeagueWalletUseCase: WithdrawFromLeagueWalletUseCase,
|
||||
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
@@ -438,4 +442,32 @@ export class LeagueService {
|
||||
races: [],
|
||||
};
|
||||
}
|
||||
|
||||
async getLeagueWallet(leagueId: string): Promise<GetLeagueWalletOutputDTO> {
|
||||
this.logger.debug('Getting league wallet', { leagueId });
|
||||
const result = await this.getLeagueWalletUseCase.execute({ leagueId });
|
||||
if (result.isErr()) {
|
||||
throw new Error(result.unwrapErr().message);
|
||||
}
|
||||
return result.unwrap();
|
||||
}
|
||||
|
||||
async withdrawFromLeagueWallet(leagueId: string, input: WithdrawFromLeagueWalletInputDTO): Promise<WithdrawFromLeagueWalletOutputDTO> {
|
||||
this.logger.debug('Withdrawing from league wallet', { leagueId, amount: input.amount });
|
||||
const result = await this.withdrawFromLeagueWalletUseCase.execute({
|
||||
leagueId,
|
||||
amount: input.amount,
|
||||
currency: input.currency,
|
||||
seasonId: input.seasonId,
|
||||
destinationAccount: input.destinationAccount,
|
||||
});
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
if (error.code === 'WITHDRAWAL_NOT_ALLOWED') {
|
||||
return { success: false, message: error.message };
|
||||
}
|
||||
throw new Error(error.message);
|
||||
}
|
||||
return result.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
59
apps/api/src/domain/league/dtos/GetLeagueWalletOutputDTO.ts
Normal file
59
apps/api/src/domain/league/dtos/GetLeagueWalletOutputDTO.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class WalletTransactionDTO {
|
||||
@ApiProperty()
|
||||
id: string;
|
||||
|
||||
@ApiProperty({ enum: ['sponsorship', 'membership', 'withdrawal', 'prize'] })
|
||||
type: 'sponsorship' | 'membership' | 'withdrawal' | 'prize';
|
||||
|
||||
@ApiProperty()
|
||||
description: string;
|
||||
|
||||
@ApiProperty()
|
||||
amount: number;
|
||||
|
||||
@ApiProperty()
|
||||
fee: number;
|
||||
|
||||
@ApiProperty()
|
||||
netAmount: number;
|
||||
|
||||
@ApiProperty()
|
||||
date: string;
|
||||
|
||||
@ApiProperty({ enum: ['completed', 'pending', 'failed'] })
|
||||
status: 'completed' | 'pending' | 'failed';
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
reference?: string;
|
||||
}
|
||||
|
||||
export class GetLeagueWalletOutputDTO {
|
||||
@ApiProperty()
|
||||
balance: number;
|
||||
|
||||
@ApiProperty()
|
||||
currency: string;
|
||||
|
||||
@ApiProperty()
|
||||
totalRevenue: number;
|
||||
|
||||
@ApiProperty()
|
||||
totalFees: number;
|
||||
|
||||
@ApiProperty()
|
||||
totalWithdrawals: number;
|
||||
|
||||
@ApiProperty()
|
||||
pendingPayouts: number;
|
||||
|
||||
@ApiProperty()
|
||||
canWithdraw: boolean;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
withdrawalBlockReason?: string;
|
||||
|
||||
@ApiProperty({ type: [WalletTransactionDTO] })
|
||||
transactions: WalletTransactionDTO[];
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class WithdrawFromLeagueWalletInputDTO {
|
||||
@ApiProperty()
|
||||
amount: number;
|
||||
|
||||
@ApiProperty()
|
||||
currency: string;
|
||||
|
||||
@ApiProperty()
|
||||
seasonId: string;
|
||||
|
||||
@ApiProperty()
|
||||
destinationAccount: string;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class WithdrawFromLeagueWalletOutputDTO {
|
||||
@ApiProperty()
|
||||
success: boolean;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
message?: string;
|
||||
}
|
||||
Reference in New Issue
Block a user