mirror of https://github.com/EasyAbp/EShop.git
committed by
GitHub
32 changed files with 794 additions and 1 deletions
@ -0,0 +1,38 @@ |
|||
# EShop.Plugins.Inventories.DaprActors |
|||
|
|||
[](https://abp.io) |
|||
[](https://www.nuget.org/packages/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions) |
|||
[](https://www.nuget.org/packages/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions) |
|||
[](https://discord.gg/S6QaezrCRq) |
|||
[](https://www.github.com/EasyAbp/EShop) |
|||
|
|||
EShop product-inventory implementation of [Dapr Actors](https://docs.dapr.io/developing-applications/building-blocks/actors/actors-overview). |
|||
|
|||
## Installation |
|||
|
|||
1. Install the following NuGet packages. ([see how](https://github.com/EasyAbp/EasyAbpGuide/blob/master/docs/How-To.md#add-nuget-packages)) |
|||
|
|||
* EasyAbp.EShop.Products.DaprActorsInventory.Domain _(install at EasyAbp.EShop.Products.Domain location)_ |
|||
* EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore _(install at a host project to run Actors)_ |
|||
|
|||
2. Add `DependsOn(typeof(EShopXxxModule))` attribute to configure the module dependencies. ([see how](https://github.com/EasyAbp/EasyAbpGuide/blob/master/docs/How-To.md#add-module-dependencies)) |
|||
|
|||
## Usage |
|||
|
|||
1. Configure the DaprActors inventory provider as default. |
|||
```csharp |
|||
Configure<EShopProductsOptions>(options => |
|||
{ |
|||
// Configure as the default inventory provider |
|||
options.DefaultInventoryProviderName = "DaprActors"; |
|||
|
|||
// Configure as the default inventory provider for MyProductGroup |
|||
options.Groups.Configure<MyProductGroup>(group => |
|||
{ |
|||
group.DefaultInventoryProviderName = "DaprActors"; |
|||
}); |
|||
}); |
|||
``` |
|||
> Better to use `DaprActorsProductInventoryProvider.DaprActorsProductInventoryProviderName` instead of `"DaprActors"` as the provider name. |
|||
|
|||
2. Create a product and set `InventoryProviderName` to `DaprActors`. Then the product is specified to use the Dapr Actors inventory provider. |
|||
@ -0,0 +1 @@ |
|||
../../../docs/plugins/inventories/dapr-actors/README.md |
|||
@ -0,0 +1,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Dapr.Actors" Version="$(DaprSdkVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\..\..\..\modules\EasyAbp.EShop.Products\src\EasyAbp.EShop.Products.Domain.Shared\EasyAbp.EShop.Products.Domain.Shared.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,11 @@ |
|||
using EasyAbp.EShop.Products; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Inventories.DaprActors; |
|||
|
|||
[DependsOn( |
|||
typeof(EShopProductsDomainSharedModule) |
|||
)] |
|||
public class EShopPluginsInventoriesDaprActorsAbstractionsModule : AbpModule |
|||
{ |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System.Threading.Tasks; |
|||
using Dapr.Actors; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Inventories.DaprActors; |
|||
|
|||
public interface IInventoryActor : IActor |
|||
{ |
|||
Task<InventoryStateModel> GetInventoryStateAsync(); |
|||
|
|||
Task IncreaseInventoryAsync(int quantity, bool decreaseSold); |
|||
|
|||
Task ReduceInventoryAsync(int quantity, bool increaseSold); |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace EasyAbp.EShop.Plugins.Inventories.DaprActors; |
|||
|
|||
public class InventoryStateModel |
|||
{ |
|||
public int Inventory { get; set; } |
|||
|
|||
public long Sold { get; set; } |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,19 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\EasyAbp.EShop.Plugins.Inventories.DaprActors\EasyAbp.EShop.Plugins.Inventories.DaprActors.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.AspNetCore" Version="$(AbpVersion)" /> |
|||
<PackageReference Include="Dapr.Actors.AspNetCore" Version="$(DaprSdkVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,29 @@ |
|||
using EasyAbp.EShop.Products; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Routing; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.AspNetCore; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Inventories.DaprActors; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpAspNetCoreModule), |
|||
typeof(EShopProductsDomainSharedModule) |
|||
)] |
|||
public class EShopPluginsInventoriesDaprActorsAspNetCoreModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddActors(options => { options.Actors.RegisterActor<InventoryActor>(); }); |
|||
|
|||
Configure<AbpEndpointRouterOptions>(options => |
|||
{ |
|||
options.EndpointConfigureActions.Add(ctx => |
|||
{ |
|||
// Register actors handlers that interface with the Dapr runtime.
|
|||
ctx.Endpoints.MapActorsHandlers(); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions\EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Dapr.Actors" Version="$(DaprSdkVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,10 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Inventories.DaprActors; |
|||
|
|||
[DependsOn( |
|||
typeof(EShopPluginsInventoriesDaprActorsAbstractionsModule) |
|||
)] |
|||
public class EShopPluginsInventoriesDaprActorsModule : AbpModule |
|||
{ |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
using System.Threading.Tasks; |
|||
using Dapr; |
|||
using Dapr.Actors.Runtime; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Inventories.DaprActors; |
|||
|
|||
public class InventoryActor : Actor, IInventoryActor |
|||
{ |
|||
public static string InventoryStateName { get; set; } = "i"; |
|||
|
|||
public InventoryActor(ActorHost host) : base(host) |
|||
{ |
|||
} |
|||
|
|||
protected override async Task OnActivateAsync() |
|||
{ |
|||
await StateManager.TryAddStateAsync(InventoryStateName, new InventoryStateModel()); |
|||
} |
|||
|
|||
public virtual async Task<InventoryStateModel> GetInventoryStateAsync() |
|||
{ |
|||
return await StateManager.GetStateAsync<InventoryStateModel>(InventoryStateName); |
|||
} |
|||
|
|||
public virtual async Task IncreaseInventoryAsync(int quantity, bool decreaseSold) |
|||
{ |
|||
var state = await GetInventoryStateAsync(); |
|||
|
|||
InternalIncreaseInventory(state, quantity, decreaseSold); |
|||
} |
|||
|
|||
public async Task ReduceInventoryAsync(int quantity, bool increaseSold) |
|||
{ |
|||
var state = await GetInventoryStateAsync(); |
|||
|
|||
InternalReduceInventory(state, quantity, increaseSold); |
|||
} |
|||
|
|||
protected virtual void InternalIncreaseInventory(InventoryStateModel stateModel, int quantity, bool decreaseSold) |
|||
{ |
|||
if (quantity < 0) |
|||
{ |
|||
throw new DaprException("Quantity should not be less than 0."); |
|||
} |
|||
|
|||
if (decreaseSold && stateModel.Sold - quantity < 0) |
|||
{ |
|||
throw new DaprException("Target Sold cannot be less than 0."); |
|||
} |
|||
|
|||
stateModel.Inventory = checked(stateModel.Inventory + quantity); |
|||
|
|||
if (decreaseSold) |
|||
{ |
|||
stateModel.Sold -= quantity; |
|||
} |
|||
} |
|||
|
|||
protected virtual void InternalReduceInventory(InventoryStateModel stateModel, int quantity, bool increaseSold) |
|||
{ |
|||
if (quantity < 0) |
|||
{ |
|||
throw new DaprException("Quantity should not be less than 0."); |
|||
} |
|||
|
|||
if (quantity > stateModel.Inventory) |
|||
{ |
|||
throw new DaprException("Insufficient inventory."); |
|||
} |
|||
|
|||
if (increaseSold) |
|||
{ |
|||
stateModel.Sold = checked(stateModel.Sold + quantity); |
|||
} |
|||
|
|||
stateModel.Inventory -= quantity; |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,15 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\..\..\..\modules\EasyAbp.EShop.Products\src\EasyAbp.EShop.Products.Domain\EasyAbp.EShop.Products.Domain.csproj" /> |
|||
<ProjectReference Include="..\EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions\EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,113 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Dapr.Actors; |
|||
using EasyAbp.EShop.Plugins.Inventories.DaprActors; |
|||
using EasyAbp.EShop.Products.ProductInventories; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EasyAbp.EShop.Products.DaprActorsInventory; |
|||
|
|||
public class DaprActorsProductInventoryProvider : IProductInventoryProvider, ITransientDependency |
|||
{ |
|||
public static string DaprActorsProductInventoryProviderName { get; set; } = "DaprActors"; |
|||
public static string DaprActorsProductInventoryProviderDisplayName { get; set; } = "DaprActors"; |
|||
public static string DaprActorsProductInventoryProviderDescription { get; set; } = "DaprActors"; |
|||
|
|||
public string InventoryProviderName { get; } = DaprActorsProductInventoryProviderName; |
|||
|
|||
public static string ActorType { get; set; } = "InventoryActor"; |
|||
|
|||
private readonly ILogger<DaprActorsProductInventoryProvider> _logger; |
|||
protected IInventoryActorProvider InventoryActorProvider { get; } |
|||
|
|||
public DaprActorsProductInventoryProvider( |
|||
IInventoryActorProvider inventoryActorProvider, |
|||
ILogger<DaprActorsProductInventoryProvider> logger) |
|||
{ |
|||
InventoryActorProvider = inventoryActorProvider; |
|||
_logger = logger; |
|||
} |
|||
|
|||
public virtual async Task<InventoryDataModel> GetInventoryDataAsync(InventoryQueryModel model) |
|||
{ |
|||
var actor = await GetActorAsync(model); |
|||
|
|||
var stateModel = await actor.GetInventoryStateAsync(); |
|||
|
|||
return new InventoryDataModel |
|||
{ |
|||
Inventory = stateModel.Inventory, |
|||
Sold = stateModel.Sold |
|||
}; |
|||
} |
|||
|
|||
public virtual async Task<Dictionary<Guid, InventoryDataModel>> GetSkuIdInventoryDataMappingAsync( |
|||
IList<InventoryQueryModel> models) |
|||
{ |
|||
var result = new Dictionary<Guid, InventoryDataModel>(); |
|||
|
|||
foreach (var model in models) |
|||
{ |
|||
result.Add(model.ProductSkuId, await GetInventoryDataAsync(model)); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public virtual async Task<bool> TryIncreaseInventoryAsync(InventoryQueryModel model, int quantity, |
|||
bool decreaseSold) |
|||
{ |
|||
var actor = await GetActorAsync(model); |
|||
|
|||
try |
|||
{ |
|||
await actor.IncreaseInventoryAsync(quantity, decreaseSold); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
_logger.LogError("Actor threw: {Message}", e.Message); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
public virtual async Task<bool> TryReduceInventoryAsync(InventoryQueryModel model, int quantity, bool increaseSold) |
|||
{ |
|||
var actor = await GetActorAsync(model); |
|||
|
|||
var stateModel = await actor.GetInventoryStateAsync(); |
|||
|
|||
if (stateModel.Inventory < quantity) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
try |
|||
{ |
|||
await actor.ReduceInventoryAsync(quantity, increaseSold); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
_logger.LogError("Actor threw: {Message}", e.Message); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
protected virtual async Task<IInventoryActor> GetActorAsync(InventoryQueryModel model) |
|||
{ |
|||
return await InventoryActorProvider.GetAsync(GetActorId(model), ActorType); |
|||
} |
|||
|
|||
protected virtual ActorId GetActorId(InventoryQueryModel model) |
|||
{ |
|||
return new ActorId( |
|||
$"eshop_inventory_{(model.TenantId.HasValue ? model.TenantId.Value : "host")}_{model.ProductSkuId}"); |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using EasyAbp.EShop.Plugins.Inventories.DaprActors; |
|||
using EasyAbp.EShop.Products.Options; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace EasyAbp.EShop.Products.DaprActorsInventory; |
|||
|
|||
[DependsOn( |
|||
typeof(EShopProductsDomainModule), |
|||
typeof(EShopPluginsInventoriesDaprActorsAbstractionsModule) |
|||
)] |
|||
public class EShopProductsDaprActorsInventoryDomainModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<EShopProductsOptions>(options => |
|||
{ |
|||
options.InventoryProviders.Configure( |
|||
DaprActorsProductInventoryProvider.DaprActorsProductInventoryProviderName, provider => |
|||
{ |
|||
provider.DisplayName = |
|||
DaprActorsProductInventoryProvider.DaprActorsProductInventoryProviderDisplayName; |
|||
provider.Description = DaprActorsProductInventoryProvider |
|||
.DaprActorsProductInventoryProviderDescription; |
|||
provider.ProviderType = typeof(DaprActorsProductInventoryProvider); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System.Threading.Tasks; |
|||
using Dapr.Actors; |
|||
using EasyAbp.EShop.Plugins.Inventories.DaprActors; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace EasyAbp.EShop.Products.DaprActorsInventory; |
|||
|
|||
public interface IInventoryActorProvider |
|||
{ |
|||
Task<IInventoryActor> GetAsync(ActorId actorId, [NotNull] string actorType); |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System.Threading.Tasks; |
|||
using Dapr.Actors; |
|||
using Dapr.Actors.Client; |
|||
using EasyAbp.EShop.Plugins.Inventories.DaprActors; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EasyAbp.EShop.Products.DaprActorsInventory; |
|||
|
|||
public class InventoryActorProvider : IInventoryActorProvider, ITransientDependency |
|||
{ |
|||
public virtual Task<IInventoryActor> GetAsync(ActorId actorId, string actorType) |
|||
{ |
|||
return Task.FromResult(ActorProxy.Create<IInventoryActor>(actorId, actorType)); |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,48 @@ |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Products.ProductInventories; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace EasyAbp.EShop.Products.DaprActorsInventory.Domain; |
|||
|
|||
public class DaprActorsProductInventoryProviderTests : ProductsDaprActorsInventoryTestBase |
|||
{ |
|||
[Fact] |
|||
public async Task Should_Get_Inventory() |
|||
{ |
|||
var inventoryProvider = ServiceProvider.GetRequiredService<DaprActorsProductInventoryProvider>(); |
|||
|
|||
var inventoryDataModel = await inventoryProvider.GetInventoryDataAsync(new InventoryQueryModel()); |
|||
|
|||
inventoryDataModel.ShouldNotBeNull(); |
|||
inventoryDataModel.Inventory.ShouldBe(100); |
|||
inventoryDataModel.Sold.ShouldBe(0); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Change_Inventory() |
|||
{ |
|||
var inventoryProvider = ServiceProvider.GetRequiredService<DaprActorsProductInventoryProvider>(); |
|||
|
|||
var result = await inventoryProvider.TryReduceInventoryAsync(new InventoryQueryModel(), 2, true); |
|||
|
|||
result.ShouldBeTrue(); |
|||
|
|||
var inventoryDataModel = await inventoryProvider.GetInventoryDataAsync(new InventoryQueryModel()); |
|||
|
|||
inventoryDataModel.ShouldNotBeNull(); |
|||
inventoryDataModel.Inventory.ShouldBe(98); |
|||
inventoryDataModel.Sold.ShouldBe(2); |
|||
|
|||
result = await inventoryProvider.TryIncreaseInventoryAsync(new InventoryQueryModel(), 1, true); |
|||
|
|||
result.ShouldBeTrue(); |
|||
|
|||
inventoryDataModel = await inventoryProvider.GetInventoryDataAsync(new InventoryQueryModel()); |
|||
|
|||
inventoryDataModel.ShouldNotBeNull(); |
|||
inventoryDataModel.Inventory.ShouldBe(99); |
|||
inventoryDataModel.Sold.ShouldBe(1); |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using Volo.Abp; |
|||
using Volo.Abp.Authorization; |
|||
using Volo.Abp.Autofac; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace EasyAbp.EShop.Products.DaprActorsInventory.Domain; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpAutofacModule), |
|||
typeof(AbpTestBaseModule), |
|||
typeof(AbpAuthorizationModule), |
|||
typeof(EShopProductsDaprActorsInventoryDomainModule) |
|||
)] |
|||
public class EShopProductsDaprActorsInventoryDomainTestModule : AbpModule |
|||
{ |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace>EasyAbp.EShop.Products.DaprActorsInventory.Domain</RootNamespace> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> |
|||
<PackageReference Include="NSubstitute" Version="4.2.2" /> |
|||
<PackageReference Include="Shouldly" Version="4.0.1" /> |
|||
<PackageReference Include="xunit" Version="2.4.1" /> |
|||
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" /> |
|||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" /> |
|||
<PackageReference Include="Volo.Abp.Autofac" Version="$(AbpVersion)" /> |
|||
<PackageReference Include="Volo.Abp.Authorization" Version="$(AbpVersion)" /> |
|||
<PackageReference Include="Volo.Abp.TestBase" Version="$(AbpVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\EasyAbp.EShop.Products.DaprActorsInventory.Domain\EasyAbp.EShop.Products.DaprActorsInventory.Domain.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,43 @@ |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Plugins.Inventories.DaprActors; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EasyAbp.EShop.Products.DaprActorsInventory.Domain; |
|||
|
|||
public class FakeInventoryActor : IInventoryActor, ITransientDependency |
|||
{ |
|||
private InventoryStateModel StateModel { get; } = new() |
|||
{ |
|||
Inventory = 100, |
|||
Sold = 0 |
|||
}; |
|||
|
|||
public Task<InventoryStateModel> GetInventoryStateAsync() |
|||
{ |
|||
return Task.FromResult(StateModel); |
|||
} |
|||
|
|||
public Task IncreaseInventoryAsync(int quantity, bool decreaseSold) |
|||
{ |
|||
StateModel.Inventory += quantity; |
|||
|
|||
if (decreaseSold) |
|||
{ |
|||
StateModel.Sold -= quantity; |
|||
} |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
public Task ReduceInventoryAsync(int quantity, bool increaseSold) |
|||
{ |
|||
StateModel.Inventory -= quantity; |
|||
|
|||
if (increaseSold) |
|||
{ |
|||
StateModel.Sold += quantity; |
|||
} |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Testing; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace EasyAbp.EShop.Products.DaprActorsInventory.Domain |
|||
{ |
|||
/* All test classes are derived from this class, directly or indirectly. */ |
|||
public abstract class |
|||
ProductsDaprActorsInventoryTestBase : AbpIntegratedTest<EShopProductsDaprActorsInventoryDomainTestModule> |
|||
{ |
|||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|||
{ |
|||
options.UseAutofac(); |
|||
} |
|||
|
|||
protected virtual Task WithUnitOfWorkAsync(Func<Task> func) |
|||
{ |
|||
return WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), func); |
|||
} |
|||
|
|||
protected virtual async Task WithUnitOfWorkAsync(AbpUnitOfWorkOptions options, Func<Task> action) |
|||
{ |
|||
using (var scope = ServiceProvider.CreateScope()) |
|||
{ |
|||
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>(); |
|||
|
|||
using (var uow = uowManager.Begin(options)) |
|||
{ |
|||
await action(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected virtual Task<TResult> WithUnitOfWorkAsync<TResult>(Func<Task<TResult>> func) |
|||
{ |
|||
return WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), func); |
|||
} |
|||
|
|||
protected virtual async Task<TResult> WithUnitOfWorkAsync<TResult>(AbpUnitOfWorkOptions options, |
|||
Func<Task<TResult>> func) |
|||
{ |
|||
using (var scope = ServiceProvider.CreateScope()) |
|||
{ |
|||
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>(); |
|||
|
|||
using (var uow = uowManager.Begin(options)) |
|||
{ |
|||
var result = await func(); |
|||
await uow.CompleteAsync(); |
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Dapr.Actors; |
|||
using EasyAbp.EShop.Plugins.Inventories.DaprActors; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EasyAbp.EShop.Products.DaprActorsInventory.Domain; |
|||
|
|||
[Dependency(ReplaceServices = true)] |
|||
public class TestInventoryActorProvider : IInventoryActorProvider, ITransientDependency |
|||
{ |
|||
private IInventoryActor Actor { get; set; } |
|||
|
|||
private readonly IServiceProvider _serviceProvider; |
|||
|
|||
public TestInventoryActorProvider(IServiceProvider serviceProvider) |
|||
{ |
|||
_serviceProvider = serviceProvider; |
|||
} |
|||
|
|||
public Task<IInventoryActor> GetAsync(ActorId actorId, string actorType) |
|||
{ |
|||
return Task.FromResult(Actor ??= _serviceProvider.GetRequiredService<FakeInventoryActor>()); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue