Browse Source

Remove IPurchasableChecker and introduce OrderCreationAuthorizationHandler

Close #79
Remove CreatePaymentEto in PaymentCreationResource
Fix BasicPaymentCreationAuthorizationHandler
pull/103/head
gdlcf88 6 years ago
parent
commit
fde6728b1b
  1. 1
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application.Contracts/EasyAbp/EShop/Orders/Orders/Dtos/CreateOrderLineDto.cs
  2. 9
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/EShopOrdersApplicationModule.cs
  3. 53
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/BasicOrderCreationAuthorizationHandler.cs
  4. 55
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/BasicPurchasableCheckProvider.cs
  5. 14
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/CurrencyIsLimitException.cs
  6. 3
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/INewOrderGenerator.cs
  7. 14
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IPurchasableCheckProvider.cs
  8. 14
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IPurchasableChecker.cs
  9. 44
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs
  10. 13
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NotPurchasableException.cs
  11. 16
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderAppService.cs
  12. 22
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderCreationAuthorizationHandler.cs
  13. 14
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderCreationResource.cs
  14. 10
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderOperation.cs
  15. 14
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderOperationAuthorizationRequirement.cs
  16. 31
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/PurchasableChecker.cs
  17. 3
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Application/EasyAbp/EShop/Payments/Payments/BasicPaymentCreationAuthorizationHandler.cs
  18. 17
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Application/EasyAbp/EShop/Payments/Payments/PaymentAppService.cs
  19. 2
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Application/EasyAbp/EShop/Payments/Payments/PaymentCreationResource.cs

1
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application.Contracts/EasyAbp/EShop/Orders/Orders/Dtos/CreateOrderLineDto.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Volo.Abp.Data;
namespace EasyAbp.EShop.Orders.Orders.Dtos
{

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

@ -1,5 +1,7 @@
using EasyAbp.EShop.Products;
using EasyAbp.EShop.Orders.Orders;
using EasyAbp.EShop.Products;
using EasyAbp.EShop.Stores;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AutoMapper;
using Volo.Abp.Modularity;
@ -18,6 +20,11 @@ namespace EasyAbp.EShop.Orders
)]
public class EShopOrdersApplicationModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddSingleton<IAuthorizationHandler, BasicOrderCreationAuthorizationHandler>();
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<EShopOrdersApplicationModule>();

53
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/BasicOrderCreationAuthorizationHandler.cs

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using Microsoft.AspNetCore.Authorization;
namespace EasyAbp.EShop.Orders.Orders
{
public class BasicOrderCreationAuthorizationHandler : OrderCreationAuthorizationHandler
{
protected override async Task HandleOrderCreationAsync(AuthorizationHandlerContext context,
OrderOperationAuthorizationRequirement requirement, OrderCreationResource resource)
{
if (!await IsProductsPublishedAsync(resource.Input, resource.ProductDictionary))
{
context.Fail();
return;
}
if (!await IsInventoriesSufficientAsync(resource.Input, resource.ProductDictionary))
{
context.Fail();
return;
}
context.Succeed(requirement);
}
protected virtual Task<bool> IsProductsPublishedAsync(CreateOrderDto input,
Dictionary<Guid, ProductDto> productDictionary)
{
return Task.FromResult(
input.OrderLines.Select(dto => dto.ProductId).Distinct().ToArray()
.All(productId => productDictionary[productId].IsPublished)
);
}
protected virtual Task<bool> IsInventoriesSufficientAsync(CreateOrderDto input,
Dictionary<Guid, ProductDto> productDictionary)
{
return Task.FromResult(
!(from orderLine in input.OrderLines
let product = productDictionary[orderLine.ProductId]
let inventory = product.ProductSkus.Single(sku => sku.Id == orderLine.ProductSkuId).Inventory
where product.InventoryStrategy != InventoryStrategy.NoNeed && inventory < orderLine.Quantity
select orderLine).Any()
);
}
}
}

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

