| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- using Admin.NET.Plugin.AiDOP.Infrastructure.S8;
- using Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess;
- using Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.Providers;
- namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules.Definitions;
- /// <summary>
- /// S8-RULE02-ORDER-DELIVERY-WARNING-1:订单交付延期<b>预警</b>规则定义。
- ///
- /// <para><b>与 Rule 01 的本质区别</b>:Rule 01(采购交期延误)判的是「到期日已过而事情没做完」——
- /// 事后。本规则判的是「客户交期还没到,但系统自己算出来的建议交期已经晚于它」——事前。
- /// 前者用 TIMEOUT(时间轴过了没有),后者用 OUT_OF_RANGE(两个日期之差越了界)。</para>
- ///
- /// <para><b>为什么是 OUT_OF_RANGE 而不是新造一个 evaluator</b>:本规则的判据展开后就是
- /// 「<c>measured_value</c>(延期天数)> 上限 0」,正是 OUT_OF_RANGE 的既有语义。
- /// 仓内此前没有任何 OUT_OF_RANGE 规则定义(见 <see cref="S8OutOfRangeRuleEvaluator"/> 类注释),
- /// <b>本规则是第一条</b>;evaluator 本身一行未改。</para>
- /// </summary>
- public sealed class S8OrderDeliveryRuleDefinitions : IS8RuleDefinitionSource
- {
- /// <summary>
- /// Rule 02 编码。命名沿用 Rule 01 的 <c>RULE_{模块}_{业务}</c> 格式。
- /// <b>参与 dedup_key,全链身份</b>:任何地方引用必须用本常量,不写字面量。
- /// </summary>
- public const string OrderDeliveryDelayWarningCode = "RULE_S1_ORDER_DELIVERY_DELAY_WARNING";
- /// <summary>
- /// 建单使用的异常类型。<b>已存在于种子</b>
- /// (<c>S8ExceptionTypeSeedData</c>:订单交付延期预警 / scene S1 / SLA 240 分钟 / 默认 FOLLOW),
- /// 因此本批<b>不新建异常类型、不写 migration</b>。
- /// </summary>
- public const string OrderDeliveryDelayWarningExceptionTypeCode = "ORDER_DELIVERY_DELAY_WARNING";
- /// <summary>
- /// 源对象类型。<b>参与 dedup_key</b>。
- /// <para>是「订单行」而不是「订单」:交期与数量都挂在行上,同一张订单的不同行交期可以不同。
- /// 用订单号做身份会让同单多行只留下一条异常,其余行的风险被静默吞掉。</para>
- /// </summary>
- public const string SalesOrderLineObjectType = "SALES_ORDER_LINE";
- public IEnumerable<S8RuleDefinition> GetDefinitions() => new[]
- {
- new S8RuleDefinition
- {
- RuleCode = OrderDeliveryDelayWarningCode,
- DisplayName = "订单交付延期预警",
- Description =
- "订单行的系统建议交期晚于客户要求交期,且客户交期尚未到达时提报预警。" +
- "延期天数由系统建议交期与客户要求交期相减得到,不做任何推算;" +
- "尚未做过评审资源检查(没有系统建议交期)的订单行不参与判定。",
- DatasetCode = S8OrderDeliveryDatasetDefinitions.OrderDeliveryRiskCode,
- RuleType = S8OutOfRangeRuleEvaluator.RuleTypeCode,
- RuleMechanism = "DATE",
- SourceObjectType = SalesOrderLineObjectType,
- SceneCode = S8ModuleCode.S1,
- StageCode = S8ModuleCode.S1,
- // 风险在「评审/排产/测算」环节产生:系统建议交期正是该环节的资源检查结果。
- OrderFlowCode = "ORDER_REVIEW_PLAN_CALC",
- ExceptionTypeCode = OrderDeliveryDelayWarningExceptionTypeCode,
- // 面向业务管理员的只读说明。刻意不出现表名 / 列名 / DATEDIFF ——
- // 配置页要回答的是「这条规则按什么判」,不是「它怎么实现的」。
- JudgementSummary =
- "系统按产能测算出的建议交期晚于客户要求的交期,且客户交期尚未到达时预警;"
- + "晚出来的天数即预警的严重程度依据。"
- + "订单已关闭、或该订单行还没有系统建议交期时,不参与判定。",
- DedupIdentitySummary = "销售订单号 + 订单行号",
- // ────────────────────────────────────────────────────────────────
- // 判定语义。
- //
- // measured_value = 系统建议交期 - 客户要求交期(天)。由 Provider 用
- // DATEDIFF 算好后放在 canonical 的 measured_value 列,evaluator 只做越界比较。
- //
- // 上限 0 的含义:只要建议交期比要求交期晚哪怕一天就预警。
- // 这不是拍脑袋定的阈值 —— 0 是「不延期」这件事本身的定义。
- // 真正可调的宽容度留给 ToleranceAbs(见下),管理员要"晚 3 天以内不报"时
- // 改的是容差,而不是重新定义什么叫延期。
- //
- // 不设 LowerBound:建议交期<b>早于</b>要求交期是好事,不构成异常。
- // ────────────────────────────────────────────────────────────────
- OutOfRange = new S8OutOfRangeSemantics
- {
- MeasuredValueColumn = S8CanonicalColumns.MeasuredValue,
- // 去重身份 = bill_no + '#' + entry_seq,由 Provider 拼好放进 source_object_id。
- // 真库证据:crm_seorderentry 390 行,(bill_no, entry_seq) 重复组为 0。
- SourceObjectIdColumn = S8CanonicalColumns.SourceObjectId,
- RelatedObjectCodeColumn = S8CanonicalColumns.RelatedObjectCode,
- UpperBound = 0m,
- // ToleranceAbs = 0:本批没有任何业务依据说「晚几天以内可以不报」。
- // 凭空填一个 3 会在没有拍板的情况下把一批真实风险静默过滤掉。
- // 需要宽容度时由业务给出天数,再改这一处。
- ToleranceAbs = 0m,
- // ToleranceRatio 对本规则无意义:上限为 0 时 evaluator 会跳过比例判断
- // (避免除零),写非 0 值只会造成"配了但不生效"的错觉。
- ToleranceRatio = 0m
- },
- Parameters = new S8RuleParameterPolicy
- {
- // 数据是「天」粒度,且只在订单评审时变化。5 分钟一轮(Rule 01 的默认)
- // 对本规则没有任何信息增益,只是徒增数据库负载。
- PollIntervalSecondsDefault = 3600,
- TriggerCountRequiredDefault = 1,
- // 订单重新评审时系统建议交期会被改写,期间可能短暂缺失。
- // 连续两轮不命中才判恢复,避免一次重评审把预警冲掉又建回来。
- RecoverCountRequiredDefault = 2,
- // 宽限分钟是 TIMEOUT 的概念,OUT_OF_RANGE evaluator 根本不读它。
- // 保持 0,不给管理员一个改了不生效的旋钮。
- GraceMinutesDefault = 0,
- // 预警不是事故:客户交期还没到,还有处置窗口。
- // 与异常类型自身的默认严重度(FOLLOW)一致。
- SeverityDefault = S8SeverityCode.Follow,
- AllowsDepartmentDefaults = true,
- // S8-RULE-READINESS-1:本规则的数据集<b>没有部门列</b> ——
- // crm_seorderentry / crm_seorder 都不带发生部门或责任部门,
- // 命中行带不出 occurrence / responsible,建单时只能取这两个运行参数。
- // 不配就是「启用成功、调度成功、异常一条建不出来」的静默失败。
- RequiresDepartmentDefaultsForEnable = true,
- // 以下三条不是照抄 Rule 01,是按本规则异常的实际去向逐条确认的:
- //
- // · 处理池:异常带 source_rule_code,认领 / 派单走规则处理池。空池 =
- // 预警源源不断建出来、躺在列表里没人认领,而调度器每轮都报成功。
- //
- // · 复核池:S8VerifierEligibility 对「有来源规则」的异常,检验人候选<b>只</b>取
- // 该规则的复核池。空池 → 候选列表为空 → 处理人点不了「提交复核」,
- // 单据永远停在 IN_PROGRESS。
- //
- // · 升级池:本异常类型的 SLA 为 240 分钟 → 建单即写 sla_deadline →
- // 必然进入 S8TimeoutAutoEscalationService 的扫描范围。空池时该服务只会记一行
- // reason=escalation_pool_empty 然后跳过,超时无人知晓。
- RequiresHandlerPoolForEnable = true,
- RequiresVerification = true,
- SupportsTimeoutEscalation = true
- }
- }
- };
- }
|