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:
@@ -0,0 +1,17 @@
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.AddToCart;
|
||||
|
||||
public class AddToCartCommand : IRequest<AddToCartResponseDto>
|
||||
{
|
||||
public long UserId { get; set; }
|
||||
public long ProductId { get; set; }
|
||||
public int Count { get; set; }
|
||||
}
|
||||
|
||||
public class AddToCartResponseDto
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
public long CartItemId { get; set; }
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities.DiscountShop;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.AddToCart;
|
||||
|
||||
public class AddToCartCommandHandler : IRequestHandler<AddToCartCommand, AddToCartResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public AddToCartCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<AddToCartResponseDto> Handle(AddToCartCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// Check if product exists and is active
|
||||
var product = await _context.DiscountProducts
|
||||
.FirstOrDefaultAsync(p => p.Id == request.ProductId && p.IsActive, cancellationToken);
|
||||
|
||||
if (product == null)
|
||||
{
|
||||
return new AddToCartResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = "محصول یافت نشد یا غیرفعال است"
|
||||
};
|
||||
}
|
||||
|
||||
// Check stock availability
|
||||
if (product.RemainingCount < request.Count)
|
||||
{
|
||||
return new AddToCartResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = $"موجودی کافی نیست. موجودی فعلی: {product.RemainingCount}"
|
||||
};
|
||||
}
|
||||
|
||||
// Check if item already exists in cart
|
||||
var existingCartItem = await _context.DiscountShoppingCarts
|
||||
.FirstOrDefaultAsync(c => c.UserId == request.UserId && c.ProductId == request.ProductId, cancellationToken);
|
||||
|
||||
if (existingCartItem != null)
|
||||
{
|
||||
// Update quantity
|
||||
var newCount = existingCartItem.Count + request.Count;
|
||||
|
||||
if (product.RemainingCount < newCount)
|
||||
{
|
||||
return new AddToCartResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = $"موجودی کافی نیست. موجودی فعلی: {product.RemainingCount}"
|
||||
};
|
||||
}
|
||||
|
||||
existingCartItem.Count = newCount;
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new AddToCartResponseDto
|
||||
{
|
||||
Success = true,
|
||||
Message = "تعداد محصول در سبد خرید بهروزرسانی شد",
|
||||
CartItemId = existingCartItem.Id
|
||||
};
|
||||
}
|
||||
|
||||
// Add new item to cart
|
||||
var cartItem = new DiscountShoppingCart
|
||||
{
|
||||
UserId = request.UserId,
|
||||
ProductId = request.ProductId,
|
||||
Count = request.Count
|
||||
};
|
||||
|
||||
_context.DiscountShoppingCarts.Add(cartItem);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new AddToCartResponseDto
|
||||
{
|
||||
Success = true,
|
||||
Message = "محصول به سبد خرید اضافه شد",
|
||||
CartItemId = cartItem.Id
|
||||
};
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.AddToCart;
|
||||
|
||||
public class AddToCartCommandValidator : AbstractValidator<AddToCartCommand>
|
||||
{
|
||||
public AddToCartCommandValidator()
|
||||
{
|
||||
RuleFor(v => v.UserId)
|
||||
.GreaterThan(0).WithMessage("شناسه کاربر نامعتبر است");
|
||||
|
||||
RuleFor(v => v.ProductId)
|
||||
.GreaterThan(0).WithMessage("شناسه محصول نامعتبر است");
|
||||
|
||||
RuleFor(v => v.Count)
|
||||
.GreaterThan(0).WithMessage("تعداد باید بیشتر از صفر باشد");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.ClearCart;
|
||||
|
||||
public class ClearCartCommand : IRequest<bool>
|
||||
{
|
||||
public long UserId { get; set; }
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.ClearCart;
|
||||
|
||||
public class ClearCartCommandHandler : IRequestHandler<ClearCartCommand, bool>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public ClearCartCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(ClearCartCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var cartItems = await _context.DiscountShoppingCarts
|
||||
.Where(c => c.UserId == request.UserId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (!cartItems.Any())
|
||||
{
|
||||
return true; // Cart already empty
|
||||
}
|
||||
|
||||
_context.DiscountShoppingCarts.RemoveRange(cartItems);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.CompleteOrderPayment;
|
||||
|
||||
public class CompleteOrderPaymentCommand : IRequest<CompleteOrderPaymentResponseDto>
|
||||
{
|
||||
public long OrderId { get; set; }
|
||||
public long TransactionId { get; set; }
|
||||
public bool PaymentSuccess { get; set; }
|
||||
public string? RefId { get; set; }
|
||||
}
|
||||
|
||||
public class CompleteOrderPaymentResponseDto
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
public long? OrderId { get; set; }
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.CompleteOrderPayment;
|
||||
|
||||
public class CompleteOrderPaymentCommandHandler : IRequestHandler<CompleteOrderPaymentCommand, CompleteOrderPaymentResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public CompleteOrderPaymentCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<CompleteOrderPaymentResponseDto> Handle(CompleteOrderPaymentCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var order = await _context.DiscountOrders
|
||||
.Include(o => o.OrderDetails)
|
||||
.ThenInclude(od => od.Product)
|
||||
.FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken);
|
||||
|
||||
if (order == null)
|
||||
{
|
||||
return new CompleteOrderPaymentResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = "سفارش یافت نشد"
|
||||
};
|
||||
}
|
||||
|
||||
var transaction = await _context.Transactions
|
||||
.FirstOrDefaultAsync(t => t.Id == request.TransactionId, cancellationToken);
|
||||
|
||||
if (transaction == null)
|
||||
{
|
||||
return new CompleteOrderPaymentResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = "تراکنش یافت نشد"
|
||||
};
|
||||
}
|
||||
|
||||
if (request.PaymentSuccess)
|
||||
{
|
||||
// Update transaction
|
||||
transaction.PaymentStatus = PaymentStatus.Success;
|
||||
transaction.PaymentDate = DateTime.UtcNow;
|
||||
transaction.RefId = request.RefId;
|
||||
|
||||
// Update order
|
||||
order.PaymentStatus = PaymentStatus.Success;
|
||||
order.PaymentDate = DateTime.UtcNow;
|
||||
order.DeliveryStatus = DeliveryStatus.InTransit;
|
||||
|
||||
// Deduct discount balance from user wallet
|
||||
var userWallet = await _context.UserWallets
|
||||
.FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken);
|
||||
|
||||
if (userWallet != null)
|
||||
{
|
||||
userWallet.DiscountBalance -= order.DiscountBalanceUsed;
|
||||
}
|
||||
|
||||
// Update product stock and sale count
|
||||
foreach (var orderDetail in order.OrderDetails)
|
||||
{
|
||||
var product = orderDetail.Product;
|
||||
product.RemainingCount -= orderDetail.Count;
|
||||
product.SaleCount += orderDetail.Count;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new CompleteOrderPaymentResponseDto
|
||||
{
|
||||
Success = true,
|
||||
Message = "پرداخت با موفقیت انجام شد",
|
||||
OrderId = order.Id
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Payment failed
|
||||
transaction.PaymentStatus = PaymentStatus.Reject;
|
||||
order.PaymentStatus = PaymentStatus.Reject;
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new CompleteOrderPaymentResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = "پرداخت ناموفق بود",
|
||||
OrderId = order.Id
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.CreateDiscountCategory;
|
||||
|
||||
public class CreateDiscountCategoryCommand : IRequest<long>
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? ImagePath { get; set; }
|
||||
public long? ParentCategoryId { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Entities.DiscountShop;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.CreateDiscountCategory;
|
||||
|
||||
public class CreateDiscountCategoryCommandHandler : IRequestHandler<CreateDiscountCategoryCommand, long>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public CreateDiscountCategoryCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<long> Handle(CreateDiscountCategoryCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// بررسی وجود دستهبندی با همین نام
|
||||
var existingCategory = await _context.DiscountCategories
|
||||
.FirstOrDefaultAsync(c => c.Name == request.Name, cancellationToken);
|
||||
|
||||
if (existingCategory != null)
|
||||
{
|
||||
throw new InvalidOperationException("دستهبندی با این نام قبلاً ثبت شده است");
|
||||
}
|
||||
|
||||
// بررسی وجود دستهبندی والد
|
||||
if (request.ParentCategoryId.HasValue)
|
||||
{
|
||||
var parentExists = await _context.DiscountCategories
|
||||
.AnyAsync(c => c.Id == request.ParentCategoryId.Value, cancellationToken);
|
||||
|
||||
if (!parentExists)
|
||||
{
|
||||
throw new InvalidOperationException("دستهبندی والد یافت نشد");
|
||||
}
|
||||
}
|
||||
|
||||
var category = new DiscountCategory
|
||||
{
|
||||
Name = request.Name,
|
||||
Title = request.Title,
|
||||
Description = request.Description,
|
||||
ImagePath = request.ImagePath,
|
||||
ParentCategoryId = request.ParentCategoryId,
|
||||
SortOrder = request.SortOrder,
|
||||
IsActive = request.IsActive,
|
||||
Created = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_context.DiscountCategories.Add(category);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return category.Id;
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.CreateDiscountCategory;
|
||||
|
||||
public class CreateDiscountCategoryCommandValidator : AbstractValidator<CreateDiscountCategoryCommand>
|
||||
{
|
||||
public CreateDiscountCategoryCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.Name)
|
||||
.NotEmpty().WithMessage("نام دستهبندی الزامی است")
|
||||
.MaximumLength(100).WithMessage("نام دستهبندی نباید بیشتر از 100 کاراکتر باشد");
|
||||
|
||||
RuleFor(x => x.Title)
|
||||
.NotEmpty().WithMessage("عنوان دستهبندی الزامی است")
|
||||
.MaximumLength(200).WithMessage("عنوان دستهبندی نباید بیشتر از 200 کاراکتر باشد");
|
||||
|
||||
RuleFor(x => x.Description)
|
||||
.MaximumLength(1000).WithMessage("توضیحات نباید بیشتر از 1000 کاراکتر باشد")
|
||||
.When(x => !string.IsNullOrEmpty(x.Description));
|
||||
|
||||
RuleFor(x => x.ImagePath)
|
||||
.MaximumLength(500).WithMessage("مسیر تصویر نباید بیشتر از 500 کاراکتر باشد")
|
||||
.When(x => !string.IsNullOrEmpty(x.ImagePath));
|
||||
|
||||
RuleFor(x => x.ParentCategoryId)
|
||||
.GreaterThan(0).WithMessage("شناسه دستهبندی والد باید مثبت باشد")
|
||||
.When(x => x.ParentCategoryId.HasValue);
|
||||
|
||||
RuleFor(x => x.SortOrder)
|
||||
.GreaterThanOrEqualTo(0).WithMessage("ترتیب نمایش نمیتواند منفی باشد");
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.CreateDiscountProduct;
|
||||
|
||||
public class CreateDiscountProductCommand : IRequest<long>
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string ShortInfomation { get; set; }
|
||||
public string FullInformation { get; set; }
|
||||
public long Price { get; set; }
|
||||
public int MaxDiscountPercent { get; set; }
|
||||
public string ImagePath { get; set; }
|
||||
public string ThumbnailPath { get; set; }
|
||||
public int RemainingCount { get; set; }
|
||||
public List<long> CategoryIds { get; set; } = new();
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities.DiscountShop;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.CreateDiscountProduct;
|
||||
|
||||
public class CreateDiscountProductCommandHandler : IRequestHandler<CreateDiscountProductCommand, long>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public CreateDiscountProductCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<long> Handle(CreateDiscountProductCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var product = new DiscountProduct
|
||||
{
|
||||
Title = request.Title,
|
||||
ShortInfomation = request.ShortInfomation,
|
||||
FullInformation = request.FullInformation,
|
||||
Price = request.Price,
|
||||
MaxDiscountPercent = request.MaxDiscountPercent,
|
||||
ImagePath = request.ImagePath,
|
||||
ThumbnailPath = request.ThumbnailPath,
|
||||
RemainingCount = request.RemainingCount,
|
||||
Rate = 0,
|
||||
SaleCount = 0,
|
||||
ViewCount = 0,
|
||||
IsActive = true
|
||||
};
|
||||
|
||||
_context.DiscountProducts.Add(product);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// Add product categories
|
||||
if (request.CategoryIds.Any())
|
||||
{
|
||||
var productCategories = request.CategoryIds.Select(categoryId => new DiscountProductCategory
|
||||
{
|
||||
ProductId = product.Id,
|
||||
CategoryId = categoryId
|
||||
}).ToList();
|
||||
|
||||
_context.DiscountProductCategories.AddRange(productCategories);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
return product.Id;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.CreateDiscountProduct;
|
||||
|
||||
public class CreateDiscountProductCommandValidator : AbstractValidator<CreateDiscountProductCommand>
|
||||
{
|
||||
public CreateDiscountProductCommandValidator()
|
||||
{
|
||||
RuleFor(v => v.Title)
|
||||
.NotEmpty().WithMessage("عنوان محصول الزامی است")
|
||||
.MaximumLength(200).WithMessage("عنوان محصول نمیتواند بیشتر از 200 کاراکتر باشد");
|
||||
|
||||
RuleFor(v => v.ShortInfomation)
|
||||
.NotEmpty().WithMessage("توضیحات کوتاه الزامی است")
|
||||
.MaximumLength(500).WithMessage("توضیحات کوتاه نمیتواند بیشتر از 500 کاراکتر باشد");
|
||||
|
||||
RuleFor(v => v.FullInformation)
|
||||
.NotEmpty().WithMessage("توضیحات کامل الزامی است")
|
||||
.MaximumLength(2000).WithMessage("توضیحات کامل نمیتواند بیشتر از 2000 کاراکتر باشد");
|
||||
|
||||
RuleFor(v => v.Price)
|
||||
.GreaterThan(0).WithMessage("قیمت باید بیشتر از صفر باشد");
|
||||
|
||||
RuleFor(v => v.MaxDiscountPercent)
|
||||
.InclusiveBetween(0, 100).WithMessage("درصد تخفیف باید بین 0 تا 100 باشد");
|
||||
|
||||
RuleFor(v => v.RemainingCount)
|
||||
.GreaterThanOrEqualTo(0).WithMessage("موجودی نمیتواند منفی باشد");
|
||||
|
||||
RuleFor(v => v.ImagePath)
|
||||
.NotEmpty().WithMessage("تصویر محصول الزامی است");
|
||||
|
||||
RuleFor(v => v.ThumbnailPath)
|
||||
.NotEmpty().WithMessage("تصویر بندانگشتی الزامی است");
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.DeleteDiscountCategory;
|
||||
|
||||
public class DeleteDiscountCategoryCommand : IRequest<bool>
|
||||
{
|
||||
public long CategoryId { get; set; }
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.DeleteDiscountCategory;
|
||||
|
||||
public class DeleteDiscountCategoryCommandHandler : IRequestHandler<DeleteDiscountCategoryCommand, bool>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public DeleteDiscountCategoryCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(DeleteDiscountCategoryCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var category = await _context.DiscountCategories
|
||||
.Include(c => c.ChildCategories)
|
||||
.Include(c => c.ProductCategories)
|
||||
.FirstOrDefaultAsync(c => c.Id == request.CategoryId, cancellationToken);
|
||||
|
||||
if (category == null)
|
||||
{
|
||||
throw new Exception($"Discount category with ID {request.CategoryId} not found");
|
||||
}
|
||||
|
||||
// Check if category has child categories
|
||||
if (category.ChildCategories.Any())
|
||||
{
|
||||
throw new Exception($"Cannot delete category. It has {category.ChildCategories.Count} child categories. Please delete child categories first.");
|
||||
}
|
||||
|
||||
// Check if category has products
|
||||
if (category.ProductCategories.Any())
|
||||
{
|
||||
throw new Exception($"Cannot delete category. It has {category.ProductCategories.Count} products. Please move or delete products first.");
|
||||
}
|
||||
|
||||
_context.DiscountCategories.Remove(category);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.DeleteDiscountProduct;
|
||||
|
||||
public class DeleteDiscountProductCommand : IRequest<bool>
|
||||
{
|
||||
public long ProductId { get; set; }
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.DeleteDiscountProduct;
|
||||
|
||||
public class DeleteDiscountProductCommandHandler : IRequestHandler<DeleteDiscountProductCommand, bool>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public DeleteDiscountProductCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(DeleteDiscountProductCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var product = await _context.DiscountProducts
|
||||
.FirstOrDefaultAsync(p => p.Id == request.ProductId, cancellationToken);
|
||||
|
||||
if (product == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// حذف رابطههای دستهبندی
|
||||
var productCategories = await _context.DiscountProductCategories
|
||||
.Where(pc => pc.ProductId == request.ProductId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
_context.DiscountProductCategories.RemoveRange(productCategories);
|
||||
|
||||
// حذف محصول
|
||||
_context.DiscountProducts.Remove(product);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.PlaceOrder;
|
||||
|
||||
public class PlaceOrderCommand : IRequest<PlaceOrderResponseDto>
|
||||
{
|
||||
public long UserId { get; set; }
|
||||
public long UserAddressId { get; set; }
|
||||
public long DiscountBalanceToUse { get; set; }
|
||||
}
|
||||
|
||||
public class PlaceOrderResponseDto
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
public long? OrderId { get; set; }
|
||||
public long? TransactionId { get; set; }
|
||||
public long TotalAmount { get; set; }
|
||||
public long DiscountBalanceUsed { get; set; }
|
||||
public long GatewayAmountRequired { get; set; }
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities.DiscountShop;
|
||||
using CMSMicroservice.Domain.Entities.Payment;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.PlaceOrder;
|
||||
|
||||
public class PlaceOrderCommandHandler : IRequestHandler<PlaceOrderCommand, PlaceOrderResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public PlaceOrderCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<PlaceOrderResponseDto> Handle(PlaceOrderCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// Get user wallet
|
||||
var userWallet = await _context.UserWallets
|
||||
.FirstOrDefaultAsync(w => w.UserId == request.UserId, cancellationToken);
|
||||
|
||||
if (userWallet == null)
|
||||
{
|
||||
return new PlaceOrderResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = "کیف پول کاربر یافت نشد"
|
||||
};
|
||||
}
|
||||
|
||||
// Get cart items with products
|
||||
var cartItems = await _context.DiscountShoppingCarts
|
||||
.Where(c => c.UserId == request.UserId)
|
||||
.Include(c => c.Product)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (!cartItems.Any())
|
||||
{
|
||||
return new PlaceOrderResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = "سبد خرید خالی است"
|
||||
};
|
||||
}
|
||||
|
||||
// Validate stock and calculate totals
|
||||
long totalAmount = 0;
|
||||
long totalDiscountAmount = 0;
|
||||
var orderDetails = new List<DiscountOrderDetail>();
|
||||
|
||||
foreach (var cartItem in cartItems)
|
||||
{
|
||||
var product = cartItem.Product;
|
||||
|
||||
// Check stock
|
||||
if (product.RemainingCount < cartItem.Count)
|
||||
{
|
||||
return new PlaceOrderResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = $"موجودی محصول '{product.Title}' کافی نیست"
|
||||
};
|
||||
}
|
||||
|
||||
// Check if product is active
|
||||
if (!product.IsActive)
|
||||
{
|
||||
return new PlaceOrderResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = $"محصول '{product.Title}' غیرفعال است"
|
||||
};
|
||||
}
|
||||
|
||||
// Calculate discount for this product
|
||||
var itemTotal = product.Price * cartItem.Count;
|
||||
var maxDiscountForItem = (itemTotal * product.MaxDiscountPercent) / 100;
|
||||
|
||||
totalAmount += itemTotal;
|
||||
totalDiscountAmount += maxDiscountForItem;
|
||||
|
||||
orderDetails.Add(new DiscountOrderDetail
|
||||
{
|
||||
ProductId = product.Id,
|
||||
Count = cartItem.Count,
|
||||
UnitPrice = product.Price,
|
||||
DiscountPercentUsed = product.MaxDiscountPercent,
|
||||
DiscountAmount = maxDiscountForItem,
|
||||
FinalPrice = itemTotal - maxDiscountForItem
|
||||
});
|
||||
}
|
||||
|
||||
// Validate discount balance usage
|
||||
var maxDiscountBalanceUsable = totalDiscountAmount;
|
||||
var actualDiscountBalanceUsed = Math.Min(request.DiscountBalanceToUse, maxDiscountBalanceUsable);
|
||||
actualDiscountBalanceUsed = Math.Min(actualDiscountBalanceUsed, userWallet.DiscountBalance);
|
||||
|
||||
if (actualDiscountBalanceUsed < request.DiscountBalanceToUse)
|
||||
{
|
||||
return new PlaceOrderResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = $"موجودی تخفیف کافی نیست. حداکثر قابل استفاده: {maxDiscountBalanceUsable:N0} تومان، موجودی شما: {userWallet.DiscountBalance:N0} تومان"
|
||||
};
|
||||
}
|
||||
|
||||
var gatewayAmountRequired = totalAmount - actualDiscountBalanceUsed;
|
||||
|
||||
// Calculate VAT (9%)
|
||||
var vatAmount = (gatewayAmountRequired * 9) / 100;
|
||||
var finalGatewayAmount = gatewayAmountRequired + vatAmount;
|
||||
|
||||
// Create transaction for gateway payment
|
||||
var transaction = new Transaction
|
||||
{
|
||||
Amount = finalGatewayAmount,
|
||||
Description = $"خرید از فروشگاه تخفیف - مبلغ کل: {totalAmount:N0}، اعتبار تخفیف: {actualDiscountBalanceUsed:N0}",
|
||||
PaymentStatus = PaymentStatus.Pending,
|
||||
Type = TransactionType.DiscountShopPurchase
|
||||
};
|
||||
|
||||
_context.Transactions.Add(transaction);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// Create order
|
||||
var order = new DiscountOrder
|
||||
{
|
||||
UserId = request.UserId,
|
||||
TotalAmount = totalAmount,
|
||||
DiscountBalanceUsed = actualDiscountBalanceUsed,
|
||||
GatewayAmountPaid = finalGatewayAmount,
|
||||
VatAmount = vatAmount,
|
||||
PaymentStatus = PaymentStatus.Pending,
|
||||
TransactionId = transaction.Id,
|
||||
UserAddressId = request.UserAddressId,
|
||||
DeliveryStatus = DeliveryStatus.Pending
|
||||
};
|
||||
|
||||
_context.DiscountOrders.Add(order);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// Add order details
|
||||
foreach (var detail in orderDetails)
|
||||
{
|
||||
detail.DiscountOrderId = order.Id;
|
||||
}
|
||||
|
||||
_context.DiscountOrderDetails.AddRange(orderDetails);
|
||||
|
||||
// Clear cart
|
||||
_context.DiscountShoppingCarts.RemoveRange(cartItems);
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new PlaceOrderResponseDto
|
||||
{
|
||||
Success = true,
|
||||
Message = "سفارش ایجاد شد. لطفا پرداخت را تکمیل کنید",
|
||||
OrderId = order.Id,
|
||||
TransactionId = transaction.Id,
|
||||
TotalAmount = totalAmount,
|
||||
DiscountBalanceUsed = actualDiscountBalanceUsed,
|
||||
GatewayAmountRequired = finalGatewayAmount
|
||||
};
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.PlaceOrder;
|
||||
|
||||
public class PlaceOrderCommandValidator : AbstractValidator<PlaceOrderCommand>
|
||||
{
|
||||
public PlaceOrderCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.UserId)
|
||||
.GreaterThan(0).WithMessage("شناسه کاربر باید مثبت باشد");
|
||||
|
||||
RuleFor(x => x.UserAddressId)
|
||||
.GreaterThan(0).WithMessage("آدرس تحویل باید انتخاب شود");
|
||||
|
||||
RuleFor(x => x.DiscountBalanceToUse)
|
||||
.GreaterThanOrEqualTo(0).WithMessage("مبلغ استفاده از موجودی تخفیف نمیتواند منفی باشد");
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.RemoveFromCart;
|
||||
|
||||
public class RemoveFromCartCommand : IRequest<RemoveFromCartResponseDto>
|
||||
{
|
||||
public long UserId { get; set; }
|
||||
public long ProductId { get; set; }
|
||||
}
|
||||
|
||||
public class RemoveFromCartResponseDto
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.RemoveFromCart;
|
||||
|
||||
public class RemoveFromCartCommandHandler : IRequestHandler<RemoveFromCartCommand, RemoveFromCartResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public RemoveFromCartCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<RemoveFromCartResponseDto> Handle(RemoveFromCartCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var cartItem = await _context.DiscountShoppingCarts
|
||||
.FirstOrDefaultAsync(c => c.UserId == request.UserId && c.ProductId == request.ProductId, cancellationToken);
|
||||
|
||||
if (cartItem == null)
|
||||
{
|
||||
return new RemoveFromCartResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = "محصول در سبد خرید یافت نشد"
|
||||
};
|
||||
}
|
||||
|
||||
_context.DiscountShoppingCarts.Remove(cartItem);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new RemoveFromCartResponseDto
|
||||
{
|
||||
Success = true,
|
||||
Message = "محصول از سبد خرید حذف شد"
|
||||
};
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.RemoveFromCart;
|
||||
|
||||
public class RemoveFromCartCommandValidator : AbstractValidator<RemoveFromCartCommand>
|
||||
{
|
||||
public RemoveFromCartCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.UserId)
|
||||
.GreaterThan(0).WithMessage("شناسه کاربر باید مثبت باشد");
|
||||
|
||||
RuleFor(x => x.ProductId)
|
||||
.GreaterThan(0).WithMessage("شناسه محصول باید مثبت باشد");
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.UpdateCartItemCount;
|
||||
|
||||
public class UpdateCartItemCountCommand : IRequest<UpdateCartItemCountResponseDto>
|
||||
{
|
||||
public long UserId { get; set; }
|
||||
public long ProductId { get; set; }
|
||||
public int NewCount { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateCartItemCountResponseDto
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.UpdateCartItemCount;
|
||||
|
||||
public class UpdateCartItemCountCommandHandler : IRequestHandler<UpdateCartItemCountCommand, UpdateCartItemCountResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public UpdateCartItemCountCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<UpdateCartItemCountResponseDto> Handle(UpdateCartItemCountCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// پیدا کردن آیتم سبد خرید
|
||||
var cartItem = await _context.DiscountShoppingCarts
|
||||
.Include(c => c.Product)
|
||||
.FirstOrDefaultAsync(c => c.UserId == request.UserId && c.ProductId == request.ProductId, cancellationToken);
|
||||
|
||||
if (cartItem == null)
|
||||
{
|
||||
return new UpdateCartItemCountResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = "آیتم در سبد خرید یافت نشد"
|
||||
};
|
||||
}
|
||||
|
||||
// بررسی موجودی محصول
|
||||
if (request.NewCount > cartItem.Product.RemainingCount)
|
||||
{
|
||||
return new UpdateCartItemCountResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = $"موجودی محصول کافی نیست. موجودی فعلی: {cartItem.Product.RemainingCount}"
|
||||
};
|
||||
}
|
||||
|
||||
// اگر تعداد جدید صفر یا منفی باشد، آیتم را حذف کن
|
||||
if (request.NewCount <= 0)
|
||||
{
|
||||
_context.DiscountShoppingCarts.Remove(cartItem);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new UpdateCartItemCountResponseDto
|
||||
{
|
||||
Success = true,
|
||||
Message = "محصول از سبد خرید حذف شد"
|
||||
};
|
||||
}
|
||||
|
||||
// بهروزرسانی تعداد
|
||||
cartItem.Count = request.NewCount;
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new UpdateCartItemCountResponseDto
|
||||
{
|
||||
Success = true,
|
||||
Message = "تعداد محصول بهروزرسانی شد"
|
||||
};
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.UpdateCartItemCount;
|
||||
|
||||
public class UpdateCartItemCountCommandValidator : AbstractValidator<UpdateCartItemCountCommand>
|
||||
{
|
||||
public UpdateCartItemCountCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.UserId)
|
||||
.GreaterThan(0).WithMessage("شناسه کاربر باید مثبت باشد");
|
||||
|
||||
RuleFor(x => x.ProductId)
|
||||
.GreaterThan(0).WithMessage("شناسه محصول باید مثبت باشد");
|
||||
|
||||
RuleFor(x => x.NewCount)
|
||||
.GreaterThanOrEqualTo(0).WithMessage("تعداد نمیتواند منفی باشد");
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.UpdateDiscountCategory;
|
||||
|
||||
public class UpdateDiscountCategoryCommand : IRequest<bool>
|
||||
{
|
||||
public long CategoryId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? ImagePath { get; set; }
|
||||
public long? ParentCategoryId { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.UpdateDiscountCategory;
|
||||
|
||||
public class UpdateDiscountCategoryCommandHandler : IRequestHandler<UpdateDiscountCategoryCommand, bool>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public UpdateDiscountCategoryCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(UpdateDiscountCategoryCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var category = await _context.DiscountCategories
|
||||
.FirstOrDefaultAsync(c => c.Id == request.CategoryId, cancellationToken);
|
||||
|
||||
if (category == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// بررسی وجود دستهبندی دیگری با همین نام (به جز خودش)
|
||||
var duplicateName = await _context.DiscountCategories
|
||||
.AnyAsync(c => c.Name == request.Name && c.Id != request.CategoryId, cancellationToken);
|
||||
|
||||
if (duplicateName)
|
||||
{
|
||||
throw new InvalidOperationException("دستهبندی دیگری با این نام وجود دارد");
|
||||
}
|
||||
|
||||
// بررسی عدم ایجاد حلقه در سلسله مراتب
|
||||
if (request.ParentCategoryId.HasValue)
|
||||
{
|
||||
if (request.ParentCategoryId.Value == request.CategoryId)
|
||||
{
|
||||
throw new InvalidOperationException("دستهبندی نمیتواند والد خودش باشد");
|
||||
}
|
||||
|
||||
// بررسی وجود دستهبندی والد
|
||||
var parentExists = await _context.DiscountCategories
|
||||
.AnyAsync(c => c.Id == request.ParentCategoryId.Value, cancellationToken);
|
||||
|
||||
if (!parentExists)
|
||||
{
|
||||
throw new InvalidOperationException("دستهبندی والد یافت نشد");
|
||||
}
|
||||
|
||||
// بررسی اینکه والد جدید زیرمجموعه این دستهبندی نباشد
|
||||
var isDescendant = await IsDescendant(request.ParentCategoryId.Value, request.CategoryId, cancellationToken);
|
||||
if (isDescendant)
|
||||
{
|
||||
throw new InvalidOperationException("دستهبندی والد نمیتواند زیرمجموعه این دستهبندی باشد");
|
||||
}
|
||||
}
|
||||
|
||||
category.Name = request.Name;
|
||||
category.Title = request.Title;
|
||||
category.Description = request.Description;
|
||||
category.ImagePath = request.ImagePath;
|
||||
category.ParentCategoryId = request.ParentCategoryId;
|
||||
category.SortOrder = request.SortOrder;
|
||||
category.IsActive = request.IsActive;
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> IsDescendant(long potentialDescendantId, long ancestorId, CancellationToken cancellationToken)
|
||||
{
|
||||
var category = await _context.DiscountCategories
|
||||
.FirstOrDefaultAsync(c => c.Id == potentialDescendantId, cancellationToken);
|
||||
|
||||
if (category == null || !category.ParentCategoryId.HasValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (category.ParentCategoryId.Value == ancestorId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return await IsDescendant(category.ParentCategoryId.Value, ancestorId, cancellationToken);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.UpdateDiscountCategory;
|
||||
|
||||
public class UpdateDiscountCategoryCommandValidator : AbstractValidator<UpdateDiscountCategoryCommand>
|
||||
{
|
||||
public UpdateDiscountCategoryCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.CategoryId)
|
||||
.GreaterThan(0).WithMessage("شناسه دستهبندی باید مثبت باشد");
|
||||
|
||||
RuleFor(x => x.Name)
|
||||
.NotEmpty().WithMessage("نام دستهبندی الزامی است")
|
||||
.MaximumLength(100).WithMessage("نام دستهبندی نباید بیشتر از 100 کاراکتر باشد");
|
||||
|
||||
RuleFor(x => x.Title)
|
||||
.NotEmpty().WithMessage("عنوان دستهبندی الزامی است")
|
||||
.MaximumLength(200).WithMessage("عنوان دستهبندی نباید بیشتر از 200 کاراکتر باشد");
|
||||
|
||||
RuleFor(x => x.Description)
|
||||
.MaximumLength(1000).WithMessage("توضیحات نباید بیشتر از 1000 کاراکتر باشد")
|
||||
.When(x => !string.IsNullOrEmpty(x.Description));
|
||||
|
||||
RuleFor(x => x.ImagePath)
|
||||
.MaximumLength(500).WithMessage("مسیر تصویر نباید بیشتر از 500 کاراکتر باشد")
|
||||
.When(x => !string.IsNullOrEmpty(x.ImagePath));
|
||||
|
||||
RuleFor(x => x.ParentCategoryId)
|
||||
.GreaterThan(0).WithMessage("شناسه دستهبندی والد باید مثبت باشد")
|
||||
.When(x => x.ParentCategoryId.HasValue);
|
||||
|
||||
RuleFor(x => x.SortOrder)
|
||||
.GreaterThanOrEqualTo(0).WithMessage("ترتیب نمایش نمیتواند منفی باشد");
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.UpdateDiscountProduct;
|
||||
|
||||
public class UpdateDiscountProductCommand : IRequest<Unit>
|
||||
{
|
||||
public long ProductId { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string ShortInfomation { get; set; }
|
||||
public string FullInformation { get; set; }
|
||||
public long Price { get; set; }
|
||||
public int MaxDiscountPercent { get; set; }
|
||||
public string ImagePath { get; set; }
|
||||
public string ThumbnailPath { get; set; }
|
||||
public int RemainingCount { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public List<long> CategoryIds { get; set; } = new();
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities.DiscountShop;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.UpdateDiscountProduct;
|
||||
|
||||
public class UpdateDiscountProductCommandHandler : IRequestHandler<UpdateDiscountProductCommand, Unit>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public UpdateDiscountProductCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Unit> Handle(UpdateDiscountProductCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var product = await _context.DiscountProducts
|
||||
.FirstOrDefaultAsync(p => p.Id == request.ProductId, cancellationToken);
|
||||
|
||||
if (product == null)
|
||||
throw new Exception("محصول یافت نشد");
|
||||
|
||||
product.Title = request.Title;
|
||||
product.ShortInfomation = request.ShortInfomation;
|
||||
product.FullInformation = request.FullInformation;
|
||||
product.Price = request.Price;
|
||||
product.MaxDiscountPercent = request.MaxDiscountPercent;
|
||||
product.ImagePath = request.ImagePath;
|
||||
product.ThumbnailPath = request.ThumbnailPath;
|
||||
product.RemainingCount = request.RemainingCount;
|
||||
product.IsActive = request.IsActive;
|
||||
|
||||
// Update categories
|
||||
var existingCategories = await _context.DiscountProductCategories
|
||||
.Where(pc => pc.ProductId == request.ProductId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
_context.DiscountProductCategories.RemoveRange(existingCategories);
|
||||
|
||||
if (request.CategoryIds.Any())
|
||||
{
|
||||
var newCategories = request.CategoryIds.Select(categoryId => new DiscountProductCategory
|
||||
{
|
||||
ProductId = product.Id,
|
||||
CategoryId = categoryId
|
||||
}).ToList();
|
||||
|
||||
_context.DiscountProductCategories.AddRange(newCategories);
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return Unit.Value;
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.UpdateDiscountProduct;
|
||||
|
||||
public class UpdateDiscountProductCommandValidator : AbstractValidator<UpdateDiscountProductCommand>
|
||||
{
|
||||
public UpdateDiscountProductCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.ProductId)
|
||||
.GreaterThan(0).WithMessage("شناسه محصول باید مثبت باشد");
|
||||
|
||||
RuleFor(x => x.Title)
|
||||
.NotEmpty().WithMessage("عنوان محصول الزامی است")
|
||||
.MaximumLength(200).WithMessage("عنوان محصول نباید بیشتر از 200 کاراکتر باشد");
|
||||
|
||||
RuleFor(x => x.ShortInfomation)
|
||||
.NotEmpty().WithMessage("توضیحات کوتاه الزامی است")
|
||||
.MaximumLength(500).WithMessage("توضیحات کوتاه نباید بیشتر از 500 کاراکتر باشد");
|
||||
|
||||
RuleFor(x => x.FullInformation)
|
||||
.NotEmpty().WithMessage("توضیحات کامل الزامی است")
|
||||
.MaximumLength(5000).WithMessage("توضیحات کامل نباید بیشتر از 5000 کاراکتر باشد");
|
||||
|
||||
RuleFor(x => x.Price)
|
||||
.GreaterThan(0).WithMessage("قیمت باید بزرگتر از صفر باشد");
|
||||
|
||||
RuleFor(x => x.MaxDiscountPercent)
|
||||
.InclusiveBetween(0, 100).WithMessage("درصد تخفیف باید بین 0 تا 100 باشد");
|
||||
|
||||
RuleFor(x => x.ImagePath)
|
||||
.NotEmpty().WithMessage("مسیر تصویر اصلی الزامی است")
|
||||
.MaximumLength(500).WithMessage("مسیر تصویر نباید بیشتر از 500 کاراکتر باشد");
|
||||
|
||||
RuleFor(x => x.ThumbnailPath)
|
||||
.NotEmpty().WithMessage("مسیر تصویر بندانگشتی الزامی است")
|
||||
.MaximumLength(500).WithMessage("مسیر تصویر نباید بیشتر از 500 کاراکتر باشد");
|
||||
|
||||
RuleFor(x => x.RemainingCount)
|
||||
.GreaterThanOrEqualTo(0).WithMessage("موجودی نمیتواند منفی باشد");
|
||||
|
||||
RuleFor(x => x.CategoryIds)
|
||||
.NotEmpty().WithMessage("حداقل یک دستهبندی باید انتخاب شود")
|
||||
.Must(ids => ids.All(id => id > 0)).WithMessage("شناسه دستهبندیها باید مثبت باشند");
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.UpdateOrderStatus;
|
||||
|
||||
public class UpdateOrderStatusCommand : IRequest<UpdateOrderStatusResponseDto>
|
||||
{
|
||||
public long OrderId { get; set; }
|
||||
public DeliveryStatus DeliveryStatus { get; set; }
|
||||
public string? TrackingCode { get; set; }
|
||||
public string? AdminNotes { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateOrderStatusResponseDto
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.UpdateOrderStatus;
|
||||
|
||||
public class UpdateOrderStatusCommandHandler : IRequestHandler<UpdateOrderStatusCommand, UpdateOrderStatusResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public UpdateOrderStatusCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<UpdateOrderStatusResponseDto> Handle(UpdateOrderStatusCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var order = await _context.DiscountOrders
|
||||
.FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken);
|
||||
|
||||
if (order == null)
|
||||
{
|
||||
return new UpdateOrderStatusResponseDto
|
||||
{
|
||||
Success = false,
|
||||
Message = "سفارش یافت نشد"
|
||||
};
|
||||
}
|
||||
|
||||
// بهروزرسانی وضعیت
|
||||
order.DeliveryStatus = request.DeliveryStatus;
|
||||
|
||||
if (!string.IsNullOrEmpty(request.TrackingCode))
|
||||
{
|
||||
order.TrackingCode = request.TrackingCode;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new UpdateOrderStatusResponseDto
|
||||
{
|
||||
Success = true,
|
||||
Message = "وضعیت سفارش بهروزرسانی شد"
|
||||
};
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace CMSMicroservice.Application.DiscountShopCQ.Commands.UpdateOrderStatus;
|
||||
|
||||
public class UpdateOrderStatusCommandValidator : AbstractValidator<UpdateOrderStatusCommand>
|
||||
{
|
||||
public UpdateOrderStatusCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.OrderId)
|
||||
.GreaterThan(0).WithMessage("شناسه سفارش باید مثبت باشد");
|
||||
|
||||
RuleFor(x => x.DeliveryStatus)
|
||||
.IsInEnum().WithMessage("وضعیت ارسال نامعتبر است");
|
||||
|
||||
RuleFor(x => x.TrackingCode)
|
||||
.MaximumLength(50).WithMessage("کد رهگیری نباید بیشتر از 50 کاراکتر باشد")
|
||||
.When(x => !string.IsNullOrEmpty(x.TrackingCode));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user