fix data flow issues

This commit is contained in:
2025-12-19 21:58:03 +01:00
parent 94fc538f44
commit ec177a75ce
37 changed files with 1336 additions and 534 deletions

View File

@@ -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;
}

View File

@@ -0,0 +1,4 @@
export interface DriverRatingProvider {
getRating(driverId: string): number | null;
getRatings(driverIds: string[]): Map<string, number>;
}

View File

@@ -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[];
}

View File

@@ -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;
}

View File

@@ -0,0 +1,4 @@
export interface WithdrawFromLeagueWalletOutputPort {
success: boolean;
message?: string;
}

View 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' });
}
}
}

View File

@@ -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);

View File

@@ -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' });
}
}
}