fix: package-based compliance audit — feature DIFF, remove hardcoded fallbacks
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m12s
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m12s
- ActivateClubMembership: replace GetAllFeatureIds with PackageFeature DIFF (Q20) - ActivateClubMembership: fix re-activation early return — check current cycle - AcceptClubMembershipContract: same feature DIFF logic - VerifyPackagePurchase: remove ?? 2.0m fallback, throw if package null - PackageService: remove ?? 2.0m fallback for DiscountMultiplier - UserOrderService: remove ?? 1B fallback for MagicWalletMaxDeposit - SystemConstants: remove 9 dead [Obsolete] constants (per-package values)
This commit is contained in:
+57
-30
@@ -262,36 +262,8 @@ public class AcceptClubMembershipContractCommandHandler
|
|||||||
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
// 10. اعطای ویژگیهای باشگاه برای کاربر (فقط برای عضویت جدید)
|
// 10. اعمال ویژگیهای پکیج — DIFF/تفاضل (Q20)
|
||||||
if (isNewMembership)
|
await ApplyFeatureDiffAsync(user.Id, clubMembership.Id, package.Id, isNewMembership, activationDate, cancellationToken);
|
||||||
{
|
|
||||||
var featureIds = ClubFeatureTypeExtensions.GetAllFeatureIds();
|
|
||||||
var clubFeatures = await _context.ClubFeatures
|
|
||||||
.Where(f => !f.IsDeleted && featureIds.Contains(f.Id))
|
|
||||||
.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
if (clubFeatures.Any())
|
|
||||||
{
|
|
||||||
var userClubFeatures = clubFeatures.Select(feature => new UserClubFeature
|
|
||||||
{
|
|
||||||
UserId = user.Id,
|
|
||||||
ClubMembershipId = clubMembership.Id,
|
|
||||||
ClubFeatureId = feature.Id,
|
|
||||||
GrantedAt = activationDate,
|
|
||||||
IsActive = true,
|
|
||||||
Notes = "اعطا شده هنگام امضای قرارداد"
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
_context.UserClubFeatures.AddRange(userClubFeatures);
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
_logger.LogInformation(
|
|
||||||
"Granted {Count} club features to UserId {UserId}",
|
|
||||||
clubFeatures.Count,
|
|
||||||
user.Id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"Club membership contract accepted and activated for UserId: {UserId}, ContractId: {ContractId}, MembershipId: {MembershipId}",
|
"Club membership contract accepted and activated for UserId: {UserId}, ContractId: {ContractId}, MembershipId: {MembershipId}",
|
||||||
@@ -321,6 +293,61 @@ public class AcceptClubMembershipContractCommandHandler
|
|||||||
return week.Id;
|
return week.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ApplyFeatureDiffAsync(
|
||||||
|
long userId, long membershipId, long packageId,
|
||||||
|
bool isNewMembership, DateTime activationDate,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var newFeatureIds = await _context.PackageFeatures
|
||||||
|
.Where(pf => pf.PackageId == packageId && pf.IsIncluded && !pf.IsDeleted)
|
||||||
|
.Select(pf => pf.ClubFeatureId)
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
if (!newFeatureIds.Any())
|
||||||
|
{
|
||||||
|
_logger.LogWarning("No PackageFeatures for PackageId {PackageId}", packageId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentFeatures = await _context.UserClubFeatures
|
||||||
|
.Where(ucf => ucf.UserId == userId && ucf.IsActive)
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
var currentFeatureIds = currentFeatures.Select(f => f.ClubFeatureId).ToHashSet();
|
||||||
|
var toAdd = newFeatureIds.Where(id => !currentFeatureIds.Contains(id)).ToList();
|
||||||
|
var toRemove = currentFeatures.Where(f => !newFeatureIds.Contains(f.ClubFeatureId)).ToList();
|
||||||
|
|
||||||
|
foreach (var feature in toRemove)
|
||||||
|
{
|
||||||
|
feature.IsActive = false;
|
||||||
|
feature.Notes = $"حذف شده بابت تغییر پکیج (PackageId: {packageId})";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toAdd.Any())
|
||||||
|
{
|
||||||
|
var newUserFeatures = toAdd.Select(featureId => new UserClubFeature
|
||||||
|
{
|
||||||
|
UserId = userId,
|
||||||
|
ClubMembershipId = membershipId,
|
||||||
|
ClubFeatureId = featureId,
|
||||||
|
GrantedAt = activationDate,
|
||||||
|
IsActive = true,
|
||||||
|
Notes = isNewMembership
|
||||||
|
? "اعطا شده هنگام امضای قرارداد"
|
||||||
|
: $"اضافه شده بابت خرید مجدد پکیج (PackageId: {packageId})"
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
_context.UserClubFeatures.AddRange(newUserFeatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Feature DIFF applied for UserId={UserId}: Added={Added}, Removed={Removed}, Kept={Kept}",
|
||||||
|
userId, toAdd.Count, toRemove.Count,
|
||||||
|
currentFeatureIds.Count - toRemove.Count);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<(bool Success, string Message)> VerifyOtpAsync(
|
private async Task<(bool Success, string Message)> VerifyOtpAsync(
|
||||||
string mobile,
|
string mobile,
|
||||||
string code,
|
string code,
|
||||||
|
|||||||
+80
-40
@@ -221,16 +221,20 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (existingMembership.IsActive)
|
// بررسی آیا چرخه فعال دارد — اگر بله، واقعاً فعال است و نیازی به کار نیست
|
||||||
|
var hasCurrentCycle = await _context.ClubMembershipCycles
|
||||||
|
.AnyAsync(c => c.UserId == user.Id && c.IsCurrentCycle, cancellationToken);
|
||||||
|
|
||||||
|
if (existingMembership.IsActive && hasCurrentCycle)
|
||||||
{
|
{
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"User {UserId} is already an active club member",
|
"User {UserId} is already an active club member with a running cycle",
|
||||||
user.Id
|
user.Id
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// فعالسازی مجدد — FirstActivation حفظ میشه (Q21), Last بروزرسانی میشه
|
// فعالسازی مجدد یا خرید مجدد (Q19/Q20) — FirstActivation حفظ میشه (Q21), Last بروزرسانی میشه
|
||||||
entity = existingMembership;
|
entity = existingMembership;
|
||||||
entity.IsActive = true;
|
entity.IsActive = true;
|
||||||
entity.LastActivationDate = activationDate;
|
entity.LastActivationDate = activationDate;
|
||||||
@@ -240,8 +244,9 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
_context.ClubMemberships.Update(entity);
|
_context.ClubMemberships.Update(entity);
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"Reactivated club membership for UserId {UserId}",
|
"Re-activating club membership for UserId {UserId} (re-purchase scenario, IsActive was {WasActive})",
|
||||||
user.Id
|
user.Id,
|
||||||
|
existingMembership.IsActive
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,41 +348,8 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
// 9. اضافه کردن ویژگیهای باشگاه برای کاربر (فقط برای عضویت جدید)
|
// 9. اعمال ویژگیهای پکیج — DIFF/تفاضل (Q20)
|
||||||
if (isNewMembership)
|
await ApplyFeatureDiffAsync(user.Id, entity.Id, package.Id, isNewMembership, activationDate, cancellationToken);
|
||||||
{
|
|
||||||
var featureIds = ClubFeatureTypeExtensions.GetAllFeatureIds();
|
|
||||||
var clubFeatures = await _context.ClubFeatures
|
|
||||||
.Where(f => !f.IsDeleted && featureIds.Contains(f.Id))
|
|
||||||
.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
if (clubFeatures.Any())
|
|
||||||
{
|
|
||||||
var userClubFeatures = clubFeatures.Select(feature => new UserClubFeature
|
|
||||||
{
|
|
||||||
UserId = user.Id,
|
|
||||||
ClubMembershipId = entity.Id,
|
|
||||||
ClubFeatureId = feature.Id,
|
|
||||||
GrantedAt = activationDate,
|
|
||||||
IsActive = true,
|
|
||||||
Notes = "اعطا شده بهطور خودکار هنگام فعالسازی"
|
|
||||||
}).ToList(); _context.UserClubFeatures.AddRange(userClubFeatures);
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
_logger.LogInformation(
|
|
||||||
"Granted {Count} club features to UserId {UserId}",
|
|
||||||
clubFeatures.Count,
|
|
||||||
user.Id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogWarning(
|
|
||||||
"No club features found to grant to UserId {UserId}",
|
|
||||||
user.Id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"Club membership activated successfully. UserId: {UserId}, MembershipId: {MembershipId}",
|
"Club membership activated successfully. UserId: {UserId}, MembershipId: {MembershipId}",
|
||||||
@@ -398,6 +370,74 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// اعمال DIFF فیچرها بر اساس پکیج (Q20)
|
||||||
|
/// عضویت جدید: همه فیچرهای پکیج اضافه میشه
|
||||||
|
/// خرید مجدد: تفاضل فیچرهای فعلی و پکیج جدید
|
||||||
|
/// </summary>
|
||||||
|
private async Task ApplyFeatureDiffAsync(
|
||||||
|
long userId, long membershipId, long packageId,
|
||||||
|
bool isNewMembership, DateTime activationDate,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
// فیچرهای پکیج جدید
|
||||||
|
var newFeatureIds = await _context.PackageFeatures
|
||||||
|
.Where(pf => pf.PackageId == packageId && pf.IsIncluded && !pf.IsDeleted)
|
||||||
|
.Select(pf => pf.ClubFeatureId)
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
if (!newFeatureIds.Any())
|
||||||
|
{
|
||||||
|
_logger.LogWarning(
|
||||||
|
"No PackageFeatures found for PackageId {PackageId} — skipping feature assignment for UserId {UserId}",
|
||||||
|
packageId, userId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// فیچرهای فعال فعلی کاربر
|
||||||
|
var currentFeatures = await _context.UserClubFeatures
|
||||||
|
.Where(ucf => ucf.UserId == userId && ucf.IsActive)
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
var currentFeatureIds = currentFeatures.Select(f => f.ClubFeatureId).ToHashSet();
|
||||||
|
|
||||||
|
// DIFF
|
||||||
|
var toAdd = newFeatureIds.Where(id => !currentFeatureIds.Contains(id)).ToList();
|
||||||
|
var toRemove = currentFeatures.Where(f => !newFeatureIds.Contains(f.ClubFeatureId)).ToList();
|
||||||
|
|
||||||
|
// حذف فیچرهای قدیمی که پکیج جدید ندارد
|
||||||
|
foreach (var feature in toRemove)
|
||||||
|
{
|
||||||
|
feature.IsActive = false;
|
||||||
|
feature.Notes = $"حذف شده بابت تغییر پکیج (PackageId: {packageId})";
|
||||||
|
}
|
||||||
|
|
||||||
|
// اضافه فیچرهای جدید
|
||||||
|
if (toAdd.Any())
|
||||||
|
{
|
||||||
|
var newUserFeatures = toAdd.Select(featureId => new UserClubFeature
|
||||||
|
{
|
||||||
|
UserId = userId,
|
||||||
|
ClubMembershipId = membershipId,
|
||||||
|
ClubFeatureId = featureId,
|
||||||
|
GrantedAt = activationDate,
|
||||||
|
IsActive = true,
|
||||||
|
Notes = isNewMembership
|
||||||
|
? "اعطا شده هنگام فعالسازی اولیه"
|
||||||
|
: $"اضافه شده بابت خرید مجدد پکیج (PackageId: {packageId})"
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
_context.UserClubFeatures.AddRange(newUserFeatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Feature DIFF applied for UserId={UserId}: Added={Added}, Removed={Removed}, Kept={Kept}",
|
||||||
|
userId, toAdd.Count, toRemove.Count,
|
||||||
|
currentFeatureIds.Count - toRemove.Count);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// دریافت شناسه تعریف هفته جاری
|
/// دریافت شناسه تعریف هفته جاری
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
+3
-1
@@ -101,7 +101,9 @@ public class VerifyPackagePurchaseCommandHandler
|
|||||||
|
|
||||||
// شارژ DiscountBalance (موجودی تخفیف) — ضریب تخفیف از پکیج
|
// شارژ DiscountBalance (موجودی تخفیف) — ضریب تخفیف از پکیج
|
||||||
var oldDiscountBalance = wallet.DiscountBalance;
|
var oldDiscountBalance = wallet.DiscountBalance;
|
||||||
var discountMultiplier = order.Package?.DiscountMultiplier ?? 2.0m;
|
if (order.Package == null)
|
||||||
|
throw new NotFoundException("پکیج مرتبط با سفارش یافت نشد");
|
||||||
|
var discountMultiplier = order.Package.DiscountMultiplier;
|
||||||
var discountAmount = (long)(order.Amount * discountMultiplier);
|
var discountAmount = (long)(order.Amount * discountMultiplier);
|
||||||
wallet.DiscountBalance += discountAmount;
|
wallet.DiscountBalance += discountAmount;
|
||||||
|
|
||||||
|
|||||||
@@ -20,42 +20,6 @@ public static class SystemConstants
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Club Settings
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// مبلغ هدیه حق عضویت باشگاه (ریال) - این مبلغ از کیف پول کم نمیشود
|
|
||||||
/// [DEPRECATED] از Package.Price استفاده کنید
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Moved to Package entity. Use package.Price or package.ActivationFee instead.")]
|
|
||||||
public const long ClubMembershipGiftValue = 25_200_000;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// هزینه فعالسازی عضویت باشگاه (ریال)
|
|
||||||
/// [DEPRECATED] از Package.ActivationFee استفاده کنید
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Moved to Package.ActivationFee. Read from Package entity instead.")]
|
|
||||||
public const long ClubActivationFee = 25_200_000;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Package Settings
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// مبلغ پکیج پایه (ریال)
|
|
||||||
/// [DEPRECATED] از Package.Price استفاده کنید
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Moved to Package.Price. Read from Package entity instead.")]
|
|
||||||
public const long BasePackageAmount = 56_000_000;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// مبلغ وام دایا (ریال)
|
|
||||||
/// [DEPRECATED] از Package.Price استفاده کنید
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Moved to Package.Price. Read from Package entity with SupportsDayaPurchase=true.")]
|
|
||||||
public const long DayaLoanAmount = 56_000_000;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Commission Settings
|
#region Commission Settings
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -68,20 +32,6 @@ public static class SystemConstants
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const long CommissionMinWithdrawalAmount = 1_000_000;
|
public const long CommissionMinWithdrawalAmount = 1_000_000;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// سقف تعادل هفتگی برای هر دست (چپ یا راست) - حداکثر کل = 600
|
|
||||||
/// [DEPRECATED] از Package.MaxBalancesPerLeg استفاده کنید
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Moved to Package.MaxBalancesPerLeg. Read from Package entity instead.")]
|
|
||||||
public const int CommissionMaxWeeklyBalancesPerLeg = 300;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// حداکثر عمق شبکه برای محاسبه کمیسیون (تعداد لول زیرمجموعه)
|
|
||||||
/// [DEPRECATED] از Package.MaxNetworkLevel استفاده کنید
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Moved to Package.MaxNetworkLevel. Read from Package entity instead.")]
|
|
||||||
public const int CommissionMaxNetworkLevel = 15;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// روش محاسبه (ORM یا SP)
|
/// روش محاسبه (ORM یا SP)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -105,26 +55,8 @@ public static class SystemConstants
|
|||||||
|
|
||||||
#region Magic Wallet Settings
|
#region Magic Wallet Settings
|
||||||
|
|
||||||
/// <summary>
|
// Per-package magic wallet settings are now in Package entity:
|
||||||
/// ضریب شارژ کیفپول جادویی — واریز × 2.5 = اعتبار
|
// Package.MagicWalletMultiplier, Package.MagicWalletMaxDeposit, Package.MagicWalletMaxCredit
|
||||||
/// [DEPRECATED] از Package.MagicWalletMultiplier استفاده کنید
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Moved to Package.MagicWalletMultiplier. Read from Package entity instead.")]
|
|
||||||
public const decimal MagicWalletMultiplier = 2.5m;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// سقف واریز در هر دور جادویی (ریال) — 100M تومان
|
|
||||||
/// [DEPRECATED] از Package.MagicWalletMaxDeposit استفاده کنید
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Moved to Package.MagicWalletMaxDeposit. Read from Package entity instead.")]
|
|
||||||
public const long MagicWalletMaxDeposit = 1_000_000_000;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// سقف اعتبار در هر دور جادویی (ریال) — 250M تومان
|
|
||||||
/// [DEPRECATED] از Package.MagicWalletMaxCredit استفاده کنید
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Moved to Package.MagicWalletMaxCredit. Read from Package entity instead.")]
|
|
||||||
public const long MagicWalletMaxCredit = 2_500_000_000;
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@@ -314,7 +314,9 @@ public class PackageService : PackageContract.PackageContractBase
|
|||||||
await _context.SaveChangesAsync(context.CancellationToken);
|
await _context.SaveChangesAsync(context.CancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
var discountAmount = (long)(purchase.Amount * (double)(purchase.Package?.DiscountMultiplier ?? 2.0m));
|
var discountMultiplier = purchase.Package?.DiscountMultiplier
|
||||||
|
?? throw new RpcException(new Status(StatusCode.Internal, "پکیج سفارش یافت نشد"));
|
||||||
|
var discountAmount = (long)(purchase.Amount * (double)discountMultiplier);
|
||||||
wallet.Balance += purchase.Amount;
|
wallet.Balance += purchase.Amount;
|
||||||
wallet.DiscountBalance += discountAmount;
|
wallet.DiscountBalance += discountAmount;
|
||||||
|
|
||||||
|
|||||||
@@ -352,7 +352,8 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
|
|||||||
var userPackage = userCycle != null
|
var userPackage = userCycle != null
|
||||||
? await _context.Packages.FirstOrDefaultAsync(p => p.Id == userCycle.PackageId, context.CancellationToken)
|
? await _context.Packages.FirstOrDefaultAsync(p => p.Id == userCycle.PackageId, context.CancellationToken)
|
||||||
: await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, context.CancellationToken);
|
: await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, context.CancellationToken);
|
||||||
var magicMaxDeposit = userPackage?.MagicWalletMaxDeposit ?? 1_000_000_000;
|
var magicMaxDeposit = userPackage?.MagicWalletMaxDeposit
|
||||||
|
?? throw new InvalidOperationException($"Package not found for magic wallet exit check, UserId={userId}");
|
||||||
|
|
||||||
if (wallet.MagicTotalDeposited >= magicMaxDeposit)
|
if (wallet.MagicTotalDeposited >= magicMaxDeposit)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user