fengling-risk-control/Fengling.RiskControl.Client/RiskControlClientServiceExtensions.cs
Sam 293209b1dc feat: add Fengling.RiskControl.Client SDK
- Implement RedisCounterService for rate limiting
- Implement RuleLoader with timer refresh
- Implement RiskEvaluator for local rule evaluation
- Implement SamplingService for CAP events
- Implement CapEventPublisher for async event publishing
- Implement FailoverStrategy for Redis failure handling
- Add configuration classes and DI extensions
- Add unit tests (9 tests)
- Add NuGet publishing script
2026-02-06 00:16:53 +08:00

99 lines
3.4 KiB
C#

using Fengling.RiskControl;
using Fengling.RiskControl.Configuration;
using Fengling.RiskControl.Counter;
using Fengling.RiskControl.Evaluation;
using Fengling.RiskControl.Events;
using Fengling.RiskControl.Failover;
using Fengling.RiskControl.Rules;
using Fengling.RiskControl.Sampling;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using DotNetCore.CAP;
using StackExchange.Redis;
namespace Microsoft.Extensions.DependencyInjection;
public static class RiskControlClientServiceExtensions
{
public static IServiceCollection AddRiskControlClientCore(
this IServiceCollection services,
Action<RiskControlClientOptions>? configureOptions = null)
{
if (configureOptions != null)
{
services.Configure(configureOptions);
}
services.TryAddSingleton<RiskControlClientOptions>(sp =>
{
var options = sp.GetRequiredService<IOptions<RiskControlClientOptions>>().Value;
return options;
});
services.TryAddSingleton<IConnectionMultiplexer>(sp =>
{
var options = sp.GetRequiredService<RiskControlClientOptions>();
var connectionString = options.Redis.ConnectionString;
var connection = ConnectionMultiplexer.Connect(connectionString);
return connection;
});
services.TryAddSingleton<IRuleLoader, RedisRuleLoader>();
services.TryAddSingleton<IRiskCounterService, RedisCounterService>();
services.TryAddSingleton<IRiskEvaluator, RiskEvaluator>();
services.TryAddSingleton<ISamplingService, SamplingService>();
services.TryAddSingleton<IFailoverStrategy, FailoverStrategy>();
if (services.Any(s => s.ServiceType == typeof(ICapPublisher)))
{
services.TryAddSingleton<IRiskEventPublisher, CapEventPublisher>();
}
else
{
services.TryAddSingleton<IRiskEventPublisher, NoOpEventPublisher>();
}
services.TryAddSingleton<IRiskControlClient, RiskControlClient>();
services.TryAddSingleton<RiskControlClientHostedService>();
services.TryAddSingleton<IHostedService>(sp => sp.GetRequiredService<RiskControlClientHostedService>());
return services;
}
public static IServiceCollection AddRiskControlClient(
this IServiceCollection services,
Action<RiskControlClientOptions> configureOptions)
{
services.Configure(configureOptions);
services.AddRiskControlClientCore();
return services;
}
}
internal class NoOpEventPublisher : IRiskEventPublisher
{
private readonly ILogger<NoOpEventPublisher> _logger;
public NoOpEventPublisher(ILogger<NoOpEventPublisher> logger)
{
_logger = logger;
}
public Task PublishRiskAssessmentAsync(RiskEvaluationRequest request, RiskEvaluationResult result)
{
_logger.LogDebug("CAP publisher not configured, skipping RiskAssessmentEvent");
return Task.CompletedTask;
}
public Task PublishRiskAlertAsync(RiskEvaluationRequest request, RiskEvaluationResult result)
{
_logger.LogDebug("CAP publisher not configured, skipping RiskAlertEvent");
return Task.CompletedTask;
}
}