Browse Source

Introduce inventory rollback feature and refactor IProductInventoryProvider

pull/158/head
gdlcf88 4 years ago
parent
commit
4545ef0087
  1. 13
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductInventories/ProductInventoryAppService.cs
  2. 102
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductAppService.cs
  3. 17
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/ProductInventories/IProductInventoryProvider.cs
  4. 2
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/ProductInventories/InventoryDataModel.cs
  5. 28
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/ProductInventories/InventoryQueryModel.cs
  6. 2
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/ProductInventories/IProductInventoryRepository.cs
  7. 23
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ConsumeInventoryModel.cs
  8. 58
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductInventoryProvider.cs
  9. 17
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductInventoryProvider.cs
  10. 1
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IProductManager.cs
  11. 45
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/OrderCreatedEventHandler.cs
  12. 55
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/OrderPaidEventHandler.cs
  13. 59
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductManager.cs
  14. 5
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.EntityFrameworkCore/EasyAbp/EShop/Products/ProductInventories/ProductInventoryRepository.cs

13
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductInventories/ProductInventoryAppService.cs

@ -39,7 +39,7 @@ namespace EasyAbp.EShop.Products.ProductInventories
{ {
throw new EntityNotFoundException(typeof(ProductSku), productSkuId); throw new EntityNotFoundException(typeof(ProductSku), productSkuId);
} }
productInventory = new ProductInventory(GuidGenerator.Create(), CurrentTenant.Id, productId, productInventory = new ProductInventory(GuidGenerator.Create(), CurrentTenant.Id, productId,
productSkuId, 0, 0); productSkuId, 0, 0);
@ -60,7 +60,7 @@ namespace EasyAbp.EShop.Products.ProductInventories
await AuthorizationService.CheckMultiStorePolicyAsync(product.StoreId, await AuthorizationService.CheckMultiStorePolicyAsync(product.StoreId,
ProductsPermissions.ProductInventory.Update, ProductsPermissions.ProductInventory.CrossStore); ProductsPermissions.ProductInventory.Update, ProductsPermissions.ProductInventory.CrossStore);
var productInventory = await _repository.FindAsync(x => x.ProductSkuId == input.ProductSkuId); var productInventory = await _repository.FindAsync(x => x.ProductSkuId == input.ProductSkuId);
if (productInventory == null) if (productInventory == null)
@ -80,10 +80,12 @@ namespace EasyAbp.EShop.Products.ProductInventories
protected virtual async Task ChangeInventoryAsync(Product product, ProductInventory productInventory, protected virtual async Task ChangeInventoryAsync(Product product, ProductInventory productInventory,
int changedInventory) int changedInventory)
{ {
var model = new InventoryQueryModel(product.TenantId, product.StoreId, product.Id,
productInventory.ProductSkuId);
if (changedInventory >= 0) if (changedInventory >= 0)
{ {
if (!await _productInventoryProvider.TryIncreaseInventoryAsync(product, productInventory, if (!await _productInventoryProvider.TryIncreaseInventoryAsync(model, changedInventory, false))
changedInventory, false))
{ {
throw new InventoryChangeFailedException(productInventory.ProductId, productInventory.ProductSkuId, throw new InventoryChangeFailedException(productInventory.ProductId, productInventory.ProductSkuId,
productInventory.Inventory, changedInventory); productInventory.Inventory, changedInventory);
@ -91,8 +93,7 @@ namespace EasyAbp.EShop.Products.ProductInventories
} }
else else
{ {
if (!await _productInventoryProvider.TryReduceInventoryAsync(product, productInventory, if (!await _productInventoryProvider.TryReduceInventoryAsync(model, -changedInventory, false))
-changedInventory, false))
{ {
throw new InventoryChangeFailedException(productInventory.ProductId, productInventory.ProductSkuId, throw new InventoryChangeFailedException(productInventory.ProductId, productInventory.ProductSkuId,
productInventory.Inventory, changedInventory); productInventory.Inventory, changedInventory);

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

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using EasyAbp.EShop.Products.Options; using EasyAbp.EShop.Products.Options;
using EasyAbp.EShop.Products.ProductInventories;
using EasyAbp.EShop.Products.Products.CacheItems; using EasyAbp.EShop.Products.Products.CacheItems;
using EasyAbp.EShop.Stores.Stores; using EasyAbp.EShop.Stores.Stores;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -14,7 +15,9 @@ using Volo.Abp.Domain.Entities;
namespace EasyAbp.EShop.Products.Products namespace EasyAbp.EShop.Products.Products
{ {
public class ProductAppService : MultiStoreCrudAppService<Product, ProductDto, Guid, GetProductListInput, CreateUpdateProductDto, CreateUpdateProductDto>, IProductAppService public class ProductAppService :
MultiStoreCrudAppService<Product, ProductDto, Guid, GetProductListInput, CreateUpdateProductDto,
CreateUpdateProductDto>, IProductAppService
{ {
protected override string CreatePolicyName { get; set; } = ProductsPermissions.Products.Create; protected override string CreatePolicyName { get; set; } = ProductsPermissions.Products.Create;
protected override string DeletePolicyName { get; set; } = ProductsPermissions.Products.Delete; protected override string DeletePolicyName { get; set; } = ProductsPermissions.Products.Delete;
@ -81,14 +84,11 @@ namespace EasyAbp.EShop.Products.Products
await _productManager.CreateAsync(product, input.CategoryIds); await _productManager.CreateAsync(product, input.CategoryIds);
var dto = await MapToGetOutputDtoAsync(product); var dto = await MapToGetOutputDtoAsync(product);
await LoadDtoExtraDataAsync(product, dto); await LoadDtoExtraDataAsync(product, dto);
await LoadDtosProductGroupDisplayNameAsync(new[] {dto}); await LoadDtosProductGroupDisplayNameAsync(new[] { dto });
UnitOfWorkManager.Current.OnCompleted(async () => UnitOfWorkManager.Current.OnCompleted(async () => { await ClearProductViewCacheAsync(product.StoreId); });
{
await ClearProductViewCacheAsync(product.StoreId);
});
return dto; return dto;
} }
@ -101,14 +101,14 @@ namespace EasyAbp.EShop.Products.Products
public override async Task<ProductDto> UpdateAsync(Guid id, CreateUpdateProductDto input) public override async Task<ProductDto> UpdateAsync(Guid id, CreateUpdateProductDto input)
{ {
var product = await GetEntityByIdAsync(id); var product = await GetEntityByIdAsync(id);
await CheckMultiStorePolicyAsync(product.StoreId, UpdatePolicyName); await CheckMultiStorePolicyAsync(product.StoreId, UpdatePolicyName);
if (input.StoreId != product.StoreId) if (input.StoreId != product.StoreId)
{ {
await CheckMultiStorePolicyAsync(input.StoreId, UpdatePolicyName); await CheckMultiStorePolicyAsync(input.StoreId, UpdatePolicyName);
} }
CheckProductIsNotStatic(product); CheckProductIsNotStatic(product);
MapToEntity(input, product); MapToEntity(input, product);
@ -118,15 +118,12 @@ namespace EasyAbp.EShop.Products.Products
await _productManager.UpdateAsync(product, input.CategoryIds); await _productManager.UpdateAsync(product, input.CategoryIds);
var dto = await MapToGetOutputDtoAsync(product); var dto = await MapToGetOutputDtoAsync(product);
await LoadDtoExtraDataAsync(product, dto); await LoadDtoExtraDataAsync(product, dto);
await LoadDtosProductGroupDisplayNameAsync(new[] {dto}); await LoadDtosProductGroupDisplayNameAsync(new[] { dto });
UnitOfWorkManager.Current.OnCompleted(async () => { await ClearProductViewCacheAsync(product.StoreId); });
UnitOfWorkManager.Current.OnCompleted(async () =>
{
await ClearProductViewCacheAsync(product.StoreId);
});
return dto; return dto;
} }
@ -137,10 +134,10 @@ namespace EasyAbp.EShop.Products.Products
var usedAttributeOptionIds = new HashSet<Guid>(); var usedAttributeOptionIds = new HashSet<Guid>();
foreach (var serializedAttributeOptionIds in product.ProductSkus.Select(sku => foreach (var serializedAttributeOptionIds in product.ProductSkus.Select(sku =>
sku.SerializedAttributeOptionIds)) sku.SerializedAttributeOptionIds))
{ {
foreach (var attributeOptionId in await _attributeOptionIdsSerializer.DeserializeAsync( foreach (var attributeOptionId in await _attributeOptionIdsSerializer.DeserializeAsync(
serializedAttributeOptionIds)) serializedAttributeOptionIds))
{ {
usedAttributeOptionIds.Add(attributeOptionId); usedAttributeOptionIds.Add(attributeOptionId);
} }
@ -182,9 +179,9 @@ namespace EasyAbp.EShop.Products.Products
.Except(attributeDto.ProductAttributeOptions.Select(o => o.DisplayName)).ToList(); .Except(attributeDto.ProductAttributeOptions.Select(o => o.DisplayName)).ToList();
if (!isProductSkusEmpty && removedOptionNames.Any() && usedAttributeOptionIds if (!isProductSkusEmpty && removedOptionNames.Any() && usedAttributeOptionIds
.Intersect(attribute.ProductAttributeOptions .Intersect(attribute.ProductAttributeOptions
.Where(option => removedOptionNames.Contains(option.DisplayName)) .Where(option => removedOptionNames.Contains(option.DisplayName))
.Select(option => option.Id)).Any()) .Select(option => option.Id)).Any())
{ {
throw new ProductAttributeOptionsDeletionFailedException(); throw new ProductAttributeOptionsDeletionFailedException();
} }
@ -217,7 +214,7 @@ namespace EasyAbp.EShop.Products.Products
var dto = await MapToGetOutputDtoAsync(product); var dto = await MapToGetOutputDtoAsync(product);
await LoadDtoExtraDataAsync(product, dto); await LoadDtoExtraDataAsync(product, dto);
await LoadDtosProductGroupDisplayNameAsync(new[] {dto}); await LoadDtosProductGroupDisplayNameAsync(new[] { dto });
return dto; return dto;
} }
@ -248,7 +245,7 @@ namespace EasyAbp.EShop.Products.Products
var dto = await MapToGetOutputDtoAsync(product); var dto = await MapToGetOutputDtoAsync(product);
await LoadDtoExtraDataAsync(product, dto); await LoadDtoExtraDataAsync(product, dto);
await LoadDtosProductGroupDisplayNameAsync(new[] {dto}); await LoadDtosProductGroupDisplayNameAsync(new[] { dto });
return dto; return dto;
} }
@ -290,7 +287,10 @@ namespace EasyAbp.EShop.Products.Products
protected virtual async Task<ProductDto> LoadDtoInventoryDataAsync(Product product, ProductDto productDto) protected virtual async Task<ProductDto> LoadDtoInventoryDataAsync(Product product, ProductDto productDto)
{ {
var inventoryDataDict = await _productInventoryProvider.GetInventoryDataDictionaryAsync(product); var models = product.ProductSkus.Select(x =>
new InventoryQueryModel(product.TenantId, product.StoreId, product.Id, x.Id)).ToList();
var inventoryDataDict = await _productInventoryProvider.GetSkuIdInventoryDataMappingAsync(models);
productDto.Sold = 0; productDto.Sold = 0;
@ -313,7 +313,7 @@ namespace EasyAbp.EShop.Products.Products
return productDto; return productDto;
} }
protected virtual async Task<ProductDto> LoadDtoPriceDataAsync(Product product, ProductDto productDto) protected virtual async Task<ProductDto> LoadDtoPriceDataAsync(Product product, ProductDto productDto)
{ {
foreach (var productSku in product.ProductSkus) foreach (var productSku in product.ProductSkus)
@ -321,7 +321,7 @@ namespace EasyAbp.EShop.Products.Products
var productSkuDto = productDto.ProductSkus.First(x => x.Id == productSku.Id); var productSkuDto = productDto.ProductSkus.First(x => x.Id == productSku.Id);
var priceDataModel = await _productManager.GetRealPriceAsync(product, productSku); var priceDataModel = await _productManager.GetRealPriceAsync(product, productSku);
productSkuDto.Price = priceDataModel.Price; productSkuDto.Price = priceDataModel.Price;
productSkuDto.DiscountedPrice = priceDataModel.DiscountedPrice; productSkuDto.DiscountedPrice = priceDataModel.DiscountedPrice;
} }
@ -338,17 +338,14 @@ namespace EasyAbp.EShop.Products.Products
public override async Task DeleteAsync(Guid id) public override async Task DeleteAsync(Guid id)
{ {
var product = await GetEntityByIdAsync(id); var product = await GetEntityByIdAsync(id);
await CheckMultiStorePolicyAsync(product.StoreId, DeletePolicyName); await CheckMultiStorePolicyAsync(product.StoreId, DeletePolicyName);
CheckProductIsNotStatic(product); CheckProductIsNotStatic(product);
await _productManager.DeleteAsync(product); await _productManager.DeleteAsync(product);
UnitOfWorkManager.Current.OnCompleted(async () => UnitOfWorkManager.Current.OnCompleted(async () => { await ClearProductViewCacheAsync(product.StoreId); });
{
await ClearProductViewCacheAsync(product.StoreId);
});
} }
private static void CheckProductIsNotStatic(Product product) private static void CheckProductIsNotStatic(Product product)
@ -362,9 +359,9 @@ namespace EasyAbp.EShop.Products.Products
public async Task<ProductDto> CreateSkuAsync(Guid productId, CreateProductSkuDto input) public async Task<ProductDto> CreateSkuAsync(Guid productId, CreateProductSkuDto input)
{ {
var product = await GetEntityByIdAsync(productId); var product = await GetEntityByIdAsync(productId);
await CheckMultiStorePolicyAsync(product.StoreId, UpdatePolicyName); await CheckMultiStorePolicyAsync(product.StoreId, UpdatePolicyName);
CheckProductIsNotStatic(product); CheckProductIsNotStatic(product);
var sku = ObjectMapper.Map<CreateProductSkuDto, ProductSku>(input); var sku = ObjectMapper.Map<CreateProductSkuDto, ProductSku>(input);
@ -374,15 +371,12 @@ namespace EasyAbp.EShop.Products.Products
await _productManager.CreateSkuAsync(product, sku); await _productManager.CreateSkuAsync(product, sku);
var dto = await MapToGetOutputDtoAsync(product); var dto = await MapToGetOutputDtoAsync(product);
await LoadDtoExtraDataAsync(product, dto); await LoadDtoExtraDataAsync(product, dto);
await LoadDtosProductGroupDisplayNameAsync(new[] {dto}); await LoadDtosProductGroupDisplayNameAsync(new[] { dto });
UnitOfWorkManager.Current.OnCompleted(async () => { await ClearProductViewCacheAsync(product.StoreId); });
UnitOfWorkManager.Current.OnCompleted(async () =>
{
await ClearProductViewCacheAsync(product.StoreId);
});
return dto; return dto;
} }
@ -401,15 +395,12 @@ namespace EasyAbp.EShop.Products.Products
await _productManager.UpdateSkuAsync(product, sku); await _productManager.UpdateSkuAsync(product, sku);
var dto = await MapToGetOutputDtoAsync(product); var dto = await MapToGetOutputDtoAsync(product);
await LoadDtoExtraDataAsync(product, dto); await LoadDtoExtraDataAsync(product, dto);
await LoadDtosProductGroupDisplayNameAsync(new[] {dto}); await LoadDtosProductGroupDisplayNameAsync(new[] { dto });
UnitOfWorkManager.Current.OnCompleted(async () => { await ClearProductViewCacheAsync(product.StoreId); });
UnitOfWorkManager.Current.OnCompleted(async () =>
{
await ClearProductViewCacheAsync(product.StoreId);
});
return dto; return dto;
} }
@ -426,15 +417,12 @@ namespace EasyAbp.EShop.Products.Products
await _productManager.DeleteSkuAsync(product, sku); await _productManager.DeleteSkuAsync(product, sku);
var dto = await MapToGetOutputDtoAsync(product); var dto = await MapToGetOutputDtoAsync(product);
await LoadDtoExtraDataAsync(product, dto); await LoadDtoExtraDataAsync(product, dto);
await LoadDtosProductGroupDisplayNameAsync(new[] {dto}); await LoadDtosProductGroupDisplayNameAsync(new[] { dto });
UnitOfWorkManager.Current.OnCompleted(async () => { await ClearProductViewCacheAsync(product.StoreId); });
UnitOfWorkManager.Current.OnCompleted(async () =>
{
await ClearProductViewCacheAsync(product.StoreId);
});
return dto; return dto;
} }

17
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/ProductInventories/IProductInventoryProvider.cs

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace EasyAbp.EShop.Products.ProductInventories
{
public interface IProductInventoryProvider
{
Task<InventoryDataModel> GetInventoryDataAsync(InventoryQueryModel model);
Task<Dictionary<Guid, InventoryDataModel>> GetSkuIdInventoryDataMappingAsync(IList<InventoryQueryModel> models);
Task<bool> TryIncreaseInventoryAsync(InventoryQueryModel model, int quantity, bool decreaseSold);
Task<bool> TryReduceInventoryAsync(InventoryQueryModel model, int quantity, bool increaseSold);
}
}

2
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/InventoryDataModel.cs → modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/ProductInventories/InventoryDataModel.cs

@ -1,4 +1,4 @@
namespace EasyAbp.EShop.Products.Products namespace EasyAbp.EShop.Products.ProductInventories
{ {
public class InventoryDataModel public class InventoryDataModel
{ {

28
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/ProductInventories/InventoryQueryModel.cs

@ -0,0 +1,28 @@
using System;
using EasyAbp.EShop.Stores.Stores;
using Volo.Abp.MultiTenancy;
namespace EasyAbp.EShop.Products.ProductInventories;
public class InventoryQueryModel : IMultiTenant, IMultiStore
{
public Guid? TenantId { get; set; }
public Guid StoreId { get; set; }
public Guid ProductId { get; set; }
public Guid ProductSkuId { get; set; }
public InventoryQueryModel()
{
}
public InventoryQueryModel(Guid? tenantId, Guid storeId, Guid productId, Guid productSkuId)
{
TenantId = tenantId;
StoreId = storeId;
ProductId = productId;
ProductSkuId = productSkuId;
}
}

2
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/ProductInventories/IProductInventoryRepository.cs

@ -11,6 +11,6 @@ namespace EasyAbp.EShop.Products.ProductInventories
{ {
Task<InventoryDataModel> GetInventoryDataAsync(Guid productSkuId, CancellationToken cancellationToken = default); Task<InventoryDataModel> GetInventoryDataAsync(Guid productSkuId, CancellationToken cancellationToken = default);
Task<Dictionary<Guid, InventoryDataModel>> GetInventoryDataDictionaryAsync(List<Guid> productSkuIds, CancellationToken cancellationToken = default); Task<Dictionary<Guid, InventoryDataModel>> GetSkuIdInventoryDataMappingAsync(List<Guid> productSkuIds, CancellationToken cancellationToken = default);
} }
} }

23
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ConsumeInventoryModel.cs

@ -4,12 +4,27 @@ namespace EasyAbp.EShop.Products.Products
{ {
public class ConsumeInventoryModel public class ConsumeInventoryModel
{ {
public Product Product { get; set; } public Product Product { get; }
public ProductSku ProductSku { get; set; } public ProductSku ProductSku { get; }
public Guid StoreId { get; set; } public Guid StoreId { get; }
public int Quantity { get; set; } public int Quantity { get; }
public bool Consumed { get; private set;}
public ConsumeInventoryModel(Product product, ProductSku productSku, Guid storeId, int quantity)
{
Product = product;
ProductSku = productSku;
StoreId = storeId;
Quantity = quantity;
}
public void SetConsumed(bool consumed)
{
Consumed = consumed;
}
} }
} }

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

@ -33,50 +33,54 @@ namespace EasyAbp.EShop.Products.Products
_distributedEventBus = distributedEventBus; _distributedEventBus = distributedEventBus;
_productInventoryRepository = productInventoryRepository; _productInventoryRepository = productInventoryRepository;
} }
[UnitOfWork] [UnitOfWork]
public virtual async Task<InventoryDataModel> GetInventoryDataAsync(Product product, ProductSku productSku) public virtual async Task<InventoryDataModel> GetInventoryDataAsync(InventoryQueryModel model)
{ {
return await _productInventoryRepository.GetInventoryDataAsync(productSku.Id); return await _productInventoryRepository.GetInventoryDataAsync(model.ProductSkuId);
} }
[UnitOfWork] [UnitOfWork]
public virtual async Task<Dictionary<Guid, InventoryDataModel>> GetInventoryDataDictionaryAsync(Product product) public virtual async Task<Dictionary<Guid, InventoryDataModel>> GetSkuIdInventoryDataMappingAsync(
IList<InventoryQueryModel> models)
{ {
var dict = await _productInventoryRepository.GetInventoryDataDictionaryAsync(product.ProductSkus var dict = await _productInventoryRepository.GetSkuIdInventoryDataMappingAsync(
.Select(sku => sku.Id).ToList()); models.Select(x => x.ProductSkuId).ToList());
foreach (var sku in product.ProductSkus) foreach (var model in models)
{ {
dict.GetOrAdd(sku.Id, () => new InventoryDataModel()); dict.GetOrAdd(model.ProductSkuId, () => new InventoryDataModel());
} }
return dict; return dict;
} }
[UnitOfWork(true)] [UnitOfWork(true)]
public virtual async Task<bool> TryIncreaseInventoryAsync(Product product, ProductSku productSku, int quantity, bool decreaseSold) public virtual async Task<bool> TryIncreaseInventoryAsync(InventoryQueryModel model, int quantity,
bool decreaseSold)
{ {
var productInventory = await GetOrCreateProductInventoryAsync(product.Id, productSku.Id); var productInventory = await GetOrCreateProductInventoryAsync(model.ProductId, model.ProductSkuId);
return await TryIncreaseInventoryAsync(product, productInventory, quantity, decreaseSold); return await TryIncreaseInventoryAsync(model, productInventory, quantity, decreaseSold);
} }
[UnitOfWork(true)] [UnitOfWork(true)]
public virtual async Task<bool> TryReduceInventoryAsync(Product product, ProductSku productSku, int quantity, bool increaseSold) public virtual async Task<bool> TryReduceInventoryAsync(InventoryQueryModel model, int quantity,
bool increaseSold)
{ {
var productInventory = await GetOrCreateProductInventoryAsync(product.Id, productSku.Id); var productInventory = await GetOrCreateProductInventoryAsync(model.ProductId, model.ProductSkuId);
return await TryReduceInventoryAsync(product, productInventory, quantity, increaseSold); return await TryReduceInventoryAsync(model, productInventory, quantity, increaseSold);
} }
[UnitOfWork] [UnitOfWork]
protected virtual async Task<ProductInventory> GetOrCreateProductInventoryAsync(Guid productId, Guid productSkuId) protected virtual async Task<ProductInventory> GetOrCreateProductInventoryAsync(Guid productId,
Guid productSkuId)
{ {
var productInventory = var productInventory =
await _productInventoryRepository.FindAsync(x => await _productInventoryRepository.FindAsync(x =>
x.ProductId == productId && x.ProductSkuId == productSkuId); x.ProductId == productId && x.ProductSkuId == productSkuId);
if (productInventory is null) if (productInventory is null)
{ {
productInventory = new ProductInventory(_guidGenerator.Create(), _currentTenant.Id, productId, productInventory = new ProductInventory(_guidGenerator.Create(), _currentTenant.Id, productId,
@ -87,15 +91,16 @@ namespace EasyAbp.EShop.Products.Products
return productInventory; return productInventory;
} }
[UnitOfWork(true)] [UnitOfWork(true)]
public virtual async Task<bool> TryIncreaseInventoryAsync(Product product, ProductInventory productInventory, int quantity, bool decreaseSold) protected virtual async Task<bool> TryIncreaseInventoryAsync(InventoryQueryModel model,
ProductInventory productInventory, int quantity, bool decreaseSold)
{ {
if (quantity < 0) if (quantity < 0)
{ {
return false; return false;
} }
var originalInventory = productInventory.Inventory; var originalInventory = productInventory.Inventory;
if (!productInventory.TryIncreaseInventory(quantity, decreaseSold)) if (!productInventory.TryIncreaseInventory(quantity, decreaseSold))
@ -105,15 +110,16 @@ namespace EasyAbp.EShop.Products.Products
await _productInventoryRepository.UpdateAsync(productInventory, true); await _productInventoryRepository.UpdateAsync(productInventory, true);
await PublishInventoryChangedEventAsync(product.TenantId, product.StoreId, await PublishInventoryChangedEventAsync(model.TenantId, model.StoreId,
productInventory.ProductId, productInventory.ProductSkuId, originalInventory, productInventory.ProductId, productInventory.ProductSkuId, originalInventory,
productInventory.Inventory, productInventory.Sold); productInventory.Inventory, productInventory.Sold);
return true; return true;
} }
[UnitOfWork(true)] [UnitOfWork(true)]
public virtual async Task<bool> TryReduceInventoryAsync(Product product, ProductInventory productInventory, int quantity, bool increaseSold) protected virtual async Task<bool> TryReduceInventoryAsync(InventoryQueryModel model,
ProductInventory productInventory, int quantity, bool increaseSold)
{ {
if (quantity < 0) if (quantity < 0)
{ {
@ -129,7 +135,7 @@ namespace EasyAbp.EShop.Products.Products
await _productInventoryRepository.UpdateAsync(productInventory, true); await _productInventoryRepository.UpdateAsync(productInventory, true);
await PublishInventoryChangedEventAsync(product.TenantId, product.StoreId, await PublishInventoryChangedEventAsync(model.TenantId, model.StoreId,
productInventory.ProductId, productInventory.ProductSkuId, originalInventory, productInventory.ProductId, productInventory.ProductSkuId, originalInventory,
productInventory.Inventory, productInventory.Sold); productInventory.Inventory, productInventory.Sold);

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

@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace EasyAbp.EShop.Products.Products
{
public interface IProductInventoryProvider
{
Task<InventoryDataModel> GetInventoryDataAsync(Product product, ProductSku productSku);
Task<Dictionary<Guid, InventoryDataModel>> GetInventoryDataDictionaryAsync(Product product);
Task<bool> TryIncreaseInventoryAsync(Product product, ProductSku productSku, int quantity, bool decreaseSold);
Task<bool> TryReduceInventoryAsync(Product product, ProductSku productSku, int quantity, bool increaseSold);
}
}

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

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using EasyAbp.EShop.Products.ProductInventories;
using Volo.Abp.Domain.Services; using Volo.Abp.Domain.Services;
namespace EasyAbp.EShop.Products.Products namespace EasyAbp.EShop.Products.Products

45
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/OrderCreatedEventHandler.cs

@ -2,6 +2,7 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders; using EasyAbp.EShop.Orders.Orders;
using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus.Distributed; using Volo.Abp.EventBus.Distributed;
@ -15,6 +16,7 @@ namespace EasyAbp.EShop.Products.Products
private readonly ICurrentTenant _currentTenant; private readonly ICurrentTenant _currentTenant;
private readonly IUnitOfWorkManager _unitOfWorkManager; private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IDistributedEventBus _distributedEventBus; private readonly IDistributedEventBus _distributedEventBus;
private readonly ILogger<OrderCreatedEventHandler> _logger;
private readonly IProductRepository _productRepository; private readonly IProductRepository _productRepository;
private readonly IProductManager _productManager; private readonly IProductManager _productManager;
@ -22,12 +24,14 @@ namespace EasyAbp.EShop.Products.Products
ICurrentTenant currentTenant, ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager, IUnitOfWorkManager unitOfWorkManager,
IDistributedEventBus distributedEventBus, IDistributedEventBus distributedEventBus,
ILogger<OrderCreatedEventHandler> logger,
IProductRepository productRepository, IProductRepository productRepository,
IProductManager productManager) IProductManager productManager)
{ {
_currentTenant = currentTenant; _currentTenant = currentTenant;
_unitOfWorkManager = unitOfWorkManager; _unitOfWorkManager = unitOfWorkManager;
_distributedEventBus = distributedEventBus; _distributedEventBus = distributedEventBus;
_logger = logger;
_productRepository = productRepository; _productRepository = productRepository;
_productManager = productManager; _productManager = productManager;
} }
@ -65,33 +69,52 @@ namespace EasyAbp.EShop.Products.Products
return; return;
} }
models.Add(new ConsumeInventoryModel models.Add(new ConsumeInventoryModel(
{ product, productSku, eventData.Entity.StoreId, orderLine.Quantity));
Product = product,
ProductSku = productSku,
StoreId = eventData.Entity.StoreId,
Quantity = orderLine.Quantity
});
} }
foreach (var model in models) foreach (var model in models)
{ {
if (await _productManager.TryReduceInventoryAsync(model.Product, model.ProductSku, model.Quantity, true)) if (await _productManager.TryReduceInventoryAsync(
model.Product, model.ProductSku, model.Quantity, true))
{ {
continue; continue;
} }
// Todo: should release unused inventory since (external) inventory providers may not be transactional. await TryRollbackInventoriesAsync(models);
await _unitOfWorkManager.Current.RollbackAsync(); await _unitOfWorkManager.Current.RollbackAsync();
await PublishInventoryReductionResultEventAsync(eventData, false, true); await PublishInventoryReductionResultEventAsync(eventData, false, true);
return; return;
} }
await PublishInventoryReductionResultEventAsync(eventData, true); await PublishInventoryReductionResultEventAsync(eventData, true);
} }
protected virtual async Task<bool> TryRollbackInventoriesAsync(IEnumerable<ConsumeInventoryModel> models)
{
var result = true;
foreach (var model in models.Where(x => x.Consumed))
{
if (await _productManager.TryIncreaseInventoryAsync(
model.Product, model.ProductSku, model.Quantity, true))
{
continue;
}
result = false;
_logger.LogWarning(
"OrderCreatedEventHandler: inventory rollback failed! productId = {productId}, productSkuId = {productSkuId}, quantity = {quantity}, reduceSold = {reduceSold}",
model.Product.Id, model.ProductSku.Id, model.Quantity, true);
}
return result;
}
protected virtual async Task PublishInventoryReductionResultEventAsync(EntityCreatedEto<OrderEto> orderCreatedEto, bool isSuccess, bool publishNow = false) protected virtual async Task PublishInventoryReductionResultEventAsync(EntityCreatedEto<OrderEto> orderCreatedEto, bool isSuccess, bool publishNow = false)
{ {
await _distributedEventBus.PublishAsync(new ProductInventoryReductionAfterOrderPlacedResultEto await _distributedEventBus.PublishAsync(new ProductInventoryReductionAfterOrderPlacedResultEto

55
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/OrderPaidEventHandler.cs

@ -2,6 +2,7 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders; using EasyAbp.EShop.Orders.Orders;
using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed; using Volo.Abp.EventBus.Distributed;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
@ -13,6 +14,7 @@ namespace EasyAbp.EShop.Products.Products
{ {
private readonly ICurrentTenant _currentTenant; private readonly ICurrentTenant _currentTenant;
private readonly IUnitOfWorkManager _unitOfWorkManager; private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly ILogger<OrderPaidEventHandler> _logger;
private readonly IDistributedEventBus _distributedEventBus; private readonly IDistributedEventBus _distributedEventBus;
private readonly IProductRepository _productRepository; private readonly IProductRepository _productRepository;
private readonly IProductManager _productManager; private readonly IProductManager _productManager;
@ -20,24 +22,26 @@ namespace EasyAbp.EShop.Products.Products
public OrderPaidEventHandler( public OrderPaidEventHandler(
ICurrentTenant currentTenant, ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager, IUnitOfWorkManager unitOfWorkManager,
ILogger<OrderPaidEventHandler> logger,
IDistributedEventBus distributedEventBus, IDistributedEventBus distributedEventBus,
IProductRepository productRepository, IProductRepository productRepository,
IProductManager productManager) IProductManager productManager)
{ {
_currentTenant = currentTenant; _currentTenant = currentTenant;
_unitOfWorkManager = unitOfWorkManager; _unitOfWorkManager = unitOfWorkManager;
_logger = logger;
_distributedEventBus = distributedEventBus; _distributedEventBus = distributedEventBus;
_productRepository = productRepository; _productRepository = productRepository;
_productManager = productManager; _productManager = productManager;
} }
[UnitOfWork(true)] [UnitOfWork(true)]
public virtual async Task HandleEventAsync(OrderPaidEto eventData) public virtual async Task HandleEventAsync(OrderPaidEto eventData)
{ {
using var changeTenant = _currentTenant.Change(eventData.Order.TenantId); using var changeTenant = _currentTenant.Change(eventData.Order.TenantId);
var models = new List<ConsumeInventoryModel>(); var models = new List<ConsumeInventoryModel>();
foreach (var orderLine in eventData.Order.OrderLines) foreach (var orderLine in eventData.Order.OrderLines)
{ {
// Todo: Should use ProductHistory. // Todo: Should use ProductHistory.
@ -48,10 +52,10 @@ namespace EasyAbp.EShop.Products.Products
if (productSku == null) if (productSku == null)
{ {
await PublishInventoryReductionResultEventAsync(eventData, false); await PublishInventoryReductionResultEventAsync(eventData, false);
return; return;
} }
if (product.InventoryStrategy != InventoryStrategy.ReduceAfterPayment) if (product.InventoryStrategy != InventoryStrategy.ReduceAfterPayment)
{ {
continue; continue;
@ -60,37 +64,56 @@ namespace EasyAbp.EShop.Products.Products
if (!await _productManager.IsInventorySufficientAsync(product, productSku, orderLine.Quantity)) if (!await _productManager.IsInventorySufficientAsync(product, productSku, orderLine.Quantity))
{ {
await PublishInventoryReductionResultEventAsync(eventData, false); await PublishInventoryReductionResultEventAsync(eventData, false);
return; return;
} }
models.Add(new ConsumeInventoryModel models.Add(new ConsumeInventoryModel(product, productSku, eventData.Order.StoreId, orderLine.Quantity));
{
Product = product,
ProductSku = productSku,
StoreId = eventData.Order.StoreId,
Quantity = orderLine.Quantity
});
} }
foreach (var model in models) foreach (var model in models)
{ {
if (await _productManager.TryReduceInventoryAsync(model.Product, model.ProductSku, model.Quantity, true)) if (await _productManager.TryReduceInventoryAsync(
model.Product, model.ProductSku, model.Quantity, true))
{ {
continue; continue;
} }
await TryRollbackInventoriesAsync(models);
await _unitOfWorkManager.Current.RollbackAsync(); await _unitOfWorkManager.Current.RollbackAsync();
await PublishInventoryReductionResultEventAsync(eventData, false, true); await PublishInventoryReductionResultEventAsync(eventData, false, true);
return; return;
} }
await PublishInventoryReductionResultEventAsync(eventData, true); await PublishInventoryReductionResultEventAsync(eventData, true);
} }
protected virtual async Task PublishInventoryReductionResultEventAsync(OrderPaidEto orderPaidEto, bool isSuccess, bool publishNow = false) protected virtual async Task<bool> TryRollbackInventoriesAsync(IEnumerable<ConsumeInventoryModel> models)
{
var result = true;
foreach (var model in models.Where(x => x.Consumed))
{
if (await _productManager.TryIncreaseInventoryAsync(
model.Product, model.ProductSku, model.Quantity, true))
{
continue;
}
result = false;
_logger.LogWarning(
"OrderPaidEventHandler: inventory rollback failed! productId = {productId}, productSkuId = {productSkuId}, quantity = {quantity}, reduceSold = {reduceSold}",
model.Product.Id, model.ProductSku.Id, model.Quantity, true);
}
return result;
}
protected virtual async Task PublishInventoryReductionResultEventAsync(OrderPaidEto orderPaidEto,
bool isSuccess, bool publishNow = false)
{ {
await _distributedEventBus.PublishAsync(new ProductInventoryReductionAfterOrderPaidResultEto await _distributedEventBus.PublishAsync(new ProductInventoryReductionAfterOrderPaidResultEto
{ {

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

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using EasyAbp.EShop.Products.Options.ProductGroups; using EasyAbp.EShop.Products.Options.ProductGroups;
using EasyAbp.EShop.Products.ProductCategories; using EasyAbp.EShop.Products.ProductCategories;
using EasyAbp.EShop.Products.ProductDetails; using EasyAbp.EShop.Products.ProductDetails;
using EasyAbp.EShop.Products.ProductInventories;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Services; using Volo.Abp.Domain.Services;
@ -44,9 +45,9 @@ namespace EasyAbp.EShop.Products.Products
public virtual async Task<Product> CreateAsync(Product product, IEnumerable<Guid> categoryIds = null) public virtual async Task<Product> CreateAsync(Product product, IEnumerable<Guid> categoryIds = null)
{ {
product.TrimUniqueName(); product.TrimUniqueName();
await CheckProductGroupNameAsync(product); await CheckProductGroupNameAsync(product);
await CheckProductUniqueNameAsync(product); await CheckProductUniqueNameAsync(product);
await _productRepository.InsertAsync(product, autoSave: true); await _productRepository.InsertAsync(product, autoSave: true);
@ -64,7 +65,7 @@ namespace EasyAbp.EShop.Products.Products
{ {
throw new NonexistentProductGroupException(product.ProductGroupName); throw new NonexistentProductGroupException(product.ProductGroupName);
} }
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -91,7 +92,7 @@ namespace EasyAbp.EShop.Products.Products
await _productRepository.DeleteAsync(product, true); await _productRepository.DeleteAsync(product, true);
} }
[UnitOfWork(true)] [UnitOfWork(true)]
public virtual async Task DeleteAsync(Guid id) public virtual async Task DeleteAsync(Guid id)
{ {
@ -108,13 +109,13 @@ namespace EasyAbp.EShop.Products.Products
await CheckSkuAttributeOptionsAsync(product, productSku); await CheckSkuAttributeOptionsAsync(product, productSku);
await CheckProductSkuNameUniqueAsync(product, productSku); await CheckProductSkuNameUniqueAsync(product, productSku);
productSku.TrimName(); productSku.TrimName();
product.ProductSkus.AddIfNotContains(productSku); product.ProductSkus.AddIfNotContains(productSku);
await CheckProductDetailAsync(product); await CheckProductDetailAsync(product);
return await _productRepository.UpdateAsync(product, true); return await _productRepository.UpdateAsync(product, true);
} }
@ -124,9 +125,9 @@ namespace EasyAbp.EShop.Products.Products
{ {
return Task.CompletedTask; return Task.CompletedTask;
} }
if (product.ProductSkus.Where(sku => sku.Id != productSku.Id) if (product.ProductSkus.Where(sku => sku.Id != productSku.Id)
.FirstOrDefault(sku => sku.Name == productSku.Name) != null) .FirstOrDefault(sku => sku.Name == productSku.Name) != null)
{ {
throw new ProductSkuCodeDuplicatedException(product.Id, productSku.Name); throw new ProductSkuCodeDuplicatedException(product.Id, productSku.Name);
} }
@ -169,7 +170,7 @@ namespace EasyAbp.EShop.Products.Products
public virtual async Task<Product> DeleteSkuAsync(Product product, ProductSku productSku) public virtual async Task<Product> DeleteSkuAsync(Product product, ProductSku productSku)
{ {
product.ProductSkus.Remove(productSku); product.ProductSkus.Remove(productSku);
return await _productRepository.UpdateAsync(product, true); return await _productRepository.UpdateAsync(product, true);
} }
@ -178,20 +179,20 @@ namespace EasyAbp.EShop.Products.Products
{ {
await _productRepository.CheckUniqueNameAsync(product); await _productRepository.CheckUniqueNameAsync(product);
} }
protected virtual async Task CheckProductDetailAsync(Product product) protected virtual async Task CheckProductDetailAsync(Product product)
{ {
if (product.ProductDetailId.HasValue) if (product.ProductDetailId.HasValue)
{ {
await CheckProductDetailExistAsync(product.ProductDetailId.Value, product.StoreId); await CheckProductDetailExistAsync(product.ProductDetailId.Value, product.StoreId);
} }
foreach (var sku in product.ProductSkus.Where(x => x.ProductDetailId.HasValue)) foreach (var sku in product.ProductSkus.Where(x => x.ProductDetailId.HasValue))
{ {
await CheckProductDetailExistAsync(sku.ProductDetailId!.Value, product.StoreId); await CheckProductDetailExistAsync(sku.ProductDetailId!.Value, product.StoreId);
} }
} }
[UnitOfWork] [UnitOfWork]
protected virtual async Task CheckProductDetailExistAsync(Guid productDetailId, Guid storeId) protected virtual async Task CheckProductDetailExistAsync(Guid productDetailId, Guid storeId)
{ {
@ -202,7 +203,7 @@ namespace EasyAbp.EShop.Products.Products
throw new EntityNotFoundException(typeof(ProductDetail), productDetailId); throw new EntityNotFoundException(typeof(ProductDetail), productDetailId);
} }
} }
[UnitOfWork(true)] [UnitOfWork(true)]
protected virtual async Task UpdateProductCategoriesAsync(Guid productId, IEnumerable<Guid> categoryIds) protected virtual async Task UpdateProductCategoriesAsync(Guid productId, IEnumerable<Guid> categoryIds)
{ {
@ -212,7 +213,7 @@ namespace EasyAbp.EShop.Products.Products
{ {
return; return;
} }
foreach (var categoryId in categoryIds) foreach (var categoryId in categoryIds)
{ {
await _productCategoryRepository.InsertAsync( await _productCategoryRepository.InsertAsync(
@ -222,24 +223,34 @@ namespace EasyAbp.EShop.Products.Products
public virtual async Task<bool> IsInventorySufficientAsync(Product product, ProductSku productSku, int quantity) public virtual async Task<bool> IsInventorySufficientAsync(Product product, ProductSku productSku, int quantity)
{ {
var inventoryData = await _productInventoryProvider.GetInventoryDataAsync(product, productSku); var model = new InventoryQueryModel(product.TenantId, product.StoreId, product.Id, productSku.Id);
var inventoryData = await _productInventoryProvider.GetInventoryDataAsync(model);
return product.InventoryStrategy == InventoryStrategy.NoNeed || inventoryData.Inventory - quantity >= 0; return product.InventoryStrategy == InventoryStrategy.NoNeed || inventoryData.Inventory - quantity >= 0;
} }
public virtual async Task<InventoryDataModel> GetInventoryDataAsync(Product product, ProductSku productSku) public virtual async Task<InventoryDataModel> GetInventoryDataAsync(Product product, ProductSku productSku)
{ {
return await _productInventoryProvider.GetInventoryDataAsync(product, productSku); var model = new InventoryQueryModel(product.TenantId, product.StoreId, product.Id, productSku.Id);
return await _productInventoryProvider.GetInventoryDataAsync(model);
} }
public virtual async Task<bool> TryIncreaseInventoryAsync(Product product, ProductSku productSku, int quantity, bool reduceSold) public virtual async Task<bool> TryIncreaseInventoryAsync(Product product, ProductSku productSku, int quantity,
bool reduceSold)
{ {
return await _productInventoryProvider.TryIncreaseInventoryAsync(product, productSku, quantity, reduceSold); var model = new InventoryQueryModel(product.TenantId, product.StoreId, product.Id, productSku.Id);
return await _productInventoryProvider.TryIncreaseInventoryAsync(model, quantity, reduceSold);
} }
public virtual async Task<bool> TryReduceInventoryAsync(Product product, ProductSku productSku, int quantity, bool increaseSold) public virtual async Task<bool> TryReduceInventoryAsync(Product product, ProductSku productSku, int quantity,
bool increaseSold)
{ {
return await _productInventoryProvider.TryReduceInventoryAsync(product, productSku, quantity, increaseSold); var model = new InventoryQueryModel(product.TenantId, product.StoreId, product.Id, productSku.Id);
return await _productInventoryProvider.TryReduceInventoryAsync(model, quantity, increaseSold);
} }
public virtual async Task<PriceDataModel> GetRealPriceAsync(Product product, ProductSku productSku) public virtual async Task<PriceDataModel> GetRealPriceAsync(Product product, ProductSku productSku)
@ -247,7 +258,7 @@ namespace EasyAbp.EShop.Products.Products
var price = await _productPriceProvider.GetPriceAsync(product, productSku); var price = await _productPriceProvider.GetPriceAsync(product, productSku);
var discountedPrice = price; var discountedPrice = price;
// Todo: provider execution ordering. // Todo: provider execution ordering.
foreach (var provider in LazyServiceProvider.LazyGetService<IEnumerable<IProductDiscountProvider>>()) foreach (var provider in LazyServiceProvider.LazyGetService<IEnumerable<IProductDiscountProvider>>())
{ {

5
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.EntityFrameworkCore/EasyAbp/EShop/Products/ProductInventories/ProductInventoryRepository.cs

@ -4,7 +4,6 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using EasyAbp.EShop.Products.EntityFrameworkCore; using EasyAbp.EShop.Products.EntityFrameworkCore;
using EasyAbp.EShop.Products.Products;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore;
@ -29,9 +28,9 @@ namespace EasyAbp.EShop.Products.ProductInventories
.FirstOrDefaultAsync(cancellationToken); .FirstOrDefaultAsync(cancellationToken);
} }
public async Task<Dictionary<Guid, InventoryDataModel>> GetInventoryDataDictionaryAsync(List<Guid> productSkuIds, CancellationToken cancellationToken = default) public async Task<Dictionary<Guid, InventoryDataModel>> GetSkuIdInventoryDataMappingAsync(List<Guid> productSkuIds, CancellationToken cancellationToken = default)
{ {
return await GetQueryable() return await (await GetQueryableAsync())
.Where(x => productSkuIds.Contains(x.ProductSkuId)) .Where(x => productSkuIds.Contains(x.ProductSkuId))
.ToDictionaryAsync(x => x.ProductSkuId, x => new InventoryDataModel .ToDictionaryAsync(x => x.ProductSkuId, x => new InventoryDataModel
{ {

Loading…
Cancel
Save