23 changed files with 391 additions and 156 deletions
@ -0,0 +1,27 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Rules.RulesEngine |
||||
|
{ |
||||
|
public class AbpRulesEngineResolveOptions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 合并规则
|
||||
|
/// 如果为 true,在上一个提供者解析规则之后继续执行下一个提供者
|
||||
|
/// 如果为 false,在上一个提供者解析规则之后立即执行规则
|
||||
|
/// 默认:false
|
||||
|
/// </summary>
|
||||
|
public bool MergingRuels { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 规则解析提供者
|
||||
|
/// </summary>
|
||||
|
[NotNull] |
||||
|
public List<IWorkflowRulesResolveContributor> WorkflowRulesResolvers { get; } |
||||
|
|
||||
|
public AbpRulesEngineResolveOptions() |
||||
|
{ |
||||
|
MergingRuels = false; |
||||
|
WorkflowRulesResolvers = new List<IWorkflowRulesResolveContributor>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
namespace LINGYUN.Abp.Rules.RulesEngine.FileProviders.Physical |
||||
|
{ |
||||
|
public class AbpRulesEnginePthsicalFileResolveOptions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 本地文件路径
|
||||
|
/// </summary>
|
||||
|
public string PhysicalPath { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -1,42 +0,0 @@ |
|||||
using Microsoft.Extensions.Caching.Memory; |
|
||||
using Microsoft.Extensions.FileProviders; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
using System; |
|
||||
using System.IO; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
using Volo.Abp.Json; |
|
||||
|
|
||||
namespace LINGYUN.Abp.Rules.RulesEngine.FileProviders.Physical |
|
||||
{ |
|
||||
public class PhysicalFileWorkflowRulesContributor : FileProviderWorkflowRulesContributor, ISingletonDependency |
|
||||
{ |
|
||||
private readonly RuleIdGenerator _ruleIdGenerator; |
|
||||
private readonly AbpRulesEngineOptions _options; |
|
||||
public PhysicalFileWorkflowRulesContributor( |
|
||||
IMemoryCache ruleCache, |
|
||||
RuleIdGenerator ruleIdGenerator, |
|
||||
IJsonSerializer jsonSerializer, |
|
||||
IOptions<AbpRulesEngineOptions> options) |
|
||||
: base(ruleCache, jsonSerializer) |
|
||||
{ |
|
||||
_ruleIdGenerator = ruleIdGenerator; |
|
||||
|
|
||||
_options = options.Value; |
|
||||
} |
|
||||
|
|
||||
protected override IFileProvider BuildFileProvider() |
|
||||
{ |
|
||||
// 未指定路径不启用
|
|
||||
if (!_options.PhysicalPath.IsNullOrWhiteSpace() && |
|
||||
Directory.Exists(_options.PhysicalPath)) |
|
||||
{ |
|
||||
return new PhysicalFileProvider(_options.PhysicalPath); |
|
||||
} |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
protected override int GetRuleId<T>() => _ruleIdGenerator.CreateRuleId(typeof(T), _options.IgnoreMultiTenancy); |
|
||||
|
|
||||
protected override string GetRuleName<T>() => $"{_ruleIdGenerator.CreateRuleName(typeof(T), _options.IgnoreMultiTenancy)}.json"; |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,44 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.FileProviders; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using System; |
||||
|
using System.IO; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Rules.RulesEngine.FileProviders.Physical |
||||
|
{ |
||||
|
public class PhysicalFileWorkflowRulesResolveContributor : FileProviderWorkflowRulesResolveContributor, ISingletonDependency |
||||
|
{ |
||||
|
public override string Name => "PhysicalFile"; |
||||
|
|
||||
|
private RuleIdGenerator _ruleIdGenerator; |
||||
|
private AbpRulesEngineOptions _rulesEngineOptions; |
||||
|
private AbpRulesEnginePthsicalFileResolveOptions _fileResolveOptions; |
||||
|
|
||||
|
public PhysicalFileWorkflowRulesResolveContributor() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override void Initialize(IServiceProvider serviceProvider) |
||||
|
{ |
||||
|
_ruleIdGenerator = serviceProvider.GetRequiredService<RuleIdGenerator>(); |
||||
|
_rulesEngineOptions = serviceProvider.GetRequiredService<IOptions<AbpRulesEngineOptions>>().Value; |
||||
|
_fileResolveOptions = serviceProvider.GetRequiredService<IOptions<AbpRulesEnginePthsicalFileResolveOptions>>().Value; |
||||
|
} |
||||
|
|
||||
|
protected override IFileProvider BuildFileProvider(RulesInitializationContext context) |
||||
|
{ |
||||
|
// 未指定路径不启用
|
||||
|
if (!_fileResolveOptions.PhysicalPath.IsNullOrWhiteSpace() && |
||||
|
Directory.Exists(_fileResolveOptions.PhysicalPath)) |
||||
|
{ |
||||
|
return new PhysicalFileProvider(_fileResolveOptions.PhysicalPath); |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
protected override int GetRuleId(Type type) => _ruleIdGenerator.CreateRuleId(type, _rulesEngineOptions.IgnoreMultiTenancy); |
||||
|
|
||||
|
protected override string GetRuleName(Type type) => $"{_ruleIdGenerator.CreateRuleName(type, _rulesEngineOptions.IgnoreMultiTenancy)}.json"; |
||||
|
} |
||||
|
} |
||||
@ -1,15 +0,0 @@ |
|||||
using RulesEngine.Models; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
|
|
||||
namespace LINGYUN.Abp.Rules.RulesEngine |
|
||||
{ |
|
||||
public interface IWorkflowRulesContributor |
|
||||
{ |
|
||||
void Initialize(); |
|
||||
|
|
||||
Task<WorkflowRules[]> LoadAsync<T>(CancellationToken cancellationToken = default); |
|
||||
|
|
||||
void Shutdown(); |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,19 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using RulesEngine.Models; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Rules.RulesEngine |
||||
|
{ |
||||
|
public interface IWorkflowRulesResolveContext : IServiceProviderAccessor |
||||
|
{ |
||||
|
[CanBeNull] |
||||
|
IEnumerable<WorkflowRules> WorkflowRules { get; set; } |
||||
|
|
||||
|
[NotNull] |
||||
|
Type Type { get; } |
||||
|
|
||||
|
bool Handled { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Rules.RulesEngine |
||||
|
{ |
||||
|
public interface IWorkflowRulesResolveContributor |
||||
|
{ |
||||
|
string Name { get; } |
||||
|
|
||||
|
Task ResolveAsync(IWorkflowRulesResolveContext context); |
||||
|
|
||||
|
void Initialize(RulesInitializationContext context); |
||||
|
|
||||
|
void Shutdown(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Rules.RulesEngine |
||||
|
{ |
||||
|
public interface IWorkflowRulesResolver |
||||
|
{ |
||||
|
void Initialize(RulesInitializationContext context); |
||||
|
|
||||
|
[NotNull] |
||||
|
Task<WorkflowRulesResolveResult> ResolveWorkflowRulesAsync(Type type); |
||||
|
|
||||
|
void Shutdown(); |
||||
|
} |
||||
|
} |
||||
@ -1,25 +0,0 @@ |
|||||
using RulesEngine.Models; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
|
|
||||
namespace LINGYUN.Abp.Rules.RulesEngine |
|
||||
{ |
|
||||
public class NullWorkflowRulesContributor : IWorkflowRulesContributor, ISingletonDependency |
|
||||
{ |
|
||||
public void Initialize() |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
|
|
||||
public Task<WorkflowRules[]> LoadAsync<T>(CancellationToken cancellationToken = default) |
|
||||
{ |
|
||||
return Task.FromResult(new WorkflowRules[0]); |
|
||||
} |
|
||||
|
|
||||
public void Shutdown() |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,16 @@ |
|||||
|
using RulesEngine.Models; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Rules.RulesEngine.Persistent |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 实现此接口以用于从其他持久化存储中获取规则
|
||||
|
/// </summary>
|
||||
|
public interface IWorkflowRuleStore |
||||
|
{ |
||||
|
Task<IEnumerable<WorkflowRules>> GetRulesAsync(Type inputType, CancellationToken cancellationToken = default); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
using RulesEngine.Models; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Rules.RulesEngine.Persistent |
||||
|
{ |
||||
|
[Dependency(TryRegister = true)] |
||||
|
public class NullWorkflowRuleStore : IWorkflowRuleStore, ISingletonDependency |
||||
|
{ |
||||
|
public Task<IEnumerable<WorkflowRules>> GetRulesAsync(Type inputType, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
IEnumerable<WorkflowRules> rules = new WorkflowRules[0]; |
||||
|
return Task.FromResult(rules); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Rules.RulesEngine.Persistent |
||||
|
{ |
||||
|
public class PersistentWorkflowRulesResolveContributor : WorkflowRulesResolveContributorBase, ITransientDependency |
||||
|
{ |
||||
|
private IWorkflowRuleStore _store; |
||||
|
public override string Name => "Persistent"; |
||||
|
|
||||
|
protected IWorkflowRuleStore Store => _store; |
||||
|
|
||||
|
public PersistentWorkflowRulesResolveContributor() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public override void Initialize(RulesInitializationContext context) |
||||
|
{ |
||||
|
_store = context.ServiceProvider.GetRequiredService<IWorkflowRuleStore>(); |
||||
|
} |
||||
|
|
||||
|
public override async Task ResolveAsync(IWorkflowRulesResolveContext context) |
||||
|
{ |
||||
|
var rules = await Store.GetRulesAsync(context.Type); |
||||
|
|
||||
|
context.Handled = true; |
||||
|
context.WorkflowRules = rules; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using RulesEngine.Models; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Volo.Abp; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Rules.RulesEngine |
||||
|
{ |
||||
|
public class WorkflowRulesResolveContext : IWorkflowRulesResolveContext |
||||
|
{ |
||||
|
public Type Type { get; } |
||||
|
public IServiceProvider ServiceProvider { get; } |
||||
|
public IEnumerable<WorkflowRules> WorkflowRules { get; set; } |
||||
|
public bool Handled { get; set; } |
||||
|
|
||||
|
public bool HasResolved() |
||||
|
{ |
||||
|
return Handled && WorkflowRules?.Any() == true; |
||||
|
} |
||||
|
|
||||
|
public WorkflowRulesResolveContext( |
||||
|
[NotNull] Type type, |
||||
|
IServiceProvider serviceProvider) |
||||
|
{ |
||||
|
Type = Check.NotNull(type, nameof(type)); |
||||
|
ServiceProvider = serviceProvider; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Rules.RulesEngine |
||||
|
{ |
||||
|
public abstract class WorkflowRulesResolveContributorBase : IWorkflowRulesResolveContributor |
||||
|
{ |
||||
|
public abstract string Name { get; } |
||||
|
|
||||
|
public virtual void Initialize(RulesInitializationContext context) |
||||
|
{ |
||||
|
} |
||||
|
public abstract Task ResolveAsync(IWorkflowRulesResolveContext context); |
||||
|
|
||||
|
public virtual void Shutdown() |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using RulesEngine.Models; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Rules.RulesEngine |
||||
|
{ |
||||
|
public class WorkflowRulesResolveResult |
||||
|
{ |
||||
|
public List<WorkflowRules> WorkflowRules { get; set; } |
||||
|
|
||||
|
public List<string> AppliedResolvers { get; } |
||||
|
|
||||
|
public WorkflowRulesResolveResult() |
||||
|
{ |
||||
|
AppliedResolvers = new List<string>(); |
||||
|
WorkflowRules = new List<WorkflowRules>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,67 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Rules.RulesEngine |
||||
|
{ |
||||
|
public class WorkflowRulesResolver : IWorkflowRulesResolver, ITransientDependency |
||||
|
{ |
||||
|
private readonly IServiceProvider _serviceProvider; |
||||
|
private readonly AbpRulesEngineResolveOptions _options; |
||||
|
|
||||
|
public WorkflowRulesResolver( |
||||
|
IServiceProvider serviceProvider, |
||||
|
IOptions<AbpRulesEngineResolveOptions> options) |
||||
|
{ |
||||
|
_options = options.Value; |
||||
|
_serviceProvider = serviceProvider; |
||||
|
} |
||||
|
|
||||
|
public virtual void Initialize(RulesInitializationContext context) |
||||
|
{ |
||||
|
foreach (var workflowRulesResolver in _options.WorkflowRulesResolvers) |
||||
|
{ |
||||
|
workflowRulesResolver.Initialize(context); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<WorkflowRulesResolveResult> ResolveWorkflowRulesAsync(Type type) |
||||
|
{ |
||||
|
var result = new WorkflowRulesResolveResult(); |
||||
|
|
||||
|
using (var serviceScope = _serviceProvider.CreateScope()) |
||||
|
{ |
||||
|
var context = new WorkflowRulesResolveContext(type, serviceScope.ServiceProvider); |
||||
|
|
||||
|
foreach (var workflowRulesResolver in _options.WorkflowRulesResolvers) |
||||
|
{ |
||||
|
await workflowRulesResolver.ResolveAsync(context); |
||||
|
|
||||
|
result.AppliedResolvers.Add(workflowRulesResolver.Name); |
||||
|
|
||||
|
if (context.HasResolved()) |
||||
|
{ |
||||
|
result.WorkflowRules.AddRange(context.WorkflowRules); |
||||
|
|
||||
|
if (!_options.MergingRuels) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
public virtual void Shutdown() |
||||
|
{ |
||||
|
foreach (var workflowRulesResolver in _options.WorkflowRulesResolvers) |
||||
|
{ |
||||
|
workflowRulesResolver.Shutdown(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue