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,22 @@
namespace FrontOffice.BFF.Application.ClubMembershipCQ.Commands.ActivateMyClubMembership;
/// <summary>
/// فعال‌سازی عضویت باشگاه کاربر جاری (پرداخت 56M)
/// </summary>
public record ActivateMyClubMembershipCommand : IRequest<ActivateMyClubMembershipResponseDto>
{
/// <summary>
/// شناسه پکیج (PackageId)
/// </summary>
public long PackageId { get; init; }
/// <summary>
/// کد فعال‌سازی (اختیاری)
/// </summary>
public string? ActivationCode { get; init; }
/// <summary>
/// مدت زمان عضویت به ماه (پیش‌فرض: 12)
/// </summary>
public int DurationMonths { get; init; } = 12;
}
@@ -0,0 +1,51 @@
using CMSMicroservice.Protobuf.Protos.ClubMembership;
namespace FrontOffice.BFF.Application.ClubMembershipCQ.Commands.ActivateMyClubMembership;
public class ActivateMyClubMembershipCommandHandler : IRequestHandler<ActivateMyClubMembershipCommand, ActivateMyClubMembershipResponseDto>
{
private readonly IApplicationContractContext _context;
private readonly ICurrentUserService _currentUserService;
public ActivateMyClubMembershipCommandHandler(
IApplicationContractContext context,
ICurrentUserService currentUserService)
{
_context = context;
_currentUserService = currentUserService;
}
public async Task<ActivateMyClubMembershipResponseDto> Handle(ActivateMyClubMembershipCommand request, CancellationToken cancellationToken)
{
var userId = _currentUserService.UserId ?? throw new UnauthorizedAccessException("User not authenticated");
var grpcRequest = new ActivateClubMembershipRequest
{
UserId = userId,
PackageId = request.PackageId,
DurationMonths = request.DurationMonths
};
// اگر کد فعال‌سازی ارسال شده، اضافه کن
if (!string.IsNullOrEmpty(request.ActivationCode))
{
grpcRequest.ActivationCode = request.ActivationCode;
}
await _context.ClubMemberships.ActivateClubMembershipAsync(
grpcRequest,
cancellationToken: cancellationToken);
var activationDate = DateTime.UtcNow;
var expirationDate = activationDate.AddMonths(request.DurationMonths);
return new ActivateMyClubMembershipResponseDto
{
Success = true,
Message = "عضویت باشگاه با موفقیت فعال شد",
ActivationDate = activationDate,
ExpirationDate = expirationDate,
AmountPaid = 56_000_000 // 56M تومان
};
}
}
@@ -0,0 +1,29 @@
namespace FrontOffice.BFF.Application.ClubMembershipCQ.Commands.ActivateMyClubMembership;
public class ActivateMyClubMembershipResponseDto
{
/// <summary>
/// آیا فعال‌سازی موفق بود؟
/// </summary>
public bool Success { get; set; }
/// <summary>
/// پیام نتیجه
/// </summary>
public string Message { get; set; } = string.Empty;
/// <summary>
/// تاریخ فعال‌سازی
/// </summary>
public DateTime ActivationDate { get; set; }
/// <summary>
/// تاریخ انقضا
/// </summary>
public DateTime ExpirationDate { get; set; }
/// <summary>
/// مبلغ پرداخت شده
/// </summary>
public long AmountPaid { get; set; }
}
@@ -0,0 +1,6 @@
namespace FrontOffice.BFF.Application.ClubMembershipCQ.Queries.GetMyClubMembership;
/// <summary>
/// دریافت وضعیت عضویت باشگاه کاربر جاری
/// </summary>
public record GetMyClubMembershipQuery : IRequest<GetMyClubMembershipResponseDto>;
@@ -0,0 +1,65 @@
using CMSMicroservice.Protobuf.Protos.ClubMembership;
namespace FrontOffice.BFF.Application.ClubMembershipCQ.Queries.GetMyClubMembership;
public class GetMyClubMembershipQueryHandler : IRequestHandler<GetMyClubMembershipQuery, GetMyClubMembershipResponseDto>
{
private readonly IApplicationContractContext _context;
private readonly ICurrentUserService _currentUserService;
public GetMyClubMembershipQueryHandler(
IApplicationContractContext context,
ICurrentUserService currentUserService)
{
_context = context;
_currentUserService = currentUserService;
}
public async Task<GetMyClubMembershipResponseDto> Handle(GetMyClubMembershipQuery request, CancellationToken cancellationToken)
{
var userId = _currentUserService.UserId ?? throw new UnauthorizedAccessException("User not authenticated");
var cmsRequest = new GetClubMembershipRequest
{
UserId = userId
};
var response = await _context.ClubMemberships.GetClubMembershipAsync(cmsRequest, cancellationToken: cancellationToken);
// Fixed: Use ActivatedAt and ExpiresAt from proto (not ActivationDate/ExpirationDate)
var activationDate = response.ActivatedAt?.ToDateTime();
var expirationDate = response.ExpiresAt?.ToDateTime();
var daysRemaining = expirationDate.HasValue
? Math.Max(0, (int)(expirationDate.Value - DateTime.UtcNow).TotalDays)
: 0;
return new GetMyClubMembershipResponseDto
{
UserId = response.UserId,
IsActive = response.IsActive,
ActivationDate = activationDate,
ExpirationDate = expirationDate,
PackageId = response.PackageId,
PackageName = response.PackageName,
ActivationCode = response.ActivationCode,
Status = DetermineStatus(response.IsActive, expirationDate),
DaysRemaining = daysRemaining,
IsTrialPeriod = !response.IsActive && daysRemaining > 0
};
}
private static string DetermineStatus(bool isActive, DateTime? expirationDate)
{
if (!isActive && !expirationDate.HasValue)
return "Inactive";
if (!isActive && expirationDate.HasValue && expirationDate.Value < DateTime.UtcNow)
return "Expired";
if (!isActive)
return "Trial";
return "Active";
}
}
@@ -0,0 +1,54 @@
namespace FrontOffice.BFF.Application.ClubMembershipCQ.Queries.GetMyClubMembership;
public class GetMyClubMembershipResponseDto
{
/// <summary>
/// شناسه کاربر
/// </summary>
public long UserId { get; set; }
/// <summary>
/// شناسه پکیج
/// </summary>
public long PackageId { get; set; }
/// <summary>
/// نام پکیج
/// </summary>
public string PackageName { get; set; } = string.Empty;
/// <summary>
/// کد فعال‌سازی
/// </summary>
public string ActivationCode { get; set; } = string.Empty;
/// <summary>
/// وضعیت فعال/غیرفعال
/// </summary>
public bool IsActive { get; set; }
/// <summary>
/// تاریخ فعال‌سازی (mapped from ActivatedAt)
/// </summary>
public DateTime? ActivationDate { get; set; }
/// <summary>
/// تاریخ انقضا (mapped from ExpiresAt)
/// </summary>
public DateTime? ExpirationDate { get; set; }
/// <summary>
/// نوع عضویت (Trial/Active/Expired/Inactive)
/// </summary>
public string Status { get; set; } = string.Empty;
/// <summary>
/// تعداد روزهای باقی‌مانده
/// </summary>
public int DaysRemaining { get; set; }
/// <summary>
/// آیا در دوره آزمایشی است؟
/// </summary>
public bool IsTrialPeriod { get; set; }
}