F2-F7: نوتیفیکیشنها+نامپکیج، ستون پکیج در CSV، مقادیر داینامیک کیفپول جادویی، ولیدیتور SystemConstants
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m29s
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m29s
F2: اضافه شدن packageName به SmsTemplates، IUserNotificationService، UserNotificationService
- DayaLoan، ClubActivated، CommissionDeposited حالا نام پکیج را نشان میدهند
F4: اضافه شدن ستون پکیج به CSV خروجیها
- commission.proto: package_name در WithdrawalRequestModel
- manualpayment.proto: package_name در ManualPaymentModel
- کوئریهندلرها UserPackagePurchases لوکاپ اضافه شد
F6: مقادیر داینامیک کیفپول جادویی از پکیج
- userwallet.proto: magic_multiplier + magic_max_credit
- UserWalletService: پاپیولیت فیلدهای جدید + فالبک به SystemConstants
F7: ولیدیتورها از SystemConstants استفاده میکنند
- WalletMaxSafeAmount (10B ریال) به عنوان حصار ایمنی
- MagicWalletMinCharge، DiscountWalletMinCharge ثابتهای مرکزی
- سقف واقعی per-package در هندلرها اعمال میشود
Proto: v0.0.189
This commit is contained in:
+11
-1
@@ -51,6 +51,15 @@ public class GetWithdrawalRequestsQueryHandler : IRequestHandler<GetWithdrawalRe
|
|||||||
.PaginatedListAsync(paginationState: request.PaginationState)
|
.PaginatedListAsync(paginationState: request.PaginationState)
|
||||||
.ToListAsync(cancellationToken);
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
// دریافت PackageName از آخرین خرید پکیج کاربران
|
||||||
|
var userIds = models.Select(x => x.UserId).Distinct().ToList();
|
||||||
|
var userPackageNames = await _context.UserPackagePurchases
|
||||||
|
.Where(p => userIds.Contains(p.UserId))
|
||||||
|
.Include(p => p.Package)
|
||||||
|
.GroupBy(p => p.UserId)
|
||||||
|
.Select(g => new { UserId = g.Key, PackageName = g.OrderByDescending(x => x.PurchasedAt).First().Package.Title })
|
||||||
|
.ToDictionaryAsync(x => x.UserId, x => x.PackageName, cancellationToken);
|
||||||
|
|
||||||
var result = models.Select(x => new WithdrawalRequestModel
|
var result = models.Select(x => new WithdrawalRequestModel
|
||||||
{
|
{
|
||||||
Id = x.Id,
|
Id = x.Id,
|
||||||
@@ -69,7 +78,8 @@ public class GetWithdrawalRequestsQueryHandler : IRequestHandler<GetWithdrawalRe
|
|||||||
BankReferenceId = x.BankReferenceId,
|
BankReferenceId = x.BankReferenceId,
|
||||||
BankTrackingCode = x.BankTrackingCode,
|
BankTrackingCode = x.BankTrackingCode,
|
||||||
PaymentFailureReason = x.PaymentFailureReason,
|
PaymentFailureReason = x.PaymentFailureReason,
|
||||||
Created = x.Created
|
Created = x.Created,
|
||||||
|
PackageName = userPackageNames.GetValueOrDefault(x.UserId)
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
return new GetWithdrawalRequestsResponseDto
|
return new GetWithdrawalRequestsResponseDto
|
||||||
|
|||||||
+1
@@ -25,4 +25,5 @@ public class WithdrawalRequestModel
|
|||||||
public string? BankTrackingCode { get; set; }
|
public string? BankTrackingCode { get; set; }
|
||||||
public string? PaymentFailureReason { get; set; }
|
public string? PaymentFailureReason { get; set; }
|
||||||
public DateTime Created { get; set; }
|
public DateTime Created { get; set; }
|
||||||
|
public string? PackageName { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ public interface IUserNotificationService
|
|||||||
long userId,
|
long userId,
|
||||||
decimal amount,
|
decimal amount,
|
||||||
int weekNumber,
|
int weekNumber,
|
||||||
|
string? packageName = null,
|
||||||
CancellationToken cancellationToken = default);
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -42,6 +43,7 @@ public interface IUserNotificationService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Task SendClubActivationNotificationAsync(
|
Task SendClubActivationNotificationAsync(
|
||||||
long userId,
|
long userId,
|
||||||
|
string? packageName = null,
|
||||||
CancellationToken cancellationToken = default);
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -50,5 +52,6 @@ public interface IUserNotificationService
|
|||||||
Task SendPayoutErrorNotificationAsync(
|
Task SendPayoutErrorNotificationAsync(
|
||||||
long userId,
|
long userId,
|
||||||
string errorMessage,
|
string errorMessage,
|
||||||
|
string? packageName = null,
|
||||||
CancellationToken cancellationToken = default);
|
CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-6
@@ -122,6 +122,7 @@ public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler<CheckAndPr
|
|||||||
contract.IsProcessed = true;
|
contract.IsProcessed = true;
|
||||||
result.WasProcessed = true;
|
result.WasProcessed = true;
|
||||||
result.NewWalletBalance = processResult.NewBalance;
|
result.NewWalletBalance = processResult.NewBalance;
|
||||||
|
result.PackageName = processResult.PackageName;
|
||||||
result.Message = "وام پردازش و کیف پول شارژ شد";
|
result.Message = "وام پردازش و کیف پول شارژ شد";
|
||||||
processedCount++;
|
processedCount++;
|
||||||
|
|
||||||
@@ -181,7 +182,7 @@ public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler<CheckAndPr
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// پردازش مالی وام دایا
|
/// پردازش مالی وام دایا
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task<(long NewBalance, long TransactionId)> ProcessDayaLoanAsync(
|
private async Task<(long NewBalance, long TransactionId, string PackageName)> ProcessDayaLoanAsync(
|
||||||
User user,
|
User user,
|
||||||
string contractNumber,
|
string contractNumber,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
@@ -279,25 +280,25 @@ public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler<CheckAndPr
|
|||||||
await _context.SaveChangesAsync(cancellationToken);
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
// 8. ارسال SMS
|
// 8. ارسال SMS
|
||||||
await SendDayaLoanSmsAsync(user);
|
await SendDayaLoanSmsAsync(user, package.Title);
|
||||||
|
|
||||||
return (wallet.Balance, transaction.Id);
|
return (wallet.Balance, transaction.Id, package.Title);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ارسال SMS اطلاعرسانی
|
/// ارسال SMS اطلاعرسانی
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task SendDayaLoanSmsAsync(User user)
|
private async Task SendDayaLoanSmsAsync(User user, string packageName)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var userName = SmsTemplates.GetUserName(user.FirstName+" "+user.LastName);
|
var userName = SmsTemplates.GetUserName(user.FirstName+" "+user.LastName);
|
||||||
var message = SmsTemplates.DayaLoanReceived(userName);
|
var message = SmsTemplates.DayaLoanReceived(userName, packageName);
|
||||||
|
|
||||||
await _smsService.SendAsync(user.Mobile, message);
|
await _smsService.SendAsync(user.Mobile, message);
|
||||||
|
|
||||||
// ارسال SMS به ادمین برای اطلاع
|
// ارسال SMS به ادمین برای اطلاع
|
||||||
var adminMessage = $"وام دایا دریافت شد\nکاربر: {user.FirstName} {user.LastName}\nموبایل: {user.Mobile}\nکدملی: {user.NationalCode}";
|
var adminMessage = $"وام دایا دریافت شد\nکاربر: {user.FirstName} {user.LastName}\nموبایل: {user.Mobile}\nکدملی: {user.NationalCode}\nپکیج: {packageName}";
|
||||||
await _smsService.SendAsync("09199877503", adminMessage);
|
await _smsService.SendAsync("09199877503", adminMessage);
|
||||||
|
|
||||||
_logger.LogInformation("Daya loan SMS sent to User {UserId}", user.Id);
|
_logger.LogInformation("Daya loan SMS sent to User {UserId}", user.Id);
|
||||||
|
|||||||
+5
@@ -63,6 +63,11 @@ public class DayaLoanProcessResult
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public long? NewWalletBalance { get; set; }
|
public long? NewWalletBalance { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// نام پکیج
|
||||||
|
/// </summary>
|
||||||
|
public string? PackageName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// پیام
|
/// پیام
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
+4
-2
@@ -1,3 +1,4 @@
|
|||||||
|
using CMSMicroservice.Domain.Common;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.ManualPaymentCQ.Commands.CreateManualPayment;
|
namespace CMSMicroservice.Application.ManualPaymentCQ.Commands.CreateManualPayment;
|
||||||
@@ -10,11 +11,12 @@ public class CreateManualPaymentCommandValidator : AbstractValidator<CreateManua
|
|||||||
.GreaterThan(0)
|
.GreaterThan(0)
|
||||||
.WithMessage("شناسه کاربر باید بزرگتر از صفر باشد");
|
.WithMessage("شناسه کاربر باید بزرگتر از صفر باشد");
|
||||||
|
|
||||||
|
// حصار ایمنی — مبلغ واقعی از پکیج کاربر خوانده میشود
|
||||||
RuleFor(x => x.Amount)
|
RuleFor(x => x.Amount)
|
||||||
.GreaterThan(0)
|
.GreaterThan(0)
|
||||||
.WithMessage("مبلغ باید بزرگتر از صفر باشد")
|
.WithMessage("مبلغ باید بزرگتر از صفر باشد")
|
||||||
.LessThanOrEqualTo(1_000_000_000)
|
.LessThanOrEqualTo(SystemConstants.WalletMaxSafeAmount)
|
||||||
.WithMessage("مبلغ نمیتواند بیشتر از 1 میلیارد ریال باشد");
|
.WithMessage("مبلغ وارد شده بیش از حد مجاز است");
|
||||||
|
|
||||||
RuleFor(x => x.Type)
|
RuleFor(x => x.Type)
|
||||||
.IsInEnum()
|
.IsInEnum()
|
||||||
|
|||||||
+15
@@ -69,6 +69,14 @@ public class GetAllManualPaymentsQueryHandler
|
|||||||
|
|
||||||
// Pagination
|
// Pagination
|
||||||
var skip = (request.PageNumber - 1) * request.PageSize;
|
var skip = (request.PageNumber - 1) * request.PageSize;
|
||||||
|
|
||||||
|
// دریافت PackageName از آخرین خرید پکیج کاربر
|
||||||
|
var userPackageNames = await _context.UserPackagePurchases
|
||||||
|
.Include(p => p.Package)
|
||||||
|
.GroupBy(p => p.UserId)
|
||||||
|
.Select(g => new { UserId = g.Key, PackageName = g.OrderByDescending(x => x.PurchasedAt).First().Package.Title })
|
||||||
|
.ToDictionaryAsync(x => x.UserId, x => x.PackageName, cancellationToken);
|
||||||
|
|
||||||
var data = await query
|
var data = await query
|
||||||
.Skip(skip)
|
.Skip(skip)
|
||||||
.Take(request.PageSize)
|
.Take(request.PageSize)
|
||||||
@@ -96,6 +104,13 @@ public class GetAllManualPaymentsQueryHandler
|
|||||||
})
|
})
|
||||||
.ToListAsync(cancellationToken);
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
// تکمیل PackageName
|
||||||
|
foreach (var item in data)
|
||||||
|
{
|
||||||
|
if (userPackageNames.TryGetValue(item.UserId, out var pkgName))
|
||||||
|
item.PackageName = pkgName;
|
||||||
|
}
|
||||||
|
|
||||||
var metaData = new MetaData
|
var metaData = new MetaData
|
||||||
{
|
{
|
||||||
TotalCount = totalCount,
|
TotalCount = totalCount,
|
||||||
|
|||||||
+1
@@ -30,4 +30,5 @@ public class ManualPaymentDto
|
|||||||
public string? RejectionReason { get; set; }
|
public string? RejectionReason { get; set; }
|
||||||
public long? TransactionId { get; set; }
|
public long? TransactionId { get; set; }
|
||||||
public DateTime Created { get; set; }
|
public DateTime Created { get; set; }
|
||||||
|
public string? PackageName { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -1,3 +1,4 @@
|
|||||||
|
using CMSMicroservice.Domain.Common;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.WalletCQ.Commands.ChargeDiscountWallet;
|
namespace CMSMicroservice.Application.WalletCQ.Commands.ChargeDiscountWallet;
|
||||||
@@ -10,10 +11,11 @@ public class ChargeDiscountWalletCommandValidator : AbstractValidator<ChargeDisc
|
|||||||
.GreaterThan(0)
|
.GreaterThan(0)
|
||||||
.WithMessage("شناسه کاربر باید بزرگتر از صفر باشد");
|
.WithMessage("شناسه کاربر باید بزرگتر از صفر باشد");
|
||||||
|
|
||||||
|
// حصار ایمنی — سقف تخفیفی فعلاً سراسری است
|
||||||
RuleFor(x => x.Amount)
|
RuleFor(x => x.Amount)
|
||||||
.GreaterThanOrEqualTo(10_000)
|
.GreaterThanOrEqualTo(SystemConstants.DiscountWalletMinCharge)
|
||||||
.WithMessage("حداقل مبلغ شارژ 10,000 تومان است")
|
.WithMessage("حداقل مبلغ شارژ ۱,۰۰۰ تومان است")
|
||||||
.LessThanOrEqualTo(1_000_000_000)
|
.LessThanOrEqualTo(SystemConstants.WalletMaxSafeAmount)
|
||||||
.WithMessage("حداکثر مبلغ شارژ 1,000,000,000 تومان است");
|
.WithMessage("مبلغ وارد شده بیش از حد مجاز است");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-3
@@ -1,3 +1,4 @@
|
|||||||
|
using CMSMicroservice.Domain.Common;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.WalletCQ.Commands.ChargeMagicWallet;
|
namespace CMSMicroservice.Application.WalletCQ.Commands.ChargeMagicWallet;
|
||||||
@@ -10,10 +11,11 @@ public class ChargeMagicWalletCommandValidator : AbstractValidator<ChargeMagicWa
|
|||||||
.GreaterThan(0)
|
.GreaterThan(0)
|
||||||
.WithMessage("شناسه کاربر باید بزرگتر از صفر باشد");
|
.WithMessage("شناسه کاربر باید بزرگتر از صفر باشد");
|
||||||
|
|
||||||
|
// حصار ایمنی — سقف واقعی per-package در هندلر اعمال میشود
|
||||||
RuleFor(x => x.Amount)
|
RuleFor(x => x.Amount)
|
||||||
.GreaterThanOrEqualTo(100_000)
|
.GreaterThanOrEqualTo(SystemConstants.MagicWalletMinCharge)
|
||||||
.WithMessage("حداقل مبلغ شارژ جادویی ۱۰,۰۰۰ تومان است")
|
.WithMessage("حداقل مبلغ شارژ جادویی ۱۰,۰۰۰ تومان است")
|
||||||
.LessThanOrEqualTo(1_000_000_000)
|
.LessThanOrEqualTo(SystemConstants.WalletMaxSafeAmount)
|
||||||
.WithMessage("حداکثر مبلغ شارژ جادویی ۱۰۰,۰۰۰,۰۰۰ تومان است");
|
.WithMessage("مبلغ وارد شده بیش از حد مجاز است");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,18 @@ public static class SmsTemplates
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// پیام دریافت اعتبار دایا
|
/// پیام دریافت اعتبار دایا
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string DayaLoanReceived(string userName) =>
|
public static string DayaLoanReceived(string userName, string? packageName = null) =>
|
||||||
$"{userName} عزیز، تامین اعتبار شما انجام شد. شما میتوانید با مراجعه به سامانه کارابازار فرایند فعالسازی خود را ادامه دهید. با تشکر از حسن انتخاب شما";
|
packageName != null
|
||||||
|
? $"{userName} عزیز، تامین اعتبار {packageName} شما انجام شد. شما میتوانید با مراجعه به سامانه کارابازار فرایند فعالسازی خود را ادامه دهید. با تشکر از حسن انتخاب شما"
|
||||||
|
: $"{userName} عزیز، تامین اعتبار شما انجام شد. شما میتوانید با مراجعه به سامانه کارابازار فرایند فعالسازی خود را ادامه دهید. با تشکر از حسن انتخاب شما";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// پیام فعالسازی باشگاه مشتریان
|
/// پیام فعالسازی باشگاه مشتریان
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string ClubActivated(string userName) =>
|
public static string ClubActivated(string userName, string? packageName = null) =>
|
||||||
$"{userName} گرامی، عضویت شما در باشگاه مشتریان کارابازار با موفقیت فعال شد. با تشکر از حسن انتخاب شما";
|
packageName != null
|
||||||
|
? $"{userName} گرامی، عضویت شما در باشگاه مشتریان کارابازار با {packageName} فعال شد. با تشکر از حسن انتخاب شما"
|
||||||
|
: $"{userName} گرامی، عضویت شما در باشگاه مشتریان کارابازار با موفقیت فعال شد. با تشکر از حسن انتخاب شما";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// پیام خرید پکیج
|
/// پیام خرید پکیج
|
||||||
@@ -26,8 +30,10 @@ public static class SmsTemplates
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// پیام واریز کمیسیون
|
/// پیام واریز کمیسیون
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string CommissionDeposited(string userName, long amount) =>
|
public static string CommissionDeposited(string userName, long amount, string? packageName = null) =>
|
||||||
$"{userName} گرامی، مبلغ {amount:N0} ریال بابت کمیسیون به کیف پول شما واریز شد.";
|
packageName != null
|
||||||
|
? $"{userName} گرامی، مبلغ {amount:N0} ریال بابت کمیسیون {packageName} به کیف پول شما واریز شد."
|
||||||
|
: $"{userName} گرامی، مبلغ {amount:N0} ریال بابت کمیسیون به کیف پول شما واریز شد.";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// پیام برداشت موفق
|
/// پیام برداشت موفق
|
||||||
|
|||||||
@@ -66,6 +66,41 @@ public static class SystemConstants
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const long MagicWalletEntryThreshold = 1_000_000;
|
public const long MagicWalletEntryThreshold = 1_000_000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مقدار پیشفرض ضریب جادویی وقتی پکیج یافت نشود (بازگشت به پایه)
|
||||||
|
/// </summary>
|
||||||
|
public const double MagicWalletDefaultMultiplier = 2.5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مقدار پیشفرض حداکثر واریز جادویی وقتی پکیج یافت نشود (ریال)
|
||||||
|
/// </summary>
|
||||||
|
public const long MagicWalletDefaultMaxDeposit = 1_000_000_000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مقدار پیشفرض حداکثر اعتبار جادویی وقتی پکیج یافت نشود (ریال)
|
||||||
|
/// </summary>
|
||||||
|
public const long MagicWalletDefaultMaxCredit = 2_500_000_000;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Wallet Validation Settings
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// حداکثر مبلغ مجاز در ولیدیتور (حصار ایمنی — مقدار واقعی از پکیج خوانده میشود).
|
||||||
|
/// ۱۰ میلیارد ریال = ۱ میلیارد تومان
|
||||||
|
/// </summary>
|
||||||
|
public const long WalletMaxSafeAmount = 10_000_000_000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// حداقل مبلغ شارژ جادویی (ریال) — ۱۰۰٬۰۰۰ ریال = ۱۰ هزار تومان
|
||||||
|
/// </summary>
|
||||||
|
public const long MagicWalletMinCharge = 100_000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// حداقل مبلغ شارژ تخفیفی (ریال) — ۱۰٬۰۰۰ ریال = ۱ هزار تومان
|
||||||
|
/// </summary>
|
||||||
|
public const long DiscountWalletMinCharge = 10_000;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Shop Settings
|
#region Shop Settings
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ public class UserNotificationService : IUserNotificationService
|
|||||||
long userId,
|
long userId,
|
||||||
decimal amount,
|
decimal amount,
|
||||||
int weekNumber,
|
int weekNumber,
|
||||||
|
string? packageName = null,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
@@ -69,14 +70,15 @@ public class UserNotificationService : IUserNotificationService
|
|||||||
if (string.IsNullOrEmpty(userFullName)) userFullName = "کاربر عزیز";
|
if (string.IsNullOrEmpty(userFullName)) userFullName = "کاربر عزیز";
|
||||||
|
|
||||||
var formattedAmount = amount.ToString("N0", new System.Globalization.CultureInfo("fa-IR"));
|
var formattedAmount = amount.ToString("N0", new System.Globalization.CultureInfo("fa-IR"));
|
||||||
|
var packageInfo = !string.IsNullOrEmpty(packageName) ? $" ({packageName})" : "";
|
||||||
|
|
||||||
// Send Email
|
// Send Email
|
||||||
if (_emailSettings.Enabled && !string.IsNullOrEmpty(user.Email))
|
if (_emailSettings.Enabled && !string.IsNullOrEmpty(user.Email))
|
||||||
{
|
{
|
||||||
var emailSubject = $"واریز کمیسیون هفته {weekNumber}";
|
var emailSubject = $"واریز کمیسیون هفته {weekNumber}{packageInfo}";
|
||||||
var emailBody = "<div dir='rtl' style='font-family: Tahoma, Arial; text-align: right;'>" +
|
var emailBody = "<div dir='rtl' style='font-family: Tahoma, Arial; text-align: right;'>" +
|
||||||
$"<h2>سلام {userFullName}</h2>" +
|
$"<h2>سلام {userFullName}</h2>" +
|
||||||
$"<p>کمیسیون هفته {weekNumber} شما به مبلغ <strong>{formattedAmount} ریال</strong> به کیف پول شما واریز شد.</p>" +
|
$"<p>کمیسیون هفته {weekNumber} شما{packageInfo} به مبلغ <strong>{formattedAmount} ریال</strong> به کیف پول شما واریز شد.</p>" +
|
||||||
"<p>از اعتماد شما سپاسگزاریم.</p>" +
|
"<p>از اعتماد شما سپاسگزاریم.</p>" +
|
||||||
"<hr/>" +
|
"<hr/>" +
|
||||||
"<p style='color: #666; font-size: 12px;'>FourSat - سیستم مدیریت باشگاه مشتریان</p>" +
|
"<p style='color: #666; font-size: 12px;'>FourSat - سیستم مدیریت باشگاه مشتریان</p>" +
|
||||||
@@ -95,7 +97,7 @@ public class UserNotificationService : IUserNotificationService
|
|||||||
{
|
{
|
||||||
await SendSmsAsync(
|
await SendSmsAsync(
|
||||||
phoneNumber: user.Mobile,
|
phoneNumber: user.Mobile,
|
||||||
message: $"سلام {userFullName}\nکمیسیون هفته {weekNumber} شما به مبلغ {formattedAmount} ریال واریز شد.\nFourSat",
|
message: $"سلام {userFullName}\nکمیسیون هفته {weekNumber} شما{packageInfo} به مبلغ {formattedAmount} ریال واریز شد.\nFourSat",
|
||||||
cancellationToken: cancellationToken);
|
cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +111,7 @@ public class UserNotificationService : IUserNotificationService
|
|||||||
|
|
||||||
public async Task SendClubActivationNotificationAsync(
|
public async Task SendClubActivationNotificationAsync(
|
||||||
long userId,
|
long userId,
|
||||||
|
string? packageName = null,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("🎉 Sending club activation notification: User={UserId}", userId);
|
_logger.LogInformation("🎉 Sending club activation notification: User={UserId}", userId);
|
||||||
@@ -120,6 +123,7 @@ public class UserNotificationService : IUserNotificationService
|
|||||||
|
|
||||||
var userFullName = $"{user.FirstName} {user.LastName}".Trim();
|
var userFullName = $"{user.FirstName} {user.LastName}".Trim();
|
||||||
if (string.IsNullOrEmpty(userFullName)) userFullName = "کاربر عزیز";
|
if (string.IsNullOrEmpty(userFullName)) userFullName = "کاربر عزیز";
|
||||||
|
var packageInfo = !string.IsNullOrEmpty(packageName) ? $" با {packageName}" : "";
|
||||||
|
|
||||||
// Send Email
|
// Send Email
|
||||||
if (_emailSettings.Enabled && !string.IsNullOrEmpty(user.Email))
|
if (_emailSettings.Enabled && !string.IsNullOrEmpty(user.Email))
|
||||||
@@ -127,7 +131,7 @@ public class UserNotificationService : IUserNotificationService
|
|||||||
var emailSubject = "فعالسازی باشگاه مشتریان FourSat";
|
var emailSubject = "فعالسازی باشگاه مشتریان FourSat";
|
||||||
var emailBody = "<div dir='rtl' style='font-family: Tahoma, Arial; text-align: right;'>" +
|
var emailBody = "<div dir='rtl' style='font-family: Tahoma, Arial; text-align: right;'>" +
|
||||||
$"<h2>تبریک {userFullName}!</h2>" +
|
$"<h2>تبریک {userFullName}!</h2>" +
|
||||||
"<p>عضویت شما در <strong>باشگاه مشتریان FourSat</strong> با موفقیت فعال شد.</p>" +
|
$"<p>عضویت شما در <strong>باشگاه مشتریان FourSat</strong>{packageInfo} با موفقیت فعال شد.</p>" +
|
||||||
"<p>از این پس میتوانید از مزایای ویژه باشگاه بهرهمند شوید.</p>" +
|
"<p>از این پس میتوانید از مزایای ویژه باشگاه بهرهمند شوید.</p>" +
|
||||||
"<hr/>" +
|
"<hr/>" +
|
||||||
"<p style='color: #666; font-size: 12px;'>FourSat - سیستم مدیریت باشگاه مشتریان</p>" +
|
"<p style='color: #666; font-size: 12px;'>FourSat - سیستم مدیریت باشگاه مشتریان</p>" +
|
||||||
@@ -146,7 +150,7 @@ public class UserNotificationService : IUserNotificationService
|
|||||||
{
|
{
|
||||||
await SendSmsAsync(
|
await SendSmsAsync(
|
||||||
phoneNumber: user.Mobile,
|
phoneNumber: user.Mobile,
|
||||||
message: $"تبریک! عضویت شما در باشگاه مشتریان FourSat فعال شد.",
|
message: $"تبریک! عضویت شما در باشگاه مشتریان FourSat{packageInfo} فعال شد.",
|
||||||
cancellationToken: cancellationToken);
|
cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,6 +163,7 @@ public class UserNotificationService : IUserNotificationService
|
|||||||
public async Task SendPayoutErrorNotificationAsync(
|
public async Task SendPayoutErrorNotificationAsync(
|
||||||
long userId,
|
long userId,
|
||||||
string errorMessage,
|
string errorMessage,
|
||||||
|
string? packageName = null,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(
|
_logger.LogWarning(
|
||||||
|
|||||||
@@ -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.188</Version>
|
<Version>0.0.189</Version>
|
||||||
<DebugType>None</DebugType>
|
<DebugType>None</DebugType>
|
||||||
<DebugSymbols>False</DebugSymbols>
|
<DebugSymbols>False</DebugSymbols>
|
||||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||||
|
|||||||
@@ -531,6 +531,7 @@ message WithdrawalRequestModel
|
|||||||
string bank_reference_id = 15;
|
string bank_reference_id = 15;
|
||||||
string bank_tracking_code = 16;
|
string bank_tracking_code = 16;
|
||||||
string payment_failure_reason = 17;
|
string payment_failure_reason = 17;
|
||||||
|
google.protobuf.StringValue package_name = 18; // نام پکیج کاربر
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ Week Definitions (for dropdowns) ============
|
// ============ Week Definitions (for dropdowns) ============
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ message ManualPaymentModel
|
|||||||
google.protobuf.Timestamp created = 19;
|
google.protobuf.Timestamp created = 19;
|
||||||
google.protobuf.StringValue image_path = 20;
|
google.protobuf.StringValue image_path = 20;
|
||||||
google.protobuf.Int64Value image_document_id = 21;
|
google.protobuf.Int64Value image_document_id = 21;
|
||||||
|
google.protobuf.StringValue package_name = 22; // نام پکیج کاربر
|
||||||
}
|
}
|
||||||
|
|
||||||
message ProcessManualMembershipPaymentRequest
|
message ProcessManualMembershipPaymentRequest
|
||||||
|
|||||||
@@ -251,6 +251,8 @@ message GetMagicWalletStatusResponse
|
|||||||
int64 balance = 6;
|
int64 balance = 6;
|
||||||
google.protobuf.Timestamp magic_activated_at = 7;
|
google.protobuf.Timestamp magic_activated_at = 7;
|
||||||
int32 purchase_cycle_count = 8; // تعداد دورهای خرید پکیج (0 = هنوز هیچ دوری تکمیل نشده)
|
int32 purchase_cycle_count = 8; // تعداد دورهای خرید پکیج (0 = هنوز هیچ دوری تکمیل نشده)
|
||||||
|
double magic_multiplier = 9; // ضریب جادویی (مثلاً 2.5)
|
||||||
|
int64 magic_max_credit = 10; // حداکثر اعتبار (ریال)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============= Discount Wallet Messages =============
|
// ============= Discount Wallet Messages =============
|
||||||
|
|||||||
@@ -233,7 +233,9 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase
|
|||||||
var package = currentCycle != null
|
var package = currentCycle != null
|
||||||
? await _context.Packages.FirstOrDefaultAsync(p => p.Id == currentCycle.PackageId, context.CancellationToken)
|
? await _context.Packages.FirstOrDefaultAsync(p => p.Id == currentCycle.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 = package?.MagicWalletMaxDeposit ?? 1_000_000_000;
|
var magicMaxDeposit = package?.MagicWalletMaxDeposit ?? SystemConstants.MagicWalletDefaultMaxDeposit;
|
||||||
|
var magicMultiplier = package != null ? (double)package.MagicWalletMultiplier : SystemConstants.MagicWalletDefaultMultiplier;
|
||||||
|
var magicMaxCredit = package?.MagicWalletMaxCredit ?? SystemConstants.MagicWalletDefaultMaxCredit;
|
||||||
|
|
||||||
var response = new GetMagicWalletStatusResponse
|
var response = new GetMagicWalletStatusResponse
|
||||||
{
|
{
|
||||||
@@ -243,7 +245,9 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase
|
|||||||
MagicMaxDeposit = magicMaxDeposit,
|
MagicMaxDeposit = magicMaxDeposit,
|
||||||
MagicRemainingDeposit = Math.Max(0, magicMaxDeposit - wallet.MagicTotalDeposited),
|
MagicRemainingDeposit = Math.Max(0, magicMaxDeposit - wallet.MagicTotalDeposited),
|
||||||
Balance = wallet.Balance,
|
Balance = wallet.Balance,
|
||||||
PurchaseCycleCount = cycleCount
|
PurchaseCycleCount = cycleCount,
|
||||||
|
MagicMultiplier = magicMultiplier,
|
||||||
|
MagicMaxCredit = magicMaxCredit
|
||||||
};
|
};
|
||||||
|
|
||||||
if (wallet.MagicActivatedAt.HasValue)
|
if (wallet.MagicActivatedAt.HasValue)
|
||||||
|
|||||||
Reference in New Issue
Block a user