From 24a15126d0fbb3acf269eb7aa952caa0d2da69be Mon Sep 17 00:00:00 2001 From: duxingchen Date: Tue, 22 Sep 2026 13:14:24 +0800 Subject: [PATCH] =?UTF-8?q?fix(webhook):=20=E5=BF=BD=E7=95=A5=E5=8E=9F?= =?UTF-8?q?=E5=9B=A0=E4=B8=8E=20IRIS=20=E5=AE=9E=E4=BE=8B=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E4=B8=BA=20ignored=5Fcompany?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IRIS 侧用的是 ignored_company,LICA 侧我原先写的是 org_mismatch —— MOM 不解析这个字段,但排查时两边日志要对着看,字段名不一致会白白浪费时间。 统一成 IRIS 已在用的 ignored_company(IRIS 在线上,不该为这点小事动它)。 IRIS 侧实测建议里说的「语法编译检查证明不了行为」是对的,我这边也是按 真实载荷逐个验的。 顺带把约定写进 AGENTS.md —— 包括「两边规则刻意相反、不要统一」这条, 很容易被后来者顺手改掉。 实测: LICA -> {"ok":true,"matched":false} (无 reason = 通过校验) IRIS/空串/缺失 -> {"ok":true,"matched":false,"reason":"ignored_company"} outbound 同规则 --- AGENTS.md | 20 ++++++++++++++++++++ backend/app/api/v1/endpoints/webhooks.py | 7 +++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 31791ce..b7232fe 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -92,6 +92,26 @@ - 统计接口(dashboard/analytics/screen 共 20 个路由)**已从匿名可访问变为需登录** 并受范围过滤。注意:大屏挂满 7 天后 refresh token 过期会跳登录页。 +- **MOM 回执的部门校验**(`endpoints/webhooks.py`,2026-09 新增) + MOM 同时对接 IRIS 与 LICA 两个 Track 实例,按载荷里的 `company_name` 分流。 + 本实例(LICA)采取**严格白名单**: + + | `company_name` | 本实例行为 | + |---|---| + | `"LICA"`(strip 后) | 正常处理 | + | 空白 / 缺失 / `"IRIS"` / 未知值 | 忽略,返回 `reason: "ignored_company"` | + + ⚠️ **这与 IRIS 实例的策略刻意相反,不要"统一"**: + IRIS 对空白值要**放行** —— MOM 判定不出公司时会回落到指向 IRIS 的扁平配置, + 不收就彻底丢了;LICA 没有兜底角色,空白值本就该由 IRIS 兜。 + LICA 的原则是**宁可漏、不可误收**:误收会改掉别的部门的设备状态。 + + ⚠️ 拦截时返回 **200 + `matched=False`** 而非 4xx —— 与"未命中"保持同一契约, + 避免 MOM 侧当成故障去重试。`reason: "ignored_company"` 这个取值与 IRIS 实例 + 保持一致(MOM 不解析它,但排查时两边日志要对着看)。 + + 排查口径:**有 `reason` = 被部门校验拦下;无 `reason` = 通过了校验、只是产品没匹配上。** + ## 本地起环境(关键,踩过的坑都在这) 1. **本机没有 Postgres 时需要先装**(容器内 `sudo` 可用): diff --git a/backend/app/api/v1/endpoints/webhooks.py b/backend/app/api/v1/endpoints/webhooks.py index 6d42b1b..f55c674 100644 --- a/backend/app/api/v1/endpoints/webhooks.py +++ b/backend/app/api/v1/endpoints/webhooks.py @@ -176,8 +176,11 @@ async def mom_inbound_webhook( # 旧配置直接打这个接口,也不会把别的部门的设备状态改掉。 # 返回 200 + matched=False 而非 4xx —— 与「未命中」保持同一契约, # 避免 MOM 侧把它当成故障去重试。 + # + # ⚠️ reason 的取值 "ignored_company" 与 IRIS 实例保持一致 —— MOM 侧不解析 + # 它,但排查时两边日志对着看,字段名不一致会白白浪费时间。 if not _belongs_to_this_org(payload.company_name): - return {"ok": True, "matched": False, "reason": "org_mismatch"} + return {"ok": True, "matched": False, "reason": "ignored_company"} explicit_revoke = _is_outbound_revoke(payload) @@ -390,7 +393,7 @@ async def mom_outbound_webhook( # ── 部门校验(纵深防御)—— 同 mom_inbound,理由见该处注释 if not _belongs_to_this_org(payload.company_name): - return {"ok": True, "matched": False, "reason": "org_mismatch"} + return {"ok": True, "matched": False, "reason": "ignored_company"} # ── 按 serial_number(优先)或 sku 匹配"在仓库/已入库"的产品 ── product = None