Compare commits

..

14 Commits

Author SHA1 Message Date
masoodafar-web
6ae1b0cd70 feat: update commission payout model with user names and timestamps
All checks were successful
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 4m11s
2025-12-12 19:31:32 +03:30
masoodafar-web
2ae6034fbb refactor: remove admin user id from manual payment command
All checks were successful
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 2m13s
2025-12-12 10:22:09 +03:30
masoodafar-web
094846ce8b feat: add manual membership payment processing with wallet and order management
All checks were successful
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 7m8s
2025-12-12 10:07:14 +03:30
masoodafar-web
b19cf5e32e fix: use Gregorian week number for API and Persian for display
All checks were successful
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 2m18s
2025-12-12 09:39:20 +03:30
masoodafar-web
b1c3fcfd66 feat: add Persian calendar support for week numbers and dates
All checks were successful
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 6m35s
2025-12-12 09:18:31 +03:30
masoodafar-web
1f6c5a1f45 fix: update default sorting from descending to ascending Created
All checks were successful
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 2m43s
2025-12-12 07:49:54 +03:30
masoodafar-web
99b217d5b5 fix: change weekly balance sorting from WeekNumber to Created
All checks were successful
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 2m39s
2025-12-12 07:32:22 +03:30
masoodafar-web
8ee3fe6f7b fix: update week calculation to start from Saturday
All checks were successful
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 2m2s
2025-12-12 07:07:28 +03:30
masoodafar-web
3c7ac68eeb feat: enhance network membership response with detailed stats and hierarchy
All checks were successful
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 2m14s
2025-12-12 06:07:23 +03:30
masoodafar-web
f27418cff4 fix: adjust network tree depth limit and update commission mapping
All checks were successful
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 2m5s
2025-12-12 05:42:19 +03:30
masoodafar-web
aba534e07c fix: update week calculation to use Saturday as the start of the week
All checks were successful
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 2m12s
2025-12-12 04:37:34 +03:30
masoodafar-web
af3a29ed27 fix: adjust week calculation to FirstDay and Saturday
All checks were successful
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 3m9s
2025-12-12 04:23:14 +03:30
masoodafar-web
db951699f8 fix: handle null pool and adjust week calculation rules
Some checks failed
Build and Deploy to Kubernetes / build-and-deploy (push) Has been cancelled
2025-12-12 04:22:16 +03:30
masoodafar-web
bb6b7c709c feat: add search text filter for user query
All checks were successful
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 2m40s
2025-12-12 03:18:18 +03:30
32 changed files with 795 additions and 88 deletions

View File

@@ -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);

View File

@@ -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);

View File

@@ -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}";
}
}

View File

@@ -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);

View File

@@ -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,

View File

@@ -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; }

View File

@@ -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);

View File

@@ -27,6 +27,6 @@ public class GetWeeklyCommissionPoolQueryHandler : IRequestHandler<GetWeeklyComm
})
.FirstOrDefaultAsync(cancellationToken);
return pool;
return pool??new WeeklyCommissionPoolDto();
}
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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; }
}

View File

@@ -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;
}
}
}

View File

@@ -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 کاراکتر باشد");
}
}

View File

@@ -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; } = "پرداخت دستی با موفقیت ثبت شد";
}

View File

@@ -11,7 +11,7 @@ public record GetNetworkMembershipHistoryQuery : IRequest<GetNetworkMembershipHi
public long? UserId { get; init; }
/// <summary>
/// مرتب‌سازی (پیش‌فرض: -Created)
/// مرتب‌سازی (پیش‌فرض: Created)
/// </summary>
public string? SortBy { get; init; }

View File

@@ -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);

View File

@@ -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) =>

View File

@@ -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;
}
}

View File

@@ -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; }
}

View File

@@ -10,6 +10,8 @@ public record GetAllUserByFilterQuery : IRequest<GetAllUserByFilterResponseDto>
}public class GetAllUserByFilterFilter
{
//جستجوی متنی (نام، نام خانوادگی، موبایل، کدملی)
public string? SearchText { get; set; }
//شناسه
public long? Id { get; set; }
//نام

View File

@@ -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))

View File

@@ -4,4 +4,5 @@ public enum PaymentMethod
{
IPG = 0,
Wallet = 1,
Deposit = 2,
}

View File

@@ -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)

View File

@@ -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>

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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
{

View File

@@ -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));
}
}

View File

@@ -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)

View File

@@ -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
};
}
}

View File

@@ -114,7 +114,7 @@ public class DayaLoanCheckWorker
recurringJobManager.AddOrUpdate<DayaLoanCheckWorker>(
"daya-loan-check",
worker => worker.ExecuteAsync(),
"*/01 * * * *", // هر 15 دقیقه
"*/20 * * * *", // هر 15 دقیقه
TimeZoneInfo.Local
);
}