mirror of https://github.com/EasyAbp/EShop.git
13 changed files with 4111 additions and 49 deletions
@ -0,0 +1,13 @@ |
|||
using System; |
|||
|
|||
namespace EasyAbp.EShop.Orders.Orders |
|||
{ |
|||
public class OrderPaidEto |
|||
{ |
|||
public OrderEto Order { get; set; } |
|||
|
|||
public Guid PaymentId { get; set; } |
|||
|
|||
public Guid PaymentItemId { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System; |
|||
using Volo.Abp; |
|||
|
|||
namespace EasyAbp.EShop.Orders.Orders |
|||
{ |
|||
public class OrderIsInWrongStageException : BusinessException |
|||
{ |
|||
public OrderIsInWrongStageException(Guid orderId) : base(message: $"The order {orderId} is in the wrong stage.") |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
|
|||
namespace EasyAbp.EShop.Products.Products |
|||
{ |
|||
[Serializable] |
|||
public class ProductInventoryReductionAfterOrderPaidResultEto |
|||
{ |
|||
public Guid? TenantId { get; set; } |
|||
|
|||
public Guid OrderId { get; set; } |
|||
|
|||
public Guid PaymentId { get; set; } |
|||
|
|||
public Guid PaymentItemId { get; set; } |
|||
|
|||
public bool IsSuccess { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using EasyAbp.EShop.Orders.Orders; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
|
|||
namespace EasyAbp.EShop.Products.Products |
|||
{ |
|||
public interface IOrderPaidEventHandler : IDistributedEventHandler<OrderPaidEto> |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,110 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Orders.Orders; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace EasyAbp.EShop.Products.Products |
|||
{ |
|||
public class OrderPaidEventHandler : IOrderPaidEventHandler, ITransientDependency |
|||
{ |
|||
private readonly ICurrentTenant _currentTenant; |
|||
private readonly IUnitOfWorkManager _unitOfWorkManager; |
|||
private readonly IDistributedEventBus _distributedEventBus; |
|||
private readonly IProductRepository _productRepository; |
|||
private readonly IProductManager _productManager; |
|||
|
|||
public OrderPaidEventHandler( |
|||
ICurrentTenant currentTenant, |
|||
IUnitOfWorkManager unitOfWorkManager, |
|||
IDistributedEventBus distributedEventBus, |
|||
IProductRepository productRepository, |
|||
IProductManager productManager) |
|||
{ |
|||
_currentTenant = currentTenant; |
|||
_unitOfWorkManager = unitOfWorkManager; |
|||
_distributedEventBus = distributedEventBus; |
|||
_productRepository = productRepository; |
|||
_productManager = productManager; |
|||
} |
|||
|
|||
public virtual async Task HandleEventAsync(OrderPaidEto eventData) |
|||
{ |
|||
using var changeTenant = _currentTenant.Change(eventData.Order.TenantId); |
|||
|
|||
using var uow = _unitOfWorkManager.Begin(isTransactional: true); |
|||
|
|||
var models = new List<ReduceInventoryModel>(); |
|||
|
|||
foreach (var orderLine in eventData.Order.OrderLines) |
|||
{ |
|||
// Todo: Should use ProductHistory.
|
|||
var product = await _productRepository.FindAsync(orderLine.ProductId); |
|||
|
|||
var productSku = product?.ProductSkus.FirstOrDefault(sku => sku.Id == orderLine.ProductSkuId); |
|||
|
|||
if (productSku == null) |
|||
{ |
|||
await PublishResultEventAsync(eventData, false); |
|||
|
|||
return; |
|||
} |
|||
|
|||
if (product.InventoryStrategy != InventoryStrategy.ReduceAfterPayment) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
if (!await _productManager.IsInventorySufficientAsync(product, productSku, eventData.Order.StoreId, |
|||
orderLine.Quantity)) |
|||
{ |
|||
await PublishResultEventAsync(eventData, false); |
|||
|
|||
return; |
|||
} |
|||
|
|||
models.Add(new ReduceInventoryModel |
|||
{ |
|||
Product = product, |
|||
ProductSku = productSku, |
|||
StoreId = eventData.Order.StoreId, |
|||
Quantity = orderLine.Quantity |
|||
}); |
|||
} |
|||
|
|||
foreach (var model in models) |
|||
{ |
|||
if (await _productManager.TryReduceInventoryAsync(model.Product, model.ProductSku, model.StoreId, |
|||
model.Quantity)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
await uow.RollbackAsync(); |
|||
|
|||
await PublishResultEventAsync(eventData, false); |
|||
|
|||
return; |
|||
} |
|||
|
|||
await uow.CompleteAsync(); |
|||
|
|||
await PublishResultEventAsync(eventData, true); |
|||
} |
|||
|
|||
protected virtual async Task PublishResultEventAsync(OrderPaidEto orderPaidEto, bool isSuccess) |
|||
{ |
|||
await _distributedEventBus.PublishAsync(new ProductInventoryReductionAfterOrderPaidResultEto |
|||
{ |
|||
TenantId = orderPaidEto.Order.TenantId, |
|||
OrderId = orderPaidEto.Order.Id, |
|||
PaymentId = orderPaidEto.PaymentId, |
|||
PaymentItemId = orderPaidEto.PaymentItemId, |
|||
IsSuccess = isSuccess |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System; |
|||
|
|||
namespace EasyAbp.EShop.Products.Products |
|||
{ |
|||
public class ReduceInventoryModel |
|||
{ |
|||
public Product Product { get; set; } |
|||
|
|||
public ProductSku ProductSku { get; set; } |
|||
|
|||
public Guid StoreId { get; set; } |
|||
|
|||
public int Quantity { get; set; } |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,127 @@ |
|||
using System; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
|
|||
namespace EShopSample.Migrations |
|||
{ |
|||
public partial class UpgradedToAbp2_9_0 : Migration |
|||
{ |
|||
protected override void Up(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.CreateTable( |
|||
name: "AbpOrganizationUnits", |
|||
columns: table => new |
|||
{ |
|||
Id = table.Column<Guid>(nullable: false), |
|||
ExtraProperties = table.Column<string>(nullable: true), |
|||
ConcurrencyStamp = table.Column<string>(nullable: true), |
|||
CreationTime = table.Column<DateTime>(nullable: false), |
|||
CreatorId = table.Column<Guid>(nullable: true), |
|||
LastModificationTime = table.Column<DateTime>(nullable: true), |
|||
LastModifierId = table.Column<Guid>(nullable: true), |
|||
IsDeleted = table.Column<bool>(nullable: false, defaultValue: false), |
|||
DeleterId = table.Column<Guid>(nullable: true), |
|||
DeletionTime = table.Column<DateTime>(nullable: true), |
|||
TenantId = table.Column<Guid>(nullable: true), |
|||
ParentId = table.Column<Guid>(nullable: true), |
|||
Code = table.Column<string>(maxLength: 95, nullable: false), |
|||
DisplayName = table.Column<string>(maxLength: 128, nullable: false) |
|||
}, |
|||
constraints: table => |
|||
{ |
|||
table.PrimaryKey("PK_AbpOrganizationUnits", x => x.Id); |
|||
table.ForeignKey( |
|||
name: "FK_AbpOrganizationUnits_AbpOrganizationUnits_ParentId", |
|||
column: x => x.ParentId, |
|||
principalTable: "AbpOrganizationUnits", |
|||
principalColumn: "Id", |
|||
onDelete: ReferentialAction.Restrict); |
|||
}); |
|||
|
|||
migrationBuilder.CreateTable( |
|||
name: "AbpOrganizationUnitRoles", |
|||
columns: table => new |
|||
{ |
|||
RoleId = table.Column<Guid>(nullable: false), |
|||
OrganizationUnitId = table.Column<Guid>(nullable: false), |
|||
CreationTime = table.Column<DateTime>(nullable: false), |
|||
CreatorId = table.Column<Guid>(nullable: true), |
|||
TenantId = table.Column<Guid>(nullable: true) |
|||
}, |
|||
constraints: table => |
|||
{ |
|||
table.PrimaryKey("PK_AbpOrganizationUnitRoles", x => new { x.OrganizationUnitId, x.RoleId }); |
|||
table.ForeignKey( |
|||
name: "FK_AbpOrganizationUnitRoles_AbpOrganizationUnits_OrganizationUnitId", |
|||
column: x => x.OrganizationUnitId, |
|||
principalTable: "AbpOrganizationUnits", |
|||
principalColumn: "Id", |
|||
onDelete: ReferentialAction.Cascade); |
|||
table.ForeignKey( |
|||
name: "FK_AbpOrganizationUnitRoles_AbpRoles_RoleId", |
|||
column: x => x.RoleId, |
|||
principalTable: "AbpRoles", |
|||
principalColumn: "Id", |
|||
onDelete: ReferentialAction.Cascade); |
|||
}); |
|||
|
|||
migrationBuilder.CreateTable( |
|||
name: "AbpUserOrganizationUnits", |
|||
columns: table => new |
|||
{ |
|||
UserId = table.Column<Guid>(nullable: false), |
|||
OrganizationUnitId = table.Column<Guid>(nullable: false), |
|||
CreationTime = table.Column<DateTime>(nullable: false), |
|||
CreatorId = table.Column<Guid>(nullable: true), |
|||
TenantId = table.Column<Guid>(nullable: true) |
|||
}, |
|||
constraints: table => |
|||
{ |
|||
table.PrimaryKey("PK_AbpUserOrganizationUnits", x => new { x.OrganizationUnitId, x.UserId }); |
|||
table.ForeignKey( |
|||
name: "FK_AbpUserOrganizationUnits_AbpOrganizationUnits_OrganizationUnitId", |
|||
column: x => x.OrganizationUnitId, |
|||
principalTable: "AbpOrganizationUnits", |
|||
principalColumn: "Id", |
|||
onDelete: ReferentialAction.Cascade); |
|||
table.ForeignKey( |
|||
name: "FK_AbpUserOrganizationUnits_AbpUsers_UserId", |
|||
column: x => x.UserId, |
|||
principalTable: "AbpUsers", |
|||
principalColumn: "Id", |
|||
onDelete: ReferentialAction.Cascade); |
|||
}); |
|||
|
|||
migrationBuilder.CreateIndex( |
|||
name: "IX_AbpOrganizationUnitRoles_RoleId_OrganizationUnitId", |
|||
table: "AbpOrganizationUnitRoles", |
|||
columns: new[] { "RoleId", "OrganizationUnitId" }); |
|||
|
|||
migrationBuilder.CreateIndex( |
|||
name: "IX_AbpOrganizationUnits_Code", |
|||
table: "AbpOrganizationUnits", |
|||
column: "Code"); |
|||
|
|||
migrationBuilder.CreateIndex( |
|||
name: "IX_AbpOrganizationUnits_ParentId", |
|||
table: "AbpOrganizationUnits", |
|||
column: "ParentId"); |
|||
|
|||
migrationBuilder.CreateIndex( |
|||
name: "IX_AbpUserOrganizationUnits_UserId_OrganizationUnitId", |
|||
table: "AbpUserOrganizationUnits", |
|||
columns: new[] { "UserId", "OrganizationUnitId" }); |
|||
} |
|||
|
|||
protected override void Down(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.DropTable( |
|||
name: "AbpOrganizationUnitRoles"); |
|||
|
|||
migrationBuilder.DropTable( |
|||
name: "AbpUserOrganizationUnits"); |
|||
|
|||
migrationBuilder.DropTable( |
|||
name: "AbpOrganizationUnits"); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue