diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationModule.cs index 1f38ffa1..6086e4d7 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationModule.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationModule.cs @@ -1,7 +1,5 @@ -using EasyAbp.EShop.Orders.Orders; -using EasyAbp.EShop.Plugins.FlashSales; +using EasyAbp.EShop.Plugins.FlashSales; using EasyAbp.EShop.Products; -using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AutoMapper; using Volo.Abp.Modularity; diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanAppService.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanAppService.cs index 49d27aee..86121c50 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanAppService.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanAppService.cs @@ -29,6 +29,21 @@ public class FlashSalePlanAppService : MultiStoreCrudAppService, IFlashSalePlanAppService { + /// + /// The cache key format. + /// {0}: FlashSalePlan ID + /// {1}: User ID + /// + public const string PreOrderCacheKeyFormat = "eshopflashsales_{0}_{1}"; + + /// + /// The cache key format. + /// {0}: Tenant ID + /// {1}: FlashSalePlan ID + /// {2}: User ID + /// + public const string UserFlashSaleResultCacheKeyFormat = "eshopflashsales-result_{0}_{1}_{2}"; + protected override string CrossStorePolicyName { get; set; } = FlashSalesPermissions.FlashSalePlan.CrossStore; protected override string GetPolicyName { get; set; } protected override string GetListPolicyName { get; set; } @@ -203,6 +218,12 @@ public class FlashSalePlanAppService : public virtual async Task OrderAsync(Guid id, CreateOrderInput input) { + var preOrderCache = await GetPreOrderCacheAsync(id); + if (preOrderCache == null) + { + throw new BusinessException(FlashSalesErrorCodes.PreOrderExpired); + } + var plan = await GetFlashSalePlanCacheAsync(id); var now = Clock.Now; if (plan.BeginTime > now) @@ -215,13 +236,7 @@ public class FlashSalePlanAppService : throw new BusinessException(FlashSalesErrorCodes.FlashSaleIsOver); } - var preOrderCache = await GetPreOrderCacheAsync(plan); - if (preOrderCache == null) - { - throw new BusinessException(FlashSalesErrorCodes.PreOrderExpired); - } - - await RemovePreOrderCacheAsync(plan); + await RemovePreOrderCacheAsync(id); var userId = CurrentUser.GetId(); @@ -234,7 +249,7 @@ public class FlashSalePlanAppService : throw new BusinessException(FlashSalesErrorCodes.BusyToCreateFlashSaleOrder); } - var userFlashSaleResultCache = await GetUserFlashSaleResultCacheAsync(plan, userId); + var userFlashSaleResultCache = await GetUserFlashSaleResultCacheAsync(plan.Id); if (!userFlashSaleResultCache.IsNullOrWhiteSpace()) { throw new BusinessException(FlashSalesErrorCodes.DuplicateFlashSalesOrder); @@ -249,7 +264,7 @@ public class FlashSalePlanAppService : var result = await CreatePendingFlashSaleResultAsync(plan, userId, async (existsResultId) => { - await SetUserFlashSaleResultCacheAsync(plan, userId, existsResultId); + await SetUserFlashSaleResultCacheAsync(plan.Id, existsResultId); await FlashSaleInventoryManager.TryRollBackInventoryAsync( plan.TenantId, preOrderCache.InventoryProviderName, @@ -257,7 +272,7 @@ public class FlashSalePlanAppService : ); }); - await SetUserFlashSaleResultCacheAsync(plan, userId, result.Id); + await SetUserFlashSaleResultCacheAsync(plan.Id, result.Id); var createFlashSaleOrderEto = await PrepareCreateFlashSaleOrderEtoAsync(plan, result.Id, input, userId, now, preOrderCache.HashToken); @@ -277,26 +292,26 @@ public class FlashSalePlanAppService : }); } - protected virtual Task GetPreOrderCacheKeyAsync(FlashSalePlanCacheItem plan) + protected virtual Task GetPreOrderCacheKeyAsync(Guid planId) { - return Task.FromResult($"eshopflashsales_{CurrentTenant.Id}_{CurrentUser.Id}_{plan.ProductSkuId}"); + return Task.FromResult(string.Format(PreOrderCacheKeyFormat, planId, CurrentUser.Id)); } - protected virtual async Task GetPreOrderCacheAsync(FlashSalePlanCacheItem plan) + protected virtual async Task GetPreOrderCacheAsync(Guid planId) { - return await PreOrderDistributedCache.GetAsync(await GetPreOrderCacheKeyAsync(plan)); + return await PreOrderDistributedCache.GetAsync(await GetPreOrderCacheKeyAsync(planId)); } - protected virtual async Task RemovePreOrderCacheAsync(FlashSalePlanCacheItem plan) + protected virtual async Task RemovePreOrderCacheAsync(Guid planId) { - await PreOrderDistributedCache.RemoveAsync(await GetPreOrderCacheKeyAsync(plan)); + await PreOrderDistributedCache.RemoveAsync(await GetPreOrderCacheKeyAsync(planId)); } protected virtual async Task SetPreOrderCacheAsync(FlashSalePlanCacheItem plan, ProductDto product, ProductSkuDto productSku, DateTimeOffset expirationTime) { var hashToken = await FlashSalePlanHasher.HashAsync(plan.LastModificationTime, product.LastModificationTime, productSku.LastModificationTime); - await PreOrderDistributedCache.SetAsync(await GetPreOrderCacheKeyAsync(plan), new FlashSalePlanPreOrderCacheItem() + await PreOrderDistributedCache.SetAsync(await GetPreOrderCacheKeyAsync(plan.Id), new FlashSalePlanPreOrderCacheItem() { HashToken = hashToken, PlanId = plan.Id, @@ -313,20 +328,20 @@ public class FlashSalePlanAppService : #region UserFlashSaleResultCache - protected virtual Task GetUserFlashSaleResultCacheKeyAsync(FlashSalePlanCacheItem plan, Guid userId) + protected virtual Task GetUserFlashSaleResultCacheKeyAsync(Guid planId) { - return Task.FromResult($"flash-sale-result-{plan.Id}-{userId}"); + return Task.FromResult(string.Format(UserFlashSaleResultCacheKeyFormat, CurrentTenant.Id, planId, CurrentUser.GetId())); } - protected virtual async Task GetUserFlashSaleResultCacheAsync(FlashSalePlanCacheItem plan, Guid userId) + protected virtual async Task GetUserFlashSaleResultCacheAsync(Guid planId) { - var userFlashSaleResultCacheKey = await GetUserFlashSaleResultCacheKeyAsync(plan, userId); + var userFlashSaleResultCacheKey = await GetUserFlashSaleResultCacheKeyAsync(planId); return await DistributedCache.GetStringAsync(userFlashSaleResultCacheKey); } - protected virtual async Task SetUserFlashSaleResultCacheAsync(FlashSalePlanCacheItem plan, Guid userId, Guid resultId) + protected virtual async Task SetUserFlashSaleResultCacheAsync(Guid planId, Guid resultId) { - var userFlashSaleResultCacheKey = await GetUserFlashSaleResultCacheKeyAsync(plan, userId); + var userFlashSaleResultCacheKey = await GetUserFlashSaleResultCacheKeyAsync(planId); await DistributedCache.SetStringAsync(userFlashSaleResultCacheKey, resultId.ToString()); } @@ -427,10 +442,7 @@ public class FlashSalePlanAppService : tenantId: CurrentTenant.Id, storeId: plan.StoreId, planId: plan.Id, - status: FlashSaleResultStatus.Pending, - reason: null, - userId: userId, - orderId: null + userId: userId ); return await FlashSaleResultRepository.InsertAsync(result, autoSave: true); diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanTokenCacheItem.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanTokenCacheItem.cs index 4aac4954..efaff0e9 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanTokenCacheItem.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanTokenCacheItem.cs @@ -1,10 +1,13 @@ using System; +using Volo.Abp.MultiTenancy; namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; [Serializable] -public class FlashSalePlanPreOrderCacheItem +public class FlashSalePlanPreOrderCacheItem : IMultiTenant { + public Guid? TenantId { get; set; } + public string HashToken { get; set; } public Guid PlanId { get; set; } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/RollBackInventoryCreateFlashSaleOrderCompleteEventHandler.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/RollBackInventoryCreateFlashSaleOrderCompleteEventHandler.cs index 42bcfbf4..6aad2f50 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/RollBackInventoryCreateFlashSaleOrderCompleteEventHandler.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/RollBackInventoryCreateFlashSaleOrderCompleteEventHandler.cs @@ -7,7 +7,6 @@ using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Logging; using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus.Distributed; -using Volo.Abp.Uow; namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalePlans; @@ -23,12 +22,14 @@ public class RollBackInventoryCreateFlashSaleOrderCompleteEventHandler : IDistri IFlashSaleInventoryManager flashSaleInventoryManager, IDistributedCache distributedCache, IFlashSalePlanRepository flashSalePlanRepository, - IProductAppService productAppService) + IProductAppService productAppService, + ILogger logger) { FlashSaleInventoryManager = flashSaleInventoryManager; DistributedCache = distributedCache; FlashSalePlanRepository = flashSalePlanRepository; ProductAppService = productAppService; + Logger = logger; } public virtual async Task HandleEventAsync(CreateFlashSaleOrderCompleteEto eventData) @@ -60,7 +61,7 @@ public class RollBackInventoryCreateFlashSaleOrderCompleteEventHandler : IDistri protected virtual Task GetUserFlashSaleResultCacheKeyAsync(FlashSalePlan plan, Guid userId) { - return Task.FromResult($"flash-sale-result-{plan.Id}-{userId}"); + return Task.FromResult(string.Format(FlashSalePlanAppService.UserFlashSaleResultCacheKeyFormat, plan.TenantId, plan.Id, userId)); } protected virtual async Task RemoveUserFlashSaleResultCacheAsync(FlashSalePlan plan, Guid userId) diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanHasher.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanHasher.cs index 8531b8a7..783c44b3 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanHasher.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalePlans/FlashSalePlanHasher.cs @@ -10,7 +10,7 @@ public class FlashSalePlanHasher : IFlashSalePlanHasher, ITransientDependency { public virtual Task HashAsync(DateTime? planLastModificationTime, DateTime? productLastModificationTime, DateTime? productSkuLastModificationTime) { - var input = $"{planLastModificationTime}|{productLastModificationTime}|{productSkuLastModificationTime}"; + var input = $"{planLastModificationTime?.Ticks}|{productLastModificationTime?.Ticks}|{productSkuLastModificationTime?.Ticks}"; return Task.FromResult(CreateMd5(input)); } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesErrorCodes.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesErrorCodes.cs index 5857aaba..a38555b3 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesErrorCodes.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesErrorCodes.cs @@ -27,4 +27,6 @@ public static class FlashSalesErrorCodes public const string DuplicateFlashSalesOrder = $"{Namespace}:{nameof(DuplicateFlashSalesOrder)}"; public const string RelatedFlashSaleResultsExist = $"{Namespace}:{nameof(RelatedFlashSaleResultsExist)}"; + + public const string FlashSaleResultStatusNotPending = $"{Namespace}:{nameof(FlashSaleResultStatusNotPending)}"; } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/en.json b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/en.json index a0863199..0b5b2c37 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/en.json +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/en.json @@ -13,6 +13,7 @@ "EasyAbp.EShop.Plugins.FlashSales:BusyToCreateFlashSaleOrder": "Busy to create flash-sale order", "EasyAbp.EShop.Plugins.FlashSales:DuplicateFlashSalesOrder": "Duplicate flash-sales order", "EasyAbp.EShop.Plugins.FlashSales:RelatedFlashSaleResultsExist": "Related flash-sales results exist", + "EasyAbp.EShop.Plugins.FlashSales:FlashSaleResultStatusNotPending": "The status of flash-sale result has been changed", "Menu:FlashSalesManagement": "Flash-Sales Management", "Permission:FlashSalePlan": "FlashSale Plan", "Permission:Manage": "Manage", diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/zh-Hans.json b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/zh-Hans.json index 2327818b..5aa756b8 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/zh-Hans.json +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/zh-Hans.json @@ -13,6 +13,7 @@ "EasyAbp.EShop.Plugins.FlashSales:BusyToCreateFlashSaleOrder": "闪购下单繁忙", "EasyAbp.EShop.Plugins.FlashSales:DuplicateFlashSalesOrder": "重复闪购下单", "EasyAbp.EShop.Plugins.FlashSales:RelatedFlashSaleResultsExist": "已存在关联的闪购结果", + "EasyAbp.EShop.Plugins.FlashSales:FlashSaleResultStatusNotPending": "闪购结果的状态已经变更", "Menu:FlashSalesManagement": "闪购", "Permission:FlashSalePlan": "闪购计划", "Permission:Manage": "管理", diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/zh-Hant.json b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/zh-Hant.json index 2b7767ee..6f7a3e84 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/zh-Hant.json +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/Localization/zh-Hant.json @@ -13,6 +13,7 @@ "EasyAbp.EShop.Plugins.FlashSales:BusyToCreateFlashSaleOrder": "閃購下單繁忙", "EasyAbp.EShop.Plugins.FlashSales:DuplicateFlashSalesOrder": "重複閃購下單", "EasyAbp.EShop.Plugins.FlashSales:RelatedFlashSaleResultsExist": "已存在關聯的閃購結果", + "EasyAbp.EShop.Plugins.FlashSales:FlashSaleResultStatusNotPending": "閃購結果的状态已經變更", "Menu:FlashSalesManagement": "閃購管理", "Permission:FlashSalePlan": "閃購計畫", "Permission:Manage": "管理", diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult.cs index 3d90d8ff..2617a14a 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResult.cs @@ -24,26 +24,32 @@ public class FlashSaleResult : FullAuditedAggregateRoot, IMultiTenant, IMu protected FlashSaleResult() { } - public FlashSaleResult(Guid id, Guid? tenantId, Guid storeId, Guid planId, FlashSaleResultStatus status, string reason, Guid userId, Guid? orderId) + public FlashSaleResult(Guid id, Guid? tenantId, Guid storeId, Guid planId, Guid userId) : base(id) { TenantId = tenantId; StoreId = storeId; PlanId = planId; - Status = status; - Reason = reason; + Status = FlashSaleResultStatus.Pending; UserId = userId; - OrderId = orderId; } public void MarkAsSuccessful(Guid orderId) { + if (Status != FlashSaleResultStatus.Pending) + { + throw new FlashSaleResultStatusNotPendingException(Id); + } Status = FlashSaleResultStatus.Successful; OrderId = orderId; } public void MarkAsFailed(string reason) { + if (Status != FlashSaleResultStatus.Pending) + { + throw new FlashSaleResultStatusNotPendingException(Id); + } Status = FlashSaleResultStatus.Failed; Reason = Check.NotNullOrEmpty(reason, nameof(reason)); } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultStatusNotPendingException.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultStatusNotPendingException.cs new file mode 100644 index 00000000..9402b683 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/FlashSaleResults/FlashSaleResultStatusNotPendingException.cs @@ -0,0 +1,12 @@ +using System; +using Volo.Abp; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSaleResults; + +public class FlashSaleResultStatusNotPendingException : BusinessException +{ + public FlashSaleResultStatusNotPendingException(Guid resultId) : base(FlashSalesErrorCodes.FlashSaleResultStatusNotPending) + { + WithData(nameof(resultId), resultId); + } +}