3. 食品保质期数据模型设计
保质期预警系统能否算得准、报得及时,取决于底层数据模型是否把“时间”这件事表达清楚。很多项目上线后预警频繁误报或漏报,追根溯源往往不是算法问题,而是数据模型里只有一个模糊的“保质期天数”,缺少批次粒度、缺少到期日期、缺少可比较的库龄口径。本节围绕一个可落地的保质期数据模型展开,把 SKU、批次、生产日期、到期日期、剩余保质期、库龄与预警状态这七个核心对象的定义、来源与计算关系讲清楚。
一、先分清两类字段:静态主数据与动态计算字段
设计数据模型的第一步不是急着建表,而是给每个字段定性。SKU 层面的保质期规则属于静态主数据,变更频率低,由商品档案维护;批次层面的生产日期、到期日期属于业务事实数据,随每次入库产生;剩余保质期、库龄、预警状态则属于动态计算字段,它们不应被当作手工录入值长期存储,而应由系统按统一口径实时或定时计算得出。把这三类混在一起,是后续口径混乱的主要根源。
具体来说,SKU 主数据至少需要承载:保质期天数(或保质期月数)、是否允许按天精确计算、预警阈值规则(如剩余保质期占比低于 1/3 触发黄色预警、低于 1/6 触发红色预警)、是否启用批次管理。批次数据则承载:批次号、生产日期、到期日期、入库日期、当前库存数量、库位。计算字段承载:剩余保质期天数、剩余保质期占比、库龄天数、预警状态。下面这张图展示了各字段之间的依赖与计算链路。
二、关键字段的定义与计算口径
SKU 编码是主数据主键,保质期天数以它为准。需要注意“保质期月数”与“天数”的换算问题:若商品标注“保质期 6 个月”,不能简单按 180 天处理,应按生产日期加 6 个自然月再减 1 天得到到期日期,否则跨月长度不同会造成偏差。建议在 SKU 主数据中同时保留“保质期单位(天/月)”字段,由系统按单位分别计算。
批次号是保质期管理的实际粒度。同一 SKU 不同批次的生产日期不同,到期日期也不同,预警必须落到批次上,否则无法指导“先出哪个批次”。批次号建议采用“生产日期 + 流水号”的可读编码,如 B20250312-01,便于人工核对。
生产日期与到期日期是一对强关联字段。到期日期可以由生产日期加保质期推算,但更稳妥的做法是入库时同时记录两者并做一致性校验:若供应商提供的到期日期与推算值偏差超过 1 天,应标记为“日期待核实”,避免因标签印刷差异或录入错误导致预警失真。对于进口食品,还需注意保质期标注格式差异(如“Best Before”日期)。
剩余保质期建议同时保留两个值:剩余天数与剩余占比。剩余天数 = 到期日期 − 当前日期;剩余占比 = 剩余天数 ÷ 保质期天数。仅看天数不够——保质期 7 天的鲜奶剩 2 天已很危险,而保质期 365 天的罐头剩 2 天同样危险但剩 60 天时却完全正常,占比能统一不同保质期长度的商品口径。
库龄是常被忽略但极有价值的字段。库龄天数 = 当前日期 − 入库日期。它与剩余保质期并不等价:一批货可能生产很早但入库很晚(如调拨、退货再入库),此时库龄短而剩余保质期短,说明该批次在流通环节已消耗大量时间,应优先处理。库龄还用于分析仓储周转效率,与预警状态配合可区分“临期但刚入库”和“临期且长期积压”两类问题。
预警状态是计算字段的最终输出,建议采用四态:正常、黄色预警、红色预警、已过期。判定逻辑基于剩余占比与 SKU 阈值规则,例如占比 ≤ 1/3 且 > 1/6 为黄色,≤ 1/6 且 > 0 为红色,≤ 0 为已过期。状态应由系统按日批量重算,而不是在查询时临时判断,以保证预警记录可追溯、可统计。
三、一个可参考的字段定义表
下表给出可直接用于建表或配置的字段清单,字段类型与示例值均为教学示例,实际项目应按所用数据库与业务规模调整。
| 字段 | 所属对象 | 类型 | 示例值 | 说明 |
|---|---|---|---|---|
| sku_code | SKU | 字符串 | SKU-10086 | 主数据主键 |
| shelf_life_value | SKU | 整数 | 180 | 保质期数值 |
| shelf_life_unit | SKU | 枚举 | 天 | 天 / 月 |
| warn_ratio_yellow | SKU | 小数 | 0.333 | 黄色预警占比阈值 |
| warn_ratio_red | SKU | 小数 | 0.167 | 红色预警占比阈值 |
| batch_no | 批次 | 字符串 | B20250312-01 | 批次唯一标识 |
| produce_date | 批次 | 日期 | 2025-03-12 | 生产日期 |
| expire_date | 批次 | 日期 | 2025-09-07 | 到期日期 |
| inbound_date | 批次 | 日期 | 2025-03-20 | 入库日期 |
| remain_days | 计算 | 整数 | 45 | 剩余保质期天数 |
| remain_ratio | 计算 | 小数 | 0.25 | 剩余保质期占比 |
| stock_age_days | 计算 | 整数 | 37 | 库龄天数 |
| warn_status | 计算 | 枚举 | 黄色预警 | 正常/黄色/红色/已过期 |
四、用示例数据走一遍计算
假设某常温酸奶 SKU-10086,保质期 180 天,黄色阈值 1/3、红色阈值 1/6。批次 B20250312-01 生产日期 2025-03-12,到期日期按“生产日期 + 180 − 1”推算为 2025-09-07,入库日期 2025-03-20。若当前日期为 2025-07-25,则:
剩余保质期天数 = 2025-09-07 − 2025-07-25 = 44 天
剩余保质期占比 = 44 ÷ 180 ≈ 0.244
库龄天数 = 2025-07-25 − 2025-03-20 = 127 天
预警判定 = 0.244 ≤ 1/3 且 > 1/6 → 黄色预警
此时系统应输出黄色预警,并建议“优先出库、安排促销”。若再过 15 天,剩余占比降至约 0.161,低于 1/6,状态自动升级为红色预警。可以看到,同一批次的状态是随日期推移而变化的,这正是预警状态必须由系统按日重算、而不能人工维护的原因。
最后需要强调口径一致性:剩余保质期、库龄、预警判定必须共用同一个“当前日期”基准,且明确是否含当日。建议在系统配置中统一约定“到期日期当天仍可销售,次日起视为已过期”,并在所有计算与报表中沿用,避免库管看到的剩余天数与预警模块相差一天而引发争议。数据模型的价值不在于字段多,而在于每个字段的定义唯一、来源清晰、计算可复现。