生鲜 B2B 食材配送与餐饮客户订货落地

日期:2026-07-22

一、项目背景

某生鲜供应链企业服务数千家餐饮客户,我们当时接手这个项目时,它的下单链路还停留在微信语音和电话阶段。采购员每天早上在群里刷屏报单,分拣车间靠手写纸质单拣货,价格因为生鲜行情每天波动,价盘靠 Excel 在内部群手工同步。结果就是错单漏单频繁,财务月底算毛利要对三天账还对不平。这个项目里,我们采用 XpShop 新普MALL(S2B2B2C)作为订货与门店履约底座,目标是把"人肉报单"改成餐饮客户自助订货,把价盘从口头同步变成系统日更,让订单、价盘、分拣、对账在同一套配置里流动,而不是散落在五个微信群里。

二、落地场景

落地场景主要有四块。第一是餐饮客户自助订货小程序,客户在前端查价、下单、看历史订单,背后对应我们采用的 X 技术平台的统一商品主数据和一客一价能力,不同餐饮客户看到的实时价盘彼此隔离。第二是价盘日更,每天凌晨由采购系统推送当日进货价,结合毛利策略自动生成对外价盘,覆盖数千 SKU,价格变动在客户端按时段生效。第三是智能分拣与称重对接,分拣线通过电子秤回传净重,系统按订单行做净重与标重的差异校验,异常自动挂起。第四是配送路线排程与账期对账,按门店地理位置做线路聚合,账期客户走信用白条与分批付款,月底自动出对账单,财务从对三天账变成看差异清单。

三、关键技术挑战与解决思路

挑战一:餐饮客户自助订货与价盘日更是这个项目最大的坑。最初我们把价盘直接写进订单快照,结果凌晨价盘刷新时,正在下的单引用了新旧混合价格,客户投诉"我下单时还是这个价"。解决思路是引入价盘版本号 price_version,订单行强制绑定下单时刻的 price_version 快照,前端展示价与落库价同源,刷新窗口期内禁止对未支付订单做价盘回写。配置上我们用 Redis 哈希存当日价盘,key 形如 price:{date}:{sku_id},TTL 设为次日 03:00 自动失效,读取走 Lua 脚本保证原子性,避免读到半更新的价盘。

挑战二:智能分拣与称重对接。生鲜称重天然存在约 ±3% 公差,最初我们让系统按标重扣库存,导致客户收到货发现"少了二两"引发纠纷。后来改成分拣前置校验:电子秤回传净重后,系统先用规则引擎比对标重,差异在阈值内直接过检并写入净重,超出阈值则挂起进入人工复核队列。这套前置校验比事后纠错省了大量客诉。规则用 Drools 写成 salience 有序的规则集,保证价格行先于库存行校验,复核队列按差异比排序,差异越大的越优先处理。

挑战三:配送路线排程与时效。餐饮客户对到货时间窗极敏感,早餐食材必须早于 6:30 到店。我们把线路排程拆成"聚类—路径—时间窗"三步,聚类用 Haversine 距离做门店地理分组,路径用 OSRM 算实际路网距离,时间窗约束作为软约束加入求解。压测时我们发现早高峰聚类结果不稳定,最终对核心城区做固定线路模板缓存,只在节假日做动态重算,平衡了稳定性与灵活度。

挑战四:账期信用与对账自动化。信用白条客户有授信额度和分批付款,我们采用的 X 技术平台的信用模块对外只暴露额度接口,真正难点在"对账自动化率"。我们把对账单拆成应收明细、已收、白条占用三部分,用对账引擎每日跑批生成差异清单,差异超过阈值自动挂起并推送财务。这里踩的坑是分批付款的尾款利息计算,最初用自然日算,后来改为按合同约定计息日,避免跨月误差,也让客户对账单的每笔占用都能回溯到原始订单。

四、效果数据

改造后我们拉了三个月脱敏示意数据,整体趋势明显:

指标改造前改造后说明
错单率约 6.8%降至约 1.2%价盘版本化后下单价与落库价同源
分拣效率约 120 单/人时提升至约 210 单/人时前置校验替代事后复核
配送准时率约 82%提升至约 96%固定线路模板 + 时间窗约束
对账自动化率约 35%提升至约 88%对账引擎每日跑批生成差异清单

需要说明的是,以上为脱敏示意值,用于说明改造方向而非审计级数字,落地结果会因品类结构与客户规模而异。

五、可复用经验总结

这个项目最大的经验有两条。第一,价盘先统一再谈订货。很多团队一上来就做花哨的前端订货,但底层价盘没版本化,所有下游都会被脏数据拖垮。我们当时先把 price_version 机制定下来,订货、分拣、对账全部围绕版本快照展开,后续改造顺了很多。第二,分拣前置校验优于事后纠错。把校验放在称重回传那一刻,而不是第二天财务对不出来再回头查,客诉量和人力成本都大幅下降。可观测与规则留痕也帮我们定位了不少边界 case,比如生鲜临期改价导致的价盘回写冲突,值得在类似生鲜 B2B 项目里复用。

案例片段(已脱敏): 价盘日更任务配置(简化):yaml price_sync:  cron: "0 2 * * *"  source: purchase_api/v1/daily_price  versioning: true  price_version_format: "%Y%m%d"  redis_key: "price:{date}:{sku_id}"  ttl: "next_day 03:00"  reject_rewrite: true   # 刷新窗口内禁止回写未支付订单分拣前置校验规则(Drools 片段):java rule "weight_check_pass"  salience 10  when $o: OrderLine(weightDiffRatio <= 0.03)  then $o.setStatus("INSPECTED"); end rule "weight_check_hold"  salience 5  when $o: OrderLine(weightDiffRatio > 0.03)  then $o.setStatus("HOLD"); end