@ -1,55 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using Volo.Abp.DependencyInjection;
namespace EasyAbp.EShop.Orders.Orders
{
public class BasicPurchasableCheckProvider : IPurchasableCheckProvider, ITransientDependency
{
public virtual async Task CheckAsync(CreateOrderDto input, Dictionary<Guid, ProductDto> productDict,
Dictionary<string, object> orderExtraProperties)
{
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 product = productDict[orderLine.ProductId];
var inventory = product.ProductSkus
.Single(sku => sku.Id == orderLine.ProductSkuId).Inventory;
if (product.InventoryStrategy != InventoryStrategy.NoNeed && inventory < orderLine.Quantity)
{
throw new NotPurchasableException(orderLine.ProductId, orderLine.ProductSkuId,
"Insufficient inventory");
}
}
return Task.CompletedTask;
}
}
}

14
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/CurrencyIsLimitException.cs

@ -0,0 +1,14 @@
using System;
using Volo.Abp;
namespace EasyAbp.EShop.Orders.Orders
{
public class CurrencyIsLimitException : BusinessException
{
public CurrencyIsLimitException(string expectedCurrency) : base(
"CurrencyIsLimit",
$"Only the specified currency {expectedCurrency} is allowed.")
{
}
}
}

3
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/INewOrderGenerator.cs

@ -8,7 +8,6 @@ namespace EasyAbp.EShop.Orders.Orders
{
public interface INewOrderGenerator
{
Task<Order> GenerateAsync(CreateOrderDto input, Dictionary<Guid, ProductDto> productDict,
Dictionary<string, object> orderExtraProperties);
Task<Order> GenerateAsync(CreateOrderDto input, Dictionary<Guid, ProductDto> productDict);
}
}

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

@ -1,14 +0,0 @@
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,
Dictionary<string, object> orderExtraProperties);
}
}

14
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IPurchasableChecker.cs

@ -1,14 +0,0 @@
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 IPurchasableChecker
{
Task CheckAsync(CreateOrderDto input, Dictionary<Guid, ProductDto> productDict,
Dictionary<string, object> orderExtraProperties);
}
}

44
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs

