电子制造场景里的制造执行系统很少孤立运行,上游要接 ERP 的工单与物料,下游要对接仓储的出入库,旁边还有质量判定与设备采集。这类集成的失效很少以报错的形式出现——真正麻烦的是不报错:单据少了一条、库存多了一批、报工数字被覆盖成昨天的值。等到月末对账才发现,中间几十天的数据都已经错了。
本文不从功能清单讲起,只讨论集成契约里四个决定成败的工程点:字段归属怎么划、幂等键由谁生成、乱序靠什么排序、失败之后怎么补偿。技术点与具体厂商无关,可直接套到自研或二开项目上。
一、先划清字段归属,再谈接口
集成出问题的项目,多数在开工前没有明确一件事:某个字段到底以谁为准。工单号 ERP 改了,MES 本地也改过,两边都"有数据",同步时就是互相覆盖。
稳妥的做法是先做一张字段归属表,一个字段只允许一个写入方,其余系统一律只读。
这张表定下来之后,接口的职责就清楚了:同步接口只负责传输,不负责仲裁。凡是需要"两边比一比取一个"的字段,都是归属没划清留下的债。
二、幂等:键由谁生成,决定了会不会重复
集成里常见的重复提交,来源不是业务人员点了两次,而是传输层的重试。网关超时重发、消息中间件至少一次投递、定时任务补偿重跑,任何一条路径都可能把同一笔事务送两遍。
要让重试安全,幂等键必须在业务发生点生成,由源系统随单据一起发出,而不是由接收方或网关生成——网关生成的键在每次重试时都会变,等于没做幂等。
键的构成建议带上业务语义,而不是单纯一个流水号:
- 单据类型 + 源单号 + 行号(适用于明细级同步)
- 单据类型 + 源单号 + 版本号(适用于整单覆盖式同步)
设备事件用:设备编码 + 事件时间戳 + 设备侧序号
落库时不要先查后插。查询与插入之间的窗口足够让并发请求穿过去,正确做法是让数据库约束来兜底,冲突时静默忽略而不是报错回滚——一条重复数据回滚掉整批,补传会一直卡在同一处。
把这套写入语义拆成可对照的约定,比贴一段语句更容易在不同数据库之间复用:
这里有两个容易被忽略的点。其一,先查后插在并发下不可靠:查询与插入之间的窗口足够让两条相同请求都通过检查,结果落两条记录。其二,冲突时不能报错回滚:一条重复数据把整批补传打断,重试会一次次卡在同一条上,反而扩大缺口。用约束做判断、用静默忽略做收敛,重试才是安全的。
静默忽略的代价是接收方拿不到"这是重复件"的返回值,所以同步日志表不能省。排障时靠它区分"源系统没发"与"发了但被幂等拦下",这两种情况的责任方完全不同。
三、乱序:排序依据用版本号,不要用时间戳
跨系统同步几乎一定会乱序。ERP 改一版工单,消息 A 先发、消息 B 后发,到达顺序却可能颠倒;设备侧时钟没校准,时间戳甚至可能倒退。
用时间戳排序的项目,往往会在某次时钟漂移后开始丢更新:新数据因为时间戳更小被判为旧数据丢弃。
可行做法是给每个业务对象维护一个由源系统分配的单调递增版本号,接收方只在"来件版本号大于当前版本号"时才更新:
接收方拿到一条消息后,按下面这张表分情况处理:

实现上要让"对象标识"和"版本号大于当前值"两个条件在同一次原子写入里一起判断,不能先读出来在应用层比完再写回去——读出与写回之间,另一条消息可能已经把版本推进了。
写入返回"没有影响到任何一行"时,只可能是两种情况:对象不存在,或来件版本更旧。这两种情况必须分开处理——前者需要补建,后者记丢弃日志并计数。把它们混在一起忽略掉,是很多"数据莫名不变"问题的根源。
时间戳仍然有用,但只用于审计与排障,不参与顺序决策。
四、状态机:只允许单向推进,回退走冲销
单据状态的同步比字段同步更容易出错,因为它隐含了顺序假设。工单状态如果允许任意赋值,一条迟到的"生产中"消息就能把已经"完工"的工单改回去。
契约上应当约定:状态只允许沿既定序列推进,非法转移一律拒绝并记审计,不做"尽力而为"的兼容。
- 序列示例:已下达 → 已开工 → 生产中 → 已完工 → 已关闭
- 拒绝规则:来件状态在序列中的位置小于等于当前状态,即视为过期消息,丢弃并记日志
- 回退处理:业务确实需要回退时,走冲销单据(反向事务)生成新记录,而不是把状态改回去
冲销而非改状态,好处是历史可追、对账可核。代价是单据数量变多,报表侧要按"有效事务"过滤,这一点需要在数据模型设计阶段就定下来,不要等报表做完了再补。
五、补偿:把失败分成两类,只有一类值得重试
重试策略做错,通常是把所有失败当成同一类。实际应至少分成两类: - 瞬时失败:连接超时、连接池耗尽、目标端短时不可用。这类值得指数退避重试,退避上限与重试次数要有明确约定,不能无限重试。
- 业务拒绝:料号已停用、工单已关闭、数量超出容差。这类重试多少次都不会成功,应立即转入异常队列,由人工处置并回写处理结果。
超过约定次数的消息进入死信表,按业务日期分组做日对账:源系统当日生成的单据数、接收方当日入库的单据数、被幂等拦下的重复数、进入死信的数量,这四个数字必须能相互解释。差额解释不清的那一天,就是要去查的那一天。
对账不要只对总数。按业务日期分组是关键——跨零点的报工、跨班次交接的单据,很容易落在自然日与业务日不同的两侧,只对总数会让两天的错误互相抵消。
六、上线前该跑的四类测试
前三类偏接口层,第四类偏业务层,也是容易被漏掉的一类——它检验的不是实现细节,而是"业务日期"这个口径在两侧是否同一套定义。
以上为工程实践总结,不涉及厂商产品评价,也不构成选型建议。