Browse Source

Close #19: Move purchasable check function to order module

pull/49/head
gdlcf88 6 years ago
parent
commit
1ddc25c9e3
  1. 8
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application.Contracts/EasyAbp/EShop/Orders/Orders/Dtos/CreateOrderDto.cs
  2. 52
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/BasicPurchasableCheckProvider.cs
  3. 13
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IPurchasableCheckManager.cs
  4. 13
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IPurchasableCheckProvider.cs
  5. 13
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NotPurchasableException.cs
  6. 9
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderAppService.cs
  7. 30
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/PurchasableCheckManager.cs
  8. 3
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/IProductAppService.cs
  9. 10
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductAppService.cs
  10. 17
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/CheckProductPurchasableResult.cs
  11. 51
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/BasicProductPurchasableCheckHandler.cs
  12. 7
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductManager.cs
  13. 12
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductPurchasableCheckHandler.cs
  14. 13
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductIsNotPurchasableException.cs
  15. 29
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductManager.cs
  16. 13
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductPurchasableCheckHandlerMissingPropertyException.cs

8
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application.Contracts/EasyAbp/EShop/Orders/Orders/Dtos/CreateOrderDto.cs

@ -22,6 +22,14 @@ namespace EasyAbp.EShop.Orders.Orders.Dtos
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (OrderLines.Any(orderLine => orderLine.Quantity <= 0))
{
yield return new ValidationResult(
"Quantity should be greater than 0.",
new[] { "OrderLines" }
);
}
if (OrderLines.Select(orderLine => orderLine.Quantity).Sum() <= 0)
{
yield return new ValidationResult(

52
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/BasicPurchasableCheckProvider.cs

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.Products.Dtos;
using Volo.Abp.DependencyInjection;
namespace EasyAbp.EShop.Orders.Orders
{
public class BasicPurchasableCheckProvider : IPurchasableCheckProvider, ITransientDependency
{
public async Task CheckAsync(CreateOrderDto input, Dictionary<Guid, ProductDto> productDict)
{
await CheckProductsPublishedAsync(input, productDict);
await CheckInventoriesSufficientAsync(input, productDict);
}
protected virtual Task CheckProductsPublishedAsync(CreateOrderDto input,
Dictionary<Guid, ProductDto> productDict)
{
foreach (var productId in input.OrderLines.Select(dto => dto.ProductId).Distinct().ToArray())
{
if (!productDict[productId].IsPublished)
{
throw new NotPurchasableException(productId, null, "Unpublished project");
}
}
return Task.CompletedTask;
}
protected virtual Task CheckInventoriesSufficientAsync(CreateOrderDto input,
Dictionary<Guid, ProductDto> productDict)
{
foreach (var orderLine in input.OrderLines)
{
var inventory = productDict[orderLine.ProductId].ProductSkus
.Single(sku => sku.Id == orderLine.ProductSkuId).Inventory;
if (inventory < orderLine.Quantity)
{
throw new NotPurchasableException(orderLine.ProductId, orderLine.ProductSkuId,
"Insufficient inventory");
}
}
return Task.CompletedTask;
}
}
}

13
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IPurchasableCheckManager.cs

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.Products.Dtos;
namespace EasyAbp.EShop.Orders.Orders
{
public interface IPurchasableCheckManager
{
Task CheckAsync(CreateOrderDto input, Dictionary<Guid, ProductDto> productDict);
}
}

13
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IPurchasableCheckProvider.cs

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.Products.Dtos;
namespace EasyAbp.EShop.Orders.Orders
{
public interface IPurchasableCheckProvider
{
Task CheckAsync(CreateOrderDto input, Dictionary<Guid, ProductDto> productDict);
}
}

13
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NotPurchasableException.cs

@ -0,0 +1,13 @@
using System;
using Volo.Abp;
namespace EasyAbp.EShop.Orders.Orders
{
public class NotPurchasableException : BusinessException
{
public NotPurchasableException(Guid productId, Guid? productSkuId, string reason) : base(
message: $"Product {productId} (SKU: {productSkuId}) cannot be purchased, the reason is: {reason}")
{
}
}
}

9
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderAppService.cs

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Authorization;
@ -11,7 +10,6 @@ using Microsoft.AspNetCore.Authorization;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Json;
using Volo.Abp.Users;
namespace EasyAbp.EShop.Orders.Orders
@ -26,17 +24,20 @@ namespace EasyAbp.EShop.Orders.Orders
private readonly INewOrderGenerator _newOrderGenerator;
private readonly IProductAppService _productAppService;
private readonly IPurchasableCheckManager _purchasableCheckManager;
private readonly IOrderDiscountManager _orderDiscountManager;
private readonly IOrderRepository _repository;
public OrderAppService(
INewOrderGenerator newOrderGenerator,
IProductAppService productAppService,
IPurchasableCheckManager purchasableCheckManager,
IOrderDiscountManager orderDiscountManager,
IOrderRepository repository) : base(repository)
{
_newOrderGenerator = newOrderGenerator;
_productAppService = productAppService;
_purchasableCheckManager = purchasableCheckManager;
_orderDiscountManager = orderDiscountManager;
_repository = repository;
}
@ -101,8 +102,8 @@ namespace EasyAbp.EShop.Orders.Orders
var productDict = await GetProductDictionaryAsync(input.OrderLines.Select(dto => dto.ProductId).ToList(),
input.StoreId);
// Todo: Check if the product is purchasable.
await _purchasableCheckManager.CheckAsync(input, productDict);
var order = await _newOrderGenerator.GenerateAsync(input, productDict);

30
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/PurchasableCheckManager.cs

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.Products.Dtos;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
namespace EasyAbp.EShop.Orders.Orders
{
public class PurchasableCheckManager : IPurchasableCheckManager, ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public PurchasableCheckManager(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task CheckAsync(CreateOrderDto input, Dictionary<Guid, ProductDto> productDict)
{
var providers = _serviceProvider.GetServices<IPurchasableCheckProvider>();
foreach (var provider in providers)
{
await provider.CheckAsync(input, productDict);
}
}
}
}

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

@ -23,8 +23,5 @@ namespace EasyAbp.EShop.Products.Products
Task<ProductDto> GetAsync(Guid id, Guid storeId);
Task<ProductDto> DeleteSkuAsync(Guid productId, Guid productSkuId, Guid storeId);
Task<CheckProductPurchasableResult> CheckPurchasableAsync(Guid productId, Guid productSkuId, Guid storeId,
Dictionary<string, object> extraProperties);
}
}

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

