diff --git a/EShop.sln b/EShop.sln index 930dccee..44fc2380 100644 --- a/EShop.sln +++ b/EShop.sln @@ -413,6 +413,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Flash EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Orders.Plugins.FlashSales.Application", "plugins\FlashSales\src\EasyAbp.EShop.Orders.Plugins.FlashSales.Application\EasyAbp.EShop.Orders.Plugins.FlashSales.Application.csproj", "{5732E880-CB72-49A0-AC4F-A0620F4E4D16}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Products.Plugins.FlashSales.Application", "plugins\FlashSales\src\EasyAbp.EShop.Products.Plugins.FlashSales.Application\EasyAbp.EShop.Products.Plugins.FlashSales.Application.csproj", "{6AD2F468-D86C-4F9A-B280-3BCC15661C47}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1071,6 +1073,10 @@ Global {5732E880-CB72-49A0-AC4F-A0620F4E4D16}.Debug|Any CPU.Build.0 = Debug|Any CPU {5732E880-CB72-49A0-AC4F-A0620F4E4D16}.Release|Any CPU.ActiveCfg = Release|Any CPU {5732E880-CB72-49A0-AC4F-A0620F4E4D16}.Release|Any CPU.Build.0 = Release|Any CPU + {6AD2F468-D86C-4F9A-B280-3BCC15661C47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6AD2F468-D86C-4F9A-B280-3BCC15661C47}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6AD2F468-D86C-4F9A-B280-3BCC15661C47}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6AD2F468-D86C-4F9A-B280-3BCC15661C47}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1277,6 +1283,7 @@ Global {68993D65-2C9C-438F-8B94-15E98C469A16} = {9C180C9E-50E9-4624-BE06-5C8C24A028E4} {417AB8E2-1488-4814-9699-3B189D1ABA67} = {9C180C9E-50E9-4624-BE06-5C8C24A028E4} {5732E880-CB72-49A0-AC4F-A0620F4E4D16} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} + {6AD2F468-D86C-4F9A-B280-3BCC15661C47} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {28315BFD-90E7-4E14-A2EA-F3D23AF4126F} diff --git a/plugins/FlashSales/EasyAbp.EShop.Plugins.FlashSales.sln b/plugins/FlashSales/EasyAbp.EShop.Plugins.FlashSales.sln index 04554b96..11fd3b59 100644 --- a/plugins/FlashSales/EasyAbp.EShop.Plugins.FlashSales.sln +++ b/plugins/FlashSales/EasyAbp.EShop.Plugins.FlashSales.sln @@ -43,6 +43,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Flash EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.Installer", "src\EasyAbp.EShop.Plugins.FlashSales.Installer\EasyAbp.EShop.Plugins.FlashSales.Installer.csproj", "{BE39FD00-745B-4049-8161-FC129817CBE4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Orders.Plugins.FlashSales.Application", "src\EasyAbp.EShop.Orders.Plugins.FlashSales.Application\EasyAbp.EShop.Orders.Plugins.FlashSales.Application.csproj", "{AC7079C6-FEFE-4277-844B-2C9B8C21432D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Products.Plugins.FlashSales.Application", "src\EasyAbp.EShop.Products.Plugins.FlashSales.Application\EasyAbp.EShop.Products.Plugins.FlashSales.Application.csproj", "{5711E14D-ADF0-4BAB-BDDB-28A663A07442}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -117,6 +121,14 @@ Global {BE39FD00-745B-4049-8161-FC129817CBE4}.Debug|Any CPU.Build.0 = Debug|Any CPU {BE39FD00-745B-4049-8161-FC129817CBE4}.Release|Any CPU.ActiveCfg = Release|Any CPU {BE39FD00-745B-4049-8161-FC129817CBE4}.Release|Any CPU.Build.0 = Release|Any CPU + {AC7079C6-FEFE-4277-844B-2C9B8C21432D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC7079C6-FEFE-4277-844B-2C9B8C21432D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC7079C6-FEFE-4277-844B-2C9B8C21432D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC7079C6-FEFE-4277-844B-2C9B8C21432D}.Release|Any CPU.Build.0 = Release|Any CPU + {5711E14D-ADF0-4BAB-BDDB-28A663A07442}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5711E14D-ADF0-4BAB-BDDB-28A663A07442}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5711E14D-ADF0-4BAB-BDDB-28A663A07442}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5711E14D-ADF0-4BAB-BDDB-28A663A07442}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -139,6 +151,8 @@ Global {1EDCD6D4-DF3A-4E3B-ABB6-C0D0B373EAB8} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} {F6AC8D4A-EDD7-4514-8E8A-5BCB019864DB} = {E400416D-2895-4512-9D17-90681EEC7E0A} {BE39FD00-745B-4049-8161-FC129817CBE4} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {AC7079C6-FEFE-4277-844B-2C9B8C21432D} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {5711E14D-ADF0-4BAB-BDDB-28A663A07442} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Orders/CreateFlashSalesOrderEventHandler.cs b/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Orders/CreateFlashSalesOrderEventHandler.cs index 2deefa73..2e524250 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Orders/CreateFlashSalesOrderEventHandler.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Orders/CreateFlashSalesOrderEventHandler.cs @@ -41,9 +41,6 @@ public class CreateFlashSalesOrderEventHandler : IDistributedEventHandler(MemberList.Source) - .ForSourceMember(x => x.TenantId, opt => opt.DoNotValidate()) + CreateMap(MemberList.Destination) + .Ignore(dto => dto.Sold) + .Ignore(dto => dto.MinimumPrice) + .Ignore(dto => dto.MaximumPrice) .MapExtraProperties(); - CreateMap(MemberList.Source) - .ForMember(x => x.AttributeOptionIds, opt => opt.Ignore()) - .AfterMap((src, dest) => dest.AttributeOptionIds = jsonSerializer.Deserialize>(src.SerializedAttributeOptionIds)) + CreateMap(MemberList.Destination) + .ForSourceMember(entity => entity.SerializedAttributeOptionIds, opt => opt.DoNotValidate()) + .Ignore(dto => dto.DiscountedPrice) + .Ignore(dto => dto.Inventory) + .Ignore(dto => dto.Sold) .MapExtraProperties(); - CreateMap(MemberList.Source) + CreateMap(MemberList.Destination) .MapExtraProperties(); - CreateMap(MemberList.Source) + CreateMap(MemberList.Destination) .MapExtraProperties(); - CreateMap(MemberList.Source) + CreateMap(MemberList.Destination) .MapExtraProperties(); } } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationModule.cs similarity index 61% rename from plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationModule.cs rename to plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationModule.cs index a0ecf1f7..75ba925b 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationModule.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Orders.Plugins.FlashSales.Application/EasyAbp/EShop/Orders/Plugins/FlashSales/EShopOrdersPluginsFlashSalesApplicationModule.cs @@ -1,12 +1,12 @@ -using EasyAbp.EShop.Orders; -using EasyAbp.EShop.Orders.Orders; +using EasyAbp.EShop.Orders.Orders; +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; -namespace EasyAbp.EShop.Plugins.FlashSales; +namespace EasyAbp.EShop.Orders.Plugins.FlashSales; [DependsOn( typeof(EShopOrdersApplicationModule), @@ -22,16 +22,10 @@ public class EShopOrdersPluginsFlashSalesApplicationModule : AbpModule public override void ConfigureServices(ServiceConfigurationContext context) { - context.Services.AddAutoMapperObjectMapper(); + context.Services.AddAutoMapperObjectMapper(); Configure(options => { - options.Configurators.Add(abpAutoMapperConfigurationContext => - { - var profile = abpAutoMapperConfigurationContext.ServiceProvider - .GetRequiredService(); - - abpAutoMapperConfigurationContext.MapperConfiguration.AddProfile(profile); - }); + options.AddMaps(validate: true); }); } } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/Dtos/FlashSalesPlanUpdateDto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/Dtos/FlashSalesPlanUpdateDto.cs index 868f0657..36db2173 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/Dtos/FlashSalesPlanUpdateDto.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application.Contracts/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/Dtos/FlashSalesPlanUpdateDto.cs @@ -7,6 +7,8 @@ namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos; [Serializable] public class FlashSalesPlanUpdateDto : ExtensibleEntityDto, IHasConcurrencyStamp { + public Guid StoreId { get; set; } + public DateTime BeginTime { get; set; } public DateTime EndTime { get; set; } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesApplicationModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesApplicationModule.cs index 260175c9..c88bd54a 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesApplicationModule.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesApplicationModule.cs @@ -7,6 +7,7 @@ using Volo.Abp.Caching; using Volo.Abp.Modularity; namespace EasyAbp.EShop.Plugins.FlashSales; + [DependsOn( typeof(EShopProductsApplicationContractsModule), typeof(EShopPluginsFlashSalesDomainModule), @@ -23,14 +24,7 @@ public class EShopPluginsFlashSalesApplicationModule : AbpModule context.Services.AddAutoMapperObjectMapper(); Configure(options => { - options.Configurators.Add(abpAutoMapperConfigurationContext => - { - var profile = abpAutoMapperConfigurationContext.ServiceProvider - .GetRequiredService(); - - abpAutoMapperConfigurationContext.MapperConfiguration.AddProfile(profile); - }); + options.AddMaps(validate: true); }); - } } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesApplicationAutoMapperProfile.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesApplicationAutoMapperProfile.cs index a5bf39b1..8e597523 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesApplicationAutoMapperProfile.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesApplicationAutoMapperProfile.cs @@ -1,48 +1,28 @@ -using System.Linq; using AutoMapper; using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos; using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults.Dtos; -using EasyAbp.EShop.Products.ProductDetails.Dtos; -using EasyAbp.EShop.Products.Products.Dtos; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Json; namespace EasyAbp.EShop.Plugins.FlashSales; -public class FlashSalesApplicationAutoMapperProfile : Profile, ISingletonDependency +public class FlashSalesApplicationAutoMapperProfile : Profile { - public FlashSalesApplicationAutoMapperProfile(IJsonSerializer jsonSerializer) + public FlashSalesApplicationAutoMapperProfile() { /* You can configure your AutoMapper mapping configuration here. * Alternatively, you can split your mapping configurations * into multiple profile classes for a better organization. */ CreateMap() .MapExtraProperties(); - CreateMap() - .ForMember(x => x.TenantId, opt => opt.Ignore()) - .MapExtraProperties(); CreateMap(MemberList.Source); CreateMap(MemberList.Source); CreateMap() .MapExtraProperties(); - - CreateMap() + CreateMap() .MapExtraProperties(); - CreateMap() - .ForMember(x => x.TenantId, opt => opt.Ignore()) - .MapExtraProperties(); - CreateMap() - .ForMember(x => x.SerializedAttributeOptionIds, opt => opt.Ignore()) - .AfterMap((src, dest) => dest.SerializedAttributeOptionIds = jsonSerializer.Serialize(src.AttributeOptionIds.OrderBy(x => x))) - .MapExtraProperties(); - CreateMap() - .MapExtraProperties(); - CreateMap() - .MapExtraProperties(); - CreateMap() + CreateMap() .MapExtraProperties(); } } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanAppService.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanAppService.cs index 67e8ddeb..72794d3b 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanAppService.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanAppService.cs @@ -1,16 +1,13 @@ using System; using System.Linq; -using System.Numerics; -using System.Security.Cryptography; -using System.Text; using System.Threading.Tasks; using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos; using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; using EasyAbp.EShop.Plugins.FlashSales.Permissions; using EasyAbp.EShop.Products.ProductDetails; -using EasyAbp.EShop.Products.ProductDetails.Dtos; using EasyAbp.EShop.Products.Products; using EasyAbp.EShop.Products.Products.Dtos; +using EasyAbp.EShop.Stores.Authorization; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Caching.Distributed; using Volo.Abp; @@ -22,7 +19,6 @@ using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories; using Volo.Abp.EventBus.Distributed; using Volo.Abp.Users; -using static EasyAbp.EShop.Products.Permissions.ProductsPermissions; namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; @@ -38,15 +34,25 @@ public class FlashSalesPlanAppService : protected override string DeletePolicyName { get; set; } = FlashSalesPermissions.FlashSalesPlan.Delete; protected IFlashSalesPlanRepository FlashSalesPlanRepository { get; } + protected IProductAppService ProductAppService { get; } + protected IProductDetailAppService ProductDetailAppService { get; } + protected IDistributedCache TokenDistributedCache { get; } + protected IDistributedCache DistributedCache { get; } + protected IDistributedEventBus DistributedEventBus { get; } + protected FlashSalesPlanManager FlashSalesPlanManager { get; } + protected IFlashSalesResultRepository FlashSalesResultRepository { get; } + protected IAbpDistributedLock DistributedLock { get; } + protected IFlashSalesPlanHasher FlashSalesPlanHasher { get; } + public FlashSalesPlanAppService( IFlashSalesPlanRepository flashSalesPlanRepository, IProductAppService productAppService, @@ -56,7 +62,8 @@ public class FlashSalesPlanAppService : IDistributedEventBus distributedEventBus, FlashSalesPlanManager flashSalesPlanManager, IFlashSalesResultRepository flashSalesResultRepository, - IAbpDistributedLock distributedLock) + IAbpDistributedLock distributedLock, + IFlashSalesPlanHasher flashSalesPlanHasher) : base(flashSalesPlanRepository) { FlashSalesPlanRepository = flashSalesPlanRepository; @@ -68,6 +75,7 @@ public class FlashSalesPlanAppService : FlashSalesPlanManager = flashSalesPlanManager; FlashSalesResultRepository = flashSalesResultRepository; DistributedLock = distributedLock; + FlashSalesPlanHasher = flashSalesPlanHasher; } public override async Task GetAsync(Guid id) @@ -86,7 +94,7 @@ public class FlashSalesPlanAppService : public override async Task CreateAsync(FlashSalesPlanCreateDto input) { - await CheckCreatePolicyAsync(); + await AuthorizationService.CheckMultiStorePolicyAsync(input.StoreId, CreatePolicyName); var flashSalesPlan = await FlashSalesPlanManager.CreateAsync( input.StoreId, @@ -104,7 +112,7 @@ public class FlashSalesPlanAppService : public override async Task UpdateAsync(Guid id, FlashSalesPlanUpdateDto input) { - await CheckUpdatePolicyAsync(); + await AuthorizationService.CheckMultiStorePolicyAsync(input.StoreId, UpdatePolicyName); var flashSalesPlan = await GetEntityByIdAsync(id); @@ -124,6 +132,15 @@ public class FlashSalesPlanAppService : return await MapToGetOutputDtoAsync(flashSalesPlan); } + public override async Task DeleteAsync(Guid id) + { + var flashSalesPlan = await GetEntityByIdAsync(id); + + await AuthorizationService.CheckMultiStorePolicyAsync(flashSalesPlan.StoreId, DeletePolicyName); + + await FlashSalesPlanRepository.DeleteAsync(flashSalesPlan); + } + protected override async Task> CreateFilteredQueryAsync(FlashSalesPlanGetListInput input) { if (!input.OnlyShowPublished) @@ -212,58 +229,44 @@ public class FlashSalesPlanAppService : throw new BusinessException(FlashSalesErrorCodes.PreOrderExipred); } - var product = await ProductAppService.GetAsync(plan.ProductId); - var productSku = product.GetSkuById(plan.ProductSkuId); - - if (!await ComparekHashTokenAsync(cacheHashToken, plan, product, productSku)) - { - throw new BusinessException(FlashSalesErrorCodes.PreOrderExipred); - } - await RemoveCacheHashTokenAsync(plan); var userId = CurrentUser.GetId(); var result = await CreatePendingFlashSalesResultAsync(plan, userId); - var CreateFlashSalesOrderEto = await PrepareCreateFlashSalesOrderEtoAsync(plan, product, productSku, result, input, userId, now); + var flashSalesReduceInventoryEto = await PrepareFlashSalesReduceInventoryEtoAsync(plan, result.Id, input, userId, now, cacheHashToken); - await DistributedEventBus.PublishAsync(CreateFlashSalesOrderEto); + /* + * FlashSalesReduceInventoryEto(success) -> CreateFlashSalesOrderEto -> CreateFlashSalesOrderCompleteEto + * FlashSalesReduceInventoryEto(failed) -> CreateFlashSalesOrderCompleteEto + */ + await DistributedEventBus.PublishAsync(flashSalesReduceInventoryEto); } - protected virtual async Task PrepareCreateFlashSalesOrderEtoAsync( - FlashSalesPlanDto plan, - ProductDto product, - ProductSkuDto productSku, - FlashSalesResult result, + protected virtual Task PrepareFlashSalesReduceInventoryEtoAsync( + FlashSalesPlanCacheItem plan, + Guid resultId, CreateOrderInput input, Guid userId, - DateTime now) + DateTime now, + string hashToken) { - FlashSalesProductDetailEto productDetail = null; - var productDetailId = productSku.ProductDetailId ?? product.ProductDetailId; - if (productDetailId.HasValue) - { - productDetail = ObjectMapper.Map(await ProductDetailAppService.GetAsync(productDetailId.Value)); - } - var productEto = ObjectMapper.Map(product); - productEto.TenantId = CurrentTenant.Id; - var planEto = ObjectMapper.Map(plan); + var planEto = ObjectMapper.Map(plan); planEto.TenantId = CurrentTenant.Id; - var eto = new CreateFlashSalesOrderEto() + var eto = new FlashSalesReduceInventoryEto() { TenantId = CurrentTenant.Id, PlanId = plan.Id, UserId = userId, - PendingResultId = result.Id, + PendingResultId = resultId, StoreId = plan.StoreId, CreateTime = now, CustomerRemark = input.CustomerRemark, Quantity = 1,//should configure - Product = productEto, - ProductDetail = productDetail, - Plan = planEto + Plan = planEto, + HashToken = hashToken }; foreach (var item in input.ExtraProperties) @@ -271,7 +274,7 @@ public class FlashSalesPlanAppService : eto.ExtraProperties.Add(item.Key, item.Value); } - return eto; + return Task.FromResult(eto); } protected virtual async Task GetFlashSalesPlanCacheAsync(Guid id) @@ -283,54 +286,44 @@ public class FlashSalesPlanAppService : }); } - protected virtual Task GetCacheKeyAsync(FlashSalesPlanDto plan) + protected virtual Task GetCacheKeyAsync(FlashSalesPlanCacheItem plan) { return Task.FromResult($"eshopflashsales_{CurrentUser.Id}_{plan.ProductSkuId}"); } - protected virtual async Task GetCacheHashTokenAsync(FlashSalesPlanDto plan) + protected virtual async Task GetCacheHashTokenAsync(FlashSalesPlanCacheItem plan) { return await TokenDistributedCache.GetStringAsync(await GetCacheKeyAsync(plan)); } - protected virtual async Task RemoveCacheHashTokenAsync(FlashSalesPlanDto plan) + protected virtual async Task RemoveCacheHashTokenAsync(FlashSalesPlanCacheItem plan) { await TokenDistributedCache.RemoveAsync(await GetCacheKeyAsync(plan)); } - protected virtual async Task SetCacheHashTokenAsync(FlashSalesPlanDto plan, ProductDto product, ProductSkuDto productSku) + protected virtual async Task SetCacheHashTokenAsync(FlashSalesPlanCacheItem plan, ProductDto product, ProductSkuDto productSku) { - await TokenDistributedCache.SetStringAsync(await GetCacheKeyAsync(plan), await GetHashTokenAsync(plan, product, productSku), new DistributedCacheEntryOptions() + var hashToken = await FlashSalesPlanHasher.HashAsync(plan.LastModificationTime, product.LastModificationTime, productSku.LastModificationTime); + + await TokenDistributedCache.SetStringAsync(await GetCacheKeyAsync(plan), hashToken, new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(3) }); } - protected virtual Task GetHashTokenAsync(FlashSalesPlanDto plan, ProductDto product, ProductSkuDto productSku) - { - using var md5 = MD5.Create(); - var inputBytes = Encoding.UTF8.GetBytes($"{plan.LastModificationTime}|{product.LastModificationTime}|{productSku.LastModificationTime}"); - var sb = new StringBuilder(); - foreach (var t in md5.ComputeHash(inputBytes)) - { - sb.Append(t.ToString("X2")); - } - return Task.FromResult(sb.ToString()); - } - - protected virtual async Task ComparekHashTokenAsync(string cacheHashToken, FlashSalesPlanDto plan, ProductDto product, ProductSkuDto productSku) + protected virtual async Task ComparekHashTokenAsync(string cacheHashToken, FlashSalesPlanCacheItem plan, ProductDto product, ProductSkuDto productSku) { if (cacheHashToken.IsNullOrWhiteSpace()) { return false; } - var hashToken = await GetHashTokenAsync(plan, product, productSku); + var hashToken = await FlashSalesPlanHasher.HashAsync(plan.LastModificationTime, product.LastModificationTime, productSku.LastModificationTime); return cacheHashToken == hashToken; } - protected virtual async Task CreatePendingFlashSalesResultAsync(FlashSalesPlanDto plan, Guid userId) + protected virtual async Task CreatePendingFlashSalesResultAsync(FlashSalesPlanCacheItem plan, Guid userId) { var lockKey = $"create-flash-sales-order-{plan.Id}-{userId}"; @@ -342,7 +335,9 @@ public class FlashSalesPlanAppService : } // Prevent repeat submit - if (await FlashSalesResultRepository.AnyAsync(x => x.PlanId == plan.Id && x.UserId == userId)) + if (await FlashSalesResultRepository.AnyAsync(x => + x.PlanId == plan.Id && x.UserId == userId && (x.Status == FlashSalesResultStatus.Successful || x.Status == FlashSalesResultStatus.Pending)) + ) { throw new BusinessException(FlashSalesErrorCodes.AlreadySubmitCreateFlashSalesOrder); } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanCacheInvalidator.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanCacheInvalidator.cs index 7bc37dd3..94434dbd 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanCacheInvalidator.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanCacheInvalidator.cs @@ -1,16 +1,21 @@ using System; using System.Threading.Tasks; -using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans.Dtos; using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Entities.Events; using Volo.Abp.EventBus; namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; -public class FlashSalesPlanCacheInvalidator : ILocalEventHandler> +public class FlashSalesPlanCacheInvalidator : ILocalEventHandler>, ITransientDependency { protected IDistributedCache DistributedCache { get; } + public FlashSalesPlanCacheInvalidator(IDistributedCache distributedCache) + { + DistributedCache = distributedCache; + } + public virtual async Task HandleEventAsync(EntityChangedEventData eventData) { await DistributedCache.RemoveAsync(eventData.Entity.Id); diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanCacheItem.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanCacheItem.cs index f28bee5e..c6ea6537 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanCacheItem.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Application/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanCacheItem.cs @@ -6,4 +6,5 @@ namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; [Serializable] public class FlashSalesPlanCacheItem : FlashSalesPlanDto { + public Guid? TenantId { get; set; } } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/CreateFlashSalesOrderCompleteEto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/CreateFlashSalesOrderCompleteEto.cs index 5c6942b5..6427320b 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/CreateFlashSalesOrderCompleteEto.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/CreateFlashSalesOrderCompleteEto.cs @@ -1,5 +1,4 @@ using System; -using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectExtending; diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanEto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanEto.cs index 23450eca..06cc6fdd 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanEto.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanEto.cs @@ -4,6 +4,7 @@ using Volo.Abp.ObjectExtending; namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; +[Serializable] public class FlashSalesPlanEto : FullAuditedEntityEto, IMultiTenant { public Guid? TenantId { get; set; } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanHasher.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanHasher.cs new file mode 100644 index 00000000..069689c4 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlanHasher.cs @@ -0,0 +1,31 @@ +using System; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; + +public class FlashSalesPlanHasher : IFlashSalesPlanHasher, ITransientDependency +{ + public virtual Task HashAsync(DateTime? planLastModificationTime, DateTime? productLastModificationTime, DateTime? productSkuLastModificationTime) + { + var input = $"{planLastModificationTime}|{productLastModificationTime}|{productSkuLastModificationTime}"; + + return Task.FromResult(CreateMd5(input)); + } + + private static string CreateMd5(string input) + { + using var md5 = MD5.Create(); + + var inputBytes = Encoding.UTF8.GetBytes(input); + + var sb = new StringBuilder(); + foreach (var t in md5.ComputeHash(inputBytes)) + { + sb.Append(t.ToString("X2")); + } + return sb.ToString(); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductAttributeEto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductAttributeEto.cs index c26be79e..d68e06af 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductAttributeEto.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductAttributeEto.cs @@ -4,6 +4,7 @@ using EasyAbp.EShop.Products.Products; namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; +[Serializable] public class FlashSalesProductAttributeEto : FullAuditedEntityEto, IProductAttribute { public string DisplayName { get; set; } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductAttributeOptionEto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductAttributeOptionEto.cs index 3e613049..67116407 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductAttributeOptionEto.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductAttributeOptionEto.cs @@ -3,6 +3,7 @@ using EasyAbp.EShop.Products.Products; namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; +[Serializable] public class FlashSalesProductAttributeOptionEto : FullAuditedEntityEto, IProductAttributeOption { public string DisplayName { get; set; } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductEto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductEto.cs index 7a0a4a99..57922d52 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductEto.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductEto.cs @@ -5,6 +5,7 @@ using Volo.Abp.MultiTenancy; namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; +[Serializable] public class FlashSalesProductEto : FullAuditedEntityEto, IProduct, IMultiTenant { public Guid? TenantId { get; set; } @@ -13,6 +14,8 @@ public class FlashSalesProductEto : FullAuditedEntityEto, IProduct, IMulti public string ProductGroupName { get; set; } + public string ProductGroupDisplayName { get; set; } + public Guid? ProductDetailId { get; set; } public string UniqueName { get; set; } @@ -33,6 +36,8 @@ public class FlashSalesProductEto : FullAuditedEntityEto, IProduct, IMulti public bool IsHidden { get; set; } + public TimeSpan? PaymentExpireIn { get; set; } + public List ProductAttributes { get; set; } public List ProductSkus { get; set; } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductSkuEto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductSkuEto.cs index 09abe06c..3df32c9b 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductSkuEto.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesProductSkuEto.cs @@ -1,10 +1,14 @@ using System; +using System.Collections.Generic; using EasyAbp.EShop.Products.Products; namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; +[Serializable] public class FlashSalesProductSkuEto : FullAuditedEntityEto, IProductSku { + public List AttributeOptionIds { get; set; } + public string SerializedAttributeOptionIds { get; set; } public string Name { get; set; } @@ -19,6 +23,8 @@ public class FlashSalesProductSkuEto : FullAuditedEntityEto, IProductSku public int OrderMaxQuantity { get; set; } + public TimeSpan? PaymentExpireIn { get; set; } + public string MediaResources { get; set; } public Guid? ProductDetailId { get; set; } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesReduceInventoryEto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesReduceInventoryEto.cs new file mode 100644 index 00000000..4286811c --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesReduceInventoryEto.cs @@ -0,0 +1,29 @@ +using System; +using Volo.Abp.MultiTenancy; +using Volo.Abp.ObjectExtending; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; + +[Serializable] +public class FlashSalesReduceInventoryEto : ExtensibleObject, IMultiTenant +{ + public Guid? TenantId { get; set; } + + public Guid StoreId { get; set; } + + public Guid PlanId { get; set; } + + public Guid UserId { get; set; } + + public Guid PendingResultId { get; set; } + + public DateTime CreateTime { get; set; } + + public string CustomerRemark { get; set; } + + public int Quantity { get; set; } + + public FlashSalesPlanEto Plan { get; set; } + + public string HashToken { get; set; } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/IFlashSalesPlanHasher.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/IFlashSalesPlanHasher.cs new file mode 100644 index 00000000..153f7b11 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/IFlashSalesPlanHasher.cs @@ -0,0 +1,9 @@ +using System; +using System.Threading.Tasks; + +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; + +public interface IFlashSalesPlanHasher +{ + Task HashAsync(DateTime? planLastModificationTime, DateTime? productLastModificationTime, DateTime? productSkuLastModificationTime); +} \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/ProductDetailEto.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/ProductDetailEto.cs index d9ebef4d..71e05917 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/ProductDetailEto.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesPlans/ProductDetailEto.cs @@ -1,9 +1,13 @@ using System; +using Volo.Abp.MultiTenancy; namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; -public class FlashSalesProductDetailEto : FullAuditedEntityEto +[Serializable] +public class FlashSalesProductDetailEto : FullAuditedEntityEto, IMultiTenant { + public Guid? TenantId { get; set; } + public Guid? StoreId { get; set; } public string Description { get; set; } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesResults/FlashSalesResultFailedReason.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesResults/FlashSalesResultFailedReason.cs new file mode 100644 index 00000000..76f3d5c7 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain.Shared/EasyAbp/EShop/Plugins/FlashSales/FlashSalesResults/FlashSalesResultFailedReason.cs @@ -0,0 +1,8 @@ +namespace EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; + +public static class FlashSalesResultFailedReason +{ + public const string InsufficientInventory = nameof(InsufficientInventory); + + public const string PreOrderExipred = nameof(PreOrderExipred); +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainModule.cs index 0ce9dd05..826de7aa 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainModule.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Domain/EasyAbp/EShop/Plugins/FlashSales/EShopPluginsFlashSalesDomainModule.cs @@ -20,7 +20,7 @@ public class EShopPluginsFlashSalesDomainModule : AbpModule Configure(options => { - options.AddProfile(validate: true); + options.AddMaps(validate: true); }); Configure(options => diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlan/ViewModels/EditFlashSalesPlanViewModel.cs b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlan/ViewModels/EditFlashSalesPlanViewModel.cs index 972d0980..73ca7dfe 100644 --- a/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlan/ViewModels/EditFlashSalesPlanViewModel.cs +++ b/plugins/FlashSales/src/EasyAbp.EShop.Plugins.FlashSales.Web/Pages/EShop/Plugins/FlashSales/FlashSalesPlans/FlashSalesPlan/ViewModels/EditFlashSalesPlanViewModel.cs @@ -8,8 +8,6 @@ namespace EasyAbp.EShop.Plugins.FlashSales.Web.Pages.EShop.Plugins.FlashSales.Fl public class EditFlashSalesPlanViewModel : IHasConcurrencyStamp { - [DisabledInput] - [ReadOnlyInput] [Display(Name = "FlashSalesPlanStoreId")] public Guid StoreId { get; set; } diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp.EShop.Products.Plugins.FlashSales.Application.csproj b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp.EShop.Products.Plugins.FlashSales.Application.csproj new file mode 100644 index 00000000..308d5504 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp.EShop.Products.Plugins.FlashSales.Application.csproj @@ -0,0 +1,15 @@ + + + + + + net6.0 + + + + + + + + + diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationAutoMapperProfile.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationAutoMapperProfile.cs new file mode 100644 index 00000000..2e2636eb --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationAutoMapperProfile.cs @@ -0,0 +1,37 @@ +using System.Linq; +using AutoMapper; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; +using EasyAbp.EShop.Products.Options; +using EasyAbp.EShop.Products.ProductDetails; +using EasyAbp.EShop.Products.Products; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace EasyAbp.EShop.Plugins.FlashSales; + +public class EShopProductsPluginsFlashSalesApplicationAutoMapperProfile : Profile, ISingletonDependency +{ + public EShopProductsPluginsFlashSalesApplicationAutoMapperProfile( + IAttributeOptionIdsSerializer attributeOptionIdsSerializer, + IOptionsMonitor options) + { + CreateMap() + .ForMember(x => x.ProductGroupDisplayName, opt => opt.Ignore()) + .AfterMap((src, dest) => + { + var dict = options.CurrentValue.Groups.GetConfigurationsDictionary(); + dest.ProductGroupDisplayName = dict[src.ProductGroupName].DisplayName; + }) + .MapExtraProperties(); + CreateMap() + .ForMember(x => x.AttributeOptionIds, opt => opt.Ignore()) + .AfterMap(async (src, dest) => dest.AttributeOptionIds = (await attributeOptionIdsSerializer.DeserializeAsync(src.SerializedAttributeOptionIds)).ToList()) + .MapExtraProperties(); + CreateMap() + .MapExtraProperties(); + CreateMap() + .MapExtraProperties(); + CreateMap() + .MapExtraProperties(); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationModule.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationModule.cs new file mode 100644 index 00000000..a1a599dc --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Plugins/FlashSales/EShopProductsPluginsFlashSalesApplicationModule.cs @@ -0,0 +1,28 @@ +using EasyAbp.EShop.Plugins.FlashSales; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AutoMapper; +using Volo.Abp.Modularity; + +namespace EasyAbp.EShop.Products.Plugins.FlashSales; + +[DependsOn( + typeof(EShopProductsApplicationModule), + typeof(EShopPluginsFlashSalesApplicationContractsModule) +)] +public class EShopProductsPluginsFlashSalesApplicationModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAutoMapperObjectMapper(); + Configure(options => + { + options.Configurators.Add(abpAutoMapperConfigurationContext => + { + var profile = abpAutoMapperConfigurationContext.ServiceProvider + .GetRequiredService(); + + abpAutoMapperConfigurationContext.MapperConfiguration.AddProfile(profile); + }); + }); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/FlashSalesReduceInventoryEventHandler.cs b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/FlashSalesReduceInventoryEventHandler.cs new file mode 100644 index 00000000..d5a0375b --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/EasyAbp/EShop/Products/Products/FlashSalesReduceInventoryEventHandler.cs @@ -0,0 +1,113 @@ +using System.Linq; +using System.Threading.Tasks; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalesPlans; +using EasyAbp.EShop.Plugins.FlashSales.FlashSalesResults; +using EasyAbp.EShop.Products.ProductDetails; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.ObjectMapping; +using Volo.Abp.Uow; + +namespace EasyAbp.EShop.Products.Products; + +public class FlashSalesReduceInventoryEventHandler : IDistributedEventHandler, ITransientDependency +{ + protected IProductRepository ProductRepository { get; } + + protected IProductDetailRepository ProductDetailRepository { get; } + + protected ProductManager ProductManager { get; } + + protected IDistributedEventBus DistributedEventBus { get; } + + protected IObjectMapper ObjectMapper { get; } + + protected IFlashSalesPlanHasher FlashSalesPlanHasher { get; } + + public FlashSalesReduceInventoryEventHandler( + IProductRepository productRepository, + IProductDetailRepository productDetailRepository, + ProductManager productManager, + IDistributedEventBus distributedEventBus, + IObjectMapper objectMapper, + IFlashSalesPlanHasher flashSalesPlanHasher) + { + ProductRepository = productRepository; + ProductDetailRepository = productDetailRepository; + ProductManager = productManager; + DistributedEventBus = distributedEventBus; + ObjectMapper = objectMapper; + FlashSalesPlanHasher = flashSalesPlanHasher; + } + + [UnitOfWork] + public virtual async Task HandleEventAsync(FlashSalesReduceInventoryEto eventData) + { + var product = await ProductRepository.GetAsync(eventData.Plan.ProductId); + var productSku = product.ProductSkus.Single(x => x.Id == eventData.Plan.ProductSkuId); + + var hashToken = await FlashSalesPlanHasher.HashAsync(eventData.Plan.LastModificationTime, product.LastModificationTime, productSku.LastModificationTime); + + if (hashToken != eventData.HashToken) + { + await DistributedEventBus.PublishAsync(new CreateFlashSalesOrderCompleteEto() + { + TenantId = eventData.TenantId, + PlanId = eventData.PlanId, + OrderId = null, + UserId = eventData.UserId, + StoreId = eventData.StoreId, + PendingResultId = eventData.PendingResultId, + Success = false, + Reason = FlashSalesResultFailedReason.PreOrderExipred + }); + return; + } + + if (!await ProductManager.TryReduceInventoryAsync(product, productSku, eventData.Quantity, true)) + { + await DistributedEventBus.PublishAsync(new CreateFlashSalesOrderCompleteEto() + { + TenantId = eventData.TenantId, + PlanId = eventData.PlanId, + OrderId = null, + UserId = eventData.UserId, + StoreId = eventData.StoreId, + PendingResultId = eventData.PendingResultId, + Success = false, + Reason = FlashSalesResultFailedReason.InsufficientInventory + }); + return; + } + + var productDetailId = productSku.ProductDetailId ?? product.ProductDetailId; + + var productDetailEto = productDetailId.HasValue ? + ObjectMapper.Map(await ProductDetailRepository.GetAsync(productDetailId.Value)) : + null; + + var productEto = ObjectMapper.Map(product); + + var createFlashSalesOrderEto = new CreateFlashSalesOrderEto() + { + TenantId = eventData.TenantId, + PlanId = eventData.PlanId, + UserId = eventData.UserId, + PendingResultId = eventData.PendingResultId, + StoreId = eventData.StoreId, + CreateTime = eventData.CreateTime, + CustomerRemark = eventData.CustomerRemark, + Quantity = eventData.Quantity, + Product = productEto, + ProductDetail = productDetailEto, + Plan = eventData.Plan + }; + + foreach (var item in eventData.ExtraProperties) + { + createFlashSalesOrderEto.ExtraProperties.Add(item.Key, item.Value); + } + + await DistributedEventBus.PublishAsync(createFlashSalesOrderEto); + } +} diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/FodyWeavers.xml b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/FodyWeavers.xml new file mode 100644 index 00000000..00e1d9a1 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/FodyWeavers.xsd b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/FodyWeavers.xsd new file mode 100644 index 00000000..3f3946e2 --- /dev/null +++ b/plugins/FlashSales/src/EasyAbp.EShop.Products.Plugins.FlashSales.Application/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSample.Application.csproj b/samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSample.Application.csproj index c106fcf0..c2fe33cb 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSample.Application.csproj +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSample.Application.csproj @@ -14,6 +14,7 @@ + diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSampleApplicationModule.cs b/samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSampleApplicationModule.cs index 6ff2e91d..6e580e6f 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSampleApplicationModule.cs +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Application/EShopSampleApplicationModule.cs @@ -1,8 +1,10 @@ using EasyAbp.EShop; using EasyAbp.EShop.Orders.Plugins.Coupons; +using EasyAbp.EShop.Orders.Plugins.FlashSales; using EasyAbp.EShop.Plugins.Baskets; using EasyAbp.EShop.Plugins.Coupons; using EasyAbp.EShop.Plugins.FlashSales; +using EasyAbp.EShop.Products.Plugins.FlashSales; using EasyAbp.PaymentService; using EasyAbp.PaymentService.Prepayment; using EasyAbp.PaymentService.WeChatPay; @@ -30,6 +32,7 @@ namespace EShopSample typeof(EShopOrdersPluginsCouponsModule), typeof(EShopPluginsFlashSalesApplicationModule), typeof(EShopOrdersPluginsFlashSalesApplicationModule), + typeof(EShopProductsPluginsFlashSalesApplicationModule), typeof(PaymentServiceApplicationModule), typeof(PaymentServiceWeChatPayApplicationModule), typeof(PaymentServicePrepaymentApplicationModule)