refactor racing use cases
This commit is contained in:
@@ -1,66 +1,142 @@
|
||||
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 type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
|
||||
import type { ILeagueWalletRepository } from '../../domain/repositories/ILeagueWalletRepository';
|
||||
import type { ITransactionRepository } from '../../domain/repositories/ITransactionRepository';
|
||||
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 {
|
||||
export type WithdrawFromLeagueWalletInput = {
|
||||
leagueId: string;
|
||||
requestedById: string;
|
||||
amount: number;
|
||||
currency: string;
|
||||
seasonId: string;
|
||||
destinationAccount: string;
|
||||
}
|
||||
currency: 'USD' | 'EUR' | 'GBP';
|
||||
reason?: string;
|
||||
};
|
||||
|
||||
export type WithdrawFromLeagueWalletResult = {
|
||||
leagueId: string;
|
||||
amount: Money;
|
||||
transactionId: string;
|
||||
walletBalanceAfter: Money;
|
||||
};
|
||||
|
||||
export type WithdrawFromLeagueWalletErrorCode =
|
||||
| 'LEAGUE_NOT_FOUND'
|
||||
| 'WALLET_NOT_FOUND'
|
||||
| 'INSUFFICIENT_FUNDS'
|
||||
| 'UNAUTHORIZED_WITHDRAWAL'
|
||||
| 'REPOSITORY_ERROR';
|
||||
|
||||
/**
|
||||
* Use Case for withdrawing from league wallet.
|
||||
*/
|
||||
export class WithdrawFromLeagueWalletUseCase {
|
||||
constructor(
|
||||
private readonly leagueWalletRepository: ILeagueWalletRepository,
|
||||
private readonly leagueRepository: ILeagueRepository,
|
||||
private readonly walletRepository: ILeagueWalletRepository,
|
||||
private readonly transactionRepository: ITransactionRepository,
|
||||
private readonly logger: Logger,
|
||||
private readonly output: UseCaseOutputPort<WithdrawFromLeagueWalletResult>,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
params: WithdrawFromLeagueWalletUseCaseParams,
|
||||
): Promise<Result<WithdrawFromLeagueWalletOutputPort, ApplicationErrorCode<'REPOSITORY_ERROR' | 'INSUFFICIENT_BALANCE' | 'WITHDRAWAL_NOT_ALLOWED'>>> {
|
||||
input: WithdrawFromLeagueWalletInput,
|
||||
): Promise<
|
||||
Result<
|
||||
void,
|
||||
ApplicationErrorCode<
|
||||
WithdrawFromLeagueWalletErrorCode,
|
||||
{
|
||||
message: string;
|
||||
}
|
||||
>
|
||||
>
|
||||
> {
|
||||
try {
|
||||
const wallet = await this.leagueWalletRepository.findByLeagueId(params.leagueId);
|
||||
const league = await this.leagueRepository.findById(input.leagueId);
|
||||
if (!league) {
|
||||
return Result.err({
|
||||
code: 'LEAGUE_NOT_FOUND',
|
||||
details: { message: `League with id ${input.leagueId} not found` },
|
||||
});
|
||||
}
|
||||
|
||||
const wallet = await this.walletRepository.findByLeagueId(input.leagueId);
|
||||
if (!wallet) {
|
||||
return Result.err({ code: 'REPOSITORY_ERROR', message: 'Wallet not found' });
|
||||
return Result.err({
|
||||
code: 'WALLET_NOT_FOUND',
|
||||
details: { message: `Wallet for league ${input.leagueId} 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' });
|
||||
if (league.ownerId.toString() !== input.requestedById) {
|
||||
return Result.err({
|
||||
code: 'UNAUTHORIZED_WITHDRAWAL',
|
||||
details: { message: 'Only the league owner can withdraw from the league wallet' },
|
||||
});
|
||||
}
|
||||
|
||||
// For now, always block withdrawal
|
||||
return Result.err({ code: 'WITHDRAWAL_NOT_ALLOWED', message: 'Season 2 is still active. Withdrawals are available after season completion.' });
|
||||
const withdrawalAmount = Money.create(input.amount, input.currency);
|
||||
|
||||
// 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 },
|
||||
// });
|
||||
if (!wallet.canWithdraw(withdrawalAmount)) {
|
||||
return Result.err({
|
||||
code: 'INSUFFICIENT_FUNDS',
|
||||
details: { message: 'Insufficient balance for withdrawal' },
|
||||
});
|
||||
}
|
||||
|
||||
// const updatedWallet = wallet.withdrawFunds(Money.create(params.amount, params.currency), transactionId.toString());
|
||||
const transactionId = TransactionId.create(`txn-${Date.now()}`);
|
||||
const transaction = Transaction.create({
|
||||
id: transactionId,
|
||||
walletId: LeagueWalletId.create(wallet.id.toString()),
|
||||
type: 'withdrawal',
|
||||
amount: withdrawalAmount,
|
||||
description: input.reason ?? 'League wallet withdrawal',
|
||||
metadata: {
|
||||
reason: input.reason,
|
||||
requestedById: input.requestedById,
|
||||
},
|
||||
completedAt: undefined,
|
||||
});
|
||||
|
||||
// await this.transactionRepository.create(transaction);
|
||||
// await this.leagueWalletRepository.update(updatedWallet);
|
||||
const updatedWallet = wallet.withdrawFunds(withdrawalAmount, transactionId.toString());
|
||||
|
||||
// return Result.ok({ success: true });
|
||||
} catch {
|
||||
return Result.err({ code: 'REPOSITORY_ERROR', message: 'Failed to process withdrawal' });
|
||||
await this.transactionRepository.create(transaction);
|
||||
await this.walletRepository.update(updatedWallet);
|
||||
|
||||
const result: WithdrawFromLeagueWalletResult = {
|
||||
leagueId: input.leagueId,
|
||||
amount: withdrawalAmount,
|
||||
transactionId: transactionId.toString(),
|
||||
walletBalanceAfter: updatedWallet.balance,
|
||||
};
|
||||
|
||||
this.output.present(result);
|
||||
|
||||
return Result.ok(undefined);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
'Failed to withdraw from league wallet',
|
||||
error instanceof Error ? error : undefined,
|
||||
{
|
||||
leagueId: input.leagueId,
|
||||
requestedById: input.requestedById,
|
||||
},
|
||||
);
|
||||
|
||||
return Result.err({
|
||||
code: 'REPOSITORY_ERROR',
|
||||
details: {
|
||||
message:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Failed to withdraw from league wallet',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user