使用分布式事件实现订单支付库存通知
This commit is contained in:
parent
c16d6ece15
commit
d47ff517ad
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using HelloShop.OrderingService.DistributedEvents.Events;
|
||||||
|
using HelloShop.OrderingService.Entities.Orders;
|
||||||
|
using HelloShop.OrderingService.Infrastructure;
|
||||||
|
using HelloShop.ServiceDefaults.DistributedEvents.Abstractions;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace HelloShop.OrderingService.DistributedEvents.EventHandling
|
||||||
|
{
|
||||||
|
public class OrderPaymentSucceededDistributedEventHandler(OrderingServiceDbContext dbContext, IDistributedEventBus distributedEventBus) : IDistributedEventHandler<OrderPaymentSucceededDistributedEvent>
|
||||||
|
{
|
||||||
|
public async Task HandleAsync(OrderPaymentSucceededDistributedEvent @event)
|
||||||
|
{
|
||||||
|
var strategy = dbContext.Database.CreateExecutionStrategy();
|
||||||
|
|
||||||
|
await strategy.ExecuteAsync(async () =>
|
||||||
|
{
|
||||||
|
await using var transaction = await dbContext.Database.BeginTransactionAsync();
|
||||||
|
|
||||||
|
DbSet<Order> orders = dbContext.Set<Order>();
|
||||||
|
|
||||||
|
Order order = await orders.FindAsync(@event.OrderId) ?? throw new Exception($"Order with id {@event.OrderId} not found");
|
||||||
|
|
||||||
|
await orders.Entry(order).Collection(i => i.OrderItems).LoadAsync();
|
||||||
|
|
||||||
|
order.OrderStatus = OrderStatus.Paid;
|
||||||
|
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
await transaction.CommitAsync();
|
||||||
|
|
||||||
|
var orderStockList = order.OrderItems.Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.Units));
|
||||||
|
|
||||||
|
var integrationEvent = new OrderPaidDistributedEvent(order.Id, orderStockList);
|
||||||
|
|
||||||
|
await distributedEventBus.PublishAsync(integrationEvent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using HelloShop.ServiceDefaults.DistributedEvents.Abstractions;
|
||||||
|
|
||||||
|
namespace HelloShop.OrderingService.DistributedEvents.Events
|
||||||
|
{
|
||||||
|
public record OrderPaidDistributedEvent(int OrderId, IEnumerable<OrderStockItem> OrderStockItems) : DistributedEvent;
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using HelloShop.ServiceDefaults.DistributedEvents.Abstractions;
|
||||||
|
|
||||||
|
namespace HelloShop.OrderingService.DistributedEvents.Events
|
||||||
|
{
|
||||||
|
public record OrderPaymentFailedDistributedEvent : DistributedEvent
|
||||||
|
{
|
||||||
|
public int OrderId { get; }
|
||||||
|
|
||||||
|
public OrderPaymentFailedDistributedEvent(int orderId) => OrderId = orderId;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using HelloShop.ServiceDefaults.DistributedEvents.Abstractions;
|
||||||
|
|
||||||
|
namespace HelloShop.OrderingService.DistributedEvents.Events
|
||||||
|
{
|
||||||
|
public record OrderPaymentSucceededDistributedEvent(int OrderId) : DistributedEvent;
|
||||||
|
}
|
@ -47,6 +47,7 @@ namespace HelloShop.OrderingService.Extensions
|
|||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddHostedService<GracePeriodWorker>();
|
builder.Services.AddHostedService<GracePeriodWorker>();
|
||||||
|
builder.Services.AddHostedService<PaymentWorker>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WebApplication MapApplicationEndpoints(this WebApplication app)
|
public static WebApplication MapApplicationEndpoints(this WebApplication app)
|
||||||
|
50
src/HelloShop.OrderingService/Workers/PaymentWorker.cs
Normal file
50
src/HelloShop.OrderingService/Workers/PaymentWorker.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using HelloShop.OrderingService.DistributedEvents.Events;
|
||||||
|
using HelloShop.OrderingService.Entities.Orders;
|
||||||
|
using HelloShop.OrderingService.Infrastructure;
|
||||||
|
using HelloShop.ServiceDefaults.DistributedEvents.Abstractions;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace HelloShop.OrderingService.Workers
|
||||||
|
{
|
||||||
|
public class PaymentWorker(IServiceScopeFactory serviceScopeFactory, ILogger<PaymentWorker> logger) : BackgroundService
|
||||||
|
{
|
||||||
|
protected async override Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
|
||||||
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
if (logger.IsEnabled(LogLevel.Information))
|
||||||
|
{
|
||||||
|
logger.LogInformation("{Worker} background task is doing background work.", GetType().Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var scope = serviceScopeFactory.CreateAsyncScope();
|
||||||
|
var dbContext = scope.ServiceProvider.GetRequiredService<OrderingServiceDbContext>();
|
||||||
|
var distributedEventBus = scope.ServiceProvider.GetRequiredService<IDistributedEventBus>();
|
||||||
|
|
||||||
|
bool paymentSucceeded = Random.Shared.NextDouble() > 0.3;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var orders = await dbContext.Set<Order>().Include(o => o.OrderItems).Where(o => o.OrderStatus == OrderStatus.StockConfirmed).ToListAsync(stoppingToken);
|
||||||
|
|
||||||
|
foreach (var order in orders)
|
||||||
|
{
|
||||||
|
DistributedEvent @event = paymentSucceeded ? new OrderPaymentSucceededDistributedEvent(order.Id) : new OrderPaymentFailedDistributedEvent(order.Id);
|
||||||
|
|
||||||
|
await distributedEventBus.PublishAsync(@event, stoppingToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "Error occurred executing {Worker} background task.", GetType().Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using HelloShop.ProductService.DistributedEvents.Events;
|
||||||
|
using HelloShop.ProductService.Entities.Products;
|
||||||
|
using HelloShop.ProductService.Infrastructure;
|
||||||
|
using HelloShop.ServiceDefaults.DistributedEvents.Abstractions;
|
||||||
|
|
||||||
|
namespace HelloShop.ProductService.DistributedEvents.EventHandling
|
||||||
|
{
|
||||||
|
public class OrderPaidDistributedEventHandler(ProductServiceDbContext dbContext) : IDistributedEventHandler<OrderPaidDistributedEvent>
|
||||||
|
{
|
||||||
|
public async Task HandleAsync(OrderPaidDistributedEvent @event)
|
||||||
|
{
|
||||||
|
foreach (var orderStockItem in @event.OrderStockItems)
|
||||||
|
{
|
||||||
|
var product = await dbContext.Set<Product>().FindAsync(orderStockItem.ProductId) ?? throw new Exception($"Product with id {orderStockItem.ProductId} not found");
|
||||||
|
|
||||||
|
product.AvailableStock -= orderStockItem.Units;
|
||||||
|
}
|
||||||
|
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using HelloShop.ServiceDefaults.DistributedEvents.Abstractions;
|
||||||
|
|
||||||
|
namespace HelloShop.ProductService.DistributedEvents.Events
|
||||||
|
{
|
||||||
|
public record OrderPaidDistributedEvent(int OrderId, IEnumerable<OrderStockItem> OrderStockItems) : DistributedEvent;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user