返回博客

ERP 对接踩坑:organizationId 不是 counterCode,静默成功却写入 null

对接某第三方 POS / ERP 系统的会员注册接口时,我们写了一个透传代理:前端把门店柜台编码(counterCode,形如 200611)连同用户信息一起发过来,后端原样转发给 ERP。联调阶段一切正常,ERP 返回 code:10000,日志全绿。

上线两个月后,运营反馈导出的会员数据里有大批"已注册但无门店归属"——ERP 那边 belongCounterCode 字段全是 null

现象

  • 本地数据库:会员记录完整,store_id 有值
  • ERP 侧查询:同一批会员存在,但 belongCounterCode = null,门店绑定缺失
  • 翻调用日志:每次 API 调用都是 code:10000 success,没有任何报错
  • 问题时间跨度长达数月,95+ 条记录受影响

根因

ERP 的注册/更新接口要求传两个字段:

| 字段 | 含义 | 示例值 | |------|------|--------| | organizationId | POS 内部数字型组织 ID | 1234567890123 | | counterCodeBelonging | 柜台编码(人类可读) | 200611 |

我们的透传代码把 counterCode 同时赋给了两个字段:

// 错误写法:把柜台编码当成组织 ID 传过去
$pos->updateNew([
    'mobile'               => $req['mobile'],
    'organizationId'       => $req['counterCode'],   // ❌ 传了 "200611"
    'counterCodeBelonging' => $req['counterCode'],
]);

ERP 接到 organizationId = "200611" 时,既不报错也不拒绝,它内部大概做了一次"找不到对应组织就忽略"的处理,然后返回成功——但门店绑定字段自然没有写入。

坑:字段名相似,但语义完全不同,ERP 对错误值静默放行

organizationId 必须是 ERP 内部分配的数字型组织主键(长整型字符串),和业务侧自定义的柜台编码(短数字码)是两个完全不同的空间。

ERP 不做类型/范围校验,传入任何字符串都返回 code:10000。只有后续查询时才会发现门店绑定是空的。这类"静默成功 + 延迟失效"是第三方接口对接中最难排查的坑之一。

正解

在本地数据库维护一张映射缓存,字段可命名为 offline_org_id,在同步门店信息时顺手写入:counterCode → organizationId。每次调用 ERP 写接口前先查这张缓存,缓存 miss 则调用 ERP 的组织查询接口(如 searchOrganizationForThirdParty)拉取并回写,之后再重试写操作。

// 正确写法:先反查 orgId,缺失则主动拉取
$orgId = $this->getOrgIdByCounterCode($counterCode);
if (!$orgId) {
    // 缺失不能静默降级——主动报错,强制初始化
    throw new BusinessException("门店组织映射未初始化,counter_code={$counterCode}");
}
 
$pos->updateNew([
    'mobile'               => $req['mobile'],
    'organizationId'       => $orgId,        // ✅ ERP 内部 orgId
    'counterCodeBelonging' => $counterCode,  // ✅ 柜台编码单独传
]);

对已有系统做快速诊断,可以跑一条 SQL 确认缓存完整性:

SELECT counter_code, offline_org_id
FROM store_config
WHERE del_flag = 0
  AND offline_org_id IS NULL
LIMIT 50;

如果出现结果,说明映射缓存没有被初始化,是根因,不是 ERP API 的行为问题。

正解:在本地缓存映射,fail-closed 拒绝未知 orgId

维护 counterCode → orgId 本地缓存;写接口调用前先查缓存,miss 则拉取并回写;永远不要把 counterCode 直接当 organizationId 传入,也不要在映射未知时静默降级(应显式报错)。

一句话外卖

对接第三方 ERP/POS 时,"人类可读的业务编码"和"系统内部主键"往往共存于同一接口,名字越像、越要警惕——它们的验证规则不同,错传不会立即报错,只会在下游查询时悄悄爆炸。