Phase 2: Replace all deprecated SystemConstants with Package entity reads

- ActivateClubMembership: Load Package, set First/Last fields, PackageId on Cycle+Pool
- AcceptClubMembershipContract: Load Package, set First/Last fields, PackageId on Pool
- CalculateWeeklyBalances: Load base package, use package.MaxBalancesPerLeg/MaxNetworkLevel, set PackageId on NetworkWeeklyBalance
- ProcessUserPayouts: Load base package, use package.MaxNetworkLevel, set PackageId on UserCommissionPayout
- CheckAndProcessDayaLoans: Load base package (IsBasePackage), replace DayaLoanAmount→package.Price, fix hardcoded p.Id==4
- CreateManualPayment: Load base package, replace BasePackageAmount→package.Price, use DiscountMultiplier
- InitiateBasePackagePayment: Remove hardcoded BasePackageId=4, load from IsBasePackage, use package.Price/Id
- VerifyBasePackagePayment: Load Package from order.PackageId, use package.Price+DiscountMultiplier
- ChargeMagicWallet: Load user's package from current cycle, use package.MagicWalletMaxDeposit
- VerifyMagicWalletCharge: Load user's package, use package.MagicWalletMultiplier/MagicWalletMaxDeposit
- OrmCommissionCalculationStrategy: Load base package in both methods, set PackageId on Balance+Payout
- ConfigurationService: Inject IApplicationDbContext, read package values from DB instead of SystemConstants
- UserOrderService: Load user's package for EXIT trigger MagicWalletMaxDeposit check
- UserWalletService: Load user's package for GetMagicWalletStatus display

