mirror of https://github.com/EasyAbp/EShop.git
19 changed files with 446 additions and 0 deletions
@ -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,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.AspNetCore" Version="$(DaprSdkVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,15 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Inventories.DaprActors; |
|||
|
|||
[DependsOn( |
|||
typeof(EShopPluginsInventoriesDaprActorsAbstractionsModule) |
|||
)] |
|||
public class EShopPluginsInventoriesDaprActorsModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddActors(options => { options.Actors.RegisterActor<InventoryActor>(); }); |
|||
} |
|||
} |
|||
@ -0,0 +1,76 @@ |
|||
using System.Threading.Tasks; |
|||
using Dapr; |
|||
using Dapr.Actors.Runtime; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Inventories.DaprActors; |
|||
|
|||
public class InventoryActor : Actor, IInventoryActor |
|||
{ |
|||
public InventoryActor(ActorHost host) : base(host) |
|||
{ |
|||
} |
|||
|
|||
protected override async Task OnActivateAsync() |
|||
{ |
|||
await StateManager.TryAddStateAsync(Id.GetId(), new InventoryStateModel()); |
|||
} |
|||
|
|||
public virtual async Task<InventoryStateModel> GetInventoryStateAsync() |
|||
{ |
|||
return await StateManager.GetStateAsync<InventoryStateModel>(Id.GetId()); |
|||
} |
|||
|
|||
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,110 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Dapr.Actors; |
|||
using Dapr.Actors.Client; |
|||
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; |
|||
|
|||
public DaprActorsProductInventoryProvider(ILogger<DaprActorsProductInventoryProvider> logger) |
|||
{ |
|||
_logger = logger; |
|||
} |
|||
|
|||
public virtual async Task<InventoryDataModel> GetInventoryDataAsync(InventoryQueryModel model) |
|||
{ |
|||
var actor = GetActor(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 = GetActor(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 = GetActor(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 IInventoryActor GetActor(InventoryQueryModel model) |
|||
{ |
|||
return ActorProxy.Create<IInventoryActor>(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,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> |
|||
Loading…
Reference in new issue