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);