60 changed files with 1738 additions and 134 deletions
@ -0,0 +1,31 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.Modularity; |
|||
using NRule = NRules.Fluent.Dsl.Rule; |
|||
|
|||
namespace LINGYUN.Abp.Rules.NRules |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpRulesModule) |
|||
)] |
|||
public class AbpNRulesModule : AbpModule |
|||
{ |
|||
private readonly AbpNRulesOptions options = new AbpNRulesOptions(); |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddObjectAccessor(options); |
|||
context.Services.OnRegistred(ctx => |
|||
{ |
|||
if (ctx.ImplementationType.IsAssignableTo(typeof(NRule))) |
|||
{ |
|||
options.Rules.AddIfNotContains(ctx.ImplementationType); |
|||
} |
|||
}); |
|||
} |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddNRules(options); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using Volo.Abp.Collections; |
|||
using NRule = NRules.Fluent.Dsl.Rule; |
|||
|
|||
namespace LINGYUN.Abp.Rules |
|||
{ |
|||
public class AbpNRulesOptions |
|||
{ |
|||
public ITypeList<NRule> Rules { get; } |
|||
public AbpNRulesOptions() |
|||
{ |
|||
Rules = new TypeList<NRule>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
using NRules.RuleModel; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Rules |
|||
{ |
|||
public class AbpRuleRepository : INRulesRepository |
|||
{ |
|||
public void Add(IRuleSet ruleSet) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public IEnumerable<IRuleSet> GetRuleSets() |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public void Remove(string ruleSetName) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public void Remove(IRuleSet ruleSet) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public IRuleSet GetRuleSet(string ruleSetName) |
|||
{ |
|||
return new RuleSet(ruleSetName); |
|||
} |
|||
|
|||
public IRuleSet GetRuleSet(RuleGroup group) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public IEnumerable<IRuleSet> GetRuleSets(IEnumerable<RuleGroup> groups) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using NRules.Extensibility; |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.Rules |
|||
{ |
|||
public class DependencyResolver : IDependencyResolver |
|||
{ |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
public DependencyResolver(IServiceProvider serviceProvider) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
} |
|||
|
|||
public virtual object Resolve(IResolutionContext context, Type serviceType) |
|||
{ |
|||
return ServiceProvider.GetRequiredService(serviceType); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using NRules.RuleModel; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Rules |
|||
{ |
|||
public interface INRulesRepository : IRuleRepository |
|||
{ |
|||
void Add(IRuleSet ruleSet); |
|||
|
|||
void Remove(string ruleSetName); |
|||
|
|||
void Remove(IRuleSet ruleSet); |
|||
|
|||
IRuleSet GetRuleSet(string ruleSetName); |
|||
|
|||
IRuleSet GetRuleSet(RuleGroup group); |
|||
|
|||
IEnumerable<IRuleSet> GetRuleSets(IEnumerable<RuleGroup> groups); |
|||
} |
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using NRules.RuleModel; |
|||
using NRules.RuleModel.Builders; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Linq.Dynamic.Core; |
|||
using System.Linq.Expressions; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.Rules |
|||
{ |
|||
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)] |
|||
[ExposeServices(typeof(IEntityRuleContributor))] |
|||
public class NRulesEntityRuleContributor : IEntityRuleContributor |
|||
{ |
|||
protected INRulesRepository Repository { get; } |
|||
public Task ApplyAsync(EntityRuleContext context) |
|||
{ |
|||
var entityType = context.Entity.GetType(); |
|||
var entityRuleName = RuleNameAttribute.GetRuleName(entityType); |
|||
|
|||
|
|||
|
|||
IEnumerable<IRuleSet> groupRuleSets = new List<IRuleSet>(); |
|||
|
|||
groupRuleSets = Repository.GetRuleSets(context.Groups); |
|||
|
|||
var sessionFactory = Repository.Compile(context.Groups); |
|||
var session = sessionFactory.CreateSession(); |
|||
session.Insert(context.Entity); |
|||
|
|||
session.Fire(); |
|||
|
|||
foreach (var groupRuleSet in groupRuleSets) |
|||
{ |
|||
} |
|||
|
|||
foreach (var group in context.Groups) |
|||
{ |
|||
var groupRuleSet = new RuleSet(group.Name); |
|||
|
|||
Repository.GetRuleSet(group.Name); |
|||
|
|||
foreach (var rule in group.Rules) |
|||
{ |
|||
var builder = new RuleBuilder(); |
|||
builder.Name(rule.Name); |
|||
|
|||
PatternBuilder thisRulePattern = builder.LeftHandSide().Pattern(entityType, entityRuleName); |
|||
ParameterExpression thisRuleParameter = thisRulePattern.Declaration.ToParameterExpression(); |
|||
var ruleCondition = Expression.Lambda(DynamicExpressionParser.ParseLambda(typeof(bool), rule.Expression), thisRuleParameter); |
|||
thisRulePattern.Condition(ruleCondition); |
|||
} |
|||
} |
|||
throw new NotImplementedException(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using NRules.Fluent; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.DependencyInjection; |
|||
using NRule = NRules.Fluent.Dsl.Rule; |
|||
|
|||
namespace LINGYUN.Abp.Rules |
|||
{ |
|||
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)] |
|||
[ExposeServices(typeof(IRuleActivator))] |
|||
public class RuleActivator : IRuleActivator |
|||
{ |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
public RuleActivator(IServiceProvider serviceProvider) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
} |
|||
public virtual IEnumerable<NRule> Activate(Type type) |
|||
{ |
|||
return (IEnumerable<NRule>)ServiceProvider.GetServices(type); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
using LINGYUN.Abp.Rules; |
|||
using NRules; |
|||
using NRules.Fluent; |
|||
using NRules.RuleModel; |
|||
using System; |
|||
|
|||
namespace Microsoft.Extensions.DependencyInjection |
|||
{ |
|||
public static class NRulesServiceCollectionExtensions |
|||
{ |
|||
public static IServiceCollection AddNRules(this IServiceCollection services, AbpNRulesOptions options) |
|||
{ |
|||
services.RegisterRepository(); |
|||
services.RegisterSessionFactory(); |
|||
services.RegisterSession(); |
|||
|
|||
return services; |
|||
} |
|||
|
|||
public static IServiceCollection RegisterRepository(this IServiceCollection services) |
|||
{ |
|||
services.AddSingleton<IRuleActivator, RuleActivator>(); |
|||
|
|||
services.AddSingleton<IRuleRepository, AbpRuleRepository>(); |
|||
services.AddSingleton<INRulesRepository, AbpRuleRepository>(); |
|||
|
|||
return services; |
|||
} |
|||
|
|||
public static IServiceCollection RegisterSessionFactory(this IServiceCollection services) |
|||
{ |
|||
services.RegisterSessionFactory((provider) => provider.GetRequiredService<IRuleRepository>().Compile()); |
|||
|
|||
return services; |
|||
} |
|||
|
|||
public static IServiceCollection RegisterSessionFactory(this IServiceCollection services, Func<IServiceProvider, ISessionFactory> compileFunc) |
|||
{ |
|||
services.AddSingleton<IRuleActivator, RuleActivator>(); |
|||
services.AddSingleton(compileFunc); |
|||
|
|||
return services; |
|||
} |
|||
|
|||
public static IServiceCollection RegisterSession(this IServiceCollection services) |
|||
{ |
|||
return services.RegisterSession((provider) => provider.GetRequiredService<ISessionFactory>().CreateSession()); |
|||
} |
|||
|
|||
public static IServiceCollection RegisterSession(this IServiceCollection services, Func<IServiceProvider, ISession> factoryFunc) |
|||
{ |
|||
return services.AddScoped(factoryFunc); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using NRules; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Rules |
|||
{ |
|||
public static class RuleRepositoryExtensions |
|||
{ |
|||
public static ISessionFactory Compile(this INRulesRepository repository, IEnumerable<RuleGroup> groups) |
|||
{ |
|||
var compiler = new RuleCompiler(); |
|||
ISessionFactory factory = compiler.Compile(repository.GetRuleSets(groups)); |
|||
return factory; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
using System; |
|||
using System.Reflection; |
|||
|
|||
namespace LINGYUN.Abp.Rules |
|||
{ |
|||
public class RuleNameAttribute : Attribute |
|||
{ |
|||
public string Name { get; } |
|||
public RuleNameAttribute(string name) |
|||
{ |
|||
Name = name; |
|||
} |
|||
|
|||
public virtual string GetRuleNameForType(Type ruleType) |
|||
{ |
|||
return Name; |
|||
} |
|||
|
|||
public static string GetRuleName<TEntity>() |
|||
{ |
|||
return GetRuleName(typeof(TEntity)); |
|||
} |
|||
|
|||
public static string GetRuleName(Type entityType) |
|||
{ |
|||
var ruleAttribute = entityType.GetSingleAttributeOrNull<RuleNameAttribute>(); |
|||
if (ruleAttribute != null) |
|||
{ |
|||
return ruleAttribute.GetRuleNameForType(entityType); |
|||
} |
|||
|
|||
return entityType.Name.RemovePostFix("Rule").ToKebabCase(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,22 +0,0 @@ |
|||
using LINGYUN.Abp.Rules; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.AutoMapper; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.RulesEngine |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpRulesModule), |
|||
typeof(AbpAutoMapperModule))] |
|||
public class AbpRulesEngineModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddAutoMapperObjectMapper<AbpRulesEngineModule>(); |
|||
Configure<AbpAutoMapperOptions>(options => |
|||
{ |
|||
options.AddProfile<MsRulesEngineMapperProfile>(validate: true); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -1,50 +0,0 @@ |
|||
using LINGYUN.Abp.Rules; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Logging; |
|||
using RulesEngine.Extensions; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.ObjectMapping; |
|||
using MsRulesEngine = RulesEngine.RulesEngine; |
|||
using MsWorkflowRules = RulesEngine.Models.WorkflowRules; |
|||
|
|||
namespace LINGYUN.Abp.RulesEngine |
|||
{ |
|||
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)] |
|||
[ExposeServices(typeof(IEntityRuleContributor))] |
|||
public class MsEntityRuleContributor : IEntityRuleContributor |
|||
{ |
|||
protected ILogger Logger { get; } |
|||
protected IObjectMapper ObjectMapper { get; } |
|||
public MsEntityRuleContributor( |
|||
IObjectMapper objectMapper, |
|||
ILogger<MsEntityRuleContributor> logger) |
|||
{ |
|||
Logger = logger; |
|||
ObjectMapper = objectMapper; |
|||
} |
|||
|
|||
public Task ApplyAsync(EntityRuleContext context) |
|||
{ |
|||
var workflowRules = ObjectMapper.Map<List<RuleGroup>, List<MsWorkflowRules>>(context.Groups); |
|||
|
|||
var rulesEngine = new MsRulesEngine(workflowRules.ToArray(), Logger); |
|||
|
|||
foreach(var workflow in workflowRules) |
|||
{ |
|||
var ruleRsults = rulesEngine.ExecuteRule(workflow.WorkflowName, context.Entity); |
|||
ruleRsults.OnSuccess((eventName) => |
|||
{ |
|||
Logger.LogDebug($"{workflow.WorkflowName} evaluation resulted in succees - {eventName}"); |
|||
}); |
|||
ruleRsults.OnFail(() => |
|||
{ |
|||
Logger.LogWarning($"{workflow.WorkflowName} evaluation resulted in failure"); |
|||
}); |
|||
} |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
} |
|||
@ -1,27 +0,0 @@ |
|||
using AutoMapper; |
|||
using LINGYUN.Abp.Rules; |
|||
using MsExpressionType = RulesEngine.Models.RuleExpressionType; |
|||
using MsRule = RulesEngine.Models.Rule; |
|||
using MsRuleErrorType = RulesEngine.Models.ErrorType; |
|||
using MsRuleParam = RulesEngine.Models.LocalParam; |
|||
using MsWorkflowRules = RulesEngine.Models.WorkflowRules; |
|||
|
|||
namespace LINGYUN.Abp.RulesEngine |
|||
{ |
|||
public class MsRulesEngineMapperProfile : Profile |
|||
{ |
|||
public MsRulesEngineMapperProfile() |
|||
{ |
|||
CreateMap<RuleParam, MsRuleParam>(); |
|||
CreateMap<Rule, MsRule>() |
|||
.ForMember(r => r.LocalParams, map => map.MapFrom(m => m.Params)) |
|||
.ForMember(r => r.WorkflowRulesToInject, map => map.MapFrom(m => m.InjectRules)) |
|||
.ForMember(r => r.ErrorType, map => map.MapFrom(m => (MsRuleErrorType)m.ErrorType.GetHashCode())) |
|||
.ForMember(r => r.RuleExpressionType, map => map.MapFrom(m => (MsExpressionType)m.ExpressionType.GetHashCode())); |
|||
CreateMap<RuleGroup, MsWorkflowRules>() |
|||
.ForMember(wr => wr.WorkflowName, map => map.MapFrom(m => m.Name)) |
|||
.ForMember(wr => wr.Rules, map => map.MapFrom(m => m.Rules)) |
|||
.ForMember(wr => wr.WorkflowRulesToInject, map => map.MapFrom(m => m.InjectRules)); |
|||
} |
|||
} |
|||
} |
|||
@ -1,7 +0,0 @@ |
|||
# LINGYUN.Abp.RulesEngine |
|||
|
|||
规则引擎 [microsoft/RulesEngine](https://github.com/microsoft/RulesEngine) (RulesEngine) 实现 |
|||
|
|||
## 配置使用 |
|||
|
|||
待完善 |
|||
@ -0,0 +1,8 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public class Class1 |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" Version="3.2.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Folder Include="LINGYUN\Abp\RulesManagement\" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,8 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement.Application |
|||
{ |
|||
public class Class1 |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Ddd.Application" Version="3.2.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Folder Include="LINGYUN\Abp\RulesManagement\" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,19 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Auditing" Version="3.2.0" /> |
|||
<PackageReference Include="Volo.Abp.Validation" Version="3.2.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Folder Include="LINGYUN\Abp\RulesManagement\Localization\Resources\" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,28 @@ |
|||
using LINGYUN.Abp.RulesManagement.Localization; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Validation; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpValidationModule))] |
|||
public class AbpRulesManagementDomainSharedModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpRulesManagementDomainSharedModule>(); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<RulesResource>() |
|||
.AddVirtualJson("/LINGYUN/Abp/RulesManagement/Localization/Resources"); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public static class EntityRuleConsts |
|||
{ |
|||
public static int MaxNameLength { get; set; } = 64; |
|||
public static int MaxDisplayNameLength { get; set; } = 256; |
|||
public static int MaxOperatorLength { get; set; } = 64; |
|||
public static int MaxErrorMessageLength { get; set; } = 256; |
|||
public static int MaxExpressionLength { get; set; } = 1024; |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using Volo.Abp.Auditing; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public class EntityRuleEto : IMultiTenant, ICreationAuditedObject, IModificationAuditedObject |
|||
{ |
|||
public Guid Id { get; set; } |
|||
public Guid? TenantId { get; set; } |
|||
public string Name { get; set; } |
|||
public string DisplayName { get; set; } |
|||
public Guid? LastModifierId { get; set; } |
|||
public DateTime? LastModificationTime { get; set; } |
|||
public DateTime CreationTime { get; set; } |
|||
public Guid? CreatorId { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public static class EntityRuleGroupConsts |
|||
{ |
|||
public static int MaxNameLength { get; set; } = 64; |
|||
public static int MaxDisplayNameLength { get; set; } = 256; |
|||
public static int MaxEntiyFullTypeNameLength { get; set; } = 256; |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using Volo.Abp.Auditing; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public class EntityRuleGroupEto : IMultiTenant, ICreationAuditedObject, IModificationAuditedObject |
|||
{ |
|||
public Guid Id { get; set; } |
|||
public Guid? TenantId { get; set; } |
|||
public string Name { get; set; } |
|||
public string DisplayName { get; set; } |
|||
public Guid? LastModifierId { get; set; } |
|||
public DateTime? LastModificationTime { get; set; } |
|||
public DateTime CreationTime { get; set; } |
|||
public Guid? CreatorId { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public static class EntityRuleParamConsts |
|||
{ |
|||
public static int MaxNameLength { get; set; } = 64; |
|||
public static int MaxExpressionLength { get; set; } = 1024; |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement.Localization |
|||
{ |
|||
[LocalizationResourceName("AbpRulesManagement")] |
|||
public class RulesResource |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.AutoMapper" Version="3.2.0" /> |
|||
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="3.2.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\common\LINGYUN.Abp.Rules\LINGYUN.Abp.Rules.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.RulesManagement.Domain.Shared\LINGYUN.Abp.RulesManagement.Domain.Shared.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,31 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.AutoMapper; |
|||
using Volo.Abp.Domain; |
|||
using Volo.Abp.Domain.Entities.Events.Distributed; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpDddDomainModule), |
|||
typeof(AbpAutoMapperModule), |
|||
typeof(AbpRulesManagementDomainSharedModule))] |
|||
public class AbpRulesManagementDomainModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddAutoMapperObjectMapper<AbpRulesManagementDomainModule>(); |
|||
|
|||
Configure<AbpAutoMapperOptions>(options => |
|||
{ |
|||
options.AddProfile<RulesManagementMapperProfile>(validate: true); |
|||
}); |
|||
|
|||
Configure<AbpDistributedEntityEventOptions>(options => |
|||
{ |
|||
options.EtoMappings.Add<EntityRule, EntityRuleEto>(typeof(AbpRulesManagementDomainModule)); |
|||
options.EtoMappings.Add<EntityRuleInGroup, EntityRuleGroupEto>(typeof(AbpRulesManagementDomainModule)); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,163 @@ |
|||
using JetBrains.Annotations; |
|||
using LINGYUN.Abp.Rules; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.ObjectModel; |
|||
using System.Linq; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
using Volo.Abp.Guids; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public class EntityRule : FullAuditedAggregateRoot<Guid>, IMultiTenant |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
/// <summary>
|
|||
/// 规则名称
|
|||
/// </summary>
|
|||
public virtual string Name { get; protected set; } |
|||
/// <summary>
|
|||
/// 显示名称
|
|||
/// </summary>
|
|||
public virtual string DisplayName { get; protected set; } |
|||
/// <summary>
|
|||
/// 操作类型
|
|||
/// </summary>
|
|||
public virtual string Operator { get; protected set; } |
|||
/// <summary>
|
|||
/// 错误提示
|
|||
/// </summary>
|
|||
public virtual string ErrorMessage { get; protected set; } |
|||
/// <summary>
|
|||
/// 错误类型
|
|||
/// </summary>
|
|||
public virtual ErrorType ErrorType { get; protected set; } |
|||
/// <summary>
|
|||
/// 表达式类型
|
|||
/// </summary>
|
|||
public virtual ExpressionType? ExpressionType { get; set; } |
|||
/// <summary>
|
|||
/// 所属规则
|
|||
/// </summary>
|
|||
public virtual Guid? ParentId { get; protected set; } |
|||
|
|||
public virtual ICollection<EntitySubRule> SubRules { get; protected set; } |
|||
public virtual ICollection<EntityRuleInject> InjectRules { get; protected set; } |
|||
public virtual ICollection<EntityRuleParam> Params { get; protected set; } |
|||
public virtual string Expression { get; protected set; } |
|||
|
|||
protected EntityRule() |
|||
{ |
|||
} |
|||
|
|||
public EntityRule( |
|||
[NotNull] Guid id, |
|||
[NotNull] string name, |
|||
[CanBeNull] string operation, |
|||
[CanBeNull] string displayName, |
|||
[CanBeNull] Guid? parentId = null, |
|||
[CanBeNull] Guid? tenantId = null) |
|||
{ |
|||
Check.NotNull(id, nameof(id)); |
|||
Check.NotNullOrWhiteSpace(name, nameof(name), EntityRuleConsts.MaxNameLength); |
|||
|
|||
Id = id; |
|||
Name = name; |
|||
Operator = operation; |
|||
DisplayName = displayName; |
|||
ParentId = parentId; |
|||
TenantId = tenantId; |
|||
|
|||
ExpressionType = Abp.Rules.ExpressionType.LambdaExpression; |
|||
|
|||
Params = new Collection<EntityRuleParam>(); |
|||
SubRules = new Collection<EntitySubRule>(); |
|||
InjectRules = new Collection<EntityRuleInject>(); |
|||
ExtraProperties = new Dictionary<string, object>(); |
|||
} |
|||
|
|||
public virtual void SetErrorInfomation(string errorMessage, ErrorType errorType = ErrorType.Warning) |
|||
{ |
|||
ErrorType = errorType; |
|||
ErrorMessage = errorMessage; |
|||
} |
|||
|
|||
public virtual void AddParamter([NotNull] IGuidGenerator guidGenerator, [NotNull] string name, [NotNull] string expression) |
|||
{ |
|||
Check.NotNull(guidGenerator, nameof(guidGenerator)); |
|||
|
|||
if (Params.Any(p => p.Name == name)) |
|||
{ |
|||
return; |
|||
} |
|||
Params.Add(new EntityRuleParam(guidGenerator.Create(), Id, name, expression, TenantId)); |
|||
} |
|||
|
|||
public virtual void RemoveParamter([NotNull] string name) |
|||
{ |
|||
Check.NotNullOrWhiteSpace(name, nameof(name)); |
|||
|
|||
Params.RemoveAll(p => p.Name == name); |
|||
} |
|||
|
|||
public virtual void AddInjectRule([NotNull] EntityRule rule) |
|||
{ |
|||
Check.NotNull(rule, nameof(rule)); |
|||
if (IsInjectRule(rule.Id)) |
|||
{ |
|||
return; |
|||
} |
|||
InjectRules.Add(new EntityRuleInject(rule.Id, Id, TenantId)); |
|||
} |
|||
|
|||
public virtual void RemoveInjectRule([NotNull] Guid ruleId) |
|||
{ |
|||
Check.NotNull(ruleId, nameof(ruleId)); |
|||
if (IsInjectRule(ruleId)) |
|||
{ |
|||
return; |
|||
} |
|||
InjectRules.RemoveAll(rule => rule.RuleId == ruleId); |
|||
} |
|||
|
|||
public virtual bool IsInjectRule([NotNull] Guid ruleId) |
|||
{ |
|||
Check.NotNull(ruleId, nameof(ruleId)); |
|||
|
|||
return InjectRules.Any(rule => rule.RuleId == ruleId); |
|||
} |
|||
|
|||
|
|||
public virtual void AddSubRule([NotNull] EntityRule rule) |
|||
{ |
|||
Check.NotNull(rule, nameof(rule)); |
|||
|
|||
if (IsInRule(rule.Id)) |
|||
{ |
|||
return; |
|||
} |
|||
SubRules.Add(new EntitySubRule(Id, rule.Id, TenantId)); |
|||
} |
|||
|
|||
public virtual void RemoveSubRule([NotNull] Guid ruleId) |
|||
{ |
|||
Check.NotNull(ruleId, nameof(ruleId)); |
|||
|
|||
if (!IsInRule(ruleId)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
SubRules.RemoveAll(r => r.SubId == ruleId); |
|||
} |
|||
|
|||
public virtual bool IsInRule([NotNull] Guid ruleId) |
|||
{ |
|||
Check.NotNull(ruleId, nameof(ruleId)); |
|||
|
|||
return SubRules.Any(r => r.SubId == ruleId); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Guids; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public class EntityRuleDataSeeder : IEntityRuleDataSeeder, ITransientDependency |
|||
{ |
|||
protected IGuidGenerator GuidGenerator { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected IEntityRuleGroupRepository Repository { get; } |
|||
|
|||
public EntityRuleDataSeeder( |
|||
IGuidGenerator guidGenerator, |
|||
ICurrentTenant currentTenant, |
|||
IEntityRuleGroupRepository repository) |
|||
{ |
|||
GuidGenerator = guidGenerator; |
|||
CurrentTenant = currentTenant; |
|||
Repository = repository; |
|||
} |
|||
|
|||
public virtual async Task SeedAsync(EntityRuleInGroup group) |
|||
{ |
|||
var findGroup = await Repository.GetByNameAsync(group.Name); |
|||
if (findGroup != null) |
|||
{ |
|||
return; |
|||
} |
|||
await Repository.InsertAsync(group); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,131 @@ |
|||
using LINGYUN.Abp.Rules; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Services; |
|||
using Volo.Abp.ObjectMapping; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)] |
|||
[ExposeServices(typeof(IRuleFinder))] |
|||
public class EntityRuleFinder : DomainService, IRuleFinder |
|||
{ |
|||
private IObjectMapper _objectMapper; |
|||
protected IObjectMapper ObjectMapper => LazyGetRequiredService(ref _objectMapper); |
|||
|
|||
private IEntityRuleGroupRepository _ruleGroupRepository; |
|||
protected IEntityRuleGroupRepository RuleGroupRepository => LazyGetRequiredService(ref _ruleGroupRepository); |
|||
|
|||
private IEntityRuleRepository _rruleRepository; |
|||
protected IEntityRuleRepository RuleRepository => LazyGetRequiredService(ref _rruleRepository); |
|||
|
|||
public virtual async Task<List<RuleGroup>> GetRuleGroupsAsync(Type entityType) |
|||
{ |
|||
var entityFullTypeName = entityType.FullName; |
|||
if (entityType.IsGenericType) |
|||
{ |
|||
entityFullTypeName = entityType.GetGenericTypeDefinition().FullName; |
|||
} |
|||
else if (entityType.IsArray) |
|||
{ |
|||
entityFullTypeName = entityType.GetElementType().FullName; |
|||
} |
|||
var entityRuleGroups = await RuleGroupRepository.GetListByTypeAsync(entityFullTypeName, includeDetails: true); |
|||
|
|||
var ruleGroups = ObjectMapper.Map<List<EntityRuleGroup>, List<RuleGroup>>(entityRuleGroups); |
|||
|
|||
foreach(var group in ruleGroups) |
|||
{ |
|||
var entityRuleGroup = entityRuleGroups.Find(g => g.Name.Equals(group.Name)); |
|||
if (entityRuleGroup != null) |
|||
{ |
|||
foreach(var ruleInGroup in entityRuleGroup.Rules) |
|||
{ |
|||
await AddRuleAsync(group, ruleInGroup.RuleId); |
|||
} |
|||
|
|||
foreach(var ruleInject in entityRuleGroup.InjectRules) |
|||
{ |
|||
await AddToInjectRuleAsync(group, ruleInject.RuleId); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return ruleGroups; |
|||
} |
|||
|
|||
protected virtual async Task AddRuleAsync(RuleGroup ruleGroup, Guid ruleId) |
|||
{ |
|||
var entityRule = await RuleRepository.FindAsync(ruleId); |
|||
if (entityRule == null) |
|||
{ |
|||
return; |
|||
} |
|||
var rule = ObjectMapper.Map<EntityRule, Rule>(entityRule); |
|||
ruleGroup.WithRule(rule); |
|||
|
|||
foreach (var subEntityRule in entityRule.SubRules) |
|||
{ |
|||
await AddSubRuleAsync(rule, subEntityRule.SubId); |
|||
} |
|||
|
|||
foreach (var ruleInject in entityRule.InjectRules) |
|||
{ |
|||
// 如果依赖于某个规则,需要把此规则添加到集合
|
|||
await AddRuleAsync(ruleGroup, ruleInject.InjectId); |
|||
|
|||
// 添加依赖规则
|
|||
await AddToInjectRuleAsync(ruleGroup, ruleInject.InjectId); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task AddSubRuleAsync(Rule rule, Guid subRuleId) |
|||
{ |
|||
var entityRule = await RuleRepository.FindAsync(subRuleId); |
|||
if (entityRule == null) |
|||
{ |
|||
return; |
|||
} |
|||
var subRule = ObjectMapper.Map<EntityRule, Rule>(entityRule); |
|||
rule.CreateChildren(subRule); |
|||
foreach (var subEntityRule in entityRule.SubRules) |
|||
{ |
|||
await AddSubRuleAsync(subRule, subEntityRule.SubId); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task AddToInjectRuleAsync(RuleGroup group, Guid ruleId) |
|||
{ |
|||
var entityRule = await RuleRepository.FindAsync(ruleId); |
|||
if (entityRule == null) |
|||
{ |
|||
return; |
|||
} |
|||
group.WithInjectRule(entityRule.Name); |
|||
foreach (var injectRule in entityRule.InjectRules) |
|||
{ |
|||
await AddToInjectRuleAsync(group, injectRule.InjectId); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task AddToInjectRuleAsync(Rule rule, Guid ruleId) |
|||
{ |
|||
var entityRule = await RuleRepository.FindAsync(ruleId); |
|||
if (entityRule == null) |
|||
{ |
|||
return; |
|||
} |
|||
var injectRule = ObjectMapper.Map<EntityRule, Rule>(entityRule); |
|||
rule.CreateChildren(injectRule); |
|||
rule.InjectRule(entityRule.Name); |
|||
foreach (var injectSubRule in entityRule.InjectRules) |
|||
{ |
|||
await AddToInjectRuleAsync(injectRule, injectSubRule.InjectId); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
using JetBrains.Annotations; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.ObjectModel; |
|||
using System.Linq; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public class EntityRuleGroup : FullAuditedAggregateRoot<Guid>, IMultiTenant |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
public virtual string Name { get; protected set; } |
|||
public virtual string DisplayName { get; protected set; } |
|||
public virtual string EntityFullTypeName { get; protected set; } |
|||
public virtual ICollection<EntityRuleInGroup> Rules { get; protected set; } |
|||
public virtual ICollection<EntityRuleInject> InjectRules { get; protected set; } |
|||
protected EntityRuleGroup() |
|||
{ |
|||
} |
|||
|
|||
public EntityRuleGroup( |
|||
[NotNull] Guid id, |
|||
[NotNull] string name, |
|||
[NotNull] string entiyFullTypeName, |
|||
[CanBeNull] string displayName, |
|||
[CanBeNull] Guid? tenantId = null) |
|||
{ |
|||
Check.NotNull(id, nameof(id)); |
|||
Check.NotNullOrWhiteSpace(name, nameof(name), EntityRuleGroupConsts.MaxNameLength); |
|||
Check.NotNullOrWhiteSpace(entiyFullTypeName, nameof(entiyFullTypeName), EntityRuleGroupConsts.MaxEntiyFullTypeNameLength); |
|||
|
|||
Id = id; |
|||
Name = name; |
|||
DisplayName = displayName; |
|||
TenantId = tenantId; |
|||
|
|||
Rules = new Collection<EntityRuleInGroup>(); |
|||
InjectRules = new Collection<EntityRuleInject>(); |
|||
ExtraProperties = new Dictionary<string, object>(); |
|||
} |
|||
|
|||
public virtual void AddInjectRule([NotNull] EntityRule rule) |
|||
{ |
|||
Check.NotNull(rule, nameof(rule)); |
|||
if (IsInjectRule(rule.Id)) |
|||
{ |
|||
return; |
|||
} |
|||
InjectRules.Add(new EntityRuleInject(rule.Id, Id, TenantId)); |
|||
} |
|||
|
|||
public virtual void RemoveInjectRule([NotNull] Guid ruleId) |
|||
{ |
|||
Check.NotNull(ruleId, nameof(ruleId)); |
|||
if (IsInjectRule(ruleId)) |
|||
{ |
|||
return; |
|||
} |
|||
InjectRules.RemoveAll(rule => rule.RuleId == ruleId); |
|||
} |
|||
|
|||
public virtual bool IsInjectRule([NotNull] Guid ruleId) |
|||
{ |
|||
Check.NotNull(ruleId, nameof(ruleId)); |
|||
|
|||
return InjectRules.Any(rule => rule.RuleId == ruleId); |
|||
} |
|||
|
|||
|
|||
public virtual void AddRule([NotNull] EntityRule rule) |
|||
{ |
|||
Check.NotNull(rule, nameof(rule)); |
|||
|
|||
if (IsInRule(rule.Id)) |
|||
{ |
|||
return; |
|||
} |
|||
Rules.Add(new EntityRuleInGroup(rule.Id, Id, TenantId)); |
|||
} |
|||
|
|||
public virtual void RemoveRule([NotNull] Guid ruleId) |
|||
{ |
|||
Check.NotNull(ruleId, nameof(ruleId)); |
|||
|
|||
if (!IsInRule(ruleId)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
Rules.RemoveAll(r => r.RuleId == ruleId); |
|||
} |
|||
|
|||
public virtual bool IsInRule([NotNull] Guid ruleId) |
|||
{ |
|||
Check.NotNull(ruleId, nameof(ruleId)); |
|||
|
|||
return Rules.Any(r => r.RuleId == ruleId); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
using JetBrains.Annotations; |
|||
using System; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Entities; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public class EntityRuleInGroup : Entity, IMultiTenant |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
public virtual Guid RuleId { get; protected set; } |
|||
public virtual Guid GroupId { get; protected set; } |
|||
protected EntityRuleInGroup() |
|||
{ |
|||
} |
|||
|
|||
public EntityRuleInGroup( |
|||
[NotNull] Guid ruleId, |
|||
[NotNull] Guid groupId, |
|||
[CanBeNull] Guid? tenantId = null) |
|||
{ |
|||
Check.NotNull(ruleId, nameof(ruleId)); |
|||
Check.NotNull(groupId, nameof(groupId)); |
|||
|
|||
RuleId = ruleId; |
|||
GroupId = groupId; |
|||
TenantId = tenantId; |
|||
} |
|||
public override object[] GetKeys() |
|||
{ |
|||
return new object[] { RuleId, GroupId }; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using JetBrains.Annotations; |
|||
using System; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Entities; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public class EntityRuleInject : Entity, IMultiTenant |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
/// <summary>
|
|||
/// 规则标识
|
|||
/// </summary>
|
|||
public virtual Guid RuleId { get; protected set; } |
|||
/// <summary>
|
|||
/// 依赖的规则标识
|
|||
/// </summary>
|
|||
public virtual Guid InjectId { get; protected set; } |
|||
|
|||
protected EntityRuleInject() { } |
|||
public EntityRuleInject( |
|||
[NotNull] Guid ruleId, |
|||
[NotNull] Guid injectId, |
|||
[CanBeNull] Guid? tenantId = null) |
|||
{ |
|||
Check.NotNull(ruleId, nameof(ruleId)); |
|||
Check.NotNull(injectId, nameof(injectId)); |
|||
|
|||
RuleId = ruleId; |
|||
InjectId = injectId; |
|||
TenantId = tenantId; |
|||
} |
|||
public override object[] GetKeys() |
|||
{ |
|||
return new object[] { RuleId , InjectId }; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
using JetBrains.Annotations; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.Domain.Entities; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public class EntityRuleParam : Entity<Guid>, IMultiTenant, IHasExtraProperties |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
public virtual Guid RuleId { get; protected set; } |
|||
public virtual string Name { get; protected set; } |
|||
public virtual string Expression { get; protected set; } |
|||
|
|||
public virtual Dictionary<string, object> ExtraProperties { get; protected set; } |
|||
|
|||
protected EntityRuleParam() |
|||
{ |
|||
} |
|||
public EntityRuleParam( |
|||
[NotNull] Guid id, |
|||
[NotNull] Guid ruleId, |
|||
[NotNull] string name, |
|||
[NotNull] string expression, |
|||
[CanBeNull] Guid? tenantId = null) |
|||
{ |
|||
Check.NotNull(id, nameof(id)); |
|||
Check.NotNull(ruleId, nameof(ruleId)); |
|||
Check.NotNullOrWhiteSpace(name, nameof(name), EntityRuleParamConsts.MaxNameLength); |
|||
Check.NotNullOrWhiteSpace(expression, nameof(expression), EntityRuleParamConsts.MaxExpressionLength); |
|||
|
|||
Id = id; |
|||
RuleId = ruleId; |
|||
Name = name; |
|||
Expression = expression; |
|||
TenantId = tenantId; |
|||
|
|||
ExtraProperties = new Dictionary<string, object>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
using JetBrains.Annotations; |
|||
using System; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Entities; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public class EntitySubRule : Entity, IMultiTenant |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
public virtual Guid RuleId { get; protected set; } |
|||
public virtual Guid SubId { get; protected set; } |
|||
protected EntitySubRule() |
|||
{ |
|||
} |
|||
|
|||
public EntitySubRule( |
|||
[NotNull] Guid ruleId, |
|||
[NotNull] Guid subId, |
|||
[CanBeNull] Guid? tenantId = null) |
|||
{ |
|||
Check.NotNull(ruleId, nameof(ruleId)); |
|||
Check.NotNull(subId, nameof(subId)); |
|||
|
|||
RuleId = ruleId; |
|||
SubId = subId; |
|||
TenantId = tenantId; |
|||
} |
|||
public override object[] GetKeys() |
|||
{ |
|||
return new object[] { RuleId, SubId }; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public interface IEntityRuleDataSeeder |
|||
{ |
|||
Task SeedAsync(EntityRuleInGroup group); |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public interface IEntityRuleGroupRepository : IBasicRepository<EntityRuleGroup, Guid> |
|||
{ |
|||
Task<EntityRuleGroup> GetByNameAsync( |
|||
string name, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
Task<List<EntityRuleGroup>> GetListByTypeAsync( |
|||
string entityFullTypeName, |
|||
string sorting = null, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<EntityRule>> GetRulesAsync( |
|||
Guid groupId, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
|
|||
Task<long> GetCountAsync( |
|||
string filter = null, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<EntityRuleGroup>> GetListAsync( |
|||
string filter = null, |
|||
string sorting = null, |
|||
int skipCount = 1, |
|||
int maxResultCount = 10, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public interface IEntityRuleRepository : IBasicRepository<EntityRule, Guid> |
|||
{ |
|||
Task<List<string>> GetInjectRuleNamesAsync( |
|||
Guid ruleId, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task<List<EntityRule>> GetInjectRulesAsync( |
|||
Guid ruleId, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task<long> GetCountAsync( |
|||
string filter = null, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<EntityRule>> GetListAsync( |
|||
string filter = null, |
|||
string sorting = null, |
|||
int skipCount = 1, |
|||
int maxResultCount = 10, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public static class RulesManagementDbProperties |
|||
{ |
|||
public static string DbTablePrefix { get; set; } = "App"; |
|||
|
|||
public static string DbSchema { get; set; } = AbpCommonDbProperties.DbSchema; |
|||
|
|||
public const string ConnectionStringName = "AppRulesManagement"; |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
using AutoMapper; |
|||
using LINGYUN.Abp.Rules; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement |
|||
{ |
|||
public class RulesManagementMapperProfile : Profile |
|||
{ |
|||
public RulesManagementMapperProfile() |
|||
{ |
|||
CreateMap<EntityRuleParam, RuleParam>(); |
|||
CreateMap<EntityRule, Rule>() |
|||
.ForMember(rule => rule.InjectRules, map => map.Ignore()) |
|||
.ForMember(rule => rule.Rules, map => map.Ignore()); |
|||
CreateMap<EntityRuleGroup, RuleGroup>() |
|||
.ForMember(rule => rule.InjectRules, map => map.Ignore()) |
|||
.ForMember(rule => rule.Rules, map => map.Ignore()); |
|||
|
|||
CreateMap<EntityRule, EntityRuleEto>(); |
|||
CreateMap<EntityRuleGroup, EntityRuleGroupEto>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="3.2.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Folder Include="LINGYUN\Abp\RulesManagement\" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,21 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement.EntityFrameworkCore |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpRulesManagementDomainModule), |
|||
typeof(AbpEntityFrameworkCoreModule))] |
|||
public class AbpRulesManagementEntityFrameworkCoreModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddAbpDbContext<RulesManagementDbContext>(options => |
|||
{ |
|||
options.AddRepository<EntityRule, EfCoreEntityRuleRepository>(); |
|||
options.AddRepository<EntityRuleInGroup, EfCoreEntityRuleGroupRepository>(); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,98 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Linq.Dynamic.Core; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement.EntityFrameworkCore |
|||
{ |
|||
public class EfCoreEntityRuleGroupRepository : EfCoreRepository<IRulesManagementDbContext, EntityRuleGroup, Guid>, IEntityRuleGroupRepository |
|||
{ |
|||
public EfCoreEntityRuleGroupRepository( |
|||
IDbContextProvider<IRulesManagementDbContext> dbContextProvider) |
|||
: base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public virtual async Task<EntityRuleGroup> GetByNameAsync( |
|||
string name, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await this |
|||
.Where(ug => ug.Name.Equals(name)) |
|||
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<List<EntityRule>> GetRulesAsync( |
|||
Guid groupId, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
var query = from ruleGroup in DbContext.Set<EntityRuleInGroup>() |
|||
join rule in DbContext.EntityRules.IncludeDetails(includeDetails) on ruleGroup.RuleId equals rule.Id |
|||
where ruleGroup.GroupId == groupId |
|||
select rule; |
|||
|
|||
return await query.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<long> GetCountAsync( |
|||
string filter = null, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await this.WhereIf( |
|||
!filter.IsNullOrWhiteSpace(), |
|||
rg => |
|||
rg.Name.Contains(filter) || |
|||
rg.DisplayName.Contains(filter) || |
|||
rg.EntityFullTypeName.Contains(filter) |
|||
) |
|||
.LongCountAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<List<EntityRuleGroup>> GetListByTypeAsync( |
|||
string entityFullTypeName, |
|||
string sorting = null, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await DbSet |
|||
.IncludeDetails(includeDetails) |
|||
.Where(ug => ug.EntityFullTypeName.Equals(entityFullTypeName)) |
|||
.OrderBy(sorting ?? nameof(EntityRuleGroup.CreationTime)) |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<List<EntityRuleGroup>> GetListAsync( |
|||
string filter = null, |
|||
string sorting = null, |
|||
int skipCount = 1, |
|||
int maxResultCount = 10, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await DbSet |
|||
.IncludeDetails(includeDetails) |
|||
.WhereIf( |
|||
!filter.IsNullOrWhiteSpace(), |
|||
rg => |
|||
rg.Name.Contains(filter) || |
|||
rg.DisplayName.Contains(filter) || |
|||
rg.EntityFullTypeName.Contains(filter) |
|||
) |
|||
.OrderBy(sorting ?? nameof(EntityRuleGroup.CreationTime)) |
|||
.PageBy(skipCount, maxResultCount) |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public override IQueryable<EntityRuleGroup> WithDetails() |
|||
{ |
|||
return GetQueryable().IncludeDetails(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,76 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Linq.Dynamic.Core; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement.EntityFrameworkCore |
|||
{ |
|||
public class EfCoreEntityRuleRepository : EfCoreRepository<IRulesManagementDbContext, EntityRule, Guid>, IEntityRuleRepository |
|||
{ |
|||
public EfCoreEntityRuleRepository( |
|||
IDbContextProvider<IRulesManagementDbContext> dbContextProvider) |
|||
: base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public virtual async Task<List<EntityRule>> GetInjectRulesAsync( |
|||
Guid ruleId, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default |
|||
) |
|||
{ |
|||
var query = from injectRule in DbContext.Set<EntityRuleInject>() |
|||
join rule in DbContext.EntityRules.IncludeDetails(includeDetails) on injectRule.InjectId equals rule.Id |
|||
where injectRule.RuleId == ruleId |
|||
select rule; |
|||
|
|||
return await query.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<long> GetCountAsync( |
|||
string filter = null, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await this |
|||
.WhereIf( |
|||
!filter.IsNullOrWhiteSpace(), |
|||
rg => |
|||
rg.Name.Contains(filter) || |
|||
rg.DisplayName.Contains(filter) || |
|||
rg.Operator.Contains(filter) |
|||
) |
|||
.LongCountAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public virtual async Task<List<EntityRule>> GetListAsync( |
|||
string filter = null, |
|||
string sorting = null, |
|||
int skipCount = 1, |
|||
int maxResultCount = 10, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await this |
|||
.WhereIf( |
|||
!filter.IsNullOrWhiteSpace(), |
|||
rg => |
|||
rg.Name.Contains(filter) || |
|||
rg.DisplayName.Contains(filter) || |
|||
rg.Operator.Contains(filter) |
|||
) |
|||
.OrderBy(sorting ?? nameof(EntityRule.CreationTime)) |
|||
.PageBy(skipCount, maxResultCount) |
|||
.ToListAsync(GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public override IQueryable<EntityRule> WithDetails() |
|||
{ |
|||
return GetQueryable().IncludeDetails(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement.EntityFrameworkCore |
|||
{ |
|||
[ConnectionStringName(RulesManagementDbProperties.ConnectionStringName)] |
|||
public interface IRulesManagementDbContext : IEfCoreDbContext |
|||
{ |
|||
DbSet<EntityRule> EntityRules { get; set; } |
|||
DbSet<EntityRuleGroup> EntityRuleGroups { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement.EntityFrameworkCore |
|||
{ |
|||
[ConnectionStringName(RulesManagementDbProperties.ConnectionStringName)] |
|||
public class RulesManagementDbContext : AbpDbContext<RulesManagementDbContext>, IRulesManagementDbContext |
|||
{ |
|||
public DbSet<EntityRule> EntityRules { get; set; } |
|||
public DbSet<EntityRuleGroup> EntityRuleGroups { get; set; } |
|||
|
|||
public RulesManagementDbContext( |
|||
DbContextOptions<RulesManagementDbContext> options) : base(options) |
|||
{ |
|||
} |
|||
|
|||
protected override void OnModelCreating(ModelBuilder builder) |
|||
{ |
|||
base.OnModelCreating(builder); |
|||
|
|||
builder.ConfigureRulesManagement(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
using JetBrains.Annotations; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using Volo.Abp; |
|||
using Volo.Abp.EntityFrameworkCore.Modeling; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement.EntityFrameworkCore |
|||
{ |
|||
public static class RulesManagementDbContextModelBuilderExtensions |
|||
{ |
|||
public static void ConfigureRulesManagement( |
|||
[NotNull] this ModelBuilder builder, |
|||
[CanBeNull] Action<RulesManagementModelBuilderConfigurationOptions> optionsAction = null) |
|||
{ |
|||
Check.NotNull(builder, nameof(builder)); |
|||
|
|||
var options = new RulesManagementModelBuilderConfigurationOptions( |
|||
RulesManagementDbProperties.DbTablePrefix, |
|||
RulesManagementDbProperties.DbSchema |
|||
); |
|||
|
|||
optionsAction?.Invoke(options); |
|||
|
|||
builder.Entity<EntityRuleParam>(b => |
|||
{ |
|||
b.ToTable(options.TablePrefix + "EntityRuleParams", options.Schema); |
|||
|
|||
b.ConfigureMultiTenant(); |
|||
b.ConfigureExtraProperties(); |
|||
|
|||
b.Property(x => x.Id).ValueGeneratedNever(); |
|||
|
|||
b.Property(rp => rp.Name).HasMaxLength(EntityRuleParamConsts.MaxNameLength).IsRequired(); |
|||
b.Property(rp => rp.Expression).HasMaxLength(EntityRuleParamConsts.MaxExpressionLength).IsRequired(); |
|||
|
|||
b.HasIndex(rp => rp.Name); |
|||
}); |
|||
|
|||
builder.Entity<EntityRule>(b => |
|||
{ |
|||
b.ToTable(options.TablePrefix + "EntityRules", options.Schema); |
|||
|
|||
b.ConfigureByConvention(); |
|||
|
|||
b.Property(x => x.Id).ValueGeneratedNever(); |
|||
|
|||
b.Property(r => r.Name).HasMaxLength(EntityRuleConsts.MaxNameLength).IsRequired(); |
|||
b.Property(r => r.Expression).HasMaxLength(EntityRuleConsts.MaxExpressionLength).IsRequired(); |
|||
|
|||
b.Property(r => r.Operator).HasMaxLength(EntityRuleConsts.MaxOperatorLength); |
|||
b.Property(r => r.DisplayName).HasMaxLength(EntityRuleConsts.MaxDisplayNameLength); |
|||
b.Property(r => r.ErrorMessage).HasMaxLength(EntityRuleConsts.MaxErrorMessageLength); |
|||
|
|||
b.HasMany(r => r.SubRules).WithOne().HasForeignKey(r => r.RuleId).IsRequired(); |
|||
b.HasMany(r => r.InjectRules).WithOne().HasForeignKey(r => r.InjectId).IsRequired(); |
|||
b.HasMany(r => r.Params).WithOne().HasForeignKey(r => r.RuleId).IsRequired(); |
|||
|
|||
b.HasIndex(r => r.Name); |
|||
}); |
|||
|
|||
builder.Entity<EntityRuleGroup>(b => |
|||
{ |
|||
b.ToTable(options.TablePrefix + "EntityRuleGroups", options.Schema); |
|||
|
|||
b.ConfigureByConvention(); |
|||
|
|||
b.Property(x => x.Id).ValueGeneratedNever(); |
|||
|
|||
b.Property(rg => rg.Name).HasMaxLength(EntityRuleGroupConsts.MaxNameLength).IsRequired(); |
|||
b.Property(rg => rg.EntityFullTypeName).HasMaxLength(EntityRuleGroupConsts.MaxEntiyFullTypeNameLength).IsRequired(); |
|||
|
|||
b.Property(rg => rg.DisplayName).HasMaxLength(EntityRuleGroupConsts.MaxDisplayNameLength); |
|||
|
|||
b.HasMany(rg => rg.Rules).WithOne().HasForeignKey(rg => rg.GroupId).IsRequired(); |
|||
b.HasMany(rg => rg.InjectRules).WithOne().HasForeignKey(rg => rg.InjectId).IsRequired(); |
|||
|
|||
b.HasIndex(uc => uc.Name); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System.Linq; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement.EntityFrameworkCore |
|||
{ |
|||
public static class RulesManagementEfCoreQueryableExtensions |
|||
{ |
|||
public static IQueryable<EntityRuleGroup> IncludeDetails(this IQueryable<EntityRuleGroup> queryable, bool include = true) |
|||
{ |
|||
if (!include) |
|||
{ |
|||
return queryable; |
|||
} |
|||
|
|||
return queryable |
|||
.Include(x => x.Rules) |
|||
.Include(x => x.InjectRules); |
|||
} |
|||
|
|||
public static IQueryable<EntityRule> IncludeDetails(this IQueryable<EntityRule> queryable, bool include = true) |
|||
{ |
|||
if (!include) |
|||
{ |
|||
return queryable; |
|||
} |
|||
|
|||
return queryable |
|||
.Include(x => x.InjectRules) |
|||
.Include(x => x.Params); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.EntityFrameworkCore.Modeling; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement.EntityFrameworkCore |
|||
{ |
|||
public class RulesManagementModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions |
|||
{ |
|||
public RulesManagementModelBuilderConfigurationOptions( |
|||
[NotNull] string tablePrefix, |
|||
[CanBeNull] string schema) |
|||
: base(tablePrefix, schema) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement.HttpApi.Client |
|||
{ |
|||
public class Class1 |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Http.Client" Version="3.2.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Folder Include="LINGYUN\Abp\RulesManagement\" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,8 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.RulesManagement.HttpApi |
|||
{ |
|||
public class Class1 |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="3.2.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Folder Include="LINGYUN\Abp\RulesManagement\" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,9 @@ |
|||
# LINGYUN.Abp.RulesManagement |
|||
|
|||
规则引擎管理 |
|||
|
|||
想法很美好,现实很残酷,.NET平台还没有Java阵营那么强大的规则引擎,目前还在研究NRules.Language库,这个库应该可以实现简单的规则动态管理 |
|||
|
|||
## 配置使用 |
|||
|
|||
待完善 |
|||
Loading…
Reference in new issue