// EN: Handler for CreateOrderCommand. // VI: Handler cho CreateOrderCommand. using MediatR; using OrderService.API.Hubs; using OrderService.Domain.AggregatesModel.OrderAggregate; using OrderService.Domain.Strategies; namespace OrderService.API.Application.Commands; /// /// EN: Handler for creating a new order with strategy-based validation. /// VI: Handler tạo order mới với validation qua strategy. /// public class CreateOrderCommandHandler : IRequestHandler { private readonly IOrderRepository _orderRepository; private readonly IEnumerable _strategies; private readonly IPosNotificationService _posNotificationService; private readonly ILogger _logger; public CreateOrderCommandHandler( IOrderRepository orderRepository, IEnumerable strategies, IPosNotificationService posNotificationService, ILogger logger) { _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); _strategies = strategies ?? throw new ArgumentNullException(nameof(strategies)); _posNotificationService = posNotificationService ?? throw new ArgumentNullException(nameof(posNotificationService)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task Handle( CreateOrderCommand request, CancellationToken cancellationToken) { _logger.LogInformation( "EN: Creating order for shop {ShopId} / VI: Tạo order cho shop {ShopId}", request.ShopId); // EN: Create order aggregate // VI: Tạo order aggregate var order = new Order(request.ShopId, request.CustomerId, request.TableId); // EN: Add items to order // VI: Thêm items vào order foreach (var itemRequest in request.Items) { var orderItem = new OrderItem( itemRequest.ProductId, itemRequest.ProductName, itemRequest.ProductType, itemRequest.Quantity, itemRequest.UnitPrice, trackInventory: itemRequest.TrackInventory); order.AddItem(orderItem); } // EN: Validate all items through their strategies // VI: Validate tất cả items qua strategies của chúng foreach (var item in order.Items) { var strategy = GetStrategy(item.ProductType); var isValid = await strategy.ValidateAsync(item, request.ShopId, cancellationToken); if (!isValid) { throw new InvalidOperationException( $"Validation failed for item {item.ProductName} (type: {item.ProductType})"); } } // EN: Execute all items via their strategies (e.g., create kitchen tickets for PreparedFood) // VI: Thực thi tất cả items qua strategies (vd: tạo phiếu bếp cho PreparedFood) foreach (var item in order.Items) { var strategy = GetStrategy(item.ProductType); await strategy.ExecuteAsync(item, request.ShopId, cancellationToken); } // EN: Apply discount if provided // VI: Áp dụng giảm giá nếu có if (request.DiscountAmount is > 0) { order.ApplyDiscount(request.DiscountAmount.Value, request.DiscountType, request.DiscountReference); } // EN: Mark order as validated after all items pass validation // VI: Đánh dấu order là validated sau khi tất cả items pass validation order.MarkAsValidated(); // EN: Save order // VI: Lưu order _orderRepository.Add(order); await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); _logger.LogInformation( "EN: Order created successfully / VI: Tạo order thành công: {OrderId}", order.Id); // EN: Send real-time notification to POS/KDS clients // VI: Gửi thông báo real-time đến POS/KDS clients try { var orderNotification = new OrderNotificationDto( OrderId: order.Id, ShopId: order.ShopId, Status: order.Status.Name, Items: order.Items.Select(i => new OrderItemNotificationDto( ItemId: i.Id, ProductId: i.ProductId, ProductName: i.ProductName, ProductType: i.ProductType, Quantity: i.Quantity, UnitPrice: i.UnitPrice, TotalPrice: i.TotalPrice, Status: i.Status )).ToList().AsReadOnly(), TotalAmount: order.TotalAmount, CustomerId: order.CustomerId, TableId: order.TableId, CreatedAt: order.CreatedAt, UpdatedAt: order.UpdatedAt ); await _posNotificationService.NotifyOrderCreatedAsync( order.ShopId, orderNotification, cancellationToken); } catch (Exception ex) { // EN: Don't fail the command if notification fails // VI: Không fail command nếu notification thất bại _logger.LogWarning(ex, "EN: Failed to send POS notification for order {OrderId} / " + "VI: Gửi thông báo POS thất bại cho order {OrderId}", order.Id); } return new CreateOrderResult( order.Id, order.TotalAmount, order.Status.Name); } private ILineItemStrategy GetStrategy(string productType) { var strategy = _strategies.FirstOrDefault(s => s.SupportedType == productType); if (strategy == null) { throw new InvalidOperationException( $"EN: No strategy found for product type / VI: Không tìm thấy strategy cho loại sản phẩm: {productType}"); } return strategy; } }