Browse Source

Merge pull request #89 from colinin/3.1

More granular limiting functionality
pull/115/head
cKey 5 years ago
committed by GitHub
parent
commit
905d9e4037
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN.Abp.Features.Validation.Redis.csproj
  2. 10
      aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/AbpRedisRequiresLimitFeatureOptions.cs
  3. 2
      aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/Lua/check.lua
  4. 2
      aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/Lua/process.lua
  5. 4
      aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN/Abp/Features/Validation/Redis/RedisRequiresLimitFeatureChecker.cs
  6. 5
      aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/AbpFeaturesValidationModule.cs
  7. 47
      aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/AbpFeaturesValidationOptions.cs
  8. 34
      aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/FeaturesValidationInterceptor.cs
  9. 16
      aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/LimitPolicy.cs
  10. 34
      aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/RequiresLimitFeatureAttribute.cs
  11. 42
      aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/RequiresLimitFeatureContext.cs
  12. 24
      aspnet-core/tests/LINGYUN.Abp.Features.Validation.Redis.Tests/LINGYUN/Abp/Features/Validation/Redis/RedisRequiresLimitFeatureCheckerTests.cs
  13. 6
      aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/AbpFeaturesValidationTestModule.cs
  14. 47
      aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/FakeRequiresFeatureLimitChecker.cs
  15. 11
      aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/FeaturesValidationTests.cs
  16. 6
      aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/TestFeatureDefinitionProvider.cs
  17. 4
      aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/TestFeatureNames.cs
  18. 38
      aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/TestValidationFeatureClass.cs

1
aspnet-core/modules/common/LINGYUN.Abp.Features.Validation.Redis/LINGYUN.Abp.Features.Validation.Redis.csproj

@ -8,7 +8,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.7" /> <PackageReference Include="Microsoft.Extensions.Options" Version="3.1.7" />
<PackageReference Include="Polly" Version="7.2.1" />
<PackageReference Include="StackExchange.Redis" Version="2.0.593" /> <PackageReference Include="StackExchange.Redis" Version="2.0.593" />
<PackageReference Include="Volo.Abp.Core" Version="3.1.0" /> <PackageReference Include="Volo.Abp.Core" Version="3.1.0" />
</ItemGroup> </ItemGroup>

10
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 Configuration { get; set; }
public string InstanceName { get; set; } public string InstanceName { get; set; }
public ConfigurationOptions ConfigurationOptions { get; set; } public ConfigurationOptions ConfigurationOptions { get; set; }
/// <summary>
/// 失败重试次数
/// default: 3
/// </summary>
public int FailedRetryCount { get; set; } = 3;
/// <summary>
/// 失败重试间隔 ms
/// default: 1000
/// </summary>
public int FailedRetryInterval { get; set; } = 1000;
AbpRedisRequiresLimitFeatureOptions IOptions<AbpRedisRequiresLimitFeatureOptions>.Value AbpRedisRequiresLimitFeatureOptions IOptions<AbpRedisRequiresLimitFeatureOptions>.Value
{ {
get { return this; } get { return this; }

2
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 if (redis.call('EXISTS', KEYS[1]) == 0) then
redis.call('SETEX',KEYS[1],ARGV[1], 0) return 0
end end
return tonumber(redis.call('GET', KEYS[1])) return tonumber(redis.call('GET', KEYS[1]))

2
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 if (redis.call('EXISTS',KEYS[1]) ~= 0) then
redis.call('INCRBY',KEYS[1], 1) redis.call('INCRBY',KEYS[1], 1)
else else
redis.call('SETEX',KEYS[1],ARGV[1],0) redis.call('SETEX',KEYS[1],ARGV[1],1)
end end
return tonumber(redis.call('GET',KEYS[1])) return tonumber(redis.call('GET',KEYS[1]))

4
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) 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) private void RegistenConnectionEvent(ConnectionMultiplexer connection)

5
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) public override void PreConfigureServices(ServiceConfigurationContext context)
{ {
context.Services.OnRegistred(FeaturesValidationInterceptorRegistrar.RegisterIfNeeded); context.Services.OnRegistred(FeaturesValidationInterceptorRegistrar.RegisterIfNeeded);
Configure<AbpFeaturesValidationOptions>(options =>
{
options.MapDefaultEffectPolicys();
});
} }
} }
} }

