using Admin.NET.Plugin.AiDOP.Entity; using SqlSugar; namespace Admin.NET.Plugin.AiDOP.Infrastructure.FormulaExpr; /// /// 基于 ParseResult 做引用有效性校验。 /// Q4-C 策略: /// - 当 KpiMaster.IsEnabled=true → Errors(硬校验,阻止保存) /// - 当 IsEnabled=false(草稿/禁用) → Warnings(软警告,允许保存) /// public static class FormulaValidator { public sealed class ValidationResult { public List Errors { get; } = new(); public List Warnings { get; } = new(); public bool HasErrors => Errors.Count > 0; } public static async Task ValidateAsync( ISqlSugarClient db, long tenantId, FormulaParser.ParseResult parsed, bool isEnabled, long? excludeKpiId = null) { var result = new ValidationResult(); // 1) Parser 阶段错误 foreach (var e in parsed.Errors) { if (isEnabled) result.Errors.Add(e); else result.Warnings.Add(e); } if (parsed.IsEmpty) return result; // 2) MetricRefs 有效性:必须存在于本租户的 KpiMaster if (parsed.MetricRefs.Count > 0) { var existMetrics = await db.Queryable() .Where(x => x.TenantId == tenantId && parsed.MetricRefs.Contains(x.MetricCode)) .WhereIF(excludeKpiId.HasValue, x => x.Id != excludeKpiId!.Value) .Select(x => x.MetricCode) .ToListAsync(); var missing = parsed.MetricRefs.Except(existMetrics, StringComparer.OrdinalIgnoreCase).ToList(); foreach (var m in missing) { var msg = $"引用的指标 @{m} 不存在于本租户 KpiMaster"; if (isEnabled) result.Errors.Add(msg); else result.Warnings.Add(msg); } } // 3) FactRefs 有效性:必须存在于本租户的 BusinessFact if (parsed.FactRefs.Count > 0) { var existFacts = await db.Queryable() .Where(x => x.TenantId == tenantId && parsed.FactRefs.Contains(x.FactCode)) .Select(x => x.FactCode) .ToListAsync(); var missing = parsed.FactRefs.Except(existFacts, StringComparer.OrdinalIgnoreCase).ToList(); foreach (var f in missing) { var msg = $"引用的事实 ${f} 不存在于业务事实字典"; if (isEnabled) result.Errors.Add(msg); else result.Warnings.Add(msg); } } return result; } }