feat: Implement Club Membership features including activation and retrieval of membership status

- Added command and handler for activating club membership with optional activation code and duration.
- Created response DTO for club membership activation.
- Implemented query and handler to retrieve current user's club membership status.
- Added necessary Protobuf service calls for club membership operations.
- Introduced new queries for retrieving network statistics and network tree structure.
- Enhanced commission queries to fetch user commission payouts and weekly balances.
- Updated application contract context to include new services for club and network memberships.
This commit is contained in:
masoodafar-web
2025-12-04 17:29:34 +03:30
parent bcf2bc2a52
commit 75e446f80f
28 changed files with 1036 additions and 32 deletions
@@ -0,0 +1,27 @@
namespace FrontOffice.BFF.Application.CommissionCQ.Queries.GetMyCommissionPayouts;
/// <summary>
/// دریافت لیست پرداخت‌های کمیسیون کاربر جاری
/// </summary>
public record GetMyCommissionPayoutsQuery : IRequest<GetMyCommissionPayoutsResponseDto>
{
/// <summary>
/// شماره هفته در فرمت ISO (مثال: "2025-W48"، null = همه)
/// </summary>
public string? WeekNumber { get; init; }
/// <summary>
/// وضعیت: 0=Pending, 1=Calculated, 2=Paid, 3=Withdrawn (null = همه)
/// </summary>
public int? Status { get; init; }
/// <summary>
/// شماره صفحه (1-based, internally converted to 0-based PageIndex)
/// </summary>
public int PageNumber { get; init; } = 1;
/// <summary>
/// تعداد در صفحه
/// </summary>
public int PageSize { get; init; } = 20;
}
@@ -0,0 +1,97 @@
using CMSMicroservice.Protobuf.Protos.Commission;
namespace FrontOffice.BFF.Application.CommissionCQ.Queries.GetMyCommissionPayouts;
public class GetMyCommissionPayoutsQueryHandler : IRequestHandler<GetMyCommissionPayoutsQuery, GetMyCommissionPayoutsResponseDto>
{
private readonly IApplicationContractContext _context;
private readonly ICurrentUserService _currentUserService;
public GetMyCommissionPayoutsQueryHandler(
IApplicationContractContext context,
ICurrentUserService currentUserService)
{
_context = context;
_currentUserService = currentUserService;
}
public async Task<GetMyCommissionPayoutsResponseDto> Handle(GetMyCommissionPayoutsQuery request, CancellationToken cancellationToken)
{
var userId = _currentUserService.UserId ?? throw new UnauthorizedAccessException("User not authenticated");
// Fixed: Use PageIndex (0-based) not PageNumber (1-based), and WeekNumber is string
var cmsRequest = new GetUserCommissionPayoutsRequest
{
UserId = userId, // long? type in proto - just assign directly
PageIndex = Math.Max(0, request.PageNumber - 1), // Convert 1-based to 0-based
PageSize = request.PageSize
};
// WeekNumber is string type in proto - assign directly
if (!string.IsNullOrEmpty(request.WeekNumber))
cmsRequest.WeekNumber = request.WeekNumber;
if (request.Status.HasValue)
cmsRequest.Status = request.Status.Value; // int? type in proto
var response = await _context.Commission.GetUserCommissionPayoutsAsync(cmsRequest, cancellationToken: cancellationToken);
var payouts = response.Models.Select(p => new CommissionPayoutDto
{
Id = p.Id,
WeekNumber = p.WeekNumber,
WeekLabel = $"هفته {p.WeekNumber}",
BalancesEarned = p.BalancesEarned,
TotalAmount = p.TotalAmount,
AmountFormatted = FormatCurrency(p.TotalAmount),
Status = MapStatus(p.Status),
StatusBadgeColor = GetStatusColor(p.Status),
CalculatedDate = p.Created?.ToDateTime() ?? DateTime.UtcNow,
DatePersian = FormatPersianDate(p.Created?.ToDateTime())
}).ToList();
return new GetMyCommissionPayoutsResponseDto
{
Payouts = payouts,
TotalCount = (int)response.MetaData.TotalCount,
PageNumber = request.PageNumber,
PageSize = request.PageSize
};
}
private static string MapStatus(int status)
{
return status switch
{
0 => "Pending",
1 => "Calculated",
2 => "Paid",
3 => "Withdrawn",
_ => "Unknown"
};
}
private static string GetStatusColor(int status)
{
return status switch
{
0 => "warning",
1 => "info",
2 => "success",
3 => "success",
_ => "default"
};
}
private static string FormatCurrency(long amount)
{
return $"{amount:N0} تومان";
}
private static string FormatPersianDate(DateTime? date)
{
if (!date.HasValue) return string.Empty;
// TODO: استفاده از PersianCalendar
return date.Value.ToString("yyyy/MM/dd");
}
}
@@ -0,0 +1,62 @@
namespace FrontOffice.BFF.Application.CommissionCQ.Queries.GetMyCommissionPayouts;
public class GetMyCommissionPayoutsResponseDto
{
public List<CommissionPayoutDto> Payouts { get; set; } = new();
public int TotalCount { get; set; }
public int PageNumber { get; set; }
public int PageSize { get; set; }
}
public class CommissionPayoutDto
{
/// <summary>
/// شناسه
/// </summary>
public long Id { get; set; }
/// <summary>
/// شماره هفته (e.g., "2024-W45")
/// </summary>
public string WeekNumber { get; set; } = string.Empty;
/// <summary>
/// لیبل هفته (هفته 45 - آذر 1403)
/// </summary>
public string WeekLabel { get; set; } = string.Empty;
/// <summary>
/// تعداد Balance کسب شده
/// </summary>
public int BalancesEarned { get; set; }
/// <summary>
/// مبلغ کل
/// </summary>
public long TotalAmount { get; set; }
/// <summary>
/// مبلغ فرمت شده
/// </summary>
public string AmountFormatted { get; set; } = string.Empty;
/// <summary>
/// وضعیت (Pending/Calculated/Paid/Withdrawn)
/// </summary>
public string Status { get; set; } = string.Empty;
/// <summary>
/// رنگ Badge
/// </summary>
public string StatusBadgeColor { get; set; } = string.Empty;
/// <summary>
/// تاریخ محاسبه
/// </summary>
public DateTime CalculatedDate { get; set; }
/// <summary>
/// تاریخ شمسی
/// </summary>
public string DatePersian { get; set; } = string.Empty;
}