feat: implement Kavenegar SMS service and refactor system configurations to use static constants
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 1m38s
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 1m38s
This commit is contained in:
+8
-31
@@ -1,5 +1,6 @@
|
|||||||
using CMSMicroservice.Application.Common.Exceptions;
|
using CMSMicroservice.Application.Common.Exceptions;
|
||||||
using CMSMicroservice.Application.Common.Interfaces;
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
|
using CMSMicroservice.Domain.Common;
|
||||||
using CMSMicroservice.Domain.Entities;
|
using CMSMicroservice.Domain.Entities;
|
||||||
using CMSMicroservice.Domain.Entities.Club;
|
using CMSMicroservice.Domain.Entities.Club;
|
||||||
using CMSMicroservice.Domain.Entities.Commission;
|
using CMSMicroservice.Domain.Entities.Commission;
|
||||||
@@ -126,38 +127,14 @@ public class AcceptClubMembershipContractCommandHandler
|
|||||||
};
|
};
|
||||||
await _context.UserContracts.AddAsync(userContract, cancellationToken);
|
await _context.UserContracts.AddAsync(userContract, cancellationToken);
|
||||||
|
|
||||||
// 6. دریافت مقادیر از تنظیمات سیستم
|
// 6. دریافت مقادیر از SystemConstants (استاتیک)
|
||||||
var giftValueConfig = await _context.SystemConfigurations
|
long giftValue = SystemConstants.ClubMembershipGiftValue;
|
||||||
.FirstOrDefaultAsync(
|
long activationFeeValue = SystemConstants.ClubActivationFee;
|
||||||
c => c.Key == "Club.MembershipGiftValue" && c.IsActive,
|
|
||||||
cancellationToken
|
|
||||||
);
|
|
||||||
|
|
||||||
var activationFeeConfig = await _context.SystemConfigurations
|
_logger.LogInformation(
|
||||||
.FirstOrDefaultAsync(
|
"Using Club.MembershipGiftValue: {GiftValue}, Club.ActivationFee: {ActivationFee}",
|
||||||
c => c.Key == "Club.ActivationFee" && c.IsActive,
|
giftValue, activationFeeValue
|
||||||
cancellationToken
|
);
|
||||||
);
|
|
||||||
|
|
||||||
long giftValue = 28_000_000; // مقدار پیشفرض
|
|
||||||
if (giftValueConfig != null && long.TryParse(giftValueConfig.Value, out var configValue))
|
|
||||||
{
|
|
||||||
giftValue = configValue;
|
|
||||||
_logger.LogInformation(
|
|
||||||
"Using Club.MembershipGiftValue from configuration: {GiftValue}",
|
|
||||||
giftValue
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
long activationFeeValue = 25_200_000; // مقدار پیشفرض
|
|
||||||
if (activationFeeConfig != null && long.TryParse(activationFeeConfig.Value, out var activationFeeConfigValue))
|
|
||||||
{
|
|
||||||
activationFeeValue = activationFeeConfigValue;
|
|
||||||
_logger.LogInformation(
|
|
||||||
"Using Club.ActivationFee from configuration: {activationFeeValue}",
|
|
||||||
activationFeeValue
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. فعالسازی باشگاه مشتریان
|
// 7. فعالسازی باشگاه مشتریان
|
||||||
ClubMembership clubMembership;
|
ClubMembership clubMembership;
|
||||||
|
|||||||
+10
-46
@@ -1,6 +1,7 @@
|
|||||||
using CMSMicroservice.Application.Common.Exceptions;
|
using CMSMicroservice.Application.Common.Exceptions;
|
||||||
using CMSMicroservice.Application.Common.Interfaces;
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
using CMSMicroservice.Application.Common.Models;
|
using CMSMicroservice.Application.Common.Models;
|
||||||
|
using CMSMicroservice.Domain.Common;
|
||||||
using CMSMicroservice.Domain.Entities;
|
using CMSMicroservice.Domain.Entities;
|
||||||
using CMSMicroservice.Domain.Entities.Club;
|
using CMSMicroservice.Domain.Entities.Club;
|
||||||
using CMSMicroservice.Domain.Entities.Commission;
|
using CMSMicroservice.Domain.Entities.Commission;
|
||||||
@@ -75,7 +76,7 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
throw new NotFoundException("کیف پول کاربر یافت نشد");
|
throw new NotFoundException("کیف پول کاربر یافت نشد");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wallet.Balance < 56_000_000)
|
if (wallet.Balance < SystemConstants.BasePackageAmount)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(
|
_logger.LogWarning(
|
||||||
"User {UserId} has insufficient balance: {Balance}",
|
"User {UserId} has insufficient balance: {Balance}",
|
||||||
@@ -83,7 +84,7 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
wallet.Balance
|
wallet.Balance
|
||||||
);
|
);
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
"برای فعالسازی باشگاه مشتریان باید حداقل 56 میلیون تومان موجودی اصلی داشته باشید"
|
$"برای فعالسازی باشگاه مشتریان باید حداقل {SystemConstants.BasePackageAmount:N0} ریال موجودی اصلی داشته باشید"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,51 +136,14 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
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. دریافت مبلغ هدیه از تنظیمات
|
// 6.1. دریافت مبلغ هدیه و هزینه فعالسازی از SystemConstants
|
||||||
var giftValueConfig = await _context.SystemConfigurations
|
long giftValue = SystemConstants.ClubMembershipGiftValue;
|
||||||
.FirstOrDefaultAsync(
|
long activationFeeValue = SystemConstants.ClubActivationFee;
|
||||||
c => c.Key == "Club.MembershipGiftValue" && c.IsActive,
|
|
||||||
cancellationToken
|
|
||||||
);
|
|
||||||
|
|
||||||
var activationFeeConfig = await _context.SystemConfigurations
|
_logger.LogInformation(
|
||||||
.FirstOrDefaultAsync(
|
"Using Club.MembershipGiftValue: {GiftValue}, Club.ActivationFee: {ActivationFee}",
|
||||||
c => c.Key == "Club.ActivationFee" && c.IsActive,
|
giftValue, activationFeeValue
|
||||||
cancellationToken
|
);
|
||||||
);
|
|
||||||
|
|
||||||
long giftValue = 28_000_000; // مقدار پیشفرض
|
|
||||||
if (giftValueConfig != null && long.TryParse(giftValueConfig.Value, out var configValue))
|
|
||||||
{
|
|
||||||
giftValue = configValue;
|
|
||||||
_logger.LogInformation(
|
|
||||||
"Using Club.MembershipGiftValue from configuration: {GiftValue}",
|
|
||||||
giftValue
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogWarning(
|
|
||||||
"Club.MembershipGiftValue not found in configuration, using default: {GiftValue}",
|
|
||||||
giftValue
|
|
||||||
);
|
|
||||||
}
|
|
||||||
long activationFeeValue = 25_200_000; // مقدار پیشفرض
|
|
||||||
if (activationFeeConfig != null && long.TryParse(activationFeeConfig.Value, out var activationFeeConfigValue))
|
|
||||||
{
|
|
||||||
activationFeeValue = activationFeeConfigValue;
|
|
||||||
_logger.LogInformation(
|
|
||||||
"Using Club.ActivationFee from configuration: {activationFeeValue}",
|
|
||||||
activationFeeValue
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogWarning(
|
|
||||||
"Club.ActivationFee not found in configuration, using default: {activationFeeValue}",
|
|
||||||
activationFeeValue
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ClubMembership entity;
|
ClubMembership entity;
|
||||||
bool isNewMembership = existingMembership == null;
|
bool isNewMembership = existingMembership == null;
|
||||||
|
|||||||
+5
-13
@@ -1,3 +1,5 @@
|
|||||||
|
using CMSMicroservice.Domain.Common;
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.CommissionCQ.Commands.CalculateWeeklyBalances;
|
namespace CMSMicroservice.Application.CommissionCQ.Commands.CalculateWeeklyBalances;
|
||||||
|
|
||||||
public class CalculateWeeklyBalancesCommandHandler : IRequestHandler<CalculateWeeklyBalancesCommand, int>
|
public class CalculateWeeklyBalancesCommandHandler : IRequestHandler<CalculateWeeklyBalancesCommand, int>
|
||||||
@@ -74,21 +76,11 @@ public class CalculateWeeklyBalancesCommandHandler : IRequestHandler<CalculateWe
|
|||||||
var balancesList = new List<NetworkWeeklyBalance>();
|
var balancesList = new List<NetworkWeeklyBalance>();
|
||||||
var calculatedAt = DateTime.Now;
|
var calculatedAt = DateTime.Now;
|
||||||
|
|
||||||
// خواندن یکباره Configuration ها (بهینهسازی - به جای N query)
|
// استفاده از SystemConstants (استاتیک - بدون کوئری به دیتابیس)
|
||||||
var configs = await _context.SystemConfigurations
|
|
||||||
.Where(x => x.IsActive && (
|
|
||||||
x.Key == "Club.ActivationFee" ||
|
|
||||||
x.Key == "Commission.WeeklyPoolContributionPercent" ||
|
|
||||||
x.Key == "Commission.MaxWeeklyBalancesPerLeg" ||
|
|
||||||
x.Key == "Commission.MaxNetworkLevel"))
|
|
||||||
.ToDictionaryAsync(x => x.Key, x => x.Value, cancellationToken);
|
|
||||||
|
|
||||||
// var activationFee = long.Parse(configs.GetValueOrDefault("Club.ActivationFee", "25000000"));
|
|
||||||
// var poolPercent = decimal.Parse(configs.GetValueOrDefault("Commission.WeeklyPoolContributionPercent", "20")) / 100m;
|
|
||||||
// سقف تعادل هفتگی برای هر دست (نه کل) - 300 برای چپ + 300 برای راست = حداکثر 600 تعادل
|
// سقف تعادل هفتگی برای هر دست (نه کل) - 300 برای چپ + 300 برای راست = حداکثر 600 تعادل
|
||||||
var maxBalancesPerLeg = int.Parse(configs.GetValueOrDefault("Commission.MaxWeeklyBalancesPerLeg", "300"));
|
var maxBalancesPerLeg = SystemConstants.CommissionMaxWeeklyBalancesPerLeg;
|
||||||
// حداکثر عمق شبکه برای شمارش اعضا (15 لول)
|
// حداکثر عمق شبکه برای شمارش اعضا (15 لول)
|
||||||
var maxNetworkLevel = int.Parse(configs.GetValueOrDefault("Commission.MaxNetworkLevel", "15"));
|
var maxNetworkLevel = SystemConstants.CommissionMaxNetworkLevel;
|
||||||
|
|
||||||
foreach (var user in usersInNetwork.OrderBy(o=>o.Id))
|
foreach (var user in usersInNetwork.OrderBy(o=>o.Id))
|
||||||
{
|
{
|
||||||
|
|||||||
+4
-6
@@ -1,3 +1,5 @@
|
|||||||
|
using CMSMicroservice.Domain.Common;
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.CommissionCQ.Commands.ProcessUserPayouts;
|
namespace CMSMicroservice.Application.CommissionCQ.Commands.ProcessUserPayouts;
|
||||||
|
|
||||||
public class ProcessUserPayoutsCommandHandler : IRequestHandler<ProcessUserPayoutsCommand, int>
|
public class ProcessUserPayoutsCommandHandler : IRequestHandler<ProcessUserPayoutsCommand, int>
|
||||||
@@ -47,12 +49,8 @@ public class ProcessUserPayoutsCommandHandler : IRequestHandler<ProcessUserPayou
|
|||||||
await _context.SaveChangesAsync(cancellationToken);
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⭐ خواندن MaxNetworkLevel از Config
|
// ⭐ خواندن MaxNetworkLevel از SystemConstants (استاتیک)
|
||||||
var maxNetworkLevelConfig = await _context.SystemConfigurations
|
var maxNetworkLevel = SystemConstants.CommissionMaxNetworkLevel;
|
||||||
.Where(x => x.Key == "Commission.MaxNetworkLevel" && x.IsActive)
|
|
||||||
.Select(x => x.Value)
|
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
|
||||||
var maxNetworkLevel = int.Parse(maxNetworkLevelConfig ?? "15");
|
|
||||||
|
|
||||||
// دریافت همه تعادلهای هفتگی (شامل صفرها هم برای محاسبه زیرمجموعه)
|
// دریافت همه تعادلهای هفتگی (شامل صفرها هم برای محاسبه زیرمجموعه)
|
||||||
var allWeeklyBalances = await _context.NetworkWeeklyBalances
|
var allWeeklyBalances = await _context.NetworkWeeklyBalances
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ public interface IApplicationDbContext
|
|||||||
DbSet<UserPackagePurchase> UserPackagePurchases { get; }
|
DbSet<UserPackagePurchase> UserPackagePurchases { get; }
|
||||||
DbSet<UserWallet> UserWallets { get; }
|
DbSet<UserWallet> UserWallets { get; }
|
||||||
DbSet<UserWalletChangeLog> UserWalletChangeLogs { get; }
|
DbSet<UserWalletChangeLog> UserWalletChangeLogs { get; }
|
||||||
DbSet<SystemConfiguration> SystemConfigurations { get; }
|
|
||||||
DbSet<SystemConfigurationHistory> SystemConfigurationHistories { get; }
|
|
||||||
DbSet<ManualPayment> ManualPayments { get; }
|
DbSet<ManualPayment> ManualPayments { get; }
|
||||||
DbSet<PublicMessage> PublicMessages { get; }
|
DbSet<PublicMessage> PublicMessages { get; }
|
||||||
DbSet<ClubMembership> ClubMemberships { get; }
|
DbSet<ClubMembership> ClubMemberships { get; }
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
namespace CMSMicroservice.Application.Common.Interfaces;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// سرویس ارسال SMS با کاوهنگار
|
||||||
|
/// </summary>
|
||||||
|
public interface IKavenegarService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ارسال پیامک ساده
|
||||||
|
/// </summary>
|
||||||
|
Task SendAsync(string mobile, string message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ارسال پیامک با قالب (VerifyLookup)
|
||||||
|
/// </summary>
|
||||||
|
Task VerifyLookupAsync(string mobile, string token, string template = "Afrino");
|
||||||
|
}
|
||||||
-17
@@ -1,17 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Commands.DeactivateConfiguration;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Command برای غیرفعال کردن یک Configuration
|
|
||||||
/// </summary>
|
|
||||||
public record DeactivateConfigurationCommand : IRequest<Unit>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// شناسه Configuration
|
|
||||||
/// </summary>
|
|
||||||
public long ConfigurationId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// دلیل غیرفعالسازی
|
|
||||||
/// </summary>
|
|
||||||
public string? Reason { get; init; }
|
|
||||||
}
|
|
||||||
-51
@@ -1,51 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Commands.DeactivateConfiguration;
|
|
||||||
|
|
||||||
public class DeactivateConfigurationCommandHandler : IRequestHandler<DeactivateConfigurationCommand, Unit>
|
|
||||||
{
|
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
private readonly ICurrentUserService _currentUser;
|
|
||||||
|
|
||||||
public DeactivateConfigurationCommandHandler(
|
|
||||||
IApplicationDbContext context,
|
|
||||||
ICurrentUserService currentUser)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_currentUser = currentUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Unit> Handle(DeactivateConfigurationCommand request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var entity = await _context.SystemConfigurations
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == request.ConfigurationId, cancellationToken)
|
|
||||||
?? throw new NotFoundException(nameof(SystemConfiguration), request.ConfigurationId);
|
|
||||||
|
|
||||||
// اگر از قبل غیرفعال است، خطا ندهیم
|
|
||||||
if (!entity.IsActive)
|
|
||||||
{
|
|
||||||
return Unit.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var oldValue = entity.Value;
|
|
||||||
entity.IsActive = false;
|
|
||||||
|
|
||||||
_context.SystemConfigurations.Update(entity);
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
// ثبت تاریخچه
|
|
||||||
var history = new SystemConfigurationHistory
|
|
||||||
{
|
|
||||||
ConfigurationId = entity.Id,
|
|
||||||
Scope = entity.Scope,
|
|
||||||
Key = entity.Key,
|
|
||||||
OldValue = oldValue,
|
|
||||||
NewValue = entity.Value,
|
|
||||||
Reason = request.Reason ?? "Configuration deactivated",
|
|
||||||
PerformedBy = _currentUser.GetPerformedBy()
|
|
||||||
};
|
|
||||||
|
|
||||||
await _context.SystemConfigurationHistories.AddAsync(history, cancellationToken);
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return Unit.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-29
@@ -1,29 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Commands.DeactivateConfiguration;
|
|
||||||
|
|
||||||
public class DeactivateConfigurationCommandValidator : AbstractValidator<DeactivateConfigurationCommand>
|
|
||||||
{
|
|
||||||
public DeactivateConfigurationCommandValidator()
|
|
||||||
{
|
|
||||||
RuleFor(x => x.ConfigurationId)
|
|
||||||
.GreaterThan(0)
|
|
||||||
.WithMessage("شناسه Configuration معتبر نیست");
|
|
||||||
|
|
||||||
RuleFor(x => x.Reason)
|
|
||||||
.MaximumLength(500)
|
|
||||||
.WithMessage("دلیل غیرفعالسازی نمیتواند بیشتر از 500 کاراکتر باشد")
|
|
||||||
.When(x => !string.IsNullOrEmpty(x.Reason));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
|
||||||
{
|
|
||||||
var result = await ValidateAsync(
|
|
||||||
ValidationContext<DeactivateConfigurationCommand>.CreateWithOptions(
|
|
||||||
(DeactivateConfigurationCommand)model,
|
|
||||||
x => x.IncludeProperties(propertyName)));
|
|
||||||
|
|
||||||
if (result.IsValid)
|
|
||||||
return Array.Empty<string>();
|
|
||||||
|
|
||||||
return result.Errors.Select(e => e.ErrorMessage);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
-77
@@ -1,77 +0,0 @@
|
|||||||
using CMSMicroservice.Application.Common.Interfaces;
|
|
||||||
using CMSMicroservice.Domain.Enums;
|
|
||||||
using MediatR;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Commands.SeedVATConfiguration;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Seed initial VAT configuration
|
|
||||||
/// نرخ مالیات پیشفرض ۹٪
|
|
||||||
/// </summary>
|
|
||||||
public class SeedVATConfigurationCommand : IRequest<Unit>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SeedVATConfigurationCommandHandler : IRequestHandler<SeedVATConfigurationCommand, Unit>
|
|
||||||
{
|
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
private readonly ILogger<SeedVATConfigurationCommandHandler> _logger;
|
|
||||||
|
|
||||||
public SeedVATConfigurationCommandHandler(
|
|
||||||
IApplicationDbContext context,
|
|
||||||
ILogger<SeedVATConfigurationCommandHandler> logger)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Unit> Handle(SeedVATConfigurationCommand request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var configs = new[]
|
|
||||||
{
|
|
||||||
new
|
|
||||||
{
|
|
||||||
Scope = ConfigurationScope.VAT,
|
|
||||||
Key = "Rate",
|
|
||||||
Value = "0.09",
|
|
||||||
Description = "نرخ مالیات بر ارزش افزوده (۹٪)"
|
|
||||||
},
|
|
||||||
new
|
|
||||||
{
|
|
||||||
Scope = ConfigurationScope.VAT,
|
|
||||||
Key = "IsEnabled",
|
|
||||||
Value = "true",
|
|
||||||
Description = "فعال/غیرفعال بودن محاسبه مالیات"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var config in configs)
|
|
||||||
{
|
|
||||||
var exists = _context.SystemConfigurations
|
|
||||||
.Any(x => x.Scope == config.Scope && x.Key == config.Key);
|
|
||||||
|
|
||||||
if (!exists)
|
|
||||||
{
|
|
||||||
_context.SystemConfigurations.Add(new Domain.Entities.Configuration.SystemConfiguration
|
|
||||||
{
|
|
||||||
Scope = config.Scope,
|
|
||||||
Key = config.Key,
|
|
||||||
Value = config.Value,
|
|
||||||
Description = config.Description
|
|
||||||
});
|
|
||||||
|
|
||||||
_logger.LogInformation(
|
|
||||||
"VAT configuration seeded: {Scope}.{Key} = {Value}",
|
|
||||||
config.Scope,
|
|
||||||
config.Key,
|
|
||||||
config.Value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return Unit.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Commands.SetConfigurationValue;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Command برای تنظیم یا بهروزرسانی یک Configuration
|
|
||||||
/// </summary>
|
|
||||||
public record SetConfigurationValueCommand : IRequest<long>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// محدوده تنظیمات (System, Network, Club, Commission)
|
|
||||||
/// </summary>
|
|
||||||
public ConfigurationScope Scope { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// کلید یکتا برای تنظیمات
|
|
||||||
/// </summary>
|
|
||||||
public string Key { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// مقدار تنظیمات (JSON format)
|
|
||||||
/// </summary>
|
|
||||||
public string Value { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// توضیحات تنظیمات
|
|
||||||
/// </summary>
|
|
||||||
public string? Description { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// دلیل تغییر (برای History)
|
|
||||||
/// </summary>
|
|
||||||
public string? ChangeReason { get; init; }
|
|
||||||
}
|
|
||||||
-78
@@ -1,78 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Commands.SetConfigurationValue;
|
|
||||||
|
|
||||||
public class SetConfigurationValueCommandHandler : IRequestHandler<SetConfigurationValueCommand, long>
|
|
||||||
{
|
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
private readonly ICurrentUserService _currentUser;
|
|
||||||
|
|
||||||
public SetConfigurationValueCommandHandler(
|
|
||||||
IApplicationDbContext context,
|
|
||||||
ICurrentUserService currentUser)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_currentUser = currentUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<long> Handle(SetConfigurationValueCommand request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
// بررسی وجود Configuration با همین Scope و Key
|
|
||||||
var existingConfig = await _context.SystemConfigurations
|
|
||||||
.FirstOrDefaultAsync(x =>
|
|
||||||
x.Scope == request.Scope &&
|
|
||||||
x.Key == request.Key,
|
|
||||||
cancellationToken);
|
|
||||||
|
|
||||||
SystemConfiguration entity;
|
|
||||||
bool isNewRecord = existingConfig == null;
|
|
||||||
string oldValue = null;
|
|
||||||
|
|
||||||
if (isNewRecord)
|
|
||||||
{
|
|
||||||
// ایجاد Configuration جدید
|
|
||||||
entity = new SystemConfiguration
|
|
||||||
{
|
|
||||||
Scope = request.Scope,
|
|
||||||
Key = request.Key,
|
|
||||||
Value = request.Value,
|
|
||||||
Description = request.Description,
|
|
||||||
IsActive = true
|
|
||||||
};
|
|
||||||
|
|
||||||
await _context.SystemConfigurations.AddAsync(entity, cancellationToken);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// بهروزرسانی Configuration موجود
|
|
||||||
entity = existingConfig;
|
|
||||||
oldValue = entity.Value;
|
|
||||||
|
|
||||||
entity.Value = request.Value;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(request.Description))
|
|
||||||
{
|
|
||||||
entity.Description = request.Description;
|
|
||||||
}
|
|
||||||
|
|
||||||
_context.SystemConfigurations.Update(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
// ثبت تاریخچه
|
|
||||||
var history = new SystemConfigurationHistory
|
|
||||||
{
|
|
||||||
ConfigurationId = entity.Id,
|
|
||||||
Scope = entity.Scope,
|
|
||||||
Key = entity.Key,
|
|
||||||
OldValue = oldValue,
|
|
||||||
NewValue = entity.Value,
|
|
||||||
Reason = request.ChangeReason ?? (isNewRecord ? "Initial creation" : "Value updated"),
|
|
||||||
PerformedBy = "System" // TODO: باید از Current User گرفته شود
|
|
||||||
};
|
|
||||||
|
|
||||||
await _context.SystemConfigurationHistories.AddAsync(history, cancellationToken);
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return entity.Id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-48
@@ -1,48 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Commands.SetConfigurationValue;
|
|
||||||
|
|
||||||
public class SetConfigurationValueCommandValidator : AbstractValidator<SetConfigurationValueCommand>
|
|
||||||
{
|
|
||||||
public SetConfigurationValueCommandValidator()
|
|
||||||
{
|
|
||||||
RuleFor(x => x.Scope)
|
|
||||||
.IsInEnum()
|
|
||||||
.WithMessage("محدوده تنظیمات معتبر نیست");
|
|
||||||
|
|
||||||
RuleFor(x => x.Key)
|
|
||||||
.NotEmpty()
|
|
||||||
.WithMessage("کلید تنظیمات الزامی است")
|
|
||||||
.MaximumLength(100)
|
|
||||||
.WithMessage("کلید تنظیمات نمیتواند بیشتر از 100 کاراکتر باشد")
|
|
||||||
.Matches(@"^[a-zA-Z0-9_\.]+$")
|
|
||||||
.WithMessage("کلید تنظیمات فقط میتواند شامل حروف انگلیسی، اعداد، نقطه و آندرلاین باشد");
|
|
||||||
|
|
||||||
RuleFor(x => x.Value)
|
|
||||||
.NotEmpty()
|
|
||||||
.WithMessage("مقدار تنظیمات الزامی است")
|
|
||||||
.MaximumLength(2000)
|
|
||||||
.WithMessage("مقدار تنظیمات نمیتواند بیشتر از 2000 کاراکتر باشد");
|
|
||||||
|
|
||||||
RuleFor(x => x.Description)
|
|
||||||
.MaximumLength(500)
|
|
||||||
.WithMessage("توضیحات نمیتواند بیشتر از 500 کاراکتر باشد")
|
|
||||||
.When(x => !string.IsNullOrEmpty(x.Description));
|
|
||||||
|
|
||||||
RuleFor(x => x.ChangeReason)
|
|
||||||
.MaximumLength(500)
|
|
||||||
.WithMessage("دلیل تغییر نمیتواند بیشتر از 500 کاراکتر باشد")
|
|
||||||
.When(x => !string.IsNullOrEmpty(x.ChangeReason));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
|
||||||
{
|
|
||||||
var result = await ValidateAsync(
|
|
||||||
ValidationContext<SetConfigurationValueCommand>.CreateWithOptions(
|
|
||||||
(SetConfigurationValueCommand)model,
|
|
||||||
x => x.IncludeProperties(propertyName)));
|
|
||||||
|
|
||||||
if (result.IsValid)
|
|
||||||
return Array.Empty<string>();
|
|
||||||
|
|
||||||
return result.Errors.Select(e => e.ErrorMessage);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
-40
@@ -1,40 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Queries.GetAllConfigurations;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Query برای دریافت لیست تمام Configuration ها با فیلتر
|
|
||||||
/// </summary>
|
|
||||||
public record GetAllConfigurationsQuery : IRequest<GetAllConfigurationsResponseDto>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// موقعیت صفحهبندی
|
|
||||||
/// </summary>
|
|
||||||
public PaginationState? PaginationState { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// مرتبسازی بر اساس
|
|
||||||
/// </summary>
|
|
||||||
public string? SortBy { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// فیلتر
|
|
||||||
/// </summary>
|
|
||||||
public GetAllConfigurationsFilter? Filter { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GetAllConfigurationsFilter
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// فیلتر بر اساس محدوده
|
|
||||||
/// </summary>
|
|
||||||
public ConfigurationScope? Scope { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// جستجو در کلید
|
|
||||||
/// </summary>
|
|
||||||
public string? KeyContains { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// فقط Configuration های فعال
|
|
||||||
/// </summary>
|
|
||||||
public bool? IsActive { get; set; }
|
|
||||||
}
|
|
||||||
-50
@@ -1,50 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Queries.GetAllConfigurations;
|
|
||||||
|
|
||||||
public class GetAllConfigurationsQueryHandler : IRequestHandler<GetAllConfigurationsQuery, GetAllConfigurationsResponseDto>
|
|
||||||
{
|
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
|
|
||||||
public GetAllConfigurationsQueryHandler(IApplicationDbContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<GetAllConfigurationsResponseDto> Handle(GetAllConfigurationsQuery request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var query = _context.SystemConfigurations
|
|
||||||
.ApplyOrder(sortBy: request.SortBy)
|
|
||||||
.AsNoTracking()
|
|
||||||
.AsQueryable();
|
|
||||||
|
|
||||||
if (request.Filter is not null)
|
|
||||||
{
|
|
||||||
query = query
|
|
||||||
.Where(x => request.Filter.Scope == null || x.Scope == request.Filter.Scope)
|
|
||||||
.Where(x => request.Filter.KeyContains == null || x.Key.Contains(request.Filter.KeyContains))
|
|
||||||
.Where(x => request.Filter.IsActive == null || x.IsActive == request.Filter.IsActive);
|
|
||||||
}
|
|
||||||
|
|
||||||
var meta = await query.GetMetaData(request.PaginationState, cancellationToken);
|
|
||||||
|
|
||||||
var models = await query
|
|
||||||
.PaginatedListAsync(paginationState: request.PaginationState)
|
|
||||||
.Select(x => new GetAllConfigurationsResponseModel
|
|
||||||
{
|
|
||||||
Id = x.Id,
|
|
||||||
Scope = x.Scope,
|
|
||||||
Key = x.Key,
|
|
||||||
Value = x.Value,
|
|
||||||
Description = x.Description,
|
|
||||||
IsActive = x.IsActive,
|
|
||||||
Created = x.Created,
|
|
||||||
LastModified = x.LastModified
|
|
||||||
})
|
|
||||||
.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
return new GetAllConfigurationsResponseDto
|
|
||||||
{
|
|
||||||
MetaData = meta,
|
|
||||||
Models = models
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-25
@@ -1,25 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Queries.GetAllConfigurations;
|
|
||||||
|
|
||||||
public class GetAllConfigurationsQueryValidator : AbstractValidator<GetAllConfigurationsQuery>
|
|
||||||
{
|
|
||||||
public GetAllConfigurationsQueryValidator()
|
|
||||||
{
|
|
||||||
RuleFor(x => x.Filter.Scope)
|
|
||||||
.IsInEnum()
|
|
||||||
.WithMessage("محدوده تنظیمات معتبر نیست")
|
|
||||||
.When(x => x.Filter?.Scope != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
|
||||||
{
|
|
||||||
var result = await ValidateAsync(
|
|
||||||
ValidationContext<GetAllConfigurationsQuery>.CreateWithOptions(
|
|
||||||
(GetAllConfigurationsQuery)model,
|
|
||||||
x => x.IncludeProperties(propertyName)));
|
|
||||||
|
|
||||||
if (result.IsValid)
|
|
||||||
return Array.Empty<string>();
|
|
||||||
|
|
||||||
return result.Errors.Select(e => e.ErrorMessage);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
-19
@@ -1,19 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Queries.GetAllConfigurations;
|
|
||||||
|
|
||||||
public class GetAllConfigurationsResponseDto
|
|
||||||
{
|
|
||||||
public MetaData MetaData { get; set; }
|
|
||||||
public List<GetAllConfigurationsResponseModel> Models { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GetAllConfigurationsResponseModel
|
|
||||||
{
|
|
||||||
public long Id { get; set; }
|
|
||||||
public ConfigurationScope Scope { get; set; }
|
|
||||||
public string Key { get; set; }
|
|
||||||
public string Value { get; set; }
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public bool IsActive { get; set; }
|
|
||||||
public DateTimeOffset Created { get; set; }
|
|
||||||
public DateTimeOffset? LastModified { get; set; }
|
|
||||||
}
|
|
||||||
-16
@@ -1,16 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Queries.GetConfigurationByKey;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// DTO برای نمایش اطلاعات Configuration
|
|
||||||
/// </summary>
|
|
||||||
public class ConfigurationDto
|
|
||||||
{
|
|
||||||
public long Id { get; set; }
|
|
||||||
public ConfigurationScope Scope { get; set; }
|
|
||||||
public string Key { get; set; }
|
|
||||||
public string Value { get; set; }
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public bool IsActive { get; set; }
|
|
||||||
public DateTimeOffset Created { get; set; }
|
|
||||||
public DateTimeOffset? LastModified { get; set; }
|
|
||||||
}
|
|
||||||
-17
@@ -1,17 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Queries.GetConfigurationByKey;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Query برای دریافت یک Configuration بر اساس Scope و Key
|
|
||||||
/// </summary>
|
|
||||||
public record GetConfigurationByKeyQuery : IRequest<ConfigurationDto?>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// محدوده تنظیمات
|
|
||||||
/// </summary>
|
|
||||||
public ConfigurationScope Scope { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// کلید تنظیمات
|
|
||||||
/// </summary>
|
|
||||||
public string Key { get; init; }
|
|
||||||
}
|
|
||||||
-34
@@ -1,34 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Queries.GetConfigurationByKey;
|
|
||||||
|
|
||||||
public class GetConfigurationByKeyQueryHandler : IRequestHandler<GetConfigurationByKeyQuery, ConfigurationDto?>
|
|
||||||
{
|
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
|
|
||||||
public GetConfigurationByKeyQueryHandler(IApplicationDbContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<ConfigurationDto?> Handle(GetConfigurationByKeyQuery request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var config = await _context.SystemConfigurations
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(x => x.Scope == request.Scope && x.Key == request.Key)
|
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
|
||||||
|
|
||||||
if (config == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return new ConfigurationDto
|
|
||||||
{
|
|
||||||
Id = config.Id,
|
|
||||||
Scope = config.Scope,
|
|
||||||
Key = config.Key,
|
|
||||||
Value = config.Value,
|
|
||||||
Description = config.Description,
|
|
||||||
IsActive = config.IsActive,
|
|
||||||
Created = config.Created,
|
|
||||||
LastModified = config.LastModified
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-28
@@ -1,28 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Queries.GetConfigurationByKey;
|
|
||||||
|
|
||||||
public class GetConfigurationByKeyQueryValidator : AbstractValidator<GetConfigurationByKeyQuery>
|
|
||||||
{
|
|
||||||
public GetConfigurationByKeyQueryValidator()
|
|
||||||
{
|
|
||||||
RuleFor(x => x.Scope)
|
|
||||||
.IsInEnum()
|
|
||||||
.WithMessage("محدوده تنظیمات معتبر نیست");
|
|
||||||
|
|
||||||
RuleFor(x => x.Key)
|
|
||||||
.NotEmpty()
|
|
||||||
.WithMessage("کلید تنظیمات الزامی است");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
|
||||||
{
|
|
||||||
var result = await ValidateAsync(
|
|
||||||
ValidationContext<GetConfigurationByKeyQuery>.CreateWithOptions(
|
|
||||||
(GetConfigurationByKeyQuery)model,
|
|
||||||
x => x.IncludeProperties(propertyName)));
|
|
||||||
|
|
||||||
if (result.IsValid)
|
|
||||||
return Array.Empty<string>();
|
|
||||||
|
|
||||||
return result.Errors.Select(e => e.ErrorMessage);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Queries.GetConfigurationHistory;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Query برای دریافت تاریخچه تغییرات یک Configuration
|
|
||||||
/// </summary>
|
|
||||||
public record GetConfigurationHistoryQuery : IRequest<GetConfigurationHistoryResponseDto>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// شناسه Configuration
|
|
||||||
/// </summary>
|
|
||||||
public long ConfigurationId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// موقعیت صفحهبندی
|
|
||||||
/// </summary>
|
|
||||||
public PaginationState? PaginationState { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// مرتبسازی بر اساس
|
|
||||||
/// </summary>
|
|
||||||
public string? SortBy { get; init; }
|
|
||||||
}
|
|
||||||
-53
@@ -1,53 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Queries.GetConfigurationHistory;
|
|
||||||
|
|
||||||
public class GetConfigurationHistoryQueryHandler : IRequestHandler<GetConfigurationHistoryQuery, GetConfigurationHistoryResponseDto>
|
|
||||||
{
|
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
|
|
||||||
public GetConfigurationHistoryQueryHandler(IApplicationDbContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<GetConfigurationHistoryResponseDto> Handle(GetConfigurationHistoryQuery request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
// بررسی وجود Configuration
|
|
||||||
var configExists = await _context.SystemConfigurations
|
|
||||||
.AnyAsync(x => x.Id == request.ConfigurationId, cancellationToken);
|
|
||||||
|
|
||||||
if (!configExists)
|
|
||||||
{
|
|
||||||
throw new NotFoundException(nameof(SystemConfiguration), request.ConfigurationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
var query = _context.SystemConfigurationHistories
|
|
||||||
.Where(x => x.ConfigurationId == request.ConfigurationId)
|
|
||||||
.ApplyOrder(sortBy: request.SortBy ?? "Created") // پیشفرض: جدیدترین اول
|
|
||||||
.AsNoTracking()
|
|
||||||
.AsQueryable();
|
|
||||||
|
|
||||||
var meta = await query.GetMetaData(request.PaginationState, cancellationToken);
|
|
||||||
|
|
||||||
var models = await query
|
|
||||||
.PaginatedListAsync(paginationState: request.PaginationState)
|
|
||||||
.Select(x => new GetConfigurationHistoryResponseModel
|
|
||||||
{
|
|
||||||
Id = x.Id,
|
|
||||||
ConfigurationId = x.ConfigurationId,
|
|
||||||
Scope = x.Scope,
|
|
||||||
Key = x.Key,
|
|
||||||
OldValue = x.OldValue,
|
|
||||||
NewValue = x.NewValue,
|
|
||||||
ChangeReason = x.Reason,
|
|
||||||
ChangedBy = x.PerformedBy,
|
|
||||||
Created = x.Created
|
|
||||||
})
|
|
||||||
.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
return new GetConfigurationHistoryResponseDto
|
|
||||||
{
|
|
||||||
MetaData = meta,
|
|
||||||
Models = models
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-24
@@ -1,24 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Queries.GetConfigurationHistory;
|
|
||||||
|
|
||||||
public class GetConfigurationHistoryQueryValidator : AbstractValidator<GetConfigurationHistoryQuery>
|
|
||||||
{
|
|
||||||
public GetConfigurationHistoryQueryValidator()
|
|
||||||
{
|
|
||||||
RuleFor(x => x.ConfigurationId)
|
|
||||||
.GreaterThan(0)
|
|
||||||
.WithMessage("شناسه Configuration معتبر نیست");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
|
||||||
{
|
|
||||||
var result = await ValidateAsync(
|
|
||||||
ValidationContext<GetConfigurationHistoryQuery>.CreateWithOptions(
|
|
||||||
(GetConfigurationHistoryQuery)model,
|
|
||||||
x => x.IncludeProperties(propertyName)));
|
|
||||||
|
|
||||||
if (result.IsValid)
|
|
||||||
return Array.Empty<string>();
|
|
||||||
|
|
||||||
return result.Errors.Select(e => e.ErrorMessage);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
-20
@@ -1,20 +0,0 @@
|
|||||||
namespace CMSMicroservice.Application.ConfigurationCQ.Queries.GetConfigurationHistory;
|
|
||||||
|
|
||||||
public class GetConfigurationHistoryResponseDto
|
|
||||||
{
|
|
||||||
public MetaData MetaData { get; set; }
|
|
||||||
public List<GetConfigurationHistoryResponseModel> Models { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GetConfigurationHistoryResponseModel
|
|
||||||
{
|
|
||||||
public long Id { get; set; }
|
|
||||||
public long ConfigurationId { get; set; }
|
|
||||||
public ConfigurationScope Scope { get; set; }
|
|
||||||
public string Key { get; set; }
|
|
||||||
public string? OldValue { get; set; }
|
|
||||||
public string NewValue { get; set; }
|
|
||||||
public string ChangeReason { get; set; }
|
|
||||||
public string ChangedBy { get; set; }
|
|
||||||
public DateTimeOffset Created { get; set; }
|
|
||||||
}
|
|
||||||
+8
-12
@@ -1,5 +1,6 @@
|
|||||||
using CMSMicroservice.Domain.Events;
|
using CMSMicroservice.Domain.Events;
|
||||||
using CMSMicroservice.Domain.Enums;
|
using CMSMicroservice.Domain.Enums;
|
||||||
|
using CMSMicroservice.Domain.Common;
|
||||||
using CMSMicroservice.Application.DayaLoanCQ.Services;
|
using CMSMicroservice.Application.DayaLoanCQ.Services;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
@@ -10,11 +11,6 @@ public class CheckDayaLoanStatusCommandHandler : IRequestHandler<CheckDayaLoanSt
|
|||||||
private readonly IApplicationDbContext _context;
|
private readonly IApplicationDbContext _context;
|
||||||
private readonly IDayaLoanApiService _dayaApiService;
|
private readonly IDayaLoanApiService _dayaApiService;
|
||||||
private readonly ILogger<CheckDayaLoanStatusCommandHandler> _logger;
|
private readonly ILogger<CheckDayaLoanStatusCommandHandler> _logger;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// مبلغ وام دایا - 56 میلیون ریال
|
|
||||||
/// </summary>
|
|
||||||
private const long DAYA_LOAN_AMOUNT = 56_000_000;
|
|
||||||
|
|
||||||
public CheckDayaLoanStatusCommandHandler(
|
public CheckDayaLoanStatusCommandHandler(
|
||||||
IApplicationDbContext context,
|
IApplicationDbContext context,
|
||||||
@@ -69,7 +65,7 @@ public class CheckDayaLoanStatusCommandHandler : IRequestHandler<CheckDayaLoanSt
|
|||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"Daya loan processed for User {UserId}, ContractNumber: {ContractNumber}, Amount: {Amount}",
|
"Daya loan processed for User {UserId}, ContractNumber: {ContractNumber}, Amount: {Amount}",
|
||||||
existingContract.UserId, dayaResult.ContractNumber, DAYA_LOAN_AMOUNT);
|
existingContract.UserId, dayaResult.ContractNumber, SystemConstants.DayaLoanAmount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -90,7 +86,7 @@ public class CheckDayaLoanStatusCommandHandler : IRequestHandler<CheckDayaLoanSt
|
|||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"Daya loan processed for new contract - User {UserId}, ContractNumber: {ContractNumber}, Amount: {Amount}",
|
"Daya loan processed for new contract - User {UserId}, ContractNumber: {ContractNumber}, Amount: {Amount}",
|
||||||
user.Id, dayaResult.ContractNumber, DAYA_LOAN_AMOUNT);
|
user.Id, dayaResult.ContractNumber, SystemConstants.DayaLoanAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
var newContract = new DayaLoanContract
|
var newContract = new DayaLoanContract
|
||||||
@@ -187,13 +183,13 @@ public class CheckDayaLoanStatusCommandHandler : IRequestHandler<CheckDayaLoanSt
|
|||||||
var previousBalance = wallet.Balance;
|
var previousBalance = wallet.Balance;
|
||||||
var previousDiscountBalance = wallet.DiscountBalance;
|
var previousDiscountBalance = wallet.DiscountBalance;
|
||||||
|
|
||||||
wallet.Balance += DAYA_LOAN_AMOUNT;
|
wallet.Balance += SystemConstants.DayaLoanAmount;
|
||||||
wallet.DiscountBalance += DAYA_LOAN_AMOUNT;
|
wallet.DiscountBalance += SystemConstants.DayaLoanAmount;
|
||||||
|
|
||||||
// 2. ثبت تراکنش - RefId = شماره قرارداد، Status = 0 (Success)، Type = 2 (DepositExternal1)
|
// 2. ثبت تراکنش - RefId = شماره قرارداد، Status = 0 (Success)، Type = 2 (DepositExternal1)
|
||||||
var transaction = new Transaction
|
var transaction = new Transaction
|
||||||
{
|
{
|
||||||
Amount = DAYA_LOAN_AMOUNT,
|
Amount = SystemConstants.DayaLoanAmount,
|
||||||
Description = $"شارژ کیف پول از وام دایا - قرارداد {contractNumber}",
|
Description = $"شارژ کیف پول از وام دایا - قرارداد {contractNumber}",
|
||||||
PaymentStatus = PaymentStatus.Success, // 0
|
PaymentStatus = PaymentStatus.Success, // 0
|
||||||
PaymentDate = DateTime.Now,
|
PaymentDate = DateTime.Now,
|
||||||
@@ -209,11 +205,11 @@ public class CheckDayaLoanStatusCommandHandler : IRequestHandler<CheckDayaLoanSt
|
|||||||
{
|
{
|
||||||
WalletId = wallet.Id,
|
WalletId = wallet.Id,
|
||||||
CurrentBalance = wallet.Balance,
|
CurrentBalance = wallet.Balance,
|
||||||
ChangeValue = DAYA_LOAN_AMOUNT,
|
ChangeValue = SystemConstants.DayaLoanAmount,
|
||||||
CurrentNetworkBalance = wallet.NetworkBalance,
|
CurrentNetworkBalance = wallet.NetworkBalance,
|
||||||
ChangeNerworkValue = 0,
|
ChangeNerworkValue = 0,
|
||||||
CurrentDiscountBalance = wallet.DiscountBalance,
|
CurrentDiscountBalance = wallet.DiscountBalance,
|
||||||
ChangeDiscountValue = DAYA_LOAN_AMOUNT,
|
ChangeDiscountValue = SystemConstants.DayaLoanAmount,
|
||||||
IsIncrease = true,
|
IsIncrease = true,
|
||||||
RefrenceId = transaction.Id
|
RefrenceId = transaction.Id
|
||||||
};
|
};
|
||||||
|
|||||||
+7
-6
@@ -1,4 +1,5 @@
|
|||||||
using CMSMicroservice.Domain.Enums;
|
using CMSMicroservice.Domain.Enums;
|
||||||
|
using CMSMicroservice.Domain.Common;
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.DayaLoanCQ.Commands.ProcessDayaLoanApproval;
|
namespace CMSMicroservice.Application.DayaLoanCQ.Commands.ProcessDayaLoanApproval;
|
||||||
|
|
||||||
@@ -18,17 +19,17 @@ public record ProcessDayaLoanApprovalCommand : IRequest<ProcessDayaLoanApprovalR
|
|||||||
public string ContractNumber { get; init; }
|
public string ContractNumber { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// مبلغ کیف پول عادی (56 میلیون)
|
/// مبلغ کیف پول عادی
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public long WalletAmount { get; init; } = 56_000_000;
|
public long WalletAmount { get; init; } = SystemConstants.DayaLoanAmount;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// مبلغ کیف پول قفل شده (56 میلیون)
|
/// مبلغ کیف پول قفل شده
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public long LockedWalletAmount { get; init; } = 56_000_000;
|
public long LockedWalletAmount { get; init; } = SystemConstants.DayaLoanAmount;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// مبلغ کیف پول تخفیف (56 میلیون)
|
/// مبلغ کیف پول تخفیف (دو برابر)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public long DiscountWalletAmount { get; init; } = 56_000_000;
|
public long DiscountWalletAmount { get; init; } = SystemConstants.DayaLoanAmount * 2;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-18
@@ -76,22 +76,7 @@ public class ProcessDayaLoanApprovalCommandHandler : IRequestHandler<ProcessDaya
|
|||||||
};
|
};
|
||||||
await _context.UserWalletChangeLogs.AddAsync(mainLog, cancellationToken);
|
await _context.UserWalletChangeLogs.AddAsync(mainLog, cancellationToken);
|
||||||
|
|
||||||
// شارژ کیف پول شبکه/کارمزد (56 میلیون) - نامگذاری قدیم: کیف پول قفل شده
|
|
||||||
var balanceBeforeLocked = wallet.NetworkBalance;
|
|
||||||
wallet.NetworkBalance += request.LockedWalletAmount;
|
|
||||||
|
|
||||||
// لاگ کیف پول شبکه
|
|
||||||
var networkLog = new UserWalletChangeLog
|
|
||||||
{
|
|
||||||
WalletId = wallet.Id,
|
|
||||||
CurrentBalance = wallet.Balance,
|
|
||||||
ChangeValue = 0,
|
|
||||||
CurrentNetworkBalance = wallet.NetworkBalance,
|
|
||||||
ChangeNerworkValue = request.LockedWalletAmount,
|
|
||||||
IsIncrease = true,
|
|
||||||
RefrenceId = transaction.Id
|
|
||||||
};
|
|
||||||
await _context.UserWalletChangeLogs.AddAsync(networkLog, cancellationToken);
|
|
||||||
|
|
||||||
// شارژ کیف پول تخفیف (56 میلیون)
|
// شارژ کیف پول تخفیف (56 میلیون)
|
||||||
var balanceBeforeDiscount = wallet.DiscountBalance;
|
var balanceBeforeDiscount = wallet.DiscountBalance;
|
||||||
@@ -119,9 +104,9 @@ public class ProcessDayaLoanApprovalCommandHandler : IRequestHandler<ProcessDaya
|
|||||||
// تنظیم نحوه خرید پکیج به DayaLoan
|
// تنظیم نحوه خرید پکیج به DayaLoan
|
||||||
user.PackagePurchaseMethod = PackagePurchaseMethod.DayaLoan;
|
user.PackagePurchaseMethod = PackagePurchaseMethod.DayaLoan;
|
||||||
|
|
||||||
// ثبت سفارش پکیج (فعلاً پکیج طلایی)
|
// ثبت سفارش پکیج (فعلاً پکیج پایه)
|
||||||
var goldenPackage = await _context.Packages
|
var goldenPackage = await _context.Packages
|
||||||
.FirstOrDefaultAsync(p => p.Title.Contains("طلایی") || p.Title.Contains("Golden"), cancellationToken);
|
.FirstOrDefaultAsync(p => p.Id==4, cancellationToken);
|
||||||
|
|
||||||
if (goldenPackage != null)
|
if (goldenPackage != null)
|
||||||
{
|
{
|
||||||
|
|||||||
+5
-9
@@ -2,6 +2,7 @@ using CMSMicroservice.Application.Common.Exceptions;
|
|||||||
using CMSMicroservice.Application.Common.Interfaces;
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
using CMSMicroservice.Domain.Entities;
|
using CMSMicroservice.Domain.Entities;
|
||||||
using CMSMicroservice.Domain.Enums;
|
using CMSMicroservice.Domain.Enums;
|
||||||
|
using CMSMicroservice.Domain.Common;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@@ -19,11 +20,6 @@ public class InitiateBasePackagePaymentCommandHandler : IRequestHandler<Initiate
|
|||||||
private readonly IApplicationDbContext _context;
|
private readonly IApplicationDbContext _context;
|
||||||
private readonly ILogger<InitiateBasePackagePaymentCommandHandler> _logger;
|
private readonly ILogger<InitiateBasePackagePaymentCommandHandler> _logger;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// مبلغ پکیج پایه (56 میلیون تومان)
|
|
||||||
/// </summary>
|
|
||||||
private const long BasePackageAmount = 56_000_000;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// شناسه پکیج پایه در دیتابیس
|
/// شناسه پکیج پایه در دیتابیس
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -97,7 +93,7 @@ public class InitiateBasePackagePaymentCommandHandler : IRequestHandler<Initiate
|
|||||||
Message = "سفارش قبلی در انتظار پرداخت یافت شد.",
|
Message = "سفارش قبلی در انتظار پرداخت یافت شد.",
|
||||||
OrderId = pendingOrder.Id,
|
OrderId = pendingOrder.Id,
|
||||||
TransactionId = existingTransaction?.Id ?? 0,
|
TransactionId = existingTransaction?.Id ?? 0,
|
||||||
Amount = BasePackageAmount
|
Amount = SystemConstants.BasePackageAmount
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +112,7 @@ public class InitiateBasePackagePaymentCommandHandler : IRequestHandler<Initiate
|
|||||||
// 5. ایجاد Transaction با وضعیت Pending
|
// 5. ایجاد Transaction با وضعیت Pending
|
||||||
var transaction = new Transaction
|
var transaction = new Transaction
|
||||||
{
|
{
|
||||||
Amount = BasePackageAmount,
|
Amount = SystemConstants.BasePackageAmount,
|
||||||
Description = $"خرید پکیج پایه ۵۶ میلیونی - کاربر #{user.Id}",
|
Description = $"خرید پکیج پایه ۵۶ میلیونی - کاربر #{user.Id}",
|
||||||
PaymentStatus = PaymentStatus.Pending,
|
PaymentStatus = PaymentStatus.Pending,
|
||||||
Type = TransactionType.DepositIpg
|
Type = TransactionType.DepositIpg
|
||||||
@@ -135,7 +131,7 @@ public class InitiateBasePackagePaymentCommandHandler : IRequestHandler<Initiate
|
|||||||
{
|
{
|
||||||
UserId = user.Id,
|
UserId = user.Id,
|
||||||
PackageId = BasePackageId,
|
PackageId = BasePackageId,
|
||||||
Amount = BasePackageAmount,
|
Amount = SystemConstants.BasePackageAmount,
|
||||||
PaymentStatus = PaymentStatus.Pending,
|
PaymentStatus = PaymentStatus.Pending,
|
||||||
DeliveryStatus = DeliveryStatus.None,
|
DeliveryStatus = DeliveryStatus.None,
|
||||||
UserAddressId = defaultAddress.Id,
|
UserAddressId = defaultAddress.Id,
|
||||||
@@ -158,7 +154,7 @@ public class InitiateBasePackagePaymentCommandHandler : IRequestHandler<Initiate
|
|||||||
Message = "تراکنش و سفارش با موفقیت ثبت شد.",
|
Message = "تراکنش و سفارش با موفقیت ثبت شد.",
|
||||||
OrderId = order.Id,
|
OrderId = order.Id,
|
||||||
TransactionId = transaction.Id,
|
TransactionId = transaction.Id,
|
||||||
Amount = BasePackageAmount
|
Amount = SystemConstants.BasePackageAmount
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
+5
-9
@@ -2,6 +2,7 @@ using CMSMicroservice.Application.Common.Exceptions;
|
|||||||
using CMSMicroservice.Application.Common.Interfaces;
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
using CMSMicroservice.Domain.Entities;
|
using CMSMicroservice.Domain.Entities;
|
||||||
using CMSMicroservice.Domain.Enums;
|
using CMSMicroservice.Domain.Enums;
|
||||||
|
using CMSMicroservice.Domain.Common;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@@ -24,11 +25,6 @@ public class VerifyBasePackagePaymentCommandHandler : IRequestHandler<VerifyBase
|
|||||||
{
|
{
|
||||||
private readonly IApplicationDbContext _context;
|
private readonly IApplicationDbContext _context;
|
||||||
private readonly ILogger<VerifyBasePackagePaymentCommandHandler> _logger;
|
private readonly ILogger<VerifyBasePackagePaymentCommandHandler> _logger;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// مبلغ پکیج پایه (56 میلیون تومان)
|
|
||||||
/// </summary>
|
|
||||||
private const long BasePackageAmount = 56_000_000;
|
|
||||||
|
|
||||||
public VerifyBasePackagePaymentCommandHandler(
|
public VerifyBasePackagePaymentCommandHandler(
|
||||||
IApplicationDbContext context,
|
IApplicationDbContext context,
|
||||||
@@ -136,8 +132,8 @@ public class VerifyBasePackagePaymentCommandHandler : IRequestHandler<VerifyBase
|
|||||||
var oldBalance = userWallet.Balance;
|
var oldBalance = userWallet.Balance;
|
||||||
var oldDiscountBalance = userWallet.DiscountBalance;
|
var oldDiscountBalance = userWallet.DiscountBalance;
|
||||||
|
|
||||||
userWallet.Balance += BasePackageAmount;
|
userWallet.Balance += SystemConstants.BasePackageAmount;
|
||||||
userWallet.DiscountBalance += BasePackageAmount;
|
userWallet.DiscountBalance += SystemConstants.BasePackageAmount;
|
||||||
|
|
||||||
_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}",
|
||||||
@@ -157,11 +153,11 @@ public class VerifyBasePackagePaymentCommandHandler : IRequestHandler<VerifyBase
|
|||||||
{
|
{
|
||||||
WalletId = userWallet.Id,
|
WalletId = userWallet.Id,
|
||||||
CurrentBalance = userWallet.Balance,
|
CurrentBalance = userWallet.Balance,
|
||||||
ChangeValue = BasePackageAmount,
|
ChangeValue = SystemConstants.BasePackageAmount,
|
||||||
CurrentNetworkBalance = userWallet.NetworkBalance,
|
CurrentNetworkBalance = userWallet.NetworkBalance,
|
||||||
ChangeNerworkValue = 0,
|
ChangeNerworkValue = 0,
|
||||||
CurrentDiscountBalance = userWallet.DiscountBalance,
|
CurrentDiscountBalance = userWallet.DiscountBalance,
|
||||||
ChangeDiscountValue = BasePackageAmount,
|
ChangeDiscountValue = SystemConstants.BasePackageAmount,
|
||||||
IsIncrease = true,
|
IsIncrease = true,
|
||||||
RefrenceId = transaction.Id
|
RefrenceId = transaction.Id
|
||||||
};
|
};
|
||||||
|
|||||||
+10
-24
@@ -1,3 +1,4 @@
|
|||||||
|
using CMSMicroservice.Domain.Common;
|
||||||
using CMSMicroservice.Domain.Enums;
|
using CMSMicroservice.Domain.Enums;
|
||||||
using CMSMicroservice.Domain.Events;
|
using CMSMicroservice.Domain.Events;
|
||||||
using CMSMicroservice.Domain.Entities.Order;
|
using CMSMicroservice.Domain.Entities.Order;
|
||||||
@@ -63,16 +64,11 @@ public class
|
|||||||
}
|
}
|
||||||
|
|
||||||
long finalAmount = 0;
|
long finalAmount = 0;
|
||||||
var vatIsEnable =
|
|
||||||
_context.SystemConfigurations
|
// استفاده از SystemConstants برای VAT
|
||||||
.FirstOrDefault(f => f.Scope == ConfigurationScope.VAT && f.Key == "IsEnabled")?.Value ??
|
if (SystemConstants.ShopVATEnabled)
|
||||||
"0";
|
|
||||||
if (vatIsEnable=="1")
|
|
||||||
{
|
{
|
||||||
_vatRate = float.Parse(
|
_vatRate = (float)SystemConstants.ShopVAT;
|
||||||
_context.SystemConfigurations
|
|
||||||
.FirstOrDefault(f => f.Scope == ConfigurationScope.VAT && f.Key == "Shop.VAT")?.Value ??
|
|
||||||
throw new InvalidOperationException());
|
|
||||||
finalAmount = AddVAT(user.UserCarts.Sum(s => s.Count * s.Product.Price));
|
finalAmount = AddVAT(user.UserCarts.Sum(s => s.Count * s.Product.Price));
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"Calculating final amount with VAT. Base Amount: {BaseAmount}, VAT Rate: {VATRate}, Final Amount: {FinalAmount}",
|
"Calculating final amount with VAT. Base Amount: {BaseAmount}, VAT Rate: {VATRate}, Final Amount: {FinalAmount}",
|
||||||
@@ -147,7 +143,7 @@ public class
|
|||||||
{
|
{
|
||||||
ProductId = s.ProductId,
|
ProductId = s.ProductId,
|
||||||
Count = s.Count,
|
Count = s.Count,
|
||||||
UnitPrice =vatIsEnable=="1" ?AddVAT(s.Product.Price): s.Product.Price,
|
UnitPrice = SystemConstants.ShopVATEnabled ? AddVAT(s.Product.Price) : s.Product.Price,
|
||||||
OrderId = newOrder.Id
|
OrderId = newOrder.Id
|
||||||
});
|
});
|
||||||
await _context.FactorDetails.AddRangeAsync(factorDetailsList, cancellationToken);
|
await _context.FactorDetails.AddRangeAsync(factorDetailsList, cancellationToken);
|
||||||
@@ -183,25 +179,15 @@ public class
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// بررسی فعال بودن VAT
|
// بررسی فعال بودن VAT از SystemConstants
|
||||||
var vatEnabledConfig = await _context.SystemConfigurations
|
if (!SystemConstants.ShopVATEnabled)
|
||||||
.FirstOrDefaultAsync(x => x.Scope == ConfigurationScope.VAT && x.Key == "IsEnabled", cancellationToken);
|
|
||||||
|
|
||||||
if (vatEnabledConfig == null || !bool.TryParse(vatEnabledConfig.Value, out var isEnabled) || !isEnabled)
|
|
||||||
{
|
{
|
||||||
_logger.LogInformation("VAT is disabled. Skipping VAT calculation for order {OrderId}", orderId);
|
_logger.LogInformation("VAT is disabled. Skipping VAT calculation for order {OrderId}", orderId);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// دریافت نرخ VAT
|
// دریافت نرخ VAT از SystemConstants
|
||||||
var vatRateConfig = await _context.SystemConfigurations
|
var vatRate = SystemConstants.ShopVAT;
|
||||||
.FirstOrDefaultAsync(x => x.Scope == ConfigurationScope.VAT && x.Key == "Shop.VAT", cancellationToken);
|
|
||||||
|
|
||||||
if (vatRateConfig == null || !decimal.TryParse(vatRateConfig.Value, out var vatRate))
|
|
||||||
{
|
|
||||||
_logger.LogWarning("VAT Rate configuration not found or invalid. Using default 0.09");
|
|
||||||
vatRate = 0.09m;
|
|
||||||
}
|
|
||||||
|
|
||||||
// محاسبه مالیات
|
// محاسبه مالیات
|
||||||
var vatAmount = (long)(orderAmount * vatRate);
|
var vatAmount = (long)(orderAmount * vatRate);
|
||||||
|
|||||||
@@ -0,0 +1,176 @@
|
|||||||
|
namespace CMSMicroservice.Domain.Common;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// تنظیمات ثابت سیستم - Static و در مموری
|
||||||
|
/// این مقادیر هرگز تغییر نمیکنند و نیازی به جدول ندارند
|
||||||
|
/// </summary>
|
||||||
|
public static class SystemConstants
|
||||||
|
{
|
||||||
|
#region Network Settings
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// اجازه حذف والدین که فرزند دارند
|
||||||
|
/// </summary>
|
||||||
|
public const bool NetworkAllowOrphanNodes = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// حداکثر تعداد فرزند مستقیم در هر پا
|
||||||
|
/// </summary>
|
||||||
|
public const int NetworkMaxChildrenPerLeg = 1;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Club Settings
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مبلغ هدیه حق عضویت باشگاه (ریال) - این مبلغ از کیف پول کم نمیشود
|
||||||
|
/// </summary>
|
||||||
|
public const long ClubMembershipGiftValue = 25_200_000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// هزینه فعالسازی عضویت باشگاه (ریال)
|
||||||
|
/// </summary>
|
||||||
|
public const long ClubActivationFee = 25_200_000;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Package Settings
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مبلغ پکیج طلایی / پایه (ریال) - 56 میلیون تومان
|
||||||
|
/// شامل: هدیه باشگاه + هزینه فعالسازی + مزایای دیگر
|
||||||
|
/// </summary>
|
||||||
|
public const long BasePackageAmount = 56_000_000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مبلغ وام دایا (ریال) - همان مبلغ پکیج طلایی
|
||||||
|
/// </summary>
|
||||||
|
public const long DayaLoanAmount = 56_000_000;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Commission Settings
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// امکان برداشت نقدی فعال باشد
|
||||||
|
/// </summary>
|
||||||
|
public const bool CommissionCashWithdrawalEnabled = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// حداقل مبلغ برداشت (ریال)
|
||||||
|
/// </summary>
|
||||||
|
public const long CommissionMinWithdrawalAmount = 1_000_000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// سقف تعادل هفتگی برای هر دست (چپ یا راست) - حداکثر کل = 600
|
||||||
|
/// </summary>
|
||||||
|
public const int CommissionMaxWeeklyBalancesPerLeg = 300;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// حداکثر عمق شبکه برای محاسبه کمیسیون (تعداد لول زیرمجموعه)
|
||||||
|
/// </summary>
|
||||||
|
public const int CommissionMaxNetworkLevel = 15;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// روش محاسبه (ORM یا SP)
|
||||||
|
/// </summary>
|
||||||
|
public const string CommissionCalculationStrategy = "SP";
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region System Settings
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// حالت تعمیر و نگهداری سیستم
|
||||||
|
/// </summary>
|
||||||
|
public const bool SystemMaintenanceMode = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// فعالسازی لاگ تغییرات
|
||||||
|
/// </summary>
|
||||||
|
public const bool SystemEnableAuditLog = true;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Shop Settings
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مالیات بر ارزش افزوده (10%)
|
||||||
|
/// </summary>
|
||||||
|
public const decimal ShopVAT = 0.1m;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مالیات فعال است؟
|
||||||
|
/// </summary>
|
||||||
|
public const bool ShopVATEnabled = true;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Helper Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// دریافت مقدار به صورت دیکشنری برای نمایش در Admin Panel
|
||||||
|
/// </summary>
|
||||||
|
public static Dictionary<string, object> GetAllAsDict()
|
||||||
|
{
|
||||||
|
return new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
// Network
|
||||||
|
["Network.AllowOrphanNodes"] = NetworkAllowOrphanNodes,
|
||||||
|
["Network.MaxChildrenPerLeg"] = NetworkMaxChildrenPerLeg,
|
||||||
|
|
||||||
|
// Club
|
||||||
|
["Club.MembershipGiftValue"] = ClubMembershipGiftValue,
|
||||||
|
["Club.ActivationFee"] = ClubActivationFee,
|
||||||
|
|
||||||
|
// Commission
|
||||||
|
["Commission.CashWithdrawalEnabled"] = CommissionCashWithdrawalEnabled,
|
||||||
|
["Commission.MinWithdrawalAmount"] = CommissionMinWithdrawalAmount,
|
||||||
|
["Commission.MaxWeeklyBalancesPerLeg"] = CommissionMaxWeeklyBalancesPerLeg,
|
||||||
|
["Commission.MaxNetworkLevel"] = CommissionMaxNetworkLevel,
|
||||||
|
["Commission.CalculationStrategy"] = CommissionCalculationStrategy,
|
||||||
|
|
||||||
|
// System
|
||||||
|
["System.MaintenanceMode"] = SystemMaintenanceMode,
|
||||||
|
["System.EnableAuditLog"] = SystemEnableAuditLog,
|
||||||
|
|
||||||
|
// Shop
|
||||||
|
["Shop.VAT"] = ShopVAT,
|
||||||
|
["Shop.VATEnabled"] = ShopVATEnabled
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// دریافت لیست تنظیمات با توضیحات
|
||||||
|
/// </summary>
|
||||||
|
public static List<(string Key, object Value, string Description)> GetAllWithDescriptions()
|
||||||
|
{
|
||||||
|
return new List<(string Key, object Value, string Description)>
|
||||||
|
{
|
||||||
|
// Network
|
||||||
|
("Network.AllowOrphanNodes", NetworkAllowOrphanNodes, "اجازه حذف والدین که فرزند دارند"),
|
||||||
|
("Network.MaxChildrenPerLeg", NetworkMaxChildrenPerLeg, "حداکثر تعداد فرزند مستقیم در هر پا"),
|
||||||
|
|
||||||
|
// Club
|
||||||
|
("Club.MembershipGiftValue", ClubMembershipGiftValue, "مبلغ هدیه حق عضویت باشگاه (ریال)"),
|
||||||
|
("Club.ActivationFee", ClubActivationFee, "هزینه فعالسازی عضویت باشگاه (ریال)"),
|
||||||
|
|
||||||
|
// Commission
|
||||||
|
("Commission.CashWithdrawalEnabled", CommissionCashWithdrawalEnabled, "امکان برداشت نقدی فعال باشد"),
|
||||||
|
("Commission.MinWithdrawalAmount", CommissionMinWithdrawalAmount, "حداقل مبلغ برداشت (ریال)"),
|
||||||
|
("Commission.MaxWeeklyBalancesPerLeg", CommissionMaxWeeklyBalancesPerLeg, "سقف تعادل هفتگی برای هر دست"),
|
||||||
|
("Commission.MaxNetworkLevel", CommissionMaxNetworkLevel, "حداکثر عمق شبکه برای محاسبه کمیسیون"),
|
||||||
|
("Commission.CalculationStrategy", CommissionCalculationStrategy, "روش محاسبه (ORM/SP)"),
|
||||||
|
|
||||||
|
// System
|
||||||
|
("System.MaintenanceMode", SystemMaintenanceMode, "حالت تعمیر و نگهداری سیستم"),
|
||||||
|
("System.EnableAuditLog", SystemEnableAuditLog, "فعالسازی لاگ تغییرات"),
|
||||||
|
|
||||||
|
// Shop
|
||||||
|
("Shop.VAT", ShopVAT, "مالیات بر ارزش افزوده"),
|
||||||
|
("Shop.VATEnabled", ShopVATEnabled, "مالیات فعال است؟")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
namespace CMSMicroservice.Domain.Entities.Configuration;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// تنظیمات پویای سیستم - قابل تغییر بدون Deployment
|
|
||||||
/// </summary>
|
|
||||||
public class SystemConfiguration : BaseAuditableEntity
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// محدوده تنظیمات (System, Network, Club, Commission)
|
|
||||||
/// </summary>
|
|
||||||
public ConfigurationScope Scope { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// کلید تنظیم (مثلاً "MaxWeeklyBalancesPerUser")
|
|
||||||
/// </summary>
|
|
||||||
public string Key { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// مقدار بهصورت رشته (تفسیر در Application Layer)
|
|
||||||
/// </summary>
|
|
||||||
public string Value { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// نوع داده برای Validation و UI (Int/Decimal/Bool/String/Json)
|
|
||||||
/// </summary>
|
|
||||||
public string? DataType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// توضیحات برای ادمین
|
|
||||||
/// </summary>
|
|
||||||
public string? Description { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// فعال یا غیرفعال
|
|
||||||
/// </summary>
|
|
||||||
public bool IsActive { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// SystemConfigurationHistory Collection Navigation Reference
|
|
||||||
/// </summary>
|
|
||||||
public virtual ICollection<SystemConfigurationHistory>? SystemConfigurationHistories { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
namespace CMSMicroservice.Domain.Entities.History;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// تاریخچه تغییرات تنظیمات سیستم (برای Audit)
|
|
||||||
/// </summary>
|
|
||||||
public class SystemConfigurationHistory : BaseAuditableEntity
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// شناسه تنظیم
|
|
||||||
/// </summary>
|
|
||||||
public long ConfigurationId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// SystemConfiguration Navigation Property
|
|
||||||
/// </summary>
|
|
||||||
public virtual SystemConfiguration? Configuration { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// محدوده تنظیمات
|
|
||||||
/// </summary>
|
|
||||||
public ConfigurationScope Scope { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// کلید تنظیم
|
|
||||||
/// </summary>
|
|
||||||
public string Key { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// مقدار قبل از تغییر
|
|
||||||
/// </summary>
|
|
||||||
public string OldValue { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// مقدار بعد از تغییر
|
|
||||||
/// </summary>
|
|
||||||
public string NewValue { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// دلیل تغییر (اختیاری)
|
|
||||||
/// </summary>
|
|
||||||
public string? Reason { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// چه کسی انجام داده (UserId یا "System")
|
|
||||||
/// </summary>
|
|
||||||
public string? PerformedBy { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
namespace CMSMicroservice.Domain.Enums;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// محدوده تنظیمات سیستم (Scope)
|
|
||||||
/// </summary>
|
|
||||||
public enum ConfigurationScope
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// تنظیمات کلی سیستم
|
|
||||||
/// </summary>
|
|
||||||
System = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// تنظیمات شبکه باینری
|
|
||||||
/// </summary>
|
|
||||||
Network = 1,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// تنظیمات باشگاه مشتریان
|
|
||||||
/// </summary>
|
|
||||||
Club = 2,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// تنظیمات کمیسیون
|
|
||||||
/// </summary>
|
|
||||||
Commission = 3,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// تنظیمات مالیات بر ارزش افزوده
|
|
||||||
/// </summary>
|
|
||||||
VAT = 4
|
|
||||||
}
|
|
||||||
@@ -34,6 +34,7 @@ public static class ConfigureServices
|
|||||||
services.AddScoped<INetworkPlacementService, NetworkPlacementService>();
|
services.AddScoped<INetworkPlacementService, NetworkPlacementService>();
|
||||||
services.AddScoped<IAlertService, AlertService>();
|
services.AddScoped<IAlertService, AlertService>();
|
||||||
services.AddScoped<IUserNotificationService, UserNotificationService>();
|
services.AddScoped<IUserNotificationService, UserNotificationService>();
|
||||||
|
services.AddScoped<IKavenegarService, KavenegarService>();
|
||||||
|
|
||||||
// Daya Loan API Service - قابل تغییر بین Mock و Real
|
// Daya Loan API Service - قابل تغییر بین Mock و Real
|
||||||
var useMockDayaApi = configuration.GetValue<bool>("DayaApi:UseMock", false);
|
var useMockDayaApi = configuration.GetValue<bool>("DayaApi:UseMock", false);
|
||||||
|
|||||||
@@ -84,9 +84,7 @@ public class ApplicationDbContext : DbContext, IApplicationDbContext
|
|||||||
|
|
||||||
// ============= Network Club System DbSets =============
|
// ============= Network Club System DbSets =============
|
||||||
|
|
||||||
// Configuration
|
// App Version
|
||||||
public DbSet<SystemConfiguration> SystemConfigurations => Set<SystemConfiguration>();
|
|
||||||
public DbSet<SystemConfigurationHistory> SystemConfigurationHistories => Set<SystemConfigurationHistory>();
|
|
||||||
public DbSet<AppVersion> AppVersions => Set<AppVersion>();
|
public DbSet<AppVersion> AppVersions => Set<AppVersion>();
|
||||||
|
|
||||||
// Club Management
|
// Club Management
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using CMSMicroservice.Domain.Entities.Configuration;
|
|
||||||
using CMSMicroservice.Domain.Enums;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace CMSMicroservice.Infrastructure.Persistence;
|
namespace CMSMicroservice.Infrastructure.Persistence;
|
||||||
|
|
||||||
public class ApplicationDbContextInitialiser
|
public class ApplicationDbContextInitialiser
|
||||||
{
|
{
|
||||||
private readonly ApplicationDbContext _context;
|
private readonly ApplicationDbContext _context;
|
||||||
private readonly ILogger<ApplicationDbContextInitialiser> _logger;
|
private readonly ILogger<ApplicationDbContextInitialiser> _logger;
|
||||||
@@ -32,6 +29,7 @@ public class ApplicationDbContextInitialiser
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SeedAsync()
|
public async Task SeedAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -44,113 +42,12 @@ public class ApplicationDbContextInitialiser
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public async Task TrySeedAsync()
|
|
||||||
|
public Task TrySeedAsync()
|
||||||
{
|
{
|
||||||
// Seed / upsert default System Configurations for Network-Club-Commission System
|
// SystemConfigurations دیگه در دیتابیس نیست
|
||||||
var desiredConfigurations = new List<SystemConfiguration>
|
// مقادیر کانفیگ حالا در SystemConstants.cs به صورت const تعریف شدن
|
||||||
{
|
_logger.LogInformation("Database seeding completed. System configurations are now defined as compile-time constants in SystemConstants.cs");
|
||||||
// Network Configuration
|
return Task.CompletedTask;
|
||||||
new SystemConfiguration
|
|
||||||
{
|
|
||||||
Key = "Network.MaxNetworkDepth",
|
|
||||||
Value = "15",
|
|
||||||
Description = "حداکثر عمق شبکه باینری",
|
|
||||||
Scope = ConfigurationScope.Network,
|
|
||||||
IsActive = true
|
|
||||||
},
|
|
||||||
new SystemConfiguration
|
|
||||||
{
|
|
||||||
Key = "Network.MaxChildrenPerLeg",
|
|
||||||
Value = "1",
|
|
||||||
Description = "حداکثر تعداد فرزند مستقیم در هر پا",
|
|
||||||
Scope = ConfigurationScope.Network,
|
|
||||||
IsActive = true
|
|
||||||
},
|
|
||||||
|
|
||||||
// Commission Configuration
|
|
||||||
new SystemConfiguration
|
|
||||||
{
|
|
||||||
Key = "Commission.MaxWeeklyBalancesPerLeg",
|
|
||||||
Value = "300",
|
|
||||||
Description = "سقف تعادل هفتگی برای هر دست (چپ یا راست) - حداکثر کل = 600",
|
|
||||||
Scope = ConfigurationScope.Commission,
|
|
||||||
IsActive = true
|
|
||||||
},
|
|
||||||
new SystemConfiguration
|
|
||||||
{
|
|
||||||
Key = "Commission.MaxNetworkLevel",
|
|
||||||
Value = "15",
|
|
||||||
Description = "حداکثر عمق شبکه برای محاسبه کمیسیون (تعداد لول زیرمجموعه)",
|
|
||||||
Scope = ConfigurationScope.Commission,
|
|
||||||
IsActive = true
|
|
||||||
},
|
|
||||||
new SystemConfiguration
|
|
||||||
{
|
|
||||||
Key = "Commission.MinWithdrawalAmount",
|
|
||||||
Value = "1000000",
|
|
||||||
Description = "حداقل مبلغ برداشت (ریال)",
|
|
||||||
Scope = ConfigurationScope.Commission,
|
|
||||||
IsActive = true
|
|
||||||
},
|
|
||||||
new SystemConfiguration
|
|
||||||
{
|
|
||||||
Key = "Commission.DefaultInitialContribution",
|
|
||||||
Value = "25000000",
|
|
||||||
Description = "مبلغ پیشفرض مشارکت/هزینه فعالسازی",
|
|
||||||
Scope = ConfigurationScope.Commission,
|
|
||||||
IsActive = true
|
|
||||||
},
|
|
||||||
new SystemConfiguration
|
|
||||||
{
|
|
||||||
Key = "Commission.WeeklyPoolContributionPercent",
|
|
||||||
Value = "20",
|
|
||||||
Description = "درصد مشارکت در استخر هفتگی از کل فعالسازیهای جدید شبکه (20%)",
|
|
||||||
Scope = ConfigurationScope.Commission,
|
|
||||||
IsActive = true
|
|
||||||
},
|
|
||||||
|
|
||||||
// Club Configuration
|
|
||||||
new SystemConfiguration
|
|
||||||
{
|
|
||||||
Key = "Club.ActivationFee",
|
|
||||||
Value = "25000000",
|
|
||||||
Description = "هزینه فعالسازی عضویت باشگاه (ریال)",
|
|
||||||
Scope = ConfigurationScope.Club,
|
|
||||||
IsActive = true
|
|
||||||
},
|
|
||||||
new SystemConfiguration
|
|
||||||
{
|
|
||||||
Key = "Club.MembershipGiftValue",
|
|
||||||
Value = "25200000",
|
|
||||||
Description = "مبلغ هدیه حق عضویت باشگاه (ریال) - این مبلغ از کیف پول کم نمیشود",
|
|
||||||
Scope = ConfigurationScope.Club,
|
|
||||||
IsActive = true
|
|
||||||
},
|
|
||||||
|
|
||||||
// System Configuration
|
|
||||||
new SystemConfiguration
|
|
||||||
{
|
|
||||||
Key = "System.EnableAuditLog",
|
|
||||||
Value = "true",
|
|
||||||
Description = "فعالسازی لاگ تغییرات",
|
|
||||||
Scope = ConfigurationScope.System,
|
|
||||||
IsActive = true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var existingKeys = _context.SystemConfigurations
|
|
||||||
.Select(c => c.Key)
|
|
||||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
var newConfigs = desiredConfigurations
|
|
||||||
.Where(c => !existingKeys.Contains(c.Key))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (newConfigs.Any())
|
|
||||||
{
|
|
||||||
await _context.SystemConfigurations.AddRangeAsync(newConfigs);
|
|
||||||
await _context.SaveChangesAsync();
|
|
||||||
_logger.LogInformation("Seeded {Count} default system configurations", newConfigs.Count);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
|
||||||
|
|
||||||
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// تنظیمات پویای سیستم
|
|
||||||
/// </summary>
|
|
||||||
public class SystemConfigurationConfiguration : IEntityTypeConfiguration<SystemConfiguration>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<SystemConfiguration> builder)
|
|
||||||
{
|
|
||||||
builder.HasQueryFilter(p => !p.IsDeleted);
|
|
||||||
builder.Ignore(entity => entity.DomainEvents);
|
|
||||||
|
|
||||||
builder.HasKey(entity => entity.Id);
|
|
||||||
builder.Property(entity => entity.Id).UseIdentityColumn();
|
|
||||||
|
|
||||||
builder.Property(entity => entity.Scope).IsRequired();
|
|
||||||
builder.Property(entity => entity.Key).IsRequired().HasMaxLength(200);
|
|
||||||
builder.Property(entity => entity.Value).IsRequired().HasMaxLength(1000);
|
|
||||||
builder.Property(entity => entity.DataType).IsRequired(false).HasMaxLength(50);
|
|
||||||
builder.Property(entity => entity.Description).IsRequired(false).HasMaxLength(500);
|
|
||||||
builder.Property(entity => entity.IsActive).IsRequired();
|
|
||||||
|
|
||||||
// Composite Index برای جستجوی سریع
|
|
||||||
builder.HasIndex(e => new { e.Scope, e.Key })
|
|
||||||
.IsUnique()
|
|
||||||
.HasDatabaseName("IX_SystemConfiguration_Scope_Key");
|
|
||||||
|
|
||||||
// Index برای IsActive
|
|
||||||
builder.HasIndex(e => e.IsActive)
|
|
||||||
.HasDatabaseName("IX_SystemConfiguration_IsActive");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-41
@@ -1,41 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
|
||||||
|
|
||||||
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// تاریخچه تغییرات تنظیمات سیستم
|
|
||||||
/// </summary>
|
|
||||||
public class SystemConfigurationHistoryConfiguration : IEntityTypeConfiguration<SystemConfigurationHistory>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<SystemConfigurationHistory> builder)
|
|
||||||
{
|
|
||||||
builder.HasQueryFilter(p => !p.IsDeleted);
|
|
||||||
builder.Ignore(entity => entity.DomainEvents);
|
|
||||||
|
|
||||||
builder.HasKey(entity => entity.Id);
|
|
||||||
builder.Property(entity => entity.Id).UseIdentityColumn();
|
|
||||||
|
|
||||||
builder.Property(entity => entity.ConfigurationId).IsRequired();
|
|
||||||
builder.Property(entity => entity.Scope).IsRequired();
|
|
||||||
builder.Property(entity => entity.Key).IsRequired().HasMaxLength(200);
|
|
||||||
builder.Property(entity => entity.OldValue).IsRequired().HasMaxLength(1000);
|
|
||||||
builder.Property(entity => entity.NewValue).IsRequired().HasMaxLength(1000);
|
|
||||||
builder.Property(entity => entity.Reason).IsRequired(false).HasMaxLength(500);
|
|
||||||
builder.Property(entity => entity.PerformedBy).IsRequired(false).HasMaxLength(100);
|
|
||||||
|
|
||||||
// رابطه با SystemConfiguration
|
|
||||||
builder.HasOne(entity => entity.Configuration)
|
|
||||||
.WithMany(sc => sc.SystemConfigurationHistories)
|
|
||||||
.HasForeignKey(entity => entity.ConfigurationId)
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
// Index برای ConfigurationId و Created
|
|
||||||
builder.HasIndex(e => new { e.ConfigurationId, e.Created })
|
|
||||||
.HasDatabaseName("IX_SystemConfigurationHistory_ConfigId_Created");
|
|
||||||
|
|
||||||
// Index برای Scope و Key
|
|
||||||
builder.HasIndex(e => new { e.Scope, e.Key })
|
|
||||||
.HasDatabaseName("IX_SystemConfigurationHistory_Scope_Key");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+3561
File diff suppressed because it is too large
Load Diff
+108
@@ -0,0 +1,108 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class RemoveSystemConfigurationsTables : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "SystemConfigurationHistories",
|
||||||
|
schema: "CMS");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "SystemConfigurations",
|
||||||
|
schema: "CMS");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "SystemConfigurations",
|
||||||
|
schema: "CMS",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("SqlServer:Identity", "1, 1"),
|
||||||
|
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
CreatedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
DataType = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
|
||||||
|
Description = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||||
|
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
||||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: false),
|
||||||
|
Key = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
|
||||||
|
LastModified = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
LastModifiedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
Scope = table.Column<int>(type: "int", nullable: false),
|
||||||
|
Value = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_SystemConfigurations", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "SystemConfigurationHistories",
|
||||||
|
schema: "CMS",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("SqlServer:Identity", "1, 1"),
|
||||||
|
ConfigurationId = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
CreatedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: false),
|
||||||
|
Key = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
|
||||||
|
LastModified = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
LastModifiedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
NewValue = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: false),
|
||||||
|
OldValue = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: false),
|
||||||
|
PerformedBy = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
|
||||||
|
Reason = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||||
|
Scope = table.Column<int>(type: "int", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_SystemConfigurationHistories", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_SystemConfigurationHistories_SystemConfigurations_ConfigurationId",
|
||||||
|
column: x => x.ConfigurationId,
|
||||||
|
principalSchema: "CMS",
|
||||||
|
principalTable: "SystemConfigurations",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_SystemConfigurationHistory_ConfigId_Created",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "SystemConfigurationHistories",
|
||||||
|
columns: new[] { "ConfigurationId", "Created" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_SystemConfigurationHistory_Scope_Key",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "SystemConfigurationHistories",
|
||||||
|
columns: new[] { "Scope", "Key" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_SystemConfiguration_IsActive",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "SystemConfigurations",
|
||||||
|
column: "IsActive");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_SystemConfiguration_Scope_Key",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "SystemConfigurations",
|
||||||
|
columns: new[] { "Scope", "Key" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
-138
@@ -503,65 +503,6 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.ToTable("AppVersions", "CMS");
|
b.ToTable("AppVersions", "CMS");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Configuration.SystemConfiguration", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("Created")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.Property<string>("CreatedBy")
|
|
||||||
.HasColumnType("nvarchar(max)");
|
|
||||||
|
|
||||||
b.Property<string>("DataType")
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("nvarchar(50)");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("nvarchar(500)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsActive")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<bool>("IsDeleted")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("nvarchar(200)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("LastModified")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.Property<string>("LastModifiedBy")
|
|
||||||
.HasColumnType("nvarchar(max)");
|
|
||||||
|
|
||||||
b.Property<int>("Scope")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(1000)
|
|
||||||
.HasColumnType("nvarchar(1000)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("IsActive")
|
|
||||||
.HasDatabaseName("IX_SystemConfiguration_IsActive");
|
|
||||||
|
|
||||||
b.HasIndex("Scope", "Key")
|
|
||||||
.IsUnique()
|
|
||||||
.HasDatabaseName("IX_SystemConfiguration_Scope_Key");
|
|
||||||
|
|
||||||
b.ToTable("SystemConfigurations", "CMS");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Contract", b =>
|
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Contract", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
@@ -1504,69 +1445,6 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.ToTable("NetworkMembershipHistories", "CMS");
|
b.ToTable("NetworkMembershipHistories", "CMS");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.SystemConfigurationHistory", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
|
|
||||||
|
|
||||||
b.Property<long>("ConfigurationId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<DateTime>("Created")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.Property<string>("CreatedBy")
|
|
||||||
.HasColumnType("nvarchar(max)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsDeleted")
|
|
||||||
.HasColumnType("bit");
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("nvarchar(200)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("LastModified")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.Property<string>("LastModifiedBy")
|
|
||||||
.HasColumnType("nvarchar(max)");
|
|
||||||
|
|
||||||
b.Property<string>("NewValue")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(1000)
|
|
||||||
.HasColumnType("nvarchar(1000)");
|
|
||||||
|
|
||||||
b.Property<string>("OldValue")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(1000)
|
|
||||||
.HasColumnType("nvarchar(1000)");
|
|
||||||
|
|
||||||
b.Property<string>("PerformedBy")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("nvarchar(100)");
|
|
||||||
|
|
||||||
b.Property<string>("Reason")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("nvarchar(500)");
|
|
||||||
|
|
||||||
b.Property<int>("Scope")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ConfigurationId", "Created")
|
|
||||||
.HasDatabaseName("IX_SystemConfigurationHistory_ConfigId_Created");
|
|
||||||
|
|
||||||
b.HasIndex("Scope", "Key")
|
|
||||||
.HasDatabaseName("IX_SystemConfigurationHistory_Scope_Key");
|
|
||||||
|
|
||||||
b.ToTable("SystemConfigurationHistories", "CMS");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Network.NetworkWeeklyBalance", b =>
|
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Network.NetworkWeeklyBalance", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
@@ -3238,17 +3116,6 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.Navigation("WeekDefinition");
|
b.Navigation("WeekDefinition");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.SystemConfigurationHistory", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("CMSMicroservice.Domain.Entities.Configuration.SystemConfiguration", "Configuration")
|
|
||||||
.WithMany("SystemConfigurationHistories")
|
|
||||||
.HasForeignKey("ConfigurationId")
|
|
||||||
.OnDelete(DeleteBehavior.Restrict)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Configuration");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Network.NetworkWeeklyBalance", b =>
|
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Network.NetworkWeeklyBalance", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("CMSMicroservice.Domain.Entities.User", "User")
|
b.HasOne("CMSMicroservice.Domain.Entities.User", "User")
|
||||||
@@ -3553,11 +3420,6 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.Navigation("UserCommissionPayouts");
|
b.Navigation("UserCommissionPayouts");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Configuration.SystemConfiguration", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("SystemConfigurationHistories");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Contract", b =>
|
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Contract", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("UserContracts");
|
b.Navigation("UserContracts");
|
||||||
|
|||||||
+4
-12
@@ -65,19 +65,11 @@ BEGIN
|
|||||||
AND IsActive = 1;
|
AND IsActive = 1;
|
||||||
|
|
||||||
-- =============================================
|
-- =============================================
|
||||||
-- 4. خواندن Configuration ها
|
-- 4. مقادیر ثابت (Hardcoded - از SystemConstants)
|
||||||
-- =============================================
|
-- =============================================
|
||||||
SELECT @MaxBalancesPerLeg = CAST(Value AS INT)
|
-- این مقادیر ثابت هستند و تغییر نمیکنند
|
||||||
FROM CMS.SystemConfigurations
|
SET @MaxBalancesPerLeg = 300; -- سقف تعادل هر پا
|
||||||
WHERE [Key] = 'Commission.MaxWeeklyBalancesPerLeg' AND IsActive = 1;
|
SET @MaxNetworkLevel = 15; -- حداکثر عمق شبکه
|
||||||
|
|
||||||
SELECT @MaxNetworkLevel = CAST(Value AS INT)
|
|
||||||
FROM CMS.SystemConfigurations
|
|
||||||
WHERE [Key] = 'Commission.MaxNetworkLevel' AND IsActive = 1;
|
|
||||||
|
|
||||||
-- مقادیر پیشفرض
|
|
||||||
SET @MaxBalancesPerLeg = ISNULL(@MaxBalancesPerLeg, 300);
|
|
||||||
SET @MaxNetworkLevel = ISNULL(@MaxNetworkLevel, 15);
|
|
||||||
|
|
||||||
-- =============================================
|
-- =============================================
|
||||||
-- 5. ایجاد جدول موقت برای نتایج
|
-- 5. ایجاد جدول موقت برای نتایج
|
||||||
|
|||||||
+5
-14
@@ -1,4 +1,5 @@
|
|||||||
using CMSMicroservice.Application.Common.Interfaces;
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
|
using CMSMicroservice.Domain.Common;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
@@ -13,13 +14,6 @@ public class CommissionCalculationStrategyFactory : ICommissionCalculationStrate
|
|||||||
private readonly IWeekDefinitionRepository _weekRepository;
|
private readonly IWeekDefinitionRepository _weekRepository;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// کلید Config برای انتخاب استراتژی
|
|
||||||
/// مقدار: "ORM" یا "SP"
|
|
||||||
/// پیشفرض: "ORM"
|
|
||||||
/// </summary>
|
|
||||||
private const string ConfigKey = "Commission.CalculationStrategy";
|
|
||||||
|
|
||||||
public CommissionCalculationStrategyFactory(
|
public CommissionCalculationStrategyFactory(
|
||||||
IApplicationDbContext context,
|
IApplicationDbContext context,
|
||||||
IWeekDefinitionRepository weekRepository,
|
IWeekDefinitionRepository weekRepository,
|
||||||
@@ -31,13 +25,10 @@ public class CommissionCalculationStrategyFactory : ICommissionCalculationStrate
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<ICommissionCalculationStrategy> CreateStrategyAsync(CancellationToken cancellationToken = default)
|
public Task<ICommissionCalculationStrategy> CreateStrategyAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
// خواندن Config از دیتابیس
|
// خواندن Config از SystemConstants (استاتیک)
|
||||||
var config = await _context.SystemConfigurations
|
var strategyValue = SystemConstants.CommissionCalculationStrategy.ToUpperInvariant();
|
||||||
.FirstOrDefaultAsync(x => x.Key == ConfigKey && x.IsActive, cancellationToken);
|
|
||||||
|
|
||||||
var strategyValue = config?.Value?.ToUpperInvariant() ?? "ORM";
|
|
||||||
|
|
||||||
var strategyType = strategyValue switch
|
var strategyType = strategyValue switch
|
||||||
{
|
{
|
||||||
@@ -45,7 +36,7 @@ public class CommissionCalculationStrategyFactory : ICommissionCalculationStrate
|
|||||||
_ => CommissionCalculationStrategyType.Orm
|
_ => CommissionCalculationStrategyType.Orm
|
||||||
};
|
};
|
||||||
|
|
||||||
return CreateStrategy(strategyType);
|
return Task.FromResult(CreateStrategy(strategyType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
+4
-9
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using CMSMicroservice.Application.Common.Interfaces;
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
|
using CMSMicroservice.Domain.Common;
|
||||||
using CMSMicroservice.Domain.Entities.Club;
|
using CMSMicroservice.Domain.Entities.Club;
|
||||||
using CMSMicroservice.Domain.Entities.Commission;
|
using CMSMicroservice.Domain.Entities.Commission;
|
||||||
using CMSMicroservice.Domain.Entities.Network;
|
using CMSMicroservice.Domain.Entities.Network;
|
||||||
@@ -87,15 +88,9 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy
|
|||||||
var balancesList = new List<NetworkWeeklyBalance>();
|
var balancesList = new List<NetworkWeeklyBalance>();
|
||||||
var calculatedAt = DateTime.Now;
|
var calculatedAt = DateTime.Now;
|
||||||
|
|
||||||
// خواندن یکباره Configuration ها
|
// استفاده از SystemConstants به جای دیتابیس
|
||||||
var configs = await _context.SystemConfigurations
|
var maxBalancesPerLeg = SystemConstants.CommissionMaxWeeklyBalancesPerLeg;
|
||||||
.Where(x => x.IsActive && (
|
var maxNetworkLevel = SystemConstants.CommissionMaxNetworkLevel;
|
||||||
x.Key == "Commission.MaxWeeklyBalancesPerLeg" ||
|
|
||||||
x.Key == "Commission.MaxNetworkLevel"))
|
|
||||||
.ToDictionaryAsync(x => x.Key, x => x.Value, cancellationToken);
|
|
||||||
|
|
||||||
var maxBalancesPerLeg = int.Parse(configs.GetValueOrDefault("Commission.MaxWeeklyBalancesPerLeg", "300"));
|
|
||||||
var maxNetworkLevel = int.Parse(configs.GetValueOrDefault("Commission.MaxNetworkLevel", "15"));
|
|
||||||
|
|
||||||
foreach (var user in usersInNetwork.OrderBy(o => o.Id))
|
foreach (var user in usersInNetwork.OrderBy(o => o.Id))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
|
using CMSMicroservice.Infrastructure.Configuration;
|
||||||
|
using Kavenegar;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Infrastructure.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// پیادهسازی سرویس ارسال SMS با کاوهنگار
|
||||||
|
/// </summary>
|
||||||
|
public class KavenegarService : IKavenegarService
|
||||||
|
{
|
||||||
|
private readonly KavenegarApi? _kavenegarApi;
|
||||||
|
private readonly SmsSettings _smsSettings;
|
||||||
|
private readonly ILogger<KavenegarService> _logger;
|
||||||
|
|
||||||
|
public KavenegarService(
|
||||||
|
IOptions<SmsSettings> smsSettings,
|
||||||
|
ILogger<KavenegarService> logger)
|
||||||
|
{
|
||||||
|
_smsSettings = smsSettings.Value;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
// Initialize Kavenegar API
|
||||||
|
if (_smsSettings.Enabled && !string.IsNullOrEmpty(_smsSettings.KavenegarApiKey))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_kavenegarApi = new KavenegarApi(_smsSettings.KavenegarApiKey);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "❌ Failed to initialize Kavenegar API");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task SendAsync(string mobile, string message)
|
||||||
|
{
|
||||||
|
if (!_smsSettings.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("SMS is disabled. Skipping send to {Mobile}", mobile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_kavenegarApi == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("⚠️ Kavenegar API not initialized, cannot send SMS");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Kavenegar Send is synchronous
|
||||||
|
await Task.Run(() =>
|
||||||
|
{
|
||||||
|
var result = _kavenegarApi.Send(
|
||||||
|
sender: _smsSettings.Sender,
|
||||||
|
receptor: mobile,
|
||||||
|
message: message);
|
||||||
|
|
||||||
|
_logger.LogInformation("📱 SMS sent successfully to {Mobile}, MessageId: {MessageId}", mobile, result.Messageid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "❌ Kavenegar error sending SMS to {Mobile}: {Message}", mobile, ex.Message);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task VerifyLookupAsync(string mobile, string token, string template = "Afrino")
|
||||||
|
{
|
||||||
|
if (!_smsSettings.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("SMS is disabled. Skipping VerifyLookup to {Mobile}", mobile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_kavenegarApi == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("⚠️ Kavenegar API not initialized, cannot send VerifyLookup");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Kavenegar VerifyLookup is synchronous
|
||||||
|
await Task.Run(() =>
|
||||||
|
{
|
||||||
|
var result = _kavenegarApi.VerifyLookup(
|
||||||
|
receptor: mobile,
|
||||||
|
token: token,
|
||||||
|
template: template);
|
||||||
|
|
||||||
|
_logger.LogInformation("📱 VerifyLookup SMS sent successfully to {Mobile} with template {Template}, MessageId: {MessageId}",
|
||||||
|
mobile, template, result.Messageid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "❌ Kavenegar error sending VerifyLookup to {Mobile}: {Message}", mobile, ex.Message);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Version>0.0.159</Version>
|
<Version>0.0.161</Version>
|
||||||
<DebugType>None</DebugType>
|
<DebugType>None</DebugType>
|
||||||
<DebugSymbols>False</DebugSymbols>
|
<DebugSymbols>False</DebugSymbols>
|
||||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
using CMSMicroservice.Protobuf.Protos.Configuration;
|
|
||||||
using CMSMicroservice.WebApi.Common.Services;
|
|
||||||
using CMSMicroservice.Application.ConfigurationCQ.Commands.SetConfigurationValue;
|
|
||||||
using CMSMicroservice.Application.ConfigurationCQ.Commands.DeactivateConfiguration;
|
|
||||||
using CMSMicroservice.Application.ConfigurationCQ.Queries.GetConfigurationByKey;
|
|
||||||
using CMSMicroservice.Application.ConfigurationCQ.Queries.GetAllConfigurations;
|
|
||||||
using CMSMicroservice.Application.ConfigurationCQ.Queries.GetConfigurationHistory;
|
|
||||||
|
|
||||||
namespace CMSMicroservice.WebApi.Services;
|
|
||||||
|
|
||||||
public class ConfigurationService : ConfigurationContract.ConfigurationContractBase
|
|
||||||
{
|
|
||||||
private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
|
|
||||||
|
|
||||||
public ConfigurationService(IDispatchRequestToCQRS dispatchRequestToCQRS)
|
|
||||||
{
|
|
||||||
_dispatchRequestToCQRS = dispatchRequestToCQRS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<Empty> CreateOrUpdateConfiguration(CreateOrUpdateConfigurationRequest request, ServerCallContext context)
|
|
||||||
{
|
|
||||||
return await _dispatchRequestToCQRS.Handle<CreateOrUpdateConfigurationRequest, SetConfigurationValueCommand>(request, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<Empty> DeactivateConfiguration(DeactivateConfigurationRequest request, ServerCallContext context)
|
|
||||||
{
|
|
||||||
return await _dispatchRequestToCQRS.Handle<DeactivateConfigurationRequest, DeactivateConfigurationCommand>(request, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<GetConfigurationByKeyResponse> GetConfigurationByKey(GetConfigurationByKeyRequest request, ServerCallContext context)
|
|
||||||
{
|
|
||||||
return await _dispatchRequestToCQRS.Handle<GetConfigurationByKeyRequest, GetConfigurationByKeyQuery, GetConfigurationByKeyResponse>(request, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<GetAllConfigurationsResponse> GetAllConfigurations(GetAllConfigurationsRequest request, ServerCallContext context)
|
|
||||||
{
|
|
||||||
return await _dispatchRequestToCQRS.Handle<GetAllConfigurationsRequest, GetAllConfigurationsQuery, GetAllConfigurationsResponse>(request, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<GetConfigurationHistoryResponse> GetConfigurationHistory(GetConfigurationHistoryRequest request, ServerCallContext context)
|
|
||||||
{
|
|
||||||
return await _dispatchRequestToCQRS.Handle<GetConfigurationHistoryRequest, GetConfigurationHistoryQuery, GetConfigurationHistoryResponse>(request, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -84,7 +84,8 @@ public class DayaLoanCheckWorker
|
|||||||
var processCommand = new ProcessDayaLoanApprovalCommand
|
var processCommand = new ProcessDayaLoanApprovalCommand
|
||||||
{
|
{
|
||||||
UserId = user.Id,
|
UserId = user.Id,
|
||||||
ContractNumber = result.ContractNumber
|
ContractNumber = result.ContractNumber,
|
||||||
|
LockedWalletAmount = 0, // TODO: not needed for now
|
||||||
};
|
};
|
||||||
|
|
||||||
var processResult = await _mediator.Send(processCommand);
|
var processResult = await _mediator.Send(processCommand);
|
||||||
|
|||||||
@@ -37,8 +37,8 @@
|
|||||||
"Sms": {
|
"Sms": {
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
"Provider": "Kavenegar",
|
"Provider": "Kavenegar",
|
||||||
"KavenegarApiKey": "YOUR_KAVENEGAR_API_KEY",
|
"KavenegarApiKey": "497263626F32626A48685A6137524C4F78575A766E4C74694A556B79317648424964655030682B554545413D",
|
||||||
"Sender": "10008663"
|
"Sender": "1000001110100"
|
||||||
},
|
},
|
||||||
"DayaPayment": {
|
"DayaPayment": {
|
||||||
"BaseUrl": "https://api.daya.ir",
|
"BaseUrl": "https://api.daya.ir",
|
||||||
|
|||||||
Reference in New Issue
Block a user