diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommand.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommand.cs new file mode 100644 index 0000000..43dc95d --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommand.cs @@ -0,0 +1,22 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.ActivateClubMembership; + +/// +/// Command برای فعال‌سازی عضویت باشگاه مشتریان یک کاربر +/// +public record ActivateClubMembershipCommand : IRequest +{ + /// + /// شناسه کاربر + /// + public long UserId { get; init; } + + /// + /// تاریخ فعال‌سازی (اختیاری - پیش‌فرض: الان) + /// + public DateTimeOffset? ActivationDate { get; init; } + + /// + /// دلیل فعال‌سازی (برای History) + /// + public string? Reason { get; init; } +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandHandler.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandHandler.cs new file mode 100644 index 0000000..7af3898 --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandHandler.cs @@ -0,0 +1,84 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.ActivateClubMembership; + +public class ActivateClubMembershipCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _context; + + public ActivateClubMembershipCommandHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task Handle(ActivateClubMembershipCommand request, CancellationToken cancellationToken) + { + // بررسی وجود کاربر + var userExists = await _context.Users + .AnyAsync(x => x.Id == request.UserId, cancellationToken); + + if (!userExists) + { + throw new NotFoundException(nameof(User), request.UserId); + } + + // بررسی عضویت فعلی + var existingMembership = await _context.ClubMemberships + .FirstOrDefaultAsync(x => x.UserId == request.UserId, cancellationToken); + + ClubMembership entity; + bool isNewMembership = existingMembership == null; + var activationDate = request.ActivationDate ?? DateTimeOffset.UtcNow; + + if (isNewMembership) + { + // ایجاد عضویت جدید + entity = new ClubMembership + { + UserId = request.UserId, + IsActive = true, + ActivatedAt = activationDate.DateTime, + InitialContribution = 0, + TotalEarned = 0 + }; + + await _context.ClubMemberships.AddAsync(entity, cancellationToken); + } + else + { + // فعال‌سازی مجدد عضویت موجود + entity = existingMembership; + + if (entity.IsActive) + { + // اگر از قبل فعال است، فقط تاریخ را به‌روز می‌کنیم + entity.ActivatedAt = activationDate.DateTime; + } + else + { + // فعال‌سازی عضویت غیرفعال + entity.IsActive = true; + entity.ActivatedAt = activationDate.DateTime; + } + + _context.ClubMemberships.Update(entity); + } + + await _context.SaveChangesAsync(cancellationToken); + + // ثبت تاریخچه + var history = new ClubMembershipHistory + { + ClubMembershipId = entity.Id, + UserId = entity.UserId, + OldIsActive = !isNewMembership && !existingMembership!.IsActive, + NewIsActive = true, + Action = ClubMembershipAction.Activated, + Reason = request.Reason ?? (isNewMembership ? "Initial activation" : "Reactivated"), + PerformedBy = "System" // TODO: باید از Current User گرفته شود + }; + + await _context.ClubMembershipHistories.AddAsync(history, cancellationToken); + await _context.SaveChangesAsync(cancellationToken); + + return entity.Id; + } +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandValidator.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandValidator.cs new file mode 100644 index 0000000..ce01175 --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandValidator.cs @@ -0,0 +1,34 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.ActivateClubMembership; + +public class ActivateClubMembershipCommandValidator : AbstractValidator +{ + public ActivateClubMembershipCommandValidator() + { + RuleFor(x => x.UserId) + .GreaterThan(0) + .WithMessage("شناسه کاربر معتبر نیست"); + + RuleFor(x => x.ActivationDate) + .LessThanOrEqualTo(DateTimeOffset.UtcNow.AddDays(1)) + .WithMessage("تاریخ فعال‌سازی نمی‌تواند در آینده باشد") + .When(x => x.ActivationDate.HasValue); + + RuleFor(x => x.Reason) + .MaximumLength(500) + .WithMessage("دلیل فعال‌سازی نمی‌تواند بیشتر از 500 کاراکتر باشد") + .When(x => !string.IsNullOrEmpty(x.Reason)); + } + + public Func>> ValidateValue => async (model, propertyName) => + { + var result = await ValidateAsync( + ValidationContext.CreateWithOptions( + (ActivateClubMembershipCommand)model, + x => x.IncludeProperties(propertyName))); + + if (result.IsValid) + return Array.Empty(); + + return result.Errors.Select(e => e.ErrorMessage); + }; +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AssignClubFeature/AssignClubFeatureCommand.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AssignClubFeature/AssignClubFeatureCommand.cs new file mode 100644 index 0000000..32eac76 --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AssignClubFeature/AssignClubFeatureCommand.cs @@ -0,0 +1,27 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.AssignClubFeature; + +/// +/// Command برای اختصاص Feature به عضو باشگاه +/// +public record AssignClubFeatureCommand : IRequest +{ + /// + /// شناسه کاربر + /// + public long UserId { get; init; } + + /// + /// شناسه Feature + /// + public long FeatureId { get; init; } + + /// + /// تاریخ اعطای Feature (اختیاری - پیش‌فرض: الان) + /// + public DateTime? GrantedAt { get; init; } + + /// + /// یادداشت اختیاری + /// + public string? Notes { get; init; } +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AssignClubFeature/AssignClubFeatureCommandHandler.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AssignClubFeature/AssignClubFeatureCommandHandler.cs new file mode 100644 index 0000000..1b33ecb --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AssignClubFeature/AssignClubFeatureCommandHandler.cs @@ -0,0 +1,68 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.AssignClubFeature; + +public class AssignClubFeatureCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _context; + + public AssignClubFeatureCommandHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task Handle(AssignClubFeatureCommand request, CancellationToken cancellationToken) + { + // بررسی وجود عضویت فعال + var membership = await _context.ClubMemberships + .FirstOrDefaultAsync(x => x.UserId == request.UserId && x.IsActive, cancellationToken); + + if (membership == null) + { + throw new NotFoundException(nameof(ClubMembership), $"Active membership for UserId: {request.UserId}"); + } + + // بررسی وجود Feature + var featureExists = await _context.ClubFeatures + .AnyAsync(x => x.Id == request.FeatureId && x.IsActive, cancellationToken); + + if (!featureExists) + { + throw new NotFoundException(nameof(ClubFeature), request.FeatureId); + } + + // بررسی وجود قبلی + var existingAssignment = await _context.UserClubFeatures + .FirstOrDefaultAsync(x => + x.UserId == request.UserId && + x.ClubFeatureId == request.FeatureId, + cancellationToken); + + UserClubFeature entity; + + if (existingAssignment != null) + { + // به‌روزرسانی notes + entity = existingAssignment; + entity.Notes = request.Notes; + + _context.UserClubFeatures.Update(entity); + } + else + { + // ایجاد جدید + entity = new UserClubFeature + { + UserId = request.UserId, + ClubMembershipId = membership.Id, + ClubFeatureId = request.FeatureId, + GrantedAt = request.GrantedAt ?? DateTime.UtcNow, + Notes = request.Notes + }; + + await _context.UserClubFeatures.AddAsync(entity, cancellationToken); + } + + await _context.SaveChangesAsync(cancellationToken); + + return entity.Id; + } +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AssignClubFeature/AssignClubFeatureCommandValidator.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AssignClubFeature/AssignClubFeatureCommandValidator.cs new file mode 100644 index 0000000..ce80cf8 --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AssignClubFeature/AssignClubFeatureCommandValidator.cs @@ -0,0 +1,33 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.AssignClubFeature; + +public class AssignClubFeatureCommandValidator : AbstractValidator +{ + public AssignClubFeatureCommandValidator() + { + RuleFor(x => x.UserId) + .GreaterThan(0) + .WithMessage("شناسه کاربر معتبر نیست"); + + RuleFor(x => x.FeatureId) + .GreaterThan(0) + .WithMessage("شناسه Feature معتبر نیست"); + + RuleFor(x => x.Notes) + .MaximumLength(500) + .WithMessage("طول یادداشت نباید بیشتر از 500 کاراکتر باشد") + .When(x => !string.IsNullOrEmpty(x.Notes)); + } + + public Func>> ValidateValue => async (model, propertyName) => + { + var result = await ValidateAsync( + ValidationContext.CreateWithOptions( + (AssignClubFeatureCommand)model, + x => x.IncludeProperties(propertyName))); + + if (result.IsValid) + return Array.Empty(); + + return result.Errors.Select(e => e.ErrorMessage); + }; +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/DeactivateClubMembership/DeactivateClubMembershipCommand.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/DeactivateClubMembership/DeactivateClubMembershipCommand.cs new file mode 100644 index 0000000..e600f86 --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/DeactivateClubMembership/DeactivateClubMembershipCommand.cs @@ -0,0 +1,17 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.DeactivateClubMembership; + +/// +/// Command برای غیرفعال‌سازی عضویت باشگاه مشتریان +/// +public record DeactivateClubMembershipCommand : IRequest +{ + /// + /// شناسه کاربر + /// + public long UserId { get; init; } + + /// + /// دلیل غیرفعال‌سازی + /// + public string? Reason { get; init; } +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/DeactivateClubMembership/DeactivateClubMembershipCommandHandler.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/DeactivateClubMembership/DeactivateClubMembershipCommandHandler.cs new file mode 100644 index 0000000..9ce9619 --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/DeactivateClubMembership/DeactivateClubMembershipCommandHandler.cs @@ -0,0 +1,50 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.DeactivateClubMembership; + +public class DeactivateClubMembershipCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _context; + + public DeactivateClubMembershipCommandHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task Handle(DeactivateClubMembershipCommand request, CancellationToken cancellationToken) + { + var membership = await _context.ClubMemberships + .FirstOrDefaultAsync(x => x.UserId == request.UserId, cancellationToken); + + if (membership == null) + { + throw new NotFoundException(nameof(ClubMembership), $"UserId: {request.UserId}"); + } + + // اگر از قبل غیرفعال است، هیچ کاری نکن + if (!membership.IsActive) + { + return Unit.Value; + } + + membership.IsActive = false; + + _context.ClubMemberships.Update(membership); + await _context.SaveChangesAsync(cancellationToken); + + // ثبت تاریخچه + var history = new ClubMembershipHistory + { + ClubMembershipId = membership.Id, + UserId = membership.UserId, + OldIsActive = true, + NewIsActive = false, + Action = ClubMembershipAction.Deactivated, + Reason = request.Reason ?? "Membership deactivated", + PerformedBy = "System" // TODO: باید از Current User گرفته شود + }; + + await _context.ClubMembershipHistories.AddAsync(history, cancellationToken); + await _context.SaveChangesAsync(cancellationToken); + + return Unit.Value; + } +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/DeactivateClubMembership/DeactivateClubMembershipCommandValidator.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/DeactivateClubMembership/DeactivateClubMembershipCommandValidator.cs new file mode 100644 index 0000000..80ffa43 --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/DeactivateClubMembership/DeactivateClubMembershipCommandValidator.cs @@ -0,0 +1,29 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.DeactivateClubMembership; + +public class DeactivateClubMembershipCommandValidator : AbstractValidator +{ + public DeactivateClubMembershipCommandValidator() + { + RuleFor(x => x.UserId) + .GreaterThan(0) + .WithMessage("شناسه کاربر معتبر نیست"); + + RuleFor(x => x.Reason) + .MaximumLength(500) + .WithMessage("دلیل غیرفعال‌سازی نمی‌تواند بیشتر از 500 کاراکتر باشد") + .When(x => !string.IsNullOrEmpty(x.Reason)); + } + + public Func>> ValidateValue => async (model, propertyName) => + { + var result = await ValidateAsync( + ValidationContext.CreateWithOptions( + (DeactivateClubMembershipCommand)model, + x => x.IncludeProperties(propertyName))); + + if (result.IsValid) + return Array.Empty(); + + return result.Errors.Select(e => e.ErrorMessage); + }; +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetAllClubMemberships/GetAllClubMembershipsQuery.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetAllClubMemberships/GetAllClubMembershipsQuery.cs new file mode 100644 index 0000000..d0ed890 --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetAllClubMemberships/GetAllClubMembershipsQuery.cs @@ -0,0 +1,45 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetAllClubMemberships; + +/// +/// Query برای دریافت لیست عضویت‌های باشگاه +/// +public record GetAllClubMembershipsQuery : IRequest +{ + /// + /// موقعیت صفحه‌بندی + /// + public PaginationState? PaginationState { get; init; } + + /// + /// مرتب‌سازی بر اساس + /// + public string? SortBy { get; init; } + + /// + /// فیلتر + /// + public GetAllClubMembershipsFilter? Filter { get; init; } +} + +public class GetAllClubMembershipsFilter +{ + /// + /// فیلتر بر اساس شناسه کاربر + /// + public long? UserId { get; set; } + + /// + /// فقط عضویت‌های فعال + /// + public bool? IsActive { get; set; } + + /// + /// فیلتر بر اساس تاریخ فعال‌سازی (از) + /// + public DateTimeOffset? ActivationDateFrom { get; set; } + + /// + /// فیلتر بر اساس تاریخ فعال‌سازی (تا) + /// + public DateTimeOffset? ActivationDateTo { get; set; } +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetAllClubMemberships/GetAllClubMembershipsQueryHandler.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetAllClubMemberships/GetAllClubMembershipsQueryHandler.cs new file mode 100644 index 0000000..ce5c075 --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetAllClubMemberships/GetAllClubMembershipsQueryHandler.cs @@ -0,0 +1,51 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetAllClubMemberships; + +public class GetAllClubMembershipsQueryHandler : IRequestHandler +{ + private readonly IApplicationDbContext _context; + + public GetAllClubMembershipsQueryHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task Handle(GetAllClubMembershipsQuery request, CancellationToken cancellationToken) + { + var query = _context.ClubMemberships + .ApplyOrder(sortBy: request.SortBy) + .AsNoTracking() + .AsQueryable(); + + if (request.Filter is not null) + { + query = query + .Where(x => request.Filter.UserId == null || x.UserId == request.Filter.UserId) + .Where(x => request.Filter.IsActive == null || x.IsActive == request.Filter.IsActive) + .Where(x => request.Filter.ActivationDateFrom == null || x.ActivatedAt >= request.Filter.ActivationDateFrom) + .Where(x => request.Filter.ActivationDateTo == null || x.ActivatedAt <= request.Filter.ActivationDateTo); + } + + var meta = await query.GetMetaData(request.PaginationState, cancellationToken); + + var models = await query + .PaginatedListAsync(paginationState: request.PaginationState) + .Select(x => new GetAllClubMembershipsResponseModel + { + Id = x.Id, + UserId = x.UserId, + IsActive = x.IsActive, + ActivatedAt = x.ActivatedAt, + InitialContribution = x.InitialContribution, + TotalEarned = x.TotalEarned, + Created = x.Created, + LastModified = x.LastModified + }) + .ToListAsync(cancellationToken); + + return new GetAllClubMembershipsResponseDto + { + MetaData = meta, + Models = models + }; + } +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetAllClubMemberships/GetAllClubMembershipsQueryValidator.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetAllClubMemberships/GetAllClubMembershipsQueryValidator.cs new file mode 100644 index 0000000..4b625b4 --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetAllClubMemberships/GetAllClubMembershipsQueryValidator.cs @@ -0,0 +1,30 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetAllClubMemberships; + +public class GetAllClubMembershipsQueryValidator : AbstractValidator +{ + public GetAllClubMembershipsQueryValidator() + { + RuleFor(x => x.Filter.UserId) + .GreaterThan(0) + .WithMessage("شناسه کاربر معتبر نیست") + .When(x => x.Filter?.UserId != null); + + RuleFor(x => x.Filter.ActivationDateTo) + .GreaterThanOrEqualTo(x => x.Filter.ActivationDateFrom) + .WithMessage("تاریخ پایان باید بعد از تاریخ شروع باشد") + .When(x => x.Filter?.ActivationDateFrom != null && x.Filter?.ActivationDateTo != null); + } + + public Func>> ValidateValue => async (model, propertyName) => + { + var result = await ValidateAsync( + ValidationContext.CreateWithOptions( + (GetAllClubMembershipsQuery)model, + x => x.IncludeProperties(propertyName))); + + if (result.IsValid) + return Array.Empty(); + + return result.Errors.Select(e => e.ErrorMessage); + }; +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetAllClubMemberships/GetAllClubMembershipsResponseDto.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetAllClubMemberships/GetAllClubMembershipsResponseDto.cs new file mode 100644 index 0000000..f42c6f0 --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetAllClubMemberships/GetAllClubMembershipsResponseDto.cs @@ -0,0 +1,19 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetAllClubMemberships; + +public class GetAllClubMembershipsResponseDto +{ + public MetaData MetaData { get; set; } + public List Models { get; set; } +} + +public class GetAllClubMembershipsResponseModel +{ + public long Id { get; set; } + public long UserId { get; set; } + public bool IsActive { get; set; } + public DateTime? ActivatedAt { get; set; } + public long InitialContribution { get; set; } + public long TotalEarned { get; set; } + public DateTimeOffset Created { get; set; } + public DateTimeOffset? LastModified { get; set; } +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembership/ClubMembershipDto.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembership/ClubMembershipDto.cs new file mode 100644 index 0000000..dfc3e3f --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembership/ClubMembershipDto.cs @@ -0,0 +1,16 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetClubMembership; + +/// +/// DTO برای نمایش اطلاعات عضویت باشگاه +/// +public class ClubMembershipDto +{ + public long Id { get; set; } + public long UserId { get; set; } + public bool IsActive { get; set; } + public DateTime? ActivatedAt { get; set; } + public long InitialContribution { get; set; } + public long TotalEarned { get; set; } + public DateTimeOffset Created { get; set; } + public DateTimeOffset? LastModified { get; set; } +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembership/GetClubMembershipQuery.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembership/GetClubMembershipQuery.cs new file mode 100644 index 0000000..1aa04c8 --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembership/GetClubMembershipQuery.cs @@ -0,0 +1,12 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetClubMembership; + +/// +/// Query برای دریافت عضویت باشگاه یک کاربر +/// +public record GetClubMembershipQuery : IRequest +{ + /// + /// شناسه کاربر + /// + public long UserId { get; init; } +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembership/GetClubMembershipQueryHandler.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembership/GetClubMembershipQueryHandler.cs new file mode 100644 index 0000000..2252d21 --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembership/GetClubMembershipQueryHandler.cs @@ -0,0 +1,32 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetClubMembership; + +public class GetClubMembershipQueryHandler : IRequestHandler +{ + private readonly IApplicationDbContext _context; + + public GetClubMembershipQueryHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task Handle(GetClubMembershipQuery request, CancellationToken cancellationToken) + { + var membership = await _context.ClubMemberships + .AsNoTracking() + .Where(x => x.UserId == request.UserId) + .Select(x => new ClubMembershipDto + { + Id = x.Id, + UserId = x.UserId, + IsActive = x.IsActive, + ActivatedAt = x.ActivatedAt, + InitialContribution = x.InitialContribution, + TotalEarned = x.TotalEarned, + Created = x.Created, + LastModified = x.LastModified + }) + .FirstOrDefaultAsync(cancellationToken); + + return membership; + } +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembership/GetClubMembershipQueryValidator.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembership/GetClubMembershipQueryValidator.cs new file mode 100644 index 0000000..ac26fb0 --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembership/GetClubMembershipQueryValidator.cs @@ -0,0 +1,24 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetClubMembership; + +public class GetClubMembershipQueryValidator : AbstractValidator +{ + public GetClubMembershipQueryValidator() + { + RuleFor(x => x.UserId) + .GreaterThan(0) + .WithMessage("شناسه کاربر معتبر نیست"); + } + + public Func>> ValidateValue => async (model, propertyName) => + { + var result = await ValidateAsync( + ValidationContext.CreateWithOptions( + (GetClubMembershipQuery)model, + x => x.IncludeProperties(propertyName))); + + if (result.IsValid) + return Array.Empty(); + + return result.Errors.Select(e => e.ErrorMessage); + }; +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembershipHistory/GetClubMembershipHistoryQuery.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembershipHistory/GetClubMembershipHistoryQuery.cs new file mode 100644 index 0000000..c44d23a --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembershipHistory/GetClubMembershipHistoryQuery.cs @@ -0,0 +1,27 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetClubMembershipHistory; + +/// +/// Query برای دریافت تاریخچه تغییرات عضویت باشگاه +/// +public record GetClubMembershipHistoryQuery : IRequest +{ + /// + /// شناسه عضویت (اختیاری) + /// + public long? MembershipId { get; init; } + + /// + /// شناسه کاربر (اختیاری) + /// + public long? UserId { get; init; } + + /// + /// موقعیت صفحه‌بندی + /// + public PaginationState? PaginationState { get; init; } + + /// + /// مرتب‌سازی بر اساس + /// + public string? SortBy { get; init; } +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembershipHistory/GetClubMembershipHistoryQueryHandler.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembershipHistory/GetClubMembershipHistoryQueryHandler.cs new file mode 100644 index 0000000..f81264c --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembershipHistory/GetClubMembershipHistoryQueryHandler.cs @@ -0,0 +1,56 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetClubMembershipHistory; + +public class GetClubMembershipHistoryQueryHandler : IRequestHandler +{ + private readonly IApplicationDbContext _context; + + public GetClubMembershipHistoryQueryHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task Handle(GetClubMembershipHistoryQuery request, CancellationToken cancellationToken) + { + var query = _context.ClubMembershipHistories + .AsNoTracking() + .AsQueryable(); + + if (request.MembershipId.HasValue) + { + query = query.Where(x => x.ClubMembershipId == request.MembershipId.Value); + } + + if (request.UserId.HasValue) + { + query = query.Where(x => x.UserId == request.UserId.Value); + } + + query = query.ApplyOrder(sortBy: request.SortBy ?? "-Created"); // پیش‌فرض: جدیدترین اول + + var meta = await query.GetMetaData(request.PaginationState, cancellationToken); + + var models = await query + .PaginatedListAsync(paginationState: request.PaginationState) + .Select(x => new GetClubMembershipHistoryResponseModel + { + Id = x.Id, + ClubMembershipId = x.ClubMembershipId, + UserId = x.UserId, + OldIsActive = x.OldIsActive, + NewIsActive = x.NewIsActive, + OldInitialContribution = x.OldInitialContribution, + NewInitialContribution = x.NewInitialContribution, + Action = x.Action, + Reason = x.Reason, + PerformedBy = x.PerformedBy, + Created = x.Created + }) + .ToListAsync(cancellationToken); + + return new GetClubMembershipHistoryResponseDto + { + MetaData = meta, + Models = models + }; + } +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembershipHistory/GetClubMembershipHistoryQueryValidator.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembershipHistory/GetClubMembershipHistoryQueryValidator.cs new file mode 100644 index 0000000..c9d4db0 --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembershipHistory/GetClubMembershipHistoryQueryValidator.cs @@ -0,0 +1,34 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetClubMembershipHistory; + +public class GetClubMembershipHistoryQueryValidator : AbstractValidator +{ + public GetClubMembershipHistoryQueryValidator() + { + RuleFor(x => x.MembershipId) + .GreaterThan(0) + .WithMessage("شناسه عضویت معتبر نیست") + .When(x => x.MembershipId.HasValue); + + RuleFor(x => x.UserId) + .GreaterThan(0) + .WithMessage("شناسه کاربر معتبر نیست") + .When(x => x.UserId.HasValue); + + RuleFor(x => x) + .Must(x => x.MembershipId.HasValue || x.UserId.HasValue) + .WithMessage("حداقل یکی از MembershipId یا UserId باید مقداردهی شود"); + } + + public Func>> ValidateValue => async (model, propertyName) => + { + var result = await ValidateAsync( + ValidationContext.CreateWithOptions( + (GetClubMembershipHistoryQuery)model, + x => x.IncludeProperties(propertyName))); + + if (result.IsValid) + return Array.Empty(); + + return result.Errors.Select(e => e.ErrorMessage); + }; +} diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembershipHistory/GetClubMembershipHistoryResponseDto.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembershipHistory/GetClubMembershipHistoryResponseDto.cs new file mode 100644 index 0000000..1411d7b --- /dev/null +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Queries/GetClubMembershipHistory/GetClubMembershipHistoryResponseDto.cs @@ -0,0 +1,22 @@ +namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetClubMembershipHistory; + +public class GetClubMembershipHistoryResponseDto +{ + public MetaData MetaData { get; set; } + public List Models { get; set; } +} + +public class GetClubMembershipHistoryResponseModel +{ + public long Id { get; set; } + public long ClubMembershipId { get; set; } + public long UserId { get; set; } + public bool OldIsActive { get; set; } + public bool NewIsActive { get; set; } + public long? OldInitialContribution { get; set; } + public long? NewInitialContribution { get; set; } + public ClubMembershipAction Action { get; set; } + public string? Reason { get; set; } + public string? PerformedBy { get; set; } + public DateTimeOffset Created { get; set; } +}