@ -358,16 +358,6 @@ namespace EasyAbp.EShop.Products.Products
return ObjectMapper.Map<Product, ProductDto>(product);
}
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 _productManager.GetPurchasableStatusAsync(product, productSku, storeId, extraProperties);
}
protected virtual async Task UpdateProductCategoriesAsync(Guid productId, IEnumerable<Guid> categoryIds)
{
await _productCategoryRepository.DeleteAsync(x => x.ProductId.Equals(productId));

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

@ -1,17 +0,0 @@
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;
}
}
}

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

@ -1,51 +0,0 @@
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));
}
}
}

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

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
@ -7,12 +6,6 @@ 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);
Task<bool> IsInventorySufficientAsync(Product product, ProductSku productSku, Guid storeId, int quantity);
Task<int> GetInventoryAsync(Product product, ProductSku productSku, Guid storeId);

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

@ -1,12 +0,0 @@
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/ProductIsNotPurchasableException.cs

@ -1,13 +0,0 @@
using System;
using Volo.Abp;
namespace EasyAbp.EShop.Products.Products
{
public class ProductIsNotPurchasableException : BusinessException
{
public ProductIsNotPurchasableException(Guid id, string reason) : base(
message: $"Product {id} cannot be purchased, the reason is: {reason}")
{
}
}
}

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

@ -16,35 +16,6 @@ namespace EasyAbp.EShop.Products.Products
_productInventoryProvider = productInventoryProvider;
}
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);
}
public async Task<bool> IsInventorySufficientAsync(Product product, ProductSku productSku, Guid storeId, int quantity)
{
return await _productInventoryProvider.IsInventorySufficientAsync(product, productSku, storeId, quantity);

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

@ -1,13 +0,0 @@
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}")
{
}
}
}
Loading…
Cancel
Save