直接基于上一轮建好的 PTP 本体给出SWRL 规则驱动的“三单匹配异常检测” 完整写法分「异常类型 → 规则 → 业务语义 → 推理结果」四栏对照方便你直接搬进 Protege / MetaERP 规则引擎。0. 前置用到的属性速查属性含义orderQty(POLine, Q)订单行数量receivedQty(GRN, Q)实收数量invoiceQty(Inv, Q)发票数量unitPrice(POLine, P)订单单价invoiceAmount(Inv, A)发票不含税金额receivesAgainst(GRN, POLine)收货→订单行invoicesAgainst(Inv, PO)发票→POhasPOLine(PO, POLine)PO→行matchPO/MatchGRN/MatchInv匹配事务绑定qcResult(GRN, PASS/REJECT)质检结果约定SWRL 里用swrlb:内置函数greaterThan、subtract、multiply、abs…1. 异常一数量不一致超收 / 少收 / 票实不符超收3% 允差POLine(?l), orderQty(?l, ?qo), GRN(?g), receivesAgainst(?g, ?l), receivedQty(?g, ?qr), swrlb:multiply(?tol, ?qo, 1.03), swrlb:greaterThan(?qr, ?tol) - OverReceiptException(?g), exceptionReason(?g, 超收超3%)发票数量 ≠ 实收数量GRN(?g), receivedQty(?g, ?qr), Inv(?i), invoicesAgainst(?i, ?po), hasPOLine(?po, ?l), receivesAgainst(?g, ?l), invoiceQty(?i, ?qi), swrlb:notEqual(?qi, ?qr) - QtyMismatchException(?i), exceptionReason(?i, 票实数量不符)2. 异常二金额异常价格漂移 / 含税倒挂发票单价 订单单价POLine(?l), unitPrice(?l, ?up), Inv(?i), invoicesAgainst(?i,?po), hasPOLine(?po,?l), invoiceQty(?i,?qi), invoiceAmount(?i,?amt), swrlb:divide(?invUnit, ?amt, ?qi), swrlb:greaterThan(?invUnit, ?up) - PriceDriftException(?i), exceptionReason(?i, 发票单价高于订单价)含税金额逻辑校验发票税额/税率异常Inv(?i), invoiceAmount(?i,?net), vatAmount(?i,?vat), swrlb:divide(?rate, ?vat, ?net), swrlb:greaterThan(?rate, 0.13) - TaxRateException(?i), exceptionReason(?i, 税率超13%或倒算异常)3. 异常三单据缺失缺收 / 缺票 / 无匹配事务有PO无收货PO(?po), hasPOLine(?po,?l), not ReceiptOfLine(?g,?l) - MissingGRNException(?po), exceptionReason(?po, 已审批PO无收货记录)SWRL 无原生not exists实际写法用negation-as-failureNAFReceivesAgainst(?g,?l) ⊥有收货无发票GRN(?g), receivesAgainst(?g,?l), PO(?po), hasPOLine(?po,?l), not InvoicesAgainst(?i,?po) - MissingInvoiceException(?g), exceptionReason(?g, 已收货未收到发票)三单齐但无匹配事务PO(?po), GRN(?g), Inv(?i), receivesAgainst(?g,?l), hasPOLine(?po,?l), invoicesAgainst(?i,?po), not (ThreeWayMatch(?m) ^ matchPO(?m,?po) ^ matchGRN(?m,?g) ^ matchInv(?m,?i)) - UnmatchedTripleException(?po), exceptionReason(?po, 三单齐但未建匹配)4. 异常四质检未过却已匹配合规硬拦截GRN(?g), qcResult(?g, REJECT), ThreeWayMatch(?m), matchGRN(?m,?g), matchStatus(?m, MATCHED) - ComplianceBlockException(?m), exceptionSeverity(?m, FATAL), exceptionReason(?m, 不合格收货被误匹配)5. 异常五跨期 / 时间异常PO(?po), poDate(?po,?d1), Inv(?i), invoicesAgainst(?i,?po), invoiceDate(?i,?d2), swrlb:subtract(?days, ?d2, ?d1), swrlb:greaterThan(?days, 90) - CrossPeriodException(?i), exceptionReason(?i, 发票晚于PO超90天)6. 汇总异常等级本体推荐补一层erp:MatchException a owl:Class . erp:OverReceiptException rdfs:subClassOf erp:MatchException ; erp:severity WARN. erp:ComplianceBlockException rdfs:subClassOf erp:MatchException ; erp:severity FATAL. erp:PriceDriftException rdfs:subClassOf erp:MatchException ; erp:severity WARN.推理机Pellet / HermiT跑完后可自动得到FATAL→ 阻断 AP 生成MetaERP 挂起WARN→ 进人工稽核工作台7. 在 MetaERP 里的落地组合实践建议层技术职责语义层OWL SWRL定义“什么算异常”校验层SHACL做字段级硬约束PO必须审批、金额≥0推理层Pellet/HermiT离线 规则编译为Drools在线SWRL→Drools 转换跑在交易态执行层MetaERP PTP 引擎命中FATAL→挂起APWARN→标记RISK_FLAG可观测异常实例写入MATCH_EXCEPTION_LOG供稽核/大模型追问 经验做法SWRL 只写“异常判定”不写“动作”动作挂起/告警交给 Drools/状态机避免本体变“流程脚本”。8. 一个端到端推理示例PO 行50台 × 38500GRN实收 52 台QCPASS发票52台 × 39000推理链52 50×1.0351.5→OverReceiptException(WARN)发票单价39000 38500 →PriceDriftException(WARN)数量齐但超收 → 匹配事务若强行建 MATCHED → 触发人工复核标记最终 AP不自动生成进入「采购异常工作台」。一句话收束SWRL 做三单异常检测的本质把“超收3%、票实不符、质检未过却匹配”写成可推理的语义命题让“异常”从代码 if-else 变成本体里能被推理机“算出来”的事实。