Files
CMS/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandHandler.cs
T
masoodafar-web 2a569a024f
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m36s
feat: Implement Magic Wallet functionality
- Added ClubMembershipCycle entity and DbSet to IApplicationDbContext.
- Updated VAT rate from 9% to 10% in VatCalculator and related areas.
- Introduced Magic Wallet settings in SystemConstants.
- Enhanced UserWallet entity to support Magic Wallet features.
- Updated TransactionType enum to include Magic Wallet transactions.
- Configured UserWallet to handle Magic Wallet properties in ApplicationDbContext.
- Implemented OrmCommissionCalculationStrategy to exclude Magic Wallet users from commission calculations.
- Added Protobuf definitions for Magic Wallet methods and responses.
- Created PaymentCallbackController endpoint for handling Magic Wallet charge callbacks.
- Updated UserOrderService to manage Magic Wallet state transitions.
- Developed UserWalletService to support Magic Wallet operations.
- Created ChargeMagicWalletCommand and its handler for initiating Magic Wallet charges.
- Implemented VerifyMagicWalletChargeCommand and handler for payment verification.
- Added validation for ChargeMagicWalletCommand.
- Established ClubMembershipCycle configuration for EF Core.
- Introduced WalletMode enum to differentiate between Normal and Magic modes.
2026-02-21 19:50:28 +03:30

363 lines
14 KiB
C#

