using Fengling.RiskControl.Domain.Aggregates.RiskRules; using Fengling.RiskControl.Configuration; using Fengling.RiskControl.Counter; using Fengling.RiskControl.Rules; using Microsoft.Extensions.Logging; namespace Fengling.RiskControl.Evaluation; public interface IRiskEvaluator { Task EvaluateAsync(RiskEvaluationRequest request); Task IsAllowedAsync(RiskEvaluationRequest request); } public class RiskEvaluator : IRiskEvaluator { private readonly IRuleLoader _ruleLoader; private readonly IRiskCounterService _counterService; private readonly RiskControlClientOptions _options; private readonly ILogger _logger; public RiskEvaluator( IRuleLoader ruleLoader, IRiskCounterService counterService, RiskControlClientOptions options, ILogger logger) { _ruleLoader = ruleLoader; _counterService = counterService; _options = options; _logger = logger; } public async Task EvaluateAsync(RiskEvaluationRequest request) { var rules = await _ruleLoader.GetActiveRulesAsync(); var factors = new List(); var totalScore = 0; foreach (var rule in rules) { var factor = await EvaluateRuleAsync(rule, request); if (factor != null) { factors.Add(factor); totalScore += factor.Points; } } var riskLevel = DetermineRiskLevel(totalScore); var recommendedAction = DetermineAction(riskLevel); var blocked = recommendedAction == RiskRuleAction.Block; return new RiskEvaluationResult { TotalScore = totalScore, RiskLevel = riskLevel, RecommendedAction = recommendedAction, Blocked = blocked, Message = blocked ? "操作被风险控制系统拒绝" : "操作已通过风险评估", Factors = factors, EvaluatedAt = DateTime.UtcNow }; } public Task IsAllowedAsync(RiskEvaluationRequest request) { return EvaluateAsync(request).ContinueWith(t => !t.Result.Blocked); } private async Task EvaluateRuleAsync(RiskRule rule, RiskEvaluationRequest request) { return rule.RuleType switch { RiskRuleType.FrequencyLimit => await EvaluateFrequencyLimitAsync(rule, request), RiskRuleType.AmountLimit => EvaluateAmountLimit(rule, request), RiskRuleType.Blacklist => EvaluateBlacklist(rule, request), RiskRuleType.DeviceFingerprint => EvaluateDeviceFingerprint(rule, request), RiskRuleType.VelocityCheck => EvaluateVelocityCheck(rule, request), _ => null }; } private async Task EvaluateFrequencyLimitAsync(RiskRule rule, RiskEvaluationRequest request) { var config = rule.GetConfig(); if (config == null) { _logger.LogWarning("Rule {RuleId} has invalid FrequencyLimitConfig", rule.Id); return null; } var metricKey = $"{request.EventType.ToLower()}_count"; var currentCount = await _counterService.GetValueAsync(request.MemberId, metricKey); var limit = config.MaxCount; if (currentCount >= limit) { return new RiskFactorResult { RuleName = rule.Name, RuleType = rule.RuleType.ToString(), Points = rule.Priority, Detail = $"已超过{request.EventType}次数限制({currentCount}/{limit})" }; } await _counterService.IncrementAsync(request.MemberId, metricKey); return null; } private RiskFactorResult? EvaluateAmountLimit(RiskRule rule, RiskEvaluationRequest request) { if (!request.Amount.HasValue) return null; var config = rule.GetConfig(); if (config == null) return null; var metricKey = $"{request.EventType.ToLower()}_amount"; var currentAmount = _counterService.GetValueAsync(request.MemberId, metricKey).GetAwaiter().GetResult(); if (currentAmount + request.Amount.Value > config.MaxAmount) { return new RiskFactorResult { RuleName = rule.Name, RuleType = rule.RuleType.ToString(), Points = rule.Priority, Detail = $"已超过{request.EventType}金额限制({currentAmount + request.Amount.Value}/{config.MaxAmount})" }; } _counterService.IncrementAsync(request.MemberId, metricKey, request.Amount.Value).GetAwaiter().GetResult(); return null; } private RiskFactorResult? EvaluateBlacklist(RiskRule rule, RiskEvaluationRequest request) { var config = rule.GetConfig(); if (config == null) return null; var memberValues = _counterService.GetAllValuesAsync(request.MemberId).GetAwaiter().GetResult(); if (memberValues.TryGetValue("is_blacklisted", out var isBlacklisted) && isBlacklisted == 1) { return new RiskFactorResult { RuleName = rule.Name, RuleType = rule.RuleType.ToString(), Points = rule.Priority, Detail = "用户已被加入黑名单" }; } return null; } private RiskFactorResult? EvaluateDeviceFingerprint(RiskRule rule, RiskEvaluationRequest request) { if (string.IsNullOrEmpty(request.DeviceFingerprint)) return null; var config = rule.GetConfig(); if (config == null) return null; return null; } private RiskFactorResult? EvaluateVelocityCheck(RiskRule rule, RiskEvaluationRequest request) { var config = rule.GetConfig(); if (config == null) return null; return null; } private RiskLevel DetermineRiskLevel(int totalScore) { if (totalScore >= _options.Evaluation.HighRiskThreshold) return RiskLevel.High; if (totalScore >= _options.Evaluation.MediumRiskThreshold) return RiskLevel.Medium; return RiskLevel.Low; } private RiskRuleAction DetermineAction(RiskLevel riskLevel) { return riskLevel switch { RiskLevel.High => RiskRuleAction.Block, RiskLevel.Medium => RiskRuleAction.RequireVerification, _ => RiskRuleAction.Allow }; } } public class FrequencyLimitConfig { public int MaxCount { get; set; } = 10; public string TimeWindow { get; set; } = "Day"; } public class AmountLimitConfig { public int MaxAmount { get; set; } = 1000; } public class BlacklistConfig { public List BlockedMembers { get; set; } = new(); } public class DeviceFingerprintConfig { public int MaxAccountsPerDevice { get; set; } = 3; } public class VelocityCheckConfig { public int MaxRequestsPerMinute { get; set; } = 100; }