diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN.Abp.Features.Validation.Redis.csproj b/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN.Abp.Features.Validation.Redis.csproj index 16c2c6c76..6a5cac13d 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN.Abp.Features.Validation.Redis.csproj +++ b/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN.Abp.Features.Validation.Redis.csproj @@ -8,7 +8,6 @@ - diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/AbpRedisRequiresLimitFeatureOptions.cs b/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/AbpRedisRequiresLimitFeatureOptions.cs index ae09acb15..af8fb5248 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/AbpRedisRequiresLimitFeatureOptions.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/AbpRedisRequiresLimitFeatureOptions.cs @@ -8,16 +8,6 @@ namespace LINGYUN.Abp.Features.Validation.Redis public string Configuration { get; set; } public string InstanceName { get; set; } public ConfigurationOptions ConfigurationOptions { get; set; } - /// - /// 失败重试次数 - /// default: 3 - /// - public int FailedRetryCount { get; set; } = 3; - /// - /// 失败重试间隔 ms - /// default: 1000 - /// - public int FailedRetryInterval { get; set; } = 1000; AbpRedisRequiresLimitFeatureOptions IOptions.Value { get { return this; } diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/Lua/check.lua b/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/Lua/check.lua index 5a96ae20a..d0ade3b74 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/Lua/check.lua +++ b/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/Lua/check.lua @@ -1,4 +1,4 @@ if (redis.call('EXISTS', KEYS[1]) == 0) then - redis.call('SETEX',KEYS[1],ARGV[1], 0) + return 0 end return tonumber(redis.call('GET', KEYS[1])) \ No newline at end of file diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/Lua/process.lua b/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/Lua/process.lua index 9585c4990..fc8ef5307 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/Lua/process.lua +++ b/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/Lua/process.lua @@ -1,6 +1,6 @@ if (redis.call('EXISTS',KEYS[1]) ~= 0) then redis.call('INCRBY',KEYS[1], 1) else - redis.call('SETEX',KEYS[1],ARGV[1],0) + redis.call('SETEX',KEYS[1],ARGV[1],1) end return tonumber(redis.call('GET',KEYS[1])) \ No newline at end of file diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/RedisRequiresLimitFeatureChecker.cs b/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/RedisRequiresLimitFeatureChecker.cs index c07e601e5..690df549f 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/RedisRequiresLimitFeatureChecker.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/RedisRequiresLimitFeatureChecker.cs @@ -99,9 +99,9 @@ namespace LINGYUN.Abp.Features.Validation.Redis { if (_currentTenant.IsAvailable) { - return $"{_instance}t:RequiresLimitFeature;t:{_currentTenant.Id};f:{context.Feature}"; + return $"{_instance}t:RequiresLimitFeature;t:{_currentTenant.Id};f:{context.LimitFeature}"; } - return $"{_instance}c:RequiresLimitFeature;f:{context.Feature}"; + return $"{_instance}c:RequiresLimitFeature;f:{context.LimitFeature}"; } private void RegistenConnectionEvent(ConnectionMultiplexer connection) diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/AbpFeaturesValidationModule.cs b/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/AbpFeaturesValidationModule.cs index 07ec363e1..7fb56873b 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/AbpFeaturesValidationModule.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/AbpFeaturesValidationModule.cs @@ -10,6 +10,11 @@ namespace LINGYUN.Abp.Features.Validation public override void PreConfigureServices(ServiceConfigurationContext context) { context.Services.OnRegistred(FeaturesValidationInterceptorRegistrar.RegisterIfNeeded); + + Configure(options => + { + options.MapDefaultEffectPolicys(); + }); } } } diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/AbpFeaturesValidationOptions.cs b/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/AbpFeaturesValidationOptions.cs new file mode 100644 index 000000000..f26d98f0c --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/AbpFeaturesValidationOptions.cs @@ -0,0 +1,47 @@ +using JetBrains.Annotations; +using System; +using System.Collections.Generic; +using Volo.Abp; + +namespace LINGYUN.Abp.Features.Validation +{ + public class AbpFeaturesValidationOptions + { + public IDictionary> EffectPolicys { get; } + public AbpFeaturesValidationOptions() + { + EffectPolicys = new Dictionary>(); + } + /// + /// 变更功能限制策略时长计算方法 + /// + /// 限制策略 + /// 自定义的计算方法 + /// + /// 返回值一定要是秒钟刻度 + /// + public void MapEffectPolicy(LimitPolicy policy,[NotNull] Func func) + { + Check.NotNull(func, nameof(func)); + + if (EffectPolicys.ContainsKey(policy)) + { + EffectPolicys[policy] = func; + } + else + { + EffectPolicys.Add(policy, func); + } + } + + internal void MapDefaultEffectPolicys() + { + MapEffectPolicy(LimitPolicy.Minute, (time) => { return (long)(DateTimeOffset.UtcNow.AddMinutes(time) - DateTimeOffset.UtcNow).TotalSeconds; }); + MapEffectPolicy(LimitPolicy.Hours, (time) => { return (long)(DateTimeOffset.UtcNow.AddHours(time) - DateTimeOffset.UtcNow).TotalSeconds; }); + MapEffectPolicy(LimitPolicy.Days, (time) => { return (long)(DateTimeOffset.UtcNow.AddDays(time) - DateTimeOffset.UtcNow).TotalSeconds; }); + MapEffectPolicy(LimitPolicy.Weeks, (time) => { return (long)(DateTimeOffset.UtcNow.AddDays(time * 7) - DateTimeOffset.UtcNow).TotalSeconds; }); + MapEffectPolicy(LimitPolicy.Month, (time) => { return (long)(DateTimeOffset.UtcNow.AddMonths(time) - DateTimeOffset.UtcNow).TotalSeconds; }); + MapEffectPolicy(LimitPolicy.Years, (time) => { return (long)(DateTimeOffset.UtcNow.AddYears(time) - DateTimeOffset.UtcNow).TotalSeconds; }); + } + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/FeaturesValidationInterceptor.cs b/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/FeaturesValidationInterceptor.cs index 4035664fd..5093d4bff 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/FeaturesValidationInterceptor.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/FeaturesValidationInterceptor.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using Microsoft.Extensions.Options; +using System.Reflection; using System.Threading.Tasks; using Volo.Abp.Aspects; using Volo.Abp.DependencyInjection; @@ -11,14 +12,17 @@ namespace LINGYUN.Abp.Features.Validation public class FeaturesValidationInterceptor : AbpInterceptor, ITransientDependency { private readonly IFeatureChecker _featureChecker; + private readonly AbpFeaturesValidationOptions _options; private readonly IRequiresLimitFeatureChecker _limitFeatureChecker; private readonly IFeatureDefinitionManager _featureDefinitionManager; public FeaturesValidationInterceptor( IFeatureChecker featureChecker, IRequiresLimitFeatureChecker limitFeatureChecker, - IFeatureDefinitionManager featureDefinitionManager) + IFeatureDefinitionManager featureDefinitionManager, + IOptions options) { + _options = options.Value; _featureChecker = featureChecker; _limitFeatureChecker = limitFeatureChecker; _featureDefinitionManager = featureDefinitionManager; @@ -41,10 +45,12 @@ namespace LINGYUN.Abp.Features.Validation return; } + // 获取功能限制上限 + var limit = await _featureChecker.GetAsync(limitFeature.LimitFeature, limitFeature.DefaultLimit); // 获取功能限制时长 - var limit = await _featureChecker.GetAsync(limitFeature.Feature, limitFeature.DefaultLimit); - - var limitFeatureContext = new RequiresLimitFeatureContext(limitFeature.Feature, limitFeature.Policy, limit); + var interval = await _featureChecker.GetAsync(limitFeature.IntervalFeature, limitFeature.DefaultInterval); + // 必要的上下文参数 + var limitFeatureContext = new RequiresLimitFeatureContext(limitFeature.LimitFeature, _options, limitFeature.Policy, interval, limit); // 检查次数限制 await PreCheckFeatureAsync(limitFeatureContext); // 执行代理方法 @@ -69,14 +75,22 @@ namespace LINGYUN.Abp.Features.Validation var limitFeature = methodInfo.GetCustomAttribute(false); if (limitFeature != null) { - var featureDefinition = _featureDefinitionManager.GetOrNull(limitFeature.Feature); - if (featureDefinition != null && - typeof(NumericValueValidator).IsAssignableFrom(featureDefinition.ValueType.Validator.GetType())) + // 限制次数定义的不是范围参数,则不参与限制功能 + var featureLimitDefinition = _featureDefinitionManager.GetOrNull(limitFeature.LimitFeature); + if (featureLimitDefinition == null || + !typeof(NumericValueValidator).IsAssignableFrom(featureLimitDefinition.ValueType.Validator.GetType())) + { + return null; + } + // 时长刻度定义的不是范围参数,则不参与限制功能 + var featureIntervalDefinition = _featureDefinitionManager.GetOrNull(limitFeature.IntervalFeature); + if (featureIntervalDefinition == null || + !typeof(NumericValueValidator).IsAssignableFrom(featureIntervalDefinition.ValueType.Validator.GetType())) { - return limitFeature; + return null; } } - return null; + return limitFeature; } } } diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/LimitPolicy.cs b/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/LimitPolicy.cs index b09e07ba7..587e13675 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/LimitPolicy.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/LimitPolicy.cs @@ -1,26 +1,30 @@ namespace LINGYUN.Abp.Features.Validation { - public enum LimitPolicy + public enum LimitPolicy : byte { + /// + /// 按分钟限制 + /// + Minute = 0, /// /// 按小时限制 /// - Hours = 0, + Hours = 10, /// /// 按天限制 /// - Days = 1, + Days = 20, /// /// 按周限制 /// - Weeks = 2, + Weeks = 30, /// /// 按月限制 /// - Month = 3, + Month = 40, /// /// 按年限制 /// - Years = 4 + Years = 50 } } diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/RequiresLimitFeatureAttribute.cs b/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/RequiresLimitFeatureAttribute.cs index 1d19af4e5..23e39ec48 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/RequiresLimitFeatureAttribute.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/RequiresLimitFeatureAttribute.cs @@ -1,10 +1,15 @@ -using System; +using JetBrains.Annotations; +using System; +using Volo.Abp; namespace LINGYUN.Abp.Features.Validation { /// /// 单个功能的调用量限制 /// + /// + /// 需要对于限制时长和限制上限功能区分,以便于更细粒度的限制 + /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public class RequiresLimitFeatureAttribute : Attribute { @@ -17,18 +22,33 @@ namespace LINGYUN.Abp.Features.Validation /// public int DefaultLimit { get; } /// - /// 功能名称 + /// 限制上限名称 /// - public string Feature { get; } + public string LimitFeature { get; } + /// + /// 默认限制时长 + /// + public int DefaultInterval { get; } + /// + /// 限制时长名称 + /// + public string IntervalFeature { get; } public RequiresLimitFeatureAttribute( - string feature, + [NotNull] string limitFeature, + [NotNull] string intervalFeature, LimitPolicy policy = LimitPolicy.Month, - int defaultLimit = 1) + int defaultLimit = 1, + int defaultInterval = 1) { - DefaultLimit = defaultLimit; + Check.NotNullOrWhiteSpace(limitFeature, nameof(limitFeature)); + Check.NotNullOrWhiteSpace(intervalFeature, nameof(intervalFeature)); + Policy = policy; - Feature = feature; + LimitFeature = limitFeature; + DefaultLimit = defaultLimit; + IntervalFeature = intervalFeature; + DefaultInterval = defaultInterval; } } } diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/RequiresLimitFeatureContext.cs b/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/RequiresLimitFeatureContext.cs index 568b0f793..7e15f1e4f 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/RequiresLimitFeatureContext.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/RequiresLimitFeatureContext.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; - -namespace LINGYUN.Abp.Features.Validation +namespace LINGYUN.Abp.Features.Validation { public class RequiresLimitFeatureContext { @@ -10,47 +7,40 @@ namespace LINGYUN.Abp.Features.Validation /// public LimitPolicy Policy { get; } /// + /// 限制时长 + /// + public int Interval { get; } + /// /// 功能限制时长 /// public int Limit { get; } /// - /// 功能名称 + /// 功能限制次数名称 /// - public string Feature { get; } + public string LimitFeature { get; } - private Lazy>> effectPolicysLazy; - private IDictionary> effectPolicys => effectPolicysLazy.Value; + public AbpFeaturesValidationOptions Options { get; } public RequiresLimitFeatureContext( - string feature, + string limitFeature, + AbpFeaturesValidationOptions options, LimitPolicy policy = LimitPolicy.Month, + int interval = 1, int limit = 1) { Limit = limit; Policy = policy; - Feature = feature; - - effectPolicysLazy = new Lazy>>(() => CreateFeatureLimitPolicy()); + Interval = interval; + LimitFeature = limitFeature; + Options = options; } /// - /// 获取生效时间戳 + /// 获取生效时间跨度,单位:s /// /// public long GetEffectTicks() { - return effectPolicys[Policy](Limit); - } - - protected IDictionary> CreateFeatureLimitPolicy() - { - return new Dictionary>() - { - { LimitPolicy.Days, (time) => { return (long)(DateTimeOffset.UtcNow.AddDays(time) - DateTimeOffset.UtcNow).TotalSeconds; } }, - { LimitPolicy.Hours, (time) => { return (long)(DateTimeOffset.UtcNow.AddHours(time) - DateTimeOffset.UtcNow).TotalSeconds; } }, - { LimitPolicy.Month, (time) => { return (long)(DateTimeOffset.UtcNow.AddMonths(time) - DateTimeOffset.UtcNow).TotalSeconds; } }, - { LimitPolicy.Weeks, (time) => { return (long)(DateTimeOffset.UtcNow.AddDays(time * 7) - DateTimeOffset.UtcNow).TotalSeconds; } }, - { LimitPolicy.Years, (time) => { return (long)(DateTimeOffset.UtcNow.AddYears(time) - DateTimeOffset.UtcNow).TotalSeconds; } } - }; + return Options.EffectPolicys[Policy](Interval); } } } diff --git a/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Redis.Tests/LINGYUN/Abp/Features/Validation/Redis/RedisRequiresLimitFeatureCheckerTests.cs b/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Redis.Tests/LINGYUN/Abp/Features/Validation/Redis/RedisRequiresLimitFeatureCheckerTests.cs index 642ab3aa6..56d23ad92 100644 --- a/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Redis.Tests/LINGYUN/Abp/Features/Validation/Redis/RedisRequiresLimitFeatureCheckerTests.cs +++ b/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Redis.Tests/LINGYUN/Abp/Features/Validation/Redis/RedisRequiresLimitFeatureCheckerTests.cs @@ -1,5 +1,7 @@ -using Shouldly; +using Microsoft.Extensions.Options; +using Shouldly; using System; +using System.Threading; using System.Threading.Tasks; using Volo.Abp.Authorization; using Xunit; @@ -8,10 +10,12 @@ namespace LINGYUN.Abp.Features.Validation.Redis { public class RedisRequiresLimitFeatureCheckerTests : AbpFeaturesValidationRedisTestBase { + protected AbpFeaturesValidationOptions Options { get; } protected IRequiresLimitFeatureChecker RequiresLimitFeatureChecker { get; } protected TestValidationFeatureClass TestValidationFeatureClass { get; } public RedisRequiresLimitFeatureCheckerTests() { + Options = GetRequiredService>().Value; RequiresLimitFeatureChecker = GetRequiredService(); TestValidationFeatureClass = GetRequiredService(); } @@ -19,35 +23,33 @@ namespace LINGYUN.Abp.Features.Validation.Redis [Fact] public virtual async Task Check_Test_Async() { - var context = new RequiresLimitFeatureContext(TestFeatureNames.TestFeature1, LimitPolicy.Days, 10); + var context = new RequiresLimitFeatureContext(TestFeatureNames.TestLimitFeature, Options, LimitPolicy.Minute); await RequiresLimitFeatureChecker.CheckAsync(context); } [Fact] public virtual async Task Process_Test_Async() { - var context = new RequiresLimitFeatureContext(TestFeatureNames.TestFeature1, LimitPolicy.Days, 10); + var context = new RequiresLimitFeatureContext(TestFeatureNames.TestLimitFeature, Options, LimitPolicy.Minute); await RequiresLimitFeatureChecker.ProcessAsync(context); } [Fact] public virtual async Task Check_Limit_Test_Async() { - var context = new RequiresLimitFeatureContext(TestFeatureNames.TestFeature1, LimitPolicy.Days, 5); + var context = new RequiresLimitFeatureContext(TestFeatureNames.TestLimitFeature, Options, LimitPolicy.Minute, 1 ,5); await RequiresLimitFeatureChecker.ProcessAsync(context); await RequiresLimitFeatureChecker.ProcessAsync(context); await RequiresLimitFeatureChecker.ProcessAsync(context); await RequiresLimitFeatureChecker.ProcessAsync(context); await RequiresLimitFeatureChecker.ProcessAsync(context); - await RequiresLimitFeatureChecker.ProcessAsync(context); - try + await Assert.ThrowsAsync(async () => { await RequiresLimitFeatureChecker.CheckAsync(context); - } - catch(Exception ex) - { - ex.ShouldBeOfType(); - } + }); + Thread.Sleep(61000); + // it's ok + await RequiresLimitFeatureChecker.ProcessAsync(context); } } } diff --git a/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/AbpFeaturesValidationTestModule.cs b/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/AbpFeaturesValidationTestModule.cs index 2f8f68595..16ee3c2e4 100644 --- a/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/AbpFeaturesValidationTestModule.cs +++ b/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/AbpFeaturesValidationTestModule.cs @@ -13,10 +13,14 @@ namespace LINGYUN.Abp.Features.Validation { Configure(options => { - options.Map(TestFeatureNames.TestFeature1, (feature) => + options.Map(TestFeatureNames.TestLimitFeature, (feature) => { return 2.ToString(); }); + options.Map(TestFeatureNames.TestIntervalFeature, (feature) => + { + return 1.ToString(); + }); }); } } diff --git a/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/FakeRequiresFeatureLimitChecker.cs b/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/FakeRequiresFeatureLimitChecker.cs index 41929b8be..231ebe6ad 100644 --- a/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/FakeRequiresFeatureLimitChecker.cs +++ b/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/FakeRequiresFeatureLimitChecker.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -11,34 +12,58 @@ namespace LINGYUN.Abp.Features.Validation [ExposeServices(typeof(IRequiresLimitFeatureChecker))] public class FakeRequiresFeatureLimitChecker : IRequiresLimitFeatureChecker { - private readonly IDictionary limitFeatures; + private readonly IDictionary limitFeatures; public FakeRequiresFeatureLimitChecker() { - limitFeatures = new Dictionary(); + limitFeatures = new Dictionary(); } public virtual Task CheckAsync(RequiresLimitFeatureContext context, CancellationToken cancellation = default) { - if (!limitFeatures.ContainsKey(context.Feature)) + if (limitFeatures.ContainsKey(context.LimitFeature)) { - limitFeatures.Add(context.Feature, 0); - } - if (limitFeatures[context.Feature] > context.Limit) - { - throw new AbpAuthorizationException("已经超出功能次数限制,请联系管理员"); + if (limitFeatures[context.LimitFeature].ExprieTime <= DateTime.Now) + { + limitFeatures.Remove(context.LimitFeature); + return Task.CompletedTask; + } + if (limitFeatures[context.LimitFeature].Limit + 1 > context.Limit) + { + throw new AbpAuthorizationException("已经超出功能次数限制,请联系管理员"); + } } + return Task.CompletedTask; } public Task ProcessAsync(RequiresLimitFeatureContext context, CancellationToken cancellation = default) { - if (!limitFeatures.ContainsKey(context.Feature)) + if (!limitFeatures.ContainsKey(context.LimitFeature)) + { + limitFeatures.Add(context.LimitFeature, new LimitFeature(1, DateTime.Now.AddSeconds(context.GetEffectTicks()))); + } + else { - limitFeatures.Add(context.Feature, 1); + limitFeatures[context.LimitFeature].Invoke(1); } - limitFeatures[context.Feature] += 1; return Task.CompletedTask; } } + + public class LimitFeature + { + public int Limit { get; private set; } + public DateTime ExprieTime { get; } + public LimitFeature(int limit, DateTime exprieTime) + { + Limit = limit; + ExprieTime = exprieTime; + } + + public void Invoke(int count) + { + Limit += count; + } + } } diff --git a/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/FeaturesValidationTests.cs b/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/FeaturesValidationTests.cs index e330f5c93..24ab8ccec 100644 --- a/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/FeaturesValidationTests.cs +++ b/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/FeaturesValidationTests.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using Volo.Abp.Authorization; using Volo.Abp.MultiTenancy; @@ -23,14 +24,16 @@ namespace LINGYUN.Abp.Features.Validation using (CurrentTenant.Change(ParseNullableGuid(tenantId))) { // it's ok - await TestValidationFeatureClass.Test1HoursAsync(); - await TestValidationFeatureClass.Test1HoursAsync(); - await TestValidationFeatureClass.Test1HoursAsync(); + await TestValidationFeatureClass.Test1MinuteAsync(); + await TestValidationFeatureClass.Test1MinuteAsync(); await Assert.ThrowsAsync(async () => { - await TestValidationFeatureClass.Test1HoursAsync(); + await TestValidationFeatureClass.Test1MinuteAsync(); }); + + Thread.Sleep(61000); + await TestValidationFeatureClass.Test1MinuteAsync(); } } diff --git a/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/TestFeatureDefinitionProvider.cs b/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/TestFeatureDefinitionProvider.cs index 73bdf8c1a..726e40ac8 100644 --- a/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/TestFeatureDefinitionProvider.cs +++ b/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/TestFeatureDefinitionProvider.cs @@ -9,9 +9,13 @@ namespace LINGYUN.Abp.Features.Validation { var featureGroup = context.AddGroup(TestFeatureNames.GroupName); featureGroup.AddFeature( - name: TestFeatureNames.TestFeature1, + name: TestFeatureNames.TestLimitFeature, defaultValue: 100.ToString(), valueType: new ToggleStringValueType(new NumericValueValidator(1, 1000))); + featureGroup.AddFeature( + name: TestFeatureNames.TestIntervalFeature, + defaultValue: 1.ToString(), + valueType: new ToggleStringValueType(new NumericValueValidator(1, 1000))); } } } diff --git a/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/TestFeatureNames.cs b/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/TestFeatureNames.cs index 3224a0cb4..5916df1c6 100644 --- a/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/TestFeatureNames.cs +++ b/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/TestFeatureNames.cs @@ -4,6 +4,8 @@ { public const string GroupName = "Abp.Features.Validation.Tests"; - public const string TestFeature1 = GroupName + ".TestFeature1"; + public const string TestLimitFeature = GroupName + ".TestLimitFeature"; + + public const string TestIntervalFeature = GroupName + ".TestIntervalFeature"; } } diff --git a/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/TestValidationFeatureClass.cs b/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/TestValidationFeatureClass.cs index bb75c3442..6d7cb355e 100644 --- a/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/TestValidationFeatureClass.cs +++ b/aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/TestValidationFeatureClass.cs @@ -7,42 +7,10 @@ namespace LINGYUN.Abp.Features.Validation { public class TestValidationFeatureClass : ITransientDependency { - [RequiresLimitFeature(TestFeatureNames.TestFeature1, LimitPolicy.Days, 1)] - public virtual Task Test1DaysAsync() + [RequiresLimitFeature(TestFeatureNames.TestLimitFeature, TestFeatureNames.TestIntervalFeature, LimitPolicy.Minute)] + public virtual Task Test1MinuteAsync() { - Console.WriteLine("this limit 1 days feature"); - - return Task.CompletedTask; - } - - [RequiresLimitFeature(TestFeatureNames.TestFeature1, LimitPolicy.Month, 1)] - public virtual Task Test1MonthsAsync() - { - Console.WriteLine("this limit 1 month feature"); - - return Task.CompletedTask; - } - - [RequiresLimitFeature(TestFeatureNames.TestFeature1, LimitPolicy.Weeks, 1)] - public virtual Task Test1WeeksAsync() - { - Console.WriteLine("this limit 1 weeks feature"); - - return Task.CompletedTask; - } - - [RequiresLimitFeature(TestFeatureNames.TestFeature1, LimitPolicy.Hours, 1)] - public virtual Task Test1HoursAsync() - { - Console.WriteLine("this limit 1 hours feature"); - - return Task.CompletedTask; - } - - [RequiresLimitFeature(TestFeatureNames.TestFeature1, LimitPolicy.Years, 1)] - public virtual Task Test1YearsAsync() - { - Console.WriteLine("this limit 1 years feature"); + Console.WriteLine("this limit 1 minute feature"); return Task.CompletedTask; }