using Admin.NET.Plugin.AiDOP.Entity.S8; namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess; /// Enable Gate 的检查结果。失败时带明确 reason code,便于前端与运维定位。 public sealed class S8DatasetEnableCheck { private S8DatasetEnableCheck(bool ok, string? reasonCode, string? message) { Ok = ok; ReasonCode = reasonCode; Message = message; } public bool Ok { get; } public string? ReasonCode { get; } public string? Message { get; } public static S8DatasetEnableCheck Pass() => new(true, null, null); public static S8DatasetEnableCheck Fail(string reasonCode, string message) => new(false, reasonCode, message); } /// /// STANDARD_DATASET 规则的统一启用前校验。 /// /// 校验顺序:先查目录层可判定的问题,再查实现层问题。 /// 理由——"这个数据集做不了 TIMEOUT"是定义问题,等 Provider 上线也不会变好; /// 若先报 provider_not_registered 会误导配置方以为等实现即可。 /// /// 1. dataset_code 是否填写 → dataset_code_missing /// 2. Catalog 中是否定义 → dataset_not_defined /// 3. 定义是否停用 → dataset_not_ready /// 4. 规则类型是否已知 → dataset_unknown_rule_type /// 5. 目录机制能力是否支持 → dataset_capability_mismatch /// 6. 目录 canonical 字段是否齐备 → dataset_missing_required_field /// 7. Provider 是否注册 → dataset_provider_not_registered /// 8. Provider 编码是否一致 → dataset_provider_code_mismatch /// 9. Provider 能力是否同样满足 → dataset_capability_mismatch / dataset_missing_required_field /// 10. 租户 / 工厂作用域 → invalid_tenant_scope / invalid_factory_scope /// /// S8-STANDARD-DATASET-HARD-CUTOVER-1:取数路径唯一,Gate 不再有"先判模式"这一步。 /// public class S8DatasetEnableGate : ITransient { private readonly IS8DatasetCatalog _catalog; private readonly S8MonitoringDataProviderRegistry _registry; public S8DatasetEnableGate(IS8DatasetCatalog catalog, S8MonitoringDataProviderRegistry registry) { _catalog = catalog; _registry = registry; } /// 检查规则是否具备启用条件。 public S8DatasetEnableCheck Check(AdoS8WatchRule rule, long tenantId, long factoryId) { // ① dataset_code if (string.IsNullOrWhiteSpace(rule.DatasetCode)) return S8DatasetEnableCheck.Fail( S8DatasetReasonCode.CodeMissing, "STANDARD_DATASET 模式的规则必须提供 dataset_code"); // ② Catalog 定义 var definition = _catalog.Find(rule.DatasetCode); if (definition == null) return S8DatasetEnableCheck.Fail( S8DatasetReasonCode.NotDefined, $"数据集 {rule.DatasetCode} 未在目录中定义"); // ③ 定义是否停用 if (!definition.IsActive) return S8DatasetEnableCheck.Fail( S8DatasetReasonCode.NotReady, $"数据集 {definition.DatasetCode} 已停用,不能用于规则执行"); // ④ 规则类型已知 if (!S8RuleTypeFieldRequirements.IsKnown(rule.RuleType)) return S8DatasetEnableCheck.Fail( S8DatasetReasonCode.UnknownRuleType, $"规则类型 {rule.RuleType} 不在 canonical 词表内,无法校验数据集能力"); // ⑤ 目录机制能力(定义层问题,先于 Provider 报出) if (!definition.Capabilities.Supports(rule.RuleType)) return S8DatasetEnableCheck.Fail( S8DatasetReasonCode.CapabilityMismatch, $"数据集 {definition.DatasetCode} 不支持 {rule.RuleType} 类规则"); // ⑥ 目录 canonical 字段齐备(防"声明支持某机制但拿不出必需字段"的自相矛盾配置) var missing = definition.Capabilities.MissingFieldsFor(rule.RuleType); if (missing is { Count: > 0 }) return S8DatasetEnableCheck.Fail( S8DatasetReasonCode.MissingRequiredField, $"数据集 {definition.DatasetCode} 缺少 {rule.RuleType} 必需字段:{string.Join(", ", missing)}"); // ⑦ Provider 注册 if (!_registry.IsRegistered(definition.DatasetCode)) return S8DatasetEnableCheck.Fail( S8DatasetReasonCode.ProviderNotRegistered, $"数据集 {definition.DatasetCode} 已定义但尚无 Provider 实现,可保存配置但不能启用"); // ⑧ Provider 自报编码与目录一致 var provider = _registry.Resolve(definition.DatasetCode); if (!string.Equals(provider.DatasetCode?.Trim(), definition.DatasetCode, StringComparison.OrdinalIgnoreCase)) return S8DatasetEnableCheck.Fail( S8DatasetReasonCode.ProviderCodeMismatch, $"Provider 自报数据集编码 {provider.DatasetCode} 与目录定义 {definition.DatasetCode} 不一致"); // ⑨ Provider 实际能力必须与目录声明同样满足(不一致时按更严格的一方拒绝) if (!provider.Capabilities.Supports(rule.RuleType)) return S8DatasetEnableCheck.Fail( S8DatasetReasonCode.CapabilityMismatch, $"Provider 实现不支持 {rule.RuleType} 类规则(数据集 {definition.DatasetCode})"); var providerMissing = provider.Capabilities.MissingFieldsFor(rule.RuleType); if (providerMissing is { Count: > 0 }) return S8DatasetEnableCheck.Fail( S8DatasetReasonCode.MissingRequiredField, $"Provider 实现缺少 {rule.RuleType} 必需字段:{string.Join(", ", providerMissing)}"); // ⑩ 作用域 if (tenantId <= 0) return S8DatasetEnableCheck.Fail(S8DatasetReasonCode.InvalidTenantScope, "租户作用域非法"); if (factoryId <= 0) return S8DatasetEnableCheck.Fail(S8DatasetReasonCode.InvalidFactoryScope, "工厂作用域非法"); return S8DatasetEnableCheck.Pass(); } /// 检查并在失败时抛 (配置类错误,API 层直接回给调用方)。 public void EnsureCanEnable(AdoS8WatchRule rule, long tenantId, long factoryId) { var result = Check(rule, tenantId, factoryId); if (!result.Ok) throw new S8BizException($"[{result.ReasonCode}] {result.Message}"); } }