47
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<LimitPolicy, Func<int, long>> EffectPolicys { get; }
public AbpFeaturesValidationOptions()
{
EffectPolicys = new Dictionary<LimitPolicy, Func<int, long>>();
}
/// <summary>
/// 变更功能限制策略时长计算方法
/// </summary>
/// <param name="policy">限制策略</param>
/// <param name="func">自定义的计算方法</param>
/// <remarks>
/// 返回值一定要是秒钟刻度
/// </remarks>
public void MapEffectPolicy(LimitPolicy policy,[NotNull] Func<int, long> 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; });
}
}
}

34
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 System.Threading.Tasks;
using Volo.Abp.Aspects; using Volo.Abp.Aspects;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -11,14 +12,17 @@ namespace LINGYUN.Abp.Features.Validation
public class FeaturesValidationInterceptor : AbpInterceptor, ITransientDependency public class FeaturesValidationInterceptor : AbpInterceptor, ITransientDependency
{ {
private readonly IFeatureChecker _featureChecker; private readonly IFeatureChecker _featureChecker;
private readonly AbpFeaturesValidationOptions _options;
private readonly IRequiresLimitFeatureChecker _limitFeatureChecker; private readonly IRequiresLimitFeatureChecker _limitFeatureChecker;
private readonly IFeatureDefinitionManager _featureDefinitionManager; private readonly IFeatureDefinitionManager _featureDefinitionManager;
public FeaturesValidationInterceptor( public FeaturesValidationInterceptor(
IFeatureChecker featureChecker, IFeatureChecker featureChecker,
IRequiresLimitFeatureChecker limitFeatureChecker, IRequiresLimitFeatureChecker limitFeatureChecker,
IFeatureDefinitionManager featureDefinitionManager) IFeatureDefinitionManager featureDefinitionManager,
IOptions<AbpFeaturesValidationOptions> options)
{ {
_options = options.Value;
_featureChecker = featureChecker; _featureChecker = featureChecker;
_limitFeatureChecker = limitFeatureChecker; _limitFeatureChecker = limitFeatureChecker;
_featureDefinitionManager = featureDefinitionManager; _featureDefinitionManager = featureDefinitionManager;
@ -41,10 +45,12 @@ namespace LINGYUN.Abp.Features.Validation
return; return;
} }
// 获取功能限制上限
var limit = await _featureChecker.GetAsync(limitFeature.LimitFeature, limitFeature.DefaultLimit);
// 获取功能限制时长 // 获取功能限制时长
var limit = await _featureChecker.GetAsync(limitFeature.Feature, limitFeature.DefaultLimit); var interval = await _featureChecker.GetAsync(limitFeature.IntervalFeature, limitFeature.DefaultInterval);
// 必要的上下文参数
var limitFeatureContext = new RequiresLimitFeatureContext(limitFeature.Feature, limitFeature.Policy, limit); var limitFeatureContext = new RequiresLimitFeatureContext(limitFeature.LimitFeature, _options, limitFeature.Policy, interval, limit);
// 检查次数限制 // 检查次数限制
await PreCheckFeatureAsync(limitFeatureContext); await PreCheckFeatureAsync(limitFeatureContext);
// 执行代理方法 // 执行代理方法
@ -69,14 +75,22 @@ namespace LINGYUN.Abp.Features.Validation
var limitFeature = methodInfo.GetCustomAttribute<RequiresLimitFeatureAttribute>(false); var limitFeature = methodInfo.GetCustomAttribute<RequiresLimitFeatureAttribute>(false);
if (limitFeature != null) if (limitFeature != null)
{ {
var featureDefinition = _featureDefinitionManager.GetOrNull(limitFeature.Feature); // 限制次数定义的不是范围参数,则不参与限制功能
if (featureDefinition != null && var featureLimitDefinition = _featureDefinitionManager.GetOrNull(limitFeature.LimitFeature);
typeof(NumericValueValidator).IsAssignableFrom(featureDefinition.ValueType.Validator.GetType())) 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;
} }
} }
} }

16
aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/LimitPolicy.cs

