using Admin.NET.Plugin.AiDOP.Entity.S8; namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess; /// /// 规则创建来源。用于区分"调用方自带 SQL"与"服务端按字典生成 SQL", /// 因为两者的技术债性质与安全面完全不同。 /// public static class S8RuleCreationOrigin { /// /// 裸 REST 创建(POST /api/aidop/s8/config/watch-rules):调用方可自带任意 expression。 /// 这是新增 SQL 技术债的来源,也是注入面。 /// public const string ExternalApi = "EXTERNAL_API"; /// /// 配置向导生成(S8ConfigDraftService.GenerateRuleAsync): /// SQL 由服务端按 ado_s8_monitor_object / ado_s8_monitor_metric 字典生成, /// 标识符经 ValidateSqlIdentifier 白名单校验,调用方不提供 SQL 文本。 /// public const string WizardGenerated = "WIZARD_GENERATED"; } /// /// S8 规则取数模式治理。 /// /// S8-LEGACY-RUNTIME-RETIREMENT-1(本次收口):S8 正式确立为数据中台**消费层** —— /// 取数走 Governed Dataset → Provider → Canonical Contract,S8 不再承担源系统接入与物理连接治理。 /// 因此 成为**唯一允许运行**的取数模式。 /// /// 与上一阶段(S8-LEGACY-SQL-RESIDUAL-CLEANUP-3,按创建来源分治)的差别: /// · 那一阶段只掐"调用方自带 SQL",保留向导的服务端生成 SQL,因为向导当时是在产流程; /// · 本阶段连向导通道一并收口 —— **没有治理 Dataset 就没有可运行 Rule**,不再用 Legacy SQL 兜底。 /// /// 刻意不做的三件事(改动前请先读): /// 1. **不改历史解析语义**:data_access_mode 为 NULL / 空白仍解析为 LEGACY_SQL /// (见 )。把历史行伪装成 STANDARD_DATASET /// 会让它们绕过 Enable Gate 去引用一个根本不存在的 Provider。 /// 2. **不禁止停用与只读维护**:Legacy 规则必须还能被关停、被查看,否则出问题时只能去改库。 /// 3. **不删历史数据**:存量 Legacy 规则与其 detection / exception 全部保留, /// 只是失去运行资格;物理清理留给后续独立批次。 /// public static class S8RuleCreationPolicy { /// 拒绝新增裸 SQL 规则时使用的 reason code。 public const string LegacySqlCreationDisabledReason = "legacy_sql_creation_disabled"; /// Legacy 规则试图进入运行态时使用的 reason code(启用 / 恢复 / 立即执行 / 调度拾取共用)。 public const string LegacySqlRetiredReason = "legacy_sql_retired"; /// 面向用户的统一退役文案。各入口共用,避免同一条产品裁决出现多种说法。 public const string LegacySqlRetiredMessage = "Legacy SQL 规则已退役。S8 仅允许运行 STANDARD_DATASET 规则:" + "请引用已注册的治理数据集(dataset_code)重建该规则;历史 Legacy 规则仍可查看与停用。"; /// /// 该规则是否具备运行资格。true 仅当解析后的取数模式为 STANDARD_DATASET。 /// /// 非法模式值(既非 LEGACY_SQL 也非 STANDARD_DATASET)同样返回 false 而非抛错: /// 调度器不能因为一条脏数据中断整个 tick,但也绝不能放它进执行路径。 /// public static bool IsRuntimeEligible(AdoS8WatchRule rule) { if (rule == null) return false; if (!S8DataAccessMode.IsAcceptable(rule.DataAccessMode)) return false; return S8DataAccessMode.Resolve(rule.DataAccessMode) == S8DataAccessMode.StandardDataset; } /// /// 新建规则时的取数模式治理。 /// /// 现在**任何来源**都只能创建 STANDARD_DATASET 规则。 予以保留: /// 它仍用于区分错误文案(裸 API 自带 SQL vs 向导生成),而删除它会破坏 /// S8WatchRuleService.CreateAsync 的既有签名契约。 /// public static void EnsureCreationAllowed(AdoS8WatchRule rule, string? origin) { // STANDARD_DATASET 是唯一被接受的方向。 if (IsRuntimeEligible(rule)) return; // 向导曾是唯一豁免通道(服务端按字典白名单生成 SQL)。本批一并收口, // 但给它单独文案:用户在向导里看到"你自带的 SQL 被拒"会完全对不上号。 if (string.Equals(origin, S8RuleCreationOrigin.WizardGenerated, StringComparison.Ordinal)) throw new S8BizException( $"[{LegacySqlRetiredReason}] 配置向导不再生成 Legacy SQL 规则。" + "当前所选监控对象没有对应的治理数据集,无法创建可运行规则;" + "请先在数据中台完成该对象的数据集治理与 Provider 上线。"); throw new S8BizException( $"[{LegacySqlCreationDisabledReason}] 不再接受新建自带 SQL 的监控规则。" + $"请改用 data_access_mode={S8DataAccessMode.StandardDataset} 并引用已注册的 dataset_code;" + "历史 LEGACY_SQL 规则仍可查看与停用。"); } /// /// 存量规则维护时的治理。只拦"进入运行态",不拦"退出运行态"。 /// /// = false(停用 / 暂停 / 改参数)一律放行 —— 这是硬要求: /// 若关停也被拦,数据集一出问题存量规则就再也关不掉。 /// public static void EnsureMaintenanceAllowed(AdoS8WatchRule rule, bool enabling) { if (!enabling) return; if (IsRuntimeEligible(rule)) return; throw new S8BizException($"[{LegacySqlRetiredReason}] {LegacySqlRetiredMessage}"); } }