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