S8PurchaseDeliveryRuleDefinitions.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. using Admin.NET.Plugin.AiDOP.Infrastructure.S8;
  2. using Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess;
  3. using Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.Providers;
  4. namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules.Definitions;
  5. /// <summary>
  6. /// S8 正式规则定义的集中登记处(当前仅 Rule 01)。
  7. ///
  8. /// <para>集中一处而非每规则一个文件,理由与 <see cref="S8BusinessDatasetDefinitions"/> 相同:
  9. /// 重复 rule_code 在数组里人工一眼可查;新增规则只加一个元素,不必再改 <c>Startup.cs</c>。</para>
  10. /// </summary>
  11. public sealed class S8PurchaseDeliveryRuleDefinitions : IS8RuleDefinitionSource
  12. {
  13. /// <summary>Rule 01 编码。全链身份,任何地方引用必须逐字用本常量,不写字面量。</summary>
  14. public const string PurchaseDeliveryDateDelayCode = "RULE_S4_PURCHASE_DELIVERY_DATE_DELAY";
  15. /// <summary>Rule 01 建单使用的异常类型。</summary>
  16. public const string PurchaseDeliveryExceptionTypeCode = "PURCHASE_DELIVERY_ABNORMAL";
  17. /// <summary>Rule 01 源对象类型。**参与 dedup_key**。</summary>
  18. public const string PurchaseOrderLineObjectType = "PURCHASE_ORDER_LINE";
  19. public IEnumerable<S8RuleDefinition> GetDefinitions() => new[]
  20. {
  21. new S8RuleDefinition
  22. {
  23. RuleCode = PurchaseDeliveryDateDelayCode,
  24. DisplayName = "采购交期延误",
  25. Description =
  26. "采购订单行已过约定到期日仍未完成交付时提报异常。" +
  27. "到期日取交付计划需求日期,缺失时回落采购订单到期日;" +
  28. "已完成 / 已关闭 / 已取消的订单行不再判为延误。",
  29. DatasetCode = S8BusinessDatasetDefinitions.PurchaseDeliveryCode,
  30. RuleType = S8TimeoutRuleEvaluator.RuleTypeCode,
  31. RuleMechanism = "DATE",
  32. SourceObjectType = PurchaseOrderLineObjectType,
  33. SceneCode = S8ModuleCode.S4,
  34. StageCode = S8ModuleCode.S4,
  35. OrderFlowCode = "MATERIAL_PURCHASE",
  36. ExceptionTypeCode = PurchaseDeliveryExceptionTypeCode,
  37. // 面向业务管理员的只读说明。刻意不写 dwd_supplier_delivery / COALESCE / 列名 ——
  38. // 配置页展示的是"这条规则按什么判",不是"它怎么实现的"。
  39. JudgementSummary =
  40. "到期日取交付计划的需求日期;该日期缺失时回落为采购订单的到期日。"
  41. + "订单行状态为已完成 / 已关闭 / 已取消时不再判为延误。",
  42. DedupIdentitySummary = "采购订单号 + 订单行号",
  43. // ────────────────────────────────────────────────────────────────
  44. // 判定语义。四个列名都是 canonical 名 —— Provider 直接以这些名字产出行
  45. // (见 S8PurchaseDeliveryDataProvider 的 SELECT 别名契约)。
  46. //
  47. // due_at 的血缘已完成取证:Provider 的 COALESCE(need_date, due_date)
  48. // 与数据中台自身的 DELAYED 判据
  49. // COALESCE(交付计划 need_date, 采购订单 need_date, 采购订单 due_date) < now
  50. // 展开后逐字等价(S3MdpSyncTransformService 的 dwd_supplier_delivery INSERT)。
  51. // 实测佐证:UAT 租户最新快照 36 行,evaluator 命中 28 条,
  52. // 与中台自行标记的 DELAYED 28 条完全一致。
  53. //
  54. // ⚠️ UNPROVEN_CONTRACT_SEMANTICS:以上只证明 S8 与中台口径一致,
  55. // 并未证明 need_date 等同于与供应商约定的承诺交期。该问题需追至源系统
  56. // PurOrdDetail.NeedDate 并由业务确认,在此之前 due_at 不得被当作交付合同依据。
  57. // ────────────────────────────────────────────────────────────────
  58. Timeout = new S8TimeoutSemantics
  59. {
  60. DueAtColumn = S8CanonicalColumns.DueAt,
  61. StatusColumn = S8CanonicalColumns.Status,
  62. // 去重身份 = po_no + '#' + po_line,由 Provider 拼好后放在 source_object_id 列。
  63. // 真库证据:dwd_supplier_delivery 全表 39854 行 / 4 租户 / 97 个快照,
  64. // (tenant_id, stat_date, po_no, po_line) 重复组为 0。
  65. SourceObjectIdColumn = S8CanonicalColumns.SourceObjectId,
  66. RelatedObjectCodeColumn = S8CanonicalColumns.RelatedObjectCode,
  67. // status 域 = COMPLETED / DELAYED / OPEN / CLOSED / CANCELLED / PARTIAL
  68. // (三个写入方合并取证:S3MdpSyncTransformService / DeliveryScheduleService /
  69. // ProcessOutsourceOrderService)。
  70. // 下列三个视为已完成;DELAYED / OPEN / PARTIAL 均可命中。
  71. CompletedStates = new[] { "COMPLETED", "CLOSED", "CANCELLED" }
  72. },
  73. Parameters = new S8RuleParameterPolicy
  74. {
  75. PollIntervalSecondsDefault = 300,
  76. TriggerCountRequiredDefault = 1,
  77. // 交期类异常抖动主要来自快照切换,连续两次未命中才判恢复。
  78. RecoverCountRequiredDefault = 2,
  79. GraceMinutesDefault = 0,
  80. SeverityDefault = S8SeverityCode.Serious,
  81. AllowsDepartmentDefaults = true,
  82. // S8-RULE-READINESS-1:本规则的数据集 dwd_supplier_delivery **没有部门列**,
  83. // 命中行带不出 occurrence / responsible,建单时部门只能来自这两个运行参数。
  84. // 不配就是「启用成功、调度成功、异常一条建不出来」——各层都报成功的静默失败。
  85. // 故声明为启用前置条件,由 S8RuleReadinessGate 在三条执行入口统一拦截。
  86. RequiresDepartmentDefaultsForEnable = true,
  87. // S8-RESPONSIBILITY-POOL-1:三条责任判据显式化。
  88. //
  89. // 处理池一条是**把既有行为原样写出来**:拆分前它被写在部门分支之后,
  90. // 对本规则本就生效;对其余规则本就被跳过。此处置 true 后行为不变。
  91. //
  92. // 复核与升级是本批新增的启用前置:本规则的异常走完整闭环
  93. // (认领 → 处理 → 提交复核 → 通过/退回),且参与 SLA 超时升级。
  94. // 少配任何一个池,对应环节都会在运行时静默断掉 ——
  95. // 复核候选为空则单据卡在 IN_PROGRESS,升级收件人为空则超时无人知晓。
  96. RequiresHandlerPoolForEnable = true,
  97. RequiresVerification = true,
  98. SupportsTimeoutEscalation = true
  99. }
  100. }
  101. };
  102. }