From f6d8fad7d04fd9b9704348daf94deb723573dd5f Mon Sep 17 00:00:00 2001 From: gdlcf88 Date: Mon, 15 Jun 2020 19:48:37 +0800 Subject: [PATCH] Close #33: Implement IProductDiscountManager --- .../Products/Products/Dtos/ProductDto.cs | 4 ++ .../Products/Products/Dtos/ProductSkuDto.cs | 16 ++++++ .../Products/Products/ProductAppService.cs | 51 ++++++++++++++----- .../ProductsApplicationAutoMapperProfile.cs | 8 ++- .../Products/IProductDiscountManager.cs | 11 ++++ .../Products/IProductDiscountProvider.cs | 10 ++++ .../Products/IProductPriceProvider.cs | 7 --- .../Products/ProductDiscountManager.cs | 22 ++++++++ 8 files changed, 107 insertions(+), 22 deletions(-) create mode 100644 modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductDiscountManager.cs create mode 100644 modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductDiscountProvider.cs delete mode 100644 modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductPriceProvider.cs create mode 100644 modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductDiscountManager.cs diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductDto.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductDto.cs index aefa7f8a..3a0289c3 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductDto.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductDto.cs @@ -28,6 +28,10 @@ namespace EasyAbp.EShop.Products.Products.Dtos public bool IsStatic { get; set; } public bool IsHidden { get; set; } + + public decimal? MinimumPrice { get; set; } + + public decimal? MaximumPrice { get; set; } public ICollection ProductAttributes { get; set; } 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 4b41d398..6ec293f5 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 @@ -13,10 +13,26 @@ namespace EasyAbp.EShop.Products.Products.Dtos public decimal? OriginalPrice { get; set; } + /// + /// Price property in the ProductSku entity. + /// public decimal Price { get; set; } + /// + /// Price from IProductDiscountManager + /// + public decimal DiscountedPrice { get; set; } + + /// + /// Inventory property in the ProductSku entity. + /// public int Inventory { get; set; } + /// + /// Inventory provider's inventory quantity (same as Inventory property if there is no provider). + /// + public int RealInventory { get; set; } + public int Sold { get; set; } public int OrderMinQuantity { 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 84f2911c..10b1125f 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 @@ -25,6 +25,7 @@ namespace EasyAbp.EShop.Products.Products protected override string GetListPolicyName { get; set; } = null; private readonly IProductManager _productManager; + private readonly IProductDiscountManager _productDiscountManager; private readonly IProductInventoryProvider _productInventoryProvider; private readonly IAttributeOptionIdsSerializer _attributeOptionIdsSerializer; private readonly IProductStoreRepository _productStoreRepository; @@ -33,6 +34,7 @@ namespace EasyAbp.EShop.Products.Products public ProductAppService( IProductManager productManager, + IProductDiscountManager productDiscountManager, IProductInventoryProvider productInventoryProvider, IAttributeOptionIdsSerializer attributeOptionIdsSerializer, IProductStoreRepository productStoreRepository, @@ -40,6 +42,7 @@ namespace EasyAbp.EShop.Products.Products IProductRepository repository) : base(repository) { _productManager = productManager; + _productDiscountManager = productDiscountManager; _productInventoryProvider = productInventoryProvider; _attributeOptionIdsSerializer = attributeOptionIdsSerializer; _productStoreRepository = productStoreRepository; @@ -198,6 +201,7 @@ namespace EasyAbp.EShop.Products.Products } await LoadRealInventoriesAsync(product, dto, storeId); + await LoadPricesAsync(product, dto, storeId); dto.CategoryIds = (await _productCategoryRepository.GetListByProductIdAsync(dto.Id)) .Select(x => x.CategoryId).ToList(); @@ -226,18 +230,6 @@ namespace EasyAbp.EShop.Products.Products return dto; } - protected virtual async Task LoadRealInventoriesAsync(Product product, ProductDto productDto, Guid storeId) - { - var inventoryDict = await _productInventoryProvider.GetInventoryDictionaryAsync(product, storeId); - - foreach (var productSkuDto in productDto.ProductSkus) - { - productSkuDto.Inventory = inventoryDict[productSkuDto.Id]; - } - - return productDto; - } - public override async Task> GetListAsync(GetProductListDto input) { await CheckGetListPolicyAsync(); @@ -250,6 +242,7 @@ namespace EasyAbp.EShop.Products.Products throw new NotAllowedToGetProductListWithShowHiddenException(); } + // Todo: Products cache. var query = CreateFilteredQuery(input); if (!isCurrentUserStoreAdmin) @@ -268,11 +261,43 @@ namespace EasyAbp.EShop.Products.Products foreach (var product in products) { - items.Add(await LoadRealInventoriesAsync(product, MapToGetListOutputDto(product), input.StoreId)); + // Todo: How to get list with details if the queryable is customized? + var productDto = MapToGetListOutputDto(await _repository.GetAsync(product.Id)); + + await LoadRealInventoriesAsync(product, productDto, input.StoreId); + await LoadPricesAsync(product, productDto, input.StoreId); + + items.Add(productDto); } return new PagedResultDto(totalCount, items); } + + protected virtual async Task LoadRealInventoriesAsync(Product product, ProductDto productDto, Guid storeId) + { + var inventoryDict = await _productInventoryProvider.GetInventoryDictionaryAsync(product, storeId); + + foreach (var productSkuDto in productDto.ProductSkus) + { + productSkuDto.RealInventory = inventoryDict[productSkuDto.Id]; + } + + return productDto; + } + + protected virtual async Task LoadPricesAsync(Product product, ProductDto productDto, Guid storeId) + { + foreach (var productSkuDto in productDto.ProductSkus) + { + productSkuDto.DiscountedPrice = await _productDiscountManager.GetDiscountedPriceAsync(product, + product.ProductSkus.Single(sku => sku.Id == productSkuDto.Id), storeId); + } + + productDto.MinimumPrice = productDto.ProductSkus.Select(sku => sku.Price).Min(); + productDto.MaximumPrice = productDto.ProductSkus.Select(sku => sku.Price).Max(); + + return productDto; + } public async Task DeleteAsync(Guid id, Guid storeId) { diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductsApplicationAutoMapperProfile.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductsApplicationAutoMapperProfile.cs index 5e734485..4775b03c 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductsApplicationAutoMapperProfile.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductsApplicationAutoMapperProfile.cs @@ -25,11 +25,15 @@ namespace EasyAbp.EShop.Products * Alternatively, you can split your mapping configurations * into multiple profile classes for a better organization. */ CreateMap() - .Ignore(dto => dto.CategoryIds); + .Ignore(dto => dto.CategoryIds) + .Ignore(dto => dto.MinimumPrice) + .Ignore(dto => dto.MaximumPrice); CreateMap(); CreateMap(); CreateMap(); - CreateMap(); + CreateMap() + .Ignore(dto => dto.DiscountedPrice) + .Ignore(dto => dto.RealInventory); CreateMap(MemberList.Source) .ForSourceMember(dto => dto.StoreId, opt => opt.DoNotValidate()) .ForSourceMember(dto => dto.CategoryIds, opt => opt.DoNotValidate()) diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductDiscountManager.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductDiscountManager.cs new file mode 100644 index 00000000..89d1eb7c --- /dev/null +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductDiscountManager.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Domain.Services; + +namespace EasyAbp.EShop.Products.Products +{ + public interface IProductDiscountManager : IDomainService + { + Task GetDiscountedPriceAsync(Product product, ProductSku productSku, Guid storeId); + } +} \ 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 new file mode 100644 index 00000000..f5721a32 --- /dev/null +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductDiscountProvider.cs @@ -0,0 +1,10 @@ +using System; +using System.Threading.Tasks; + +namespace EasyAbp.EShop.Products.Products +{ + public interface IProductDiscountProvider + { + Task GetDiscountedPriceAsync(Product product, ProductSku productSku, Guid storeId, decimal currentPrice); + } +} \ 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 deleted file mode 100644 index 58c519b4..00000000 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductPriceProvider.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace EasyAbp.EShop.Products.Products -{ - public interface IProductPriceProvider - { - - } -} \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductDiscountManager.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductDiscountManager.cs new file mode 100644 index 00000000..dc55954a --- /dev/null +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductDiscountManager.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Domain.Services; + +namespace EasyAbp.EShop.Products.Products +{ + public class ProductDiscountManager : DomainService, IProductDiscountManager + { + public async Task GetDiscountedPriceAsync(Product product, ProductSku productSku, Guid storeId) + { + var currentPrice = productSku.Price; + + foreach (var provider in ServiceProvider.GetServices()) + { + currentPrice = await provider.GetDiscountedPriceAsync(product, productSku, storeId, currentPrice); + } + + return currentPrice; + } + } +} \ No newline at end of file