diff --git a/src/CMSMicroservice.Application/DayaLoanCQ/Commands/CheckDayaLoanStatus/CheckDayaLoanStatusCommandHandler.cs b/src/CMSMicroservice.Application/DayaLoanCQ/Commands/CheckDayaLoanStatus/CheckDayaLoanStatusCommandHandler.cs
deleted file mode 100644
index e25975c..0000000
--- a/src/CMSMicroservice.Application/DayaLoanCQ/Commands/CheckDayaLoanStatus/CheckDayaLoanStatusCommandHandler.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using CMSMicroservice.Domain.Enums;
-using CMSMicroservice.Application.DayaLoanCQ.Services;
-using Microsoft.Extensions.Logging;
-
-namespace CMSMicroservice.Application.DayaLoanCQ.Commands.CheckDayaLoanStatus;
-
-///
-/// این Handler فقط وظیفه استعلام از API دایا و بهروزرسانی DayaLoanContracts را دارد.
-/// پردازش مالی (شارژ کیف پول، ثبت Order و...) توسط Worker و ProcessDayaLoanApprovalCommand انجام میشود.
-///
-public class CheckDayaLoanStatusCommandHandler : IRequestHandler
-{
- private readonly IApplicationDbContext _context;
- private readonly IDayaLoanApiService _dayaApiService;
- private readonly ILogger _logger;
-
- public CheckDayaLoanStatusCommandHandler(
- IApplicationDbContext context,
- IDayaLoanApiService dayaApiService,
- ILogger logger)
- {
- _context = context;
- _dayaApiService = dayaApiService;
- _logger = logger;
- }
-
- public async Task Handle(CheckDayaLoanStatusCommand request, CancellationToken cancellationToken)
- {
- var results = new List();
-
- try
- {
- // فراخوانی سرویس دایا (Mock یا Real)
- var dayaResults = await _dayaApiService.CheckLoanStatusAsync(request.NationalCodes, cancellationToken);
-
- foreach (var dayaResult in dayaResults)
- {
- try
- {
- results.Add(new DayaLoanStatusItem
- {
- NationalCode = dayaResult.NationalCode,
- Status = dayaResult.Status,
- ContractNumber = dayaResult.ContractNumber,
- Message = "استعلام موفق"
- });
-
- // ذخیره یا بهروزرسانی در دیتابیس (فقط اطلاعات قرارداد - بدون پردازش مالی)
- var existingContract = await _context.DayaLoanContracts
- .FirstOrDefaultAsync(d => d.NationalCode == dayaResult.NationalCode, cancellationToken);
-
- if (existingContract != null)
- {
- // بهروزرسانی قرارداد موجود
- existingContract.LastCheckDate = DateTime.Now;
- existingContract.Status = dayaResult.Status;
- existingContract.ContractNumber = dayaResult.ContractNumber;
-
- _logger.LogDebug(
- "Updated DayaLoanContract for NationalCode {NationalCode}, Status: {Status}",
- dayaResult.NationalCode, dayaResult.Status);
- }
- else
- {
- // ایجاد رکورد جدید قرارداد
- var user = await _context.Users
- .FirstOrDefaultAsync(u => u.NationalCode == dayaResult.NationalCode, cancellationToken);
-
- if (user != null)
- {
- var newContract = new DayaLoanContract
- {
- UserId = user.Id,
- NationalCode = dayaResult.NationalCode,
- Status = dayaResult.Status,
- ContractNumber = dayaResult.ContractNumber,
- LastCheckDate = DateTime.Now,
- IsProcessed = false // پردازش مالی توسط Worker انجام میشود
- };
-
- await _context.DayaLoanContracts.AddAsync(newContract, cancellationToken);
-
- _logger.LogInformation(
- "Created new DayaLoanContract for User {UserId}, NationalCode: {NationalCode}, Status: {Status}",
- user.Id, dayaResult.NationalCode, dayaResult.Status);
- }
- }
-
- await _context.SaveChangesAsync(cancellationToken);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error processing Daya result for {NationalCode}", dayaResult.NationalCode);
- }
- }
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error calling Daya API service");
-
- // در صورت خطا، نتایج خالی برمیگردانیم
- foreach (var nationalCode in request.NationalCodes)
- {
- results.Add(new DayaLoanStatusItem
- {
- NationalCode = nationalCode,
- Status = DayaLoanStatus.PendingReceive,
- ContractNumber = null,
- Message = $"خطا در استعلام: {ex.Message}"
- });
- }
- }
-
- return new CheckDayaLoanStatusResponseDto
- {
- Results = results,
- TotalChecked = request.NationalCodes.Count,
- SuccessCount = results.Count(r => r.ContractNumber != null)
- };
- }
-}
diff --git a/src/CMSMicroservice.Application/DayaLoanCQ/Commands/ProcessDayaLoanApproval/ProcessDayaLoanApprovalCommandHandler.cs b/src/CMSMicroservice.Application/DayaLoanCQ/Commands/ProcessDayaLoanApproval/ProcessDayaLoanApprovalCommandHandler.cs
deleted file mode 100644
index 6d9651b..0000000
--- a/src/CMSMicroservice.Application/DayaLoanCQ/Commands/ProcessDayaLoanApproval/ProcessDayaLoanApprovalCommandHandler.cs
+++ /dev/null
@@ -1,202 +0,0 @@
-using CMSMicroservice.Domain.Events;
-using CMSMicroservice.Domain.Enums;
-using CMSMicroservice.Domain.Common;
-using CMSMicroservice.Application.Common.Interfaces;
-using Microsoft.Extensions.Logging;
-
-namespace CMSMicroservice.Application.DayaLoanCQ.Commands.ProcessDayaLoanApproval;
-
-public class ProcessDayaLoanApprovalCommandHandler : IRequestHandler
-{
- private readonly IApplicationDbContext _context;
- private readonly IKavenegarService _smsService;
- private readonly ILogger _logger;
-
- public ProcessDayaLoanApprovalCommandHandler(
- IApplicationDbContext context,
- IKavenegarService smsService,
- ILogger logger)
- {
- _context = context;
- _smsService = smsService;
- _logger = logger;
- }
-
- public async Task Handle(ProcessDayaLoanApprovalCommand request, CancellationToken cancellationToken)
- {
- // پیدا کردن کاربر
- var user = await _context.Users
- .Include(u => u.UserWallets)
- .FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);
-
- if (user == null)
- {
- throw new NotFoundException(nameof(User), request.UserId);
- }
-
- // چک کردن که قبلاً دریافت نکرده باشد
- if (user.HasReceivedDayaCredit)
- {
- throw new InvalidOperationException($"کاربر {request.UserId} قبلاً اعتبار دایا را دریافت کرده است");
- }
-
- // ایجاد تراکنش با RefId = شماره قرارداد دایا
- var transaction = new Transaction
- {
- Amount = request.WalletAmount , // 168 میلیون
- Description = $"دریافت اعتبار دایا - قرارداد {request.ContractNumber}",
- PaymentStatus = PaymentStatus.Success,
- PaymentDate = DateTime.Now,
- RefId = request.ContractNumber, // شماره قرارداد دایا
- Type = TransactionType.DepositExternal1
- };
-
- await _context.Transactions.AddAsync(transaction, cancellationToken);
- await _context.SaveChangesAsync(cancellationToken);
-
- // یافتن یا ایجاد کیف پول کاربر
- var wallet = user.UserWallets.FirstOrDefault();
- if (wallet == null)
- {
- wallet = new UserWallet
- {
- UserId = request.UserId,
- Balance = 0,
- NetworkBalance = 0,
- DiscountBalance = 0
- };
- await _context.UserWallets.AddAsync(wallet, cancellationToken);
- await _context.SaveChangesAsync(cancellationToken);
- }
-
- // شارژ کیف پول عادی (56 میلیون)
- var balanceBeforeMain = wallet.Balance;
- wallet.Balance += request.WalletAmount;
-
- // لاگ کیف پول عادی
- var mainLog = new UserWalletChangeLog
- {
- WalletId = wallet.Id,
- CurrentBalance = wallet.Balance,
- ChangeValue = request.WalletAmount,
- CurrentNetworkBalance = wallet.NetworkBalance,
- ChangeNerworkValue = 0,
- IsIncrease = true,
- RefrenceId = transaction.Id
- };
- await _context.UserWalletChangeLogs.AddAsync(mainLog, cancellationToken);
-
-
-
- // شارژ کیف پول تخفیف (56 میلیون)
- var balanceBeforeDiscount = wallet.DiscountBalance;
- wallet.DiscountBalance += request.DiscountWalletAmount;
-
- // لاگ کیف پول تخفیف
- var discountLog = new UserWalletChangeLog
- {
- WalletId = wallet.Id,
- CurrentBalance = wallet.Balance,
- ChangeValue = 0,
- CurrentNetworkBalance = wallet.NetworkBalance,
- ChangeNerworkValue = 0,
- CurrentDiscountBalance = wallet.DiscountBalance,
- ChangeDiscountValue = request.DiscountWalletAmount,
- IsIncrease = true,
- RefrenceId = transaction.Id
- };
- await _context.UserWalletChangeLogs.AddAsync(discountLog, cancellationToken);
-
- // بهروزرسانی وضعیت کاربر
- user.HasReceivedDayaCredit = true;
- user.DayaCreditReceivedAt = DateTime.Now;
-
- // تنظیم نحوه خرید پکیج به DayaLoan
- user.PackagePurchaseMethod = PackagePurchaseMethod.DayaLoan;
-
- // ثبت سفارش پکیج (فعلاً پکیج پایه)
- var goldenPackage = await _context.Packages
- .FirstOrDefaultAsync(p => p.Id==4, cancellationToken);
-
- if (goldenPackage != null)
- {
- // پیدا کردن آدرس پیشفرض کاربر
- var defaultAddress = await _context.UserAddresses
- .Where(a => a.UserId == request.UserId)
- .OrderByDescending(a => a.Created)
- .FirstOrDefaultAsync(cancellationToken);
-
- if (defaultAddress != null)
- {
- var packageOrder = new UserOrder
- {
- UserId = request.UserId,
- PackageId = goldenPackage.Id,
- Amount = request.WalletAmount, // 56 میلیون
- PaymentStatus = PaymentStatus.Success,
- PaymentDate = DateTime.Now,
- DeliveryStatus = DeliveryStatus.None,
- UserAddressId = defaultAddress.Id,
- TransactionId = transaction.Id,
- PaymentMethod = PaymentMethod.IPG
- };
-
- await _context.UserOrders.AddAsync(packageOrder, cancellationToken);
- }
- }
-
- // ثبت Event
- user.AddDomainEvent(new DayaLoanApprovedEvent(user, transaction, request.ContractNumber));
-
- // بهروزرسانی DayaLoanContract.IsProcessed
- var dayaContract = await _context.DayaLoanContracts
- .FirstOrDefaultAsync(d => d.UserId == request.UserId || d.ContractNumber == request.ContractNumber, cancellationToken);
-
- if (dayaContract != null)
- {
- dayaContract.IsProcessed = true;
- _logger.LogInformation("Marked DayaLoanContract as processed for User {UserId}", request.UserId);
- }
-
- await _context.SaveChangesAsync(cancellationToken);
-
- // ارسال SMS به کاربر
- await SendDayaLoanSmsAsync(user);
-
- return new ProcessDayaLoanApprovalResponseDto
- {
- UserId = user.Id,
- TransactionId = transaction.Id,
- ContractNumber = request.ContractNumber,
- MainWalletBalance = wallet.Balance,
- LockedWalletBalance = wallet.NetworkBalance,
- DiscountWalletBalance = wallet.DiscountBalance,
- Message = "اعتبار دایا با موفقیت دریافت شد"
- };
- }
-
- ///
- /// ارسال SMS اطلاعرسانی دریافت اعتبار دایا
- ///
- private async Task SendDayaLoanSmsAsync(User user)
- {
- try
- {
- var userName = SmsTemplates.GetUserName(user.FirstName);
- var message = SmsTemplates.DayaLoanReceived(userName);
-
- await _smsService.SendAsync(user.Mobile, message);
-
- _logger.LogInformation(
- "Daya loan SMS sent to User {UserId}, Mobile: {Mobile}",
- user.Id, user.Mobile);
- }
- catch (Exception ex)
- {
- // خطای SMS نباید فرایند اصلی رو متوقف کنه
- _logger.LogError(ex,
- "Failed to send Daya loan SMS to User {UserId}, Mobile: {Mobile}",
- user.Id, user.Mobile);
- }
- }
-}
diff --git a/src/CMSMicroservice.WebApi/Services/ConfigurationService.cs b/src/CMSMicroservice.WebApi/Services/ConfigurationService.cs
new file mode 100644
index 0000000..fa0fda6
--- /dev/null
+++ b/src/CMSMicroservice.WebApi/Services/ConfigurationService.cs
@@ -0,0 +1,162 @@
+using CMSMicroservice.Domain.Common;
+using CMSMicroservice.Protobuf.Protos.Configuration;
+using Google.Protobuf.WellKnownTypes;
+using Grpc.Core;
+using Microsoft.Extensions.Logging;
+
+namespace CMSMicroservice.WebApi.Services;
+
+///
+/// سرویس تنظیمات سیستم - خواندن از SystemConstants
+///
+public class ConfigurationService : ConfigurationContract.ConfigurationContractBase
+{
+ private readonly ILogger _logger;
+
+ public ConfigurationService(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ ///
+ /// دریافت تنظیمات با کلید خاص
+ ///
+ public override Task GetConfigurationByKey(GetConfigurationByKeyRequest request, ServerCallContext context)
+ {
+ var response = new GetConfigurationByKeyResponse
+ {
+ Key = request.Key,
+ Scope = request.Scope,
+ IsActive = true,
+ Created = Timestamp.FromDateTime(DateTime.UtcNow),
+ LastModified = Timestamp.FromDateTime(DateTime.UtcNow)
+ };
+
+ // خواندن مقدار از SystemConstants بر اساس کلید
+ response.Value = GetConfigurationValue(request.Key);
+ response.Description = GetConfigurationDescription(request.Key);
+
+ _logger.LogDebug("Configuration requested: Key={Key}, Value={Value}", request.Key, response.Value);
+
+ return Task.FromResult(response);
+ }
+
+ ///
+ /// دریافت تمام تنظیمات
+ ///
+ public override Task GetAllConfigurations(GetAllConfigurationsRequest request, ServerCallContext context)
+ {
+ 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));
+
+ // Commission Settings
+ 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.CashWithdrawalEnabled", SystemConstants.CommissionCashWithdrawalEnabled.ToString(), "امکان برداشت نقدی", 3));
+ response.Models.Add(CreateConfigModel("Commission.CalculationStrategy", SystemConstants.CommissionCalculationStrategy, "روش محاسبه کمیسیون", 3));
+
+ // Network Settings
+ 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));
+
+ // System Settings
+ response.Models.Add(CreateConfigModel("System.MaintenanceMode", SystemConstants.SystemMaintenanceMode.ToString(), "حالت تعمیر و نگهداری", 0));
+ response.Models.Add(CreateConfigModel("System.EnableAuditLog", SystemConstants.SystemEnableAuditLog.ToString(), "فعالسازی لاگ تغییرات", 0));
+
+ // Shop Settings
+ response.Models.Add(CreateConfigModel("Shop.VAT", SystemConstants.ShopVAT.ToString(), "مالیات بر ارزش افزوده", 0));
+
+ return Task.FromResult(response);
+ }
+
+ ///
+ /// سایر عملیاتها که فعلاً پیادهسازی نشدهاند (چون از constant استفاده میکنیم)
+ ///
+ public override Task CreateOrUpdateConfiguration(CreateOrUpdateConfigurationRequest request, ServerCallContext context)
+ {
+ _logger.LogWarning("CreateOrUpdateConfiguration called but SystemConstants are read-only");
+ throw new RpcException(new Status(StatusCode.Unimplemented, "تنظیمات سیستم فقط خواندنی هستند"));
+ }
+
+ public override Task DeactivateConfiguration(DeactivateConfigurationRequest request, ServerCallContext context)
+ {
+ _logger.LogWarning("DeactivateConfiguration called but SystemConstants are read-only");
+ throw new RpcException(new Status(StatusCode.Unimplemented, "تنظیمات سیستم فقط خواندنی هستند"));
+ }
+
+ public override Task GetConfigurationHistory(GetConfigurationHistoryRequest request, ServerCallContext context)
+ {
+ // چون constant هستند، تاریخچهای وجود نداره
+ return Task.FromResult(new GetConfigurationHistoryResponse());
+ }
+
+ #region Private Helpers
+
+ private static string GetConfigurationValue(string key)
+ {
+ return key switch
+ {
+ "Club.ActivationFee" => SystemConstants.ClubActivationFee.ToString(),
+ "Club.MembershipGiftValue" => SystemConstants.ClubMembershipGiftValue.ToString(),
+ "Commission.MinWithdrawalAmount" => SystemConstants.CommissionMinWithdrawalAmount.ToString(),
+ "Commission.MaxWeeklyBalancesPerLeg" => SystemConstants.CommissionMaxWeeklyBalancesPerLeg.ToString(),
+ "Commission.MaxNetworkLevel" => SystemConstants.CommissionMaxNetworkLevel.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(),
+ "System.MaintenanceMode" => SystemConstants.SystemMaintenanceMode.ToString(),
+ "System.EnableAuditLog" => SystemConstants.SystemEnableAuditLog.ToString(),
+ "Shop.VAT" => SystemConstants.ShopVAT.ToString(),
+ _ => string.Empty
+ };
+ }
+
+ private static string GetConfigurationDescription(string key)
+ {
+ return key switch
+ {
+ "Club.ActivationFee" => "هزینه فعالسازی عضویت باشگاه (ریال)",
+ "Club.MembershipGiftValue" => "مبلغ هدیه حق عضویت باشگاه (ریال)",
+ "Commission.MinWithdrawalAmount" => "حداقل مبلغ برداشت (ریال)",
+ "Commission.MaxWeeklyBalancesPerLeg" => "سقف تعادل هفتگی برای هر دست",
+ "Commission.MaxNetworkLevel" => "حداکثر عمق شبکه برای محاسبه کمیسیون",
+ "Commission.CashWithdrawalEnabled" => "امکان برداشت نقدی",
+ "Commission.CalculationStrategy" => "روش محاسبه کمیسیون",
+ "Network.AllowOrphanNodes" => "اجازه حذف والدین با فرزند",
+ "Network.MaxChildrenPerLeg" => "حداکثر تعداد فرزند مستقیم در هر پا",
+ "Package.BasePackageAmount" => "مبلغ پکیج پایه (ریال)",
+ "Package.DayaLoanAmount" => "مبلغ وام دایا (ریال)",
+ "System.MaintenanceMode" => "حالت تعمیر و نگهداری سیستم",
+ "System.EnableAuditLog" => "فعالسازی لاگ تغییرات",
+ "Shop.VAT" => "مالیات بر ارزش افزوده",
+ _ => string.Empty
+ };
+ }
+
+ private static ConfigurationModel CreateConfigModel(string key, string value, string description, int scope)
+ {
+ return new ConfigurationModel
+ {
+ Id = key.GetHashCode(),
+ Key = key,
+ Value = value,
+ Description = description,
+ Scope = scope,
+ IsActive = true,
+ Created = Timestamp.FromDateTime(DateTime.UtcNow)
+ };
+ }
+
+ #endregion
+}