From 284f345b32f369b5b8d993ab32169a6da05b1d1b Mon Sep 17 00:00:00 2001 From: gdlcf88 Date: Sun, 12 Jun 2022 05:05:56 +0800 Subject: [PATCH] Auto cancel and refund order in case of insufficient inventory --- .../EasyAbp/EShop/Orders/OrdersConsts.cs | 1 + .../ProductInventoryReductionEventHandler.cs | 72 ++++++++++++++++--- .../Dtos/CreateEShopRefundItemInput.cs | 16 +---- .../Refunds/CreateEShopRefundItemInfoModel.cs | 22 ++++++ .../EShop/Payments/Refunds/RefundOrderEto.cs | 34 +++++++++ .../Refunds/RefundOrderEventHandler.cs | 65 +++++++++++++++++ 6 files changed, 187 insertions(+), 23 deletions(-) create mode 100644 modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/CreateEShopRefundItemInfoModel.cs create mode 100644 modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/RefundOrderEto.cs create mode 100644 modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain/EasyAbp/EShop/Payments/Refunds/RefundOrderEventHandler.cs diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/OrdersConsts.cs b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/OrdersConsts.cs index df729533..44ea4412 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/OrdersConsts.cs +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/OrdersConsts.cs @@ -3,5 +3,6 @@ public static class OrdersConsts { public static string UnpaidAutoCancellationReason = "Order payment timed out and not paid"; + public static string InventoryReductionFailedAutoCancellationReason = "Insufficient inventory"; } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/ProductInventoryReductionEventHandler.cs b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/ProductInventoryReductionEventHandler.cs index 35c07fe5..2e126164 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/ProductInventoryReductionEventHandler.cs +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/ProductInventoryReductionEventHandler.cs @@ -1,6 +1,12 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using EasyAbp.EShop.Payments.Refunds; using EasyAbp.EShop.Products.Products; +using EasyAbp.PaymentService.Refunds; +using Volo.Abp.Data; using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; using Volo.Abp.MultiTenancy; using Volo.Abp.Timing; using Volo.Abp.Uow; @@ -11,18 +17,24 @@ namespace EasyAbp.EShop.Orders.Orders { private readonly IClock _clock; private readonly ICurrentTenant _currentTenant; + private readonly IOrderManager _orderManager; private readonly IOrderRepository _orderRepository; + private readonly IDistributedEventBus _distributedEventBus; public ProductInventoryReductionEventHandler( IClock clock, ICurrentTenant currentTenant, - IOrderRepository orderRepository) + IOrderManager orderManager, + IOrderRepository orderRepository, + IDistributedEventBus distributedEventBus) { _clock = clock; _currentTenant = currentTenant; + _orderManager = orderManager; _orderRepository = orderRepository; + _distributedEventBus = distributedEventBus; } - + [UnitOfWork(true)] public virtual async Task HandleEventAsync(ProductInventoryReductionAfterOrderPlacedResultEto eventData) { @@ -37,10 +49,11 @@ namespace EasyAbp.EShop.Orders.Orders if (!eventData.IsSuccess) { - // Todo: Cancel order. + await _orderManager.CancelAsync(order, OrdersConsts.InventoryReductionFailedAutoCancellationReason); + return; } - + order.SetReducedInventoryAfterPlacingTime(_clock.Now); await _orderRepository.UpdateAsync(order, true); @@ -61,15 +74,58 @@ namespace EasyAbp.EShop.Orders.Orders if (!eventData.IsSuccess) { - // Todo: Refund. - // Todo: Cancel order. + var refundOrderEto = CreateRefundOrderEto(order); + + await _orderManager.CancelAsync(order, OrdersConsts.InventoryReductionFailedAutoCancellationReason); + + await RefundOrderAsync(refundOrderEto); + return; } - + order.SetReducedInventoryAfterPaymentTime(_clock.Now); await _orderRepository.UpdateAsync(order, true); } } + + [UnitOfWork(true)] + protected virtual async Task RefundOrderAsync(RefundOrderEto refundOrderEto) + { + await _distributedEventBus.PublishAsync(refundOrderEto); + } + + protected virtual RefundOrderEto CreateRefundOrderEto(Order order) + { + if (!order.PaymentId.HasValue) + { + throw new OrderIsInWrongStageException(order.Id); + } + + var eto = new RefundOrderEto( + order.TenantId, + order.Id, + order.StoreId, + order.PaymentId.Value, + OrdersConsts.InventoryReductionFailedAutoCancellationReason, + OrdersConsts.InventoryReductionFailedAutoCancellationReason, + OrdersConsts.InventoryReductionFailedAutoCancellationReason); + + eto.OrderLines.AddRange(order.OrderLines.Select(x => new OrderLineRefundInfoModel + { + OrderLineId = x.Id, + Quantity = x.Quantity - x.RefundedQuantity, + TotalAmount = x.ActualTotalPrice - x.RefundAmount + })); + + eto.OrderExtraFees.AddRange(order.OrderExtraFees.Select(x => new OrderExtraFeeRefundInfoModel + { + Name = x.Name, + Key = x.Key, + TotalAmount = x.Fee - x.RefundAmount + })); + + return eto; + } } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Application.Contracts/EasyAbp/EShop/Payments/Refunds/Dtos/CreateEShopRefundItemInput.cs b/modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Application.Contracts/EasyAbp/EShop/Payments/Refunds/Dtos/CreateEShopRefundItemInput.cs index 0eb0c99b..2d211c63 100644 --- a/modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Application.Contracts/EasyAbp/EShop/Payments/Refunds/Dtos/CreateEShopRefundItemInput.cs +++ b/modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Application.Contracts/EasyAbp/EShop/Payments/Refunds/Dtos/CreateEShopRefundItemInput.cs @@ -1,23 +1,9 @@ using System; -using System.Collections.Generic; -using JetBrains.Annotations; -using Volo.Abp.ObjectExtending; namespace EasyAbp.EShop.Payments.Refunds.Dtos { [Serializable] - public class CreateEShopRefundItemInput : ExtensibleObject + public class CreateEShopRefundItemInput : CreateEShopRefundItemInfoModel { - public Guid OrderId { get; set; } - - [CanBeNull] - public string CustomerRemark { get; set; } - - [CanBeNull] - public string StaffRemark { get; set; } - - public List OrderLines { get; set; } = new(); - - public List OrderExtraFees { get; set; } = new(); } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/CreateEShopRefundItemInfoModel.cs b/modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/CreateEShopRefundItemInfoModel.cs new file mode 100644 index 00000000..03176bf7 --- /dev/null +++ b/modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/CreateEShopRefundItemInfoModel.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; +using Volo.Abp.ObjectExtending; + +namespace EasyAbp.EShop.Payments.Refunds; + +[Serializable] +public class CreateEShopRefundItemInfoModel : ExtensibleObject +{ + public Guid OrderId { get; set; } + + [CanBeNull] + public string CustomerRemark { get; set; } + + [CanBeNull] + public string StaffRemark { get; set; } + + public List OrderLines { get; set; } = new(); + + public List OrderExtraFees { get; set; } = new(); +} \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/RefundOrderEto.cs b/modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/RefundOrderEto.cs new file mode 100644 index 00000000..a594a7c3 --- /dev/null +++ b/modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/RefundOrderEto.cs @@ -0,0 +1,34 @@ +using System; +using JetBrains.Annotations; +using Volo.Abp.MultiTenancy; + +namespace EasyAbp.EShop.Payments.Refunds; + +[Serializable] +public class RefundOrderEto : CreateEShopRefundItemInfoModel, IMultiTenant +{ + public Guid? TenantId { get; set; } + + public Guid StoreId { get; set; } + + public Guid PaymentId { get; set; } + + [CanBeNull] + public string DisplayReason { get; set; } + + protected RefundOrderEto() + { + } + + public RefundOrderEto(Guid? tenantId, Guid orderId, Guid storeId, Guid paymentId, [CanBeNull] string displayReason, + [CanBeNull] string customerRemark, [CanBeNull] string staffRemark) + { + TenantId = tenantId; + OrderId = orderId; + StoreId = storeId; + PaymentId = paymentId; + DisplayReason = displayReason; + CustomerRemark = customerRemark; + StaffRemark = staffRemark; + } +} \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain/EasyAbp/EShop/Payments/Refunds/RefundOrderEventHandler.cs b/modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain/EasyAbp/EShop/Payments/Refunds/RefundOrderEventHandler.cs new file mode 100644 index 00000000..76b9d6a1 --- /dev/null +++ b/modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain/EasyAbp/EShop/Payments/Refunds/RefundOrderEventHandler.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using EasyAbp.EShop.Payments.Payments; +using EasyAbp.PaymentService.Payments; +using EasyAbp.PaymentService.Refunds; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.Json; + +namespace EasyAbp.EShop.Payments.Refunds; + +public class RefundOrderEventHandler : IDistributedEventHandler, ITransientDependency +{ + private readonly IJsonSerializer _jsonSerializer; + private readonly IPaymentRepository _paymentRepository; + private readonly IDistributedEventBus _distributedEventBus; + + public RefundOrderEventHandler( + IJsonSerializer jsonSerializer, + IPaymentRepository paymentRepository, + IDistributedEventBus distributedEventBus) + { + _jsonSerializer = jsonSerializer; + _paymentRepository = paymentRepository; + _distributedEventBus = distributedEventBus; + } + + public virtual async Task HandleEventAsync(RefundOrderEto eventData) + { + var refundAmount = eventData.OrderLines.Sum(x => x.TotalAmount) + + eventData.OrderExtraFees.Sum(x => x.TotalAmount); + + var payment = await _paymentRepository.GetAsync(eventData.PaymentId); + + var paymentItem = payment.PaymentItems.Single(x => x.ItemKey == eventData.OrderId.ToString()); + + var createRefundItemInput = new CreateRefundItemInput + { + PaymentItemId = paymentItem.Id, + RefundAmount = refundAmount, + CustomerRemark = eventData.CustomerRemark, + StaffRemark = eventData.StaffRemark + }; + + createRefundItemInput.SetProperty(nameof(RefundItem.StoreId), eventData.StoreId); + createRefundItemInput.SetProperty(nameof(RefundItem.OrderId), eventData.OrderId); + createRefundItemInput.SetProperty(nameof(RefundItem.OrderLines), + _jsonSerializer.Serialize(eventData.OrderLines)); + createRefundItemInput.SetProperty(nameof(RefundItem.OrderExtraFees), + _jsonSerializer.Serialize(eventData.OrderExtraFees)); + + var eto = new RefundPaymentEto(eventData.TenantId, new CreateRefundInput + { + PaymentId = eventData.PaymentId, + DisplayReason = eventData.DisplayReason, + CustomerRemark = eventData.CustomerRemark, + StaffRemark = eventData.StaffRemark, + RefundItems = new List { createRefundItemInput } + }); + + await _distributedEventBus.PublishAsync(eto); + } +} \ No newline at end of file