FormulaValidator.cs 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. using Admin.NET.Plugin.AiDOP.Entity;
  2. using SqlSugar;
  3. namespace Admin.NET.Plugin.AiDOP.Infrastructure.FormulaExpr;
  4. /// <summary>
  5. /// 基于 ParseResult 做引用有效性校验。
  6. /// Q4-C 策略:
  7. /// - 当 KpiMaster.IsEnabled=true → Errors(硬校验,阻止保存)
  8. /// - 当 IsEnabled=false(草稿/禁用) → Warnings(软警告,允许保存)
  9. /// </summary>
  10. public static class FormulaValidator
  11. {
  12. public sealed class ValidationResult
  13. {
  14. public List<string> Errors { get; } = new();
  15. public List<string> Warnings { get; } = new();
  16. public bool HasErrors => Errors.Count > 0;
  17. }
  18. public static async Task<ValidationResult> ValidateAsync(
  19. ISqlSugarClient db,
  20. long tenantId,
  21. FormulaParser.ParseResult parsed,
  22. bool isEnabled,
  23. long? excludeKpiId = null)
  24. {
  25. var result = new ValidationResult();
  26. // 1) Parser 阶段错误
  27. foreach (var e in parsed.Errors)
  28. {
  29. if (isEnabled) result.Errors.Add(e);
  30. else result.Warnings.Add(e);
  31. }
  32. if (parsed.IsEmpty) return result;
  33. // 2) MetricRefs 有效性:必须存在于本租户的 KpiMaster
  34. if (parsed.MetricRefs.Count > 0)
  35. {
  36. var existMetrics = await db.Queryable<AdoSmartOpsKpiMaster>()
  37. .Where(x => x.TenantId == tenantId && parsed.MetricRefs.Contains(x.MetricCode))
  38. .WhereIF(excludeKpiId.HasValue, x => x.Id != excludeKpiId!.Value)
  39. .Select(x => x.MetricCode)
  40. .ToListAsync();
  41. var missing = parsed.MetricRefs.Except(existMetrics, StringComparer.OrdinalIgnoreCase).ToList();
  42. foreach (var m in missing)
  43. {
  44. var msg = $"引用的指标 @{m} 不存在于本租户 KpiMaster";
  45. if (isEnabled) result.Errors.Add(msg);
  46. else result.Warnings.Add(msg);
  47. }
  48. }
  49. // 3) FactRefs 有效性:必须存在于本租户的 BusinessFact
  50. if (parsed.FactRefs.Count > 0)
  51. {
  52. var existFacts = await db.Queryable<AdoSmartOpsBusinessFact>()
  53. .Where(x => x.TenantId == tenantId && parsed.FactRefs.Contains(x.FactCode))
  54. .Select(x => x.FactCode)
  55. .ToListAsync();
  56. var missing = parsed.FactRefs.Except(existFacts, StringComparer.OrdinalIgnoreCase).ToList();
  57. foreach (var f in missing)
  58. {
  59. var msg = $"引用的事实 ${f} 不存在于业务事实字典";
  60. if (isEnabled) result.Errors.Add(msg);
  61. else result.Warnings.Add(msg);
  62. }
  63. }
  64. return result;
  65. }
  66. }