using System.Text.Json; namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules; /// /// R3 OUT_OF_RANGE 类规则 params_json 解析结果。internal 可见,供测试覆盖。 /// 兼容两种协议形态: /// 1. 新协议:measuredValueField / lowerBound / upperBound / lowerBoundField / upperBoundField / objectCodeField / objectIdField / toleranceAbs / toleranceRatio / exceptionTypeCode /// 2. 旧 G01 兼容:mode=single_threshold / operator=">"|">="|"<"|"<=" / threshold / valueField / objectCodeField / compatAlertRuleCode /// 字段缺失或非法 JSON:调用方负责降级(evaluator 静默返空命中),不在此抛出业务异常。 /// internal sealed class S8OutOfRangeParams { public string? MeasuredValueField { get; set; } public string? ObjectCodeField { get; set; } public string? ObjectIdField { get; set; } public string? LowerBoundField { get; set; } public string? UpperBoundField { get; set; } public decimal? LowerBound { get; set; } public decimal? UpperBound { get; set; } public decimal ToleranceAbs { get; set; } public decimal ToleranceRatio { get; set; } public string? ExceptionTypeCode { get; set; } /// 解析 params_json;非法 JSON 抛 ,由上游捕获。 public static S8OutOfRangeParams Parse(string json) { using var doc = JsonDocument.Parse(json); var root = doc.RootElement; var p = new S8OutOfRangeParams(); // 新协议字段 if (root.TryGetProperty("measuredValueField", out var v1)) p.MeasuredValueField = v1.GetString(); if (root.TryGetProperty("objectCodeField", out var v2)) p.ObjectCodeField = v2.GetString(); if (root.TryGetProperty("objectIdField", out var v3)) p.ObjectIdField = v3.GetString(); if (root.TryGetProperty("lowerBoundField", out var v4)) p.LowerBoundField = v4.GetString(); if (root.TryGetProperty("upperBoundField", out var v5)) p.UpperBoundField = v5.GetString(); if (root.TryGetProperty("lowerBound", out var v6) && v6.ValueKind == JsonValueKind.Number) p.LowerBound = v6.GetDecimal(); if (root.TryGetProperty("upperBound", out var v7) && v7.ValueKind == JsonValueKind.Number) p.UpperBound = v7.GetDecimal(); if (root.TryGetProperty("toleranceAbs", out var v8) && v8.ValueKind == JsonValueKind.Number) p.ToleranceAbs = v8.GetDecimal(); if (root.TryGetProperty("toleranceRatio", out var v9) && v9.ValueKind == JsonValueKind.Number) p.ToleranceRatio = v9.GetDecimal(); if (root.TryGetProperty("exceptionTypeCode", out var v10)) p.ExceptionTypeCode = v10.GetString(); // G01 兼容:valueField → measuredValueField if (string.IsNullOrWhiteSpace(p.MeasuredValueField) && root.TryGetProperty("valueField", out var legacyVal)) p.MeasuredValueField = legacyVal.GetString(); // G01 兼容:mode=single_threshold + operator + threshold → upperBound 或 lowerBound if (root.TryGetProperty("mode", out var modeEl) && string.Equals(modeEl.GetString(), "single_threshold", StringComparison.OrdinalIgnoreCase) && root.TryGetProperty("operator", out var opEl) && root.TryGetProperty("threshold", out var thrEl) && thrEl.ValueKind == JsonValueKind.Number) { var op = opEl.GetString()?.Trim(); var thr = thrEl.GetDecimal(); // operator '>' 或 '>=' → measured 超过 threshold 命中 → 等价 upperBound=threshold // operator '<' 或 '<=' → measured 低于 threshold 命中 → 等价 lowerBound=threshold if (op == ">" || op == ">=") p.UpperBound ??= thr; else if (op == "<" || op == "<=") p.LowerBound ??= thr; } return p; } }