ERP与MES批处理文件接口设计:幂等、排他控制与参数化 简介这份《MES 接口设计说明书》记录了 SAP R/3 模板 MELEBUS-BMAS 与制造执行系统MES之间的接口设计面向从事 ERP 与 MES 对接的顾问、ABAP 开发及系统集成人员可作为接口雏形搭建与验证阶段的设计参照。文档为日文原版按系统规格、文件接口规格、程序规格逐层展开内容包括制造指图数据抽出与实绩数据计上两条主线的处理逻辑、R/3 侧与 MES 侧接口定义、系统间排他控制与同步处理、典型业务流程图并附有 ZMAF001、ZMAF005 两段程序规格说明和参数设定章节可对照理解接口文件清单、字段结构与批处理运行方式。资源包共 1 个 doc 文件约 178KB篇幅紧凑但目录层次完整便于按章节检索。目前已有 2667 人学习下载适合需要梳理 R/3 与 MES 数据流向、参考成熟接口样例的读者查阅。1. 需求先立住一天两批的文本文件接口为什么比实时推送更合适接到「做一套 MES 接口」的需求多数人第一反应是消息队列、实时推送、双向回调。这套 MES 接口方案偏偏反着走ERP 与 MES 之间只交换文本文件靠一个共享目录加两个锁文件跑批处理一天 1~2 次。听起来土但它要解决的问题很具体——某工厂在模板项目原型阶段需要在两三周内把「制造指图下发」和「实绩回计」两条链路跑通要求能验证、能查、能回滚而不是先扛住每秒几千条报文。这份 MES 接口设计说明书就是围绕这个目标写的一套可复用的接口雏型方案覆盖文件格式、字段定义、抽取条件、计上规则、排他控制和参数化配置。它适合两类人——正在做 ERP 与 MES 对接课程设计、毕业设计的同学可以照着这套结构交一份完整的东西以及在原型阶段要快速验证接口可行性的实施同学可以拿它当模板改成自己工厂的版本。2. 系统边界与前提条件两条链路、四类约束和一堆业务假设接口说明书翻车很少翻在代码上多半翻在开头那两页「适用范围」和「前提条件」被跳过了。写完文件格式就直接开写程序等联调时才发现对方的 MES 是按另一套粒度汇总实绩的前面设计的东西全得推翻。所以这一章先把边界钉死。2.1 系统化对象范围两条链路、四个动作整套接口只做两件事ERP 把制造指图推下去MES 把实绩计回来。注意这不是四个系统是两条链路被切成了四个动作其中两个动作在 ERP 侧、两个在 MES 侧而这份说明书只交付 ERP 侧的两个。环节执行方输入输出制造指图抽取ERP 侧批处理已发行、未完成的指图及构成品目指图数据文件、构成数据文件、两类日志指图取入MES 侧上述两个文件MES 内部指图处理完后删除接口文件实绩输出MES 侧现场投入实绩、完成实绩投入实绩文件、完成实绩文件实绩计上ERP 侧批处理上述两个实绩文件指图上的作业实绩处理完后删除接口文件这张表最值钱的一列是「执行方」。原型阶段最容易犯的错是把 MES 侧的取入逻辑也写进自己的交付范围结果两边都在改同一份文件、两边都在删文件联调时谁也说不清是谁删的。提示说明书里必须显式写一句「MES 侧的系统规格与功能以假定为前提不构成对实际系统的定义」。这句话不是免责声明是给后面所有字段设计留退路——真出问题时至少能说清哪部分是假设、哪部分是承诺。2.2 前提条件逐条翻译成工程约束原始文档列的前提条件看着像例行公事其实每一条都对应一个具体的代码约束漏一条就要在联调现场补。第一条一个工厂内的 ERP 与 MES 连携。这条限定了接口文件里必须带工厂代码而且不做多工厂路由。第二条只支持会发行制造指图的生产方式流程型、无工单的产线不适用。第三条批处理方式、1~2 次/日的处理周期——这条决定了不需要考虑并发吞吐但要考虑批次间隔内的数据堆积抽取条件里的日期范围必须留出余量。第四条两侧操作系统限定在 Windows 与 Unix 系。真正要命的不是操作系统本身而是它带来的换行码差异Windows 是 CRLFUnix 是 LF。第五条把这条差异摆到了台面上——文本文件的格式换行码和逻辑路径都可变参指定。第六条接口文件不做代码转换作为透明数据原样交给对方。第五条和第六条合起来看是个很硬的设计约束既然不做编码转换那两侧就必须事先约定同一套字符集否则品目代码里的全角字符、连字符、空格会在传输后变形而这种错在日志里长得跟「数据不存在」一模一样排查起来非常费劲。我一般的做法是在参数文件里加一个字符集声明项抽取时校验每条记录能否被目标字符集无损表示不能的直接拦在发送侧并记错误日志——把问题留在自己这边比让对方去猜要好。前提条件落到代码里的约束单一工厂内连携文件记录必带工厂代码不做多工厂路由仅限发行制造指图的生产方式抽取条件必须校验指图存在且已发行批处理 1~2 次/日不做流控与背压但抽取日期范围要留缓冲天数Windows / Unix 双环境换行码、路径分隔符全部参数化共享目录、两侧访问同一文件必须有管理文件做互斥不能依赖文件系统锁文本文件、不做编码转换两侧字符集需事先约定发送侧做无损校验2.3 业务前提比技术前提更致命一对一、不支持的副作业、分割归谁技术前提写错了顶多报错业务前提写错了会导致整批数据静默地对不上账。几条硬约束制造指图与计划订单是一对一关系指图的创建单位是 ERP 里做工程计划的单位通常是「日 品目」级别一张指图可以包含多个作业但不支持副作业。最后这条尤其要在说明书里加粗因为副作业在现场是真实存在的一旦有人拿带副作业的指图来跑抽取程序必须明确报错而不是悄悄少输出一行。ERP 与 MES 的作业是一对一对应的。现场因为生产需要会把指图或作业拆开、合并这些动作归 MES 管理而且 MES 侧不在拆分后的单位上计实绩而是在内部汇总回原指图单位再计上。这条约定直接决定了实绩文件的粒度文件里的实绩永远是指图级的不是拆分单级的。还有两条容易被忽略的运营约定。一是指图发行之后的再发行、再印刷、变更、移动单位分割、现品票发行全部由 MES 侧管理ERP 侧在抽取之后基本不再改动指图。二是特急指图、返修指图通过指图类型来区分类型会随指图一起通知给 MES。这意味着指图类型不是一个装饰字段而是 MES 侧排产优先级的依据字段定义时不能省。最后一条抽取完成后 ERP 侧原则上不再修改指图。这条约束听着像甩锅实际上是整套方案能成立的基础它保证了「抽取即冻结」后续所有变更都在 MES 侧发生不会出现两侧同时改同一张指图、谁也不认谁的情况。3. 接口文件设计四类文件、一套唯一键、一条再送规则文件设计是整份说明书里唯一会被双方同时翻的部分写含糊了联调期就是无休止的扯皮。我的原则是字段能少不多但唯一键必须一次定死。3.1 文件清单与逻辑路径约定四类业务文件加上两类日志、两类管理文件、一份参数文件一共九类。数量不多但每一类的生成方和删除方必须写清楚不写清楚就会出现「我以为你会删」。文件方向内容生成方删除方指图数据文件ERP → MES指图头信息ERP 抽取MES 取入构成数据文件ERP → MES指图所需构成品目ERP 抽取MES 取入投入实绩文件MES → ERP投入品目与数量MESERP 计上完成实绩文件MES → ERP良品数、不良数、预定完成日MESERP 计上开始/结束日志双向每次批次的起止与汇总结果处理方按保存期限清理记录级日志双向逐条处理内容与错误处理方按保存期限清理指图处理管理文件双向指图链路互斥标志处理方处理方实绩处理管理文件双向实绩链路互斥标志处理方处理方参数文件共用路径、换行码、日志期限等实施方手工维护路径用逻辑路径物理挂载点由各环境自己映射。这样做的好处是同一份参数文件可以在测试环境和生产环境之间复制只改映射不改配置。换行码同理写进参数而不是写死在代码里Unix 环境用 LF、Windows 环境用 CRLF两侧各自读同一份参数。3.2 指图数据与构成数据头与行为什么要拆成两个文件指图是典型的头行结构一张指图有品目、数量、着手日这些头信息下面挂着若干构成品目。拆成两个文件而不是一个大文件理由有三条——MES 侧取入时可以按头、行分两遍处理出错时能定位到是头错还是行错文件追加时不会因为一张指图的行数不同而破坏记录定长单文件的处理量更均匀不会出现某张巨型指图把一次批处理的内存撑爆。指图数据文件的关键字段字段说明备注工厂代码所属工厂单一工厂场景下用于校验指图号唯一标识与计划订单一一对应指图类型普通 / 特急 / 返修MES 侧排产优先级依据品目代码产出品需与 MES 物料主数据一致计划数量指图数量带单位着手日计划开始日抽取条件的核心字段发行日指图正式发行日用于追溯记录唯一键见 3.4约定置于首列构成数据文件按「指图号 行号」组织每行一个构成品目带必要量与单位。这里的必要量必须是指图发行时点 BOM 展开的结果不能是实时重算的——后面 5.4 会讲实时重算会让计上时的品目校验对不上。3.3 投入实绩与完成实绩确认区分与实绩种别实绩文件是反方向由 MES 生成、ERP 消费。它的设计难点不在字段多少而在两个枚举值。第一个枚举是确认区分部分确认 / 最终确认。MES 侧必须能判断一张指图是否已完成所以完成实绩里要带这个标志ERP 侧据此决定是做部分完工确认还是最终完工确认。少了这个字段ERP 就只能在所有实绩都到齐后一次性确认中途的进度完全看不见。第二个枚举是实绩种别累计实绩 / 日别实绩。MES 侧计上来的数据可能是「截至当前的累计值」也可能是「按作业日拆开的每日值」两种语义完全不同。方案里把它做成参数选择项一次实施只选一种不混用。混用的后果是同一张指图的实绩被重复累加数字翻倍还查不出原因。字段所属文件说明指图号 作业号两者定位到具体作业投入品目代码、投入数量投入实绩倒冲品目不允许从此处计上确认良品数、确认不良数完成实绩对应 ERP 的作业确认预定完成日完成实绩最终确认时必填确认区分完成实绩部分确认 / 最终确认实绩日两者日别实绩时必填记录唯一键两者见 3.43.4 唯一键与再送判定幂等放在读侧别放在写侧整套接口只有一条同步规则写入侧永远只做追加删除动作只由读取侧在处理完成后执行。这条规则带来的直接问题是——如果读取侧处理完但删除失败下一批会读到重复数据如果写入侧重跑也会追加重复数据。所以幂等必须做而且必须做在读侧。唯一键的构造要覆盖业务语义指图数据用「工厂 指图号」构成数据用「工厂 指图号 行号」实绩数据用「指图号 作业号 实绩日 实绩种别 确认区分」。读取侧维护一张已处理键的表读到新记录先查表命中就跳过。# 读侧幂等接口文件只追加重复键在读取时丢弃删除统一由读侧完成 def load_new_records(path, seen_keys): accepted, skipped [], 0 with open(path, r, encodingutf-8, newline) as f: for line in f: rec line.rstrip(\r\n) if not rec: continue key rec.split(\t)[0] # 约定首列为拼好的唯一键 if key in seen_keys: # 再送或重跑直接丢 skipped 1 continue seen_keys.add(key) accepted.append(rec) return accepted, skipped这段逻辑有两个参数要注意。path必须是逻辑路径解析后的物理路径不写死seen_keys不能只放在内存里要么落库要么落文件否则进程一重启就失效。返回值里特意带上skipped计数是为了在记录级日志里留痕——某个批次突然跳过几百条通常意味着上游重发了这比事后对账发现数字不对要省事得多。注意接口一旦处理完成就不提供修改事务的通道。已计上的实绩要改只能在 ERP 侧手工修正。这条要在说明书里写明否则 MES 侧会一直等着「改数接口」上线。4. 程序实现抽取、计上、排他、参数四件事怎么拆ERP 侧只有两个批处理程序但周边要配套的东西不少锁、日志、工作表、参数。四个部分各管一件事混在一起写出问题时就是黑匣子。4.1 抽取程序 ZMAF001六个步骤与抽取条件抽取程序的骨架很固定读参数、抢锁、选数据、写文件、写日志、改状态、放锁。真正需要斟酌的是「改状态」这一步因为它有两种实现方式二选一不能同时上。# ZMAF001 主流程抢锁 - 抽取 - 追加写文件 - 置状态 - 写日志 - 放锁 def zmaf001(params): if not acquire_lock(params.lock_dir, ORDER): # 管理文件不存在才继续 log(params, SKIP, 对侧处理中本批次不执行) return try: orders select_orders(params) # 抽取条件见下 write_append(params.if_dir /ORDER_HEAD, orders.heads) write_append(params.if_dir /ORDER_COMP, orders.items) mark_sent(orders, params.manage_mode) # SEND 或 LOG二选一 log(params, END, fhead{len(orders.heads)} item{len(orders.items)}) finally: release_lock(params.lock_dir, ORDER)抽取条件有四个必须同时满足着手日落在参数指定的日期范围内系统状态为已发行且未完工、未部分完工指图未被取消以及第五条——按变参择一选择的管理方式判断是否已发送。params.manage_mode这个参数是整套方案里最值得讲的一个设计点。取SEND时程序在抽取成功后把指图的用户状态 SEND 置为 ON下次抽取时跳过 SEND 已 ON 的指图取LOG时程序把本次抽取的指图号写进发送日志下次抽取时跳过日志里已有的指图。前者状态跟着业务对象走一眼能看出这张指图发没发后者不污染业务对象但日志文件要跟着数据量一起长。我一般建议新项目用 SEND因为查问题时可以直接在指图上看到状态不用去翻日志文件。日志分两类用途完全不同。开始/结束日志只在批次起止各写一行记录总件数和结果是给运维看的记录级日志逐条记录处理内容包括每条错误的原因是给开发看的。两类都写缺一类都会在排查时吃亏。4.2 计上程序 ZMAF005先落工作表再计上最后删文件计上程序有一个天然的坑如果先计上、再删接口文件中间异常终止下次重跑会重复计上如果先删文件、再计上中间异常终止数据直接丢了。两种顺序都不对。解法是引入一张工作表把「处理」拆成三段先把文件内容原样落进工作表并标记为待计上再逐条计上并更新状态全部成功后才删除接口文件。异常终止时工作表里留着待处理记录重跑时按唯一键过滤已计上的不再处理。-- 计上工作表异常终止后靠它做重放与去重避免二重计上 CREATE TABLE T_IF_ACT_WORK ( WK_KEY VARCHAR(64) NOT NULL, -- 唯一键指图号作业号实绩日种别确认区分 IF_FILE VARCHAR(128) NOT NULL, -- 来源接口文件名便于对账 RAW_LINE VARCHAR(512) NOT NULL, -- 原始行重放时不再依赖原文件 STATUS CHAR(1) NOT NULL, -- 0待计上 1已计上 2计上失败 ERR_MSG VARCHAR(256), -- 失败原因直接进记录级日志 CRT_TIME TIMESTAMP NOT NULL, PRIMARY KEY (WK_KEY) );WK_KEY是主键也是幂等的落点。RAW_LINE存原始行看起来冗余但它是唯一的后悔药——接口文件已经被删掉之后要复现某条记录为什么计上失败只能靠它。STATUS只用三个值不要加更多状态状态机越简单越不容易卡死。计上成功时投入实绩登录投入品目代码与数量完成实绩登录确认良品数、确认不良数、预定完成日。有两类数据会被判为错误并写入记录级日志倒冲品目的投入实绩这类品目的数量变更只能在 ERP 侧的作业确认里做以及指图发行时点 BOM 中不存在的构成品目。这两类错误不能静默丢弃也不能自动跳过必须让运维看到。4.3 排他控制两个管理文件能挡住什么挡不住什么接口文件放在共享目录上两侧都能访问所以互斥不能靠文件系统锁只能靠约定处理中创建一个管理文件管理文件存在时另一方不执行处理完成后删除。管理文件只有两个一个管指图链路一个管实绩链路两条链路互不干扰。这套机制能挡住并发但挡不住进程崩溃。进程在创建管理文件后异常退出文件就残留了而按规则「无管理文件才执行」整条链路会静默停摆——没有报错没有告警就是不动了。这是这套方案里最典型的翻车现场。处理方式分两层。第一层是给管理文件本身带上信息创建时写入进程标识、主机名、开始时间运维一看就知道是谁留下的。第二层是巡检脚本只做告警不做自动删除。# 管理文件残留巡检超时只告警不自动删避免打断正在跑的批次 LOCK_DIR/if/lock for f in $LOCK_DIR/ORDER.lock $LOCK_DIR/ACT.lock; do [ -e $f ] || continue age$(( $(date %s) - $(stat -c %Y $f) )) if [ $age -gt 7200 ]; then echo [WARN] $(basename $f) 已存在 ${age}s请人工确认是否有进程在跑 fi done7200这个阈值来自批处理的实际耗时单批次正常在几分钟内完成两小时还没释放基本可以断定是残留。为什么不自动删因为一旦真的有一个跑得很慢的批次在跑自动删除等于把互斥拆了比停摆更糟。宁可让人看一眼也不要机器替你赌。4.4 参数与变参把环境差异从代码里赶出去同一套程序要在 Windows 和 Unix 上跑要在测试和生产之间搬唯一的办法是把环境差异全部收进参数文件。[common] newline CRLF ; CRLF / LF两侧读取同一份配置 logical_path /if/share ; 逻辑路径物理挂载点由各环境映射 log_keep_days 30 ; 日志保存期限到期由清理任务处理 [extract] date_range_days 7 ; 抽取着手日在 今天 ~ 今天7 之间 manage_mode SEND ; SEND用户状态/ LOG发送日志二选一 max_orders 5000 ; 单批次上限超出则分批 [confirm] actual_type DAILY ; CUM累计实绩/ DAILY日别实绩四个参数各有讲究。newline必须两侧一致一侧 CRLF 一侧 LF 会导致对方把整个文件读成一行这种错在日志里表现为「记录数 1」很容易被当成文件为空。logical_path用逻辑路径而非物理路径是为了让同一份配置跨环境复制。date_range_days不能设成 0设成 0 意味着只抽当天一旦遇到停线、节假日或者批处理跳过一次中间那天的指图就永远抽不出来了。actual_type一次实施只能选一个值改这个值等于改业务语义不能在运行期切换。5. 避坑与常见问题排查五个最容易让整条链路停摆的点下面五条都是原型阶段真实会遇到的按「现象、原因、解决」写遇到了直接对号入座。管理文件残留链路静默停摆。现象是批次日志里连「开始」都没有程序像没被调用一样。原因是上一批进程异常退出管理文件没删掉而程序规则是「无管理文件才执行」于是每次都直接返回。解决办法是 4.3 里的巡检脚本加告警同时在管理文件里写入进程标识和开始时间让运维一次就能判断是残留还是真在跑。这一条是踩坑率最高的上线前一定要把巡检挂上去。换行码不一致对方把文件读成一行。现象是 MES 侧反馈「文件里只有一条记录」检查文件大小却正常。原因是发送侧用了 CRLF接收侧按 LF 解析整个文件被当成单行。解决办法是把换行码做成参数两侧读同一份配置并且在联调第一步就约定好——先发一条测试记录让对方确认能拆出多行再谈字段。字符集不做转换品目代码对不上。现象是计上报「构成品目不存在」但在 MES 侧查这个品目明明存在。原因是不做编码转换的透传设计遇到全角字符、特殊连字符时字节层面就已经变了。解决办法是在抽取侧加一道无损校验不能表示成目标字符集的记录直接拦下并记错误日志问题留在发送方解决不要推给对方猜。抽取日期范围按当天算跨月漏单。现象是月初或停线恢复后发现有一批指图从来没被抽过。原因是date_range_days设成了 0 或者很小的值中间某天没跑成那天的指图就落在窗口外了。解决办法是把范围设成覆盖最长停机周期的天数并且在抽取条件里区分「着手日在窗口内」和「已发送」两个独立判断不要用一个条件兼职两件事。计上成功但文件没删下一批重复计上。现象是实绩数字翻倍且每次翻倍的量恰好等于上一批的量。原因是没有工作表或者工作表落库和计上不在同一个事务边界内。解决办法就是 4.2 的三段式先落表标记待计上再计上更新状态最后删文件读取侧再按唯一键过滤一次双保险。6. 验收与答辩六个场景跑通比讲十页架构更有说服力这类接口项目的验收最忌讳拿「功能已实现」去交差。评审的人不会看你写了多少行只会问一句断了怎么办、重了怎么办、错了怎么办。我的做法是准备六个场景每个场景都跑一遍、留一份日志截图答辩时按场景讲比讲架构图有效得多。场景输入构造期望结果观察点正常抽取3 张已发行指图各带 2 个构成品目头文件 3 条、行文件 6 条日志总件数与文件行数一致重复抽取同一天连跑两次第二次输出 0 条管理方式为 SEND 时状态已 ON锁定互斥手工创建实绩管理文件后跑抽取批次直接跳过并记 SKIP不能有任何文件被写入异常中断重跑计上到一半强杀进程再重跑实绩不翻倍工作表里待处理记录被正确消费错误数据投入倒冲品目、BOM 外品目各一条计上失败并进记录级日志其余记录正常计上不整批回滚终态确认完成实绩带最终确认区分指图转为完工部分确认时不置完工六个场景里前两个证明「对的能跑通」中间两个证明「断的能恢复」最后两个证明「错的能识别」。评委真正想知道的其实就是这三件事。答辩时还有一个小技巧把管理文件、工作表的表结构和两类日志各打印一页带着。被问到「你怎么保证不重复计上」的时候直接把工作表主键和状态字段指出来比口头解释强十倍。被问到「接口停了怎么发现」的时候把巡检脚本的输出给他看。说实话我第一次做这类接口的时候就是栽在管理文件残留上——测试环境停了两天没人发现最后是业务那边催单才暴露。从那以后我每次交付这类批处理方案都强制走一遍「断、重、错」三个场景跑不通就不签字。这套习惯救过我不止一次。希望帮到你。本文还有配套的精品资源点击获取