From 8e5c7c520586d16d8f0774a8644ec2d47fa5ccf5 Mon Sep 17 00:00:00 2001 From: masoodafar-web Date: Wed, 25 Feb 2026 23:25:18 +0330 Subject: [PATCH] Phase 2: Replace all deprecated SystemConstants with Package entity reads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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) --- ...eptClubMembershipContractCommandHandler.cs | 31 ++++++-- .../ActivateClubMembershipCommandHandler.cs | 66 +++++++++++------ .../CalculateWeeklyBalancesCommandHandler.cs | 14 ++-- .../ProcessUserPayoutsCommandHandler.cs | 8 ++- .../CheckAndProcessDayaLoansCommandHandler.cs | 44 ++++++------ .../CreateManualPaymentCommandHandler.cs | 10 ++- ...nitiateBasePackagePaymentCommandHandler.cs | 24 ++++--- .../VerifyBasePackagePaymentCommandHandler.cs | 18 +++-- .../ChargeMagicWalletCommandHandler.cs | 11 ++- .../VerifyMagicWalletChargeCommandHandler.cs | 19 +++-- .../OrmCommissionCalculationStrategy.cs | 16 ++++- .../Services/ConfigurationService.cs | 72 +++++++++++-------- .../Services/UserOrderService.cs | 14 +++- .../Services/UserWalletService.cs | 12 +++- 14 files changed, 239 insertions(+), 120 deletions(-) diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AcceptClubMembershipContract/AcceptClubMembershipContractCommandHandler.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AcceptClubMembershipContract/AcceptClubMembershipContractCommandHandler.cs index b135fa0..5e403c8 100644 --- a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AcceptClubMembershipContract/AcceptClubMembershipContractCommandHandler.cs +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AcceptClubMembershipContract/AcceptClubMembershipContractCommandHandler.cs @@ -135,13 +135,25 @@ public class AcceptClubMembershipContractCommandHandler }; await _context.UserContracts.AddAsync(userContract, cancellationToken); - // 6. دریافت مقادیر از SystemConstants (استاتیک) - long giftValue = SystemConstants.ClubMembershipGiftValue; - long activationFeeValue = SystemConstants.ClubActivationFee; + // 6. بارگذاری پکیج از آخرین خرید کاربر یا پکیج پایه + var latestPurchase = await _context.UserPackagePurchases + .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( - "Using Club.MembershipGiftValue: {GiftValue}, Club.ActivationFee: {ActivationFee}", - giftValue, activationFeeValue + "Using Package {PackageId} ({PackageName}): GiftValue={GiftValue}, ActivationFee={ActivationFee}", + package.Id, package.Title, giftValue, activationFeeValue ); // 7. فعال‌سازی باشگاه مشتریان @@ -156,6 +168,10 @@ public class AcceptClubMembershipContractCommandHandler UserId = user.Id, IsActive = true, ActivatedAt = activationDate, + FirstActivationDate = activationDate, + FirstPackageId = package.Id, + LastActivationDate = activationDate, + LastPackageId = package.Id, InitialContribution = activationFeeValue, GiftValue = giftValue, TotalEarned = 0, @@ -175,6 +191,8 @@ public class AcceptClubMembershipContractCommandHandler clubMembership = user.ClubMembership!; clubMembership.IsActive = true; clubMembership.ActivatedAt = activationDate; + clubMembership.LastActivationDate = activationDate; + clubMembership.LastPackageId = package.Id; clubMembership.PurchaseMethod = user.PackagePurchaseMethod; _context.ClubMemberships.Update(clubMembership); @@ -206,13 +224,14 @@ public class AcceptClubMembershipContractCommandHandler // 9. اضافه کردن مبلغ به Pool هفته جاری var currentWeekDefinitionId = GetCurrentWeekDefinitionId(); var weeklyPool = await _context.WeeklyCommissionPools - .FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId, cancellationToken); + .FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId && p.PackageId == package.Id, cancellationToken); if (weeklyPool == null) { weeklyPool = new WeeklyCommissionPool { WeekDefinitionId = currentWeekDefinitionId, + PackageId = package.Id, TotalPoolAmount = activationFeeValue, TotalBalances = 0, ValuePerBalance = 0, diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandHandler.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandHandler.cs index 20e1def..b4e8e84 100644 --- a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandHandler.cs +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandHandler.cs @@ -54,6 +54,9 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler p.Id == packageOrder.PackageId && !p.IsDeleted, cancellationToken) + ?? throw new NotFoundException("پکیج یافت نشد"); + + // بررسی موجودی با مبلغ پکیج واقعی + if (wallet.Balance < package.Price) + { + throw new BadRequestException( + $"برای فعالسازی باشگاه مشتریان باید حداقل {package.Price:N0} ریال موجودی اصلی داشته باشید" + ); + } } else { @@ -161,19 +168,24 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler p.IsBasePackage && !p.IsDeleted, cancellationToken) + ?? throw new NotFoundException("پکیج پایه یافت نشد"); } // 6. بررسی عضویت فعلی var existingMembership = await _context.ClubMemberships .FirstOrDefaultAsync(c => c.UserId == user.Id, cancellationToken); - // 6.1. دریافت مبلغ هدیه و هزینه فعالسازی از SystemConstants - long giftValue = SystemConstants.ClubMembershipGiftValue; - long activationFeeValue = SystemConstants.ClubActivationFee; + // 6.1. دریافت مبلغ هدیه و هزینه فعالسازی از پکیج + long giftValue = package.ActivationFee; + long activationFeeValue = package.ActivationFee; _logger.LogInformation( - "Using Club.MembershipGiftValue: {GiftValue}, Club.ActivationFee: {ActivationFee}", - giftValue, activationFeeValue + "Using Package {PackageId} ({PackageName}): GiftValue={GiftValue}, ActivationFee={ActivationFee}, Price={Price}", + package.Id, package.Title, giftValue, activationFeeValue, package.Price ); ClubMembership entity; @@ -188,8 +200,12 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler p.WeekDefinitionId == currentWeekDefinitionId, cancellationToken); + .FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId && p.PackageId == package.Id, cancellationToken); if (weeklyPool == null) { - // ایجاد Pool جدید برای این هفته + // ایجاد Pool جدید برای این هفته × این پکیج weeklyPool = new WeeklyCommissionPool { WeekDefinitionId = currentWeekDefinitionId, - TotalPoolAmount = activationFeeValue, // مبلغ هدیه به Pool اضافه میشه + PackageId = package.Id, + TotalPoolAmount = activationFeeValue, TotalBalances = 0, // در CalculateWeeklyBalances محاسبه میشه ValuePerBalance = 0, // در CalculateWeeklyCommissionPool محاسبه میشه IsCalculated = false, diff --git a/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandHandler.cs b/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandHandler.cs index b7b2a0a..eeae512 100644 --- a/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandHandler.cs +++ b/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandHandler.cs @@ -82,11 +82,14 @@ public class CalculateWeeklyBalancesCommandHandler : IRequestHandler(); var calculatedAt = DateTime.Now; - // استفاده از SystemConstants (استاتیک - بدون کوئری به دیتابیس) - // سقف تعادل هفتگی برای هر دست (نه کل) - 300 برای چپ + 300 برای راست = حداکثر 600 تعادل - var maxBalancesPerLeg = SystemConstants.CommissionMaxWeeklyBalancesPerLeg; - // حداکثر عمق شبکه برای شمارش اعضا (15 لول) - var maxNetworkLevel = SystemConstants.CommissionMaxNetworkLevel; + // بارگذاری پکیج پایه برای خواندن سقف‌ها + var package = await _context.Packages + .FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken) + ?? throw new InvalidOperationException("پکیج پایه یافت نشد"); + + // خواندن سقف‌ها از پکیج (قبلاً از SystemConstants بود) + var maxBalancesPerLeg = package.MaxBalancesPerLeg; + var maxNetworkLevel = package.MaxNetworkLevel; foreach (var user in usersInNetwork.OrderBy(o=>o.Id)) { @@ -140,6 +143,7 @@ public class CalculateWeeklyBalancesCommandHandler : IRequestHandler p.IsBasePackage && !p.IsDeleted, cancellationToken) + ?? throw new InvalidOperationException("پکیج پایه یافت نشد"); + var maxNetworkLevel = package.MaxNetworkLevel; // دریافت همه تعادل‌های هفتگی (شامل صفرها هم برای محاسبه زیرمجموعه) var allWeeklyBalances = await _context.NetworkWeeklyBalances @@ -113,6 +116,7 @@ public class ProcessUserPayoutsCommandHandler : IRequestHandler p.IsBasePackage && !p.IsDeleted, cancellationToken) + ?? throw new InvalidOperationException("پکیج پایه یافت نشد"); + // 1. ایجاد تراکنش var transaction = new Transaction { - Amount = SystemConstants.DayaLoanAmount, + Amount = package.Price, Description = $"دریافت اعتبار دایا - قرارداد {contractNumber}", PaymentStatus = PaymentStatus.Success, PaymentDate = DateTime.Now, @@ -216,13 +221,13 @@ public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler p.Id == 4, cancellationToken); - - if (goldenPackage != null) + // 6. ثبت UserPackagePurchase برای پکیج پایه + var packagePurchase = new UserPackagePurchase { - // 6. ثبت UserPackagePurchase برای پکیج پایه - var goldenPackageId =goldenPackage.Id; - var packagePurchase = new UserPackagePurchase - { - UserId = user.Id, - PackageId = goldenPackageId, - PurchaseMethod = PackagePurchaseMethod.DayaLoan, - PurchasedAt = DateTime.Now, - Amount = SystemConstants.DayaLoanAmount, - TransactionId = transaction.Id - }; + UserId = user.Id, + PackageId = package.Id, + PurchaseMethod = PackagePurchaseMethod.DayaLoan, + PurchasedAt = DateTime.Now, + Amount = package.Price, + TransactionId = transaction.Id + }; - await _context.UserPackagePurchases.AddAsync(packagePurchase, cancellationToken); - - } + await _context.UserPackagePurchases.AddAsync(packagePurchase, cancellationToken); // 7. Domain Event user.AddDomainEvent(new DayaLoanApprovedEvent(user, transaction, contractNumber)); diff --git a/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/CreateManualPayment/CreateManualPaymentCommandHandler.cs b/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/CreateManualPayment/CreateManualPaymentCommandHandler.cs index 2b6a7ae..9402612 100644 --- a/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/CreateManualPayment/CreateManualPaymentCommandHandler.cs +++ b/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/CreateManualPayment/CreateManualPaymentCommandHandler.cs @@ -70,9 +70,13 @@ public class CreateManualPaymentCommandHandler : IRequestHandler p.IsBasePackage && !p.IsDeleted, cancellationToken) + ?? throw new NotFoundException("پکیج پایه یافت نشد"); + + var balanceAmount = package.Price; + var discountBalanceAmount = (long)(package.Price * package.DiscountMultiplier); // 5. ثبت تراکنش diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommandHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommandHandler.cs index b921ab1..0fde9b1 100644 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommandHandler.cs +++ b/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommandHandler.cs @@ -20,10 +20,7 @@ public class InitiateBasePackagePaymentCommandHandler : IRequestHandler _logger; - /// - /// شناسه پکیج پایه در دیتابیس - /// - private const long BasePackageId = 4; + // شناسه پکیج پایه از دیتابیس خوانده می‌شود (Package.IsBasePackage) public InitiateBasePackagePaymentCommandHandler( IApplicationDbContext context, @@ -68,12 +65,17 @@ public class InitiateBasePackagePaymentCommandHandler : IRequestHandler p.IsBasePackage && !p.IsDeleted, cancellationToken) + ?? throw new NotFoundException("پکیج پایه یافت نشد"); + // 3. بررسی عدم وجود سفارش Pending var pendingOrder = await _context.UserOrders .FirstOrDefaultAsync(o => o.UserId == request.UserId && o.PaymentStatus == PaymentStatus.Pending && - o.PackageId == BasePackageId, + o.PackageId == package.Id, cancellationToken); if (pendingOrder != null) @@ -93,7 +95,7 @@ public class InitiateBasePackagePaymentCommandHandler : IRequestHandler 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) var oldBalance = userWallet.Balance; var oldDiscountBalance = userWallet.DiscountBalance; - userWallet.Balance += SystemConstants.BasePackageAmount; - userWallet.DiscountBalance += SystemConstants.BasePackageAmount * 2; + userWallet.Balance += package.Price; + userWallet.DiscountBalance += (long)(package.Price * package.DiscountMultiplier); _logger.LogInformation( "Charging wallet for user {UserId}. Balance: {OldBalance} -> {NewBalance}, DiscountBalance: {OldDiscount} -> {NewDiscount}", @@ -153,11 +161,11 @@ public class VerifyBasePackagePaymentCommandHandler : IRequestHandler 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) - var remainingDeposit = SystemConstants.MagicWalletMaxDeposit - wallet.MagicTotalDeposited; + var remainingDeposit = package.MagicWalletMaxDeposit - wallet.MagicTotalDeposited; if (remainingDeposit <= 0) { diff --git a/src/CMSMicroservice.Application/WalletCQ/Commands/VerifyMagicWalletCharge/VerifyMagicWalletChargeCommandHandler.cs b/src/CMSMicroservice.Application/WalletCQ/Commands/VerifyMagicWalletCharge/VerifyMagicWalletChargeCommandHandler.cs index 9bf541c..ab78b4e 100644 --- a/src/CMSMicroservice.Application/WalletCQ/Commands/VerifyMagicWalletCharge/VerifyMagicWalletChargeCommandHandler.cs +++ b/src/CMSMicroservice.Application/WalletCQ/Commands/VerifyMagicWalletCharge/VerifyMagicWalletChargeCommandHandler.cs @@ -121,8 +121,17 @@ public class VerifyMagicWalletChargeCommandHandler throw new BadRequestException("کیف‌پول در حالت جادویی نیست"); } - // 5. محاسبه اعتبار ×2.5 - var creditAmount = (long)(depositAmount * SystemConstants.MagicWalletMultiplier); // مبلغ × 2.5 + // 4.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 // 6. ثبت تراکنش واریز واقعی (MagicWalletDeposit) @@ -143,7 +152,7 @@ public class VerifyMagicWalletChargeCommandHandler var bonusTransaction = new Transaction { Amount = bonusAmount, - Description = $"شارژ کیف‌پول جادویی - بونوس ×{SystemConstants.MagicWalletMultiplier - 1} - کاربر {userId}", + Description = $"شارژ کیف‌پول جادویی - بونوس ×{package.MagicWalletMultiplier - 1} - کاربر {userId}", PaymentStatus = PaymentStatus.Success, PaymentDate = DateTime.UtcNow, RefId = $"MAGIC_BONUS_{depositTransaction.Id}", @@ -186,10 +195,10 @@ public class VerifyMagicWalletChargeCommandHandler userId, depositAmount, creditAmount, - SystemConstants.MagicWalletMultiplier, + package.MagicWalletMultiplier, bonusAmount, wallet.MagicTotalDeposited, - SystemConstants.MagicWalletMaxDeposit, + package.MagicWalletMaxDeposit, wallet.Balance ); diff --git a/src/CMSMicroservice.Infrastructure/Services/Commission/OrmCommissionCalculationStrategy.cs b/src/CMSMicroservice.Infrastructure/Services/Commission/OrmCommissionCalculationStrategy.cs index 5db7dff..3a8faa6 100644 --- a/src/CMSMicroservice.Infrastructure/Services/Commission/OrmCommissionCalculationStrategy.cs +++ b/src/CMSMicroservice.Infrastructure/Services/Commission/OrmCommissionCalculationStrategy.cs @@ -94,9 +94,12 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy var balancesList = new List(); var calculatedAt = DateTime.Now; - // استفاده از SystemConstants به جای دیتابیس - var maxBalancesPerLeg = SystemConstants.CommissionMaxWeeklyBalancesPerLeg; - var maxNetworkLevel = SystemConstants.CommissionMaxNetworkLevel; + // بارگذاری پکیج پایه برای خواندن سقف‌ها + var package = await _context.Packages + .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)) { @@ -135,6 +138,7 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy { UserId = user.Id, WeekDefinitionId = weekDefinitionId, + PackageId = package.Id, LeftLegNewMembers = leftNewMembers, RightLegNewMembers = rightNewMembers, LeftLegCarryover = leftCarryover, @@ -189,6 +193,11 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy bool forceRecalculate, CancellationToken cancellationToken = default) { + // بارگذاری پکیج پایه برای تعیین PackageId روی پرداخت‌ها + var package = await _context.Packages + .FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken) + ?? throw new InvalidOperationException("پکیج پایه یافت نشد"); + var weekDefinition = _weekRepository.GetGregorianWeekNumber(weekDefinitionId); if (weekDefinition == null) { @@ -284,6 +293,7 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy UserId = balance.UserId, WeekDefinitionId = weekDefinitionId, WeeklyPoolId = existingPool.Id, + PackageId = package.Id, BalancesEarned = userBalance, ValuePerBalance = valuePerBalance, TotalAmount = totalAmount, diff --git a/src/CMSMicroservice.WebApi/Services/ConfigurationService.cs b/src/CMSMicroservice.WebApi/Services/ConfigurationService.cs index b2e4c7f..f6f0856 100644 --- a/src/CMSMicroservice.WebApi/Services/ConfigurationService.cs +++ b/src/CMSMicroservice.WebApi/Services/ConfigurationService.cs @@ -2,37 +2,42 @@ using CMSMicroservice.Application.ClubFeatureCQ.Queries.GetUserClubFeatures; using CMSMicroservice.Application.Common.Authorization; using CMSMicroservice.Application.Common.Interfaces; using CMSMicroservice.Domain.Common; +using CMSMicroservice.Domain.Entities; using CMSMicroservice.Protobuf.Protos.Configuration; using Google.Protobuf.WellKnownTypes; using Grpc.Core; using MediatR; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; namespace CMSMicroservice.WebApi.Services; /// -/// سرویس تنظیمات سیستم - خواندن از SystemConstants +/// سرویس تنظیمات سیستم — مقادیر پکیجی از Package entity خوانده می‌شوند /// public class ConfigurationService : ConfigurationContract.ConfigurationContractBase { private readonly ILogger _logger; private readonly ICurrentUserService _currentUserService; private readonly IMediator _mediator; + private readonly IApplicationDbContext _context; public ConfigurationService( ILogger logger, ICurrentUserService currentUserService, - IMediator mediator) + IMediator mediator, + IApplicationDbContext context) { _logger = logger; _currentUserService = currentUserService; _mediator = mediator; + _context = context; } /// /// دریافت تنظیمات با کلید خاص /// - public override Task GetConfigurationByKey(GetConfigurationByKeyRequest request, ServerCallContext context) + public override async Task GetConfigurationByKey(GetConfigurationByKeyRequest request, ServerCallContext context) { var response = new GetConfigurationByKeyResponse { @@ -43,30 +48,33 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB 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); _logger.LogDebug("Configuration requested: Key={Key}, Value={Value}", request.Key, response.Value); - return Task.FromResult(response); + return response; } /// /// دریافت تنظیمات باشگاه مشتریان /// - public override Task GetClubConfiguration(Empty request, ServerCallContext context) + public override async Task GetClubConfiguration(Empty request, ServerCallContext context) { + var package = await GetBasePackageAsync(context.CancellationToken); + var response = new GetClubConfigurationResponse { - ActivationFee = SystemConstants.ClubActivationFee, - MembershipGiftValue = SystemConstants.ClubMembershipGiftValue + ActivationFee = package.ActivationFee, + MembershipGiftValue = package.ActivationFee }; _logger.LogDebug("Club configuration requested: ActivationFee={ActivationFee}, GiftValue={GiftValue}", response.ActivationFee, response.MembershipGiftValue); - return Task.FromResult(response); + return response; } /// @@ -111,18 +119,19 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB /// دریافت تمام تنظیمات /// [RequiresPermission(PermissionNames.SettingsView)] - public override Task GetAllConfigurations(GetAllConfigurationsRequest request, ServerCallContext context) + public override async Task GetAllConfigurations(GetAllConfigurationsRequest request, ServerCallContext context) { + var package = await GetBasePackageAsync(context.CancellationToken); var response = new GetAllConfigurationsResponse(); - // Club Settings - response.Models.Add(CreateConfigModel("Club.ActivationFee", SystemConstants.ClubActivationFee.ToString(), "هزینه فعال‌سازی عضویت باشگاه", 2)); - response.Models.Add(CreateConfigModel("Club.MembershipGiftValue", SystemConstants.ClubMembershipGiftValue.ToString(), "مبلغ هدیه حق عضویت باشگاه", 2)); + // Club Settings (از Package) + response.Models.Add(CreateConfigModel("Club.ActivationFee", package.ActivationFee.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.MaxWeeklyBalancesPerLeg", SystemConstants.CommissionMaxWeeklyBalancesPerLeg.ToString(), "سقف تعادل هفتگی برای هر دست", 3)); - response.Models.Add(CreateConfigModel("Commission.MaxNetworkLevel", SystemConstants.CommissionMaxNetworkLevel.ToString(), "حداکثر عمق شبکه برای محاسبه کمیسیون", 3)); + response.Models.Add(CreateConfigModel("Commission.MaxWeeklyBalancesPerLeg", package.MaxBalancesPerLeg.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.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.MaxChildrenPerLeg", SystemConstants.NetworkMaxChildrenPerLeg.ToString(), "حداکثر تعداد فرزند مستقیم در هر پا", 1)); - // Package Settings - response.Models.Add(CreateConfigModel("Package.BasePackageAmount", SystemConstants.BasePackageAmount.ToString(), "مبلغ پکیج پایه", 0)); - response.Models.Add(CreateConfigModel("Package.DayaLoanAmount", SystemConstants.DayaLoanAmount.ToString(), "مبلغ وام دایا", 0)); + // Package Settings (از Package) + response.Models.Add(CreateConfigModel("Package.BasePackageAmount", package.Price.ToString(), "مبلغ پکیج پایه", 0)); + response.Models.Add(CreateConfigModel("Package.DayaLoanAmount", package.Price.ToString(), "مبلغ وام دایا", 0)); // System Settings response.Models.Add(CreateConfigModel("System.MaintenanceMode", SystemConstants.SystemMaintenanceMode.ToString(), "حالت تعمیر و نگهداری", 0)); @@ -141,7 +150,7 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB // Shop Settings response.Models.Add(CreateConfigModel("Shop.VAT", SystemConstants.ShopVAT.ToString(), "مالیات بر ارزش افزوده", 0)); - return Task.FromResult(response); + return response; } /// @@ -173,21 +182,21 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB #region Private Helpers - private static string GetConfigurationValue(string key) + private static string GetConfigurationValue(string key, Package? package) { return key switch { - "Club.ActivationFee" => SystemConstants.ClubActivationFee.ToString(), - "Club.MembershipGiftValue" => SystemConstants.ClubMembershipGiftValue.ToString(), + "Club.ActivationFee" => (package?.ActivationFee ?? 0).ToString(), + "Club.MembershipGiftValue" => (package?.ActivationFee ?? 0).ToString(), "Commission.MinWithdrawalAmount" => SystemConstants.CommissionMinWithdrawalAmount.ToString(), - "Commission.MaxWeeklyBalancesPerLeg" => SystemConstants.CommissionMaxWeeklyBalancesPerLeg.ToString(), - "Commission.MaxNetworkLevel" => SystemConstants.CommissionMaxNetworkLevel.ToString(), + "Commission.MaxWeeklyBalancesPerLeg" => (package?.MaxBalancesPerLeg ?? 0).ToString(), + "Commission.MaxNetworkLevel" => (package?.MaxNetworkLevel ?? 0).ToString(), "Commission.CashWithdrawalEnabled" => SystemConstants.CommissionCashWithdrawalEnabled.ToString(), "Commission.CalculationStrategy" => SystemConstants.CommissionCalculationStrategy, "Network.AllowOrphanNodes" => SystemConstants.NetworkAllowOrphanNodes.ToString(), "Network.MaxChildrenPerLeg" => SystemConstants.NetworkMaxChildrenPerLeg.ToString(), - "Package.BasePackageAmount" => SystemConstants.BasePackageAmount.ToString(), - "Package.DayaLoanAmount" => SystemConstants.DayaLoanAmount.ToString(), + "Package.BasePackageAmount" => (package?.Price ?? 0).ToString(), + "Package.DayaLoanAmount" => (package?.Price ?? 0).ToString(), "System.MaintenanceMode" => SystemConstants.SystemMaintenanceMode.ToString(), "System.EnableAuditLog" => SystemConstants.SystemEnableAuditLog.ToString(), "Shop.VAT" => SystemConstants.ShopVAT.ToString(), @@ -195,6 +204,13 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB }; } + private async Task 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) { return key switch diff --git a/src/CMSMicroservice.WebApi/Services/UserOrderService.cs b/src/CMSMicroservice.WebApi/Services/UserOrderService.cs index c55abcc..81eca8d 100644 --- a/src/CMSMicroservice.WebApi/Services/UserOrderService.cs +++ b/src/CMSMicroservice.WebApi/Services/UserOrderService.cs @@ -344,9 +344,18 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase .Include(u => u.ClubMembership) .FirstOrDefaultAsync(u => u.Id == userId, context.CancellationToken); - if (wallet.WalletMode == WalletMode.Magic - && wallet.MagicTotalDeposited >= SystemConstants.MagicWalletMaxDeposit) + if (wallet.WalletMode == WalletMode.Magic) { + // بارگذاری پکیج کاربر برای سقف کیف‌پول جادویی + 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 ── // هر دو شرط: Balance=0 و سقف 100M پر شده wallet.WalletMode = WalletMode.Normal; @@ -366,6 +375,7 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase { user.PackagePurchaseMethod = PackagePurchaseMethod.None; } + } } else if (wallet.WalletMode == WalletMode.Normal && user != null diff --git a/src/CMSMicroservice.WebApi/Services/UserWalletService.cs b/src/CMSMicroservice.WebApi/Services/UserWalletService.cs index f4760cc..5c8fc70 100644 --- a/src/CMSMicroservice.WebApi/Services/UserWalletService.cs +++ b/src/CMSMicroservice.WebApi/Services/UserWalletService.cs @@ -227,13 +227,21 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase var cycleCount = await _context.ClubMembershipCycles .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 { WalletMode = (int)wallet.WalletMode, MagicTotalDeposited = wallet.MagicTotalDeposited, MagicTotalCredited = wallet.MagicTotalCredited, - MagicMaxDeposit = SystemConstants.MagicWalletMaxDeposit, - MagicRemainingDeposit = Math.Max(0, SystemConstants.MagicWalletMaxDeposit - wallet.MagicTotalDeposited), + MagicMaxDeposit = magicMaxDeposit, + MagicRemainingDeposit = Math.Max(0, magicMaxDeposit - wallet.MagicTotalDeposited), Balance = wallet.Balance, PurchaseCycleCount = cycleCount };