@ -1,17 +1,14 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.ProductInventories;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Json;
using Volo.Abp.MultiTenancy;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Users;
namespace EasyAbp.EShop.Orders.Orders
@ -38,13 +35,20 @@ namespace EasyAbp.EShop.Orders.Orders
_productSkuDescriptionProvider = productSkuDescriptionProvider;
}
public virtual async Task<Order> GenerateAsync(CreateOrderDto input, Dictionary<Guid, ProductDto> productDict, Dictionary<string, object> orderExtraProperties)
public virtual async Task<Order> GenerateAsync(CreateOrderDto input, Dictionary<Guid, ProductDto> productDict)
{
var orderLines = new List<OrderLine>();
foreach (var orderLine in input.OrderLines)
foreach (var inputOrderLine in input.OrderLines)
{
orderLines.Add(await GenerateOrderLineAsync(orderLine, productDict, orderExtraProperties));
orderLines.Add(await GenerateOrderLineAsync(input, inputOrderLine, productDict));
}
var storeCurrency = await GetStoreCurrencyAsync(input.StoreId);
if (orderLines.Any(x => x.Currency != storeCurrency))
{
throw new CurrencyIsLimitException(storeCurrency);
}
var productTotalPrice = orderLines.Select(x => x.TotalPrice).Sum();
@ -58,17 +62,14 @@ namespace EasyAbp.EShop.Orders.Orders
tenantId: _currentTenant.Id,
storeId: input.StoreId,
customerUserId: _currentUser.GetId(),
currency: await GetStoreCurrencyAsync(input.StoreId),
currency: storeCurrency,
productTotalPrice: productTotalPrice,
totalDiscount: totalDiscount,
totalPrice: totalPrice,
actualTotalPrice: totalPrice - totalDiscount,
customerRemark: input.CustomerRemark);
foreach (var orderExtraProperty in orderExtraProperties)
{
order.SetProperty(orderExtraProperty.Key, orderExtraProperty.Value);
}
input.MapExtraPropertiesTo(order, MappingPropertyDefinitionChecks.Destination);
order.SetOrderLines(orderLines);
@ -77,18 +78,19 @@ namespace EasyAbp.EShop.Orders.Orders
return order;
}
protected virtual async Task<OrderLine> GenerateOrderLineAsync(CreateOrderLineDto input, Dictionary<Guid, ProductDto> productDict, Dictionary<string, object> orderExtraProperties)
protected virtual async Task<OrderLine> GenerateOrderLineAsync(CreateOrderDto input,
CreateOrderLineDto inputOrderLine, Dictionary<Guid, ProductDto> productDict)
{
var product = productDict[input.ProductId];
var productSku = product.GetSkuById(input.ProductSkuId);
var product = productDict[inputOrderLine.ProductId];
var productSku = product.GetSkuById(inputOrderLine.ProductSkuId);
if (!input.Quantity.IsBetween(productSku.OrderMinQuantity, productSku.OrderMaxQuantity))
if (!inputOrderLine.Quantity.IsBetween(productSku.OrderMinQuantity, productSku.OrderMaxQuantity))
{
throw new OrderLineInvalidQuantityException(product.Id, productSku.Id, input.Quantity);
throw new OrderLineInvalidQuantityException(product.Id, productSku.Id, inputOrderLine.Quantity);
}
var totalPrice = productSku.Price * input.Quantity;
var totalPrice = productSku.Price * inputOrderLine.Quantity;
return new OrderLine(
id: _guidGenerator.Create(),
productId: product.Id,
@ -107,7 +109,7 @@ namespace EasyAbp.EShop.Orders.Orders
totalPrice: totalPrice,
totalDiscount: 0,
actualTotalPrice: totalPrice,
quantity: input.Quantity
quantity: inputOrderLine.Quantity
);
}

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

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

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

@ -25,20 +25,17 @@ namespace EasyAbp.EShop.Orders.Orders
private readonly INewOrderGenerator _newOrderGenerator;
private readonly IProductAppService _productAppService;
private readonly IPurchasableChecker _purchasableChecker;
private readonly IOrderManager _orderManager;
private readonly IOrderRepository _repository;
public OrderAppService(
INewOrderGenerator newOrderGenerator,
IProductAppService productAppService,
IPurchasableChecker purchasableChecker,
IOrderManager orderManager,
IOrderRepository repository) : base(repository)
{
_newOrderGenerator = newOrderGenerator;
_productAppService = productAppService;
_purchasableChecker = purchasableChecker;
_orderManager = orderManager;
_repository = repository;
}
@ -91,11 +88,16 @@ namespace EasyAbp.EShop.Orders.Orders
var productDict = await GetProductDictionaryAsync(input.OrderLines.Select(dto => dto.ProductId).ToList(),
input.StoreId);
var orderExtraProperties = new Dictionary<string, object>();
await AuthorizationService.CheckAsync(
new OrderCreationResource
{
Input = input,
ProductDictionary = productDict
},
new OrderOperationAuthorizationRequirement(OrderOperation.Creation)
);
await _purchasableChecker.CheckAsync(input, productDict, orderExtraProperties);
var order = await _newOrderGenerator.GenerateAsync(input, productDict, orderExtraProperties);
var order = await _newOrderGenerator.GenerateAsync(input, productDict);
await _orderManager.DiscountAsync(order, input.ExtraProperties);

22
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderCreationAuthorizationHandler.cs

