Compare commits
14 Commits
12749ccb01
...
kub-stage
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ae1b0cd70 | ||
|
|
2ae6034fbb | ||
|
|
094846ce8b | ||
|
|
b19cf5e32e | ||
|
|
b1c3fcfd66 | ||
|
|
1f6c5a1f45 | ||
|
|
99b217d5b5 | ||
|
|
8ee3fe6f7b | ||
|
|
3c7ac68eeb | ||
|
|
f27418cff4 | ||
|
|
aba534e07c | ||
|
|
af3a29ed27 | ||
|
|
db951699f8 | ||
|
|
bb6b7c709c |
@@ -25,7 +25,7 @@ public class GetClubMembershipHistoryQueryHandler : IRequestHandler<GetClubMembe
|
||||
query = query.Where(x => x.UserId == request.UserId.Value);
|
||||
}
|
||||
|
||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "-Created"); // پیشفرض: جدیدترین اول
|
||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "Created"); // پیشفرض: جدیدترین اول
|
||||
|
||||
var meta = await query.GetMetaData(request.PaginationState, cancellationToken);
|
||||
|
||||
|
||||
@@ -279,10 +279,13 @@ public class CalculateWeeklyBalancesCommandHandler : IRequestHandler<CalculateWe
|
||||
var year = int.Parse(parts[0]);
|
||||
var week = int.Parse(parts[1].Replace("W", ""));
|
||||
|
||||
// محاسبه اولین روز هفته (شنبه)
|
||||
// محاسبه اولین شنبه سال
|
||||
var jan1 = new DateTime(year, 1, 1);
|
||||
var daysOffset = DayOfWeek.Saturday - jan1.DayOfWeek;
|
||||
var firstSaturday = jan1.AddDays(daysOffset);
|
||||
var jan1DayOfWeek = (int)jan1.DayOfWeek;
|
||||
// اگر 1 ژانویه شنبه باشد: offset=0، اگر یکشنبه: offset=6، دوشنبه: offset=5، ...
|
||||
var daysToFirstSaturday = jan1DayOfWeek == 6 ? 0 : (6 - jan1DayOfWeek + 7) % 7;
|
||||
var firstSaturday = jan1.AddDays(daysToFirstSaturday);
|
||||
|
||||
var weekStart = firstSaturday.AddDays((week - 1) * 7);
|
||||
var weekEnd = weekStart.AddDays(6).AddHours(23).AddMinutes(59).AddSeconds(59);
|
||||
|
||||
|
||||
@@ -87,7 +87,15 @@ public class GetAvailableWeeksQueryHandler : IRequestHandler<GetAvailableWeeksQu
|
||||
var log = executionLogs.GetValueOrDefault(weekNumber);
|
||||
|
||||
var isCalculated = pool != null && pool.IsCalculated;
|
||||
var displayText = $"{weekNumber} ({startDate:yyyy/MM/dd} - {endDate:yyyy/MM/dd})";
|
||||
|
||||
// تبدیل تاریخ به شمسی برای نمایش
|
||||
var persianCalendar = new PersianCalendar();
|
||||
var startDatePersian = $"{persianCalendar.GetYear(startDate):D4}/{persianCalendar.GetMonth(startDate):D2}/{persianCalendar.GetDayOfMonth(startDate):D2}";
|
||||
var endDatePersian = $"{persianCalendar.GetYear(endDate):D4}/{persianCalendar.GetMonth(endDate):D2}/{persianCalendar.GetDayOfMonth(endDate):D2}";
|
||||
|
||||
// تبدیل weekNumber به شمسی فقط برای نمایش
|
||||
var persianWeekNumber = ConvertWeekNumberToPersian(weekNumber, startDate);
|
||||
var displayText = $"{persianWeekNumber} ({startDatePersian} - {endDatePersian})";
|
||||
|
||||
if (isCalculated)
|
||||
{
|
||||
@@ -96,7 +104,7 @@ public class GetAvailableWeeksQueryHandler : IRequestHandler<GetAvailableWeeksQu
|
||||
|
||||
return new WeekInfoDto
|
||||
{
|
||||
WeekNumber = weekNumber,
|
||||
WeekNumber = weekNumber, // میلادی (برای API)
|
||||
StartDate = startDate,
|
||||
EndDate = endDate,
|
||||
IsCalculated = isCalculated,
|
||||
@@ -104,29 +112,81 @@ public class GetAvailableWeeksQueryHandler : IRequestHandler<GetAvailableWeeksQu
|
||||
LastExecutionStatus = log?.Status.ToString(),
|
||||
TotalPoolAmount = pool?.TotalPoolAmount,
|
||||
EligibleUsersCount = pool?.UserCommissionPayouts?.Count ?? 0,
|
||||
DisplayText = displayText
|
||||
DisplayText = displayText // شمسی (برای نمایش)
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetWeekNumber(DateTime date)
|
||||
{
|
||||
var calendar = CultureInfo.InvariantCulture.Calendar;
|
||||
var weekOfYear = calendar.GetWeekOfYear(
|
||||
date,
|
||||
CalendarWeekRule.FirstFourDayWeek,
|
||||
DayOfWeek.Monday);
|
||||
var year = date.Year;
|
||||
|
||||
return $"{date.Year}-W{weekOfYear:D2}";
|
||||
// پیدا کردن اولین شنبه سال
|
||||
var jan1 = new DateTime(year, 1, 1);
|
||||
var jan1DayOfWeek = (int)jan1.DayOfWeek; // 0=Sunday, 1=Monday, ..., 6=Saturday
|
||||
|
||||
// محاسبه تعداد روزهایی که باید اضافه کنیم تا به اولین شنبه برسیم
|
||||
var daysToFirstSaturday = jan1DayOfWeek == 6 ? 0 : (6 - jan1DayOfWeek + 7) % 7;
|
||||
var firstSaturday = jan1.AddDays(daysToFirstSaturday);
|
||||
|
||||
// پیدا کردن شنبه شروع هفته جاری
|
||||
var currentDayOfWeek = (int)date.DayOfWeek; // 0=Sun, 1=Mon, ..., 6=Sat
|
||||
var daysToCurrentSaturday = currentDayOfWeek == 6 ? 0 : (currentDayOfWeek + 1) % 7;
|
||||
var weekStartSaturday = date.Date.AddDays(-daysToCurrentSaturday);
|
||||
|
||||
// محاسبه شماره هفته
|
||||
int weekNum;
|
||||
if (weekStartSaturday < firstSaturday)
|
||||
{
|
||||
weekNum = 1; // هفته اول سال
|
||||
}
|
||||
else
|
||||
{
|
||||
var daysSinceFirstSaturday = (weekStartSaturday - firstSaturday).Days;
|
||||
weekNum = (daysSinceFirstSaturday / 7) + 1;
|
||||
}
|
||||
|
||||
return $"{year}-W{weekNum:D2}";
|
||||
}
|
||||
|
||||
private static (DateTime startDate, DateTime endDate) GetWeekRange(DateTime date)
|
||||
{
|
||||
var dayOfWeek = (int)date.DayOfWeek;
|
||||
var daysToMonday = dayOfWeek == 0 ? 6 : dayOfWeek - 1; // اگر یکشنبه باشد، 6 روز عقب برو
|
||||
// محاسبه تعداد روزهایی که باید عقب برویم تا به شنبه برسیم
|
||||
// شنبه = 6, یکشنبه = 0, دوشنبه = 1, ..., جمعه = 5
|
||||
var daysToSaturday = dayOfWeek == 6 ? 0 : (dayOfWeek + 1) % 7;
|
||||
|
||||
var startDate = date.Date.AddDays(-daysToMonday);
|
||||
var endDate = startDate.AddDays(6);
|
||||
var startDate = date.Date.AddDays(-daysToSaturday);
|
||||
var endDate = startDate.AddDays(6).AddHours(23).AddMinutes(59).AddSeconds(59);
|
||||
|
||||
return (startDate, endDate);
|
||||
}
|
||||
|
||||
private static string ConvertWeekNumberToPersian(string gregorianWeekNumber, DateTime weekStartDate)
|
||||
{
|
||||
// ورودی: "2025-W48"
|
||||
var persianCalendar = new PersianCalendar();
|
||||
var persianYear = persianCalendar.GetYear(weekStartDate);
|
||||
|
||||
// محاسبه شماره هفته شمسی
|
||||
var jan1Persian = persianCalendar.ToDateTime(persianYear, 1, 1, 0, 0, 0, 0);
|
||||
|
||||
// پیدا کردن اولین شنبه سال شمسی
|
||||
var jan1DayOfWeek = (int)jan1Persian.DayOfWeek;
|
||||
var daysToFirstSaturday = jan1DayOfWeek == 6 ? 0 : (6 - jan1DayOfWeek + 7) % 7;
|
||||
var firstSaturday = jan1Persian.AddDays(daysToFirstSaturday);
|
||||
|
||||
// محاسبه شماره هفته
|
||||
int weekNum;
|
||||
if (weekStartDate < firstSaturday)
|
||||
{
|
||||
weekNum = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
var daysSinceFirstSaturday = (weekStartDate - firstSaturday).Days;
|
||||
weekNum = (daysSinceFirstSaturday / 7) + 1;
|
||||
}
|
||||
|
||||
return $"{persianYear}-W{weekNum:D2}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public class GetCommissionPayoutHistoryQueryHandler : IRequestHandler<GetCommiss
|
||||
query = query.Where(x => x.WeekNumber == request.WeekNumber);
|
||||
}
|
||||
|
||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "-Created");
|
||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "Created");
|
||||
|
||||
var meta = await query.GetMetaData(request.PaginationState, cancellationToken);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ public class GetUserCommissionPayoutsQueryHandler : IRequestHandler<GetUserCommi
|
||||
public async Task<GetUserCommissionPayoutsResponseDto> Handle(GetUserCommissionPayoutsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.UserCommissionPayouts
|
||||
.Include(x => x.User)
|
||||
.AsNoTracking()
|
||||
.AsQueryable();
|
||||
|
||||
@@ -31,7 +32,7 @@ public class GetUserCommissionPayoutsQueryHandler : IRequestHandler<GetUserCommi
|
||||
query = query.Where(x => x.WeekNumber == request.WeekNumber);
|
||||
}
|
||||
|
||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "-Created");
|
||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "Created");
|
||||
|
||||
var meta = await query.GetMetaData(request.PaginationState, cancellationToken);
|
||||
|
||||
@@ -41,6 +42,8 @@ public class GetUserCommissionPayoutsQueryHandler : IRequestHandler<GetUserCommi
|
||||
{
|
||||
Id = x.Id,
|
||||
UserId = x.UserId,
|
||||
FirstName = x.User.FirstName,
|
||||
LastName = x.User.LastName,
|
||||
WeekNumber = x.WeekNumber,
|
||||
WeeklyPoolId = x.WeeklyPoolId,
|
||||
BalancesEarned = x.BalancesEarned,
|
||||
|
||||
@@ -10,6 +10,8 @@ public class GetUserCommissionPayoutsResponseModel
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long UserId { get; set; }
|
||||
public string FirstName { get; set; } = string.Empty;
|
||||
public string LastName { get; set; } = string.Empty;
|
||||
public string WeekNumber { get; set; } = string.Empty;
|
||||
public long WeeklyPoolId { get; set; }
|
||||
public long BalancesEarned { get; set; }
|
||||
|
||||
@@ -31,7 +31,9 @@ public class GetUserWeeklyBalancesQueryHandler : IRequestHandler<GetUserWeeklyBa
|
||||
query = query.Where(x => !x.IsExpired);
|
||||
}
|
||||
|
||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "-WeekNumber");
|
||||
// نمیتوانیم "-WeekNumber" استفاده کنیم چون WeekNumber یک string است
|
||||
// از Created برای مرتبسازی استفاده میکنیم (جدیدترین هفتهها اول)
|
||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "Created");
|
||||
|
||||
var meta = await query.GetMetaData(request.PaginationState, cancellationToken);
|
||||
|
||||
|
||||
@@ -27,6 +27,6 @@ public class GetWeeklyCommissionPoolQueryHandler : IRequestHandler<GetWeeklyComm
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
return pool;
|
||||
return pool??new WeeklyCommissionPoolDto();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public class GetWithdrawalRequestsQueryHandler : IRequestHandler<GetWithdrawalRe
|
||||
query = query.Where(x => x.IbanNumber != null && x.IbanNumber.Contains(request.IbanNumber));
|
||||
}
|
||||
|
||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "-Created");
|
||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "Created");
|
||||
|
||||
var meta = await query.GetMetaData(request.PaginationState, cancellationToken);
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ public class GetConfigurationHistoryQueryHandler : IRequestHandler<GetConfigurat
|
||||
|
||||
var query = _context.SystemConfigurationHistories
|
||||
.Where(x => x.ConfigurationId == request.ConfigurationId)
|
||||
.ApplyOrder(sortBy: request.SortBy ?? "-Created") // پیشفرض: جدیدترین اول
|
||||
.ApplyOrder(sortBy: request.SortBy ?? "Created") // پیشفرض: جدیدترین اول
|
||||
.AsNoTracking()
|
||||
.AsQueryable();
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace CMSMicroservice.Application.ManualPaymentCQ.Commands.ProcessManualMembershipPayment;
|
||||
|
||||
public record ProcessManualMembershipPaymentCommand : IRequest<ProcessManualMembershipPaymentResponseDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// شناسه کاربر
|
||||
/// </summary>
|
||||
public long UserId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// مبلغ پرداختی (ریال)
|
||||
/// </summary>
|
||||
public long Amount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// شماره مرجع تراکنش
|
||||
/// </summary>
|
||||
public string ReferenceNumber { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// توضیحات (اختیاری)
|
||||
/// </summary>
|
||||
public string? Description { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
using CMSMicroservice.Application.Common.Exceptions;
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Entities.Payment;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.ManualPaymentCQ.Commands.ProcessManualMembershipPayment;
|
||||
|
||||
public class ProcessManualMembershipPaymentCommandHandler : IRequestHandler<ProcessManualMembershipPaymentCommand, ProcessManualMembershipPaymentResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly ICurrentUserService _currentUser;
|
||||
private readonly ILogger<ProcessManualMembershipPaymentCommandHandler> _logger;
|
||||
|
||||
public ProcessManualMembershipPaymentCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
ICurrentUserService currentUser,
|
||||
ILogger<ProcessManualMembershipPaymentCommandHandler> logger)
|
||||
{
|
||||
_context = context;
|
||||
_currentUser = currentUser;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<ProcessManualMembershipPaymentResponseDto> Handle(
|
||||
ProcessManualMembershipPaymentCommand request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"Processing manual membership payment for UserId: {UserId}, Amount: {Amount}",
|
||||
request.UserId, request.Amount);
|
||||
|
||||
// 1. دریافت شناسه ادمین از کاربر جاری
|
||||
var currentUserId = _currentUser.UserId;
|
||||
if (string.IsNullOrEmpty(currentUserId))
|
||||
{
|
||||
throw new UnauthorizedAccessException("کاربر احراز هویت نشده است");
|
||||
}
|
||||
|
||||
if (!long.TryParse(currentUserId, out var adminUserId))
|
||||
{
|
||||
throw new UnauthorizedAccessException("شناسه کاربر نامعتبر است");
|
||||
}
|
||||
|
||||
// 2. بررسی وجود کاربر
|
||||
var user = await _context.Users
|
||||
.FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogError("User not found: {UserId}", request.UserId);
|
||||
throw new NotFoundException($"کاربر با شناسه {request.UserId} یافت نشد");
|
||||
}
|
||||
|
||||
// 3. ایجاد ManualPayment با وضعیت Approved
|
||||
var manualPayment = new ManualPayment
|
||||
{
|
||||
UserId = request.UserId,
|
||||
Amount = request.Amount,
|
||||
Type = ManualPaymentType.CashDeposit,
|
||||
Description = request.Description ?? "پرداخت دستی عضویت",
|
||||
ReferenceNumber = request.ReferenceNumber,
|
||||
Status = ManualPaymentStatus.Approved,
|
||||
RequestedBy = adminUserId,
|
||||
ApprovedBy = adminUserId,
|
||||
ApprovedAt = DateTime.Now
|
||||
};
|
||||
|
||||
_context.ManualPayments.Add(manualPayment);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 4. پیدا کردن یا ایجاد کیف پول
|
||||
var wallet = await _context.UserWallets
|
||||
.FirstOrDefaultAsync(w => w.UserId == request.UserId, cancellationToken);
|
||||
|
||||
if (wallet == null)
|
||||
{
|
||||
_logger.LogError("Wallet not found for UserId: {UserId}", request.UserId);
|
||||
throw new NotFoundException($"کیف پول کاربر {request.UserId} یافت نشد");
|
||||
}
|
||||
|
||||
// 6. ثبت تراکنش
|
||||
var transaction = new Transaction
|
||||
{
|
||||
Amount = request.Amount,
|
||||
Description = $"پرداخت دستی عضویت - {manualPayment.Description} - مرجع: {request.ReferenceNumber}",
|
||||
PaymentStatus = PaymentStatus.Success,
|
||||
PaymentDate = DateTime.Now,
|
||||
RefId = request.ReferenceNumber,
|
||||
Type = TransactionType.DepositExternal1
|
||||
};
|
||||
|
||||
_context.Transactions.Add(transaction);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 7. اعمال تغییرات بر کیف پول
|
||||
var oldBalance = wallet.Balance;
|
||||
var oldDiscountBalance = wallet.DiscountBalance;
|
||||
|
||||
wallet.Balance += request.Amount;
|
||||
wallet.DiscountBalance += request.Amount;
|
||||
|
||||
// 8. ثبت لاگ Balance
|
||||
var balanceLog = new UserWalletChangeLog
|
||||
{
|
||||
WalletId = wallet.Id,
|
||||
CurrentBalance = wallet.Balance,
|
||||
ChangeValue = request.Amount,
|
||||
CurrentNetworkBalance = wallet.NetworkBalance,
|
||||
ChangeNerworkValue = 0,
|
||||
CurrentDiscountBalance = wallet.DiscountBalance,
|
||||
ChangeDiscountValue = request.Amount,
|
||||
IsIncrease = true,
|
||||
RefrenceId = transaction.Id
|
||||
};
|
||||
await _context.UserWalletChangeLogs.AddAsync(balanceLog, cancellationToken);
|
||||
|
||||
|
||||
// 10. بهروزرسانی ManualPayment با TransactionId
|
||||
manualPayment.TransactionId = transaction.Id;
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 11. پیدا کردن یا ایجاد آدرس پیشفرض کاربر
|
||||
var userAddress = await _context.UserAddresses
|
||||
.Where(a => a.UserId == request.UserId)
|
||||
.OrderByDescending(a => a.IsDefault)
|
||||
.ThenBy(a => a.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (userAddress == null)
|
||||
{
|
||||
userAddress = new UserAddress
|
||||
{
|
||||
UserId = request.UserId,
|
||||
Title = "آدرس پیشفرض",
|
||||
Address = "پرداخت دستی عضویت - آدرس موقت",
|
||||
PostalCode = "0000000000",
|
||||
IsDefault = true,
|
||||
CityId = 1
|
||||
};
|
||||
await _context.UserAddresses.AddAsync(userAddress, cancellationToken);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
// 12. ثبت سفارش
|
||||
var order = new UserOrder
|
||||
{
|
||||
UserId = request.UserId,
|
||||
Amount = request.Amount,
|
||||
TransactionId = transaction.Id,
|
||||
PaymentStatus = PaymentStatus.Success,
|
||||
PaymentDate = DateTime.Now,
|
||||
PaymentMethod = PaymentMethod.Deposit,
|
||||
DeliveryStatus = DeliveryStatus.None,
|
||||
UserAddressId = userAddress.Id,
|
||||
DeliveryDescription = $"پرداخت دستی عضویت - مرجع: {request.ReferenceNumber}"
|
||||
};
|
||||
|
||||
_context.UserOrders.Add(order);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Manual membership payment processed successfully. UserId: {UserId}, Amount: {Amount}, ManualPaymentId: {ManualPaymentId}, TransactionId: {TransactionId}, OrderId: {OrderId}, AdminUserId: {AdminUserId}",
|
||||
request.UserId, request.Amount, manualPayment.Id, transaction.Id, order.Id, adminUserId);
|
||||
|
||||
return new ProcessManualMembershipPaymentResponseDto
|
||||
{
|
||||
TransactionId = transaction.Id,
|
||||
OrderId = order.Id,
|
||||
NewWalletBalance = wallet.Balance,
|
||||
Message = "پرداخت دستی با موفقیت ثبت شد"
|
||||
};
|
||||
}
|
||||
catch (Exception ex) when (ex is not NotFoundException)
|
||||
{
|
||||
_logger.LogError(ex,
|
||||
"Error processing manual membership payment for UserId: {UserId}, Amount: {Amount}",
|
||||
request.UserId, request.Amount);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace CMSMicroservice.Application.ManualPaymentCQ.Commands.ProcessManualMembershipPayment;
|
||||
|
||||
public class ProcessManualMembershipPaymentCommandValidator : AbstractValidator<ProcessManualMembershipPaymentCommand>
|
||||
{
|
||||
public ProcessManualMembershipPaymentCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.UserId)
|
||||
.GreaterThan(0)
|
||||
.WithMessage("شناسه کاربر معتبر نیست");
|
||||
|
||||
RuleFor(x => x.Amount)
|
||||
.GreaterThan(0)
|
||||
.WithMessage("مبلغ باید بزرگتر از صفر باشد");
|
||||
|
||||
RuleFor(x => x.ReferenceNumber)
|
||||
.NotEmpty()
|
||||
.WithMessage("شماره مرجع الزامی است")
|
||||
.MaximumLength(50)
|
||||
.WithMessage("شماره مرجع نباید بیشتر از 50 کاراکتر باشد");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace CMSMicroservice.Application.ManualPaymentCQ.Commands.ProcessManualMembershipPayment;
|
||||
|
||||
public class ProcessManualMembershipPaymentResponseDto
|
||||
{
|
||||
/// <summary>
|
||||
/// شناسه تراکنش ثبت شده
|
||||
/// </summary>
|
||||
public long TransactionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// شناسه سفارش ثبت شده
|
||||
/// </summary>
|
||||
public long OrderId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// موجودی جدید کیف پول
|
||||
/// </summary>
|
||||
public long NewWalletBalance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// پیام موفقیت
|
||||
/// </summary>
|
||||
public string Message { get; set; } = "پرداخت دستی با موفقیت ثبت شد";
|
||||
}
|
||||
@@ -11,7 +11,7 @@ public record GetNetworkMembershipHistoryQuery : IRequest<GetNetworkMembershipHi
|
||||
public long? UserId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// مرتبسازی (پیشفرض: -Created)
|
||||
/// مرتبسازی (پیشفرض: Created)
|
||||
/// </summary>
|
||||
public string? SortBy { get; init; }
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ public class GetNetworkMembershipHistoryQueryHandler : IRequestHandler<GetNetwor
|
||||
query = query.Where(x => x.UserId == request.UserId.Value);
|
||||
}
|
||||
|
||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "-Created");
|
||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "Created");
|
||||
|
||||
var meta = await query.GetMetaData(request.PaginationState, cancellationToken);
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ public class GetNetworkTreeQueryValidator : AbstractValidator<GetNetworkTreeQuer
|
||||
.WithMessage("شناسه کاربر معتبر نیست");
|
||||
|
||||
RuleFor(x => x.MaxDepth)
|
||||
.InclusiveBetween(1, 10)
|
||||
.WithMessage("عمق درخت باید بین 1 تا 10 باشد");
|
||||
.InclusiveBetween(1, 20)
|
||||
.WithMessage("عمق درخت باید بین 1 تا 20 باشد");
|
||||
}
|
||||
|
||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||
|
||||
@@ -11,6 +11,7 @@ public class GetUserNetworkPositionQueryHandler : IRequestHandler<GetUserNetwork
|
||||
|
||||
public async Task<UserNetworkPositionDto?> Handle(GetUserNetworkPositionQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
// واکشی اطلاعات اصلی کاربر
|
||||
var user = await _context.Users
|
||||
.AsNoTracking()
|
||||
.Where(x => x.Id == request.UserId)
|
||||
@@ -20,8 +21,17 @@ public class GetUserNetworkPositionQueryHandler : IRequestHandler<GetUserNetwork
|
||||
x.Mobile,
|
||||
x.FirstName,
|
||||
x.LastName,
|
||||
x.Email,
|
||||
x.NationalCode,
|
||||
x.ReferralCode,
|
||||
x.IsMobileVerified,
|
||||
x.BirthDate,
|
||||
x.NetworkParentId,
|
||||
x.LegPosition
|
||||
x.LegPosition,
|
||||
x.HasReceivedDayaCredit,
|
||||
x.DayaCreditReceivedAt,
|
||||
x.PackagePurchaseMethod,
|
||||
x.Created
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
@@ -30,41 +40,196 @@ public class GetUserNetworkPositionQueryHandler : IRequestHandler<GetUserNetwork
|
||||
return null;
|
||||
}
|
||||
|
||||
// شمارش فرزندان
|
||||
var childrenCount = await _context.Users
|
||||
.CountAsync(x => x.NetworkParentId == request.UserId, cancellationToken);
|
||||
|
||||
var leftChildCount = await _context.Users
|
||||
.CountAsync(x => x.NetworkParentId == request.UserId && x.LegPosition == NetworkLeg.Left,
|
||||
cancellationToken);
|
||||
|
||||
var rightChildCount = await _context.Users
|
||||
.CountAsync(x => x.NetworkParentId == request.UserId && x.LegPosition == NetworkLeg.Right,
|
||||
cancellationToken);
|
||||
|
||||
// اطلاعات والد
|
||||
string? parentMobile = null;
|
||||
string? parentFullName = null;
|
||||
if (user.NetworkParentId.HasValue)
|
||||
{
|
||||
parentMobile = await _context.Users
|
||||
var parent = await _context.Users
|
||||
.Where(x => x.Id == user.NetworkParentId)
|
||||
.Select(x => x.Mobile)
|
||||
.Select(x => new { x.Mobile, x.FirstName, x.LastName })
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
parentMobile = parent.Mobile;
|
||||
parentFullName = $"{parent.FirstName} {parent.LastName}".Trim();
|
||||
}
|
||||
}
|
||||
|
||||
// شمارش فرزندان مستقیم
|
||||
var directChildren = await _context.Users
|
||||
.Where(x => x.NetworkParentId == request.UserId)
|
||||
.Select(x => new { x.Id, x.LegPosition, x.FirstName, x.LastName, x.Mobile, x.Created })
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var leftChild = directChildren.FirstOrDefault(x => x.LegPosition == NetworkLeg.Left);
|
||||
var rightChild = directChildren.FirstOrDefault(x => x.LegPosition == NetworkLeg.Right);
|
||||
|
||||
// محاسبه تعداد کل اعضای هر شاخه (با استفاده از CTE برای عملکرد بهتر)
|
||||
var leftLegCount = await GetLegMemberCountAsync(request.UserId, NetworkLeg.Left, cancellationToken);
|
||||
var rightLegCount = await GetLegMemberCountAsync(request.UserId, NetworkLeg.Right, cancellationToken);
|
||||
var totalNetworkSize = leftLegCount + rightLegCount;
|
||||
|
||||
// محاسبه حداکثر عمق شبکه
|
||||
var maxDepth = await GetMaxNetworkDepthAsync(request.UserId, cancellationToken);
|
||||
|
||||
// آمار اعضای فعال/غیرفعال (کسانی که پکیج خریدهاند)
|
||||
var allDescendantIds = await GetAllDescendantIdsAsync(request.UserId, cancellationToken);
|
||||
var activeCount = await _context.Users
|
||||
.CountAsync(x => allDescendantIds.Contains(x.Id) &&
|
||||
x.PackagePurchaseMethod != PackagePurchaseMethod.None,
|
||||
cancellationToken);
|
||||
var inactiveCount = allDescendantIds.Count - activeCount;
|
||||
|
||||
// آمار کمیسیونهای کاربر
|
||||
var commissionStats = await _context.UserCommissionPayouts
|
||||
.Where(x => x.UserId == request.UserId)
|
||||
.GroupBy(x => x.UserId)
|
||||
.Select(g => new
|
||||
{
|
||||
TotalBalances = g.Sum(x => x.BalancesEarned),
|
||||
TotalAmount = g.Sum(x => x.TotalAmount),
|
||||
PaidAmount = g.Where(x => x.Status == CommissionPayoutStatus.Paid).Sum(x => x.TotalAmount),
|
||||
PendingAmount = g.Where(x => x.Status == CommissionPayoutStatus.Pending).Sum(x => x.TotalAmount)
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
return new UserNetworkPositionDto
|
||||
{
|
||||
// اطلاعات اصلی
|
||||
UserId = user.Id,
|
||||
Mobile = user.Mobile,
|
||||
FirstName = user.FirstName,
|
||||
LastName = user.LastName,
|
||||
Email = user.Email,
|
||||
NationalCode = user.NationalCode,
|
||||
ReferralCode = user.ReferralCode,
|
||||
IsMobileVerified = user.IsMobileVerified,
|
||||
BirthDate = user.BirthDate,
|
||||
JoinedAt = user.Created,
|
||||
|
||||
// اطلاعات شبکه
|
||||
NetworkParentId = user.NetworkParentId,
|
||||
ParentMobile = parentMobile,
|
||||
ParentFullName = parentFullName,
|
||||
LegPosition = user.LegPosition,
|
||||
TotalChildren = childrenCount,
|
||||
LeftChildCount = leftChildCount,
|
||||
RightChildCount = rightChildCount,
|
||||
IsInNetwork = user.NetworkParentId.HasValue
|
||||
IsInNetwork = user.NetworkParentId.HasValue,
|
||||
|
||||
// آمار فرزندان مستقیم
|
||||
TotalChildren = directChildren.Count,
|
||||
LeftChildCount = leftChild != null ? 1 : 0,
|
||||
RightChildCount = rightChild != null ? 1 : 0,
|
||||
|
||||
// اطلاعات فرزند چپ
|
||||
LeftChildId = leftChild?.Id,
|
||||
LeftChildFullName = leftChild != null ? $"{leftChild.FirstName} {leftChild.LastName}".Trim() : null,
|
||||
LeftChildMobile = leftChild?.Mobile,
|
||||
LeftChildJoinedAt = leftChild?.Created,
|
||||
|
||||
// اطلاعات فرزند راست
|
||||
RightChildId = rightChild?.Id,
|
||||
RightChildFullName = rightChild != null ? $"{rightChild.FirstName} {rightChild.LastName}".Trim() : null,
|
||||
RightChildMobile = rightChild?.Mobile,
|
||||
RightChildJoinedAt = rightChild?.Created,
|
||||
|
||||
// آمار کل شبکه
|
||||
TotalLeftLegMembers = leftLegCount,
|
||||
TotalRightLegMembers = rightLegCount,
|
||||
TotalNetworkSize = totalNetworkSize,
|
||||
MaxNetworkDepth = maxDepth,
|
||||
|
||||
// اطلاعات پکیج و دایا
|
||||
HasReceivedDayaCredit = user.HasReceivedDayaCredit,
|
||||
DayaCreditReceivedAt = user.DayaCreditReceivedAt,
|
||||
PackagePurchaseMethod = user.PackagePurchaseMethod,
|
||||
HasPurchasedGoldenPackage = user.PackagePurchaseMethod != PackagePurchaseMethod.None,
|
||||
|
||||
// آمار مالی
|
||||
TotalEarnedCommission = commissionStats?.TotalAmount ?? 0,
|
||||
TotalPaidCommission = commissionStats?.PaidAmount ?? 0,
|
||||
PendingCommission = commissionStats?.PendingAmount ?? 0,
|
||||
TotalBalancesEarned = commissionStats?.TotalBalances ?? 0,
|
||||
|
||||
// آمار فعالیت
|
||||
ActiveMembersInNetwork = activeCount,
|
||||
InactiveMembersInNetwork = inactiveCount
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// محاسبه تعداد اعضای یک شاخه (چپ یا راست) به صورت بازگشتی
|
||||
/// </summary>
|
||||
private async Task<int> GetLegMemberCountAsync(long userId, NetworkLeg leg, CancellationToken cancellationToken)
|
||||
{
|
||||
var directChild = await _context.Users
|
||||
.Where(x => x.NetworkParentId == userId && x.LegPosition == leg)
|
||||
.Select(x => x.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (directChild == 0)
|
||||
return 0;
|
||||
|
||||
// تعداد کل زیرمجموعه این فرزند + خود فرزند
|
||||
var descendants = await GetAllDescendantIdsAsync(directChild, cancellationToken);
|
||||
return descendants.Count + 1; // +1 برای خود فرزند
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// محاسبه حداکثر عمق شبکه
|
||||
/// </summary>
|
||||
private async Task<int> GetMaxNetworkDepthAsync(long userId, CancellationToken cancellationToken)
|
||||
{
|
||||
var maxDepth = 0;
|
||||
var currentLevelIds = new List<long> { userId };
|
||||
|
||||
while (currentLevelIds.Any())
|
||||
{
|
||||
var nextLevelIds = await _context.Users
|
||||
.Where(x => currentLevelIds.Contains(x.NetworkParentId ?? 0))
|
||||
.Select(x => x.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (nextLevelIds.Any())
|
||||
{
|
||||
maxDepth++;
|
||||
currentLevelIds = nextLevelIds;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return maxDepth;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت تمام ID های زیرمجموعه یک کاربر
|
||||
/// </summary>
|
||||
private async Task<List<long>> GetAllDescendantIdsAsync(long userId, CancellationToken cancellationToken)
|
||||
{
|
||||
var allDescendants = new List<long>();
|
||||
var currentLevelIds = new List<long> { userId };
|
||||
|
||||
while (currentLevelIds.Any())
|
||||
{
|
||||
var children = await _context.Users
|
||||
.Where(x => currentLevelIds.Contains(x.NetworkParentId ?? 0))
|
||||
.Select(x => x.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (children.Any())
|
||||
{
|
||||
allDescendants.AddRange(children);
|
||||
currentLevelIds = children;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return allDescendants;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,62 @@ namespace CMSMicroservice.Application.NetworkMembershipCQ.Queries.GetUserNetwork
|
||||
/// </summary>
|
||||
public class UserNetworkPositionDto
|
||||
{
|
||||
// اطلاعات اصلی کاربر
|
||||
public long UserId { get; set; }
|
||||
public string? Mobile { get; set; }
|
||||
public string? FirstName { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? NationalCode { get; set; }
|
||||
public string ReferralCode { get; set; } = string.Empty;
|
||||
public bool IsMobileVerified { get; set; }
|
||||
public DateTime? BirthDate { get; set; }
|
||||
public DateTime JoinedAt { get; set; }
|
||||
|
||||
// اطلاعات شبکه
|
||||
public long? NetworkParentId { get; set; }
|
||||
public string? ParentMobile { get; set; }
|
||||
public string? ParentFullName { get; set; }
|
||||
public NetworkLeg? LegPosition { get; set; }
|
||||
public bool IsInNetwork { get; set; }
|
||||
|
||||
// آمار فرزندان مستقیم
|
||||
public int TotalChildren { get; set; }
|
||||
public int LeftChildCount { get; set; }
|
||||
public int RightChildCount { get; set; }
|
||||
public bool IsInNetwork { get; set; }
|
||||
|
||||
// آمار کل زیرمجموعه (تمام سطوح)
|
||||
public int TotalLeftLegMembers { get; set; }
|
||||
public int TotalRightLegMembers { get; set; }
|
||||
public int TotalNetworkSize { get; set; }
|
||||
|
||||
// اطلاعات فرزندان مستقیم
|
||||
public long? LeftChildId { get; set; }
|
||||
public string? LeftChildFullName { get; set; }
|
||||
public string? LeftChildMobile { get; set; }
|
||||
public DateTime? LeftChildJoinedAt { get; set; }
|
||||
|
||||
public long? RightChildId { get; set; }
|
||||
public string? RightChildFullName { get; set; }
|
||||
public string? RightChildMobile { get; set; }
|
||||
public DateTime? RightChildJoinedAt { get; set; }
|
||||
|
||||
// اطلاعات پکیج و دایا
|
||||
public bool HasReceivedDayaCredit { get; set; }
|
||||
public DateTime? DayaCreditReceivedAt { get; set; }
|
||||
public PackagePurchaseMethod PackagePurchaseMethod { get; set; }
|
||||
public bool HasPurchasedGoldenPackage { get; set; }
|
||||
|
||||
// آمار مالی
|
||||
public decimal TotalEarnedCommission { get; set; }
|
||||
public decimal TotalPaidCommission { get; set; }
|
||||
public decimal PendingCommission { get; set; }
|
||||
public int TotalBalancesEarned { get; set; }
|
||||
|
||||
// عمق شبکه
|
||||
public int MaxNetworkDepth { get; set; }
|
||||
|
||||
// آمار فعالیت
|
||||
public int ActiveMembersInNetwork { get; set; }
|
||||
public int InactiveMembersInNetwork { get; set; }
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ public record GetAllUserByFilterQuery : IRequest<GetAllUserByFilterResponseDto>
|
||||
|
||||
}public class GetAllUserByFilterFilter
|
||||
{
|
||||
//جستجوی متنی (نام، نام خانوادگی، موبایل، کدملی)
|
||||
public string? SearchText { get; set; }
|
||||
//شناسه
|
||||
public long? Id { get; set; }
|
||||
//نام
|
||||
|
||||
@@ -17,6 +17,11 @@ public class GetAllUserByFilterQueryHandler : IRequestHandler<GetAllUserByFilter
|
||||
if (request.Filter is not null)
|
||||
{
|
||||
query = query
|
||||
.Where(x => request.Filter.SearchText == null ||
|
||||
x.FirstName.Contains(request.Filter.SearchText) ||
|
||||
x.LastName.Contains(request.Filter.SearchText) ||
|
||||
x.Mobile.Contains(request.Filter.SearchText) ||
|
||||
x.NationalCode.Contains(request.Filter.SearchText))
|
||||
.Where(x => request.Filter.Id == null || x.Id == request.Filter.Id)
|
||||
.Where(x => request.Filter.FirstName == null || x.FirstName.Contains(request.Filter.FirstName))
|
||||
.Where(x => request.Filter.LastName == null || x.LastName.Contains(request.Filter.LastName))
|
||||
|
||||
@@ -4,4 +4,5 @@ public enum PaymentMethod
|
||||
{
|
||||
IPG = 0,
|
||||
Wallet = 1,
|
||||
Deposit = 2,
|
||||
}
|
||||
|
||||
@@ -236,8 +236,8 @@ public class WeeklyCommissionJob
|
||||
var calendar = System.Globalization.CultureInfo.InvariantCulture.Calendar;
|
||||
var weekNumber = calendar.GetWeekOfYear(
|
||||
date,
|
||||
System.Globalization.CalendarWeekRule.FirstFourDayWeek,
|
||||
DayOfWeek.Monday);
|
||||
System.Globalization.CalendarWeekRule.FirstDay,
|
||||
DayOfWeek.Saturday);
|
||||
|
||||
var year = date.Year;
|
||||
if (weekNumber >= 52 && date.Month == 1)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>0.0.146</Version>
|
||||
<Version>0.0.152</Version>
|
||||
<DebugType>None</DebugType>
|
||||
<DebugSymbols>False</DebugSymbols>
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
|
||||
@@ -209,18 +209,20 @@ message GetUserCommissionPayoutsResponse
|
||||
|
||||
message UserCommissionPayoutModel
|
||||
{
|
||||
int64 id = 1;
|
||||
int64 user_id = 2;
|
||||
string user_name = 3;
|
||||
string week_number = 4;
|
||||
int32 balances_earned = 5;
|
||||
int64 value_per_balance = 6;
|
||||
int64 total_amount = 7;
|
||||
int32 status = 8; // CommissionPayoutStatus enum
|
||||
google.protobuf.Int32Value withdrawal_method = 9;
|
||||
string iban_number = 10;
|
||||
google.protobuf.Timestamp created = 11;
|
||||
google.protobuf.Timestamp last_modified = 12;
|
||||
int64 id = 1;
|
||||
int64 user_id = 2;
|
||||
string user_name = 3;
|
||||
string week_number = 4;
|
||||
int64 weekly_pool_id = 5;
|
||||
int64 balances_earned = 6;
|
||||
int64 value_per_balance = 7;
|
||||
int64 total_amount = 8;
|
||||
int32 status = 9; // CommissionPayoutStatus enum
|
||||
google.protobuf.Timestamp paid_at = 10;
|
||||
google.protobuf.Int32Value withdrawal_method = 11;
|
||||
string iban_number = 12;
|
||||
google.protobuf.Timestamp withdrawn_at = 13;
|
||||
google.protobuf.Timestamp created = 14;
|
||||
}
|
||||
|
||||
// GetCommissionPayoutHistory Query
|
||||
|
||||
@@ -38,6 +38,13 @@ service ManualPaymentContract
|
||||
get: "/GetAllManualPayments"
|
||||
};
|
||||
};
|
||||
|
||||
rpc ProcessManualMembershipPayment(ProcessManualMembershipPaymentRequest) returns (ProcessManualMembershipPaymentResponse){
|
||||
option (google.api.http) = {
|
||||
post: "/ProcessManualMembershipPayment"
|
||||
body: "*"
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Enums mirroring CMSMicroservice.Domain.Enums.ManualPaymentType
|
||||
@@ -128,3 +135,19 @@ message ManualPaymentModel
|
||||
google.protobuf.Timestamp created = 19;
|
||||
}
|
||||
|
||||
message ProcessManualMembershipPaymentRequest
|
||||
{
|
||||
int64 user_id = 1;
|
||||
int64 amount = 2;
|
||||
string reference_number = 3;
|
||||
google.protobuf.StringValue description = 4;
|
||||
}
|
||||
|
||||
message ProcessManualMembershipPaymentResponse
|
||||
{
|
||||
int64 transaction_id = 1;
|
||||
int64 order_id = 2;
|
||||
int64 new_wallet_balance = 3;
|
||||
string message = 4;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,20 +85,68 @@ message GetUserNetworkRequest
|
||||
|
||||
message GetUserNetworkResponse
|
||||
{
|
||||
// اطلاعات اصلی کاربر
|
||||
int64 id = 1;
|
||||
int64 user_id = 2;
|
||||
string user_name = 3;
|
||||
google.protobuf.Int64Value parent_id = 4;
|
||||
string parent_name = 5;
|
||||
int32 network_leg = 6; // NetworkLeg enum
|
||||
google.protobuf.Int64Value left_child_id = 7;
|
||||
string left_child_name = 8;
|
||||
google.protobuf.Int64Value right_child_id = 9;
|
||||
string right_child_name = 10;
|
||||
int32 network_level = 11;
|
||||
string referral_code = 12;
|
||||
google.protobuf.Timestamp joined_at = 13;
|
||||
google.protobuf.Timestamp created = 14;
|
||||
string mobile = 4;
|
||||
string email = 5;
|
||||
string national_code = 6;
|
||||
string referral_code = 7;
|
||||
bool is_mobile_verified = 8;
|
||||
google.protobuf.Timestamp birth_date = 9;
|
||||
google.protobuf.Timestamp joined_at = 10;
|
||||
|
||||
// اطلاعات والد
|
||||
google.protobuf.Int64Value parent_id = 11;
|
||||
string parent_name = 12;
|
||||
string parent_mobile = 13;
|
||||
|
||||
// موقعیت در شبکه
|
||||
int32 network_leg = 14; // NetworkLeg enum
|
||||
int32 network_level = 15;
|
||||
bool is_in_network = 16;
|
||||
|
||||
// اطلاعات فرزند چپ
|
||||
google.protobuf.Int64Value left_child_id = 17;
|
||||
string left_child_name = 18;
|
||||
string left_child_mobile = 19;
|
||||
google.protobuf.Timestamp left_child_joined_at = 20;
|
||||
|
||||
// اطلاعات فرزند راست
|
||||
google.protobuf.Int64Value right_child_id = 21;
|
||||
string right_child_name = 22;
|
||||
string right_child_mobile = 23;
|
||||
google.protobuf.Timestamp right_child_joined_at = 24;
|
||||
|
||||
// آمار فرزندان مستقیم
|
||||
int32 total_children = 25;
|
||||
int32 left_child_count = 26;
|
||||
int32 right_child_count = 27;
|
||||
|
||||
// آمار کل شبکه
|
||||
int32 total_left_leg_members = 28;
|
||||
int32 total_right_leg_members = 29;
|
||||
int32 total_network_size = 30;
|
||||
int32 max_network_depth = 31;
|
||||
|
||||
// اطلاعات پکیج و دایا
|
||||
bool has_received_daya_credit = 32;
|
||||
google.protobuf.Timestamp daya_credit_received_at = 33;
|
||||
int32 package_purchase_method = 34;
|
||||
bool has_purchased_golden_package = 35;
|
||||
|
||||
// آمار مالی
|
||||
double total_earned_commission = 36;
|
||||
double total_paid_commission = 37;
|
||||
double pending_commission = 38;
|
||||
int32 total_balances_earned = 39;
|
||||
|
||||
// آمار فعالیت
|
||||
int32 active_members_in_network = 40;
|
||||
int32 inactive_members_in_network = 41;
|
||||
|
||||
google.protobuf.Timestamp created = 42;
|
||||
}
|
||||
|
||||
// GetNetworkTree Query
|
||||
|
||||
@@ -129,20 +129,21 @@ message GetAllUserByFilterRequest
|
||||
}
|
||||
message GetAllUserByFilterFilter
|
||||
{
|
||||
google.protobuf.Int64Value id = 1;
|
||||
google.protobuf.StringValue first_name = 2;
|
||||
google.protobuf.StringValue last_name = 3;
|
||||
google.protobuf.StringValue mobile = 4;
|
||||
google.protobuf.StringValue national_code = 5;
|
||||
google.protobuf.StringValue avatar_path = 6;
|
||||
google.protobuf.Int64Value parent_id = 7;
|
||||
google.protobuf.StringValue referral_code = 8;
|
||||
google.protobuf.BoolValue is_mobile_verified = 9;
|
||||
google.protobuf.Timestamp mobile_verified_at = 10;
|
||||
google.protobuf.BoolValue email_notifications = 11;
|
||||
google.protobuf.BoolValue sms_notifications = 12;
|
||||
google.protobuf.BoolValue push_notifications = 13;
|
||||
google.protobuf.Timestamp birth_date = 14;
|
||||
google.protobuf.StringValue search_text = 1;
|
||||
google.protobuf.Int64Value id = 2;
|
||||
google.protobuf.StringValue first_name = 3;
|
||||
google.protobuf.StringValue last_name = 4;
|
||||
google.protobuf.StringValue mobile = 5;
|
||||
google.protobuf.StringValue national_code = 6;
|
||||
google.protobuf.StringValue avatar_path = 7;
|
||||
google.protobuf.Int64Value parent_id = 8;
|
||||
google.protobuf.StringValue referral_code = 9;
|
||||
google.protobuf.BoolValue is_mobile_verified = 10;
|
||||
google.protobuf.Timestamp mobile_verified_at = 11;
|
||||
google.protobuf.BoolValue email_notifications = 12;
|
||||
google.protobuf.BoolValue sms_notifications = 13;
|
||||
google.protobuf.BoolValue push_notifications = 14;
|
||||
google.protobuf.Timestamp birth_date = 15;
|
||||
}
|
||||
message GetAllUserByFilterResponse
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using CMSMicroservice.Application.CommissionCQ.Queries.GetAvailableWeeks;
|
||||
using CMSMicroservice.Application.CommissionCQ.Queries.GetUserCommissionPayouts;
|
||||
using CMSMicroservice.Protobuf.Protos.Commission;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using Mapster;
|
||||
@@ -30,9 +31,33 @@ public class CommissionProfile : IRegister
|
||||
.Map(dest => dest.CalculatedAt, src => src.CalculatedAt.HasValue
|
||||
? Timestamp.FromDateTime(src.CalculatedAt.Value.ToUniversalTime())
|
||||
: null)
|
||||
|
||||
.Map(dest => dest.LastExecutionStatus, src => src.LastExecutionStatus ?? string.Empty)
|
||||
.Map(dest => dest.TotalPoolAmount, src => src.TotalPoolAmount ?? 0)
|
||||
.Map(dest => dest.EligibleUsersCount, src => src.EligibleUsersCount ?? 0)
|
||||
.Map(dest => dest.DisplayText, src => src.DisplayText);
|
||||
|
||||
// GetUserCommissionPayouts Response Model Mapping
|
||||
config.NewConfig<GetUserCommissionPayoutsResponseModel, UserCommissionPayoutModel>()
|
||||
.Map(dest => dest.Id, src => src.Id)
|
||||
.Map(dest => dest.UserId, src => src.UserId)
|
||||
.Map(dest => dest.UserName, src => $"{src.FirstName} {src.LastName}")
|
||||
.Map(dest => dest.WeekNumber, src => src.WeekNumber)
|
||||
.Map(dest => dest.WeeklyPoolId, src => src.WeeklyPoolId)
|
||||
.Map(dest => dest.BalancesEarned, src => src.BalancesEarned)
|
||||
.Map(dest => dest.ValuePerBalance, src => src.ValuePerBalance)
|
||||
.Map(dest => dest.TotalAmount, src => src.TotalAmount)
|
||||
.Map(dest => dest.Status, src => (int)src.Status)
|
||||
.Map(dest => dest.PaidAt, src => src.PaidAt.HasValue
|
||||
? Timestamp.FromDateTime(src.PaidAt.Value.ToUniversalTime())
|
||||
: null)
|
||||
.Map(dest => dest.WithdrawalMethod, src => src.WithdrawalMethod.HasValue
|
||||
? (int)src.WithdrawalMethod.Value
|
||||
: (int?)null)
|
||||
.Map(dest => dest.IbanNumber, src => src.IbanNumber ?? string.Empty)
|
||||
.Map(dest => dest.WithdrawnAt, src => src.WithdrawnAt.HasValue
|
||||
? Timestamp.FromDateTime(src.WithdrawnAt.Value.ToUniversalTime())
|
||||
: null)
|
||||
.Map(dest => dest.Created, src => Timestamp.FromDateTimeOffset(src.Created));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using CMSMicroservice.Application.NetworkMembershipCQ.Queries.GetNetworkTree;
|
||||
using CMSMicroservice.Application.NetworkMembershipCQ.Queries.GetUserNetworkPosition;
|
||||
using CMSMicroservice.Protobuf.Protos.NetworkMembership;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
|
||||
@@ -15,7 +16,46 @@ public class NetworkMembershipProfile : IRegister
|
||||
|
||||
// Response mapping: تبدیل درخت به لیست مسطح
|
||||
config.NewConfig<NetworkTreeDto, GetNetworkTreeResponse>()
|
||||
.MapWith(src => ConvertTreeToResponse(src));
|
||||
.MapWith(src => ConvertTreeToResponse(src));
|
||||
|
||||
|
||||
config.NewConfig<UserNetworkPositionDto, GetUserNetworkResponse>()
|
||||
.Map(dest => dest.Id, src => src.UserId)
|
||||
.Map(dest => dest.UserId, src => src.UserId)
|
||||
.Map(dest => dest.UserName, src => (src.FirstName + " " + src.LastName).Trim())
|
||||
.Map(dest => dest.Mobile, src => src.Mobile ?? "")
|
||||
.Map(dest => dest.Email, src => src.Email ?? "")
|
||||
.Map(dest => dest.NationalCode, src => src.NationalCode ?? "")
|
||||
.Map(dest => dest.ReferralCode, src => src.ReferralCode)
|
||||
.Map(dest => dest.IsMobileVerified, src => src.IsMobileVerified)
|
||||
.Map(dest => dest.ParentId, src => src.NetworkParentId)
|
||||
.Map(dest => dest.ParentName, src => src.ParentFullName ?? "")
|
||||
.Map(dest => dest.ParentMobile, src => src.ParentMobile ?? "")
|
||||
.Map(dest => dest.NetworkLeg, src => (int)(src.LegPosition ?? NetworkLeg.Left))
|
||||
.Map(dest => dest.NetworkLevel, src => 0) // Deprecated field
|
||||
.Map(dest => dest.IsInNetwork, src => src.IsInNetwork)
|
||||
.Map(dest => dest.LeftChildId, src => src.LeftChildId)
|
||||
.Map(dest => dest.LeftChildName, src => src.LeftChildFullName ?? "")
|
||||
.Map(dest => dest.LeftChildMobile, src => src.LeftChildMobile ?? "")
|
||||
.Map(dest => dest.RightChildId, src => src.RightChildId)
|
||||
.Map(dest => dest.RightChildName, src => src.RightChildFullName ?? "")
|
||||
.Map(dest => dest.RightChildMobile, src => src.RightChildMobile ?? "")
|
||||
.Map(dest => dest.TotalChildren, src => src.TotalChildren)
|
||||
.Map(dest => dest.LeftChildCount, src => src.LeftChildCount)
|
||||
.Map(dest => dest.RightChildCount, src => src.RightChildCount)
|
||||
.Map(dest => dest.TotalLeftLegMembers, src => src.TotalLeftLegMembers)
|
||||
.Map(dest => dest.TotalRightLegMembers, src => src.TotalRightLegMembers)
|
||||
.Map(dest => dest.TotalNetworkSize, src => src.TotalNetworkSize)
|
||||
.Map(dest => dest.MaxNetworkDepth, src => src.MaxNetworkDepth)
|
||||
.Map(dest => dest.HasReceivedDayaCredit, src => src.HasReceivedDayaCredit)
|
||||
.Map(dest => dest.PackagePurchaseMethod, src => (int)src.PackagePurchaseMethod)
|
||||
.Map(dest => dest.HasPurchasedGoldenPackage, src => src.HasPurchasedGoldenPackage)
|
||||
.Map(dest => dest.TotalEarnedCommission, src => (double)src.TotalEarnedCommission)
|
||||
.Map(dest => dest.TotalPaidCommission, src => (double)src.TotalPaidCommission)
|
||||
.Map(dest => dest.PendingCommission, src => (double)src.PendingCommission)
|
||||
.Map(dest => dest.TotalBalancesEarned, src => src.TotalBalancesEarned)
|
||||
.Map(dest => dest.ActiveMembersInNetwork, src => src.ActiveMembersInNetwork)
|
||||
.Map(dest => dest.InactiveMembersInNetwork, src => src.InactiveMembersInNetwork);
|
||||
}
|
||||
|
||||
private static GetNetworkTreeResponse ConvertTreeToResponse(NetworkTreeDto? treeDto)
|
||||
|
||||
@@ -3,6 +3,7 @@ using CMSMicroservice.WebApi.Common.Services;
|
||||
using CMSMicroservice.Application.ManualPaymentCQ.Commands.CreateManualPayment;
|
||||
using CMSMicroservice.Application.ManualPaymentCQ.Commands.ApproveManualPayment;
|
||||
using CMSMicroservice.Application.ManualPaymentCQ.Commands.RejectManualPayment;
|
||||
using CMSMicroservice.Application.ManualPaymentCQ.Commands.ProcessManualMembershipPayment;
|
||||
using CMSMicroservice.Application.ManualPaymentCQ.Queries.GetAllManualPayments;
|
||||
using Grpc.Core;
|
||||
using Mapster;
|
||||
@@ -56,4 +57,27 @@ public class ManualPaymentService : ManualPaymentContract.ManualPaymentContractB
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<GetAllManualPaymentsRequest, GetAllManualPaymentsQuery, GetAllManualPaymentsResponse>(request, context);
|
||||
}
|
||||
|
||||
public override async Task<ProcessManualMembershipPaymentResponse> ProcessManualMembershipPayment(
|
||||
ProcessManualMembershipPaymentRequest request,
|
||||
ServerCallContext context)
|
||||
{
|
||||
var command = new ProcessManualMembershipPaymentCommand
|
||||
{
|
||||
UserId = request.UserId,
|
||||
Amount = request.Amount,
|
||||
ReferenceNumber = request.ReferenceNumber,
|
||||
Description = request.Description
|
||||
};
|
||||
|
||||
var result = await _sender.Send(command, context.CancellationToken);
|
||||
|
||||
return new ProcessManualMembershipPaymentResponse
|
||||
{
|
||||
TransactionId = result.TransactionId,
|
||||
OrderId = result.OrderId,
|
||||
NewWalletBalance = result.NewWalletBalance,
|
||||
Message = result.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ public class DayaLoanCheckWorker
|
||||
recurringJobManager.AddOrUpdate<DayaLoanCheckWorker>(
|
||||
"daya-loan-check",
|
||||
worker => worker.ExecuteAsync(),
|
||||
"*/01 * * * *", // هر 15 دقیقه
|
||||
"*/20 * * * *", // هر 15 دقیقه
|
||||
TimeZoneInfo.Local
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user