🐛 Fix recieve fund save db together to prevent cocurrent db save

This commit is contained in:
2025-11-17 00:49:10 +08:00
parent 713777cd8a
commit 260b3e7bc6

View File

@@ -113,7 +113,8 @@ public class PaymentService(
decimal amount, decimal amount,
string? remarks = null, string? remarks = null,
Shared.Models.TransactionType type = Shared.Models.TransactionType.System, Shared.Models.TransactionType type = Shared.Models.TransactionType.System,
bool silent = false bool silent = false,
bool autoSave = true
) )
{ {
if (payerWalletId == null && payeeWalletId == null) if (payerWalletId == null && payeeWalletId == null)
@@ -163,7 +164,10 @@ public class PaymentService(
} }
db.PaymentTransactions.Add(transaction); db.PaymentTransactions.Add(transaction);
await db.SaveChangesAsync(); if (autoSave)
{
await db.SaveChangesAsync();
}
if (!silent) if (!silent)
await NotifyNewTransaction(transaction, payerWallet, payeeWallet); await NotifyNewTransaction(transaction, payerWallet, payeeWallet);
@@ -171,7 +175,8 @@ public class PaymentService(
return transaction; return transaction;
} }
private async Task NotifyNewTransaction(SnWalletTransaction transaction, SnWallet? payerWallet, SnWallet? payeeWallet) private async Task NotifyNewTransaction(SnWalletTransaction transaction, SnWallet? payerWallet,
SnWallet? payeeWallet)
{ {
if (payerWallet is not null) if (payerWallet is not null)
{ {
@@ -483,7 +488,7 @@ public class PaymentService(
{ {
if (totalAmount <= 0) if (totalAmount <= 0)
throw new ArgumentException("Total amount must be positive"); throw new ArgumentException("Total amount must be positive");
// Check creator has sufficient funds // Check creator has sufficient funds
var creatorWallet = await wat.GetWalletAsync(creatorAccountId); var creatorWallet = await wat.GetWalletAsync(creatorAccountId);
if (creatorWallet == null) if (creatorWallet == null)
@@ -531,10 +536,10 @@ public class PaymentService(
// Load the fund with account data including profiles // Load the fund with account data including profiles
var createdFund = await db.WalletFunds var createdFund = await db.WalletFunds
.Include(f => f.Recipients) .Include(f => f.Recipients)
.ThenInclude(r => r.RecipientAccount) .ThenInclude(r => r.RecipientAccount)
.ThenInclude(a => a.Profile) .ThenInclude(a => a.Profile)
.Include(f => f.CreatorAccount) .Include(f => f.CreatorAccount)
.ThenInclude(a => a.Profile) .ThenInclude(a => a.Profile)
.FirstOrDefaultAsync(f => f.Id == fund.Id); .FirstOrDefaultAsync(f => f.Id == fund.Id);
return createdFund!; return createdFund!;
@@ -593,19 +598,17 @@ public class PaymentService(
return Math.Max(amountPerSplit, 0.01m); // Minimum 0.01 per claim return Math.Max(amountPerSplit, 0.01m); // Minimum 0.01 per claim
} }
// For closed mode funds: use split type calculation // For closed mode funds: use split type calculation
else
{
var unclaimedRecipients = fund.Recipients.Count(r => !r.IsReceived);
if (unclaimedRecipients == 0)
return 0;
return fund.SplitType switch var unclaimedRecipients = fund.Recipients.Count(r => !r.IsReceived);
{ if (unclaimedRecipients == 0)
Shared.Models.FundSplitType.Even => SplitEvenly(fund.RemainingAmount, unclaimedRecipients)[0], return 0;
Shared.Models.FundSplitType.Random => SplitRandomly(fund.RemainingAmount, unclaimedRecipients)[0],
_ => throw new ArgumentException("Invalid split type") return fund.SplitType switch
}; {
} Shared.Models.FundSplitType.Even => SplitEvenly(fund.RemainingAmount, unclaimedRecipients)[0],
Shared.Models.FundSplitType.Random => SplitRandomly(fund.RemainingAmount, unclaimedRecipients)[0],
_ => throw new ArgumentException("Invalid split type")
};
} }
public async Task<SnWalletTransaction> ReceiveFundAsync(Guid recipientAccountId, Guid fundId) public async Task<SnWalletTransaction> ReceiveFundAsync(Guid recipientAccountId, Guid fundId)
@@ -627,7 +630,7 @@ public class PaymentService(
throw new InvalidOperationException("Fund is no longer available"); throw new InvalidOperationException("Fund is no longer available");
var recipient = fund.Recipients.FirstOrDefault(r => r.RecipientAccountId == recipientAccountId); var recipient = fund.Recipients.FirstOrDefault(r => r.RecipientAccountId == recipientAccountId);
// Handle open mode fund - create recipient if not exists // Handle open mode fund - create recipient if not exists
if (recipient is null && fund.IsOpen) if (recipient is null && fund.IsOpen)
{ {
@@ -682,7 +685,8 @@ public class PaymentService(
amount: recipient.Amount, amount: recipient.Amount,
remarks: $"Received fund portion from {fund.CreatorAccountId}", remarks: $"Received fund portion from {fund.CreatorAccountId}",
type: Shared.Models.TransactionType.System, type: Shared.Models.TransactionType.System,
silent: true silent: true,
autoSave: false
); );
// Mark as received // Mark as received
@@ -692,18 +696,16 @@ public class PaymentService(
// Update fund status // Update fund status
if (fund.IsOpen) if (fund.IsOpen)
{ {
if (fund.RemainingAmount <= 0) fund.Status = fund.RemainingAmount <= 0
fund.Status = Shared.Models.FundStatus.FullyReceived; ? Shared.Models.FundStatus.FullyReceived
else : Shared.Models.FundStatus.PartiallyReceived;
fund.Status = Shared.Models.FundStatus.PartiallyReceived;
} }
else else
{ {
var allReceived = fund.Recipients.All(r => r.IsReceived); var allReceived = fund.Recipients.All(r => r.IsReceived);
if (allReceived) fund.Status = allReceived
fund.Status = Shared.Models.FundStatus.FullyReceived; ? Shared.Models.FundStatus.FullyReceived
else : Shared.Models.FundStatus.PartiallyReceived;
fund.Status = Shared.Models.FundStatus.PartiallyReceived;
} }
await db.SaveChangesAsync(); await db.SaveChangesAsync();
@@ -724,7 +726,8 @@ public class PaymentService(
var expiredFunds = await db.WalletFunds var expiredFunds = await db.WalletFunds
.Include(f => f.Recipients) .Include(f => f.Recipients)
.Where(f => f.Status == Shared.Models.FundStatus.Created || f.Status == Shared.Models.FundStatus.PartiallyReceived) .Where(f => f.Status == Shared.Models.FundStatus.Created ||
f.Status == Shared.Models.FundStatus.PartiallyReceived)
.Where(f => f.ExpiredAt < now) .Where(f => f.ExpiredAt < now)
.ToListAsync(); .ToListAsync();
@@ -763,10 +766,10 @@ public class PaymentService(
{ {
var fund = await db.WalletFunds var fund = await db.WalletFunds
.Include(f => f.Recipients) .Include(f => f.Recipients)
.ThenInclude(r => r.RecipientAccount) .ThenInclude(r => r.RecipientAccount)
.ThenInclude(a => a.Profile) .ThenInclude(a => a.Profile)
.Include(f => f.CreatorAccount) .Include(f => f.CreatorAccount)
.ThenInclude(a => a.Profile) .ThenInclude(a => a.Profile)
.FirstOrDefaultAsync(f => f.Id == fundId); .FirstOrDefaultAsync(f => f.Id == fundId);
if (fund == null) if (fund == null)
@@ -775,7 +778,8 @@ public class PaymentService(
return fund; return fund;
} }
public async Task<WalletOverview> GetWalletOverviewAsync(Guid accountId, DateTime? startDate = null, DateTime? endDate = null) public async Task<WalletOverview> GetWalletOverviewAsync(Guid accountId, DateTime? startDate = null,
DateTime? endDate = null)
{ {
var wallet = await wat.GetWalletAsync(accountId); var wallet = await wat.GetWalletAsync(accountId);
if (wallet == null) if (wallet == null)
@@ -876,4 +880,4 @@ public class CurrencySummary
public decimal Income { get; set; } public decimal Income { get; set; }
public decimal Spending { get; set; } public decimal Spending { get; set; }
public decimal Net { get; set; } public decimal Net { get; set; }
} }