using CMSMicroservice.Application.Common.Exceptions;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Application.Common.Models;
using CMSMicroservice.Domain.Common;
using CMSMicroservice.Domain.Entities;
using CMSMicroservice.Domain.Entities.Club;
using CMSMicroservice.Domain.Entities.Commission;
using CMSMicroservice.Domain.Entities.History;
using CMSMicroservice.Domain.Enums;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System.Globalization;
namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.ActivateClubMembership;
public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClubMembershipCommand, bool>
{
private readonly IApplicationDbContext _context;
private readonly ICurrentUserService _currentUser;
private readonly IWeekDefinitionRepository _weekRepository;
private readonly ILogger<ActivateClubMembershipCommandHandler> _logger;
public ActivateClubMembershipCommandHandler(
IApplicationDbContext context,
ICurrentUserService currentUser,
IWeekDefinitionRepository weekRepository,
ILogger<ActivateClubMembershipCommandHandler> logger)
{
_context = context;
_currentUser = currentUser;
_weekRepository = weekRepository;
_logger = logger;
}
public async Task<bool> Handle(
ActivateClubMembershipCommand request,
CancellationToken cancellationToken)
{
try
{
_logger.LogInformation(
"Activating club membership for UserId: {UserId}",
request.UserId
);
// 1. بررسی کاربر
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);
}
// 2. بررسی اینکه پکیج خریده باشد
if (user.PackagePurchaseMethod == PackagePurchaseMethod.None)
{
_logger.LogWarning(
"User {UserId} has not purchased golden package yet",
request.UserId
);
throw new BadRequestException(
"برای فعالسازی باشگاه مشتریان ابتدا باید پکیج طلایی خریداری کنید"
);
}
// 3. بررسی موجودی کیف پول
var wallet = await _context.UserWallets
.FirstOrDefaultAsync(w => w.UserId == user.Id, cancellationToken);
if (wallet == null)
{
_logger.LogError("Wallet not found for UserId: {UserId}", request.UserId);
throw new NotFoundException("کیف پول کاربر یافت نشد");
}
if (wallet.Balance < SystemConstants.BasePackageAmount)
{
_logger.LogWarning(
"User {UserId} has insufficient balance: {Balance}",
request.UserId,
wallet.Balance
);
throw new BadRequestException(
$"برای فعالسازی باشگاه مشتریان باید حداقل {SystemConstants.BasePackageAmount:N0} ریال موجودی اصلی داشته باشید"
);
}
// 4. پیدا کردن UserOrder با PackageId
var packageOrder = await _context.UserOrders
.Include(o => o.Transaction)
.Where(o =>
o.UserId == user.Id &&
o.PackageId != null &&
o.PaymentStatus == PaymentStatus.Success)
.OrderByDescending(o => o.Created)
.FirstOrDefaultAsync(cancellationToken);
if (packageOrder == null)
{
_logger.LogWarning(
"No successful package order found for UserId: {UserId}",
request.UserId
);
throw new NotFoundException("سفارش پکیج طلایی یافت نشد");
}
// 5. بررسی Transaction
if (packageOrder.Transaction == null)
{
_logger.LogError(
"Transaction not found for OrderId: {OrderId}",
packageOrder.Id
);
throw new NotFoundException("تراکنش مربوط به سفارش یافت نشد");
}
var transaction = packageOrder.Transaction;
if (transaction.Type != TransactionType.DepositIpg &&
transaction.Type != TransactionType.DepositExternal1)
{
_logger.LogWarning(
"Invalid transaction type for OrderId {OrderId}: {Type}",
packageOrder.Id,
transaction.Type
);
throw new BadRequestException(
"تراکنش معتبر برای فعالسازی باشگاه یافت نشد"
);
}
// 6. بررسی عضویت فعلی
var existingMembership = await _context.ClubMemberships
.FirstOrDefaultAsync(c => c.UserId == user.Id, cancellationToken);
// 6.1. دریافت مبلغ هدیه و هزینه فعالسازی از SystemConstants
long giftValue = SystemConstants.ClubMembershipGiftValue;
long activationFeeValue = SystemConstants.ClubActivationFee;
_logger.LogInformation(
"Using Club.MembershipGiftValue: {GiftValue}, Club.ActivationFee: {ActivationFee}",
giftValue, activationFeeValue
);
ClubMembership entity;
bool isNewMembership = existingMembership == null;
var activationDate = DateTime.Now;
if (isNewMembership)
{
// ایجاد عضویت جدید
entity = new ClubMembership
{
UserId = user.Id,
IsActive = true,
ActivatedAt = activationDate,
InitialContribution =activationFeeValue,
GiftValue = giftValue, // مقدار از تنظیمات
TotalEarned = 0,
PurchaseMethod = user.PackagePurchaseMethod
};
_context.ClubMemberships.Add(entity);
_logger.LogInformation(
"Created new club membership for UserId {UserId} via {Method}, GiftValue: {GiftValue}",
user.Id,
user.PackagePurchaseMethod,
giftValue
);
}
else
{
if (existingMembership.IsActive)
{
_logger.LogInformation(
"User {UserId} is already an active club member",
user.Id
);
return true;
}
// فعال‌سازی مجدد — ActivatedAt حفظ میشه (overwrite نمیشه)
entity = existingMembership;
entity.IsActive = true;
entity.PurchaseMethod = user.PackagePurchaseMethod;
_context.ClubMemberships.Update(entity);
_logger.LogInformation(
"Reactivated club membership for UserId {UserId}",
user.Id
);
}
await _context.SaveChangesAsync(cancellationToken);
// 6.5. ایجاد ClubMembershipCycle — هر خرید پکیج یک دور جدید
var previousCycles = await _context.ClubMembershipCycles
.Where(c => c.UserId == user.Id && c.IsCurrentCycle)
.ToListAsync(cancellationToken);
foreach (var prevCycle in previousCycles)
{
prevCycle.IsCurrentCycle = false;
}
var maxCycleNumber = await _context.ClubMembershipCycles
.Where(c => c.ClubMembershipId == entity.Id)
.MaxAsync(c => (int?)c.CycleNumber, cancellationToken) ?? 0;
var newCycle = new ClubMembershipCycle
{
UserId = user.Id,
ClubMembershipId = entity.Id,
CycleNumber = maxCycleNumber + 1,
PackagePurchasedAt = activationDate,
PurchaseMethod = user.PackagePurchaseMethod,
PackageAmount = SystemConstants.BasePackageAmount,
IsCurrentCycle = true
};
_context.ClubMembershipCycles.Add(newCycle);
await _context.SaveChangesAsync(cancellationToken);
_logger.LogInformation(
"Created ClubMembershipCycle #{CycleNumber} for UserId {UserId}, MembershipId {MembershipId}",
newCycle.CycleNumber,
user.Id,
entity.Id
);
// 7. ثبت تاریخچه
var history = new ClubMembershipHistory
{
ClubMembershipId = entity.Id,
UserId = entity.UserId,
OldIsActive = !isNewMembership && !existingMembership!.IsActive,
NewIsActive = true,
Action = ClubMembershipAction.Activated,
Reason = isNewMembership
? $"Initial activation via {user.PackagePurchaseMethod}"
: $"Reactivated via {user.PackagePurchaseMethod}",
PerformedBy = _currentUser.GetPerformedBy()
};
_context.ClubMembershipHistories.Add(history);
await _context.SaveChangesAsync(cancellationToken);
// ⭐ 8. اضافه کردن مبلغ به Pool هفته جاری
var currentWeekDefinitionId = GetCurrentWeekDefinitionId();
var weeklyPool = await _context.WeeklyCommissionPools
.FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId, cancellationToken);
if (weeklyPool == null)
{
// ایجاد Pool جدید برای این هفته
weeklyPool = new WeeklyCommissionPool
{
WeekDefinitionId = currentWeekDefinitionId,
TotalPoolAmount = activationFeeValue, // مبلغ هدیه به Pool اضافه میشه
TotalBalances = 0, // در CalculateWeeklyBalances محاسبه میشه
ValuePerBalance = 0, // در CalculateWeeklyCommissionPool محاسبه میشه
IsCalculated = false,
CalculatedAt = null
};
await _context.WeeklyCommissionPools.AddAsync(weeklyPool, cancellationToken);
_logger.LogInformation(
"Created new WeeklyCommissionPool for WeekDefinitionId={WeekDefinitionId} with initial amount: {Amount}",
currentWeekDefinitionId,
giftValue
);
}
else
{
// اضافه کردن به Pool موجود
weeklyPool.TotalPoolAmount += activationFeeValue;
_context.WeeklyCommissionPools.Update(weeklyPool);
_logger.LogInformation(
"Added {Amount} to existing WeeklyCommissionPool for WeekDefinitionId={WeekDefinitionId}. New total: {NewTotal}",
activationFeeValue,
currentWeekDefinitionId,
weeklyPool.TotalPoolAmount
);
}
await _context.SaveChangesAsync(cancellationToken);
// 9. اضافه کردن ویژگی‌های باشگاه برای کاربر (فقط برای عضویت جدید)
if (isNewMembership)
{
var featureIds = ClubFeatureTypeExtensions.GetAllFeatureIds();
var clubFeatures = await _context.ClubFeatures
.Where(f => !f.IsDeleted && featureIds.Contains(f.Id))
.ToListAsync(cancellationToken);
if (clubFeatures.Any())
{
var userClubFeatures = clubFeatures.Select(feature => new UserClubFeature
{
UserId = user.Id,
ClubMembershipId = entity.Id,
ClubFeatureId = feature.Id,
GrantedAt = activationDate,
IsActive = true,
Notes = "اعطا شده به‌طور خودکار هنگام فعالسازی"
}).ToList(); _context.UserClubFeatures.AddRange(userClubFeatures);
await _context.SaveChangesAsync(cancellationToken);
_logger.LogInformation(
"Granted {Count} club features to UserId {UserId}",
clubFeatures.Count,
user.Id
);
}
else
{
_logger.LogWarning(
"No club features found to grant to UserId {UserId}",
user.Id
);
}
}
_logger.LogInformation(
"Club membership activated successfully. UserId: {UserId}, MembershipId: {MembershipId}",
user.Id,
entity.Id
);
return true;
}
catch (Exception ex)
{
_logger.LogError(
ex,
"Error in ActivateClubMembershipCommand for UserId: {UserId}",
request.UserId
);
throw;
}
}
/// <summary>
/// دریافت شناسه تعریف هفته جاری
/// </summary>
private long GetCurrentWeekDefinitionId()
{
var week = _weekRepository.GetCurrentWeek();
if (week == null)
{
throw new InvalidOperationException("هفته جاری در سیستم تعریف نشده است");
}
return week.Id;
}
}