using Admin.NET.Plugin.AiDOP.Entity.S8; namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess; /// /// 规则取数配置校验(纯逻辑,不访问数据库)。 /// /// 刻意分成两级,因为二者的时机与严格度不同: /// /// ValidateForSave(保存 / 草稿)——只看结构:模式合法、编码填了且在目录里、 /// LEGACY_SQL 有 expression。**不查 Provider**,允许"数据集已定义、Provider 未上线"时 /// 先把规则配置好存下来。 /// /// ValidateForEnable(启用)——委托 做完整运行条件校验, /// 含 Provider 注册、能力与字段齐备、作用域。 /// public static class S8WatchRuleDataAccessValidator { /// /// 该规则是否仍需绑定物理数据源。 /// LEGACY_SQL(含历史 NULL)需要;STANDARD_DATASET 由 Provider 负责取数,无物理数据源概念。 /// public static bool RequiresDataSource(AdoS8WatchRule rule) { if (!S8DataAccessMode.IsAcceptable(rule.DataAccessMode)) return true; return S8DataAccessMode.Resolve(rule.DataAccessMode) == S8DataAccessMode.LegacySql; } /// /// 保存 / 草稿级校验:只校验结构与目录归属,不要求 Provider 已上线。 /// 失败抛 。 /// public static void ValidateForSave(AdoS8WatchRule rule, IS8DatasetCatalog catalog) { if (!S8DataAccessMode.IsAcceptable(rule.DataAccessMode)) throw new S8BizException( $"未知的取数模式 {rule.DataAccessMode},仅支持 {S8DataAccessMode.LegacySql} / {S8DataAccessMode.StandardDataset}"); var mode = S8DataAccessMode.Resolve(rule.DataAccessMode); if (mode == S8DataAccessMode.LegacySql) { if (string.IsNullOrWhiteSpace(rule.Expression)) throw new S8BizException("LEGACY_SQL 模式的规则必须提供 expression"); // 两种模式的字段混填会让实际生效路径不可预测。 if (!string.IsNullOrWhiteSpace(rule.DatasetCode)) throw new S8BizException("LEGACY_SQL 模式的规则不应填写 dataset_code"); return; } // STANDARD_DATASET:编码必须填写且必须是目录中已定义的数据集(拦截编码写错), // 但**不检查 Provider 是否已注册** —— 那是启用期的事。 if (string.IsNullOrWhiteSpace(rule.DatasetCode)) throw new S8BizException($"[{S8DatasetReasonCode.CodeMissing}] STANDARD_DATASET 模式的规则必须提供 dataset_code"); if (!catalog.IsDefined(rule.DatasetCode)) throw new S8BizException($"[{S8DatasetReasonCode.NotDefined}] 数据集 {rule.DatasetCode} 未在目录中定义"); } /// /// 启用级校验:完整运行条件。失败抛 ,消息带 reason code。 /// /// S8-LEGACY-RUNTIME-RETIREMENT-1:**先判取数模式是否还有运行资格,再判数据集条件。** /// 顺序不可颠倒 —— 对 LEGACY_SQL 是直接 Pass 的 /// (它只负责 Dataset 侧条件),把退役判定放到它后面等于永远判不到。 /// /// 这里是「Create 时带 enabled=true」与「UpdateParams 由停用切启用」两条路径的**共同必经点**, /// 因此退役门禁挂在此处即可覆盖二者,无需散落到各 service。 /// public static void ValidateForEnable( AdoS8WatchRule rule, S8DatasetEnableGate gate, long tenantId, long factoryId) { S8RuleCreationPolicy.EnsureMaintenanceAllowed(rule, enabling: true); gate.EnsureCanEnable(rule, tenantId, factoryId); } }