Commit Graph

5 Commits

Author SHA1 Message Date
700486d5d5 fix(webhook): 白名单判定容忍大小写,堵住「两边都丢弃」的静默黑洞
_belongs_to_this_org 此前是 (company_name or "").strip() == ORG_DEPARTMENT ——
只 strip 不 upper,而 IRIS 的 _is_foreign_company 做了 .upper()。两边口径不一致
的后果:

  company_name = "lica" / "Lica"
    → IRIS:.upper() 命中外来名单,拒掉
    → LICA:不等于 "LICA",白名单拒掉
    → 双方都回 200,消息彻底静默丢失

这正是本文档最该防的那种「漏」。改为 .strip().upper() 与 IRIS 对齐。

「严格」指的是白名单语义(只有本部门才收),不是逐字节比对 —— 空白值依然被
拒、未知部门名依然被拒,只是不再因为大小写写法白白丢消息。

实测 11 种取值:修复前 "lica" / "Lica" 是两个黑洞(两边都丢弃),修复后 0 个;
空白 / 全空格 / "IRIS" / "UNKNOWN" / None 仍然全部忽略,严格性未放松。

顺带修正 outbound_type 的取值注释:MOM 的 TransOutbound.outbound_type 实际是
SALES / USE / PRODUCTION(见 projects inventory-backend/app/models/outbound.py
第 136 行),原注释漏了 USE。
2026-09-22 13:46:12 +08:00
9778b8e4b2 feat(audit): MOM 回调归因到实际操作人,不再显示「未认证」
外部回调走 X-API-Key 鉴权、没有 JWT,JWT 依赖不执行,审计中间件读到的
request.state.audit_user 永远是空 —— 操作审计里就出现一堆没有归属的
「外部系统对接」记录,看不出是谁扫的码。

MOM 载荷里本来就带着实际操作人(operator,即 MOM 侧扫码的那位),写进
request.state 即可让审计归因到人;顺手解析中文姓名(查不到也不影响审计,
前端会回退显示账号)。

⚠️ 调用位置必须在 X-API-Key 校验【之后】:密钥不对说明载荷本身就不可信,
   此时把 operator 写进审计等于允许伪造人。放在部门校验之后同样有意为之 ——
   被拦下的外来消息不该留下任何归属痕迹。

取不到操作人时写 "MOM系统" 而非留空:「MOM系统」至少说明这是一次机器回调,
比继续显示「未认证」(读起来像"一个匿名的人")更准确。

本函数与 IRIS 实例(~/track)代码体逐行一致,只差 docstring —— 两侧审计口径
必须一样,否则排查时日志对不上。约定已记入 AGENTS.md。

实测:MOM 回调后审计记录显示实际操作人姓名;无 operator 时显示「MOM系统」。
2026-09-22 13:45:00 +08:00
24a15126d0 fix(webhook): 忽略原因与 IRIS 实例统一为 ignored_company
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 同规则
2026-09-22 13:14:24 +08:00
a68b2bbca0 feat(webhook): 部门校验 —— 只认 company_name == "LICA"
MOM 现在同时对接 IRIS 与 LICA 两个 Track 实例,按载荷里的 company_name 分流。
本实例采取**严格白名单**:
  · company_name == "LICA"(strip 后)→ 正常处理
  · 空白 / 缺失 / "IRIS" / 未知值      → 忽略

⚠️ 与 IRIS 实例的策略**刻意相反**,别"顺手统一成一样":
   IRIS 对空白值要放行 —— MOM 判定不出公司时会回落到指向 IRIS 的扁平配置,
   不收就彻底丢了。
   LICA 没有兜底角色,空白值只可能来自「MOM 没判定出公司」,那本就该由 IRIS 兜。
   **宁可漏,不可误收** —— 误收会把别的部门的设备状态改掉,那是数据污染,
   比漏一条通知严重得多。

实现:
  · MomInboundPayload / MomOutboundPayload 补 company_name 字段
    (不补的话会被 Pydantic 静默丢弃,校验无从谈起)
  · 新增 _belongs_to_this_org(),在**鉴权之后、匹配产品之前**拦截
  · 拦截时返回 200 + matched=False + reason=org_mismatch —— 与「未命中」保持
    同一契约,避免 MOM 侧把它当成故障去重试
  · 顺带补上 outbound 一直在发、但此前被丢弃的 outbound_type 字段

实测:
  · 7 种 company_name 取值全部符合预期
    ("LICA" / "LICA " 通过;"IRIS" / "" / 缺失 / null / "UNKNOWN" 全部忽略)
  · 无 X-API-Key 仍返回 401(鉴权没有被绕过)
  · 真实闭环:LICA 入库回调 → 产品「待仓库收货」→「已入库」
  · 对照:对同一产品发 IRIS 的回调 → 状态纹丝不动 
  · 测试数据已还原为原始值
2026-09-22 13:09:22 +08:00
3286a11bc7 chore: fork from IRIS track 供 LICA 部门独立运行
- 复制来源: /home/yueli/track @ 192c8ee (feature/ai-audit-update)
- 组织隔离目标: LICA
- 端口规划: 前端 8030 / 后端 8031 / 数据库 8032
- 已排除 deploy.sh、deploy_full.sh、docker-compose.prod.yml(IRIS 生产发布脚本)
- 已排除工作区未提交改动,取干净的 192c8ee 状态
2026-09-21 15:56:52 +08:00