All 9 deprecated SystemConstants now have ZERO usages outside SystemConstants.cs
Build: 0 errors, 521 warnings (all pre-existing)
This commit is contained in:
masoodafar-web
2026-02-25 23:25:18 +03:30
parent a9cd2fd67a
commit 8e5c7c5205
14 changed files with 239 additions and 120 deletions
@@ -135,13 +135,25 @@ public class AcceptClubMembershipContractCommandHandler
}; };
await _context.UserContracts.AddAsync(userContract, cancellationToken); await _context.UserContracts.AddAsync(userContract, cancellationToken);
// 6. دریافت مقادیر از SystemConstants (استاتیک) // 6. بارگذاری پکیج از آخرین خرید کاربر یا پکیج پایه
long giftValue = SystemConstants.ClubMembershipGiftValue; var latestPurchase = await _context.UserPackagePurchases
long activationFeeValue = SystemConstants.ClubActivationFee; .Where(p => p.UserId == userId)
.OrderByDescending(p => p.PurchasedAt)
.FirstOrDefaultAsync(cancellationToken);
var package = latestPurchase != null
? await _context.Packages.FirstOrDefaultAsync(p => p.Id == latestPurchase.PackageId && !p.IsDeleted, cancellationToken)
: await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken);
if (package == null)
throw new NotFoundException("پکیج یافت نشد");
long giftValue = package.ActivationFee;
long activationFeeValue = package.ActivationFee;
_logger.LogInformation( _logger.LogInformation(
"Using Club.MembershipGiftValue: {GiftValue}, Club.ActivationFee: {ActivationFee}", "Using Package {PackageId} ({PackageName}): GiftValue={GiftValue}, ActivationFee={ActivationFee}",
giftValue, activationFeeValue package.Id, package.Title, giftValue, activationFeeValue
); );
// 7. فعال‌سازی باشگاه مشتریان // 7. فعال‌سازی باشگاه مشتریان
@@ -156,6 +168,10 @@ public class AcceptClubMembershipContractCommandHandler
UserId = user.Id, UserId = user.Id,
IsActive = true, IsActive = true,
ActivatedAt = activationDate, ActivatedAt = activationDate,
FirstActivationDate = activationDate,
FirstPackageId = package.Id,
LastActivationDate = activationDate,
LastPackageId = package.Id,
InitialContribution = activationFeeValue, InitialContribution = activationFeeValue,
GiftValue = giftValue, GiftValue = giftValue,
TotalEarned = 0, TotalEarned = 0,
@@ -175,6 +191,8 @@ public class AcceptClubMembershipContractCommandHandler
clubMembership = user.ClubMembership!; clubMembership = user.ClubMembership!;
clubMembership.IsActive = true; clubMembership.IsActive = true;
clubMembership.ActivatedAt = activationDate; clubMembership.ActivatedAt = activationDate;
clubMembership.LastActivationDate = activationDate;
clubMembership.LastPackageId = package.Id;
clubMembership.PurchaseMethod = user.PackagePurchaseMethod; clubMembership.PurchaseMethod = user.PackagePurchaseMethod;
_context.ClubMemberships.Update(clubMembership); _context.ClubMemberships.Update(clubMembership);
@@ -206,13 +224,14 @@ public class AcceptClubMembershipContractCommandHandler
// 9. اضافه کردن مبلغ به Pool هفته جاری // 9. اضافه کردن مبلغ به Pool هفته جاری
var currentWeekDefinitionId = GetCurrentWeekDefinitionId(); var currentWeekDefinitionId = GetCurrentWeekDefinitionId();
var weeklyPool = await _context.WeeklyCommissionPools var weeklyPool = await _context.WeeklyCommissionPools
.FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId, cancellationToken); .FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId && p.PackageId == package.Id, cancellationToken);
if (weeklyPool == null) if (weeklyPool == null)
{ {
weeklyPool = new WeeklyCommissionPool weeklyPool = new WeeklyCommissionPool
{ {
WeekDefinitionId = currentWeekDefinitionId, WeekDefinitionId = currentWeekDefinitionId,
PackageId = package.Id,
TotalPoolAmount = activationFeeValue, TotalPoolAmount = activationFeeValue,
TotalBalances = 0, TotalBalances = 0,
ValuePerBalance = 0, ValuePerBalance = 0,
@@ -54,6 +54,9 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
throw new NotFoundException(nameof(User), request.UserId); throw new NotFoundException(nameof(User), request.UserId);
} }
// متغیر پکیج — در هر دو مسیر (عادی و Force) مقداردهی می‌شود
Package package;
// 2-5: بررسی‌های مالی — در حالت ForceActivation (ادمین) رد می‌شود // 2-5: بررسی‌های مالی — در حالت ForceActivation (ادمین) رد می‌شود
if (!request.ForceActivation) if (!request.ForceActivation)
{ {
@@ -79,17 +82,8 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
throw new NotFoundException("کیف پول کاربر یافت نشد"); throw new NotFoundException("کیف پول کاربر یافت نشد");
} }
if (wallet.Balance < SystemConstants.BasePackageAmount) // NOTE: balance check uses package.Price — loaded after finding order (step 4)
{ // Moved to after package loading
_logger.LogWarning(
"User {UserId} has insufficient balance: {Balance}",
request.UserId,
wallet.Balance
);
throw new BadRequestException(
$"برای فعالسازی باشگاه مشتریان باید حداقل {SystemConstants.BasePackageAmount:N0} ریال موجودی اصلی داشته باشید"
);
}
// 3.5. بررسی وضعیت کیف‌پول جادویی — اگر در حالت Magic است، فعالسازی مجاز نیست // 3.5. بررسی وضعیت کیف‌پول جادویی — اگر در حالت Magic است، فعالسازی مجاز نیست
if (wallet.WalletMode == WalletMode.Magic) if (wallet.WalletMode == WalletMode.Magic)
@@ -146,6 +140,19 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
"تراکنش معتبر برای فعالسازی باشگاه یافت نشد" "تراکنش معتبر برای فعالسازی باشگاه یافت نشد"
); );
} }
// 5.5. بارگذاری پکیج از سفارش
package = await _context.Packages
.FirstOrDefaultAsync(p => p.Id == packageOrder.PackageId && !p.IsDeleted, cancellationToken)
?? throw new NotFoundException("پکیج یافت نشد");
// بررسی موجودی با مبلغ پکیج واقعی
if (wallet.Balance < package.Price)
{
throw new BadRequestException(
$"برای فعالسازی باشگاه مشتریان باید حداقل {package.Price:N0} ریال موجودی اصلی داشته باشید"
);
}
} }
else else
{ {
@@ -161,19 +168,24 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
_context.Users.Update(user); _context.Users.Update(user);
await _context.SaveChangesAsync(cancellationToken); await _context.SaveChangesAsync(cancellationToken);
} }
// بارگذاری پکیج پایه برای فعال‌سازی اجباری
package = await _context.Packages
.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
?? throw new NotFoundException("پکیج پایه یافت نشد");
} }
// 6. بررسی عضویت فعلی // 6. بررسی عضویت فعلی
var existingMembership = await _context.ClubMemberships var existingMembership = await _context.ClubMemberships
.FirstOrDefaultAsync(c => c.UserId == user.Id, cancellationToken); .FirstOrDefaultAsync(c => c.UserId == user.Id, cancellationToken);
// 6.1. دریافت مبلغ هدیه و هزینه فعالسازی از SystemConstants // 6.1. دریافت مبلغ هدیه و هزینه فعالسازی از پکیج
long giftValue = SystemConstants.ClubMembershipGiftValue; long giftValue = package.ActivationFee;
long activationFeeValue = SystemConstants.ClubActivationFee; long activationFeeValue = package.ActivationFee;
_logger.LogInformation( _logger.LogInformation(
"Using Club.MembershipGiftValue: {GiftValue}, Club.ActivationFee: {ActivationFee}", "Using Package {PackageId} ({PackageName}): GiftValue={GiftValue}, ActivationFee={ActivationFee}, Price={Price}",
giftValue, activationFeeValue package.Id, package.Title, giftValue, activationFeeValue, package.Price
); );
ClubMembership entity; ClubMembership entity;
@@ -188,8 +200,12 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
UserId = user.Id, UserId = user.Id,
IsActive = true, IsActive = true,
ActivatedAt = activationDate, ActivatedAt = activationDate,
InitialContribution =activationFeeValue, FirstActivationDate = activationDate,
GiftValue = giftValue, // مقدار از تنظیمات FirstPackageId = package.Id,
LastActivationDate = activationDate,
LastPackageId = package.Id,
InitialContribution = activationFeeValue,
GiftValue = giftValue,
TotalEarned = 0, TotalEarned = 0,
PurchaseMethod = user.PackagePurchaseMethod PurchaseMethod = user.PackagePurchaseMethod
}; };
@@ -214,9 +230,11 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
return true; return true;
} }
// فعال‌سازی مجدد — ActivatedAt حفظ میشه (overwrite نمیشه) // فعال‌سازی مجدد — FirstActivation حفظ میشه (Q21), Last بروزرسانی میشه
entity = existingMembership; entity = existingMembership;
entity.IsActive = true; entity.IsActive = true;
entity.LastActivationDate = activationDate;
entity.LastPackageId = package.Id;
entity.PurchaseMethod = user.PackagePurchaseMethod; entity.PurchaseMethod = user.PackagePurchaseMethod;
_context.ClubMemberships.Update(entity); _context.ClubMemberships.Update(entity);
@@ -250,7 +268,8 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
CycleNumber = maxCycleNumber + 1, CycleNumber = maxCycleNumber + 1,
PackagePurchasedAt = activationDate, PackagePurchasedAt = activationDate,
PurchaseMethod = user.PackagePurchaseMethod, PurchaseMethod = user.PackagePurchaseMethod,
PackageAmount = SystemConstants.BasePackageAmount, PackageAmount = package.Price,
PackageId = package.Id,
IsCurrentCycle = true IsCurrentCycle = true
}; };
@@ -284,15 +303,16 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
// ⭐ 8. اضافه کردن مبلغ به Pool هفته جاری // ⭐ 8. اضافه کردن مبلغ به Pool هفته جاری
var currentWeekDefinitionId = GetCurrentWeekDefinitionId(); var currentWeekDefinitionId = GetCurrentWeekDefinitionId();
var weeklyPool = await _context.WeeklyCommissionPools var weeklyPool = await _context.WeeklyCommissionPools
.FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId, cancellationToken); .FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId && p.PackageId == package.Id, cancellationToken);
if (weeklyPool == null) if (weeklyPool == null)
{ {
// ایجاد Pool جدید برای این هفته // ایجاد Pool جدید برای این هفته × این پکیج
weeklyPool = new WeeklyCommissionPool weeklyPool = new WeeklyCommissionPool
{ {
WeekDefinitionId = currentWeekDefinitionId, WeekDefinitionId = currentWeekDefinitionId,
TotalPoolAmount = activationFeeValue, // مبلغ هدیه به Pool اضافه میشه PackageId = package.Id,
TotalPoolAmount = activationFeeValue,
TotalBalances = 0, // در CalculateWeeklyBalances محاسبه میشه TotalBalances = 0, // در CalculateWeeklyBalances محاسبه میشه
ValuePerBalance = 0, // در CalculateWeeklyCommissionPool محاسبه میشه ValuePerBalance = 0, // در CalculateWeeklyCommissionPool محاسبه میشه
IsCalculated = false, IsCalculated = false,
@@ -82,11 +82,14 @@ public class CalculateWeeklyBalancesCommandHandler : IRequestHandler<CalculateWe
var balancesList = new List<NetworkWeeklyBalance>(); var balancesList = new List<NetworkWeeklyBalance>();
var calculatedAt = DateTime.Now; var calculatedAt = DateTime.Now;
// استفاده از SystemConstants (استاتیک - بدون کوئری به دیتابیس) // بارگذاری پکیج پایه برای خواندن سقف‌ها
// سقف تعادل هفتگی برای هر دست (نه کل) - 300 برای چپ + 300 برای راست = حداکثر 600 تعادل var package = await _context.Packages
var maxBalancesPerLeg = SystemConstants.CommissionMaxWeeklyBalancesPerLeg; .FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
// حداکثر عمق شبکه برای شمارش اعضا (15 لول) ?? throw new InvalidOperationException("پکیج پایه یافت نشد");
var maxNetworkLevel = SystemConstants.CommissionMaxNetworkLevel;
// خواندن سقف‌ها از پکیج (قبلاً از SystemConstants بود)
var maxBalancesPerLeg = package.MaxBalancesPerLeg;
var maxNetworkLevel = package.MaxNetworkLevel;
foreach (var user in usersInNetwork.OrderBy(o=>o.Id)) foreach (var user in usersInNetwork.OrderBy(o=>o.Id))
{ {
@@ -140,6 +143,7 @@ public class CalculateWeeklyBalancesCommandHandler : IRequestHandler<CalculateWe
{ {
UserId = user.Id, UserId = user.Id,
WeekDefinitionId = request.WeekDefinitionId, WeekDefinitionId = request.WeekDefinitionId,
PackageId = package.Id,
// اطلاعات جدید // اطلاعات جدید
LeftLegNewMembers = leftNewMembers, LeftLegNewMembers = leftNewMembers,
@@ -49,8 +49,11 @@ public class ProcessUserPayoutsCommandHandler : IRequestHandler<ProcessUserPayou
await _context.SaveChangesAsync(cancellationToken); await _context.SaveChangesAsync(cancellationToken);
} }
// ⭐ خواندن MaxNetworkLevel از SystemConstants (استاتیک) // بارگذاری پکیج پایه برای خواندن سقف‌ها
var maxNetworkLevel = SystemConstants.CommissionMaxNetworkLevel; var package = await _context.Packages
.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
?? throw new InvalidOperationException("پکیج پایه یافت نشد");
var maxNetworkLevel = package.MaxNetworkLevel;
// دریافت همه تعادل‌های هفتگی (شامل صفرها هم برای محاسبه زیرمجموعه) // دریافت همه تعادل‌های هفتگی (شامل صفرها هم برای محاسبه زیرمجموعه)
var allWeeklyBalances = await _context.NetworkWeeklyBalances var allWeeklyBalances = await _context.NetworkWeeklyBalances
@@ -113,6 +116,7 @@ public class ProcessUserPayoutsCommandHandler : IRequestHandler<ProcessUserPayou
UserId = userId, UserId = userId,
WeekDefinitionId = request.WeekDefinitionId, WeekDefinitionId = request.WeekDefinitionId,
WeeklyPoolId = pool.Id, WeeklyPoolId = pool.Id,
PackageId = package.Id,
BalancesEarned = totalBalancesWithSubordinates, // ⭐ شامل زیرمجموعه BalancesEarned = totalBalancesWithSubordinates, // ⭐ شامل زیرمجموعه
ValuePerBalance = pool.ValuePerBalance, ValuePerBalance = pool.ValuePerBalance,
TotalAmount = totalAmount, TotalAmount = totalAmount,
@@ -186,10 +186,15 @@ public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler<CheckAndPr
string contractNumber, string contractNumber,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
// بارگذاری پکیج پایه
var package = await _context.Packages
.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
?? throw new InvalidOperationException("پکیج پایه یافت نشد");
// 1. ایجاد تراکنش // 1. ایجاد تراکنش
var transaction = new Transaction var transaction = new Transaction
{ {
Amount = SystemConstants.DayaLoanAmount, Amount = package.Price,
Description = $"دریافت اعتبار دایا - قرارداد {contractNumber}", Description = $"دریافت اعتبار دایا - قرارداد {contractNumber}",
PaymentStatus = PaymentStatus.Success, PaymentStatus = PaymentStatus.Success,
PaymentDate = DateTime.Now, PaymentDate = DateTime.Now,
@@ -216,13 +221,13 @@ public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler<CheckAndPr
} }
// 3. شارژ کیف پول عادی // 3. شارژ کیف پول عادی
wallet.Balance += SystemConstants.DayaLoanAmount; wallet.Balance += package.Price;
var mainLog = new UserWalletChangeLog var mainLog = new UserWalletChangeLog
{ {
WalletId = wallet.Id, WalletId = wallet.Id,
CurrentBalance = 0, CurrentBalance = 0,
ChangeValue = SystemConstants.DayaLoanAmount, ChangeValue = package.Price,
CurrentNetworkBalance = wallet.NetworkBalance, CurrentNetworkBalance = wallet.NetworkBalance,
ChangeNerworkValue = 0, ChangeNerworkValue = 0,
IsIncrease = true, IsIncrease = true,
@@ -230,8 +235,8 @@ public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler<CheckAndPr
}; };
await _context.UserWalletChangeLogs.AddAsync(mainLog, cancellationToken); await _context.UserWalletChangeLogs.AddAsync(mainLog, cancellationToken);
// 4. شارژ کیف پول تخفیف (دو برابر) // 4. شارژ کیف پول تخفیف
var discountAmount = SystemConstants.DayaLoanAmount * 2; var discountAmount = (long)(package.Price * package.DiscountMultiplier);
wallet.DiscountBalance += discountAmount; wallet.DiscountBalance += discountAmount;
var discountLog = new UserWalletChangeLog var discountLog = new UserWalletChangeLog
@@ -253,28 +258,19 @@ public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler<CheckAndPr
user.DayaCreditReceivedAt = DateTime.Now; user.DayaCreditReceivedAt = DateTime.Now;
user.PackagePurchaseMethod = PackagePurchaseMethod.DayaLoan; user.PackagePurchaseMethod = PackagePurchaseMethod.DayaLoan;
// 6. ثبت Order پکیج پایه
var goldenPackage = await _context.Packages
.FirstOrDefaultAsync(p => p.Id == 4, cancellationToken);
if (goldenPackage != null)
{
// 6. ثبت UserPackagePurchase برای پکیج پایه // 6. ثبت UserPackagePurchase برای پکیج پایه
var goldenPackageId =goldenPackage.Id;
var packagePurchase = new UserPackagePurchase var packagePurchase = new UserPackagePurchase
{ {
UserId = user.Id, UserId = user.Id,
PackageId = goldenPackageId, PackageId = package.Id,
PurchaseMethod = PackagePurchaseMethod.DayaLoan, PurchaseMethod = PackagePurchaseMethod.DayaLoan,
PurchasedAt = DateTime.Now, PurchasedAt = DateTime.Now,
Amount = SystemConstants.DayaLoanAmount, Amount = package.Price,
TransactionId = transaction.Id TransactionId = transaction.Id
}; };
await _context.UserPackagePurchases.AddAsync(packagePurchase, cancellationToken); await _context.UserPackagePurchases.AddAsync(packagePurchase, cancellationToken);
}
// 7. Domain Event // 7. Domain Event
user.AddDomainEvent(new DayaLoanApprovedEvent(user, transaction, contractNumber)); user.AddDomainEvent(new DayaLoanApprovedEvent(user, transaction, contractNumber));
@@ -70,9 +70,13 @@ public class CreateManualPaymentCommandHandler : IRequestHandler<CreateManualPay
throw new NotFoundException($"کیف پول کاربر {request.UserId} یافت نشد"); throw new NotFoundException($"کیف پول کاربر {request.UserId} یافت نشد");
} }
// 4. محاسبه مبالغ // 4. بارگذاری پکیج پایه و محاسبه مبالغ
var balanceAmount = SystemConstants.BasePackageAmount; // 56M var package = await _context.Packages
var discountBalanceAmount = SystemConstants.BasePackageAmount * 2; // 112M .FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
?? throw new NotFoundException("پکیج پایه یافت نشد");
var balanceAmount = package.Price;
var discountBalanceAmount = (long)(package.Price * package.DiscountMultiplier);
// 5. ثبت تراکنش // 5. ثبت تراکنش
@@ -20,10 +20,7 @@ public class InitiateBasePackagePaymentCommandHandler : IRequestHandler<Initiate
private readonly IApplicationDbContext _context; private readonly IApplicationDbContext _context;
private readonly ILogger<InitiateBasePackagePaymentCommandHandler> _logger; private readonly ILogger<InitiateBasePackagePaymentCommandHandler> _logger;
/// <summary> // شناسه پکیج پایه از دیتابیس خوانده می‌شود (Package.IsBasePackage)
/// شناسه پکیج پایه در دیتابیس
/// </summary>
private const long BasePackageId = 4;
public InitiateBasePackagePaymentCommandHandler( public InitiateBasePackagePaymentCommandHandler(
IApplicationDbContext context, IApplicationDbContext context,
@@ -68,12 +65,17 @@ public class InitiateBasePackagePaymentCommandHandler : IRequestHandler<Initiate
}; };
} }
// 2.5. بارگذاری پکیج پایه
var package = await _context.Packages
.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
?? throw new NotFoundException("پکیج پایه یافت نشد");
// 3. بررسی عدم وجود سفارش Pending // 3. بررسی عدم وجود سفارش Pending
var pendingOrder = await _context.UserOrders var pendingOrder = await _context.UserOrders
.FirstOrDefaultAsync(o => .FirstOrDefaultAsync(o =>
o.UserId == request.UserId && o.UserId == request.UserId &&
o.PaymentStatus == PaymentStatus.Pending && o.PaymentStatus == PaymentStatus.Pending &&
o.PackageId == BasePackageId, o.PackageId == package.Id,
cancellationToken); cancellationToken);
if (pendingOrder != null) if (pendingOrder != null)
@@ -93,7 +95,7 @@ public class InitiateBasePackagePaymentCommandHandler : IRequestHandler<Initiate
Message = "سفارش قبلی در انتظار پرداخت یافت شد.", Message = "سفارش قبلی در انتظار پرداخت یافت شد.",
OrderId = pendingOrder.Id, OrderId = pendingOrder.Id,
TransactionId = existingTransaction?.Id ?? 0, TransactionId = existingTransaction?.Id ?? 0,
Amount = SystemConstants.BasePackageAmount Amount = package.Price
}; };
} }
@@ -112,8 +114,8 @@ public class InitiateBasePackagePaymentCommandHandler : IRequestHandler<Initiate
// 5. ایجاد Transaction با وضعیت Pending // 5. ایجاد Transaction با وضعیت Pending
var transaction = new Transaction var transaction = new Transaction
{ {
Amount = SystemConstants.BasePackageAmount, Amount = package.Price,
Description = $"خرید پکیج پایه ۵۶ میلیونی - کاربر #{user.Id}", Description = $"خرید پکیج {package.Title} - کاربر #{user.Id}",
PaymentStatus = PaymentStatus.Pending, PaymentStatus = PaymentStatus.Pending,
Type = TransactionType.DepositIpg Type = TransactionType.DepositIpg
}; };
@@ -130,8 +132,8 @@ public class InitiateBasePackagePaymentCommandHandler : IRequestHandler<Initiate
var order = new UserOrder var order = new UserOrder
{ {
UserId = user.Id, UserId = user.Id,
PackageId = BasePackageId, PackageId = package.Id,
Amount = SystemConstants.BasePackageAmount, Amount = package.Price,
PaymentStatus = PaymentStatus.Pending, PaymentStatus = PaymentStatus.Pending,
DeliveryStatus = DeliveryStatus.None, DeliveryStatus = DeliveryStatus.None,
UserAddressId = defaultAddress.Id, UserAddressId = defaultAddress.Id,
@@ -154,7 +156,7 @@ public class InitiateBasePackagePaymentCommandHandler : IRequestHandler<Initiate
Message = "تراکنش و سفارش با موفقیت ثبت شد.", Message = "تراکنش و سفارش با موفقیت ثبت شد.",
OrderId = order.Id, OrderId = order.Id,
TransactionId = transaction.Id, TransactionId = transaction.Id,
Amount = SystemConstants.BasePackageAmount Amount = package.Price
}; };
} }
catch (Exception ex) catch (Exception ex)
@@ -128,12 +128,20 @@ public class VerifyBasePackagePaymentCommandHandler : IRequestHandler<VerifyBase
await _context.SaveChangesAsync(cancellationToken); await _context.SaveChangesAsync(cancellationToken);
} }
// 5.5. بارگذاری پکیج از سفارش
var package = order.PackageId.HasValue
? await _context.Packages.FirstOrDefaultAsync(p => p.Id == order.PackageId.Value, cancellationToken)
: await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken);
if (package == null)
throw new NotFoundException("پکیج یافت نشد");
// 6. شارژ کیف پول (هم Balance و هم DiscountBalance) // 6. شارژ کیف پول (هم Balance و هم DiscountBalance)
var oldBalance = userWallet.Balance; var oldBalance = userWallet.Balance;
var oldDiscountBalance = userWallet.DiscountBalance; var oldDiscountBalance = userWallet.DiscountBalance;
userWallet.Balance += SystemConstants.BasePackageAmount; userWallet.Balance += package.Price;
userWallet.DiscountBalance += SystemConstants.BasePackageAmount * 2; userWallet.DiscountBalance += (long)(package.Price * package.DiscountMultiplier);
_logger.LogInformation( _logger.LogInformation(
"Charging wallet for user {UserId}. Balance: {OldBalance} -> {NewBalance}, DiscountBalance: {OldDiscount} -> {NewDiscount}", "Charging wallet for user {UserId}. Balance: {OldBalance} -> {NewBalance}, DiscountBalance: {OldDiscount} -> {NewDiscount}",
@@ -153,11 +161,11 @@ public class VerifyBasePackagePaymentCommandHandler : IRequestHandler<VerifyBase
{ {
WalletId = userWallet.Id, WalletId = userWallet.Id,
CurrentBalance = userWallet.Balance, CurrentBalance = userWallet.Balance,
ChangeValue = SystemConstants.BasePackageAmount, ChangeValue = package.Price,
CurrentNetworkBalance = userWallet.NetworkBalance, CurrentNetworkBalance = userWallet.NetworkBalance,
ChangeNerworkValue = 0, ChangeNerworkValue = 0,
CurrentDiscountBalance = userWallet.DiscountBalance, CurrentDiscountBalance = userWallet.DiscountBalance,
ChangeDiscountValue = SystemConstants.BasePackageAmount * 2, ChangeDiscountValue = (long)(package.Price * package.DiscountMultiplier),
IsIncrease = true, IsIncrease = true,
RefrenceId = transaction.Id RefrenceId = transaction.Id
}; };
@@ -173,7 +181,7 @@ public class VerifyBasePackagePaymentCommandHandler : IRequestHandler<VerifyBase
PackageId = order.PackageId.Value, PackageId = order.PackageId.Value,
PurchaseMethod = PackagePurchaseMethod.DirectPurchase, PurchaseMethod = PackagePurchaseMethod.DirectPurchase,
PurchasedAt = DateTime.Now, PurchasedAt = DateTime.Now,
Amount = SystemConstants.BasePackageAmount, Amount = package.Price,
OrderId = order.Id, OrderId = order.Id,
TransactionId = transaction.Id TransactionId = transaction.Id
}; };
@@ -76,8 +76,17 @@ public class ChargeMagicWalletCommandHandler
); );
} }
// 3.5. بارگذاری پکیج کاربر برای سقف کیف‌پول جادویی
var currentCycle = await _context.ClubMembershipCycles
.FirstOrDefaultAsync(c => c.UserId == request.UserId && c.IsCurrentCycle, cancellationToken);
var package = currentCycle != null
? await _context.Packages.FirstOrDefaultAsync(p => p.Id == currentCycle.PackageId, cancellationToken)
: await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken);
if (package == null)
throw new NotFoundException("پکیج یافت نشد");
// 4. بررسی سقف واریزی (per-cycle) // 4. بررسی سقف واریزی (per-cycle)
var remainingDeposit = SystemConstants.MagicWalletMaxDeposit - wallet.MagicTotalDeposited; var remainingDeposit = package.MagicWalletMaxDeposit - wallet.MagicTotalDeposited;
if (remainingDeposit <= 0) if (remainingDeposit <= 0)
{ {
@@ -121,8 +121,17 @@ public class VerifyMagicWalletChargeCommandHandler
throw new BadRequestException("کیف‌پول در حالت جادویی نیست"); throw new BadRequestException("کیف‌پول در حالت جادویی نیست");
} }
// 5. محاسبه اعتبار ×2.5 // 4.5. بارگذاری پکیج کاربر برای ضریب جادویی
var creditAmount = (long)(depositAmount * SystemConstants.MagicWalletMultiplier); // مبلغ × 2.5 var currentCycle = await _context.ClubMembershipCycles
.FirstOrDefaultAsync(c => c.UserId == userId && c.IsCurrentCycle, cancellationToken);
var package = currentCycle != null
? await _context.Packages.FirstOrDefaultAsync(p => p.Id == currentCycle.PackageId, cancellationToken)
: await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken);
if (package == null)
throw new NotFoundException("پکیج یافت نشد");
// 5. محاسبه اعتبار
var creditAmount = (long)(depositAmount * package.MagicWalletMultiplier); // مبلغ × 2.5
var bonusAmount = creditAmount - depositAmount; // بونوس = مبلغ × 1.5 var bonusAmount = creditAmount - depositAmount; // بونوس = مبلغ × 1.5
// 6. ثبت تراکنش واریز واقعی (MagicWalletDeposit) // 6. ثبت تراکنش واریز واقعی (MagicWalletDeposit)
@@ -143,7 +152,7 @@ public class VerifyMagicWalletChargeCommandHandler
var bonusTransaction = new Transaction var bonusTransaction = new Transaction
{ {
Amount = bonusAmount, Amount = bonusAmount,
Description = $"شارژ کیف‌پول جادویی - بونوس ×{SystemConstants.MagicWalletMultiplier - 1} - کاربر {userId}", Description = $"شارژ کیف‌پول جادویی - بونوس ×{package.MagicWalletMultiplier - 1} - کاربر {userId}",
PaymentStatus = PaymentStatus.Success, PaymentStatus = PaymentStatus.Success,
PaymentDate = DateTime.UtcNow, PaymentDate = DateTime.UtcNow,
RefId = $"MAGIC_BONUS_{depositTransaction.Id}", RefId = $"MAGIC_BONUS_{depositTransaction.Id}",
@@ -186,10 +195,10 @@ public class VerifyMagicWalletChargeCommandHandler
userId, userId,
depositAmount, depositAmount,
creditAmount, creditAmount,
SystemConstants.MagicWalletMultiplier, package.MagicWalletMultiplier,
bonusAmount, bonusAmount,
wallet.MagicTotalDeposited, wallet.MagicTotalDeposited,
SystemConstants.MagicWalletMaxDeposit, package.MagicWalletMaxDeposit,
wallet.Balance wallet.Balance
); );
@@ -94,9 +94,12 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy
var balancesList = new List<NetworkWeeklyBalance>(); var balancesList = new List<NetworkWeeklyBalance>();
var calculatedAt = DateTime.Now; var calculatedAt = DateTime.Now;
// استفاده از SystemConstants به جای دیتابیس // بارگذاری پکیج پایه برای خواندن سقف‌ها
var maxBalancesPerLeg = SystemConstants.CommissionMaxWeeklyBalancesPerLeg; var package = await _context.Packages
var maxNetworkLevel = SystemConstants.CommissionMaxNetworkLevel; .FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
?? throw new InvalidOperationException("پکیج پایه یافت نشد");
var maxBalancesPerLeg = package.MaxBalancesPerLeg;
var maxNetworkLevel = package.MaxNetworkLevel;
foreach (var user in usersInNetwork.OrderBy(o => o.Id)) foreach (var user in usersInNetwork.OrderBy(o => o.Id))
{ {
@@ -135,6 +138,7 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy
{ {
UserId = user.Id, UserId = user.Id,
WeekDefinitionId = weekDefinitionId, WeekDefinitionId = weekDefinitionId,
PackageId = package.Id,
LeftLegNewMembers = leftNewMembers, LeftLegNewMembers = leftNewMembers,
RightLegNewMembers = rightNewMembers, RightLegNewMembers = rightNewMembers,
LeftLegCarryover = leftCarryover, LeftLegCarryover = leftCarryover,
@@ -189,6 +193,11 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy
bool forceRecalculate, bool forceRecalculate,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
// بارگذاری پکیج پایه برای تعیین PackageId روی پرداخت‌ها
var package = await _context.Packages
.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
?? throw new InvalidOperationException("پکیج پایه یافت نشد");
var weekDefinition = _weekRepository.GetGregorianWeekNumber(weekDefinitionId); var weekDefinition = _weekRepository.GetGregorianWeekNumber(weekDefinitionId);
if (weekDefinition == null) if (weekDefinition == null)
{ {
@@ -284,6 +293,7 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy
UserId = balance.UserId, UserId = balance.UserId,
WeekDefinitionId = weekDefinitionId, WeekDefinitionId = weekDefinitionId,
WeeklyPoolId = existingPool.Id, WeeklyPoolId = existingPool.Id,
PackageId = package.Id,
BalancesEarned = userBalance, BalancesEarned = userBalance,
ValuePerBalance = valuePerBalance, ValuePerBalance = valuePerBalance,
TotalAmount = totalAmount, TotalAmount = totalAmount,
@@ -2,37 +2,42 @@ using CMSMicroservice.Application.ClubFeatureCQ.Queries.GetUserClubFeatures;
using CMSMicroservice.Application.Common.Authorization; using CMSMicroservice.Application.Common.Authorization;
using CMSMicroservice.Application.Common.Interfaces; using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Common; using CMSMicroservice.Domain.Common;
using CMSMicroservice.Domain.Entities;
using CMSMicroservice.Protobuf.Protos.Configuration; using CMSMicroservice.Protobuf.Protos.Configuration;
using Google.Protobuf.WellKnownTypes; using Google.Protobuf.WellKnownTypes;
using Grpc.Core; using Grpc.Core;
using MediatR; using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace CMSMicroservice.WebApi.Services; namespace CMSMicroservice.WebApi.Services;
/// <summary> /// <summary>
/// سرویس تنظیمات سیستم - خواندن از SystemConstants /// سرویس تنظیمات سیستم — مقادیر پکیجی از Package entity خوانده می‌شوند
/// </summary> /// </summary>
public class ConfigurationService : ConfigurationContract.ConfigurationContractBase public class ConfigurationService : ConfigurationContract.ConfigurationContractBase
{ {
private readonly ILogger<ConfigurationService> _logger; private readonly ILogger<ConfigurationService> _logger;
private readonly ICurrentUserService _currentUserService; private readonly ICurrentUserService _currentUserService;
private readonly IMediator _mediator; private readonly IMediator _mediator;
private readonly IApplicationDbContext _context;
public ConfigurationService( public ConfigurationService(
ILogger<ConfigurationService> logger, ILogger<ConfigurationService> logger,
ICurrentUserService currentUserService, ICurrentUserService currentUserService,
IMediator mediator) IMediator mediator,
IApplicationDbContext context)
{ {
_logger = logger; _logger = logger;
_currentUserService = currentUserService; _currentUserService = currentUserService;
_mediator = mediator; _mediator = mediator;
_context = context;
} }
/// <summary> /// <summary>
/// دریافت تنظیمات با کلید خاص /// دریافت تنظیمات با کلید خاص
/// </summary> /// </summary>
public override Task<GetConfigurationByKeyResponse> GetConfigurationByKey(GetConfigurationByKeyRequest request, ServerCallContext context) public override async Task<GetConfigurationByKeyResponse> GetConfigurationByKey(GetConfigurationByKeyRequest request, ServerCallContext context)
{ {
var response = new GetConfigurationByKeyResponse var response = new GetConfigurationByKeyResponse
{ {
@@ -43,30 +48,33 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB
LastModified = Timestamp.FromDateTime(DateTime.UtcNow) LastModified = Timestamp.FromDateTime(DateTime.UtcNow)
}; };
// خواندن مقدار از SystemConstants بر اساس کلید // برای کلیدهای پکیجی، از دیتابیس می‌خوانیم
response.Value = GetConfigurationValue(request.Key); var package = await GetBasePackageAsync(context.CancellationToken);
response.Value = GetConfigurationValue(request.Key, package);
response.Description = GetConfigurationDescription(request.Key); response.Description = GetConfigurationDescription(request.Key);
_logger.LogDebug("Configuration requested: Key={Key}, Value={Value}", request.Key, response.Value); _logger.LogDebug("Configuration requested: Key={Key}, Value={Value}", request.Key, response.Value);
return Task.FromResult(response); return response;
} }
/// <summary> /// <summary>
/// دریافت تنظیمات باشگاه مشتریان /// دریافت تنظیمات باشگاه مشتریان
/// </summary> /// </summary>
public override Task<GetClubConfigurationResponse> GetClubConfiguration(Empty request, ServerCallContext context) public override async Task<GetClubConfigurationResponse> GetClubConfiguration(Empty request, ServerCallContext context)
{ {
var package = await GetBasePackageAsync(context.CancellationToken);
var response = new GetClubConfigurationResponse var response = new GetClubConfigurationResponse
{ {
ActivationFee = SystemConstants.ClubActivationFee, ActivationFee = package.ActivationFee,
MembershipGiftValue = SystemConstants.ClubMembershipGiftValue MembershipGiftValue = package.ActivationFee
}; };
_logger.LogDebug("Club configuration requested: ActivationFee={ActivationFee}, GiftValue={GiftValue}", _logger.LogDebug("Club configuration requested: ActivationFee={ActivationFee}, GiftValue={GiftValue}",
response.ActivationFee, response.MembershipGiftValue); response.ActivationFee, response.MembershipGiftValue);
return Task.FromResult(response); return response;
} }
/// <summary> /// <summary>
@@ -111,18 +119,19 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB
/// دریافت تمام تنظیمات /// دریافت تمام تنظیمات
/// </summary> /// </summary>
[RequiresPermission(PermissionNames.SettingsView)] [RequiresPermission(PermissionNames.SettingsView)]
public override Task<GetAllConfigurationsResponse> GetAllConfigurations(GetAllConfigurationsRequest request, ServerCallContext context) public override async Task<GetAllConfigurationsResponse> GetAllConfigurations(GetAllConfigurationsRequest request, ServerCallContext context)
{ {
var package = await GetBasePackageAsync(context.CancellationToken);
var response = new GetAllConfigurationsResponse(); var response = new GetAllConfigurationsResponse();
// Club Settings // Club Settings (از Package)
response.Models.Add(CreateConfigModel("Club.ActivationFee", SystemConstants.ClubActivationFee.ToString(), "هزینه فعال‌سازی عضویت باشگاه", 2)); response.Models.Add(CreateConfigModel("Club.ActivationFee", package.ActivationFee.ToString(), "هزینه فعال‌سازی عضویت باشگاه", 2));
response.Models.Add(CreateConfigModel("Club.MembershipGiftValue", SystemConstants.ClubMembershipGiftValue.ToString(), "مبلغ هدیه حق عضویت باشگاه", 2)); response.Models.Add(CreateConfigModel("Club.MembershipGiftValue", package.ActivationFee.ToString(), "مبلغ هدیه حق عضویت باشگاه", 2));
// Commission Settings // Commission Settings (سقف‌ها از Package)
response.Models.Add(CreateConfigModel("Commission.MinWithdrawalAmount", SystemConstants.CommissionMinWithdrawalAmount.ToString(), "حداقل مبلغ برداشت", 3)); response.Models.Add(CreateConfigModel("Commission.MinWithdrawalAmount", SystemConstants.CommissionMinWithdrawalAmount.ToString(), "حداقل مبلغ برداشت", 3));
response.Models.Add(CreateConfigModel("Commission.MaxWeeklyBalancesPerLeg", SystemConstants.CommissionMaxWeeklyBalancesPerLeg.ToString(), "سقف تعادل هفتگی برای هر دست", 3)); response.Models.Add(CreateConfigModel("Commission.MaxWeeklyBalancesPerLeg", package.MaxBalancesPerLeg.ToString(), "سقف تعادل هفتگی برای هر دست", 3));
response.Models.Add(CreateConfigModel("Commission.MaxNetworkLevel", SystemConstants.CommissionMaxNetworkLevel.ToString(), "حداکثر عمق شبکه برای محاسبه کمیسیون", 3)); response.Models.Add(CreateConfigModel("Commission.MaxNetworkLevel", package.MaxNetworkLevel.ToString(), "حداکثر عمق شبکه برای محاسبه کمیسیون", 3));
response.Models.Add(CreateConfigModel("Commission.CashWithdrawalEnabled", SystemConstants.CommissionCashWithdrawalEnabled.ToString(), "امکان برداشت نقدی", 3)); response.Models.Add(CreateConfigModel("Commission.CashWithdrawalEnabled", SystemConstants.CommissionCashWithdrawalEnabled.ToString(), "امکان برداشت نقدی", 3));
response.Models.Add(CreateConfigModel("Commission.CalculationStrategy", SystemConstants.CommissionCalculationStrategy, "روش محاسبه کمیسیون", 3)); response.Models.Add(CreateConfigModel("Commission.CalculationStrategy", SystemConstants.CommissionCalculationStrategy, "روش محاسبه کمیسیون", 3));
@@ -130,9 +139,9 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB
response.Models.Add(CreateConfigModel("Network.AllowOrphanNodes", SystemConstants.NetworkAllowOrphanNodes.ToString(), "اجازه حذف والدین با فرزند", 1)); response.Models.Add(CreateConfigModel("Network.AllowOrphanNodes", SystemConstants.NetworkAllowOrphanNodes.ToString(), "اجازه حذف والدین با فرزند", 1));
response.Models.Add(CreateConfigModel("Network.MaxChildrenPerLeg", SystemConstants.NetworkMaxChildrenPerLeg.ToString(), "حداکثر تعداد فرزند مستقیم در هر پا", 1)); response.Models.Add(CreateConfigModel("Network.MaxChildrenPerLeg", SystemConstants.NetworkMaxChildrenPerLeg.ToString(), "حداکثر تعداد فرزند مستقیم در هر پا", 1));
// Package Settings // Package Settings (از Package)
response.Models.Add(CreateConfigModel("Package.BasePackageAmount", SystemConstants.BasePackageAmount.ToString(), "مبلغ پکیج پایه", 0)); response.Models.Add(CreateConfigModel("Package.BasePackageAmount", package.Price.ToString(), "مبلغ پکیج پایه", 0));
response.Models.Add(CreateConfigModel("Package.DayaLoanAmount", SystemConstants.DayaLoanAmount.ToString(), "مبلغ وام دایا", 0)); response.Models.Add(CreateConfigModel("Package.DayaLoanAmount", package.Price.ToString(), "مبلغ وام دایا", 0));
// System Settings // System Settings
response.Models.Add(CreateConfigModel("System.MaintenanceMode", SystemConstants.SystemMaintenanceMode.ToString(), "حالت تعمیر و نگهداری", 0)); response.Models.Add(CreateConfigModel("System.MaintenanceMode", SystemConstants.SystemMaintenanceMode.ToString(), "حالت تعمیر و نگهداری", 0));
@@ -141,7 +150,7 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB
// Shop Settings // Shop Settings
response.Models.Add(CreateConfigModel("Shop.VAT", SystemConstants.ShopVAT.ToString(), "مالیات بر ارزش افزوده", 0)); response.Models.Add(CreateConfigModel("Shop.VAT", SystemConstants.ShopVAT.ToString(), "مالیات بر ارزش افزوده", 0));
return Task.FromResult(response); return response;
} }
/// <summary> /// <summary>
@@ -173,21 +182,21 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB
#region Private Helpers #region Private Helpers
private static string GetConfigurationValue(string key) private static string GetConfigurationValue(string key, Package? package)
{ {
return key switch return key switch
{ {
"Club.ActivationFee" => SystemConstants.ClubActivationFee.ToString(), "Club.ActivationFee" => (package?.ActivationFee ?? 0).ToString(),
"Club.MembershipGiftValue" => SystemConstants.ClubMembershipGiftValue.ToString(), "Club.MembershipGiftValue" => (package?.ActivationFee ?? 0).ToString(),
"Commission.MinWithdrawalAmount" => SystemConstants.CommissionMinWithdrawalAmount.ToString(), "Commission.MinWithdrawalAmount" => SystemConstants.CommissionMinWithdrawalAmount.ToString(),
"Commission.MaxWeeklyBalancesPerLeg" => SystemConstants.CommissionMaxWeeklyBalancesPerLeg.ToString(), "Commission.MaxWeeklyBalancesPerLeg" => (package?.MaxBalancesPerLeg ?? 0).ToString(),
"Commission.MaxNetworkLevel" => SystemConstants.CommissionMaxNetworkLevel.ToString(), "Commission.MaxNetworkLevel" => (package?.MaxNetworkLevel ?? 0).ToString(),
"Commission.CashWithdrawalEnabled" => SystemConstants.CommissionCashWithdrawalEnabled.ToString(), "Commission.CashWithdrawalEnabled" => SystemConstants.CommissionCashWithdrawalEnabled.ToString(),
"Commission.CalculationStrategy" => SystemConstants.CommissionCalculationStrategy, "Commission.CalculationStrategy" => SystemConstants.CommissionCalculationStrategy,
"Network.AllowOrphanNodes" => SystemConstants.NetworkAllowOrphanNodes.ToString(), "Network.AllowOrphanNodes" => SystemConstants.NetworkAllowOrphanNodes.ToString(),
"Network.MaxChildrenPerLeg" => SystemConstants.NetworkMaxChildrenPerLeg.ToString(), "Network.MaxChildrenPerLeg" => SystemConstants.NetworkMaxChildrenPerLeg.ToString(),
"Package.BasePackageAmount" => SystemConstants.BasePackageAmount.ToString(), "Package.BasePackageAmount" => (package?.Price ?? 0).ToString(),
"Package.DayaLoanAmount" => SystemConstants.DayaLoanAmount.ToString(), "Package.DayaLoanAmount" => (package?.Price ?? 0).ToString(),
"System.MaintenanceMode" => SystemConstants.SystemMaintenanceMode.ToString(), "System.MaintenanceMode" => SystemConstants.SystemMaintenanceMode.ToString(),
"System.EnableAuditLog" => SystemConstants.SystemEnableAuditLog.ToString(), "System.EnableAuditLog" => SystemConstants.SystemEnableAuditLog.ToString(),
"Shop.VAT" => SystemConstants.ShopVAT.ToString(), "Shop.VAT" => SystemConstants.ShopVAT.ToString(),
@@ -195,6 +204,13 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB
}; };
} }
private async Task<Package> GetBasePackageAsync(CancellationToken cancellationToken)
{
return await _context.Packages
.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
?? throw new RpcException(new Status(StatusCode.Internal, "پکیج پایه یافت نشد"));
}
private static string GetConfigurationDescription(string key) private static string GetConfigurationDescription(string key)
{ {
return key switch return key switch
@@ -344,8 +344,17 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
.Include(u => u.ClubMembership) .Include(u => u.ClubMembership)
.FirstOrDefaultAsync(u => u.Id == userId, context.CancellationToken); .FirstOrDefaultAsync(u => u.Id == userId, context.CancellationToken);
if (wallet.WalletMode == WalletMode.Magic if (wallet.WalletMode == WalletMode.Magic)
&& wallet.MagicTotalDeposited >= SystemConstants.MagicWalletMaxDeposit) {
// بارگذاری پکیج کاربر برای سقف کیف‌پول جادویی
var userCycle = await _context.ClubMembershipCycles
.FirstOrDefaultAsync(c => c.UserId == userId && c.IsCurrentCycle, context.CancellationToken);
var userPackage = userCycle != null
? await _context.Packages.FirstOrDefaultAsync(p => p.Id == userCycle.PackageId, context.CancellationToken)
: await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, context.CancellationToken);
var magicMaxDeposit = userPackage?.MagicWalletMaxDeposit ?? 1_000_000_000;
if (wallet.MagicTotalDeposited >= magicMaxDeposit)
{ {
// ── EXIT Magic Mode ── // ── EXIT Magic Mode ──
// هر دو شرط: Balance=0 و سقف 100M پر شده // هر دو شرط: Balance=0 و سقف 100M پر شده
@@ -367,6 +376,7 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
user.PackagePurchaseMethod = PackagePurchaseMethod.None; user.PackagePurchaseMethod = PackagePurchaseMethod.None;
} }
} }
}
else if (wallet.WalletMode == WalletMode.Normal else if (wallet.WalletMode == WalletMode.Normal
&& user != null && user != null
&& user.PackagePurchaseMethod != PackagePurchaseMethod.None && user.PackagePurchaseMethod != PackagePurchaseMethod.None
@@ -227,13 +227,21 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase
var cycleCount = await _context.ClubMembershipCycles var cycleCount = await _context.ClubMembershipCycles
.CountAsync(c => c.UserId == userId, context.CancellationToken); .CountAsync(c => c.UserId == userId, context.CancellationToken);
// بارگذاری پکیج کاربر برای سقف کیف‌پول جادویی
var currentCycle = await _context.ClubMembershipCycles
.FirstOrDefaultAsync(c => c.UserId == userId && c.IsCurrentCycle, context.CancellationToken);
var package = currentCycle != null
? await _context.Packages.FirstOrDefaultAsync(p => p.Id == currentCycle.PackageId, context.CancellationToken)
: await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, context.CancellationToken);
var magicMaxDeposit = package?.MagicWalletMaxDeposit ?? 1_000_000_000;
var response = new GetMagicWalletStatusResponse var response = new GetMagicWalletStatusResponse
{ {
WalletMode = (int)wallet.WalletMode, WalletMode = (int)wallet.WalletMode,
MagicTotalDeposited = wallet.MagicTotalDeposited, MagicTotalDeposited = wallet.MagicTotalDeposited,
MagicTotalCredited = wallet.MagicTotalCredited, MagicTotalCredited = wallet.MagicTotalCredited,
MagicMaxDeposit = SystemConstants.MagicWalletMaxDeposit, MagicMaxDeposit = magicMaxDeposit,
MagicRemainingDeposit = Math.Max(0, SystemConstants.MagicWalletMaxDeposit - wallet.MagicTotalDeposited), MagicRemainingDeposit = Math.Max(0, magicMaxDeposit - wallet.MagicTotalDeposited),
Balance = wallet.Balance, Balance = wallet.Balance,
PurchaseCycleCount = cycleCount PurchaseCycleCount = cycleCount
}; };