Browse Source

Improved product purchasable check, related to: #18

pull/49/head
gdlcf88 6 years ago
parent
commit
378d145a20
  1. 3
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/Orders/Order.cs
  2. 5
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/IProductAppService.cs
  3. 11
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductAppService.cs
  4. 17
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/CheckProductPurchasableResult.cs
  5. 9
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/GetProductPurchasableStatusResult.cs
  6. 8
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductPurchasableStatus.cs
  7. 51
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/BasicProductPurchasableCheckHandler.cs
  8. 22
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductInventoryProvider.cs
  9. 12
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductInventoryProvider.cs
  10. 16
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductManager.cs
  11. 12
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductPurchasableCheckHandler.cs
  12. 13
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductPurchasableStatusProvider.cs
  13. 40
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductManager.cs
  14. 13
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductPurchasableCheckHandlerMissingPropertyException.cs
  15. 29
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductPurchasableStatusProvider.cs

3
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/Orders/Order.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using EasyAbp.EShop.Stores.Stores;
using JetBrains.Annotations;
using Volo.Abp.Domain.Entities.Auditing;
@ -40,5 +41,7 @@ namespace EasyAbp.EShop.Orders.Orders
[CanBeNull]
public virtual string StaffRemark { get; protected set; }
public virtual List<OrderLine> OrderLines { get; protected set; }
}
}

5
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/IProductAppService.cs

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Products.Dtos;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace EasyAbp.EShop.Products.Products
@ -24,6 +24,7 @@ namespace EasyAbp.EShop.Products.Products
Task<ProductDto> DeleteSkuAsync(Guid productId, Guid productSkuId, Guid storeId);
Task<GetProductPurchasableStatusResult> GetPurchasableStatusAsync(Guid productId, Guid productSkuId, Guid storeId);
Task<CheckProductPurchasableResult> CheckPurchasableAsync(Guid productId, Guid productSkuId, Guid storeId,
Dictionary<string, object> extraProperties);
}
}

11
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductAppService.cs

@ -24,20 +24,20 @@ namespace EasyAbp.EShop.Products.Products
protected override string GetPolicyName { get; set; } = null;
protected override string GetListPolicyName { get; set; } = null;
private readonly IProductPurchasableStatusProvider _productPurchasableStatusProvider;
private readonly IProductManager _productManager;
private readonly IAttributeOptionIdsSerializer _attributeOptionIdsSerializer;
private readonly IProductStoreRepository _productStoreRepository;
private readonly IProductCategoryRepository _productCategoryRepository;
private readonly IProductRepository _repository;
public ProductAppService(
IProductPurchasableStatusProvider productPurchasableStatusProvider,
IProductManager productManager,
IAttributeOptionIdsSerializer attributeOptionIdsSerializer,
IProductStoreRepository productStoreRepository,
IProductCategoryRepository productCategoryRepository,
IProductRepository repository) : base(repository)
{
_productPurchasableStatusProvider = productPurchasableStatusProvider;
_productManager = productManager;
_attributeOptionIdsSerializer = attributeOptionIdsSerializer;
_productStoreRepository = productStoreRepository;
_productCategoryRepository = productCategoryRepository;
@ -354,13 +354,14 @@ namespace EasyAbp.EShop.Products.Products
return ObjectMapper.Map<Product, ProductDto>(product);
}
public async Task<GetProductPurchasableStatusResult> GetPurchasableStatusAsync(Guid productId, Guid productSkuId, Guid storeId)
public async Task<CheckProductPurchasableResult> CheckPurchasableAsync(Guid productId, Guid productSkuId,
Guid storeId, Dictionary<string, object> extraProperties)
{
var product = await _repository.GetAsync(productId);
var productSku = product.ProductSkus.Single(sku => sku.Id == productSkuId);
return await _productPurchasableStatusProvider.GetPurchasableStatusAsync(product, productSku, storeId);
return await _productManager.GetPurchasableStatusAsync(product, productSku, storeId, extraProperties);
}
protected virtual async Task UpdateProductCategoriesAsync(Guid productId, IEnumerable<Guid> categoryIds)

17
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/CheckProductPurchasableResult.cs

@ -0,0 +1,17 @@
namespace EasyAbp.EShop.Products.Products
{
public class CheckProductPurchasableResult
{
public bool IsPurchasable { get; set; }
public string Reason { get; set; }
public CheckProductPurchasableResult(
bool isPurchasable,
string reason = null)
{
IsPurchasable = isPurchasable;
Reason = reason;
}
}
}

9
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/GetProductPurchasableStatusResult.cs

@ -1,9 +0,0 @@
namespace EasyAbp.EShop.Products.Products
{
public class GetProductPurchasableStatusResult
{
public ProductPurchasableStatus PurchasableStatus { get; set; }
public string Reason { get; set; }
}
}

8
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductPurchasableStatus.cs

@ -1,8 +0,0 @@
namespace EasyAbp.EShop.Products.Products
{
public enum ProductPurchasableStatus
{
CanBePurchased = 1,
CanNotBePurchased = 2
}
}

