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