fengling-risk-control/Fengling.RiskControl.Client/Events/CapEventPublisher.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

121 lines
4.1 KiB
C#

using DotNetCore.CAP;
using Fengling.RiskControl.Configuration;
using Fengling.RiskControl.Evaluation;
using Microsoft.Extensions.Logging;
namespace Fengling.RiskControl.Events;
public interface IRiskEventPublisher
{
Task PublishRiskAssessmentAsync(RiskEvaluationRequest request, RiskEvaluationResult result);
Task PublishRiskAlertAsync(RiskEvaluationRequest request, RiskEvaluationResult result);
}
public class CapEventPublisher : IRiskEventPublisher
{
private readonly ICapPublisher _capPublisher;
private readonly RiskControlClientOptions _options;
private readonly ILogger<CapEventPublisher> _logger;
public CapEventPublisher(
ICapPublisher capPublisher,
RiskControlClientOptions options,
ILogger<CapEventPublisher> logger)
{
_capPublisher = capPublisher;
_options = options;
_logger = logger;
}
public async Task PublishRiskAssessmentAsync(RiskEvaluationRequest request, RiskEvaluationResult result)
{
if (!_options.Cap.PublisherEnabled)
{
_logger.LogDebug("CAP publisher is disabled, skipping event publish");
return;
}
try
{
var @event = new RiskAssessmentEvent
{
MemberId = request.MemberId,
EventType = request.EventType,
Amount = request.Amount,
DeviceFingerprint = request.DeviceFingerprint,
IpAddress = request.IpAddress,
TotalScore = result.TotalScore,
RiskLevel = result.RiskLevel.ToString(),
Blocked = result.Blocked,
Factors = result.Factors,
EvaluatedAt = result.EvaluatedAt
};
await _capPublisher.PublishAsync("fengling.risk.assessed", @event);
_logger.LogDebug("Published RiskAssessmentEvent for member {MemberId}", request.MemberId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to publish RiskAssessmentEvent for member {MemberId}", request.MemberId);
}
}
public async Task PublishRiskAlertAsync(RiskEvaluationRequest request, RiskEvaluationResult result)
{
if (!_options.Cap.PublisherEnabled)
return;
try
{
var @event = new RiskAlertEvent
{
MemberId = request.MemberId,
EventType = request.EventType,
RiskLevel = result.RiskLevel.ToString(),
TotalScore = result.TotalScore,
Blocked = result.Blocked,
AlertTime = DateTime.UtcNow,
Priority = result.RiskLevel switch
{
Evaluation.RiskLevel.High => "P0",
Evaluation.RiskLevel.Medium => "P1",
_ => "P2"
}
};
await _capPublisher.PublishAsync("fengling.risk.alert", @event);
_logger.LogWarning("Published RiskAlertEvent for member {MemberId}, level={Level}",
request.MemberId, result.RiskLevel);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to publish RiskAlertEvent for member {MemberId}", request.MemberId);
}
}
}
public class RiskAssessmentEvent
{
public string MemberId { get; set; } = string.Empty;
public string EventType { get; set; } = string.Empty;
public int? Amount { get; set; }
public string? DeviceFingerprint { get; set; }
public string? IpAddress { get; set; }
public int TotalScore { get; set; }
public string RiskLevel { get; set; } = string.Empty;
public bool Blocked { get; set; }
public List<RiskFactorResult> Factors { get; set; } = new();
public DateTime EvaluatedAt { get; set; }
}
public class RiskAlertEvent
{
public string MemberId { get; set; } = string.Empty;
public string EventType { get; set; } = string.Empty;
public string RiskLevel { get; set; } = string.Empty;
public int TotalScore { get; set; }
public bool Blocked { get; set; }
public DateTime AlertTime { get; set; }
public string Priority { get; set; } = string.Empty;
}