Browse Source

Improvement FlashSales

pull/184/head
Jadyn 4 years ago
parent
commit
1f1d307343
  1. 5
      plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Orders/CreateFlashSaleOrderEventHandler.cs
  2. 4
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/IFlashSalePlanAppService.cs
  3. 51
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanAppService.cs
  4. 3
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanCacheItem.cs
  5. 69
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/RollBackInventoryCreateFlashSaleOrderCompleteEventHandler.cs
  6. 2
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultAppService.cs
  7. 2
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/FlashSaleInventoryManager.cs
  8. 2
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultFailedReason.cs
  9. 10
      plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanController.cs
  10. 2
      plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/EasyAbp/Eshop/Products/Products/IFlashSaleInventoryManager.cs
  11. 2
      plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/FlashSaleInventoryAppService.cs
  12. 2
      plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/LocalFlashSaleInventoryManager.cs

5
plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Orders/CreateFlashSaleOrderEventHandler.cs

@ -70,7 +70,7 @@ public class CreateFlashSaleOrderEventHandler : IDistributedEventHandler<CreateF
StoreId = eventData.StoreId, StoreId = eventData.StoreId,
PendingResultId = eventData.PendingResultId, PendingResultId = eventData.PendingResultId,
Success = false, Success = false,
Reason = FlashSaleResultFailedReason.PreOrderExipred Reason = FlashSaleResultFailedReason.InvalidHashToken
}); });
return; return;
} }
@ -93,7 +93,8 @@ public class CreateFlashSaleOrderEventHandler : IDistributedEventHandler<CreateF
UserId = eventData.UserId, UserId = eventData.UserId,
StoreId = eventData.StoreId, StoreId = eventData.StoreId,
PendingResultId = eventData.PendingResultId, PendingResultId = eventData.PendingResultId,
Success = true Success = true,
Reason = null
}); });
} }

4
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/IFlashSalePlanAppService.cs

@ -15,7 +15,5 @@ public interface IFlashSalePlanAppService :
{ {
Task PreOrderAsync(Guid id); Task PreOrderAsync(Guid id);
Task CheckPreOrderAsync(Guid id); Task<bool> OrderAsync(Guid id, CreateOrderInput input);
Task CreateOrderAsync(Guid id, CreateOrderInput input);
} }

51
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanAppService.cs

