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}");
}
}