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,
PendingResultId = eventData.PendingResultId,
Success = false,
Reason = FlashSaleResultFailedReason.PreOrderExipred
Reason = FlashSaleResultFailedReason.InvalidHashToken
});
return;
}
@ -93,7 +93,8 @@ public class CreateFlashSaleOrderEventHandler : IDistributedEventHandler<CreateF
UserId = eventData.UserId,
StoreId = eventData.StoreId,
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 CheckPreOrderAsync(Guid id);
Task CreateOrderAsync(Guid id, CreateOrderInput input);
Task<bool> OrderAsync(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 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(
GuidGenerator.Create(),
@ -120,8 +120,8 @@ public class FlashSalePlanAppService :
input.StoreId,
input.BeginTime,
input.EndTime,
input.ProductId,
input.ProductSkuId,
product.Id,
productSku.Id,
input.IsPublished
);
@ -138,7 +138,7 @@ public class FlashSalePlanAppService :
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))
{
@ -146,7 +146,7 @@ public class FlashSalePlanAppService :
}
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.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp);
@ -197,28 +197,7 @@ public class FlashSalePlanAppService :
await SetPreOrderCacheAsync(plan, product, productSku);
}
public virtual async Task CheckPreOrderAsync(Guid id)
{
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)
public virtual async Task<bool> OrderAsync(Guid id, CreateOrderInput input)
{
var plan = await GetFlashSalePlanCacheAsync(id);
var now = Clock.Now;
@ -257,18 +236,18 @@ public class FlashSalePlanAppService :
throw new BusinessException(FlashSalesErrorCodes.DuplicateFlashSalesOrder);
}
if (!await FlashSaleInventoryManager.TryIncreaseInventoryAsync(
if (!await FlashSaleInventoryManager.TryReduceInventoryAsync(
plan.TenantId, preOrderCache.InventoryProviderName,
plan.StoreId, plan.ProductId, plan.ProductSkuId, 1, true))
{
throw new BusinessException(FlashSalesErrorCodes.ProductSkuInventoryExceeded);
return false;
}
var result = await CreatePendingFlashSaleResultAsync(plan, userId, async (existsResultId) =>
{
await SetUserFlashSaleResultCacheAsync(plan, userId, existsResultId);
await FlashSaleInventoryManager.TryIncreaseInventoryAsync(
await FlashSaleInventoryManager.TryRollBackInventoryAsync(
plan.TenantId, preOrderCache.InventoryProviderName,
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);
await DistributedEventBus.PublishAsync(createFlashSaleOrderEto);
return true;
}
#region PreOrderCache
@ -394,18 +375,13 @@ public class FlashSalePlanAppService :
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)
{
throw new ProductIsNotInThisStoreException(productId, storeId);
}
if (productSku == null)
{
throw new ProductSkuIsNotFoundException(productSkuId);
}
if (product.InventoryStrategy != 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)
{
// 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)
{

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

@ -1,10 +1,11 @@
using System;
using EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans.Dtos;
using Volo.Abp.MultiTenancy;
namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans;
[Serializable]
public class FlashSalePlanCacheItem : FlashSalePlanDto
public class FlashSalePlanCacheItem : FlashSalePlanDto, IMultiTenant
{
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 GetListPolicyName { get; set; }
protected IFlashSaleResultRepository FlashSaleResultRepository { get; }
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));
}
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)
{
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 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);
}
[HttpGet("{id}/check-pre-order")]
public virtual Task CheckPreOrderAsync(Guid id)
{
return Service.CheckPreOrderAsync(id);
}
[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> 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);
return await LocalFlashSaleInventoryManager.TryIncreaseInventoryAsync(
return await LocalFlashSaleInventoryManager.TryRollBackInventoryAsync(
input.TenantId,
input.ProviderName,
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);
}
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)
{
var model = new InventoryQueryModel(tenantId, storeId, productId, productSkuId);

Loading…
Cancel
Save