From fccaeab2e749c7ecb09b5d28c442f03653255615 Mon Sep 17 00:00:00 2001 From: gdlcf88 Date: Tue, 18 Apr 2023 03:13:30 +0800 Subject: [PATCH] Batch discount products to improve performance --- .../Products/Products/Dtos/ProductSkuDto.cs | 2 +- .../Products/Products/ProductAppService.cs | 109 +++++++++------- .../Products/ProductViewAppService.cs | 116 ++++++++++-------- .../GetProductsRealTimePriceContext.cs | 46 +++++++ ...tsForProduct.cs => IHasDiscountsForSku.cs} | 2 +- ...rProduct.cs => IHasFullDiscountsForSku.cs} | 2 +- .../EShop/Products/Products/IProductView.cs | 2 +- .../Products/ProductDiscountContext.cs | 34 ----- .../Products/ProductRealTimePriceInfoModel.cs | 31 +++++ ...Models.cs => ProductViewDiscountModels.cs} | 4 +- .../Products/RealTimePriceInfoModel.cs | 21 ---- .../Products/DefaultProductPriceProvider.cs | 10 +- .../Products/IProductDiscountProvider.cs | 2 +- .../Products/IProductDiscountResolver.cs | 4 +- .../Products/Products/IProductManager.cs | 2 +- .../Products/IProductPriceProvider.cs | 5 +- .../Products/ProductAndSkuDataModel.cs | 29 +++++ .../Products/ProductDiscountElectionModel.cs | 6 +- .../Products/ProductDiscountResolver.cs | 34 +++-- .../EShop/Products/Products/ProductManager.cs | 16 ++- .../EShop/Products/Products/ProductView.cs | 6 +- ...ShopProductsEntityTypeBuilderExtensions.cs | 6 +- .../Products/DemoProductDiscountProvider.cs | 87 ++++++------- .../Baskets/BasketItems/IProductData.cs | 2 +- ...ShopProductsEntityTypeBuilderExtensions.cs | 6 +- .../Dtos/DiscountProductInputDto.cs | 4 +- .../Dtos/DiscountProductOutputDto.cs | 4 +- .../IPromotionIntegrationService.cs | 2 +- .../Promotions/PromotionIntegrationService.cs | 24 +++- .../PromotionTypes/IPromotionHandler.cs | 3 +- ...inQuantityOrderDiscountPromotionHandler.cs | 13 +- .../PromotionTypes/PromotionHandlerBase.cs | 3 +- .../SimpleProductDiscountPromotionHandler.cs | 9 +- .../PromotionProductDiscountProvider.cs | 23 +++- .../MinQuantityOrderDiscountTests.cs | 21 ++-- .../SimpleProductDiscountTests.cs | 23 ++-- 36 files changed, 417 insertions(+), 296 deletions(-) create mode 100644 modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/GetProductsRealTimePriceContext.cs rename modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/{IHasDiscountsForProduct.cs => IHasDiscountsForSku.cs} (92%) rename modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/{IHasFullDiscountsForProduct.cs => IHasFullDiscountsForSku.cs} (72%) delete mode 100644 modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductDiscountContext.cs create mode 100644 modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductRealTimePriceInfoModel.cs rename modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/{DiscountForProductModels.cs => ProductViewDiscountModels.cs} (76%) delete mode 100644 modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/RealTimePriceInfoModel.cs create mode 100644 modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductAndSkuDataModel.cs diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductSkuDto.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductSkuDto.cs index abb41df1..90a4a98f 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductSkuDto.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductSkuDto.cs @@ -5,7 +5,7 @@ using Volo.Abp.Application.Dtos; namespace EasyAbp.EShop.Products.Products.Dtos { [Serializable] - public class ProductSkuDto : ExtensibleFullAuditedEntityDto, IProductSku, IHasFullDiscountsForProduct + public class ProductSkuDto : ExtensibleFullAuditedEntityDto, IProductSku, IHasFullDiscountsForSku { public List AttributeOptionIds { get; set; } diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductAppService.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductAppService.cs index 0b85fe90..9c463059 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductAppService.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductAppService.cs @@ -82,8 +82,10 @@ namespace EasyAbp.EShop.Products.Products var dto = await MapToGetOutputDtoAsync(product); - await LoadDtoExtraDataAsync(product, dto, Clock.Now); - await LoadDtosProductGroupDisplayNameAsync(new[] { dto }); + var items = new List<(Product, ProductDto)> { new(product, dto) }; + + await LoadDtosExtraDataAsync(items, Clock.Now); + await LoadDtosProductGroupDisplayNameAsync(items); UnitOfWorkManager.Current.OnCompleted(async () => { await ClearProductViewCacheAsync(product.StoreId); }); @@ -116,8 +118,10 @@ namespace EasyAbp.EShop.Products.Products var dto = await MapToGetOutputDtoAsync(product); - await LoadDtoExtraDataAsync(product, dto, Clock.Now); - await LoadDtosProductGroupDisplayNameAsync(new[] { dto }); + var items = new List<(Product, ProductDto)> { new(product, dto) }; + + await LoadDtosExtraDataAsync(items, Clock.Now); + await LoadDtosProductGroupDisplayNameAsync(items); UnitOfWorkManager.Current.OnCompleted(async () => { await ClearProductViewCacheAsync(product.StoreId); }); @@ -201,19 +205,21 @@ namespace EasyAbp.EShop.Products.Products var dto = await MapToGetOutputDtoAsync(product); - await LoadDtoExtraDataAsync(product, dto, Clock.Now); - await LoadDtosProductGroupDisplayNameAsync(new[] { dto }); + var items = new List<(Product, ProductDto)> { new(product, dto) }; + + await LoadDtosExtraDataAsync(items, Clock.Now); + await LoadDtosProductGroupDisplayNameAsync(items); return dto; } - protected virtual Task LoadDtosProductGroupDisplayNameAsync(IEnumerable dtos) + protected virtual Task LoadDtosProductGroupDisplayNameAsync(List<(Product, ProductDto)> items) { var dict = _options.Groups.GetConfigurationsDictionary(); - foreach (var dto in dtos) + foreach (var (_, productDto) in items) { - dto.ProductGroupDisplayName = dict[dto.ProductGroupName].DisplayName; + productDto.ProductGroupDisplayName = dict[productDto.ProductGroupName].DisplayName; } return Task.CompletedTask; @@ -232,8 +238,10 @@ namespace EasyAbp.EShop.Products.Products var dto = await MapToGetOutputDtoAsync(product); - await LoadDtoExtraDataAsync(product, dto, Clock.Now); - await LoadDtosProductGroupDisplayNameAsync(new[] { dto }); + var items = new List<(Product, ProductDto)> { new(product, dto) }; + + await LoadDtosExtraDataAsync(items, Clock.Now); + await LoadDtosProductGroupDisplayNameAsync(items); return dto; } @@ -258,23 +266,21 @@ namespace EasyAbp.EShop.Products.Products var products = await AsyncExecuter.ToListAsync(query); var now = Clock.Now; - var items = new List(); + var items = new List<(Product, ProductDto)>(); foreach (var product in products) { - var productDto = await MapToGetListOutputDtoAsync(product); - - await LoadDtoExtraDataAsync(product, productDto, now); - - items.Add(productDto); + items.Add(new ValueTuple( + product, await MapToGetListOutputDtoAsync(product))); } + await LoadDtosExtraDataAsync(items, now); await LoadDtosProductGroupDisplayNameAsync(items); - return new PagedResultDto(totalCount, items); + return new PagedResultDto(totalCount, items.Select(x => x.Item2).ToList()); } - protected virtual async Task LoadDtoInventoryDataAsync(Product product, ProductDto productDto) + protected virtual async Task LoadDtoInventoryDataAsync(Product product, ProductDto productDto) { var models = product.ProductSkus.Select(x => new InventoryQueryModel(product.TenantId, product.StoreId, product.Id, x.Id)).ToList(); @@ -293,41 +299,46 @@ namespace EasyAbp.EShop.Products.Products productSkuDto.Sold = inventoryData.Sold; productDto.Sold += productSkuDto.Sold; } - - return productDto; } - protected virtual async Task LoadDtoExtraDataAsync(Product product, ProductDto productDto, - DateTime now) + protected virtual async Task LoadDtosExtraDataAsync(List<(Product, ProductDto)> items, DateTime now) { - await LoadDtoInventoryDataAsync(product, productDto); - await LoadDtoPriceDataAsync(product, productDto, now); + foreach (var (product, productDto) in items) + { + await LoadDtoInventoryDataAsync(product, productDto); + } - return productDto; + await LoadDtosPriceDataAsync(items, now); } - protected virtual async Task LoadDtoPriceDataAsync(Product product, ProductDto productDto, - DateTime now) + protected virtual async Task LoadDtosPriceDataAsync(List<(Product, ProductDto)> items, DateTime now) { - foreach (var productSku in product.ProductSkus) + var context = + await _productManager.GetRealTimePricesAsync( + ProductAndSkuDataModel.CreateByProducts(items.Select(x => x.Item1)).ToList(), now); + + foreach (var (product, productDto) in items) { - var productSkuDto = productDto.ProductSkus.First(x => x.Id == productSku.Id); + foreach (var productSku in product.ProductSkus) + { + var productSkuDto = productDto.ProductSkus.First(x => x.Id == productSku.Id); - var realTimePriceInfoModel = await _productManager.GetRealTimePriceAsync(product, productSku, now); + var realTimePriceInfoModel = context.GetRealTimePrice(productSku); - productSkuDto.PriceWithoutDiscount = realTimePriceInfoModel.PriceWithoutDiscount; - productSkuDto.Price = realTimePriceInfoModel.TotalDiscountedPrice; - productSkuDto.ProductDiscounts = realTimePriceInfoModel.Discounts.ProductDiscounts; - productSkuDto.OrderDiscountPreviews = realTimePriceInfoModel.Discounts.OrderDiscountPreviews; - } + productSkuDto.PriceWithoutDiscount = realTimePriceInfoModel.PriceWithoutDiscount; + productSkuDto.Price = realTimePriceInfoModel.TotalDiscountedPrice; + productSkuDto.ProductDiscounts = realTimePriceInfoModel.ProductDiscounts; + productSkuDto.OrderDiscountPreviews = realTimePriceInfoModel.OrderDiscountPreviews; + } + + if (productDto.ProductSkus.Count <= 0) + { + continue; + } - if (productDto.ProductSkus.Count > 0) - { productDto.MinimumPrice = productDto.ProductSkus.Min(sku => sku.Price); productDto.MaximumPrice = productDto.ProductSkus.Max(sku => sku.Price); } - - return productDto; } public override async Task DeleteAsync(Guid id) @@ -367,8 +378,10 @@ namespace EasyAbp.EShop.Products.Products var dto = await MapToGetOutputDtoAsync(product); - await LoadDtoExtraDataAsync(product, dto, Clock.Now); - await LoadDtosProductGroupDisplayNameAsync(new[] { dto }); + var items = new List<(Product, ProductDto)> { new(product, dto) }; + + await LoadDtosExtraDataAsync(items, Clock.Now); + await LoadDtosProductGroupDisplayNameAsync(items); UnitOfWorkManager.Current.OnCompleted(async () => { await ClearProductViewCacheAsync(product.StoreId); }); @@ -392,8 +405,10 @@ namespace EasyAbp.EShop.Products.Products var dto = await MapToGetOutputDtoAsync(product); - await LoadDtoExtraDataAsync(product, dto, Clock.Now); - await LoadDtosProductGroupDisplayNameAsync(new[] { dto }); + var items = new List<(Product, ProductDto)> { new(product, dto) }; + + await LoadDtosExtraDataAsync(items, Clock.Now); + await LoadDtosProductGroupDisplayNameAsync(items); UnitOfWorkManager.Current.OnCompleted(async () => { await ClearProductViewCacheAsync(product.StoreId); }); @@ -414,8 +429,10 @@ namespace EasyAbp.EShop.Products.Products var dto = await MapToGetOutputDtoAsync(product); - await LoadDtoExtraDataAsync(product, dto, Clock.Now); - await LoadDtosProductGroupDisplayNameAsync(new[] { dto }); + var items = new List<(Product, ProductDto)> { new(product, dto) }; + + await LoadDtosExtraDataAsync(items, Clock.Now); + await LoadDtosProductGroupDisplayNameAsync(items); UnitOfWorkManager.Current.OnCompleted(async () => { await ClearProductViewCacheAsync(product.StoreId); }); diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductViewAppService.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductViewAppService.cs index 536ccf3f..1468f42f 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductViewAppService.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductViewAppService.cs @@ -123,23 +123,23 @@ namespace EasyAbp.EShop.Products.Products await _repository.DeleteAsync(x => x.StoreId == storeId, true); - var productViews = new List(); + var productViews = new Dictionary(); foreach (var product in products) { var productView = ObjectMapper.Map(product); - productView.ProductDiscounts ??= new List(); - productView.OrderDiscountPreviews ??= new List(); - await FillPriceInfoWithRealPriceAsync(product, productView, now); - - productViews.Add(await _repository.InsertAsync(productView)); + productViews[product] = productView; } + await FillPriceInfoWithRealPriceAsync(productViews, now); + + await _repository.InsertManyAsync(productViews.Values, true); + await uow.SaveChangesAsync(); await uow.CompleteAsync(); - var duration = await GetCacheDurationOrNullAsync(productViews, now); + var duration = await GetCacheDurationOrNullAsync(productViews.Values.ToList(), now); if (duration.HasValue) { @@ -151,7 +151,7 @@ namespace EasyAbp.EShop.Products.Products } } - protected async Task GetCacheDurationOrNullAsync(List productViews, + protected virtual async Task GetCacheDurationOrNullAsync(List productViews, DateTime now) { // refresh the cache when a new discount takes effect or an old discount ends. @@ -191,75 +191,87 @@ namespace EasyAbp.EShop.Products.Products return duration.HasValue && duration.Value < defaultDuration ? duration : defaultDuration; } - protected virtual async Task FillPriceInfoWithRealPriceAsync(Product product, ProductView productView, + protected virtual async Task FillPriceInfoWithRealPriceAsync(Dictionary productViews, DateTime now) { - if (product.ProductSkus.IsNullOrEmpty()) + var models = new List(); + + foreach (var product in productViews.Keys) { - return; + models.AddRange(ProductAndSkuDataModel.CreateByProduct(product)); } - decimal? min = null, max = null; - decimal? minWithoutDiscount = null, maxWithoutDiscount = null; + var context = await _productManager.GetRealTimePricesAsync(models, now); - var discounts = new DiscountForProductModels(); - - foreach (var productSku in product.ProductSkus) + foreach (var (product, productView) in productViews) { - var overrideProductDiscounts = false; - var realTimePrice = await _productManager.GetRealTimePriceAsync(product, productSku, now); - var discountedPrice = realTimePrice.TotalDiscountedPrice; - - if (min is null || discountedPrice < min.Value) + if (product.ProductSkus.IsNullOrEmpty()) { - min = discountedPrice; - overrideProductDiscounts = true; + return; } - if (max is null || discountedPrice > max.Value) - { - max = discountedPrice; - } + decimal? min = null, max = null; + decimal? minWithoutDiscount = null, maxWithoutDiscount = null; - if (minWithoutDiscount is null || realTimePrice.PriceWithoutDiscount < minWithoutDiscount.Value) - { - minWithoutDiscount = realTimePrice.PriceWithoutDiscount; - } + var discounts = new ProductViewDiscountModels(); - if (maxWithoutDiscount is null || realTimePrice.PriceWithoutDiscount > maxWithoutDiscount.Value) + foreach (var productSku in product.ProductSkus) { - maxWithoutDiscount = realTimePrice.PriceWithoutDiscount; - } + var overrideProductDiscounts = false; + var realTimePrice = context.GetRealTimePrice(productSku); + var discountedPrice = realTimePrice.TotalDiscountedPrice; - foreach (var discount in realTimePrice.Discounts.ProductDiscounts) - { - var existingDiscount = - discounts.ProductDiscounts.Find(x => x.Name == discount.Name && x.Key == discount.Key); + if (min is null || discountedPrice < min.Value) + { + min = discountedPrice; + overrideProductDiscounts = true; + } - if (existingDiscount is null) + if (max is null || discountedPrice > max.Value) { - discounts.ProductDiscounts.Add(discount); + max = discountedPrice; } - else if (overrideProductDiscounts) + + if (minWithoutDiscount is null || realTimePrice.PriceWithoutDiscount < minWithoutDiscount.Value) { - discounts.ProductDiscounts.ReplaceOne(existingDiscount, discount); + minWithoutDiscount = realTimePrice.PriceWithoutDiscount; } - } - foreach (var discount in realTimePrice.Discounts.OrderDiscountPreviews) - { - var existingDiscount = - discounts.OrderDiscountPreviews.Find(x => x.Name == discount.Name && x.Key == discount.Key); + if (maxWithoutDiscount is null || realTimePrice.PriceWithoutDiscount > maxWithoutDiscount.Value) + { + maxWithoutDiscount = realTimePrice.PriceWithoutDiscount; + } - if (existingDiscount is null) + foreach (var discount in realTimePrice.ProductDiscounts) { - discounts.OrderDiscountPreviews.Add(discount); + var existingDiscount = + discounts.ProductDiscounts.Find(x => x.Name == discount.Name && x.Key == discount.Key); + + if (existingDiscount is null) + { + discounts.ProductDiscounts.Add(discount); + } + else if (overrideProductDiscounts) + { + discounts.ProductDiscounts.ReplaceOne(existingDiscount, discount); + } + } + + foreach (var discount in realTimePrice.OrderDiscountPreviews) + { + var existingDiscount = + discounts.OrderDiscountPreviews.Find(x => x.Name == discount.Name && x.Key == discount.Key); + + if (existingDiscount is null) + { + discounts.OrderDiscountPreviews.Add(discount); + } } } - } - productView.SetPrices(min, max, minWithoutDiscount, maxWithoutDiscount); - productView.SetDiscounts(discounts); + productView.SetPrices(min, max, minWithoutDiscount, maxWithoutDiscount); + productView.SetDiscounts(discounts); + } } } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/GetProductsRealTimePriceContext.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/GetProductsRealTimePriceContext.cs new file mode 100644 index 00000000..82db6df4 --- /dev/null +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/GetProductsRealTimePriceContext.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Volo.Abp; + +namespace EasyAbp.EShop.Products.Products; + +[Serializable] +public class GetProductsRealTimePriceContext +{ + public DateTime Now { get; } + + /// + /// ProductId to IProduct mapping. + /// + public Dictionary Products { get; } + + /// + /// ProductSkuId to ProductRealTimePriceInfoModel mapping. + /// + public Dictionary Models { get; } + + public GetProductsRealTimePriceContext(DateTime now, IEnumerable products, + IEnumerable models) + { + Now = now; + Products = Check.NotNull(products, nameof(products)).ToDictionary(x => x.Id); + Models = Check.NotNull(models, nameof(models)).ToDictionary(x => x.ProductSkuId); + } + + /// + /// Ctor for serializers. + /// + public GetProductsRealTimePriceContext(DateTime now, Dictionary products, + Dictionary models) + { + Now = now; + Products = products; + Models = models; + } + + public ProductRealTimePriceInfoModel GetRealTimePrice(IProductSku productSku) + { + return Models[productSku.Id]; + } +} \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IHasDiscountsForProduct.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IHasDiscountsForSku.cs similarity index 92% rename from modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IHasDiscountsForProduct.cs rename to modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IHasDiscountsForSku.cs index a05c7ac4..2e52bbd5 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IHasDiscountsForProduct.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IHasDiscountsForSku.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; namespace EasyAbp.EShop.Products.Products; -public interface IHasDiscountsForProduct +public interface IHasDiscountsForSku { /// /// The Price of the ProductSku has been subtracted from these product discounts. diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IHasFullDiscountsForProduct.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IHasFullDiscountsForSku.cs similarity index 72% rename from modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IHasFullDiscountsForProduct.cs rename to modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IHasFullDiscountsForSku.cs index fa976b2d..acecd696 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IHasFullDiscountsForProduct.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IHasFullDiscountsForSku.cs @@ -1,6 +1,6 @@ namespace EasyAbp.EShop.Products.Products; -public interface IHasFullDiscountsForProduct : IHasDiscountsForProduct +public interface IHasFullDiscountsForSku : IHasDiscountsForSku { /// /// The realtime price without subtracting the discount amount. diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProductView.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProductView.cs index 2e73ecd9..fad0296b 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProductView.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProductView.cs @@ -1,6 +1,6 @@ namespace EasyAbp.EShop.Products.Products; -public interface IProductView : IProductBase, IHasDiscountsForProduct, IHasProductGroupDisplayName +public interface IProductView : IProductBase, IHasDiscountsForSku, IHasProductGroupDisplayName { decimal? MinimumPrice { get; } diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductDiscountContext.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductDiscountContext.cs deleted file mode 100644 index e4f05456..00000000 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductDiscountContext.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using Volo.Abp; - -namespace EasyAbp.EShop.Products.Products; - -[Serializable] -public class ProductDiscountContext -{ - public DateTime Now { get; } - - public decimal PriceFromPriceProvider { get; } - - public IProduct Product { get; } - - public IProductSku ProductSku { get; } - - public List CandidateProductDiscounts { get; } - - public List OrderDiscountPreviews { get; } - - public ProductDiscountContext(DateTime now, IProduct product, IProductSku productSku, - decimal priceFromPriceProvider, List candidateProductDiscounts = null, - List orderDiscountPreviews = null) - { - Now = now; - Product = Check.NotNull(product, nameof(product)); - ProductSku = Check.NotNull(productSku, nameof(productSku)); - PriceFromPriceProvider = priceFromPriceProvider; - - CandidateProductDiscounts = candidateProductDiscounts ?? new List(); - OrderDiscountPreviews = orderDiscountPreviews ?? new List(); - } -} \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductRealTimePriceInfoModel.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductRealTimePriceInfoModel.cs new file mode 100644 index 00000000..8c6376e7 --- /dev/null +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductRealTimePriceInfoModel.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace EasyAbp.EShop.Products.Products; + +public class ProductRealTimePriceInfoModel : IHasDiscountsForSku +{ + public Guid ProductId { get; } + + public Guid ProductSkuId { get; } + + public decimal PriceWithoutDiscount { get; } + + public List CandidateProductDiscounts { get; } = new(); + + public List ProductDiscounts { get; } = new(); + + public List OrderDiscountPreviews { get; } = new(); + + public decimal TotalDiscountAmount => ProductDiscounts.Where(x => x.InEffect).Sum(x => x.DiscountedAmount); + + public decimal TotalDiscountedPrice => PriceWithoutDiscount - TotalDiscountAmount; + + public ProductRealTimePriceInfoModel(Guid productId, Guid productSkuId, decimal priceWithoutDiscount) + { + ProductId = productId; + ProductSkuId = productSkuId; + PriceWithoutDiscount = priceWithoutDiscount; + } +} \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/DiscountForProductModels.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductViewDiscountModels.cs similarity index 76% rename from modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/DiscountForProductModels.cs rename to modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductViewDiscountModels.cs index 8c6d8532..9a4cce28 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/DiscountForProductModels.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductViewDiscountModels.cs @@ -2,13 +2,13 @@ using System.Collections.Generic; namespace EasyAbp.EShop.Products.Products; -public class DiscountForProductModels : IHasDiscountsForProduct +public class ProductViewDiscountModels : IHasDiscountsForSku { public List ProductDiscounts { get; set; } public List OrderDiscountPreviews { get; set; } - public DiscountForProductModels(List productDiscounts = null, + public ProductViewDiscountModels(List productDiscounts = null, List orderDiscountPreviews = null) { ProductDiscounts = productDiscounts ?? new List(); diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/RealTimePriceInfoModel.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/RealTimePriceInfoModel.cs deleted file mode 100644 index d9385121..00000000 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/RealTimePriceInfoModel.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Linq; - -namespace EasyAbp.EShop.Products.Products; - -public class RealTimePriceInfoModel -{ - public decimal PriceWithoutDiscount { get; } - - public DiscountForProductModels Discounts { get; } - - public decimal TotalDiscountAmount => - Discounts.ProductDiscounts.Where(x => x.InEffect).Sum(x => x.DiscountedAmount); - - public decimal TotalDiscountedPrice => PriceWithoutDiscount - TotalDiscountAmount; - - public RealTimePriceInfoModel(decimal priceWithoutDiscount, DiscountForProductModels discounts) - { - PriceWithoutDiscount = priceWithoutDiscount; - Discounts = discounts; - } -} \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductPriceProvider.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductPriceProvider.cs index ace15079..f0fccf04 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductPriceProvider.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductPriceProvider.cs @@ -1,13 +1,17 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using Volo.Abp.DependencyInjection; namespace EasyAbp.EShop.Products.Products { public class DefaultProductPriceProvider : IProductPriceProvider, ITransientDependency { - public virtual Task GetPriceAsync(IProduct product, IProductSku productSku) + public virtual Task> GetPricesAsync( + IEnumerable models) { - return Task.FromResult(productSku.Price); + return Task.FromResult(models.Select(x => + new ProductRealTimePriceInfoModel(x.Product.Id, x.ProductSku.Id, x.ProductSku.Price)).ToList()); } } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductDiscountProvider.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductDiscountProvider.cs index 9b3e8ce1..ce7d838c 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductDiscountProvider.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductDiscountProvider.cs @@ -6,5 +6,5 @@ public interface IProductDiscountProvider { int EffectOrder { get; } - Task DiscountAsync(ProductDiscountContext context); + Task DiscountAsync(GetProductsRealTimePriceContext context); } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductDiscountResolver.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductDiscountResolver.cs index f8210c44..75e420e7 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductDiscountResolver.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductDiscountResolver.cs @@ -1,10 +1,8 @@ -using System; using System.Threading.Tasks; namespace EasyAbp.EShop.Products.Products; public interface IProductDiscountResolver { - Task ResolveAsync(IProduct product, IProductSku productSku, - decimal priceFromPriceProvider, DateTime now); + Task ResolveAsync(GetProductsRealTimePriceContext context); } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductManager.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductManager.cs index fbed0cc9..e4cc14ff 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductManager.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductManager.cs @@ -30,6 +30,6 @@ namespace EasyAbp.EShop.Products.Products Task TryReduceInventoryAsync(Product product, ProductSku productSku, int quantity, bool increaseSold); - Task GetRealTimePriceAsync(Product product, ProductSku productSku, DateTime now); + Task GetRealTimePricesAsync(List models, DateTime now); } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductPriceProvider.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductPriceProvider.cs index 1926441e..66beba7a 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductPriceProvider.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductPriceProvider.cs @@ -1,9 +1,10 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Threading.Tasks; namespace EasyAbp.EShop.Products.Products { public interface IProductPriceProvider { - Task GetPriceAsync(IProduct product, IProductSku productSku); + Task> GetPricesAsync(IEnumerable models); } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductAndSkuDataModel.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductAndSkuDataModel.cs new file mode 100644 index 00000000..a2361227 --- /dev/null +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductAndSkuDataModel.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Linq; + +namespace EasyAbp.EShop.Products.Products; + +public class ProductAndSkuDataModel +{ + public Product Product { get; } + + public ProductSku ProductSku { get; } + + public ProductAndSkuDataModel(Product product, ProductSku productSku) + { + Product = product; + ProductSku = productSku; + } + + public static IEnumerable CreateByProduct(Product product) + { + return product.ProductSkus.Select(sku => new ProductAndSkuDataModel(product, sku)); + } + + public static IEnumerable CreateByProducts(IEnumerable products) + { + return from product in products + from productSku in product.ProductSkus + select new ProductAndSkuDataModel(product, productSku); + } +} \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductDiscountElectionModel.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductDiscountElectionModel.cs index b00396ec..61747a0f 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductDiscountElectionModel.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductDiscountElectionModel.cs @@ -7,6 +7,8 @@ namespace EasyAbp.EShop.Products.Products; public class ProductDiscountElectionModel { + public DateTime Now { get; } + /// /// The search will stop and throw an exception if the number of executions is greater than . /// = Math.Pow(2, ) @@ -38,8 +40,10 @@ public class ProductDiscountElectionModel private HashSet UsedCombinations { get; } = new(); - public ProductDiscountElectionModel(IProduct product, IProductSku productSku, decimal priceFromPriceProvider) + public ProductDiscountElectionModel(DateTime now, IProduct product, IProductSku productSku, + decimal priceFromPriceProvider) { + Now = now; Product = product; ProductSku = productSku; PriceFromPriceProvider = priceFromPriceProvider; diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductDiscountResolver.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductDiscountResolver.cs index 21b944da..52ef48a9 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductDiscountResolver.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductDiscountResolver.cs @@ -10,36 +10,34 @@ public class ProductDiscountResolver : IProductDiscountResolver, ITransientDepen { public IAbpLazyServiceProvider LazyServiceProvider { get; set; } - public virtual async Task ResolveAsync(IProduct product, IProductSku productSku, - decimal priceFromPriceProvider, DateTime now) + public virtual async Task ResolveAsync(GetProductsRealTimePriceContext context) { - var context = new ProductDiscountContext(now, product, productSku, priceFromPriceProvider); - foreach (var provider in LazyServiceProvider.LazyGetService>() .OrderBy(x => x.EffectOrder)) { await provider.DiscountAsync(context); } - if (context.CandidateProductDiscounts.IsNullOrEmpty()) + foreach (var model in context.Models.Values) { - return new DiscountForProductModels(null, context.OrderDiscountPreviews); - } + var product = context.Products[model.ProductId]; + var productSku = product.GetSkuById(model.ProductSkuId); - var electionModel = - new ProductDiscountElectionModel(context.Product, context.ProductSku, context.PriceFromPriceProvider); + var electionModel = + new ProductDiscountElectionModel(context.Now, product, productSku, model.PriceWithoutDiscount); - electionModel.TryEnqueue(new CandidateProductDiscounts(context.CandidateProductDiscounts)); + electionModel.TryEnqueue(new CandidateProductDiscounts(model.CandidateProductDiscounts)); - while (!electionModel.Done) - { - await EvolveAsync(electionModel, now); - } + while (!electionModel.Done) + { + await EvolveAsync(electionModel); + } - return new DiscountForProductModels(electionModel.GetBestScheme().Discounts, context.OrderDiscountPreviews); + model.ProductDiscounts.AddRange(electionModel.GetBestScheme().Discounts); + } } - protected virtual Task EvolveAsync(ProductDiscountElectionModel electionModel, DateTime now) + protected virtual Task EvolveAsync(ProductDiscountElectionModel electionModel) { if (electionModel.Done) { @@ -60,8 +58,8 @@ public class ProductDiscountResolver : IProductDiscountResolver, ITransientDepen var discount = new ProductDiscountInfoModel(candidate, 0m, false); productDiscountInfoModels.Add(discount); - if (candidate.FromTime.HasValue && now < candidate.FromTime || - candidate.ToTime.HasValue && now > candidate.ToTime) + if (candidate.FromTime.HasValue && electionModel.Now < candidate.FromTime || + candidate.ToTime.HasValue && electionModel.Now > candidate.ToTime) { continue; } diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductManager.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductManager.cs index 61834a5b..cd923a35 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductManager.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductManager.cs @@ -271,15 +271,19 @@ namespace EasyAbp.EShop.Products.Products .TryReduceInventoryAsync(model, quantity, increaseSold, isFlashSale); } - public virtual async Task GetRealTimePriceAsync(Product product, ProductSku productSku, - DateTime now) + public virtual async Task GetRealTimePricesAsync( + List models, DateTime now) { - var priceFromPriceProvider = await _productPriceProvider.GetPriceAsync(product, productSku); + var realTimePriceInfoModels = await _productPriceProvider.GetPricesAsync(models); - var discounts = - await _productDiscountResolver.ResolveAsync(product, productSku, priceFromPriceProvider, now); + var context = new GetProductsRealTimePriceContext( + now, + models.Select(x => x.Product).Distinct(), + realTimePriceInfoModels); - return new RealTimePriceInfoModel(priceFromPriceProvider, discounts); + await _productDiscountResolver.ResolveAsync(context); + + return context; } } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductView.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductView.cs index 173c7e1b..a92291a5 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductView.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductView.cs @@ -127,10 +127,10 @@ namespace EasyAbp.EShop.Products.Products MaximumPriceWithoutDiscount = maxWithoutDiscount; } - public void SetDiscounts(IHasDiscountsForProduct discountsForProduct) + public void SetDiscounts(IHasDiscountsForSku discountsForSku) { - ProductDiscounts = discountsForProduct.ProductDiscounts ?? new List(); - OrderDiscountPreviews = discountsForProduct.OrderDiscountPreviews ?? new List(); + ProductDiscounts = discountsForSku.ProductDiscounts ?? new List(); + OrderDiscountPreviews = discountsForSku.OrderDiscountPreviews ?? new List(); } } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.EntityFrameworkCore/EasyAbp/EShop/Products/EntityFrameworkCore/ValueMappings/EShopProductsEntityTypeBuilderExtensions.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.EntityFrameworkCore/EasyAbp/EShop/Products/EntityFrameworkCore/ValueMappings/EShopProductsEntityTypeBuilderExtensions.cs index b7247022..ce028752 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.EntityFrameworkCore/EasyAbp/EShop/Products/EntityFrameworkCore/ValueMappings/EShopProductsEntityTypeBuilderExtensions.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.EntityFrameworkCore/EasyAbp/EShop/Products/EntityFrameworkCore/ValueMappings/EShopProductsEntityTypeBuilderExtensions.cs @@ -18,12 +18,12 @@ public static class EShopProductsEntityTypeBuilderExtensions public static void TryConfigureDiscountsInfo(this EntityTypeBuilder b) { - if (b.Metadata.ClrType.IsAssignableTo()) + if (b.Metadata.ClrType.IsAssignableTo()) { - b.Property(nameof(IHasDiscountsForProduct.ProductDiscounts)) + b.Property(nameof(IHasDiscountsForSku.ProductDiscounts)) .HasConversion() .Metadata.SetValueComparer(new ProductDiscountsInfoValueComparer()); - b.Property(nameof(IHasDiscountsForProduct.OrderDiscountPreviews)) + b.Property(nameof(IHasDiscountsForSku.OrderDiscountPreviews)) .HasConversion() .Metadata.SetValueComparer(new OrderDiscountPreviewsInfoValueComparer()); } diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/Products/DemoProductDiscountProvider.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/Products/DemoProductDiscountProvider.cs index a6b1b770..4f8eba9a 100644 --- a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/Products/DemoProductDiscountProvider.cs +++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/Products/DemoProductDiscountProvider.cs @@ -17,53 +17,58 @@ public class DemoProductDiscountProvider : IProductDiscountProvider public int EffectOrder => DemoProductDiscountEffectOrder; - public Task DiscountAsync(ProductDiscountContext context) + public Task DiscountAsync(GetProductsRealTimePriceContext context) { - if (context.Product.Id != ProductsTestData.Product1Id || - context.ProductSku.Id != ProductsTestData.Product1Sku1Id) + foreach (var model in context.Models.Values) { - return Task.CompletedTask; - } + if (model.ProductId != ProductsTestData.Product1Id || + model.ProductSkuId != ProductsTestData.Product1Sku1Id) + { + return Task.CompletedTask; + } - var candidates = new List - { - // These should take effect: - new(null, "DemoDiscount", "1", "Demo Discount 1", - new DynamicDiscountAmountModel("USD", 0.10m, 0m, null), null, null), - new(null, "DemoDiscount", "2", "Demo Discount 2", - new DynamicDiscountAmountModel("USD", 0.10m, 0m, null), _clock.Now.AddDays(-1), null), - new(null, "DemoDiscount", "3", "Demo Discount 3", - new DynamicDiscountAmountModel("USD", 0.10m, 0m, null), null, _clock.Now.AddDays(1)), - new(null, "DemoDiscount", "4", "Demo Discount 4", - new DynamicDiscountAmountModel("USD", 0.10m, 0m, null), _clock.Now.AddDays(-1), _clock.Now.AddDays(1)), - // These should not take effect: - new(null, "DemoDiscount", "5", "Demo Discount 5", - new DynamicDiscountAmountModel("USD", 0.10m, 0m, null), null, _clock.Now.AddDays(-1)), - new(null, "DemoDiscount", "6", "Demo Discount 6", - new DynamicDiscountAmountModel("USD", 0.10m, 0m, null), _clock.Now.AddDays(1), null), - new(null, "DemoDiscount", "7", "Demo Discount 7", - new DynamicDiscountAmountModel("USD", 0.10m, 0m, null), _clock.Now.AddDays(1), _clock.Now.AddDays(2)), - // Only the one with the highest discount amount should take effect: - new("A", "DemoDiscount", "8", "Demo Discount 8", - new DynamicDiscountAmountModel("USD", 0.10m, 0m, null), null, null), - new("A", "DemoDiscount", "9", "Demo Discount 9", - new DynamicDiscountAmountModel("USD", 0.01m, 0m, null), null, null), - }; + var candidates = new List + { + // These should take effect: + new(null, "DemoDiscount", "1", "Demo Discount 1", + new DynamicDiscountAmountModel("USD", 0.10m, 0m, null), null, null), + new(null, "DemoDiscount", "2", "Demo Discount 2", + new DynamicDiscountAmountModel("USD", 0.10m, 0m, null), _clock.Now.AddDays(-1), null), + new(null, "DemoDiscount", "3", "Demo Discount 3", + new DynamicDiscountAmountModel("USD", 0.10m, 0m, null), null, _clock.Now.AddDays(1)), + new(null, "DemoDiscount", "4", "Demo Discount 4", + new DynamicDiscountAmountModel("USD", 0.10m, 0m, null), _clock.Now.AddDays(-1), + _clock.Now.AddDays(1)), + // These should not take effect: + new(null, "DemoDiscount", "5", "Demo Discount 5", + new DynamicDiscountAmountModel("USD", 0.10m, 0m, null), null, _clock.Now.AddDays(-1)), + new(null, "DemoDiscount", "6", "Demo Discount 6", + new DynamicDiscountAmountModel("USD", 0.10m, 0m, null), _clock.Now.AddDays(1), null), + new(null, "DemoDiscount", "7", "Demo Discount 7", + new DynamicDiscountAmountModel("USD", 0.10m, 0m, null), _clock.Now.AddDays(1), + _clock.Now.AddDays(2)), + // Only the one with the highest discount amount should take effect: + new("A", "DemoDiscount", "8", "Demo Discount 8", + new DynamicDiscountAmountModel("USD", 0.10m, 0m, null), null, null), + new("A", "DemoDiscount", "9", "Demo Discount 9", + new DynamicDiscountAmountModel("USD", 0.01m, 0m, null), null, null), + }; - foreach (var model in candidates) - { - context.CandidateProductDiscounts.Add(model); - } + foreach (var candidate in candidates) + { + model.CandidateProductDiscounts.Add(candidate); + } - var orderDiscountPreviewInfoModels = new List - { - new(null, "DemoDiscount", "1", "Demo Discount 1", null, null, null), - new(null, "DemoDiscount", "2", "Demo Discount 2", _clock.Now.AddDays(-1), _clock.Now.AddDays(1), null), - }; + var orderDiscountPreviewInfoModels = new List + { + new(null, "DemoDiscount", "1", "Demo Discount 1", null, null, null), + new(null, "DemoDiscount", "2", "Demo Discount 2", _clock.Now.AddDays(-1), _clock.Now.AddDays(1), null), + }; - foreach (var model in orderDiscountPreviewInfoModels) - { - context.OrderDiscountPreviews.Add(model); + foreach (var preview in orderDiscountPreviewInfoModels) + { + model.OrderDiscountPreviews.Add(preview); + } } return Task.CompletedTask; diff --git a/plugins/Baskets/src/EasyAbp.EShop.Plugins.Baskets.Domain.Shared/EasyAbp/EShop/Plugins/Baskets/BasketItems/IProductData.cs b/plugins/Baskets/src/EasyAbp.EShop.Plugins.Baskets.Domain.Shared/EasyAbp/EShop/Plugins/Baskets/BasketItems/IProductData.cs index afc8c5db..dbb82548 100644 --- a/plugins/Baskets/src/EasyAbp.EShop.Plugins.Baskets.Domain.Shared/EasyAbp/EShop/Plugins/Baskets/BasketItems/IProductData.cs +++ b/plugins/Baskets/src/EasyAbp.EShop.Plugins.Baskets.Domain.Shared/EasyAbp/EShop/Plugins/Baskets/BasketItems/IProductData.cs @@ -2,7 +2,7 @@ namespace EasyAbp.EShop.Plugins.Baskets.BasketItems { - public interface IProductData : IHasFullDiscountsForProduct + public interface IProductData : IHasFullDiscountsForSku { string MediaResources { get; } diff --git a/plugins/Baskets/src/EasyAbp.EShop.Plugins.Baskets.EntityFrameworkCore/EasyAbp/EShop/Plugins/Baskets/EntityFrameworkCore/ValueMappings/EShopProductsEntityTypeBuilderExtensions.cs b/plugins/Baskets/src/EasyAbp.EShop.Plugins.Baskets.EntityFrameworkCore/EasyAbp/EShop/Plugins/Baskets/EntityFrameworkCore/ValueMappings/EShopProductsEntityTypeBuilderExtensions.cs index 8134954a..35004a36 100644 --- a/plugins/Baskets/src/EasyAbp.EShop.Plugins.Baskets.EntityFrameworkCore/EasyAbp/EShop/Plugins/Baskets/EntityFrameworkCore/ValueMappings/EShopProductsEntityTypeBuilderExtensions.cs +++ b/plugins/Baskets/src/EasyAbp.EShop.Plugins.Baskets.EntityFrameworkCore/EasyAbp/EShop/Plugins/Baskets/EntityFrameworkCore/ValueMappings/EShopProductsEntityTypeBuilderExtensions.cs @@ -8,12 +8,12 @@ public static class EShopProductsEntityTypeBuilderExtensions { public static void TryConfigureDiscountsInfo(this EntityTypeBuilder b) { - if (b.Metadata.ClrType.IsAssignableTo()) + if (b.Metadata.ClrType.IsAssignableTo()) { - b.Property(nameof(IHasDiscountsForProduct.ProductDiscounts)) + b.Property(nameof(IHasDiscountsForSku.ProductDiscounts)) .HasConversion() .Metadata.SetValueComparer(new ProductDiscountsInfoValueComparer()); - b.Property(nameof(IHasDiscountsForProduct.OrderDiscountPreviews)) + b.Property(nameof(IHasDiscountsForSku.OrderDiscountPreviews)) .HasConversion() .Metadata.SetValueComparer(new OrderDiscountPreviewsInfoValueComparer()); } diff --git a/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Application.Contracts/EasyAbp/EShop/Plugins/Promotions/Promotions/Dtos/DiscountProductInputDto.cs b/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Application.Contracts/EasyAbp/EShop/Plugins/Promotions/Promotions/Dtos/DiscountProductInputDto.cs index 3870ff78..229596f8 100644 --- a/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Application.Contracts/EasyAbp/EShop/Plugins/Promotions/Promotions/Dtos/DiscountProductInputDto.cs +++ b/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Application.Contracts/EasyAbp/EShop/Plugins/Promotions/Promotions/Dtos/DiscountProductInputDto.cs @@ -6,13 +6,13 @@ namespace EasyAbp.EShop.Plugins.Promotions.Promotions.Dtos; [Serializable] public class DiscountProductInputDto { - public ProductDiscountContext Context { get; set; } + public GetProductsRealTimePriceContext Context { get; set; } public DiscountProductInputDto() { } - public DiscountProductInputDto(ProductDiscountContext context) + public DiscountProductInputDto(GetProductsRealTimePriceContext context) { Context = context; } diff --git a/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Application.Contracts/EasyAbp/EShop/Plugins/Promotions/Promotions/Dtos/DiscountProductOutputDto.cs b/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Application.Contracts/EasyAbp/EShop/Plugins/Promotions/Promotions/Dtos/DiscountProductOutputDto.cs index 1ad889ae..2033c7df 100644 --- a/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Application.Contracts/EasyAbp/EShop/Plugins/Promotions/Promotions/Dtos/DiscountProductOutputDto.cs +++ b/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Application.Contracts/EasyAbp/EShop/Plugins/Promotions/Promotions/Dtos/DiscountProductOutputDto.cs @@ -6,13 +6,13 @@ namespace EasyAbp.EShop.Plugins.Promotions.Promotions.Dtos; [Serializable] public class DiscountProductOutputDto { - public ProductDiscountContext Context { get; set; } + public GetProductsRealTimePriceContext Context { get; set; } public DiscountProductOutputDto() { } - public DiscountProductOutputDto(ProductDiscountContext context) + public DiscountProductOutputDto(GetProductsRealTimePriceContext context) { Context = context; } diff --git a/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Application.Contracts/EasyAbp/EShop/Plugins/Promotions/Promotions/IPromotionIntegrationService.cs b/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Application.Contracts/EasyAbp/EShop/Plugins/Promotions/Promotions/IPromotionIntegrationService.cs index 41999e81..51188157 100644 --- a/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Application.Contracts/EasyAbp/EShop/Plugins/Promotions/Promotions/IPromotionIntegrationService.cs +++ b/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Application.Contracts/EasyAbp/EShop/Plugins/Promotions/Promotions/IPromotionIntegrationService.cs @@ -7,7 +7,7 @@ namespace EasyAbp.EShop.Plugins.Promotions.Promotions; [IntegrationService] public interface IPromotionIntegrationService { - Task DiscountProductAsync(DiscountProductInputDto input); + Task DiscountProductsAsync(DiscountProductInputDto input); Task DiscountOrderAsync(DiscountOrderInputDto input); } \ No newline at end of file diff --git a/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Application/EasyAbp/EShop/Plugins/Promotions/Promotions/PromotionIntegrationService.cs b/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Application/EasyAbp/EShop/Plugins/Promotions/Promotions/PromotionIntegrationService.cs index ee17511b..86b009ac 100644 --- a/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Application/EasyAbp/EShop/Plugins/Promotions/Promotions/PromotionIntegrationService.cs +++ b/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Application/EasyAbp/EShop/Plugins/Promotions/Promotions/PromotionIntegrationService.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using EasyAbp.EShop.Plugins.Promotions.Options; using EasyAbp.EShop.Plugins.Promotions.Promotions.Dtos; using EasyAbp.EShop.Plugins.Promotions.PromotionTypes; +using EasyAbp.EShop.Products.Products; using Microsoft.Extensions.Options; using Volo.Abp; using Volo.Abp.Application.Services; @@ -24,17 +25,30 @@ public class PromotionIntegrationService : ApplicationService, IPromotionIntegra Options = options.Value; } - public virtual async Task DiscountProductAsync(DiscountProductInputDto input) + public virtual async Task DiscountProductsAsync(DiscountProductInputDto input) { var context = input.Context; - var promotions = await GetUnexpiredPromotionsAsync(context.Product.StoreId, context.Now, true); + var storeGroupings = input.Context.Models.GroupBy(x => input.Context.Products[x.Value.ProductId].StoreId); - foreach (var promotion in promotions.OrderByDescending(x => x.Priority)) + foreach (var storeGrouping in storeGroupings) { - var promotionHandler = GetPromotionHandler(promotion.PromotionType); + var storeId = storeGrouping.Key; + + var promotions = await GetUnexpiredPromotionsAsync(storeId, context.Now, true); + + foreach (var promotion in promotions.OrderByDescending(x => x.Priority)) + { + var promotionHandler = GetPromotionHandler(promotion.PromotionType); + + foreach (var (_, model) in storeGrouping) + { + var product = context.Products[model.ProductId]; + var productSku = product.GetSkuById(model.ProductSkuId); - await promotionHandler.HandleProductAsync(context, promotion); + await promotionHandler.HandleProductAsync(model, promotion, product, productSku); + } + } } return new DiscountProductOutputDto(context); diff --git a/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Domain/EasyAbp/EShop/Plugins/Promotions/PromotionTypes/IPromotionHandler.cs b/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Domain/EasyAbp/EShop/Plugins/Promotions/PromotionTypes/IPromotionHandler.cs index 603b73a9..c2aa0770 100644 --- a/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Domain/EasyAbp/EShop/Plugins/Promotions/PromotionTypes/IPromotionHandler.cs +++ b/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Domain/EasyAbp/EShop/Plugins/Promotions/PromotionTypes/IPromotionHandler.cs @@ -7,7 +7,8 @@ namespace EasyAbp.EShop.Plugins.Promotions.PromotionTypes; public interface IPromotionHandler { - Task HandleProductAsync(ProductDiscountContext context, Promotion promotion); + Task HandleProductAsync(ProductRealTimePriceInfoModel model, Promotion promotion, IProduct product, + IProductSku productSku); Task HandleOrderAsync(OrderDiscountContext context, Promotion promotion); diff --git a/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Domain/EasyAbp/EShop/Plugins/Promotions/PromotionTypes/MinQuantityOrderDiscount/MinQuantityOrderDiscountPromotionHandler.cs b/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Domain/EasyAbp/EShop/Plugins/Promotions/PromotionTypes/MinQuantityOrderDiscount/MinQuantityOrderDiscountPromotionHandler.cs index 6aa92efa..3c031e63 100644 --- a/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Domain/EasyAbp/EShop/Plugins/Promotions/PromotionTypes/MinQuantityOrderDiscount/MinQuantityOrderDiscountPromotionHandler.cs +++ b/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Domain/EasyAbp/EShop/Plugins/Promotions/PromotionTypes/MinQuantityOrderDiscount/MinQuantityOrderDiscountPromotionHandler.cs @@ -17,30 +17,31 @@ public class MinQuantityOrderDiscountPromotionHandler : PromotionHandlerBase, IS { } - public override async Task HandleProductAsync(ProductDiscountContext context, Promotion promotion) + public override async Task HandleProductAsync(ProductRealTimePriceInfoModel model, Promotion promotion, + IProduct product, IProductSku productSku) { foreach (var discountModel in GetConfigurations(promotion).Discounts) { - if (context.ProductSku.Currency != discountModel.DynamicDiscountAmount.Currency) + if (productSku.Currency != discountModel.DynamicDiscountAmount.Currency) { continue; } - if (!discountModel.IsInScope(context.Product.ProductGroupName, context.Product.Id, context.ProductSku.Id)) + if (!discountModel.IsInScope(product.ProductGroupName, product.Id, productSku.Id)) { continue; } var newDiscount = new OrderDiscountPreviewInfoModel(PromotionConsts.PromotionEffectGroup, PromotionConsts.PromotionDiscountName, promotion.UniqueName, promotion.DisplayName, promotion.FromTime, - promotion.ToTime, await CreateOrderDiscountPreviewRuleDataAsync(discountModel, context, promotion)); + promotion.ToTime, await CreateOrderDiscountPreviewRuleDataAsync(discountModel, model, promotion)); - context.OrderDiscountPreviews.Add(newDiscount); + model.OrderDiscountPreviews.Add(newDiscount); } } protected virtual Task CreateOrderDiscountPreviewRuleDataAsync(MinQuantityOrderDiscountModel discountModel, - ProductDiscountContext context, Promotion promotion) + ProductRealTimePriceInfoModel model, Promotion promotion) { return Task.FromResult(null); } diff --git a/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Domain/EasyAbp/EShop/Plugins/Promotions/PromotionTypes/PromotionHandlerBase.cs b/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Domain/EasyAbp/EShop/Plugins/Promotions/PromotionTypes/PromotionHandlerBase.cs index e2207bf8..aa92a724 100644 --- a/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Domain/EasyAbp/EShop/Plugins/Promotions/PromotionTypes/PromotionHandlerBase.cs +++ b/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Domain/EasyAbp/EShop/Plugins/Promotions/PromotionTypes/PromotionHandlerBase.cs @@ -20,7 +20,8 @@ public abstract class PromotionHandlerBase : IPromotionHandler JsonSerializer = jsonSerializer; } - public abstract Task HandleProductAsync(ProductDiscountContext context, Promotion promotion); + public abstract Task HandleProductAsync(ProductRealTimePriceInfoModel model, Promotion promotion, IProduct product, + IProductSku productSku); public abstract Task HandleOrderAsync(OrderDiscountContext context, Promotion promotion); diff --git a/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Domain/EasyAbp/EShop/Plugins/Promotions/PromotionTypes/SimpleProductDiscount/SimpleProductDiscountPromotionHandler.cs b/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Domain/EasyAbp/EShop/Plugins/Promotions/PromotionTypes/SimpleProductDiscount/SimpleProductDiscountPromotionHandler.cs index 6d056109..8c78112c 100644 --- a/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Domain/EasyAbp/EShop/Plugins/Promotions/PromotionTypes/SimpleProductDiscount/SimpleProductDiscountPromotionHandler.cs +++ b/plugins/Promotions/src/EasyAbp.EShop.Plugins.Promotions.Domain/EasyAbp/EShop/Plugins/Promotions/PromotionTypes/SimpleProductDiscount/SimpleProductDiscountPromotionHandler.cs @@ -16,16 +16,17 @@ public class SimpleProductDiscountPromotionHandler : PromotionHandlerBase, IScop { } - public override Task HandleProductAsync(ProductDiscountContext context, Promotion promotion) + public override Task HandleProductAsync(ProductRealTimePriceInfoModel model, Promotion promotion, IProduct product, + IProductSku productSku) { foreach (var discountModel in GetConfigurations(promotion).Discounts) { - if (context.ProductSku.Currency != discountModel.DynamicDiscountAmount.Currency) + if (productSku.Currency != discountModel.DynamicDiscountAmount.Currency) { continue; } - if (!discountModel.IsInScope(context.Product.ProductGroupName, context.Product.Id, context.ProductSku.Id)) + if (!discountModel.IsInScope(product.ProductGroupName, product.Id, productSku.Id)) { continue; } @@ -34,7 +35,7 @@ public class SimpleProductDiscountPromotionHandler : PromotionHandlerBase, IScop PromotionConsts.PromotionDiscountName, promotion.UniqueName, promotion.DisplayName, discountModel.DynamicDiscountAmount, promotion.FromTime, promotion.ToTime); - context.CandidateProductDiscounts.Add(discount); + model.CandidateProductDiscounts.Add(discount); } return Task.CompletedTask; diff --git a/plugins/Promotions/src/EasyAbp.EShop.Products.Plugins.Promotions.Domain/EasyAbp/EShop/Products/Plugins/Promotions/PromotionProductDiscountProvider.cs b/plugins/Promotions/src/EasyAbp.EShop.Products.Plugins.Promotions.Domain/EasyAbp/EShop/Products/Plugins/Promotions/PromotionProductDiscountProvider.cs index 6f72997d..a6b18ea4 100644 --- a/plugins/Promotions/src/EasyAbp.EShop.Products.Plugins.Promotions.Domain/EasyAbp/EShop/Products/Plugins/Promotions/PromotionProductDiscountProvider.cs +++ b/plugins/Promotions/src/EasyAbp.EShop.Products.Plugins.Promotions.Domain/EasyAbp/EShop/Products/Plugins/Promotions/PromotionProductDiscountProvider.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Threading.Tasks; using EasyAbp.EShop.Plugins.Promotions.Promotions; using EasyAbp.EShop.Plugins.Promotions.Promotions.Dtos; @@ -19,19 +20,29 @@ public class PromotionProductDiscountProvider : IProductDiscountProvider, ITrans PromotionIntegrationService = promotionIntegrationService; } - public virtual async Task DiscountAsync(ProductDiscountContext context) + public virtual async Task DiscountAsync(GetProductsRealTimePriceContext context) { - var dto = await PromotionIntegrationService.DiscountProductAsync(new DiscountProductInputDto(context)); + if (context.Models.IsNullOrEmpty()) + { + return; + } + + var dto = await PromotionIntegrationService.DiscountProductsAsync(new DiscountProductInputDto(context)); if (dto.Context.Equals(context)) { return; } - context.CandidateProductDiscounts.Clear(); - context.CandidateProductDiscounts.AddRange(dto.Context.CandidateProductDiscounts); + foreach (var model in context.Models.Values) + { + var targetModel = dto.Context.Models[model.ProductSkuId]; + + model.CandidateProductDiscounts.Clear(); + model.CandidateProductDiscounts.AddRange(targetModel.CandidateProductDiscounts); - context.OrderDiscountPreviews.Clear(); - context.OrderDiscountPreviews.AddRange(dto.Context.OrderDiscountPreviews); + model.OrderDiscountPreviews.Clear(); + model.OrderDiscountPreviews.AddRange(targetModel.OrderDiscountPreviews); + } } } \ No newline at end of file diff --git a/plugins/Promotions/test/EasyAbp.EShop.Plugins.Promotions.Application.Tests/PromotionTypes/MinQuantityOrderDiscountTests.cs b/plugins/Promotions/test/EasyAbp.EShop.Plugins.Promotions.Application.Tests/PromotionTypes/MinQuantityOrderDiscountTests.cs index 739a2f61..cef9f2c0 100644 --- a/plugins/Promotions/test/EasyAbp.EShop.Plugins.Promotions.Application.Tests/PromotionTypes/MinQuantityOrderDiscountTests.cs +++ b/plugins/Promotions/test/EasyAbp.EShop.Plugins.Promotions.Application.Tests/PromotionTypes/MinQuantityOrderDiscountTests.cs @@ -34,25 +34,24 @@ public class MinQuantityOrderDiscountTests : PromotionsApplicationTestBase { var promotion = await CreatePromotionAsync(); + var productSku = new ProductSkuEto + { + Currency = "USD" + }; + var product = new ProductEto { ProductGroupName = "MyProductGroup", - ProductSkus = new List - { - new() - { - Currency = "USD" - } - } + ProductSkus = new List { productSku } }; - var context = new ProductDiscountContext(DateTime.Now, product, product.ProductSkus.First(), 1.00m); + var model = new ProductRealTimePriceInfoModel(product.Id, productSku.Id, 1.00m); - await Handler.HandleProductAsync(context, promotion); + await Handler.HandleProductAsync(model, promotion, product, productSku); - context.OrderDiscountPreviews.Count.ShouldBe(1); + model.OrderDiscountPreviews.Count.ShouldBe(1); - var orderDiscount = context.OrderDiscountPreviews.First(); + var orderDiscount = model.OrderDiscountPreviews.First(); orderDiscount.ShouldNotBeNull(); orderDiscount.EffectGroup.ShouldBe(PromotionConsts.PromotionEffectGroup); orderDiscount.DisplayName.ShouldBe("test"); diff --git a/plugins/Promotions/test/EasyAbp.EShop.Plugins.Promotions.Application.Tests/PromotionTypes/SimpleProductDiscountTests.cs b/plugins/Promotions/test/EasyAbp.EShop.Plugins.Promotions.Application.Tests/PromotionTypes/SimpleProductDiscountTests.cs index 005f657c..0cd3ed7a 100644 --- a/plugins/Promotions/test/EasyAbp.EShop.Plugins.Promotions.Application.Tests/PromotionTypes/SimpleProductDiscountTests.cs +++ b/plugins/Promotions/test/EasyAbp.EShop.Plugins.Promotions.Application.Tests/PromotionTypes/SimpleProductDiscountTests.cs @@ -31,26 +31,25 @@ public class SimpleProductDiscountTests : PromotionsApplicationTestBase { var promotion = await CreatePromotionAsync(); + var productSku = new ProductSkuEto + { + Currency = "USD", + Price = 1.00m, + }; + var product = new ProductEto { ProductGroupName = "MyProductGroup", - ProductSkus = new List - { - new() - { - Currency = "USD", - Price = 1.00m, - } - } + ProductSkus = new List { productSku } }; - var context = new ProductDiscountContext(DateTime.Now, product, product.ProductSkus.First(), 1.00m); + var model = new ProductRealTimePriceInfoModel(product.Id, productSku.Id, 1.00m); - await Handler.HandleProductAsync(context, promotion); + await Handler.HandleProductAsync(model, promotion, product, productSku); - context.CandidateProductDiscounts.Count.ShouldBe(1); + model.CandidateProductDiscounts.Count.ShouldBe(1); - var productDiscount = context.CandidateProductDiscounts.First(); + var productDiscount = model.CandidateProductDiscounts.First(); productDiscount.ShouldNotBeNull(); productDiscount.EffectGroup.ShouldBe(PromotionConsts.PromotionEffectGroup); productDiscount.DisplayName.ShouldBe("test");