add order completion flow to ordering api
This commit is contained in:
parent
0b77c37210
commit
c70b02aa9c
@ -0,0 +1,12 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||
|
||||
public class CompleteOrderCommand : IRequest<bool>
|
||||
{
|
||||
[DataMember]
|
||||
public int OrderNumber { get; private set; }
|
||||
|
||||
public CompleteOrderCommand(int orderNumber)
|
||||
{
|
||||
OrderNumber = orderNumber;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||
|
||||
// Regular CommandHandler
|
||||
public class CompleteOrderCommandHandler : IRequestHandler<CompleteOrderCommand, bool>
|
||||
{
|
||||
private readonly IOrderRepository _orderRepository;
|
||||
|
||||
public CompleteOrderCommandHandler(IOrderRepository orderRepository)
|
||||
{
|
||||
_orderRepository = orderRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler which processes the command when
|
||||
/// shipment company requests to complete order from API
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> Handle(CompleteOrderCommand command, CancellationToken cancellationToken)
|
||||
{
|
||||
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber);
|
||||
|
||||
if (orderToUpdate == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
orderToUpdate.SetCompletedStatus();
|
||||
|
||||
return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Use for Idempotency in Command process
|
||||
public class CompleteOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CompleteOrderCommand, bool>
|
||||
{
|
||||
public CompleteOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager, ILogger<IdentifiedCommandHandler<CompleteOrderCommand, bool>> logger)
|
||||
: base(mediator, requestManager, logger)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool CreateResultForDuplicateRequest()
|
||||
{
|
||||
return true; // Ignore duplicate requests for processing order.
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers;
|
||||
|
||||
public partial class OrderCompletedDomainEventHandler : INotificationHandler<OrderCompletedDomainEvent>
|
||||
{
|
||||
private readonly IOrderRepository _orderRepository;
|
||||
private readonly IBuyerRepository _buyerRepository;
|
||||
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public OrderCompletedDomainEventHandler(IOrderRepository orderRepository, ILogger<OrderCompletedDomainEventHandler> logger, IBuyerRepository buyerRepository, IOrderingIntegrationEventService orderingIntegrationEventService)
|
||||
{
|
||||
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository));
|
||||
_orderingIntegrationEventService = orderingIntegrationEventService;
|
||||
}
|
||||
|
||||
public async Task Handle(OrderCompletedDomainEvent domainEvent, CancellationToken cancellationToken)
|
||||
{
|
||||
OrderingApiTrace.LogOrderStatusUpdated(_logger, domainEvent.Order.Id, nameof(OrderStatus.Completed), OrderStatus.Completed.Id);
|
||||
|
||||
var order = await _orderRepository.GetAsync(domainEvent.Order.Id);
|
||||
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString());
|
||||
|
||||
var integrationEvent = new OrderStatusChangedToCompletedIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name);
|
||||
|
||||
await _orderingIntegrationEventService.AddAndSaveEventAsync(integrationEvent);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events;
|
||||
|
||||
public record OrderStatusChangedToCompletedIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public int OrderId { get; }
|
||||
public string OrderStatus { get; }
|
||||
public string BuyerName { get; }
|
||||
|
||||
public OrderStatusChangedToCompletedIntegrationEvent(int orderId, string orderStatus, string buyerName)
|
||||
{
|
||||
OrderId = orderId;
|
||||
OrderStatus = orderStatus;
|
||||
BuyerName = buyerName;
|
||||
}
|
||||
}
|
@ -3,5 +3,5 @@
|
||||
public interface IOrderingIntegrationEventService
|
||||
{
|
||||
Task PublishEventsThroughEventBusAsync(Guid transactionId);
|
||||
Task AddAndSaveEventAsync(IntegrationEvent evt);
|
||||
Task AddAndSaveEventAsync(IntegrationEvent integrationEvent);
|
||||
}
|
||||
|
@ -44,10 +44,10 @@ public class OrderingIntegrationEventService : IOrderingIntegrationEventService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AddAndSaveEventAsync(IntegrationEvent evt)
|
||||
public async Task AddAndSaveEventAsync(IntegrationEvent integrationEvent)
|
||||
{
|
||||
_logger.LogInformation("Enqueuing integration event {IntegrationEventId} to repository ({@IntegrationEvent})", evt.Id, evt);
|
||||
_logger.LogInformation("Enqueuing integration event {IntegrationEventId} to repository ({@IntegrationEvent})", integrationEvent.Id, integrationEvent);
|
||||
|
||||
await _eventLogService.SaveEventAsync(evt, _orderingContext.GetCurrentTransaction());
|
||||
await _eventLogService.SaveEventAsync(integrationEvent, _orderingContext.GetCurrentTransaction());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations;
|
||||
|
||||
public class CompleteOrderCommandValidator : AbstractValidator<CompleteOrderCommand>
|
||||
{
|
||||
public CompleteOrderCommandValidator(ILogger<CompleteOrderCommandValidator> logger)
|
||||
{
|
||||
RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found");
|
||||
|
||||
logger.LogTrace("INSTANCE CREATED - {ClassName}", GetType().Name);
|
||||
}
|
||||
}
|
@ -138,4 +138,34 @@ public class OrdersController : ControllerBase
|
||||
|
||||
return await _mediator.Send(createOrderDraftCommand);
|
||||
}
|
||||
|
||||
[Route("complete")]
|
||||
[HttpPut]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
public async Task<IActionResult> CompleteOrderAsync([FromBody] CompleteOrderCommand command, [FromHeader(Name = "x-requestid")] string requestId)
|
||||
{
|
||||
bool commandResult = false;
|
||||
|
||||
if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty)
|
||||
{
|
||||
var requestCompleteOrder = new IdentifiedCommand<CompleteOrderCommand, bool>(command, guid);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
||||
requestCompleteOrder.GetGenericTypeName(),
|
||||
nameof(requestCompleteOrder.Command.OrderNumber),
|
||||
requestCompleteOrder.Command.OrderNumber,
|
||||
requestCompleteOrder);
|
||||
|
||||
commandResult = await _mediator.Send(requestCompleteOrder);
|
||||
}
|
||||
|
||||
if (!commandResult)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ services.AddSingleton<IValidator<CancelOrderCommand>, CancelOrderCommandValidato
|
||||
services.AddSingleton<IValidator<CreateOrderCommand>, CreateOrderCommandValidator>();
|
||||
services.AddSingleton<IValidator<IdentifiedCommand<CreateOrderCommand, bool>>, IdentifiedCommandValidator>();
|
||||
services.AddSingleton<IValidator<ShipOrderCommand>, ShipOrderCommandValidator>();
|
||||
services.AddSingleton<IValidator<CompleteOrderCommand>, CompleteOrderCommandValidator>();
|
||||
|
||||
services.AddScoped<IOrderQueries>(sp => new OrderQueries(builder.Configuration.GetConnectionString("OrderingDB")));
|
||||
services.AddScoped<IBuyerRepository, BuyerRepository>();
|
||||
|
@ -4,4 +4,5 @@ AwaitingValidation
|
||||
StockConfirmed
|
||||
Paid
|
||||
Shipped
|
||||
Cancelled
|
||||
Cancelled
|
||||
Completed
|
|
@ -190,4 +190,17 @@ public class Order
|
||||
{
|
||||
return _orderItems.Sum(o => o.GetUnits() * o.GetUnitPrice());
|
||||
}
|
||||
|
||||
public void SetCompletedStatus()
|
||||
{
|
||||
if (_orderStatusId != OrderStatus.Shipped.Id)
|
||||
{
|
||||
StatusChangeException(OrderStatus.Completed);
|
||||
}
|
||||
|
||||
_orderStatusId = OrderStatus.Completed.Id;
|
||||
_description = $"The order was {OrderStatus.Completed}.";
|
||||
|
||||
AddDomainEvent(new OrderCompletedDomainEvent(this));
|
||||
}
|
||||
}
|
||||
|
@ -5,12 +5,13 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
|
||||
public class OrderStatus
|
||||
: Enumeration
|
||||
{
|
||||
public static OrderStatus Submitted = new OrderStatus(1, nameof(Submitted).ToLowerInvariant());
|
||||
public static OrderStatus AwaitingValidation = new OrderStatus(2, nameof(AwaitingValidation).ToLowerInvariant());
|
||||
public static OrderStatus StockConfirmed = new OrderStatus(3, nameof(StockConfirmed).ToLowerInvariant());
|
||||
public static OrderStatus Paid = new OrderStatus(4, nameof(Paid).ToLowerInvariant());
|
||||
public static OrderStatus Shipped = new OrderStatus(5, nameof(Shipped).ToLowerInvariant());
|
||||
public static OrderStatus Cancelled = new OrderStatus(6, nameof(Cancelled).ToLowerInvariant());
|
||||
public static OrderStatus Submitted = new(1, nameof(Submitted).ToLowerInvariant());
|
||||
public static OrderStatus AwaitingValidation = new(2, nameof(AwaitingValidation).ToLowerInvariant());
|
||||
public static OrderStatus StockConfirmed = new(3, nameof(StockConfirmed).ToLowerInvariant());
|
||||
public static OrderStatus Paid = new(4, nameof(Paid).ToLowerInvariant());
|
||||
public static OrderStatus Shipped = new(5, nameof(Shipped).ToLowerInvariant());
|
||||
public static OrderStatus Cancelled = new(6, nameof(Cancelled).ToLowerInvariant());
|
||||
public static OrderStatus Completed = new(7, nameof(Completed).ToLowerInvariant());
|
||||
|
||||
public OrderStatus(int id, string name)
|
||||
: base(id, name)
|
||||
|
@ -0,0 +1,11 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events;
|
||||
|
||||
public class OrderCompletedDomainEvent : INotification
|
||||
{
|
||||
public Order Order { get; }
|
||||
|
||||
public OrderCompletedDomainEvent(Order order)
|
||||
{
|
||||
Order = order;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user