@ -0,0 +1,22 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
namespace EasyAbp.EShop.Orders.Orders
{
public abstract class OrderCreationAuthorizationHandler : AuthorizationHandler<OrderOperationAuthorizationRequirement, OrderCreationResource>
{
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, OrderOperationAuthorizationRequirement requirement,
OrderCreationResource resource)
{
if (requirement.OrderOperation != OrderOperation.Creation)
{
return;
}
await HandleOrderCreationAsync(context, requirement, resource);
}
protected abstract Task HandleOrderCreationAsync(AuthorizationHandlerContext context,
OrderOperationAuthorizationRequirement requirement, OrderCreationResource resource);
}
}

14
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderCreationResource.cs

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.Products.Dtos;
namespace EasyAbp.EShop.Orders.Orders
{
public class OrderCreationResource
{
public CreateOrderDto Input { get; set; }
public Dictionary<Guid, ProductDto> ProductDictionary { get; set; }
}
}

10
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderOperation.cs

@ -0,0 +1,10 @@
using System;
namespace EasyAbp.EShop.Orders.Orders
{
[Flags]
public enum OrderOperation
{
Creation = 0
}
}

14
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderOperationAuthorizationRequirement.cs

@ -0,0 +1,14 @@
using Microsoft.AspNetCore.Authorization;
namespace EasyAbp.EShop.Orders.Orders
{
public class OrderOperationAuthorizationRequirement : IAuthorizationRequirement
{
public OrderOperation OrderOperation { get; }
public OrderOperationAuthorizationRequirement(OrderOperation orderOperation)
{
OrderOperation = orderOperation;
}
}
}

31
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/PurchasableChecker.cs

@ -1,31 +0,0 @@
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 PurchasableChecker : IPurchasableChecker, ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public PurchasableChecker(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public virtual async Task CheckAsync(CreateOrderDto input, Dictionary<Guid, ProductDto> productDict,
Dictionary<string, object> orderExtraProperties)
{
var providers = _serviceProvider.GetServices<IPurchasableCheckProvider>();
foreach (var provider in providers)
{
await provider.CheckAsync(input, productDict, orderExtraProperties);
}
}
}
}

3
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Application/EasyAbp/EShop/Payments/Payments/BasicPaymentCreationAuthorizationHandler.cs

@ -17,14 +17,17 @@ namespace EasyAbp.EShop.Payments.Payments
if (resource.Orders.Any(order => order.PaymentId.HasValue || order.PaidTime.HasValue))
{
context.Fail();
return Task.CompletedTask;
}
if (resource.Orders.Select(order => order.Currency).Distinct().Count() != 1)
{
// Todo: convert to a single currency.
context.Fail();
return Task.CompletedTask;
}
context.Succeed(requirement);
return Task.CompletedTask;
}
}

17
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Application/EasyAbp/EShop/Payments/Payments/PaymentAppService.cs

@ -83,6 +83,15 @@ namespace EasyAbp.EShop.Payments.Payments
{
orders.Add(await _orderAppService.GetAsync(orderId));
}
await AuthorizationService.CheckAsync(
new PaymentCreationResource
{
Input = input,
Orders = orders
},
new PaymentOperationAuthorizationRequirement(PaymentOperation.Creation)
);
var createPaymentEto = new CreatePaymentEto
{
@ -100,14 +109,6 @@ namespace EasyAbp.EShop.Payments.Payments
}).ToList()
};
await AuthorizationService.CheckAsync(new PaymentCreationResource
{
Input = input,
Orders = orders,
CreatePaymentEto = createPaymentEto
},
new PaymentOperationAuthorizationRequirement(PaymentOperation.Creation));
await _distributedEventBus.PublishAsync(createPaymentEto);
}
}

2
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Application/EasyAbp/EShop/Payments/Payments/PaymentCreationResource.cs

@ -10,7 +10,5 @@ namespace EasyAbp.EShop.Payments.Payments
public CreatePaymentDto Input { get; set; }
public List<OrderDto> Orders { get; set; }
public CreatePaymentEto CreatePaymentEto { get; set; }
}
}
Loading…
Cancel
Save