生鲜那套小程序跑了几年,2023 年春天加了冷链配送。新东西不是页面,是「时段」。
三档时段怎么来的
客户那边三辆车,早市 5 点到 7 点一档、午前一档、下午一档。每车能装多少是固定的,装满了就得排下一档。以前只按商品库存扣,现在多了一层:菜还有,车没位置了,这单也不能接。
第一版我们把时段当成商品的一个属性,下单时查一下还剩多少位,够就减 1。老毛病,先查后写,两个人同时选最后一档,都成了。
改成条件更新:
UPDATE delivery_slot SET booked = booked + 1 WHERE id = 某档 AND booked < capacity
看受影响行数,0 就回一句「这档满了,看看下一档」,前端把可选时段里满的那档置灰。capacity 和 booked 两列,加一个 slot_date,一天三行。
预占和释放
下单不是立刻支付。创建订单时先占一个位,15 分钟不付就放出来,定时任务每 10 分钟扫一遍。这里跟 2018 年那次库存预占是同一套思路,只是那次预占商品,这次预占车。
有个细节:取消订单要同时释放商品库存和时段,两件事得在一个事务里。第一版分开写,有回释放了商品没释放时段,那档显示满了其实空着一个位,到晚上才发现。补了事务,又跑了 2000 多笔历史订单核对,找出 3 笔类似的。
24 这个数是怎么来的
下午那档 capacity 从 30 调到 24,不是拍的。上线头三天我们把每车实际装车数记下来,冷链箱比预想占地方,30 单里总有五六单要塞到过道,司机说这样路上容易倒。按 24 排,剩下 6 个位置留给它口中的「不好放的件」。
后来又加了一列 max_cold,专门记冷链箱能放几个,跟普通件分开算。一个时段超的不是总单数,是冷链箱数。这个约束第一版完全没想到,是司机在群里发了一张后备箱照片,我们才看懂。
改时段那件事
客户原本想让用户下单后还能改配送时段。我们没做,改成「取消重下」。原因很实在:改时段要同时释放原档、占新档,中间任何一步失败都会出现「两边都没占」或「两边都占着」。取消重下虽然多两步,但每一步都简单,出问题也好查。
真有人打电话来要求改,客服在后台帮他取消、重新下一单,再手工核一下优惠。一个月也就七八回,比做一套改签逻辑便宜。
那 3 笔不一致
补事务那次核出 3 笔历史订单,商品库存释放了、时段没释放,都是凌晨批量跑之前的老数据。手动把 booked 减了回去,各写了一条备注。跑完 2000 多笔核对,脚本输出一张 4 列的表,我们打印出来签了字,夹进项目文件夹。
早市那档现在还是 5 点装车,4 点 40 的导出任务加了失败告警,打印卡纸就发消息,司机手机上看纯文本那版,不用再拿笔抄。max_cold 那列是 2023 年 3 月加的,加完下午那档再没出现过塞过道的情况,司机后来也没再发过后备箱照片。
司机端那张纸
司机出发前要一张分拣单,按时段分三块,每块里按配送顺序排。我们做了个导出,A4 一页一个时段,字号 11,地址不折行,留了签字栏。
早市那档最紧,5 点装车,4 点 40 单子就得打出来。有天 4 点 45 打印机卡纸,司机自己拿笔照着屏幕抄了 8 行。后来我们在后台加了个「导出为纯文本」的按钮,手机上看也能用。
2 月 10 号上线,头一周改了两处:下午那档的 capacity 从 30 调到 24,因为实际装不下;还有「已满」的提示文案,原来写「不可选」,客户说太硬,改成「这档满了,换个时间?」

