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