4. 批次主数据建模
在智慧养殖系统中,批次主数据是连接生产计划、饲喂记录、健康事件与成本核算的“主键骨架”。如果批次模型设计得松散,后续的存栏统计、料肉比核算、追溯查询都会出现口径不一致。本节围绕三件事展开:批次实体应具备哪些属性、批次如何与栏舍/栋舍/养殖单元建立关系、以及如何用状态机与生命周期标识管理批次从入栏到归档的全过程。
一、批次实体属性设计
批次实体不是简单的“编号+名称”,而应划分为四类属性:标识属性、业务属性、时间属性和统计属性。标识属性包括批次号(业务唯一键)、批次ID(系统主键)、来源类型(自繁/外购/转入);业务属性包括畜种、品种品系、代次、性别结构、初始数量、计划出栏日龄;时间属性包括入栏日期、预计出栏日期、实际清栏日期;统计属性包括当前存栏、累计死淘、累计转出、累计耗料。设计时建议把“初始数量”与“当前存栏”分开存储,前者是批次建档时的快照,后者由事件驱动更新,避免历史数据被覆盖。
批次号建议采用可读编码规则,例如“场区码-畜种码-入栏年月-流水号”,如 F01-PIG-202503-0012。这种编码便于人工核对,但业务唯一性仍应由数据库唯一约束保证,不能仅依赖编码规则。对于外购批次,还应记录供应商、检疫证明编号、运输车牌等追溯字段,这些字段在自繁批次中可为空,因此建模时宜采用可空字段或扩展属性表,而不是强行填充。
二、批次与栏舍、栋舍、养殖单元的关系
养殖场的空间层级通常是“场区—栋舍—栏舍—养殖单元”。批次与空间的关系不是简单的一对一,而是“批次占用空间、空间承载批次”的多对多时序关系。一个批次可以先后占用多个栏舍(转群),一个栏舍在不同时间也可以承载不同批次。因此,正确的建模方式是引入“批次—栏舍占用关系”中间实体,记录批次ID、栏舍ID、占用开始时间、占用结束时间、占用数量。这样既能表达当前占用,也能还原历史转群轨迹。
栋舍和养殖单元更多承担“归属与统计”角色:栋舍用于环境控制与防疫分区,养殖单元(如产床、保育栏组)用于精细饲喂。批次可以直接关联到栋舍作为管理归属,但实际存栏应以栏舍占用关系为准。若系统只记录“批次属于某栋舍”,转群后就会出现统计偏差。下图展示了批次、占用关系与空间层级之间的结构:
三、批次状态机与生命周期标识
批次状态机应覆盖“计划—在养—转群—清栏—归档”的主线,并允许异常分支。一个可落地的状态集合为:草稿(建档未确认)、待入栏(计划已审批)、在养(已入栏且有存栏)、部分转出(存在转群但未清空)、已清栏(存栏归零)、已归档(结算完成、只读)。状态迁移必须由业务事件触发,而不是人工随意修改:入栏事件使“待入栏→在养”,转群事件使“在养→部分转出”或维持“在养”,清栏事件使“部分转出/在养→已清栏”,结算完成后进入“已归档”。
生命周期标识建议采用“状态码+状态时间+版本号”的组合。状态码用于查询过滤,状态时间记录每次迁移的时刻,版本号用于并发控制,防止两个终端同时提交转群导致存栏重复扣减。对于需要对外追溯的场景,还可增加“生命周期流水号”,每次状态迁移生成一条不可变记录,形成批次的时间线。这样即使批次主表被更新,历史轨迹仍可完整还原。
批次状态迁移示例(事件驱动):
入栏登记 : 待入栏 → 在养 记录入栏日期、初始数量
转群出栏 : 在养 → 部分转出 关闭旧栏舍占用,开启新占用
清栏确认 : 部分转出 → 已清栏 当前存栏 = 0,记录清栏日期
财务结算 : 已清栏 → 已归档 批次转为只读,禁止新增生产事件
四、建模落地时的几个判断点
第一,批次数量口径要统一。建议以“入栏数量-累计死淘-累计转出-累计销售”作为当前存栏的计算式,并定期与栏舍占用数量核对,差异超过阈值时触发盘点任务。第二,转群不要直接修改批次的栏舍字段,而应新增占用记录,否则历史成本无法按栏舍分摊。第三,状态机迁移应配置前置校验,例如“存栏大于零才能进入部分转出”“存在未结算事件时禁止归档”。第四,批次号与批次ID分离,业务人员使用批次号,系统内部关联使用批次ID,避免编码规则调整影响历史数据。
把上述三部分合起来看,批次主数据建模的本质是:用稳定的标识描述“是谁”,用占用关系描述“在哪里”,用状态机描述“处于什么阶段”。三者分离又互相约束,才能支撑智慧养殖中存栏、饲喂、健康与成本数据的统一口径。