@ -112,7 +112,7 @@ public class FlashSalePlanAppService :
var product = await ProductAppService.GetAsync(input.ProductId); var product = await ProductAppService.GetAsync(input.ProductId);
var productSku = product.GetSkuById(input.ProductSkuId); var productSku = product.GetSkuById(input.ProductSkuId);
await ValidateCreateOrUpdateProductAsync(input.ProductId, product, input.ProductSkuId, productSku, input.StoreId); await ValidateProductAsync(input.ProductId, product, input.StoreId);
var flashSalePlan = new FlashSalePlan( var flashSalePlan = new FlashSalePlan(
GuidGenerator.Create(), GuidGenerator.Create(),
@ -120,8 +120,8 @@ public class FlashSalePlanAppService :
input.StoreId, input.StoreId,
input.BeginTime, input.BeginTime,
input.EndTime, input.EndTime,
input.ProductId, product.Id,
input.ProductSkuId, productSku.Id,
input.IsPublished input.IsPublished
); );
@ -138,7 +138,7 @@ public class FlashSalePlanAppService :
await CheckMultiStorePolicyAsync(product.StoreId, UpdatePolicyName); await CheckMultiStorePolicyAsync(product.StoreId, UpdatePolicyName);
await ValidateCreateOrUpdateProductAsync(input.ProductId, product, input.ProductSkuId, productSku, flashSalePlan.StoreId); await ValidateProductAsync(input.ProductId, product, flashSalePlan.StoreId);
if (await ExistRelatedFlashSaleResultsAsync(id) && (input.ProductId != flashSalePlan.ProductId || input.ProductSkuId != flashSalePlan.ProductSkuId)) if (await ExistRelatedFlashSaleResultsAsync(id) && (input.ProductId != flashSalePlan.ProductId || input.ProductSkuId != flashSalePlan.ProductSkuId))
{ {
@ -146,7 +146,7 @@ public class FlashSalePlanAppService :
} }
flashSalePlan.SetTimeRange(input.BeginTime, input.EndTime); flashSalePlan.SetTimeRange(input.BeginTime, input.EndTime);
flashSalePlan.SetProductSku(flashSalePlan.StoreId, input.ProductId, input.ProductSkuId); flashSalePlan.SetProductSku(flashSalePlan.StoreId, product.Id, productSku.Id);
flashSalePlan.SetPublished(input.IsPublished); flashSalePlan.SetPublished(input.IsPublished);
flashSalePlan.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp); flashSalePlan.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp);
@ -197,28 +197,7 @@ public class FlashSalePlanAppService :
await SetPreOrderCacheAsync(plan, product, productSku); await SetPreOrderCacheAsync(plan, product, productSku);
} }
public virtual async Task CheckPreOrderAsync(Guid id) public virtual async Task<bool> OrderAsync(Guid id, CreateOrderInput input)
{
var plan = await GetFlashSalePlanCacheAsync(id);
var preOrderCache = await GetPreOrderCacheAsync(plan);
if (preOrderCache == null)
{
throw new BusinessException(FlashSalesErrorCodes.PreOrderExpired);
}
var product = await ProductAppService.GetAsync(plan.ProductId);
var productSku = product.GetSkuById(plan.ProductSkuId);
if (!await CompareHashTokenAsync(preOrderCache.HashToken, plan, product, productSku))
{
throw new BusinessException(FlashSalesErrorCodes.PreOrderExpired);
}
await ValidatePreOrderAsync(plan, product, productSku);
}
public virtual async Task CreateOrderAsync(Guid id, CreateOrderInput input)
{ {
var plan = await GetFlashSalePlanCacheAsync(id); var plan = await GetFlashSalePlanCacheAsync(id);
var now = Clock.Now; var now = Clock.Now;
@ -257,18 +236,18 @@ public class FlashSalePlanAppService :
throw new BusinessException(FlashSalesErrorCodes.DuplicateFlashSalesOrder); throw new BusinessException(FlashSalesErrorCodes.DuplicateFlashSalesOrder);
} }
if (!await FlashSaleInventoryManager.TryIncreaseInventoryAsync( if (!await FlashSaleInventoryManager.TryReduceInventoryAsync(
plan.TenantId, preOrderCache.InventoryProviderName, plan.TenantId, preOrderCache.InventoryProviderName,
plan.StoreId, plan.ProductId, plan.ProductSkuId, 1, true)) plan.StoreId, plan.ProductId, plan.ProductSkuId, 1, true))
{ {
throw new BusinessException(FlashSalesErrorCodes.ProductSkuInventoryExceeded); return false;
} }
var result = await CreatePendingFlashSaleResultAsync(plan, userId, async (existsResultId) => var result = await CreatePendingFlashSaleResultAsync(plan, userId, async (existsResultId) =>
{ {
await SetUserFlashSaleResultCacheAsync(plan, userId, existsResultId); await SetUserFlashSaleResultCacheAsync(plan, userId, existsResultId);
await FlashSaleInventoryManager.TryIncreaseInventoryAsync( await FlashSaleInventoryManager.TryRollBackInventoryAsync(
plan.TenantId, preOrderCache.InventoryProviderName, plan.TenantId, preOrderCache.InventoryProviderName,
plan.StoreId, plan.ProductId, plan.ProductSkuId, 1, true plan.StoreId, plan.ProductId, plan.ProductSkuId, 1, true
); );
@ -279,6 +258,8 @@ public class FlashSalePlanAppService :
var createFlashSaleOrderEto = await PrepareCreateFlashSaleOrderEtoAsync(plan, result.Id, input, userId, now, preOrderCache.HashToken); var createFlashSaleOrderEto = await PrepareCreateFlashSaleOrderEtoAsync(plan, result.Id, input, userId, now, preOrderCache.HashToken);
await DistributedEventBus.PublishAsync(createFlashSaleOrderEto); await DistributedEventBus.PublishAsync(createFlashSaleOrderEto);
return true;
} }
#region PreOrderCache #region PreOrderCache
@ -394,18 +375,13 @@ public class FlashSalePlanAppService :
return await FlashSaleResultRepository.AnyAsync(x => x.PlanId == planId); return await FlashSaleResultRepository.AnyAsync(x => x.PlanId == planId);
} }
protected virtual Task ValidateCreateOrUpdateProductAsync(Guid productId, ProductDto product, Guid productSkuId, ProductSkuDto productSku, Guid storeId) protected virtual Task ValidateProductAsync(Guid productId, ProductDto product, Guid storeId)
{ {
if (product.StoreId != storeId) if (product.StoreId != storeId)
{ {
throw new ProductIsNotInThisStoreException(productId, storeId); throw new ProductIsNotInThisStoreException(productId, storeId);
} }
if (productSku == null)
{
throw new ProductSkuIsNotFoundException(productSkuId);
}
if (product.InventoryStrategy != InventoryStrategy.FlashSales) if (product.InventoryStrategy != InventoryStrategy.FlashSales)
{ {
throw new UnexpectedInventoryStrategyException(InventoryStrategy.FlashSales); throw new UnexpectedInventoryStrategyException(InventoryStrategy.FlashSales);
@ -445,7 +421,8 @@ public class FlashSalePlanAppService :
protected virtual async Task<FlashSaleResult> CreatePendingFlashSaleResultAsync(FlashSalePlanCacheItem plan, Guid userId, Func<Guid, Task> existResultPreProcess) protected virtual async Task<FlashSaleResult> CreatePendingFlashSaleResultAsync(FlashSalePlanCacheItem plan, Guid userId, Func<Guid, Task> existResultPreProcess)
{ {
// Prevent repeat submit // Prevent repeat submit
var existsResult = await FlashSaleResultRepository.FirstOrDefaultAsync(x => x.PlanId == plan.Id && x.UserId == userId); var existsResult = await FlashSaleResultRepository.FirstOrDefaultAsync(x =>
x.PlanId == plan.Id && x.UserId == userId && x.Status != FlashSaleResultStatus.Failed && x.Reason != FlashSaleResultFailedReason.InvalidHashToken);
if (existsResult != null) if (existsResult != null)
{ {

3
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanCacheItem.cs

@ -1,10 +1,11 @@
using System; using System;
using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos; using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos;
using Volo.Abp.MultiTenancy;
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans;
[Serializable] [Serializable]
public class FlashSalePlanCacheItem : FlashSalePlanDto public class FlashSalePlanCacheItem : FlashSalePlanDto, IMultiTenant
{ {
public Guid? TenantId { get; set; } public Guid? TenantId { get; set; }
} }

69
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/RollBackInventoryCreateFlashSaleOrderCompleteEventHandler.cs

@ -0,0 +1,69 @@
using System;
using System.Threading.Tasks;
using EasyAbp.Eshop.Products.Products;
using EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults;
using EasyAbp.EShop.Products.Products;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans;
public class RollBackInventoryCreateFlashSaleOrderCompleteEventHandler : IDistributedEventHandler<CreateFlashSaleOrderCompleteEto>, ITransientDependency
{
protected IFlashSaleInventoryManager FlashSaleInventoryManager { get; }
protected IDistributedCache DistributedCache { get; }
protected IFlashSalePlanRepository FlashSalePlanRepository { get; }
protected IProductAppService ProductAppService { get; }
protected ILogger<RollBackInventoryCreateFlashSaleOrderCompleteEventHandler> Logger { get; }
public RollBackInventoryCreateFlashSaleOrderCompleteEventHandler(
IFlashSaleInventoryManager flashSaleInventoryManager,
IDistributedCache distributedCache,
IFlashSalePlanRepository flashSalePlanRepository,
IProductAppService productAppService)
{
FlashSaleInventoryManager = flashSaleInventoryManager;
DistributedCache = distributedCache;
FlashSalePlanRepository = flashSalePlanRepository;
ProductAppService = productAppService;
}
public virtual async Task HandleEventAsync(CreateFlashSaleOrderCompleteEto eventData)
{
if (eventData.Success)
{
return;
}
if (eventData.Reason != FlashSaleResultFailedReason.InvalidHashToken)
{
return;
}
var plan = await FlashSalePlanRepository.GetAsync(eventData.PlanId);
var product = await ProductAppService.GetAsync(plan.ProductId);
if (!await FlashSaleInventoryManager.TryRollBackInventoryAsync(
plan.TenantId, product.InventoryProviderName,
plan.StoreId, plan.ProductId, plan.ProductSkuId, 1, true
))
{
Logger.LogWarning("Try roll back inventory failed.");
return;
}
await RemoveUserFlashSaleResultCacheAsync(plan, eventData.UserId);
}
protected virtual Task<string> GetUserFlashSaleResultCacheKeyAsync(FlashSalePlan plan, Guid userId)
{
return Task.FromResult($"flash-sale-result-{plan.Id}-{userId}");
}
protected virtual async Task RemoveUserFlashSaleResultCacheAsync(FlashSalePlan plan, Guid userId)
{
await DistributedCache.RemoveAsync(await GetUserFlashSaleResultCacheKeyAsync(plan, userId));
}
}

2
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultAppService.cs

@ -16,8 +16,6 @@ public class FlashSaleResultAppService :
protected override string GetPolicyName { get; set; } protected override string GetPolicyName { get; set; }
protected override string GetListPolicyName { get; set; } protected override string GetListPolicyName { get; set; }
protected IFlashSaleResultRepository FlashSaleResultRepository { get; }
public FlashSaleResultAppService(IFlashSaleResultRepository flashSaleResultRepository) : base(flashSaleResultRepository) public FlashSaleResultAppService(IFlashSaleResultRepository flashSaleResultRepository) : base(flashSaleResultRepository)
{ {
} }

2
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/FlashSaleInventoryManager.cs

@ -24,7 +24,7 @@ public class FlashSaleInventoryManager : IFlashSaleInventoryManager, ITransientD
new ReduceInventoryInput(tenantId, providerName, storeId, productId, productSkuId, quantity, increaseSold)); new ReduceInventoryInput(tenantId, providerName, storeId, productId, productSkuId, quantity, increaseSold));
} }
public virtual async Task<bool> TryIncreaseInventoryAsync(Guid? tenantId, string providerName, Guid storeId, Guid productId, public virtual async Task<bool> TryRollBackInventoryAsync(Guid? tenantId, string providerName, Guid storeId, Guid productId,
Guid productSkuId, int quantity, bool decreaseSold) Guid productSkuId, int quantity, bool decreaseSold)
{ {
return await FlashSaleInventoryReducerAppService.TryIncreaseAsync return await FlashSaleInventoryReducerAppService.TryIncreaseAsync

2
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultFailedReason.cs

@ -4,5 +4,5 @@ public static class FlashSaleResultFailedReason
{ {
public const string InsufficientInventory = nameof(InsufficientInventory); public const string InsufficientInventory = nameof(InsufficientInventory);
public const string PreOrderExipred = nameof(PreOrderExipred); public const string InvalidHashToken = nameof(InvalidHashToken);
} }

10
plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.HttpApi/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanController.cs

@ -57,15 +57,9 @@ public class FlashSalePlanController :
return Service.PreOrderAsync(id); return Service.PreOrderAsync(id);
} }
[HttpGet("{id}/check-pre-order")]
public virtual Task CheckPreOrderAsync(Guid id)
{
return Service.CheckPreOrderAsync(id);
}
[HttpPost("{id}/order")] [HttpPost("{id}/order")]
public virtual Task CreateOrderAsync(Guid id, CreateOrderInput input) public virtual Task<bool> OrderAsync(Guid id, CreateOrderInput input)
{ {
return Service.CreateOrderAsync(id, input); return Service.OrderAsync(id, input);
} }
} }

2
plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Abstractions/EasyAbp/Eshop/Products/Products/IFlashSaleInventoryManager.cs

@ -7,5 +7,5 @@ public interface IFlashSaleInventoryManager
{ {
Task<bool> TryReduceInventoryAsync(Guid? tenantId, string providerName, Guid storeId, Guid productId, Guid productSkuId, int quantity, bool increaseSold); Task<bool> TryReduceInventoryAsync(Guid? tenantId, string providerName, Guid storeId, Guid productId, Guid productSkuId, int quantity, bool increaseSold);
Task<bool> TryIncreaseInventoryAsync(Guid? tenantId, string providerName, Guid storeId, Guid productId, Guid productSkuId, int quantity, bool decreaseSold); Task<bool> TryRollBackInventoryAsync(Guid? tenantId, string providerName, Guid storeId, Guid productId, Guid productSkuId, int quantity, bool decreaseSold);
} }

2
plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/FlashSaleInventoryAppService.cs

@ -32,7 +32,7 @@ public class FlashSaleInventoryAppService : ProductsAppService, IFlashSaleInvent
{ {
await CheckPolicyAsync(ProductsPluginsFlashSalesPermissions.FlashSaleInventory.Increase); await CheckPolicyAsync(ProductsPluginsFlashSalesPermissions.FlashSaleInventory.Increase);
return await LocalFlashSaleInventoryManager.TryIncreaseInventoryAsync( return await LocalFlashSaleInventoryManager.TryRollBackInventoryAsync(
input.TenantId, input.TenantId,
input.ProviderName, input.ProviderName,
input.StoreId, input.StoreId,

2
plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/LocalFlashSaleInventoryManager.cs

@ -23,7 +23,7 @@ public class LocalFlashSaleInventoryManager : ILocalFlashSaleInventoryManager, I
.TryReduceInventoryAsync(model, quantity, increaseSold); .TryReduceInventoryAsync(model, quantity, increaseSold);
} }
public virtual async Task<bool> TryIncreaseInventoryAsync(Guid? tenantId, string providerName, Guid storeId, Guid productId, public virtual async Task<bool> TryRollBackInventoryAsync(Guid? tenantId, string providerName, Guid storeId, Guid productId,
Guid productSkuId, int quantity, bool decreaseSold) Guid productSkuId, int quantity, bool decreaseSold)
{ {
var model = new InventoryQueryModel(tenantId, storeId, productId, productSkuId); var model = new InventoryQueryModel(tenantId, storeId, productId, productSkuId);

Loading…
Cancel
Save