feat: Implement Public Message Management Commands and Queries

- Add GetUserPackageStatusQueryValidator for user package status validation.
- Create ArchiveMessageCommand and ArchiveMessageCommandHandler for archiving public messages.
- Implement ArchiveMessageCommandValidator to validate message ID.
- Introduce PublishMessageCommand and PublishMessageCommandHandler for publishing messages.
- Add PublishMessageCommandValidator for validating publish message requests.
- Implement GetPublicMessageQuery and GetPublicMessageQueryHandler for retrieving public messages.
- Create GetPublicMessageQueryValidator for validating public message requests.
- Add ApplyDiscountToOrderCommand and ApplyDiscountToOrderCommandHandler for applying discounts to orders.
- Implement ApplyDiscountToOrderCommandValidator for validating discount application requests.
- Create UpdateOrderStatusCommand and UpdateOrderStatusCommandHandler for changing order statuses.
- Implement UpdateOrderStatusCommandValidator for validating order status updates.
- Add CalculateOrderPVQuery and CalculateOrderPVQueryHandler for calculating order PV.
- Implement CalculateOrderPVQueryValidator for validating PV calculation requests.
- Create GetOrdersByDateRangeQuery and GetOrdersByDateRangeQueryHandler for retrieving orders by date range.
- Implement GetOrdersByDateRangeQueryValidator for validating date range queries.
- Add PublicMessage entity to represent public messages in the system.
- Implement PublicMessageService for handling public message operations via gRPC.
This commit is contained in:
masoodafar-web
2025-12-04 03:43:19 +03:30
parent 84f642e900
commit ba6d74fe35
45 changed files with 1777 additions and 22 deletions
@@ -0,0 +1,39 @@
namespace CMSMicroservice.Application.UserOrderCQ.Commands.ApplyDiscountToOrder;
/// <summary>
/// اعمال تخفیف به سفارش
/// </summary>
public record ApplyDiscountToOrderCommand : IRequest<ApplyDiscountToOrderResponseDto>
{
/// <summary>
/// شناسه سفارش
/// </summary>
public long OrderId { get; init; }
/// <summary>
/// مبلغ تخفیف (ریال)
/// </summary>
public long DiscountAmount { get; init; }
/// <summary>
/// دلیل تخفیف
/// </summary>
public string Reason { get; init; } = string.Empty;
/// <summary>
/// کد تخفیف (اختیاری)
/// </summary>
public string? DiscountCode { get; init; }
}
/// <summary>
/// پاسخ اعمال تخفیف
/// </summary>
public class ApplyDiscountToOrderResponseDto
{
public bool Success { get; set; }
public string Message { get; set; } = string.Empty;
public long OriginalAmount { get; set; }
public long DiscountAmount { get; set; }
public long FinalAmount { get; set; }
}
@@ -0,0 +1,74 @@
using CMSMicroservice.Application.Common.Interfaces;
namespace CMSMicroservice.Application.UserOrderCQ.Commands.ApplyDiscountToOrder;
public class ApplyDiscountToOrderCommandHandler : IRequestHandler<ApplyDiscountToOrderCommand, ApplyDiscountToOrderResponseDto>
{
private readonly IApplicationDbContext _context;
private readonly ILogger<ApplyDiscountToOrderCommandHandler> _logger;
public ApplyDiscountToOrderCommandHandler(
IApplicationDbContext context,
ILogger<ApplyDiscountToOrderCommandHandler> logger)
{
_context = context;
_logger = logger;
}
public async Task<ApplyDiscountToOrderResponseDto> Handle(ApplyDiscountToOrderCommand request, CancellationToken cancellationToken)
{
// TODO: پیاده‌سازی اعمال تخفیف به سفارش
// 1. پیدا کردن سفارش:
// - var order = await _context.UserOrders.FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken)
// - بررسی null و پرتاب NotFoundException
//
// 2. بررسی شرایط اعمال تخفیف:
// - سفارش نباید Delivered یا Cancelled باشد
// - مبلغ تخفیف نباید بیشتر از Amount باشد
// - if (order.DeliveryStatus == DeliveryStatus.Delivered || order.DeliveryStatus == DeliveryStatus.Cancelled)
// throw new InvalidOperationException("نمی‌توان به این سفارش تخفیف اعمال کرد")
// - if (request.DiscountAmount > order.Amount)
// throw new InvalidOperationException("مبلغ تخفیف نمی‌تواند بیشتر از مبلغ سفارش باشد")
//
// 3. محاسبه مبلغ نهایی:
// - var originalAmount = order.Amount
// - var newDiscountedPrice = order.Amount - request.DiscountAmount
// - مطمئن شوید که منفی نشود: newDiscountedPrice = Math.Max(0, newDiscountedPrice)
//
// 4. به‌روزرسانی سفارش:
// - order.DiscountedPrice = newDiscountedPrice
// - اگر فیلد OrderDiscountAmount وجود دارد، آن را هم به‌روز کنید
// - order.OrderDiscountAmount = request.DiscountAmount
// - اضافه کردن به توضیحات:
// order.DeliveryDescription = (order.DeliveryDescription ?? "") +
// $"\nتخفیف اعمال شده: {request.DiscountAmount} ریال - دلیل: {request.Reason}"
//
// 5. ذخیره Log تخفیف (اختیاری - اگر جدول OrderDiscountLog دارید):
// - var discountLog = new OrderDiscountLog {
// OrderId = order.Id,
// DiscountAmount = request.DiscountAmount,
// Reason = request.Reason,
// DiscountCode = request.DiscountCode,
// AppliedAt = DateTime.UtcNow
// }
// - await _context.OrderDiscountLogs.AddAsync(discountLog, cancellationToken)
//
// 6. ذخیره و Log:
// - await _context.SaveChangesAsync(cancellationToken)
// - _logger.LogInformation("Discount {Amount} applied to order {OrderId}: {Reason}",
// request.DiscountAmount, request.OrderId, request.Reason)
//
// 7. برگشت Response:
// - return new ApplyDiscountToOrderResponseDto {
// Success = true,
// Message = "تخفیف با موفقیت اعمال شد",
// OriginalAmount = originalAmount,
// DiscountAmount = request.DiscountAmount,
// FinalAmount = newDiscountedPrice
// }
//
// نکته: این تخفیف برای تخفیفات دستی Admin است و جدا از تخفیف‌های محصول
throw new NotImplementedException("ApplyDiscountToOrder needs implementation");
}
}
@@ -0,0 +1,21 @@
namespace CMSMicroservice.Application.UserOrderCQ.Commands.ApplyDiscountToOrder;
public class ApplyDiscountToOrderCommandValidator : AbstractValidator<ApplyDiscountToOrderCommand>
{
public ApplyDiscountToOrderCommandValidator()
{
RuleFor(x => x.OrderId)
.GreaterThan(0)
.WithMessage("شناسه سفارش باید بزرگتر از 0 باشد");
RuleFor(x => x.DiscountAmount)
.GreaterThan(0)
.WithMessage("مبلغ تخفیف باید بزرگتر از 0 باشد");
RuleFor(x => x.Reason)
.NotEmpty()
.WithMessage("دلیل تخفیف الزامی است")
.MaximumLength(500)
.WithMessage("دلیل تخفیف نمی‌تواند بیشتر از 500 کاراکتر باشد");
}
}
@@ -0,0 +1,34 @@
using CMSMicroservice.Domain.Enums;
namespace CMSMicroservice.Application.UserOrderCQ.Commands.UpdateOrderStatus;
/// <summary>
/// تغییر وضعیت سفارش
/// </summary>
public record UpdateOrderStatusCommand : IRequest<UpdateOrderStatusResponseDto>
{
/// <summary>
/// شناسه سفارش
/// </summary>
public long OrderId { get; init; }
/// <summary>
/// وضعیت تحویل جدید
/// </summary>
public DeliveryStatus NewStatus { get; init; }
/// <summary>
/// توضیحات (اختیاری)
/// </summary>
public string? Description { get; init; }
}
/// <summary>
/// پاسخ تغییر وضعیت سفارش
/// </summary>
public class UpdateOrderStatusResponseDto
{
public bool Success { get; set; }
public string Message { get; set; } = string.Empty;
public DeliveryStatus CurrentStatus { get; set; }
}
@@ -0,0 +1,53 @@
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Enums;
namespace CMSMicroservice.Application.UserOrderCQ.Commands.UpdateOrderStatus;
public class UpdateOrderStatusCommandHandler : IRequestHandler<UpdateOrderStatusCommand, UpdateOrderStatusResponseDto>
{
private readonly IApplicationDbContext _context;
private readonly ILogger<UpdateOrderStatusCommandHandler> _logger;
public UpdateOrderStatusCommandHandler(
IApplicationDbContext context,
ILogger<UpdateOrderStatusCommandHandler> logger)
{
_context = context;
_logger = logger;
}
public async Task<UpdateOrderStatusResponseDto> Handle(UpdateOrderStatusCommand request, CancellationToken cancellationToken)
{
// TODO: پیاده‌سازی تغییر وضعیت سفارش
// 1. پیدا کردن سفارش:
// - await _context.UserOrders.FirstOrDefaultAsync(o => o.Id == request.OrderId)
// - بررسی null و پرتاب NotFoundException
//
// 2. بررسی‌های انتقال وضعیت (State Transition Validation):
// - نمی‌توان از Delivered به Cancelled رفت
// - نمی‌توان از Cancelled به سایر وضعیت‌ها رفت
// - الگوی معمول: Pending → Processing → Shipped → Delivered
// - Cancelled می‌تواند از Pending, Processing, Shipped باشد
//
// 3. تغییر وضعیت:
// - order.DeliveryStatus = request.NewStatus
// - اگر Description داریم: order.DeliveryDescription = request.Description
// - تنظیم تاریخ‌های مربوطه:
// * اگر NewStatus == Delivered → order.DeliveredAt = DateTime.UtcNow
// * اگر NewStatus == Shipped → order.ShippedAt = DateTime.UtcNow
// * اگر NewStatus == Processing → order.ProcessedAt = DateTime.UtcNow
//
// 4. ذخیره و Log:
// - await _context.SaveChangesAsync(cancellationToken)
// - _logger.LogInformation("Order {OrderId} status changed to {NewStatus}", request.OrderId, request.NewStatus)
//
// 5. برگشت Response:
// - Success = true
// - Message = "وضعیت سفارش با موفقیت تغییر کرد"
// - CurrentStatus = order.DeliveryStatus
//
// نکته: برای validation دقیق‌تر، می‌توان یک State Machine برای انتقال‌های مجاز تعریف کرد
throw new NotImplementedException("UpdateOrderStatus needs implementation");
}
}
@@ -0,0 +1,17 @@
using CMSMicroservice.Domain.Enums;
namespace CMSMicroservice.Application.UserOrderCQ.Commands.UpdateOrderStatus;
public class UpdateOrderStatusCommandValidator : AbstractValidator<UpdateOrderStatusCommand>
{
public UpdateOrderStatusCommandValidator()
{
RuleFor(x => x.OrderId)
.GreaterThan(0)
.WithMessage("شناسه سفارش باید بزرگتر از 0 باشد");
RuleFor(x => x.NewStatus)
.IsInEnum()
.WithMessage("وضعیت تحویل نامعتبر است");
}
}