IS8RuleEvaluator.cs 3.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. using Admin.NET.Plugin.AiDOP.Entity.S8;
  2. namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules;
  3. /// <summary>
  4. /// R2 规则 evaluator 抽象。每个 rule_type 对应一个实现,由 S8WatchSchedulerService 按 rule_type 分派。
  5. /// 首版仅 TIMEOUT;OUT_OF_RANGE 走 S8WatchSchedulerService 内既有兼容分支,不接入此抽象。
  6. /// 不做事件触发、不做 SLA 升级、不做严重度阶梯。
  7. /// </summary>
  8. public interface IS8RuleEvaluator
  9. {
  10. /// <summary>支持的 rule_type 字符串(TIMEOUT / SHORTAGE / OUT_OF_RANGE)。</summary>
  11. string RuleType { get; }
  12. /// <summary>评估一条 WatchRule,产出 0..N 条命中。不做去重、不建单。</summary>
  13. /// <remarks>
  14. /// S8-TENANT-ONLY-BATCH5:形参不再含 factoryId。判定作用域只认 Tenant ——
  15. /// 工厂从未参与任何 Provider 的过滤,却曾经决定 dedup 身份与规则归属,
  16. /// 那是"伪 Factory Scope":取数是租户级的,身份却按工厂切。
  17. /// </remarks>
  18. Task<List<S8RuleHit>> EvaluateAsync(
  19. long tenantId,
  20. AdoS8WatchRule rule,
  21. CancellationToken cancellationToken = default);
  22. }
  23. /// <summary>
  24. /// 内部命中模型。供 R2 evaluator 与 S8WatchSchedulerService 的 dedup/建单消费。
  25. /// 字段全部 nullable 以兼容 TIMEOUT / SHORTAGE / OUT_OF_RANGE 三类。R2 仅 TIMEOUT 路径写入。
  26. /// </summary>
  27. public sealed class S8RuleHit
  28. {
  29. public long SourceRuleId { get; set; }
  30. public string SourceRuleCode { get; set; } = string.Empty;
  31. public string SourceObjectType { get; set; } = string.Empty;
  32. public string SourceObjectId { get; set; } = string.Empty;
  33. public string RelatedObjectCode { get; set; } = string.Empty;
  34. public string ExceptionTypeCode { get; set; } = string.Empty;
  35. public string SceneCode { get; set; } = string.Empty;
  36. public string Severity { get; set; } = "FOLLOW";
  37. public string DedupKey { get; set; } = string.Empty;
  38. public string SourcePayload { get; set; } = "{}";
  39. public DateTime DetectedAt { get; set; } = DateTime.Now;
  40. public string Title { get; set; } = string.Empty;
  41. public long? OccurrenceDeptId { get; set; }
  42. public long? ResponsibleDeptId { get; set; }
  43. /// <summary>
  44. /// S8-RULE-LIFECYCLE-CREATE-GATE-1:本条命中是否可用于<b>首次建单</b>。
  45. ///
  46. /// <para><c>true</c>(默认)= 与既有行为完全一致。
  47. /// <c>false</c> = <b>这是一条真实命中</b>,只是不适合开一个新案子。</para>
  48. ///
  49. /// <para><b>false 绝不等于 MISS。</b>它必须照常出现在 <c>hits</c> 里,
  50. /// 因为恢复判定(<c>ReconcileRecoveriesForRuleAsync</c>)唯一的判据就是
  51. /// 「dedup_key 在不在本轮 hits 集合内」。一旦把它从 hits 里剔除,
  52. /// 已有异常会开始累计 miss 并最终被标成"已恢复" ——
  53. /// 那正是本机制要消灭的假恢复,只是换了个地方发生。</para>
  54. ///
  55. /// <para>由各 evaluator 按 <see cref="Definitions.S8RuleDefinition.CreateOnlyBeforeDueAt"/>
  56. /// 等声明计算;未声明的规则恒为 <c>true</c>。调度器在
  57. /// <b>「已有异常刷新」之后、「首次建单」之前</b>消费它(顺序不可调换,
  58. /// 否则跨过到期日的既有预警会刷新不到)。</para>
  59. /// </summary>
  60. public bool CreateEligible { get; set; } = true;
  61. /// <summary>
  62. /// 命中归属模块码(S1-S7)。evaluator 可显式提供以覆盖 scene 默认派生;
  63. /// 留 null 时建单方按严格 S1-S7 派生:hit.SceneCode(仅 S1-S7)→ exception_type.scene_code。
  64. /// 不接受 legacy 复合 scene(S8-EXCEPTION-CREATION-MODULE-CODE-FIX-1)。
  65. /// </summary>
  66. public string? ModuleCode { get; set; }
  67. }