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;
///
/// سرویس تنظیمات سیستم — مقادیر پکیجی از 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,
IApplicationDbContext context)
{
_logger = logger;
_currentUserService = currentUserService;
_mediator = mediator;
_context = context;
}
///
/// دریافت تنظیمات با کلید خاص
///
public override async 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)
};
// برای کلیدهای پکیجی، از دیتابیس میخوانیم
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 response;
}
///
/// دریافت تنظیمات باشگاه مشتریان
///
public override async Task GetClubConfiguration(Empty request, ServerCallContext context)
{
var package = await GetBasePackageAsync(context.CancellationToken);
var response = new GetClubConfigurationResponse
{
ActivationFee = package.ActivationFee,
MembershipGiftValue = package.ActivationFee
};
_logger.LogDebug("Club configuration requested: ActivationFee={ActivationFee}, GiftValue={GiftValue}",
response.ActivationFee, response.MembershipGiftValue);
return response;
}
///
/// دریافت ویژگیهای باشگاه مشتریان برای کاربر جاری
///
public override async Task GetClubFeatures(Empty request, ServerCallContext context)
{
// دریافت UserId از JWT
if (!long.TryParse(_currentUserService.UserId, out var userId) || userId <= 0)
{
_logger.LogWarning("GetClubFeatures called without valid user authentication");
return new GetClubFeaturesResponse(); // لیست خالی برای کاربران غیر احراز هویت شده
}
// فراخوانی GetUserClubFeatures از طریق MediatR (CQRS داخلی)
var query = new GetUserClubFeaturesQuery { UserId = userId };
var userFeatures = await _mediator.Send(query, context.CancellationToken);
// تبدیل به فرمت GetClubFeaturesResponse
var response = new GetClubFeaturesResponse();
foreach (var feature in userFeatures)
{
response.Features.Add(new ClubFeatureModel
{
Id = feature.ClubFeatureId,
Title = feature.FeatureTitle ?? string.Empty,
Description = feature.FeatureDescription ?? string.Empty,
IsEnabled = feature.IsActive,
DisplayOrder = feature.SortOrder,
GrantedAt = Timestamp.FromDateTime(DateTime.SpecifyKind(feature.GrantedAt, DateTimeKind.Utc)),
CreatedAt = Timestamp.FromDateTime(DateTime.SpecifyKind(feature.CreatedAt, DateTimeKind.Utc)),
Notes = feature.Notes ?? string.Empty
});
}
_logger.LogDebug("Club features requested for user {UserId}: {Count} features returned", userId, response.Features.Count);
return response;
}
///
/// دریافت تمام تنظیمات
///
[RequiresPermission(PermissionNames.SettingsView)]
public override async Task GetAllConfigurations(GetAllConfigurationsRequest request, ServerCallContext context)
{
var package = await GetBasePackageAsync(context.CancellationToken);
var response = new GetAllConfigurationsResponse();
// 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 (سقفها از Package)
response.Models.Add(CreateConfigModel("Commission.MinWithdrawalAmount", SystemConstants.CommissionMinWithdrawalAmount.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));
// Network Settings
response.Models.Add(CreateConfigModel("Network.AllowOrphanNodes", SystemConstants.NetworkAllowOrphanNodes.ToString(), "اجازه حذف والدین با فرزند", 1));
response.Models.Add(CreateConfigModel("Network.MaxChildrenPerLeg", SystemConstants.NetworkMaxChildrenPerLeg.ToString(), "حداکثر تعداد فرزند مستقیم در هر پا", 1));
// Package Settings (از Package)
response.Models.Add(CreateConfigModel("Package.BasePackageAmount", package.Price.ToString(), "مبلغ پکیج پایه", 0));
response.Models.Add(CreateConfigModel("Package.DayaLoanAmount", package.Price.ToString(), "مبلغ وام دایا", 0));
// Magic Wallet Settings (از Package)
response.Models.Add(CreateConfigModel("MagicWallet.Multiplier", package.MagicWalletMultiplier.ToString(), "ضریب شارژ کیفپول جادویی", 0));
response.Models.Add(CreateConfigModel("MagicWallet.MaxDeposit", package.MagicWalletMaxDeposit.ToString(), "سقف واریز هر دور جادویی", 0));
response.Models.Add(CreateConfigModel("MagicWallet.MaxCredit", package.MagicWalletMaxCredit.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 response;
}
///
/// سایر عملیاتها که فعلاً پیادهسازی نشدهاند (چون از constant استفاده میکنیم)
///
[RequiresPermission(PermissionNames.SettingsManageConfiguration)]
public override Task CreateOrUpdateConfiguration(CreateOrUpdateConfigurationRequest request, ServerCallContext context)
{
_logger.LogWarning("CreateOrUpdateConfiguration called but SystemConstants are read-only. Key: {Key}", request.Key);
throw new RpcException(new Status(StatusCode.FailedPrecondition, "تنظیمات سیستم فقط خواندنی هستند. تغییر از طریق کد انجام میشود"));
}
#region [ARCHIVED] DeactivateConfiguration & GetConfigurationHistory — SystemConstants ثابت هستند، این RPCها بیمعنی
[RequiresPermission(PermissionNames.SettingsManageConfiguration)]
[Obsolete("ARCHIVED: SystemConstants are read-only — deactivation is meaningless")]
public override Task DeactivateConfiguration(DeactivateConfigurationRequest request, ServerCallContext context)
{
_logger.LogWarning("[ARCHIVED] DeactivateConfiguration called but SystemConstants are read-only. Key: {Key}", request.Key);
throw new RpcException(new Status(StatusCode.FailedPrecondition, "تنظیمات سیستم فقط خواندنی هستند. غیرفعالسازی از طریق کد انجام میشود"));
}
[Obsolete("ARCHIVED: SystemConstants have no history — always returns empty")]
public override Task GetConfigurationHistory(GetConfigurationHistoryRequest request, ServerCallContext context)
{
// [ARCHIVED] چون constant هستند، تاریخچهای وجود نداره
return Task.FromResult(new GetConfigurationHistoryResponse());
}
#endregion
#region Private Helpers
private static string GetConfigurationValue(string key, Package? package)
{
return key switch
{
"Club.ActivationFee" => (package?.ActivationFee ?? 0).ToString(),
"Club.MembershipGiftValue" => (package?.ActivationFee ?? 0).ToString(),
"Commission.MinWithdrawalAmount" => SystemConstants.CommissionMinWithdrawalAmount.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" => (package?.Price ?? 0).ToString(),
"Package.DayaLoanAmount" => (package?.Price ?? 0).ToString(),
"MagicWallet.Multiplier" => (package?.MagicWalletMultiplier ?? 0).ToString(),
"MagicWallet.MaxDeposit" => (package?.MagicWalletMaxDeposit ?? 0).ToString(),
"MagicWallet.MaxCredit" => (package?.MagicWalletMaxCredit ?? 0).ToString(),
"System.MaintenanceMode" => SystemConstants.SystemMaintenanceMode.ToString(),
"System.EnableAuditLog" => SystemConstants.SystemEnableAuditLog.ToString(),
"Shop.VAT" => SystemConstants.ShopVAT.ToString(),
_ => string.Empty
};
}
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
{
"Club.ActivationFee" => "هزینه فعالسازی عضویت باشگاه (ریال)",
"Club.MembershipGiftValue" => "مبلغ هدیه حق عضویت باشگاه (ریال)",
"Commission.MinWithdrawalAmount" => "حداقل مبلغ برداشت (ریال)",
"Commission.MaxWeeklyBalancesPerLeg" => "سقف تعادل هفتگی برای هر دست",
"Commission.MaxNetworkLevel" => "حداکثر عمق شبکه برای محاسبه کمیسیون",
"Commission.CashWithdrawalEnabled" => "امکان برداشت نقدی",
"Commission.CalculationStrategy" => "روش محاسبه کمیسیون",
"Network.AllowOrphanNodes" => "اجازه حذف والدین با فرزند",
"Network.MaxChildrenPerLeg" => "حداکثر تعداد فرزند مستقیم در هر پا",
"Package.BasePackageAmount" => "مبلغ پکیج پایه (ریال)",
"Package.DayaLoanAmount" => "مبلغ وام دایا (ریال)",
"MagicWallet.Multiplier" => "ضریب شارژ کیفپول جادویی",
"MagicWallet.MaxDeposit" => "سقف واریز هر دور جادویی (ریال)",
"MagicWallet.MaxCredit" => "سقف اعتبار هر دور جادویی (ریال)",
"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
}