@ -1,26 +1,30 @@
namespace LINGYUN.Abp.Features.Validation namespace LINGYUN.Abp.Features.Validation
{ {
public enum LimitPolicy public enum LimitPolicy : byte
{ {
/// <summary>
/// 按分钟限制
/// </summary>
Minute = 0,
/// <summary> /// <summary>
/// 按小时限制 /// 按小时限制
/// </summary> /// </summary>
Hours = 0, Hours = 10,
/// <summary> /// <summary>
/// 按天限制 /// 按天限制
/// </summary> /// </summary>
Days = 1, Days = 20,
/// <summary> /// <summary>
/// 按周限制 /// 按周限制
/// </summary> /// </summary>
Weeks = 2, Weeks = 30,
/// <summary> /// <summary>
/// 按月限制 /// 按月限制
/// </summary> /// </summary>
Month = 3, Month = 40,
/// <summary> /// <summary>
/// 按年限制 /// 按年限制
/// </summary> /// </summary>
Years = 4 Years = 50
} }
} }

34
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 namespace LINGYUN.Abp.Features.Validation
{ {
/// <summary> /// <summary>
/// 单个功能的调用量限制 /// 单个功能的调用量限制
/// </summary> /// </summary>
/// <remarks>
/// 需要对于限制时长和限制上限功能区分,以便于更细粒度的限制
/// </remarks>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class RequiresLimitFeatureAttribute : Attribute public class RequiresLimitFeatureAttribute : Attribute
{ {
@ -17,18 +22,33 @@ namespace LINGYUN.Abp.Features.Validation
/// </summary> /// </summary>
public int DefaultLimit { get; } public int DefaultLimit { get; }
/// <summary> /// <summary>
/// 功能名称 /// 限制上限名称
/// </summary> /// </summary>
public string Feature { get; } public string LimitFeature { get; }
/// <summary>
/// 默认限制时长
/// </summary>
public int DefaultInterval { get; }
/// <summary>
/// 限制时长名称
/// </summary>
public string IntervalFeature { get; }
public RequiresLimitFeatureAttribute( public RequiresLimitFeatureAttribute(
string feature, [NotNull] string limitFeature,
[NotNull] string intervalFeature,
LimitPolicy policy = LimitPolicy.Month, 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; Policy = policy;
Feature = feature; LimitFeature = limitFeature;
DefaultLimit = defaultLimit;
IntervalFeature = intervalFeature;
DefaultInterval = defaultInterval;
} }
} }
} }

42
aspnet-core/modules/common/LINGYUN.Abp.Features/LINGYUN/Abp/Features/Validation/RequiresLimitFeatureContext.cs

@ -1,7 +1,4 @@
using System; namespace LINGYUN.Abp.Features.Validation
using System.Collections.Generic;
namespace LINGYUN.Abp.Features.Validation
{ {
public class RequiresLimitFeatureContext public class RequiresLimitFeatureContext
{ {
@ -10,47 +7,40 @@ namespace LINGYUN.Abp.Features.Validation
/// </summary> /// </summary>
public LimitPolicy Policy { get; } public LimitPolicy Policy { get; }
/// <summary> /// <summary>
/// 限制时长
/// </summary>
public int Interval { get; }
/// <summary>
/// 功能限制时长 /// 功能限制时长
/// </summary> /// </summary>
public int Limit { get; } public int Limit { get; }
/// <summary> /// <summary>
/// 功能名称 /// 功能限制次数名称
/// </summary> /// </summary>
public string Feature { get; } public string LimitFeature { get; }
private Lazy<IDictionary<LimitPolicy, Func<int, long>>> effectPolicysLazy; public AbpFeaturesValidationOptions Options { get; }
private IDictionary<LimitPolicy, Func<int, long>> effectPolicys => effectPolicysLazy.Value;
public RequiresLimitFeatureContext( public RequiresLimitFeatureContext(
string feature, string limitFeature,
AbpFeaturesValidationOptions options,
LimitPolicy policy = LimitPolicy.Month, LimitPolicy policy = LimitPolicy.Month,
int interval = 1,
int limit = 1) int limit = 1)
{ {
Limit = limit; Limit = limit;
Policy = policy; Policy = policy;
Feature = feature; Interval = interval;
LimitFeature = limitFeature;
effectPolicysLazy = new Lazy<IDictionary<LimitPolicy, Func<int, long>>>(() => CreateFeatureLimitPolicy()); Options = options;
} }
/// <summary> /// <summary>
/// 获取生效时间 /// 获取生效时间跨度,单位:s
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public long GetEffectTicks() public long GetEffectTicks()
{ {
return effectPolicys[Policy](Limit); return Options.EffectPolicys[Policy](Interval);
}
protected IDictionary<LimitPolicy, Func<int, long>> CreateFeatureLimitPolicy()
{
return new Dictionary<LimitPolicy, Func<int, long>>()
{
{ 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; } }
};
} }
} }
} }

