Skip to content

Commit

Permalink
Merge pull request #62 from EasyAbp/account-group-balance-range
Browse files Browse the repository at this point in the history
Range of valid balance values for account groups
  • Loading branch information
gdlcf88 authored Jun 21, 2023
2 parents 686329d + 33ecab8 commit 8d50fea
Show file tree
Hide file tree
Showing 12 changed files with 191 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class AccountAppService : ReadOnlyAppService<Account, AccountDto, Guid, G
private readonly IDistributedEventBus _distributedEventBus;
private readonly ITransactionRepository _transactionRepository;
private readonly IAccountRepository _repository;

public AccountAppService(
IAccountGroupConfigurationProvider accountGroupConfigurationProvider,
IOptions<PaymentServicePrepaymentOptions> options,
Expand Down Expand Up @@ -79,7 +79,7 @@ public override async Task<PagedResultDto<AccountDto>> GetListAsync(GetAccountLi
}

var allAccountGroupNames = _options.AccountGroups.GetAutoCreationAccountGroupNames();

var missingAccountGroupNames =
allAccountGroupNames.Except(result.Items.Select(x => x.AccountGroupName)).ToArray();

Expand Down Expand Up @@ -112,11 +112,11 @@ public virtual async Task<AccountDto> ChangeBalanceAsync(Guid id, ChangeBalanceI
account.Balance);

await _transactionRepository.InsertAsync(transaction, true);
account.ChangeBalance(input.ChangedBalance);

account.ChangeBalance(configuration, input.ChangedBalance);

await _repository.UpdateAsync(account, true);

return await MapToGetOutputDtoAsync(account);
}

