S8DatasetCapabilities.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess;
  2. /// <summary>
  3. /// 数据集能力声明,分两层:
  4. ///
  5. /// <b>A. 机制能力</b>(SupportsXxx):该数据集打算支撑哪几类规则判定。
  6. /// <b>B. 字段能力</b>(HasXxx):该数据集实际能提供哪些 canonical 字段。
  7. ///
  8. /// 分两层的理由:单一布尔位不可信。声明 SupportsTimeout=true 却拿不出 due_at 是一种
  9. /// 自相矛盾的配置,运行期只会表现为"永不命中"的静默失效。两层交叉校验才能在启用前发现。
  10. ///
  11. /// 字段能力回答的是"这个数据集有没有这个字段",与"某一行该字段值为 NULL"是两件事:
  12. /// 前者是能力问题(规则不能启用),后者由 evaluator 按原规则跳过该行。
  13. /// </summary>
  14. public sealed class S8DatasetCapabilities
  15. {
  16. // ---------- A. 机制能力 ----------
  17. /// <summary>可支撑 TIMEOUT。</summary>
  18. public bool SupportsTimeout { get; init; }
  19. /// <summary>可支撑 SHORTAGE。</summary>
  20. public bool SupportsShortage { get; init; }
  21. /// <summary>可支撑 OUT_OF_RANGE。</summary>
  22. public bool SupportsOutOfRange { get; init; }
  23. // ---------- B. 字段能力 ----------
  24. /// <summary>能提供 source_object_id(对象身份 + DedupKey 输入)。</summary>
  25. public bool HasSourceObjectId { get; init; }
  26. /// <summary>能提供 related_object_code。</summary>
  27. public bool HasRelatedObjectCode { get; init; }
  28. /// <summary>能提供 due_at。</summary>
  29. public bool HasDueAt { get; init; }
  30. /// <summary>能提供 status。</summary>
  31. public bool HasStatus { get; init; }
  32. /// <summary>能提供 measured_value。</summary>
  33. public bool HasMeasuredValue { get; init; }
  34. /// <summary>能提供 target_qty。</summary>
  35. public bool HasTargetQty { get; init; }
  36. /// <summary>能提供 actual_qty。</summary>
  37. public bool HasActualQty { get; init; }
  38. /// <summary>该数据集是否声明支持给定规则类型(仅机制层,未校验字段是否齐备)。</summary>
  39. public bool Supports(string? ruleType)
  40. {
  41. if (string.IsNullOrWhiteSpace(ruleType)) return false;
  42. return ruleType.Trim().ToUpperInvariant() switch
  43. {
  44. "TIMEOUT" => SupportsTimeout,
  45. "SHORTAGE" => SupportsShortage,
  46. "OUT_OF_RANGE" => SupportsOutOfRange,
  47. _ => false
  48. };
  49. }
  50. /// <summary>该数据集是否能提供给定 canonical 字段。</summary>
  51. public bool HasField(string? canonicalColumn)
  52. {
  53. if (string.IsNullOrWhiteSpace(canonicalColumn)) return false;
  54. return canonicalColumn.Trim().ToLowerInvariant() switch
  55. {
  56. S8CanonicalColumns.SourceObjectId => HasSourceObjectId,
  57. S8CanonicalColumns.RelatedObjectCode => HasRelatedObjectCode,
  58. S8CanonicalColumns.DueAt => HasDueAt,
  59. S8CanonicalColumns.Status => HasStatus,
  60. S8CanonicalColumns.MeasuredValue => HasMeasuredValue,
  61. S8CanonicalColumns.TargetQty => HasTargetQty,
  62. S8CanonicalColumns.ActualQty => HasActualQty,
  63. _ => false
  64. };
  65. }
  66. /// <summary>
  67. /// 返回该规则类型缺少的 canonical 字段。空集合表示字段能力齐备。
  68. /// 未知规则类型返回 null(由调用方按 unknown_rule_type 处理,与"字段缺失"区分)。
  69. /// </summary>
  70. public IReadOnlyList<string>? MissingFieldsFor(string? ruleType)
  71. {
  72. var required = S8RuleTypeFieldRequirements.For(ruleType);
  73. if (required == null) return null;
  74. return required.Where(field => !HasField(field)).ToList();
  75. }
  76. /// <summary>机制能力与字段能力是否都满足该规则类型。</summary>
  77. public bool CanServe(string? ruleType)
  78. {
  79. if (!Supports(ruleType)) return false;
  80. var missing = MissingFieldsFor(ruleType);
  81. return missing != null && missing.Count == 0;
  82. }
  83. }