S8DatasetEnableGate.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. using Admin.NET.Plugin.AiDOP.Entity.S8;
  2. namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess;
  3. /// <summary>Enable Gate 的检查结果。失败时带明确 reason code,便于前端与运维定位。</summary>
  4. public sealed class S8DatasetEnableCheck
  5. {
  6. private S8DatasetEnableCheck(bool ok, string? reasonCode, string? message)
  7. {
  8. Ok = ok;
  9. ReasonCode = reasonCode;
  10. Message = message;
  11. }
  12. public bool Ok { get; }
  13. public string? ReasonCode { get; }
  14. public string? Message { get; }
  15. public static S8DatasetEnableCheck Pass() => new(true, null, null);
  16. public static S8DatasetEnableCheck Fail(string reasonCode, string message) => new(false, reasonCode, message);
  17. }
  18. /// <summary>
  19. /// STANDARD_DATASET 规则的统一启用前校验。
  20. ///
  21. /// 校验顺序:<b>先查目录层可判定的问题,再查实现层问题</b>。
  22. /// 理由——"这个数据集做不了 TIMEOUT"是定义问题,等 Provider 上线也不会变好;
  23. /// 若先报 provider_not_registered 会误导配置方以为等实现即可。
  24. ///
  25. /// 1. dataset_code 是否填写 → dataset_code_missing
  26. /// 2. Catalog 中是否定义 → dataset_not_defined
  27. /// 3. 定义是否停用 → dataset_not_ready
  28. /// 4. 规则类型是否已知 → dataset_unknown_rule_type
  29. /// 5. 目录机制能力是否支持 → dataset_capability_mismatch
  30. /// 6. 目录 canonical 字段是否齐备 → dataset_missing_required_field
  31. /// 7. Provider 是否注册 → dataset_provider_not_registered
  32. /// 8. Provider 编码是否一致 → dataset_provider_code_mismatch
  33. /// 9. Provider 能力是否同样满足 → dataset_capability_mismatch / dataset_missing_required_field
  34. /// 10. 租户 / 工厂作用域 → invalid_tenant_scope / invalid_factory_scope
  35. ///
  36. /// LEGACY_SQL 规则不经过本 Gate(其校验由既有数据源 / expression 检查承担)。
  37. /// </summary>
  38. public class S8DatasetEnableGate : ITransient
  39. {
  40. private readonly IS8DatasetCatalog _catalog;
  41. private readonly S8MonitoringDataProviderRegistry _registry;
  42. public S8DatasetEnableGate(IS8DatasetCatalog catalog, S8MonitoringDataProviderRegistry registry)
  43. {
  44. _catalog = catalog;
  45. _registry = registry;
  46. }
  47. /// <summary>检查规则是否具备启用条件。LEGACY_SQL 直接通过。</summary>
  48. public S8DatasetEnableCheck Check(AdoS8WatchRule rule, long tenantId, long factoryId)
  49. {
  50. if (!S8DataAccessMode.IsAcceptable(rule.DataAccessMode))
  51. return S8DatasetEnableCheck.Fail(
  52. "data_access_mode_invalid",
  53. $"未知的取数模式 {rule.DataAccessMode}");
  54. if (S8DataAccessMode.Resolve(rule.DataAccessMode) == S8DataAccessMode.LegacySql)
  55. return S8DatasetEnableCheck.Pass();
  56. // ① dataset_code
  57. if (string.IsNullOrWhiteSpace(rule.DatasetCode))
  58. return S8DatasetEnableCheck.Fail(
  59. S8DatasetReasonCode.CodeMissing,
  60. "STANDARD_DATASET 模式的规则必须提供 dataset_code");
  61. // ② Catalog 定义
  62. var definition = _catalog.Find(rule.DatasetCode);
  63. if (definition == null)
  64. return S8DatasetEnableCheck.Fail(
  65. S8DatasetReasonCode.NotDefined,
  66. $"数据集 {rule.DatasetCode} 未在目录中定义");
  67. // ③ 定义是否停用
  68. if (!definition.IsActive)
  69. return S8DatasetEnableCheck.Fail(
  70. S8DatasetReasonCode.NotReady,
  71. $"数据集 {definition.DatasetCode} 已停用,不能用于规则执行");
  72. // ④ 规则类型已知
  73. if (!S8RuleTypeFieldRequirements.IsKnown(rule.RuleType))
  74. return S8DatasetEnableCheck.Fail(
  75. S8DatasetReasonCode.UnknownRuleType,
  76. $"规则类型 {rule.RuleType} 不在 canonical 词表内,无法校验数据集能力");
  77. // ⑤ 目录机制能力(定义层问题,先于 Provider 报出)
  78. if (!definition.Capabilities.Supports(rule.RuleType))
  79. return S8DatasetEnableCheck.Fail(
  80. S8DatasetReasonCode.CapabilityMismatch,
  81. $"数据集 {definition.DatasetCode} 不支持 {rule.RuleType} 类规则");
  82. // ⑥ 目录 canonical 字段齐备(防"声明支持某机制但拿不出必需字段"的自相矛盾配置)
  83. var missing = definition.Capabilities.MissingFieldsFor(rule.RuleType);
  84. if (missing is { Count: > 0 })
  85. return S8DatasetEnableCheck.Fail(
  86. S8DatasetReasonCode.MissingRequiredField,
  87. $"数据集 {definition.DatasetCode} 缺少 {rule.RuleType} 必需字段:{string.Join(", ", missing)}");
  88. // ⑦ Provider 注册
  89. if (!_registry.IsRegistered(definition.DatasetCode))
  90. return S8DatasetEnableCheck.Fail(
  91. S8DatasetReasonCode.ProviderNotRegistered,
  92. $"数据集 {definition.DatasetCode} 已定义但尚无 Provider 实现,可保存配置但不能启用");
  93. // ⑧ Provider 自报编码与目录一致
  94. var provider = _registry.Resolve(definition.DatasetCode);
  95. if (!string.Equals(provider.DatasetCode?.Trim(), definition.DatasetCode, StringComparison.OrdinalIgnoreCase))
  96. return S8DatasetEnableCheck.Fail(
  97. S8DatasetReasonCode.ProviderCodeMismatch,
  98. $"Provider 自报数据集编码 {provider.DatasetCode} 与目录定义 {definition.DatasetCode} 不一致");
  99. // ⑨ Provider 实际能力必须与目录声明同样满足(不一致时按更严格的一方拒绝)
  100. if (!provider.Capabilities.Supports(rule.RuleType))
  101. return S8DatasetEnableCheck.Fail(
  102. S8DatasetReasonCode.CapabilityMismatch,
  103. $"Provider 实现不支持 {rule.RuleType} 类规则(数据集 {definition.DatasetCode})");
  104. var providerMissing = provider.Capabilities.MissingFieldsFor(rule.RuleType);
  105. if (providerMissing is { Count: > 0 })
  106. return S8DatasetEnableCheck.Fail(
  107. S8DatasetReasonCode.MissingRequiredField,
  108. $"Provider 实现缺少 {rule.RuleType} 必需字段:{string.Join(", ", providerMissing)}");
  109. // ⑩ 作用域
  110. if (tenantId <= 0)
  111. return S8DatasetEnableCheck.Fail(S8DatasetReasonCode.InvalidTenantScope, "租户作用域非法");
  112. if (factoryId <= 0)
  113. return S8DatasetEnableCheck.Fail(S8DatasetReasonCode.InvalidFactoryScope, "工厂作用域非法");
  114. return S8DatasetEnableCheck.Pass();
  115. }
  116. /// <summary>检查并在失败时抛 <see cref="S8BizException"/>(配置类错误,API 层直接回给调用方)。</summary>
  117. public void EnsureCanEnable(AdoS8WatchRule rule, long tenantId, long factoryId)
  118. {
  119. var result = Check(rule, tenantId, factoryId);
  120. if (!result.Ok)
  121. throw new S8BizException($"[{result.ReasonCode}] {result.Message}");
  122. }
  123. }