Expand All @@ -125,10 +125,12 @@ public virtual async Task<AccountDto> ChangeLockedBalanceAsync(Guid id, ChangeLo
{
var account = await _repository.GetAsync(id);

account.ChangeLockedBalance(input.ChangedLockedBalance);
var configuration = _accountGroupConfigurationProvider.Get(account.AccountGroupName);

account.ChangeLockedBalance(configuration, input.ChangedLockedBalance);

await _repository.UpdateAsync(account, true);

return await MapToGetOutputDtoAsync(account);
}

Expand All @@ -146,7 +148,7 @@ public virtual async Task TopUpAsync(Guid id, TopUpInput input)
{
throw new TopUpIsAlreadyInProgressException();
}

var configuration = _accountGroupConfigurationProvider.Get(account.AccountGroupName);

await _distributedEventBus.PublishAsync(new CreatePaymentEto(
Expand Down Expand Up @@ -182,4 +184,4 @@ await accountWithdrawalManager.StartWithdrawalAsync(account, input.WithdrawalMet
input.ExtraProperties);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,25 @@
public static class PrepaymentConsts
{
public const decimal AccountMinBalance = decimal.Zero;

public const decimal AccountMaxBalance = 999999999999.99999999m;

public const decimal AccountMinLockedBalance = decimal.Zero;

public const decimal AccountMaxLockedBalance = AccountMaxBalance;

public const string ManualOperationPaymentMethod = "ManualOperation";

public const string ChangeBalanceActionName = "ChangeBalance";

public const string ChangeBalancePaymentMethod = ManualOperationPaymentMethod;

public const string PaymentActionName = "Payment";

public const string RefundActionName = "Refund";

public const string TopUpActionName = "TopUp";

public const string WithdrawalActionName = "Withdrawal";

public const string TopUpPaymentItemType = "EasyAbpPaymentServicePrepaymentTopUp";

public const string PaymentAccountIdPropertyName = "AccountId";
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using EasyAbp.PaymentService.Prepayment.Options.AccountGroups;
using JetBrains.Annotations;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities.Auditing;
Expand All @@ -9,20 +10,20 @@ namespace EasyAbp.PaymentService.Prepayment.Accounts
public class Account : FullAuditedAggregateRoot<Guid>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }

[NotNull]
public virtual string AccountGroupName { get; protected set; }

public virtual Guid UserId { get; protected set; }

public virtual decimal Balance { get; protected set; }

public virtual decimal LockedBalance { get; protected set; }

public virtual Guid? PendingTopUpPaymentId { get; protected set; }

public virtual Guid? PendingWithdrawalRecordId { get; protected set; }

public virtual decimal PendingWithdrawalAmount { get; protected set; }

protected Account()
Expand All @@ -43,44 +44,26 @@ public Account(Guid id,
LockedBalance = lockedBalance;
}

public void ChangeBalance(decimal changedBalance)
public void ChangeBalance(AccountGroupConfiguration config, decimal changedBalance)
{
var newBalance = decimal.Add(Balance, changedBalance);

if (!newBalance.IsBetween(PrepaymentConsts.AccountMinBalance, PrepaymentConsts.AccountMaxBalance))
{
throw new AmountOverflowException(PrepaymentConsts.AccountMinBalance,
PrepaymentConsts.AccountMaxBalance);
}

if (newBalance < LockedBalance)
{
throw new LockedBalanceIsGreaterThenBalanceException(LockedBalance, newBalance);
}
CheckBalanceValue(config, newBalance, LockedBalance);

Balance = newBalance;
}

public void ChangeLockedBalance(decimal changedLockedBalance, bool ignorePendingWithdrawalAmount = false)

public void ChangeLockedBalance(AccountGroupConfiguration config, decimal changedLockedBalance,
bool ignorePendingWithdrawalAmount = false)
{
var newLockedBalance = decimal.Add(LockedBalance, changedLockedBalance);

if (Balance < newLockedBalance)
{
throw new LockedBalanceIsGreaterThenBalanceException(newLockedBalance, Balance);
}

if (!newLockedBalance.IsBetween(PrepaymentConsts.AccountMinLockedBalance,
PrepaymentConsts.AccountMaxLockedBalance))
{
throw new AmountOverflowException(PrepaymentConsts.AccountMinLockedBalance,
PrepaymentConsts.AccountMaxLockedBalance);
}

CheckBalanceValue(config, Balance, newLockedBalance);

if (!ignorePendingWithdrawalAmount)
{
var pendingWithdrawalAmount = PendingWithdrawalAmount;

if (newLockedBalance < pendingWithdrawalAmount)
{
throw new LockedBalanceIsLessThenPendingWithdrawalAmountException(newLockedBalance,
Expand All @@ -91,60 +74,81 @@ public void ChangeLockedBalance(decimal changedLockedBalance, bool ignorePending
LockedBalance = newLockedBalance;
}

private static void CheckBalanceValue(AccountGroupConfiguration config, decimal balance, decimal lockedBalance)
{
var minBalance = config.AccountMinBalance ?? PrepaymentConsts.AccountMinBalance;
var maxBalance = config.AccountMaxBalance ?? PrepaymentConsts.AccountMaxBalance;

if (!balance.IsBetween(minBalance, maxBalance))
{
throw new AmountOverflowException("balance", minBalance, maxBalance);
}

if (lockedBalance < decimal.Zero)
{
throw new AmountOverflowException("locked balance", decimal.Zero, maxBalance);
}

if (balance - minBalance < lockedBalance)
{
throw new InsufficientBalanceToLockException(lockedBalance, balance);
}
}

public void SetPendingTopUpPaymentId(Guid? pendingTopUpPaymentId)
{
PendingTopUpPaymentId = pendingTopUpPaymentId;
}

public void StartWithdrawal(Guid pendingWithdrawalRecordId, decimal amount)
public void StartWithdrawal(AccountGroupConfiguration config, Guid pendingWithdrawalRecordId, decimal amount)
{
if (PendingWithdrawalRecordId.HasValue || PendingWithdrawalAmount != decimal.Zero)
{
throw new WithdrawalIsAlreadyInProgressException();
}
ChangeLockedBalance(amount);

ChangeLockedBalance(config, amount);

SetPendingWithdrawalRecordId(pendingWithdrawalRecordId);
SetPendingWithdrawalAmount(amount);
}
public void CompleteWithdrawal()

public void CompleteWithdrawal(AccountGroupConfiguration config)
{
var balanceToChange = -PendingWithdrawalAmount;

ClearPendingWithdrawal();
ChangeBalance(balanceToChange);
ClearPendingWithdrawal(config);

ChangeBalance(config, balanceToChange);
}
public void CancelWithdrawal()

public void CancelWithdrawal(AccountGroupConfiguration config)
{
ClearPendingWithdrawal();
ClearPendingWithdrawal(config);
}

private void ClearPendingWithdrawal()
private void ClearPendingWithdrawal(AccountGroupConfiguration config)
{
if (!PendingWithdrawalRecordId.HasValue || PendingWithdrawalAmount == decimal.Zero)
{
throw new WithdrawalInProgressNotFoundException();
}


ChangeLockedBalance(-PendingWithdrawalAmount, true);

ChangeLockedBalance(config, -1 * PendingWithdrawalAmount, true);

SetPendingWithdrawalRecordId(null);
SetPendingWithdrawalAmount(0m);
}

private void SetPendingWithdrawalRecordId(Guid? pendingWithdrawalRecordId)
{
PendingWithdrawalRecordId = pendingWithdrawalRecordId;
}

private void SetPendingWithdrawalAmount(decimal pendingWithdrawalAmount)
{
PendingWithdrawalAmount = pendingWithdrawalAmount;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public AccountWithdrawalManager(
public virtual async Task StartWithdrawalAsync(Account account, string withdrawalMethod, decimal amount,
ExtraPropertyDictionary inputExtraProperties)
{
var accountGroupConfiguration = _accountGroupConfigurationProvider.Get(account.AccountGroupName);

var withdrawalProvider = GetWithdrawalProvider(withdrawalMethod);

await CheckDailyWithdrawalAmountAsync(account, withdrawalMethod, amount);
Expand All @@ -45,7 +47,7 @@ public virtual async Task StartWithdrawalAsync(Account account, string withdrawa
withdrawalMethod,
amount);

account.StartWithdrawal(withdrawalRecord.Id, withdrawalRecord.Amount);
account.StartWithdrawal(accountGroupConfiguration, withdrawalRecord.Id, withdrawalRecord.Amount);

await _withdrawalRecordRepository.InsertAsync(withdrawalRecord, true);

Expand Down Expand Up @@ -84,8 +86,8 @@ private IAccountWithdrawalProvider GetWithdrawalProvider(string withdrawalMethod
_withdrawalMethodConfigurationProvider.Get(withdrawalMethodName)?.AccountWithdrawalProviderType ??
throw new UnknownWithdrawalMethodException(withdrawalMethodName);

return ServiceProvider.GetService(providerType) as IAccountWithdrawalProvider ??
throw new UnknownWithdrawalMethodException(withdrawalMethodName);
return LazyServiceProvider.LazyGetService(providerType) as IAccountWithdrawalProvider ??
throw new UnknownWithdrawalMethodException(withdrawalMethodName);
}

public virtual async Task CompleteWithdrawalAsync(Account account)
Expand All @@ -96,21 +98,21 @@ public virtual async Task CompleteWithdrawalAsync(Account account)
{
throw new WithdrawalInProgressNotFoundException();
}

var withdrawalRecord = await _withdrawalRecordRepository.GetAsync(withdrawalRecordId.Value);

var accountGroupConfiguration = _accountGroupConfigurationProvider.Get(account.AccountGroupName);

var withdrawalProvider = GetWithdrawalProvider(withdrawalRecord.WithdrawalMethod);

var originalBalance = account.Balance;

account.CompleteWithdrawal();
account.CompleteWithdrawal(accountGroupConfiguration);

withdrawalRecord.Complete(Clock.Now);

await _accountRepository.UpdateAsync(account, true);

await _withdrawalRecordRepository.UpdateAsync(withdrawalRecord, true);

var accountChangedBalance = -1 * withdrawalRecord.Amount;
Expand All @@ -120,14 +122,15 @@ public virtual async Task CompleteWithdrawalAsync(Account account)
null, accountGroupConfiguration.Currency, accountChangedBalance, originalBalance);

await _transactionRepository.InsertAsync(transaction, true);

await withdrawalProvider.OnCompleteWithdrawalAsync(account);

await withdrawalProvider.OnCompleteWithdrawalAsync(account);
}

public virtual async Task CancelWithdrawalAsync(Account account, string errorCode = null,
string errorMessage = null)
{
var accountGroupConfiguration = _accountGroupConfigurationProvider.Get(account.AccountGroupName);

var withdrawalRecordId = account.PendingWithdrawalRecordId;

if (!withdrawalRecordId.HasValue)
Expand All @@ -139,7 +142,7 @@ public virtual async Task CancelWithdrawalAsync(Account account, string errorCod

var withdrawalProvider = GetWithdrawalProvider(withdrawalRecord.WithdrawalMethod);

account.CancelWithdrawal();
account.CancelWithdrawal(accountGroupConfiguration);

withdrawalRecord.Cancel(Clock.Now, errorCode, errorMessage);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ namespace EasyAbp.PaymentService.Prepayment.Accounts
{
public class AmountOverflowException : BusinessException
{
public AmountOverflowException(decimal min, decimal max) : base(message: $"The amount should be greater than {min} and less then {max}")
public AmountOverflowException(decimal min, decimal max) : base(
message: $"The amount should be greater than {min} and less then {max}")
{
}

public AmountOverflowException(string field, decimal min, decimal max) : base(
message: $"The {field} should be greater than {min} and less then {max}")
{
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public async Task HandleEventAsync(ChangeAccountBalanceEto eventData)

await _transactionRepository.InsertAsync(transaction, true);

account.ChangeBalance(changedBalance);
account.ChangeBalance(configuration, changedBalance);

await _accountRepository.UpdateAsync(account, true);
}
Expand Down
Loading

0 comments on commit 8d50fea

Please sign in to comment.