@ -0,0 +1,7 @@ | |||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; | |||
public class CouponData | |||
{ | |||
public int Discount { get; set; } | |||
public string Code { get; set; } | |||
} |
@ -0,0 +1,28 @@ | |||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; | |||
public class CouponService : ICouponService | |||
{ | |||
public readonly HttpClient _httpClient; | |||
private readonly UrlsConfig _urls; | |||
private readonly ILogger<CouponService> _logger; | |||
public CouponService(HttpClient httpClient, IOptions<UrlsConfig> config, ILogger<CouponService> logger) | |||
{ | |||
_urls = config.Value; | |||
_httpClient = httpClient; | |||
_logger = logger; | |||
} | |||
public async Task<HttpResponseMessage> CheckCouponByCodeNumberAsync(string codeNumber) | |||
{ | |||
_logger.LogInformation("Call coupon api with codenumber: {codeNumber}", codeNumber); | |||
var url = new Uri($"{_urls.Catalog}/api/v1/coupon/{codeNumber}"); | |||
var response = await _httpClient.GetAsync(url); | |||
_logger.LogInformation("Coupon api response: {@response}", response); | |||
return response; | |||
} | |||
} |
@ -0,0 +1,6 @@ | |||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; | |||
public interface ICouponService | |||
{ | |||
Task<HttpResponseMessage> CheckCouponByCodeNumberAsync(string codeNumber); | |||
} |
@ -0,0 +1,17 @@ | |||
namespace Ordering.API.Application.Commands; | |||
public class CouponConfirmedCommand : IRequest<bool> | |||
{ | |||
[DataMember] | |||
public int OrderNumber { get; private set; } | |||
[DataMember] | |||
public int Discount { get; private set; } | |||
public CouponConfirmedCommand(int orderNumber, int discount) | |||
{ | |||
OrderNumber = orderNumber; | |||
Discount = discount; | |||
} | |||
} |
@ -0,0 +1,41 @@ | |||
namespace Ordering.API.Application.Commands; | |||
public class CouponConfirmedCommandHandler : IRequestHandler<CouponConfirmedCommand, bool> | |||
{ | |||
private readonly IOrderRepository _orderRepository; | |||
public CouponConfirmedCommandHandler(IOrderRepository orderRepository) | |||
{ | |||
_orderRepository = orderRepository; | |||
} | |||
public async Task<bool> Handle(CouponConfirmedCommand command, CancellationToken cancellationToken) | |||
{ | |||
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); | |||
if (orderToUpdate == null) | |||
{ | |||
return false; | |||
} | |||
orderToUpdate.ProcessCouponConfirmed(); | |||
return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); | |||
} | |||
} | |||
public class CouponConfirmIdenfifiedCommandHandler : IdentifiedCommandHandler<CouponConfirmedCommand, bool> | |||
{ | |||
public CouponConfirmIdenfifiedCommandHandler( | |||
IMediator mediator, | |||
IRequestManager requestManager, | |||
ILogger<IdentifiedCommandHandler<CouponConfirmedCommand, bool>> logger) | |||
: base(mediator, requestManager, logger) | |||
{ | |||
} | |||
protected override bool CreateResultForDuplicateRequest() | |||
{ | |||
return true; // Ignore duplicate requests for processing order. | |||
} | |||
} |
@ -0,0 +1,37 @@ | |||
using Ordering.API.Application.IntegrationEvents.Events; | |||
using Ordering.Domain.Events; | |||
namespace Ordering.API.Application.DomainEventHandlers.OrderCoupon; | |||
public class OrderStatusChangedToAwaitingCouponValidationDomainEventHandler : INotificationHandler<OrderStatusChangedToAwaitingCouponValidationDomainEvent> | |||
{ | |||
private readonly IOrderRepository _orderRepository; | |||
private readonly IBuyerRepository _buyerRepository; | |||
private readonly ILoggerFactory _logger; | |||
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | |||
public OrderStatusChangedToAwaitingCouponValidationDomainEventHandler( | |||
IOrderRepository orderRepository, | |||
IBuyerRepository buyerRepository, | |||
ILoggerFactory logger, | |||
IOrderingIntegrationEventService orderingIntegrationEventService) | |||
{ | |||
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | |||
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); | |||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | |||
_orderingIntegrationEventService = orderingIntegrationEventService; | |||
} | |||
public async Task Handle(OrderStatusChangedToAwaitingCouponValidationDomainEvent domainEvent, CancellationToken cancellationToken) | |||
{ | |||
_logger.CreateLogger<OrderStatusChangedToAwaitingCouponValidationDomainEventHandler>() | |||
.LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", domainEvent.OrderId, nameof(OrderStatus.StockConfirmed), OrderStatus.StockConfirmed.Id); | |||
var order = await _orderRepository.GetAsync(domainEvent.OrderId); | |||
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); | |||
var integrationEvent = new OrderStatusChangedToAwaitingCouponValidationIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name, order.DiscountCode); | |||
await _orderingIntegrationEventService.AddAndSaveEventAsync(integrationEvent); | |||
} | |||
} |
@ -0,0 +1,32 @@ | |||
using Ordering.API.Application.Commands; | |||
using Ordering.API.Application.IntegrationEvents.Events; | |||
namespace Ordering.API.Application.IntegrationEvents.EventHandling; | |||
public class OrderCouponConfirmedIntegrationEventHandler : IIntegrationEventHandler<OrderCouponConfirmedIntegrationEvent> | |||
{ | |||
private readonly IMediator _mediator; | |||
public OrderCouponConfirmedIntegrationEventHandler(IMediator mediator) | |||
{ | |||
_mediator = mediator; | |||
} | |||
public async Task Handle(OrderCouponConfirmedIntegrationEvent @event) | |||
{ | |||
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) | |||
{ | |||
Log.Information("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); | |||
var command = new CouponConfirmedCommand(@event.OrderId, @event.Discount); | |||
Log.Information("----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", | |||
command.GetGenericTypeName(), | |||
nameof(command.OrderNumber), | |||
command.OrderNumber, | |||
command); | |||
await _mediator.Send(command); | |||
} | |||
} | |||
} |
@ -0,0 +1,33 @@ | |||
using Ordering.API.Application.IntegrationEvents.Events; | |||
namespace Ordering.API.Application.IntegrationEvents.EventHandling; | |||
public class OrderCouponRejectedIntegrationEventHandler : IIntegrationEventHandler<OrderCouponRejectedIntegrationEvent> | |||
{ | |||
private readonly IMediator _mediator; | |||
public OrderCouponRejectedIntegrationEventHandler(IMediator mediator) | |||
{ | |||
_mediator = mediator; | |||
} | |||
public async Task Handle(OrderCouponRejectedIntegrationEvent @event) | |||
{ | |||
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) | |||
{ | |||
Log.Information("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); | |||
Log.Warning("Discount failed, cancelling order {OrderId}", @event.OrderId); | |||
var command = new CancelOrderCommand(@event.OrderId); | |||
Log.Information("----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", | |||
command.GetGenericTypeName(), | |||
nameof(command.OrderNumber), | |||
command.OrderNumber, | |||
command); | |||
await _mediator.Send(command); | |||
} | |||
} | |||
} |
@ -0,0 +1,12 @@ | |||
using Newtonsoft.Json; | |||
namespace Ordering.API.Application.IntegrationEvents.Events; | |||
public record OrderCouponConfirmedIntegrationEvent : IntegrationEvent | |||
{ | |||
[JsonProperty] | |||
public int OrderId { get; private set; } | |||
[JsonProperty] | |||
public int Discount { get; private set; } | |||
} |
@ -0,0 +1,12 @@ | |||
using Newtonsoft.Json; | |||
namespace Ordering.API.Application.IntegrationEvents.Events; | |||
public record OrderCouponRejectedIntegrationEvent : IntegrationEvent | |||
{ | |||
[JsonProperty] | |||
public int OrderId { get; private set; } | |||
[JsonProperty] | |||
public string Code { get; private set; } | |||
} |
@ -0,0 +1,20 @@ | |||
namespace Ordering.API.Application.IntegrationEvents.Events; | |||
public record OrderStatusChangedToAwaitingCouponValidationIntegrationEvent : IntegrationEvent | |||
{ | |||
public int OrderId { get; } | |||
public string OrderStatus { get; } | |||
public string BuyerName { get; } | |||
public string Code { get; set; } | |||
public OrderStatusChangedToAwaitingCouponValidationIntegrationEvent(int orderId, string orderStatus, string buyerName, string code) | |||
{ | |||
OrderId = orderId; | |||
OrderStatus = orderStatus; | |||
BuyerName = buyerName; | |||
Code = code; | |||
} | |||
} |
@ -0,0 +1,19 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Ordering.Domain.Events; | |||
public class OrderStatusChangedToAwaitingCouponValidationDomainEvent : INotification | |||
{ | |||
public int OrderId { get; } | |||
public string Code { get; set; } | |||
public OrderStatusChangedToAwaitingCouponValidationDomainEvent(int orderId, string code) | |||
{ | |||
OrderId = orderId; | |||
Code = code; | |||
} | |||
} |
@ -0,0 +1,30 @@ | |||
using Ordering.SignalrHub.IntegrationEvents.Events; | |||
namespace Ordering.SignalrHub.IntegrationEvents.EventHandling; | |||
public class OrderStatusChangedToAwaitingCouponValidationIntegrationEventHandler : IIntegrationEventHandler<OrderStatusChangedToAwaitingCouponValidationIntegrationEvent> | |||
{ | |||
private readonly IHubContext<NotificationsHub> _hubContext; | |||
private readonly ILogger<OrderStatusChangedToAwaitingCouponValidationIntegrationEventHandler> _logger; | |||
public OrderStatusChangedToAwaitingCouponValidationIntegrationEventHandler( | |||
IHubContext<NotificationsHub> hubContext, | |||
ILogger<OrderStatusChangedToAwaitingCouponValidationIntegrationEventHandler> logger) | |||
{ | |||
_hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); | |||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | |||
} | |||
public async Task Handle(OrderStatusChangedToAwaitingCouponValidationIntegrationEvent @event) | |||
{ | |||
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) | |||
{ | |||
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); | |||
await _hubContext.Clients | |||
.Group(@event.BuyerName) | |||
.SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); | |||
} | |||
} | |||
} |
@ -0,0 +1,17 @@ | |||
using Newtonsoft.Json; | |||
namespace Ordering.SignalrHub.IntegrationEvents.Events; | |||
public record OrderStatusChangedToAwaitingCouponValidationIntegrationEvent : IntegrationEvent | |||
{ | |||
[JsonProperty] | |||
public int OrderId { get; private set; } | |||
[JsonProperty] | |||
public string OrderStatus { get; private set; } | |||
[JsonProperty] | |||
public string BuyerName { get; private set; } | |||
[JsonProperty] | |||
public string Code { get; private set; } | |||
} |