feat: Add ClubMembershipCQ - Phase 3 Application Layer
- Implemented 3 Commands with handlers and validators: * ActivateClubMembership: Create/reactivate membership * DeactivateClubMembership: Deactivate existing membership * AssignClubFeature: Assign features to club members - Implemented 3 Queries with handlers, validators, and DTOs: * GetClubMembership: Retrieve single membership by UserId * GetAllClubMemberships: List with filtering and pagination * GetClubMembershipHistory: Audit trail with full history - All operations include history tracking via ClubMembershipHistory - Property names aligned with Domain entity definitions - 21 new files, ~600 lines of code - Build successful with 0 errors
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.ActivateClubMembership;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Command برای فعالسازی عضویت باشگاه مشتریان یک کاربر
|
||||||
|
/// </summary>
|
||||||
|
public record ActivateClubMembershipCommand : IRequest<long>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه کاربر
|
||||||
|
/// </summary>
|
||||||
|
public long UserId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// تاریخ فعالسازی (اختیاری - پیشفرض: الان)
|
||||||
|
/// </summary>
|
||||||
|
public DateTimeOffset? ActivationDate { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// دلیل فعالسازی (برای History)
|
||||||
|
/// </summary>
|
||||||
|
public string? Reason { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.ActivateClubMembership;
|
||||||
|
|
||||||
|
public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClubMembershipCommand, long>
|
||||||
|
{
|
||||||
|
private readonly IApplicationDbContext _context;
|
||||||
|
|
||||||
|
public ActivateClubMembershipCommandHandler(IApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<long> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.ActivateClubMembership;
|
||||||
|
|
||||||
|
public class ActivateClubMembershipCommandValidator : AbstractValidator<ActivateClubMembershipCommand>
|
||||||
|
{
|
||||||
|
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<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||||
|
{
|
||||||
|
var result = await ValidateAsync(
|
||||||
|
ValidationContext<ActivateClubMembershipCommand>.CreateWithOptions(
|
||||||
|
(ActivateClubMembershipCommand)model,
|
||||||
|
x => x.IncludeProperties(propertyName)));
|
||||||
|
|
||||||
|
if (result.IsValid)
|
||||||
|
return Array.Empty<string>();
|
||||||
|
|
||||||
|
return result.Errors.Select(e => e.ErrorMessage);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.AssignClubFeature;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Command برای اختصاص Feature به عضو باشگاه
|
||||||
|
/// </summary>
|
||||||
|
public record AssignClubFeatureCommand : IRequest<long>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه کاربر
|
||||||
|
/// </summary>
|
||||||
|
public long UserId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه Feature
|
||||||
|
/// </summary>
|
||||||
|
public long FeatureId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// تاریخ اعطای Feature (اختیاری - پیشفرض: الان)
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? GrantedAt { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// یادداشت اختیاری
|
||||||
|
/// </summary>
|
||||||
|
public string? Notes { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.AssignClubFeature;
|
||||||
|
|
||||||
|
public class AssignClubFeatureCommandHandler : IRequestHandler<AssignClubFeatureCommand, long>
|
||||||
|
{
|
||||||
|
private readonly IApplicationDbContext _context;
|
||||||
|
|
||||||
|
public AssignClubFeatureCommandHandler(IApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<long> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.AssignClubFeature;
|
||||||
|
|
||||||
|
public class AssignClubFeatureCommandValidator : AbstractValidator<AssignClubFeatureCommand>
|
||||||
|
{
|
||||||
|
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<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||||
|
{
|
||||||
|
var result = await ValidateAsync(
|
||||||
|
ValidationContext<AssignClubFeatureCommand>.CreateWithOptions(
|
||||||
|
(AssignClubFeatureCommand)model,
|
||||||
|
x => x.IncludeProperties(propertyName)));
|
||||||
|
|
||||||
|
if (result.IsValid)
|
||||||
|
return Array.Empty<string>();
|
||||||
|
|
||||||
|
return result.Errors.Select(e => e.ErrorMessage);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.DeactivateClubMembership;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Command برای غیرفعالسازی عضویت باشگاه مشتریان
|
||||||
|
/// </summary>
|
||||||
|
public record DeactivateClubMembershipCommand : IRequest<Unit>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه کاربر
|
||||||
|
/// </summary>
|
||||||
|
public long UserId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// دلیل غیرفعالسازی
|
||||||
|
/// </summary>
|
||||||
|
public string? Reason { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.DeactivateClubMembership;
|
||||||
|
|
||||||
|
public class DeactivateClubMembershipCommandHandler : IRequestHandler<DeactivateClubMembershipCommand, Unit>
|
||||||
|
{
|
||||||
|
private readonly IApplicationDbContext _context;
|
||||||
|
|
||||||
|
public DeactivateClubMembershipCommandHandler(IApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Unit> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.DeactivateClubMembership;
|
||||||
|
|
||||||
|
public class DeactivateClubMembershipCommandValidator : AbstractValidator<DeactivateClubMembershipCommand>
|
||||||
|
{
|
||||||
|
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<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||||
|
{
|
||||||
|
var result = await ValidateAsync(
|
||||||
|
ValidationContext<DeactivateClubMembershipCommand>.CreateWithOptions(
|
||||||
|
(DeactivateClubMembershipCommand)model,
|
||||||
|
x => x.IncludeProperties(propertyName)));
|
||||||
|
|
||||||
|
if (result.IsValid)
|
||||||
|
return Array.Empty<string>();
|
||||||
|
|
||||||
|
return result.Errors.Select(e => e.ErrorMessage);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetAllClubMemberships;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Query برای دریافت لیست عضویتهای باشگاه
|
||||||
|
/// </summary>
|
||||||
|
public record GetAllClubMembershipsQuery : IRequest<GetAllClubMembershipsResponseDto>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// موقعیت صفحهبندی
|
||||||
|
/// </summary>
|
||||||
|
public PaginationState? PaginationState { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مرتبسازی بر اساس
|
||||||
|
/// </summary>
|
||||||
|
public string? SortBy { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// فیلتر
|
||||||
|
/// </summary>
|
||||||
|
public GetAllClubMembershipsFilter? Filter { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GetAllClubMembershipsFilter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// فیلتر بر اساس شناسه کاربر
|
||||||
|
/// </summary>
|
||||||
|
public long? UserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// فقط عضویتهای فعال
|
||||||
|
/// </summary>
|
||||||
|
public bool? IsActive { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// فیلتر بر اساس تاریخ فعالسازی (از)
|
||||||
|
/// </summary>
|
||||||
|
public DateTimeOffset? ActivationDateFrom { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// فیلتر بر اساس تاریخ فعالسازی (تا)
|
||||||
|
/// </summary>
|
||||||
|
public DateTimeOffset? ActivationDateTo { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetAllClubMemberships;
|
||||||
|
|
||||||
|
public class GetAllClubMembershipsQueryHandler : IRequestHandler<GetAllClubMembershipsQuery, GetAllClubMembershipsResponseDto>
|
||||||
|
{
|
||||||
|
private readonly IApplicationDbContext _context;
|
||||||
|
|
||||||
|
public GetAllClubMembershipsQueryHandler(IApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<GetAllClubMembershipsResponseDto> 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetAllClubMemberships;
|
||||||
|
|
||||||
|
public class GetAllClubMembershipsQueryValidator : AbstractValidator<GetAllClubMembershipsQuery>
|
||||||
|
{
|
||||||
|
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<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||||
|
{
|
||||||
|
var result = await ValidateAsync(
|
||||||
|
ValidationContext<GetAllClubMembershipsQuery>.CreateWithOptions(
|
||||||
|
(GetAllClubMembershipsQuery)model,
|
||||||
|
x => x.IncludeProperties(propertyName)));
|
||||||
|
|
||||||
|
if (result.IsValid)
|
||||||
|
return Array.Empty<string>();
|
||||||
|
|
||||||
|
return result.Errors.Select(e => e.ErrorMessage);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetAllClubMemberships;
|
||||||
|
|
||||||
|
public class GetAllClubMembershipsResponseDto
|
||||||
|
{
|
||||||
|
public MetaData MetaData { get; set; }
|
||||||
|
public List<GetAllClubMembershipsResponseModel> 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; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetClubMembership;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DTO برای نمایش اطلاعات عضویت باشگاه
|
||||||
|
/// </summary>
|
||||||
|
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; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetClubMembership;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Query برای دریافت عضویت باشگاه یک کاربر
|
||||||
|
/// </summary>
|
||||||
|
public record GetClubMembershipQuery : IRequest<ClubMembershipDto?>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه کاربر
|
||||||
|
/// </summary>
|
||||||
|
public long UserId { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetClubMembership;
|
||||||
|
|
||||||
|
public class GetClubMembershipQueryHandler : IRequestHandler<GetClubMembershipQuery, ClubMembershipDto?>
|
||||||
|
{
|
||||||
|
private readonly IApplicationDbContext _context;
|
||||||
|
|
||||||
|
public GetClubMembershipQueryHandler(IApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ClubMembershipDto?> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetClubMembership;
|
||||||
|
|
||||||
|
public class GetClubMembershipQueryValidator : AbstractValidator<GetClubMembershipQuery>
|
||||||
|
{
|
||||||
|
public GetClubMembershipQueryValidator()
|
||||||
|
{
|
||||||
|
RuleFor(x => x.UserId)
|
||||||
|
.GreaterThan(0)
|
||||||
|
.WithMessage("شناسه کاربر معتبر نیست");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||||
|
{
|
||||||
|
var result = await ValidateAsync(
|
||||||
|
ValidationContext<GetClubMembershipQuery>.CreateWithOptions(
|
||||||
|
(GetClubMembershipQuery)model,
|
||||||
|
x => x.IncludeProperties(propertyName)));
|
||||||
|
|
||||||
|
if (result.IsValid)
|
||||||
|
return Array.Empty<string>();
|
||||||
|
|
||||||
|
return result.Errors.Select(e => e.ErrorMessage);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetClubMembershipHistory;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Query برای دریافت تاریخچه تغییرات عضویت باشگاه
|
||||||
|
/// </summary>
|
||||||
|
public record GetClubMembershipHistoryQuery : IRequest<GetClubMembershipHistoryResponseDto>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه عضویت (اختیاری)
|
||||||
|
/// </summary>
|
||||||
|
public long? MembershipId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه کاربر (اختیاری)
|
||||||
|
/// </summary>
|
||||||
|
public long? UserId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// موقعیت صفحهبندی
|
||||||
|
/// </summary>
|
||||||
|
public PaginationState? PaginationState { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مرتبسازی بر اساس
|
||||||
|
/// </summary>
|
||||||
|
public string? SortBy { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetClubMembershipHistory;
|
||||||
|
|
||||||
|
public class GetClubMembershipHistoryQueryHandler : IRequestHandler<GetClubMembershipHistoryQuery, GetClubMembershipHistoryResponseDto>
|
||||||
|
{
|
||||||
|
private readonly IApplicationDbContext _context;
|
||||||
|
|
||||||
|
public GetClubMembershipHistoryQueryHandler(IApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<GetClubMembershipHistoryResponseDto> 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetClubMembershipHistory;
|
||||||
|
|
||||||
|
public class GetClubMembershipHistoryQueryValidator : AbstractValidator<GetClubMembershipHistoryQuery>
|
||||||
|
{
|
||||||
|
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<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||||
|
{
|
||||||
|
var result = await ValidateAsync(
|
||||||
|
ValidationContext<GetClubMembershipHistoryQuery>.CreateWithOptions(
|
||||||
|
(GetClubMembershipHistoryQuery)model,
|
||||||
|
x => x.IncludeProperties(propertyName)));
|
||||||
|
|
||||||
|
if (result.IsValid)
|
||||||
|
return Array.Empty<string>();
|
||||||
|
|
||||||
|
return result.Errors.Select(e => e.ErrorMessage);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
namespace CMSMicroservice.Application.ClubMembershipCQ.Queries.GetClubMembershipHistory;
|
||||||
|
|
||||||
|
public class GetClubMembershipHistoryResponseDto
|
||||||
|
{
|
||||||
|
public MetaData MetaData { get; set; }
|
||||||
|
public List<GetClubMembershipHistoryResponseModel> 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; }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user