4. 追溯数据模型设计

追溯系统的本质不是“记录很多表”,而是用一套稳定的数据模型,把“谁、在什么时间、于什么地点、对哪一批物料做了什么”表达清楚。模型设计一旦含糊,后续的扫码采集、召回范围计算、上下游查询都会变得不可靠。本节围绕五个核心实体——产品、批次、位置、事件、关系——说明如何建模,并给出可直接落地讨论的数据字典样例。

一、五个核心实体及其职责边界

产品(Product)描述“物品的品类定义”,是相对静态的主数据,例如“巴氏杀菌乳 950mL 纸盒装”,包含产品编码、名称、规格、保质期天数、贮存条件等。产品不承载具体生产信息,避免同一产品在不同批次间重复冗余。

批次(Batch/Lot)是追溯的最小业务单元,也是召回时真正被圈定的对象。一个批次应绑定产品、生产日期、有效期、生产线、班次、数量与计量单位。批次编码建议采用“产品简码+生产日期+线别+流水号”的可读结构,例如 MILK-20240612-L2-0031,既便于人工核对,也便于系统索引。

位置(Location)表达物料在某一时刻所处的场所或容器,包括厂内库位、车间、冷库、运输车辆、门店货架,甚至托盘、周转箱等载具。位置建模的关键是区分“物理地点”与“载具”,二者可用同一实体加类型字段表达,也可拆为两级,视追溯粒度要求而定。

事件(Event)是模型的时间轴骨架,记录“发生了什么”。典型事件包括原料入库、投料、杀菌、灌装、检验、出库、运输交接、门店收货、销售。事件必须带时间戳、操作人、位置和关联批次,它是把静态实体串成链条的纽带。

关系(Relation)描述实体之间的连接,最重要的是批次间的“投入—产出”关系(父批与子批),以及批次与位置、批次与事件的关联。关系实体让“一批原料流向了哪些成品”这类反向追溯成为可能。

产品 Product 产品编码 / 名称 规格 / 保质期天数 贮存条件 批次 Batch 批次编码 / 产品ID 生产日期 / 有效期 数量 / 单位 / 线别 位置 Location 位置编码 / 名称 类型:库位/车间/车辆 上级位置 事件 Event 事件ID / 类型 时间戳 / 操作人 位置ID / 关联批次 关系 Relation 父批次 / 子批次 关系类型:投入/产出 数量 / 时间 1:N 存放于 发生事件 连接批次 追溯链 产品定义品类,批次承载实例,位置与事件构成时空坐标,关系串联上下游

二、建模中的三个关键决策

决策一:批次粒度定多细。粒度过粗(如按天合并)会导致召回范围过大,粒度过细(如每箱一批)会显著增加采集负担。常见折中是“同一产品、同一生产日期、同一生产线、同一班次”为一个批次,并在批次下用序列号或箱码做二级标识。示例中若某乳品厂日产量 8 万盒、分 2 条线 3 个班次,则一天约产生 6 个批次,既可控又能支撑精准召回。

决策二:位置是否分层。建议采用“地点—载具”两级:地点如“2号冷库—A区—03货位”,载具如“托盘 T-20240612-07”。事件记录载具,载具再归属地点,这样移库时只需更新载具归属,历史事件不受影响。

决策三:关系如何表达投入产出。用一张关系表记录 父批次、子批次、关系类型、数量、发生时间。例如原料批 RM-20240610-05(生鲜乳)投入 1200 kg 到成品批 MILK-20240612-L2-0031,即写入一条“投入”关系。反向查询时从成品批出发,沿关系表向上遍历即可得到全部上游原料批。

三、数据字典样例

数据字典是把模型落到字段级的关键交付物。以下为批次表与事件表的核心字段示例,字段类型与约束可直接用于建表讨论。

表字段类型约束/说明
批次batch_idVARCHAR(32)主键,如 MILK-20240612-L2-0031
批次product_idVARCHAR(20)外键,关联产品
批次prod_dateDATE非空
批次expire_dateDATE由 prod_date + 保质期推算
批次qty / uomDECIMAL / VARCHAR(8)数量与单位,如 80000 / 盒
事件event_idBIGINT主键,自增
事件event_typeVARCHAR(16)入库/投料/灌装/出库/收货
事件event_timeDATETIME非空,统一时区
事件batch_idVARCHAR(32)外键,关联批次
事件location_idVARCHAR(24)外键,关联位置或载具
关系parent_batchVARCHAR(32)父批次(上游)
关系child_batchVARCHAR(32)子批次(下游)
关系rel_typeVARCHAR(8)投入 / 产出 / 拆分 / 合并
设计时先约定时间基准:所有 event_time 统一存 UTC 或统一存本地时区并在字典中注明,避免跨厂区、跨车辆交接时出现时间错位,导致追溯链顺序颠倒。

四、用一次查询验证模型是否可用

模型好坏最终要看能否回答真实问题。假设门店反馈成品批 MILK-20240612-L2-0031 出现胀包,需要查清上游原料批。查询逻辑是:以该批为起点,在关系表中筛选 child_batch = 'MILK-20240612-L2-0031' AND rel_type = '投入',得到父批次列表;再对每个父批次重复该过程,直到无上游记录。若结果中出现原料批 RM-20240610-05,则可进一步用事件表查其入库时间、供应商与检验记录。若这一链路能在秒级返回且无断点,说明批次、事件、关系三者的外键与索引设计是成立的;若出现“查不到上游”或“同一批对应多个矛盾位置”,则应回到数据字典检查约束与采集规范。

需要强调的是,数据模型只是骨架,字段是否被真实、及时、规范地填写,决定了追溯系统能否在召回时给出可信答案。建模阶段就应把必填约束、编码规则和时区约定写进数据字典,让后续采集端有据可依。