using Admin.NET.Plugin.AiDOP.Entity.S8; namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules; /// /// R2 规则 evaluator 抽象。每个 rule_type 对应一个实现,由 S8WatchSchedulerService 按 rule_type 分派。 /// 首版仅 TIMEOUT;OUT_OF_RANGE 走 S8WatchSchedulerService 内既有兼容分支,不接入此抽象。 /// 不做事件触发、不做 SLA 升级、不做严重度阶梯。 /// public interface IS8RuleEvaluator { /// 支持的 rule_type 字符串(TIMEOUT / SHORTAGE / OUT_OF_RANGE)。 string RuleType { get; } /// 评估一条 WatchRule,产出 0..N 条命中。不做去重、不建单。 /// /// S8-TENANT-ONLY-BATCH5:形参不再含 factoryId。判定作用域只认 Tenant —— /// 工厂从未参与任何 Provider 的过滤,却曾经决定 dedup 身份与规则归属, /// 那是"伪 Factory Scope":取数是租户级的,身份却按工厂切。 /// Task> EvaluateAsync( long tenantId, AdoS8WatchRule rule, CancellationToken cancellationToken = default); } /// /// 内部命中模型。供 R2 evaluator 与 S8WatchSchedulerService 的 dedup/建单消费。 /// 字段全部 nullable 以兼容 TIMEOUT / SHORTAGE / OUT_OF_RANGE 三类。R2 仅 TIMEOUT 路径写入。 /// public sealed class S8RuleHit { public long SourceRuleId { get; set; } public string SourceRuleCode { get; set; } = string.Empty; public string SourceObjectType { get; set; } = string.Empty; public string SourceObjectId { get; set; } = string.Empty; public string RelatedObjectCode { get; set; } = string.Empty; public string ExceptionTypeCode { get; set; } = string.Empty; public string SceneCode { get; set; } = string.Empty; public string Severity { get; set; } = "FOLLOW"; public string DedupKey { get; set; } = string.Empty; public string SourcePayload { get; set; } = "{}"; public DateTime DetectedAt { get; set; } = DateTime.Now; public string Title { get; set; } = string.Empty; public long? OccurrenceDeptId { get; set; } public long? ResponsibleDeptId { get; set; } /// /// S8-RULE-LIFECYCLE-CREATE-GATE-1:本条命中是否可用于首次建单。 /// /// true(默认)= 与既有行为完全一致。 /// false = 这是一条真实命中,只是不适合开一个新案子。 /// /// false 绝不等于 MISS。它必须照常出现在 hits 里, /// 因为恢复判定(ReconcileRecoveriesForRuleAsync)唯一的判据就是 /// 「dedup_key 在不在本轮 hits 集合内」。一旦把它从 hits 里剔除, /// 已有异常会开始累计 miss 并最终被标成"已恢复" —— /// 那正是本机制要消灭的假恢复,只是换了个地方发生。 /// /// 由各 evaluator 按 /// 等声明计算;未声明的规则恒为 true。调度器在 /// 「已有异常刷新」之后、「首次建单」之前消费它(顺序不可调换, /// 否则跨过到期日的既有预警会刷新不到)。 /// public bool CreateEligible { get; set; } = true; /// /// 命中归属模块码(S1-S7)。evaluator 可显式提供以覆盖 scene 默认派生; /// 留 null 时建单方按严格 S1-S7 派生:hit.SceneCode(仅 S1-S7)→ exception_type.scene_code。 /// 不接受 legacy 复合 scene(S8-EXCEPTION-CREATION-MODULE-CODE-FIX-1)。 /// public string? ModuleCode { get; set; } }