24
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;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Authorization; using Volo.Abp.Authorization;
using Xunit; using Xunit;
@ -8,10 +10,12 @@ namespace LINGYUN.Abp.Features.Validation.Redis
{ {
public class RedisRequiresLimitFeatureCheckerTests : AbpFeaturesValidationRedisTestBase public class RedisRequiresLimitFeatureCheckerTests : AbpFeaturesValidationRedisTestBase
{ {
protected AbpFeaturesValidationOptions Options { get; }
protected IRequiresLimitFeatureChecker RequiresLimitFeatureChecker { get; } protected IRequiresLimitFeatureChecker RequiresLimitFeatureChecker { get; }
protected TestValidationFeatureClass TestValidationFeatureClass { get; } protected TestValidationFeatureClass TestValidationFeatureClass { get; }
public RedisRequiresLimitFeatureCheckerTests() public RedisRequiresLimitFeatureCheckerTests()
{ {
Options = GetRequiredService<IOptions<AbpFeaturesValidationOptions>>().Value;
RequiresLimitFeatureChecker = GetRequiredService<IRequiresLimitFeatureChecker>(); RequiresLimitFeatureChecker = GetRequiredService<IRequiresLimitFeatureChecker>();
TestValidationFeatureClass = GetRequiredService<TestValidationFeatureClass>(); TestValidationFeatureClass = GetRequiredService<TestValidationFeatureClass>();
} }
@ -19,35 +23,33 @@ namespace LINGYUN.Abp.Features.Validation.Redis
[Fact] [Fact]
public virtual async Task Check_Test_Async() 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); await RequiresLimitFeatureChecker.CheckAsync(context);
} }
[Fact] [Fact]
public virtual async Task Process_Test_Async() 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); await RequiresLimitFeatureChecker.ProcessAsync(context);
} }
[Fact] [Fact]
public virtual async Task Check_Limit_Test_Async() 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);
await RequiresLimitFeatureChecker.ProcessAsync(context); await RequiresLimitFeatureChecker.ProcessAsync(context);
await RequiresLimitFeatureChecker.ProcessAsync(context); await RequiresLimitFeatureChecker.ProcessAsync(context);
await RequiresLimitFeatureChecker.ProcessAsync(context); await Assert.ThrowsAsync<AbpAuthorizationException>(async () =>
try
{ {
await RequiresLimitFeatureChecker.CheckAsync(context); await RequiresLimitFeatureChecker.CheckAsync(context);
} });
catch(Exception ex) Thread.Sleep(61000);
{ // it's ok
ex.ShouldBeOfType<AbpAuthorizationException>(); await RequiresLimitFeatureChecker.ProcessAsync(context);
}
} }
} }
} }

6
aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/AbpFeaturesValidationTestModule.cs

@ -13,10 +13,14 @@ namespace LINGYUN.Abp.Features.Validation
{ {
Configure<FakeFeatureOptions>(options => Configure<FakeFeatureOptions>(options =>
{ {
options.Map(TestFeatureNames.TestFeature1, (feature) => options.Map(TestFeatureNames.TestLimitFeature, (feature) =>
{ {
return 2.ToString(); return 2.ToString();
}); });
options.Map(TestFeatureNames.TestIntervalFeature, (feature) =>
{
return 1.ToString();
});
}); });
} }
} }

47
aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/FakeRequiresFeatureLimitChecker.cs

