using System.Reflection; using System.Text.Json; namespace Admin.NET.Plugin.AiDOP.Rule; /// /// 规则配置纯逻辑解析器。只负责层级合并和类型转换,不访问数据库、不执行业务动作。 /// public static class RuleConfigResolver { public static RuleConfigResolveResult Resolve(TOptions defaults, IEnumerable layers) where TOptions : class, new() { var result = new RuleConfigResolveResult { Options = CopyOptions(defaults) }; var properties = typeof(TOptions) .GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(x => x.CanRead && x.CanWrite) .ToDictionary(x => Normalize(x.Name), x => x); var orderedLayers = layers.OrderBy(x => x.Priority).ToList(); foreach (var priorityGroup in orderedLayers.GroupBy(x => x.Priority)) { var duplicateRules = priorityGroup .SelectMany(x => x.Items) .GroupBy(x => Normalize(x.RuleCode)) .Where(x => x.Count() > 1) .Select(x => x.Key) .ToList(); foreach (var duplicate in duplicateRules) { result.Errors.Add($"同一优先级存在重复规则:{duplicate}"); } if (duplicateRules.Count > 0) continue; foreach (var layer in priorityGroup) { foreach (var item in layer.Items.Where(x => x.IsEnabled)) { if (!properties.TryGetValue(Normalize(item.RuleCode), out var property)) { result.Warnings.Add($"未找到规则对应属性:{item.RuleCode}"); continue; } if (TryConvert(item, property.PropertyType, out var value, out var error)) { property.SetValue(result.Options, value); result.AppliedItems.Add(new RuleConfigAppliedItem(layer.Scope, item.RuleCode, item.RuleValue)); continue; } if (item.Required) { result.Errors.Add(error); continue; } result.Warnings.Add(error); if (item.IsHighRiskSwitch && property.PropertyType == typeof(bool)) { property.SetValue(result.Options, false); result.Warnings.Add($"高风险开关配置非法,已按保守值 false 处理:{item.RuleCode}"); } } } } return result; } private static TOptions CopyOptions(TOptions source) where TOptions : class, new() { var target = new TOptions(); foreach (var property in typeof(TOptions).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.CanRead && x.CanWrite)) { property.SetValue(target, property.GetValue(source)); } return target; } private static bool TryConvert(RuleConfigItem item, Type targetType, out object? value, out string error) { var raw = item.RuleValue ?? string.Empty; var nullableType = Nullable.GetUnderlyingType(targetType); targetType = nullableType ?? targetType; try { if (targetType == typeof(string)) { value = raw; error = string.Empty; return true; } if (targetType == typeof(bool)) { if (bool.TryParse(raw, out var boolValue)) { value = boolValue; error = string.Empty; return true; } } else if (targetType == typeof(int)) { if (int.TryParse(raw, out var intValue)) { value = intValue; error = string.Empty; return true; } } else if (targetType == typeof(long)) { if (long.TryParse(raw, out var longValue)) { value = longValue; error = string.Empty; return true; } } else if (targetType == typeof(decimal)) { if (decimal.TryParse(raw, out var decimalValue)) { value = decimalValue; error = string.Empty; return true; } } else if (targetType == typeof(List)) { value = JsonSerializer.Deserialize>(raw) ?? new List(); error = string.Empty; return true; } else if (targetType == typeof(List)) { value = JsonSerializer.Deserialize>(raw) ?? new List(); error = string.Empty; return true; } else if (targetType == typeof(List)) { value = JsonSerializer.Deserialize>(raw) ?? new List(); error = string.Empty; return true; } else { value = JsonSerializer.Deserialize(raw, targetType); error = string.Empty; return true; } } catch (Exception ex) { value = null; error = $"规则 {item.RuleCode} 类型转换失败:{ex.Message}"; return false; } value = null; error = $"规则 {item.RuleCode} 类型转换失败:值 {raw} 无法转换为 {targetType.Name}"; return false; } private static string Normalize(string value) { return value.Replace("_", string.Empty).ToLowerInvariant(); } } public sealed class RuleConfigLayer { public string Scope { get; set; } = string.Empty; public int Priority { get; set; } public List Items { get; set; } = new(); } public sealed class RuleConfigItem { public string RuleCode { get; set; } = string.Empty; public string ValueType { get; set; } = string.Empty; public string? RuleValue { get; set; } public bool Required { get; set; } public bool IsEnabled { get; set; } = true; public bool IsHighRiskSwitch { get; set; } } public sealed class RuleConfigResolveResult { public TOptions Options { get; set; } = default!; public List Warnings { get; set; } = new(); public List Errors { get; set; } = new(); public List AppliedItems { get; set; } = new(); public bool Success => Errors.Count == 0; } public sealed record RuleConfigAppliedItem(string Scope, string RuleCode, string? RuleValue);