Add validators and services for Product Galleries and Product Tags
- Implemented Create, Delete, Get, and Update validators for Product Galleries. - Added Create, Delete, Get, and Update validators for Product Tags. - Created service classes for handling Discount Categories, Discount Orders, Discount Products, Discount Shopping Cart, Product Categories, Product Galleries, and Product Tags. - Each service class integrates with CQRS for command and query handling. - Established mapping profiles for Product Galleries.
This commit is contained in:
+10
@@ -0,0 +1,10 @@
|
||||
namespace CMSMicroservice.Application.TagCQ.Commands.AssignTagToProduct;
|
||||
|
||||
public record AssignTagToProductCommand : IRequest
|
||||
{
|
||||
/// <summary>شناسه محصول</summary>
|
||||
public long ProductId { get; init; }
|
||||
|
||||
/// <summary>شناسه تگ</summary>
|
||||
public long TagId { get; init; }
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
using CMSMicroservice.Application.Common.Exceptions;
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.TagCQ.Commands.AssignTagToProduct;
|
||||
|
||||
public class AssignTagToProductCommandHandler : IRequestHandler<AssignTagToProductCommand, Unit>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public AssignTagToProductCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Unit> Handle(AssignTagToProductCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// بررسی وجود محصول
|
||||
var product = await _context.Products
|
||||
.FirstOrDefaultAsync(p => p.Id == request.ProductId, cancellationToken);
|
||||
|
||||
if (product == null)
|
||||
{
|
||||
throw new NotFoundException(nameof(Product), request.ProductId);
|
||||
}
|
||||
|
||||
// بررسی وجود تگ
|
||||
var tag = await _context.Tags
|
||||
.FirstOrDefaultAsync(t => t.Id == request.TagId, cancellationToken);
|
||||
|
||||
if (tag == null)
|
||||
{
|
||||
throw new NotFoundException(nameof(Tag), request.TagId);
|
||||
}
|
||||
|
||||
// بررسی اینکه قبلاً اختصاص داده نشده باشد
|
||||
var existingProductTag = await _context.ProductTags
|
||||
.FirstOrDefaultAsync(pt => pt.ProductId == request.ProductId && pt.TagId == request.TagId, cancellationToken);
|
||||
|
||||
if (existingProductTag != null)
|
||||
{
|
||||
throw new BadRequestException("این تگ قبلاً به این محصول اختصاص داده شده است");
|
||||
}
|
||||
|
||||
var productTag = new ProductTag
|
||||
{
|
||||
ProductId = request.ProductId,
|
||||
TagId = request.TagId
|
||||
};
|
||||
|
||||
_context.ProductTags.Add(productTag);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return Unit.Value;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
namespace CMSMicroservice.Application.TagCQ.Commands.AssignTagToProduct;
|
||||
|
||||
public class AssignTagToProductCommandValidator : AbstractValidator<AssignTagToProductCommand>
|
||||
{
|
||||
public AssignTagToProductCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.ProductId)
|
||||
.GreaterThan(0).WithMessage("شناسه محصول نامعتبر است");
|
||||
|
||||
RuleFor(x => x.TagId)
|
||||
.GreaterThan(0).WithMessage("شناسه تگ نامعتبر است");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace CMSMicroservice.Application.TagCQ.Commands.CreateTag;
|
||||
|
||||
public record CreateTagCommand : IRequest<long>
|
||||
{
|
||||
/// <summary>نام لاتین تگ</summary>
|
||||
public string Name { get; init; }
|
||||
|
||||
/// <summary>عنوان فارسی تگ</summary>
|
||||
public string Title { get; init; }
|
||||
|
||||
/// <summary>توضیحات</summary>
|
||||
public string? Description { get; init; }
|
||||
|
||||
/// <summary>ترتیب نمایش</summary>
|
||||
public int SortOrder { get; init; }
|
||||
|
||||
/// <summary>وضعیت فعال/غیرفعال</summary>
|
||||
public bool IsActive { get; init; } = true;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using CMSMicroservice.Application.Common.Exceptions;
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.TagCQ.Commands.CreateTag;
|
||||
|
||||
public class CreateTagCommandHandler : IRequestHandler<CreateTagCommand, long>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public CreateTagCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<long> Handle(CreateTagCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// بررسی تکراری نبودن نام
|
||||
var existingTag = await _context.Tags
|
||||
.FirstOrDefaultAsync(t => t.Name == request.Name, cancellationToken);
|
||||
|
||||
if (existingTag != null)
|
||||
{
|
||||
throw new BadRequestException($"تگ با نام '{request.Name}' قبلاً ثبت شده است");
|
||||
}
|
||||
|
||||
var tag = new Tag
|
||||
{
|
||||
Name = request.Name,
|
||||
Title = request.Title,
|
||||
Description = request.Description,
|
||||
SortOrder = request.SortOrder,
|
||||
IsActive = request.IsActive
|
||||
};
|
||||
|
||||
_context.Tags.Add(tag);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return tag.Id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace CMSMicroservice.Application.TagCQ.Commands.CreateTag;
|
||||
|
||||
public class CreateTagCommandValidator : AbstractValidator<CreateTagCommand>
|
||||
{
|
||||
public CreateTagCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.Name)
|
||||
.NotEmpty().WithMessage("نام تگ الزامی است")
|
||||
.MaximumLength(100).WithMessage("نام تگ نباید بیشتر از 100 کاراکتر باشد")
|
||||
.Matches("^[a-zA-Z0-9_-]+$").WithMessage("نام تگ فقط باید شامل حروف انگلیسی، اعداد، خط تیره و زیرخط باشد");
|
||||
|
||||
RuleFor(x => x.Title)
|
||||
.NotEmpty().WithMessage("عنوان تگ الزامی است")
|
||||
.MaximumLength(200).WithMessage("عنوان تگ نباید بیشتر از 200 کاراکتر باشد");
|
||||
|
||||
RuleFor(x => x.Description)
|
||||
.MaximumLength(500).When(x => !string.IsNullOrEmpty(x.Description))
|
||||
.WithMessage("توضیحات نباید بیشتر از 500 کاراکتر باشد");
|
||||
|
||||
RuleFor(x => x.SortOrder)
|
||||
.GreaterThanOrEqualTo(0).WithMessage("ترتیب نمایش نمیتواند منفی باشد");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using CMSMicroservice.Application.Common.Models;
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.TagCQ.Queries.GetAllTags;
|
||||
|
||||
/// <summary>
|
||||
/// کوئری دریافت همه تگها با فیلتر و صفحهبندی
|
||||
/// </summary>
|
||||
public class GetAllTagsQuery : IRequest<GetAllTagsResponseDto>
|
||||
{
|
||||
public int PageNumber { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 20;
|
||||
public bool? IsActive { get; set; }
|
||||
public string? SearchTerm { get; set; }
|
||||
public bool OrderByDescending { get; set; } = false;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Application.Common.Models;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CMSMicroservice.Application.TagCQ.Queries.GetAllTags;
|
||||
|
||||
public class GetAllTagsQueryHandler : IRequestHandler<GetAllTagsQuery, GetAllTagsResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly ILogger<GetAllTagsQueryHandler> _logger;
|
||||
|
||||
public GetAllTagsQueryHandler(
|
||||
IApplicationDbContext context,
|
||||
ILogger<GetAllTagsQueryHandler> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<GetAllTagsResponseDto> Handle(GetAllTagsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.Tags
|
||||
.Where(x => !x.IsDeleted);
|
||||
|
||||
// فیلترها
|
||||
if (request.IsActive.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.IsActive == request.IsActive.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(request.SearchTerm))
|
||||
{
|
||||
var searchTerm = request.SearchTerm.ToLower();
|
||||
query = query.Where(x => x.Name.ToLower().Contains(searchTerm)
|
||||
|| x.Title.ToLower().Contains(searchTerm));
|
||||
}
|
||||
|
||||
var totalCount = await query.CountAsync(cancellationToken);
|
||||
|
||||
// مرتبسازی
|
||||
query = request.OrderByDescending
|
||||
? query.OrderByDescending(x => x.SortOrder).ThenByDescending(x => x.Created)
|
||||
: query.OrderBy(x => x.SortOrder).ThenBy(x => x.Created);
|
||||
|
||||
// صفحهبندی
|
||||
var tags = await query
|
||||
.Skip((request.PageNumber - 1) * request.PageSize)
|
||||
.Take(request.PageSize)
|
||||
.Select(x => new TagDto
|
||||
{
|
||||
Id = x.Id,
|
||||
Name = x.Name,
|
||||
Title = x.Title,
|
||||
Description = x.Description,
|
||||
IsActive = x.IsActive,
|
||||
SortOrder = x.SortOrder,
|
||||
ProductCount = x.ProductTags.Count(p => !p.IsDeleted),
|
||||
Created = x.Created
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var metaData = new MetaData
|
||||
{
|
||||
TotalCount = totalCount,
|
||||
PageSize = request.PageSize,
|
||||
CurrentPage = request.PageNumber,
|
||||
TotalPage = (int)Math.Ceiling(totalCount / (double)request.PageSize),
|
||||
HasNext = request.PageNumber < (int)Math.Ceiling(totalCount / (double)request.PageSize),
|
||||
HasPrevious = request.PageNumber > 1
|
||||
};
|
||||
|
||||
_logger.LogInformation("Retrieved {Count} tags. Total: {Total}", tags.Count, totalCount);
|
||||
|
||||
return new GetAllTagsResponseDto
|
||||
{
|
||||
MetaData = metaData,
|
||||
Tags = tags
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using CMSMicroservice.Application.Common.Models;
|
||||
|
||||
namespace CMSMicroservice.Application.TagCQ.Queries.GetAllTags;
|
||||
|
||||
public class GetAllTagsResponseDto
|
||||
{
|
||||
public MetaData MetaData { get; set; } = new();
|
||||
public List<TagDto> Tags { get; set; } = new();
|
||||
}
|
||||
|
||||
public class TagDto
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
public int ProductCount { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
namespace CMSMicroservice.Application.TagCQ.Queries.GetProductsByTag;
|
||||
|
||||
public record GetProductsByTagQuery : IRequest<List<ProductSimpleDto>>
|
||||
{
|
||||
/// <summary>شناسه تگ</summary>
|
||||
public long TagId { get; init; }
|
||||
}
|
||||
|
||||
public class ProductSimpleDto
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string Title { get; set; }
|
||||
public long Price { get; set; }
|
||||
public int Inventory { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public string? ImagePath { get; set; }
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
using CMSMicroservice.Application.Common.Exceptions;
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.TagCQ.Queries.GetProductsByTag;
|
||||
|
||||
public class GetProductsByTagQueryHandler : IRequestHandler<GetProductsByTagQuery, List<ProductSimpleDto>>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public GetProductsByTagQueryHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<List<ProductSimpleDto>> Handle(GetProductsByTagQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
// بررسی وجود تگ
|
||||
var tagExists = await _context.Tags
|
||||
.AnyAsync(t => t.Id == request.TagId, cancellationToken);
|
||||
|
||||
if (!tagExists)
|
||||
{
|
||||
throw new NotFoundException(nameof(Tag), request.TagId);
|
||||
}
|
||||
|
||||
var products = await _context.ProductTags
|
||||
.Where(pt => pt.TagId == request.TagId)
|
||||
.Include(pt => pt.Product)
|
||||
.Select(pt => new ProductSimpleDto
|
||||
{
|
||||
Id = pt.Product.Id,
|
||||
Title = pt.Product.Title,
|
||||
Price = pt.Product.Price,
|
||||
Inventory = pt.Product.RemainingCount,
|
||||
IsActive = true, // Product entity doesn't have IsActive field
|
||||
ImagePath = pt.Product.ImagePath
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return products;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user