@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -11,34 +12,58 @@ namespace LINGYUN.Abp.Features.Validation
[ExposeServices(typeof(IRequiresLimitFeatureChecker))] [ExposeServices(typeof(IRequiresLimitFeatureChecker))]
public class FakeRequiresFeatureLimitChecker : IRequiresLimitFeatureChecker public class FakeRequiresFeatureLimitChecker : IRequiresLimitFeatureChecker
{ {
private readonly IDictionary<string, int> limitFeatures; private readonly IDictionary<string, LimitFeature> limitFeatures;
public FakeRequiresFeatureLimitChecker() public FakeRequiresFeatureLimitChecker()
{ {
limitFeatures = new Dictionary<string, int>(); limitFeatures = new Dictionary<string, LimitFeature>();
} }
public virtual Task CheckAsync(RequiresLimitFeatureContext context, CancellationToken cancellation = default) 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.LimitFeature].ExprieTime <= DateTime.Now)
} {
if (limitFeatures[context.Feature] > context.Limit) limitFeatures.Remove(context.LimitFeature);
{ return Task.CompletedTask;
throw new AbpAuthorizationException("已经超出功能次数限制,请联系管理员"); }
if (limitFeatures[context.LimitFeature].Limit + 1 > context.Limit)
{
throw new AbpAuthorizationException("已经超出功能次数限制,请联系管理员");
}
} }
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task ProcessAsync(RequiresLimitFeatureContext context, CancellationToken cancellation = default) 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; 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;
}
}
} }

11
aspnet-core/tests/LINGYUN.Abp.Features.Validation.Tests/LINGYUN/Abp/Features/Validation/FeaturesValidationTests.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Authorization; using Volo.Abp.Authorization;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
@ -23,14 +24,16 @@ namespace LINGYUN.Abp.Features.Validation
using (CurrentTenant.Change(ParseNullableGuid(tenantId))) using (CurrentTenant.Change(ParseNullableGuid(tenantId)))
{ {
// it's ok // it's ok
await TestValidationFeatureClass.Test1HoursAsync(); await TestValidationFeatureClass.Test1MinuteAsync();
await TestValidationFeatureClass.Test1HoursAsync(); await TestValidationFeatureClass.Test1MinuteAsync();
await TestValidationFeatureClass.Test1HoursAsync();
await Assert.ThrowsAsync<AbpAuthorizationException>(async () => await Assert.ThrowsAsync<AbpAuthorizationException>(async () =>
{ {
await TestValidationFeatureClass.Test1HoursAsync(); await TestValidationFeatureClass.Test1MinuteAsync();
}); });
Thread.Sleep(61000);
await TestValidationFeatureClass.Test1MinuteAsync();
} }
} }

6
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); var featureGroup = context.AddGroup(TestFeatureNames.GroupName);
featureGroup.AddFeature( featureGroup.AddFeature(
name: TestFeatureNames.TestFeature1, name: TestFeatureNames.TestLimitFeature,
defaultValue: 100.ToString(), defaultValue: 100.ToString(),
valueType: new ToggleStringValueType(new NumericValueValidator(1, 1000))); valueType: new ToggleStringValueType(new NumericValueValidator(1, 1000)));
featureGroup.AddFeature(
name: TestFeatureNames.TestIntervalFeature,
defaultValue: 1.ToString(),
valueType: new ToggleStringValueType(new NumericValueValidator(1, 1000)));
} }
} }
} }

4
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 GroupName = "Abp.Features.Validation.Tests";
public const string TestFeature1 = GroupName + ".TestFeature1"; public const string TestLimitFeature = GroupName + ".TestLimitFeature";
public const string TestIntervalFeature = GroupName + ".TestIntervalFeature";
} }
} }

38
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 public class TestValidationFeatureClass : ITransientDependency
{ {
[RequiresLimitFeature(TestFeatureNames.TestFeature1, LimitPolicy.Days, 1)] [RequiresLimitFeature(TestFeatureNames.TestLimitFeature, TestFeatureNames.TestIntervalFeature, LimitPolicy.Minute)]
public virtual Task Test1DaysAsync() public virtual Task Test1MinuteAsync()
{ {
Console.WriteLine("this limit 1 days feature"); Console.WriteLine("this limit 1 minute 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");
return Task.CompletedTask; return Task.CompletedTask;
} }

Loading…
Cancel
Save