51
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/BasicProductPurchasableCheckHandler.cs

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace EasyAbp.EShop.Products.Products
{
public class BasicProductPurchasableCheckHandler : IProductPurchasableCheckHandler, ITransientDependency
{
private readonly IProductInventoryProvider _productInventoryProvider;
public BasicProductPurchasableCheckHandler(
IProductInventoryProvider productInventoryProvider)
{
_productInventoryProvider = productInventoryProvider;
}
public async Task<CheckProductPurchasableResult> CheckAsync(Product product, ProductSku productSku, Guid storeId,
Dictionary<string, object> extraProperties)
{
if (!await IsProductPublishedAsync(product))
{
return new CheckProductPurchasableResult(false, "Unpublished project");
}
if (!await IsInventorySufficientAsync(product, productSku, storeId, extraProperties))
{
return new CheckProductPurchasableResult(false, "Insufficient inventory");
}
return new CheckProductPurchasableResult(true);
}
protected virtual Task<bool> IsProductPublishedAsync(Product product)
{
return Task.FromResult(product.IsPublished);
}
protected virtual async Task<bool> IsInventorySufficientAsync(Product product, ProductSku productSku, Guid storeId, Dictionary<string, object> extraProperties)
{
if (!extraProperties.TryGetValue("Quantity", out var quantity))
{
throw new ProductPurchasableCheckHandlerMissingPropertyException(
nameof(BasicProductPurchasableCheckHandler), "Quantity");
}
return await _productInventoryProvider.IsInventorySufficientAsync(product, productSku, storeId,
Convert.ToInt32(quantity));
}
}
}

22
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductInventoryProvider.cs

@ -0,0 +1,22 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace EasyAbp.EShop.Products.Products
{
[Dependency(TryRegister = true)]
public class DefaultProductInventoryProvider : IProductInventoryProvider, ITransientDependency
{
public virtual async Task<bool> IsInventorySufficientAsync(Product product, ProductSku productSku, Guid storeId, int quantity)
{
var inventory = await GetInventoryAsync(product, productSku, storeId);
return product.InventoryStrategy == InventoryStrategy.NoNeed || inventory - quantity >= 0;
}
public virtual Task<int> GetInventoryAsync(Product product, ProductSku productSku, Guid storeId)
{
return Task.FromResult(productSku.Inventory);
}
}
}

12
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductInventoryProvider.cs

@ -0,0 +1,12 @@
using System;
using System.Threading.Tasks;
namespace EasyAbp.EShop.Products.Products
{
public interface IProductInventoryProvider
{
Task<bool> IsInventorySufficientAsync(Product product, ProductSku productSku, Guid storeId, int quantity);
Task<int> GetInventoryAsync(Product product, ProductSku productSku, Guid storeId);
}
}

16
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductManager.cs

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
namespace EasyAbp.EShop.Products.Products
{
public interface IProductManager : IDomainService
{
Task CheckPurchasableAsync(Product product, ProductSku productSku, Guid storeId,
Dictionary<string, object> extraProperties);
Task<CheckProductPurchasableResult> GetPurchasableStatusAsync(Product product, ProductSku productSku,
Guid storeId, Dictionary<string, object> extraProperties);
}
}

12
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductPurchasableCheckHandler.cs

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace EasyAbp.EShop.Products.Products
{
public interface IProductPurchasableCheckHandler
{
Task<CheckProductPurchasableResult> CheckAsync(Product product, ProductSku productSku, Guid storeId,
Dictionary<string, object> extraProperties);
}
}

13
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductPurchasableStatusProvider.cs

@ -1,13 +0,0 @@
using System;
using System.Threading.Tasks;
namespace EasyAbp.EShop.Products.Products
{
public interface IProductPurchasableStatusProvider
{
Task CheckPurchasableAsync(Product product, ProductSku productSku, Guid storeId);
Task<GetProductPurchasableStatusResult> GetPurchasableStatusAsync(Product product, ProductSku productSku,
Guid storeId);
}
}

40
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductManager.cs

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Domain.Services;
namespace EasyAbp.EShop.Products.Products
{
public class ProductManager : DomainService, IProductManager
{
public async Task CheckPurchasableAsync(Product product, ProductSku productSku, Guid storeId,
Dictionary<string, object> extraProperties)
{
var result = await GetPurchasableStatusAsync(product, productSku, storeId, extraProperties);
if (!result.IsPurchasable)
{
throw new ProductIsNotPurchasableException(product.Id, result.Reason);
}
}
public async Task<CheckProductPurchasableResult> GetPurchasableStatusAsync(Product product,
ProductSku productSku, Guid storeId, Dictionary<string, object> extraProperties)
{
var handlers = ServiceProvider.GetServices<IProductPurchasableCheckHandler>();
foreach (var handler in handlers)
{
var result = await handler.CheckAsync(product, productSku, storeId, extraProperties);
if (!result.IsPurchasable)
{
return result;
}
}
return new CheckProductPurchasableResult(true);
}
}
}

13
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductPurchasableCheckHandlerMissingPropertyException.cs

@ -0,0 +1,13 @@
using System;
using Volo.Abp;
namespace EasyAbp.EShop.Products.Products
{
public class ProductPurchasableCheckHandlerMissingPropertyException : BusinessException
{
public ProductPurchasableCheckHandlerMissingPropertyException(string handlerName, string propertyKey) : base(
message: $"The {handlerName} is missing extra property: {propertyKey}")
{
}
}
}

29
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductPurchasableStatusProvider.cs

@ -1,29 +0,0 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace EasyAbp.EShop.Products.Products
{
public class ProductPurchasableStatusProvider : IProductPurchasableStatusProvider, ITransientDependency
{
public async Task CheckPurchasableAsync(Product product, ProductSku productSku, Guid storeId)
{
var result = await GetPurchasableStatusAsync(product, productSku, storeId);
if (result.PurchasableStatus != ProductPurchasableStatus.CanBePurchased)
{
throw new ProductIsNotPurchasableException(product.Id, result.Reason);
}
}
public async Task<GetProductPurchasableStatusResult> GetPurchasableStatusAsync(Product product, ProductSku productSku, Guid storeId)
{
// Todo: Determine whether the product can be purchased.
return new GetProductPurchasableStatusResult
{
PurchasableStatus = ProductPurchasableStatus.CanBePurchased,
Reason = null
};
}
}
}
Loading…
Cancel
Save