From 6cc239b8f286b9b095cb9ed32113cbd80e7de1b3 Mon Sep 17 00:00:00 2001 From: Jadyn Date: Sun, 17 Jul 2022 12:41:41 +0800 Subject: [PATCH] `DefaultProductInventoryProvider` using `DistributedLock` --- .../EasyAbp.EShop.Products.Domain.csproj | 1 + .../DefaultProductInventoryProvider.cs | 41 ++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp.EShop.Products.Domain.csproj b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp.EShop.Products.Domain.csproj index 2dab26aa..663bb9c9 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp.EShop.Products.Domain.csproj +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp.EShop.Products.Domain.csproj @@ -8,6 +8,7 @@ + diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductInventoryProvider.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductInventoryProvider.cs index 613a434c..9f420ea2 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductInventoryProvider.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductInventoryProvider.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using EasyAbp.EShop.Products.ProductInventories; +using Microsoft.Extensions.Logging; using Volo.Abp.DependencyInjection; +using Volo.Abp.DistributedLocking; using Volo.Abp.EventBus.Distributed; using Volo.Abp.Guids; using Volo.Abp.MultiTenancy; @@ -13,6 +15,14 @@ namespace EasyAbp.EShop.Products.Products { public class DefaultProductInventoryProvider : IProductInventoryProvider, ITransientDependency { + /// + /// The lock key format. + /// {0}: Tenant ID + /// {1}: Product ID + /// {2}: ProductSku ID + /// + public const string DefaultProductInventoryLockKeyFormat = "eshop-product-inventory-{0}-{1}-{2}"; + public static string DefaultProductInventoryProviderName { get; set; } = "Default"; public static string DefaultProductInventoryProviderDisplayName { get; set; } = "Default"; public static string DefaultProductInventoryProviderDescription { get; set; } = "Default"; @@ -24,17 +34,23 @@ namespace EasyAbp.EShop.Products.Products private readonly ICurrentTenant _currentTenant; private readonly IDistributedEventBus _distributedEventBus; private readonly IProductInventoryRepository _productInventoryRepository; + private readonly IAbpDistributedLock _distributedLock; + private readonly ILogger _logger; public DefaultProductInventoryProvider( IGuidGenerator guidGenerator, ICurrentTenant currentTenant, IDistributedEventBus distributedEventBus, - IProductInventoryRepository productInventoryRepository) + IProductInventoryRepository productInventoryRepository, + IAbpDistributedLock distributedLock, + ILogger logger) { _guidGenerator = guidGenerator; _currentTenant = currentTenant; _distributedEventBus = distributedEventBus; _productInventoryRepository = productInventoryRepository; + _distributedLock = distributedLock; + _logger = logger; } [UnitOfWork] @@ -62,6 +78,15 @@ namespace EasyAbp.EShop.Products.Products public virtual async Task TryIncreaseInventoryAsync(InventoryQueryModel model, int quantity, bool decreaseSold) { + await using var handle = await _distributedLock.TryAcquireAsync(await GetLockKeyAsync(model), TimeSpan.FromSeconds(30)); + + if (handle == null) + { + _logger.LogWarning("TryIncreaseInventory failed to acquire lock for product inventory: {TenantId},{ProductId},{ProductSkuId}", + model.TenantId, model.ProductId, model.ProductSkuId); + return false; + } + var productInventory = await GetOrCreateProductInventoryAsync(model.ProductId, model.ProductSkuId); return await TryIncreaseInventoryAsync(model, productInventory, quantity, decreaseSold); @@ -71,6 +96,15 @@ namespace EasyAbp.EShop.Products.Products public virtual async Task TryReduceInventoryAsync(InventoryQueryModel model, int quantity, bool increaseSold) { + await using var handle = await _distributedLock.TryAcquireAsync(await GetLockKeyAsync(model), TimeSpan.FromSeconds(30)); + + if (handle == null) + { + _logger.LogWarning("TryReduceInventory failed to acquire lock for product inventory: {TenantId},{ProductId},{ProductSkuId}", + model.TenantId, model.ProductId, model.ProductSkuId); + return false; + } + var productInventory = await GetOrCreateProductInventoryAsync(model.ProductId, model.ProductSkuId); return await TryReduceInventoryAsync(model, productInventory, quantity, increaseSold); @@ -158,5 +192,10 @@ namespace EasyAbp.EShop.Products.Products newInventory, sold)); } + + protected virtual Task GetLockKeyAsync(InventoryQueryModel model) + { + return Task.FromResult(string.Format(DefaultProductInventoryLockKeyFormat, model.TenantId, model.ProductId, model.ProductSkuId)); + } } } \ No newline at end of file