427d2cce1d
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 6m45s
- Changed package purchase method from DirectPurchase to Manual in multiple command handlers to reflect new business logic. - Added Manual purchase method to PackagePurchaseMethod enum for clarity. - Introduced new RPC method GetCustomerPackagePurchaseRollup in Protobuf for summarizing customer package purchases. - Updated UserPackagePurchaseProfile and UserPackagePurchaseService to support new rollup functionality. - Bumped Protobuf project version to accommodate new features.
218 lines
8.8 KiB
C#
218 lines
8.8 KiB
C#
using CMSMicroservice.Application.ClubMembershipCQ.Commands.ActivateClubMembership;
|
|
using CMSMicroservice.Application.Common.Exceptions;
|
|
using CMSMicroservice.Application.Common.Interfaces;
|
|
using CMSMicroservice.Domain.Common;
|
|
using CMSMicroservice.Domain.Entities;
|
|
using CMSMicroservice.Domain.Entities.Payment;
|
|
using CMSMicroservice.Domain.Enums;
|
|
using MediatR;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace CMSMicroservice.Application.ManualPaymentCQ.Commands.CreateManualPayment;
|
|
|
|
public class CreateManualPaymentCommandHandler : IRequestHandler<CreateManualPaymentCommand, long>
|
|
{
|
|
private readonly IApplicationDbContext _context;
|
|
private readonly ICurrentUserService _currentUser;
|
|
private readonly ISender _sender;
|
|
private readonly ILogger<CreateManualPaymentCommandHandler> _logger;
|
|
|
|
public CreateManualPaymentCommandHandler(
|
|
IApplicationDbContext context,
|
|
ICurrentUserService currentUser,
|
|
ISender sender,
|
|
ILogger<CreateManualPaymentCommandHandler> logger)
|
|
{
|
|
_context = context;
|
|
_currentUser = currentUser;
|
|
_sender = sender;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<long> Handle(
|
|
CreateManualPaymentCommand request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
try
|
|
{
|
|
_logger.LogInformation(
|
|
"Creating manual membership payment for UserId: {UserId}, Type: {Type}",
|
|
request.UserId,
|
|
request.Type
|
|
);
|
|
|
|
// 1. بررسی Admin فعلی
|
|
var currentUserId = _currentUser.UserId;
|
|
if (string.IsNullOrEmpty(currentUserId))
|
|
{
|
|
throw new UnauthorizedAccessException("کاربر احراز هویت نشده است");
|
|
}
|
|
|
|
if (!long.TryParse(currentUserId, out var adminUserId))
|
|
{
|
|
throw new UnauthorizedAccessException("شناسه کاربر نامعتبر است");
|
|
}
|
|
|
|
// 2. بررسی وجود کاربر
|
|
var user = await _context.Users
|
|
.FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);
|
|
|
|
if (user == null)
|
|
{
|
|
_logger.LogWarning("User not found: {UserId}", request.UserId);
|
|
throw new NotFoundException(nameof(User), request.UserId);
|
|
}
|
|
|
|
// 3. پیدا کردن کیف پول
|
|
var wallet = await _context.UserWallets
|
|
.FirstOrDefaultAsync(w => w.UserId == request.UserId, cancellationToken);
|
|
|
|
if (wallet == null)
|
|
{
|
|
_logger.LogError("Wallet not found for UserId: {UserId}", request.UserId);
|
|
throw new NotFoundException($"کیف پول کاربر {request.UserId} یافت نشد");
|
|
}
|
|
|
|
// 4. بارگذاری پکیج انتخابشده (یا پکیج پایه اگر PackageId=0)
|
|
Domain.Entities.Package package;
|
|
if (request.PackageId > 0)
|
|
{
|
|
package = await _context.Packages
|
|
.FirstOrDefaultAsync(p => p.Id == request.PackageId && !p.IsDeleted, cancellationToken)
|
|
?? throw new NotFoundException($"پکیج با شناسه {request.PackageId} یافت نشد");
|
|
}
|
|
else
|
|
{
|
|
package = await _context.Packages
|
|
.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
|
|
?? throw new NotFoundException("پکیج پایه یافت نشد");
|
|
}
|
|
|
|
var balanceAmount = package.Price;
|
|
var discountBalanceAmount = (long)(package.Price * package.DiscountMultiplier);
|
|
|
|
|
|
// 5. ثبت تراکنش
|
|
var transaction = new Transaction
|
|
{
|
|
Amount = balanceAmount,
|
|
Description = $"عضویت دستی باشگاه مشتریان - {request.Description} - مرجع: {request.ReferenceNumber}",
|
|
PaymentStatus = PaymentStatus.Success,
|
|
PaymentDate = DateTime.Now,
|
|
RefId = request.ReferenceNumber,
|
|
Type = TransactionType.DepositExternal1
|
|
};
|
|
|
|
_context.Transactions.Add(transaction);
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
|
|
// 6. ایجاد ManualPayment با وضعیت Approved (بدون نیاز به تایید دو مرحلهای)
|
|
var manualPayment = new ManualPayment
|
|
{
|
|
UserId = request.UserId,
|
|
Amount = balanceAmount,
|
|
Type = request.Type,
|
|
Description = request.Description,
|
|
ReferenceNumber = request.ReferenceNumber,
|
|
ImagePath = request.ImagePath,
|
|
ImageDocumentId = request.ImageDocumentId,
|
|
Status = ManualPaymentStatus.Approved,
|
|
RequestedBy = adminUserId,
|
|
ApprovedBy = adminUserId,
|
|
ApprovedAt = DateTime.Now,
|
|
TransactionId = transaction.Id
|
|
};
|
|
|
|
_context.ManualPayments.Add(manualPayment);
|
|
|
|
// 7. اعمال تغییرات بر کیف پول
|
|
var oldBalance = wallet.Balance;
|
|
var oldDiscountBalance = wallet.DiscountBalance;
|
|
|
|
wallet.Balance += balanceAmount;
|
|
wallet.DiscountBalance += discountBalanceAmount;
|
|
|
|
// 8. ثبت لاگ کیف پول
|
|
var walletLog = new UserWalletHistory
|
|
{
|
|
WalletId = wallet.Id,
|
|
CurrentBalance = 0,
|
|
ChangeValue = balanceAmount,
|
|
CurrentNetworkBalance = wallet.NetworkBalance,
|
|
ChangeNerworkValue = 0,
|
|
CurrentDiscountBalance =0,
|
|
ChangeDiscountValue = discountBalanceAmount,
|
|
IsIncrease = true,
|
|
RefrenceId = transaction.Id,
|
|
PackageId = package.Id
|
|
};
|
|
|
|
await _context.UserWalletHistories.AddAsync(walletLog, cancellationToken);
|
|
|
|
// 9. تنظیم روش خرید پکیج + ثبت ledger خرید دستی
|
|
user.PackagePurchaseMethod = PackagePurchaseMethod.Manual;
|
|
|
|
_context.UserPackagePurchases.Add(new UserPackagePurchase
|
|
{
|
|
UserId = request.UserId,
|
|
PackageId = package.Id,
|
|
PurchaseMethod = PackagePurchaseMethod.Manual,
|
|
PurchasedAt = DateTime.Now,
|
|
Amount = balanceAmount,
|
|
TransactionId = transaction.Id
|
|
});
|
|
|
|
// 10. ذخیره همه تغییرات
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
|
|
// 11. فعالسازی باشگاه مشتریان — ساخت Cycle و آمادهسازی برای AcceptContract
|
|
// این مرحله تضمین میکند که:
|
|
// الف) ClubMembershipCycle ساخته شود تا والدین امتیاز «عضو جدید» بگیرند
|
|
// ب) Pool توسط AcceptContract (که کاربر در FO امضا میکند) شارژ شود
|
|
try
|
|
{
|
|
await _sender.Send(new ActivateClubMembershipCommand
|
|
{
|
|
UserId = request.UserId,
|
|
ForceActivation = true,
|
|
PackageId = package.Id
|
|
}, cancellationToken);
|
|
}
|
|
catch (Exception activateEx)
|
|
{
|
|
// عضویت باشگاه اختیاری است — خطا نباید پرداخت دستی را برگرداند
|
|
_logger.LogWarning(
|
|
activateEx,
|
|
"Club membership activation failed after manual payment for UserId {UserId} — payment recorded successfully",
|
|
request.UserId
|
|
);
|
|
}
|
|
|
|
_logger.LogInformation(
|
|
"Manual membership payment created successfully. " +
|
|
"ManualPaymentId: {Id}, UserId: {UserId}, TransactionId: {TransactionId}, " +
|
|
"Balance: {OldBalance} -> {NewBalance}, DiscountBalance: {OldDiscount} -> {NewDiscount}",
|
|
manualPayment.Id,
|
|
request.UserId,
|
|
transaction.Id,
|
|
oldBalance,
|
|
wallet.Balance,
|
|
oldDiscountBalance,
|
|
wallet.DiscountBalance
|
|
);
|
|
|
|
return manualPayment.Id;
|
|
}
|
|
catch (Exception ex) when (ex is not NotFoundException && ex is not UnauthorizedAccessException)
|
|
{
|
|
_logger.LogError(
|
|
ex,
|
|
"Error creating manual membership payment for UserId: {UserId}",
|
|
request.UserId
|
|
);
|
|
throw;
|
|
}
|
|
}
|
|
}
|