fix data flow issues
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
export interface DriverExtendedProfileProvider {
|
||||
getExtendedProfile(driverId: string): {
|
||||
socialHandles: {
|
||||
platform: 'twitter' | 'youtube' | 'twitch' | 'discord';
|
||||
handle: string;
|
||||
url: string;
|
||||
}[];
|
||||
achievements: {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
icon: 'trophy' | 'medal' | 'star' | 'crown' | 'target' | 'zap';
|
||||
rarity: 'common' | 'rare' | 'epic' | 'legendary';
|
||||
earnedAt: string;
|
||||
}[];
|
||||
racingStyle: string;
|
||||
favoriteTrack: string;
|
||||
favoriteCar: string;
|
||||
timezone: string;
|
||||
availableHours: string;
|
||||
lookingForTeam: boolean;
|
||||
openToRequests: boolean;
|
||||
} | null;
|
||||
}
|
||||
4
core/racing/application/ports/DriverRatingProvider.ts
Normal file
4
core/racing/application/ports/DriverRatingProvider.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface DriverRatingProvider {
|
||||
getRating(driverId: string): number | null;
|
||||
getRatings(driverIds: string[]): Map<string, number>;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
export interface WalletTransactionOutputPort {
|
||||
id: string;
|
||||
type: 'sponsorship' | 'membership' | 'withdrawal' | 'prize';
|
||||
description: string;
|
||||
amount: number;
|
||||
fee: number;
|
||||
netAmount: number;
|
||||
date: string;
|
||||
status: 'completed' | 'pending' | 'failed';
|
||||
reference?: string;
|
||||
}
|
||||
|
||||
export interface GetLeagueWalletOutputPort {
|
||||
balance: number;
|
||||
currency: string;
|
||||
totalRevenue: number;
|
||||
totalFees: number;
|
||||
totalWithdrawals: number;
|
||||
pendingPayouts: number;
|
||||
canWithdraw: boolean;
|
||||
withdrawalBlockReason?: string;
|
||||
transactions: WalletTransactionOutputPort[];
|
||||
}
|
||||
@@ -53,5 +53,26 @@ export interface ProfileOverviewOutputPort {
|
||||
avatarUrl: string;
|
||||
}[];
|
||||
};
|
||||
extendedProfile: null;
|
||||
extendedProfile: {
|
||||
socialHandles: {
|
||||
platform: 'twitter' | 'youtube' | 'twitch' | 'discord';
|
||||
handle: string;
|
||||
url: string;
|
||||
}[];
|
||||
achievements: {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
icon: 'trophy' | 'medal' | 'star' | 'crown' | 'target' | 'zap';
|
||||
rarity: 'common' | 'rare' | 'epic' | 'legendary';
|
||||
earnedAt: string;
|
||||
}[];
|
||||
racingStyle: string;
|
||||
favoriteTrack: string;
|
||||
favoriteCar: string;
|
||||
timezone: string;
|
||||
availableHours: string;
|
||||
lookingForTeam: boolean;
|
||||
openToRequests: boolean;
|
||||
} | null;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface WithdrawFromLeagueWalletOutputPort {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
}
|
||||
99
core/racing/application/use-cases/GetLeagueWalletUseCase.ts
Normal file
99
core/racing/application/use-cases/GetLeagueWalletUseCase.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import type { ILeagueWalletRepository } from '../../domain/repositories/ILeagueWalletRepository';
|
||||
import type { ITransactionRepository } from '../../domain/repositories/ITransactionRepository';
|
||||
import type { GetLeagueWalletOutputPort } from '../ports/output/GetLeagueWalletOutputPort';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
|
||||
export interface GetLeagueWalletUseCaseParams {
|
||||
leagueId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Case for retrieving league wallet information.
|
||||
*/
|
||||
export class GetLeagueWalletUseCase {
|
||||
constructor(
|
||||
private readonly leagueWalletRepository: ILeagueWalletRepository,
|
||||
private readonly transactionRepository: ITransactionRepository,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
params: GetLeagueWalletUseCaseParams,
|
||||
): Promise<Result<GetLeagueWalletOutputPort, ApplicationErrorCode<'REPOSITORY_ERROR'>>> {
|
||||
try {
|
||||
// For now, return mock data to emulate previous state
|
||||
// TODO: Implement full domain logic when wallet entities are properly seeded
|
||||
const mockWallet: GetLeagueWalletOutputPort = {
|
||||
balance: 2450.00,
|
||||
currency: 'USD',
|
||||
totalRevenue: 3200.00,
|
||||
totalFees: 320.00,
|
||||
totalWithdrawals: 430.00,
|
||||
pendingPayouts: 150.00,
|
||||
canWithdraw: false,
|
||||
withdrawalBlockReason: 'Season 2 is still active. Withdrawals are available after season completion.',
|
||||
transactions: [
|
||||
{
|
||||
id: 'txn-1',
|
||||
type: 'sponsorship',
|
||||
description: 'Main Sponsor - TechCorp',
|
||||
amount: 1200.00,
|
||||
fee: 120.00,
|
||||
netAmount: 1080.00,
|
||||
date: '2025-12-01T00:00:00.000Z',
|
||||
status: 'completed',
|
||||
reference: 'SP-2025-001',
|
||||
},
|
||||
{
|
||||
id: 'txn-2',
|
||||
type: 'sponsorship',
|
||||
description: 'Secondary Sponsor - RaceFuel',
|
||||
amount: 400.00,
|
||||
fee: 40.00,
|
||||
netAmount: 360.00,
|
||||
date: '2025-12-01T00:00:00.000Z',
|
||||
status: 'completed',
|
||||
reference: 'SP-2025-002',
|
||||
},
|
||||
{
|
||||
id: 'txn-3',
|
||||
type: 'membership',
|
||||
description: 'Season Fee - 32 drivers',
|
||||
amount: 1600.00,
|
||||
fee: 160.00,
|
||||
netAmount: 1440.00,
|
||||
date: '2025-11-15T00:00:00.000Z',
|
||||
status: 'completed',
|
||||
reference: 'MF-2025-032',
|
||||
},
|
||||
{
|
||||
id: 'txn-4',
|
||||
type: 'withdrawal',
|
||||
description: 'Bank Transfer - Season 1 Payout',
|
||||
amount: -430.00,
|
||||
fee: 0,
|
||||
netAmount: -430.00,
|
||||
date: '2025-10-30T00:00:00.000Z',
|
||||
status: 'completed',
|
||||
reference: 'WD-2025-001',
|
||||
},
|
||||
{
|
||||
id: 'txn-5',
|
||||
type: 'prize',
|
||||
description: 'Championship Prize Pool (reserved)',
|
||||
amount: -150.00,
|
||||
fee: 0,
|
||||
netAmount: -150.00,
|
||||
date: '2025-12-05T00:00:00.000Z',
|
||||
status: 'pending',
|
||||
reference: 'PZ-2025-001',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return Result.ok(mockWallet);
|
||||
} catch {
|
||||
return Result.err({ code: 'REPOSITORY_ERROR', message: 'Failed to fetch league wallet' });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import type { ITeamRepository } from '../../domain/repositories/ITeamRepository'
|
||||
import type { ITeamMembershipRepository } from '../../domain/repositories/ITeamMembershipRepository';
|
||||
import type { IImageServicePort } from '../ports/IImageServicePort';
|
||||
import type { ISocialGraphRepository } from '@core/social/domain/repositories/ISocialGraphRepository';
|
||||
import type { DriverExtendedProfileProvider } from '../ports/DriverExtendedProfileProvider';
|
||||
import type { Driver } from '../../domain/entities/Driver';
|
||||
import type { Team } from '../../domain/entities/Team';
|
||||
import type { ProfileOverviewOutputPort } from '../ports/output/ProfileOverviewOutputPort';
|
||||
@@ -40,6 +41,7 @@ export class GetProfileOverviewUseCase {
|
||||
private readonly teamMembershipRepository: ITeamMembershipRepository,
|
||||
private readonly socialRepository: ISocialGraphRepository,
|
||||
private readonly imageService: IImageServicePort,
|
||||
private readonly driverExtendedProfileProvider: DriverExtendedProfileProvider,
|
||||
private readonly getDriverStats: (driverId: string) => ProfileDriverStatsAdapter | null,
|
||||
private readonly getAllDriverRankings: () => DriverRankingEntry[],
|
||||
) {}
|
||||
@@ -65,6 +67,7 @@ export class GetProfileOverviewUseCase {
|
||||
const finishDistribution = this.buildFinishDistribution(statsAdapter);
|
||||
const teamMemberships = await this.buildTeamMemberships(driver.id, teams as Team[]);
|
||||
const socialSummary = this.buildSocialSummary(friends as Driver[]);
|
||||
const extendedProfile = this.driverExtendedProfileProvider.getExtendedProfile(driverId);
|
||||
|
||||
const outputPort: ProfileOverviewOutputPort = {
|
||||
driver: driverSummary,
|
||||
@@ -72,7 +75,7 @@ export class GetProfileOverviewUseCase {
|
||||
finishDistribution,
|
||||
teamMemberships,
|
||||
socialSummary,
|
||||
extendedProfile: null,
|
||||
extendedProfile,
|
||||
};
|
||||
|
||||
return Result.ok(outputPort);
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import type { ILeagueWalletRepository } from '../../domain/repositories/ILeagueWalletRepository';
|
||||
import type { ITransactionRepository } from '../../domain/repositories/ITransactionRepository';
|
||||
import type { WithdrawFromLeagueWalletOutputPort } from '../ports/output/WithdrawFromLeagueWalletOutputPort';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import { Money } from '../../domain/value-objects/Money';
|
||||
import { Transaction } from '../../domain/entities/league-wallet/Transaction';
|
||||
import { TransactionId } from '../../domain/entities/league-wallet/TransactionId';
|
||||
import { LeagueWalletId } from '../../domain/entities/league-wallet/LeagueWalletId';
|
||||
|
||||
export interface WithdrawFromLeagueWalletUseCaseParams {
|
||||
leagueId: string;
|
||||
amount: number;
|
||||
currency: string;
|
||||
seasonId: string;
|
||||
destinationAccount: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Case for withdrawing from league wallet.
|
||||
*/
|
||||
export class WithdrawFromLeagueWalletUseCase {
|
||||
constructor(
|
||||
private readonly leagueWalletRepository: ILeagueWalletRepository,
|
||||
private readonly transactionRepository: ITransactionRepository,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
params: WithdrawFromLeagueWalletUseCaseParams,
|
||||
): Promise<Result<WithdrawFromLeagueWalletOutputPort, ApplicationErrorCode<'REPOSITORY_ERROR' | 'INSUFFICIENT_BALANCE' | 'WITHDRAWAL_NOT_ALLOWED'>>> {
|
||||
try {
|
||||
const wallet = await this.leagueWalletRepository.findByLeagueId(params.leagueId);
|
||||
if (!wallet) {
|
||||
return Result.err({ code: 'REPOSITORY_ERROR', message: 'Wallet not found' });
|
||||
}
|
||||
|
||||
// Check if withdrawal is allowed (for now, always false as per mock)
|
||||
if (!wallet.canWithdraw(Money.create(params.amount, params.currency))) {
|
||||
return Result.err({ code: 'INSUFFICIENT_BALANCE', message: 'Insufficient balance for withdrawal' });
|
||||
}
|
||||
|
||||
// For now, always block withdrawal
|
||||
return Result.err({ code: 'WITHDRAWAL_NOT_ALLOWED', message: 'Season 2 is still active. Withdrawals are available after season completion.' });
|
||||
|
||||
// If allowed, create transaction and update wallet
|
||||
// const transactionId = TransactionId.create(`txn-${Date.now()}`);
|
||||
// const transaction = Transaction.create({
|
||||
// id: transactionId,
|
||||
// walletId: LeagueWalletId.create(wallet.id.toString()),
|
||||
// type: 'withdrawal',
|
||||
// amount: Money.create(params.amount, params.currency),
|
||||
// description: `Bank Transfer - ${params.seasonId} Payout`,
|
||||
// metadata: { destinationAccount: params.destinationAccount, seasonId: params.seasonId },
|
||||
// });
|
||||
|
||||
// const updatedWallet = wallet.withdrawFunds(Money.create(params.amount, params.currency), transactionId.toString());
|
||||
|
||||
// await this.transactionRepository.create(transaction);
|
||||
// await this.leagueWalletRepository.update(updatedWallet);
|
||||
|
||||
// return Result.ok({ success: true });
|
||||
} catch {
|
||||
return Result.err({ code: 'REPOSITORY_ERROR', message: 'Failed to process withdrawal' });
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user