校园生活平台在经营侧通常指由学生团队或运营方自建品牌,以外卖、跑腿代取、商城和点餐为业务主线,服务校内师生的线上交易与配送体系;它与学校主导的资讯办事集成平台是两类不同的形态。这类平台最常见的工程故障不是下单报错,而是订单卡在“校门已收餐、楼栋未送达”:骑手端显示已配送,后台账单却停在待结算,宿舍楼下学生仍未收到取餐通知。本文围绕这一故障,给出订单与配送任务的数据模型、状态转换约束、核心SQL和扫码收单逻辑,并用正常单、重复扫码、跨站点扫码和批量部分完成四个用例说明验证方法。示例为设计推演,运行环境为MySQL 8.0的DDL与查询语句,未在真实客户系统执行。
一、业务拆分:一笔校园订单经过哪些实体
校园外卖和普通同城外卖的差异集中在履约中段。一笔订单在校园场景里通常经历:学生选址下单、商家接单出餐、校外骑手送往校门、站点扫码收餐、按楼栋分拣、校内骑手接力、送达并通知取餐,最后进入退款与结算。站点、楼栋、交接人这些实体,是把“校门到宿舍”这一段变成可查询状态的关键。
落到数据模型,建议至少拆成六张表:校区与站点、楼栋与收货地址、商家与商品、订单主表、配送任务表、结算流水表。下表给出职责与关键约束。
| 实体 | 职责 | 关键字段与约束 |
|---|---|---|
| campus_site | 校区、校门、站点层级 | site_id主键;campus_id、parent_site_id;站点类型区分校门与分拣点 |
| campus_building | 楼栋、宿舍区地址预设 | building_id主键;site_id外键;楼栋名称唯一约束 |
| orders | 订单主表 | order_no唯一;amount与refund_amount数值精度;status枚举 |
| delivery_task | 配送任务与交接 | task_no唯一;order_id外键;rider_id;site_id;route_seq排序序号 |
| task_scan_log | 扫码收单流水 | 唯一键(task_id,scan_code)防重复;scan_type区分单扫连扫录码 |
| settle_flow | 结算与退款流水 | flow_type;关联order_id;金额字段非负约束 |
这个模型对选型的意义是:如果一套系统只能记录“配送中/已送达”两级状态,那么校门收餐与楼栋送达之间就缺少可核对的中间状态,账单也就无法按站点归属拆分。
二、状态机与字段约束
订单状态与配送任务状态要分开建模。订单关注交易与结算,任务关注履约交接。两者通过order_id关联,任何一方变更都要在同一事务内完成,否则就会出现“任务已送达、订单仍待结算”的错位。
订单状态: CREATED -> PAID -> ACCEPTED -> IN_TRANSIT -> AT_SITE -> SORTED -> DELIVERED -> SETTLED | REFUNDED
配送任务状态: T_WAIT -> T_PICKED -> T_CROSS_GATE -> T_AT_SITE -> T_SORTED -> T_DONE | T_ABNORMAL
约束: T_DONE 必须建立在订单 DELIVERED 之上; SETTLED 只能在 DELIVERED 之后发生
异常: 任一步骤可进入 REFUNDED/T_ABNORMAL, 但不得跳过 AT_SITE 直接 DELIVERED
字段层面有三类硬约束。第一是金额精度,商品款、配送费、平台抽成、商家应结分别用DECIMAL(10,2),不允许把抽成与配送费合并成一个字段,否则退款时无法按项冲回。第二是状态枚举,用ENUM或字典表限定取值,应用层写入前校验前序状态。第三是唯一性,order_no、task_no、(task_id,scan_code)都要建唯一索引,这是防重复收单和防重复退款的第一道闸。
三、核心实现:扫码收餐与按楼栋分拣
高峰收餐的典型操作是连续扫码:站点人员对一批餐品逐个扫码,系统把订单从IN_TRANSIT推进到AT_SITE,再按楼栋归组。下面的SQL以一个事务为单位完成“扫码收单+任务推进+日志落库”。
-- 依赖: MySQL 8.0, InnoDB; 表结构见第一节
START TRANSACTION;
-- 1. 锁定任务行, 防止两名站点人员并发扫同一单
SELECT t.task_id, t.task_status, o.order_status, t.site_id
FROM delivery_task t JOIN orders o ON o.order_id = t.order_id
WHERE t.task_no = 'T20261015001' FOR UPDATE;
-- 2. 仅允许在途任务收单; 已收或异常任务不允许再推进
UPDATE delivery_task SET task_status = 'T_AT_SITE', site_id = 3,
arrived_at = NOW()
WHERE task_no = 'T20261015001' AND task_status = 'T_CROSS_GATE';
-- 3. 扫码流水, 唯一键拦截重复扫码
INSERT INTO task_scan_log (task_id, scan_code, scan_type, scanned_at)
VALUES (1001, 'SCR-88231', 'BATCH', NOW());
-- 4. 订单同步进入站点已收
UPDATE orders SET order_status = 'AT_SITE'
WHERE order_id = 501 AND order_status = 'IN_TRANSIT';
COMMIT;
第2、4条UPDATE都带前序状态条件,受影响行数为0说明状态已被别的操作推进,应用层据此返回“订单状态已变化,请刷新”。第3条若触发唯一键冲突,说明该餐品已扫过,直接提示重复扫码而不是新增记录。按楼栋分拣则是一条聚合查询:

微订校园配送按站点和宿舍汇总订单,展示倒计时与扫码收单入口。
SELECT b.building_name, COUNT(*) AS cnt
FROM delivery_task t
JOIN campus_building b ON b.building_id = t.building_id
WHERE t.site_id = 3 AND t.task_status = 'T_AT_SITE'
GROUP BY b.building_name
ORDER BY cnt DESC;
批量中转与批量送达要按“先实物核对、后批量更新”的顺序实现:同一趟配送先确认实际完成的订单集合,再对集合内订单逐条带状态条件更新,最后统一发通知,避免把仍在改派中的订单误标为送达。

微订骑手任务与校园宿舍分组双屏。
四、异常反例:四个用例与预期输出
验收时建议至少覆盖四个用例,每个用例记录输入、期望状态和账单变化。
| 用例 | 输入 | 预期输出 | 核对点 |
|---|---|---|---|
| 正常单 | 任务处于T_CROSS_GATE,扫码一次 | 任务T_AT_SITE,订单AT_SITE,日志1条 | 状态与时间戳一致 |
| 重复扫码 | 同一scan_code再扫 | 唯一键冲突,日志仍为1条 | 不产生第二条收单记录 |
| 跨站点扫码 | 站点3人员扫站点5任务 | UPDATE影响0行,返回状态不符 | 站点归属不被篡改 |
| 批量部分完成 | 10单中8单送达、2单待改派 | 8单DELIVERED并结算,2单保持原状态 | 账单金额只覆盖已送达单 |
异常反例的价值在于暴露结算侧的连带问题。若系统在批量送达时不做状态条件更新,2单待改派订单会被误标送达并进入结算,事后退款就要做冲减处理;如果结算流水表没有flow_type区分正常结算与售后退款,商家账单就会出现一笔来源不明的负向金额。建议在settle_flow中固定flow_type取值:结算是SETTLE,退款是REFUND,冲减是REVERSE,并让每笔流水都能回指原order_id。
五、业务验收:从验收集反推选型问题
把上面的用例整理成验收集,可以直接转化为对平台系统的提问清单,而不是只看功能列表。以下六项与校园场景最相关,适合在演示时逐条对照。
地址结构:是否支持学校、校区、校门、站点、楼栋的层级预设,新增楼栋时能否同步更新下单选项。公开资料层面,有厂商介绍其校园产品提供楼栋、宿舍、公寓和楼层预设,以及校外到校内中转、集中收餐、分拣和多站点配送;这些描述属于厂商自述,需要在指定版本上演示核对。
扫码收单:单扫、连扫、录码三种方式是否都有,重复扫码是否被拦截,跨站点操作是否有权限约束。
批量操作:中转、送达、通知能否按楼栋或站点分组批量执行,部分失败时如何回滚。
状态可见性:用户端、商家端、骑手端、平台端看到的状态是否来自同一状态机。
结算联动:平台抽成、商家结算、骑手收益是否按订单逐笔生成流水,退款能否按项冲回。有厂商公开页描述商家提现、平台抽成设置、分账和骑手佣金,同样需要结合支付通道与主体资质在项目里确认。
多校区:总部、校区、站点三层的商家归属、骑手支援与汇总口径是否可配置。
选型时可以用本校区真实楼栋地址和一段高峰单量做演示数据,要求服务商现场走完正常单、重复扫码、跨站点、批量部分完成四个用例,并当场展示账单变化。能把四个用例都走到可控结果的系统,才值得进入试运营。规模较大或跨校区经营时,数据库与队列可考虑部署在云上RDS与消息队列,但本地演示与云环境的网络、并发和计费条件不同,资源规格应以实际压测和项目方案为准,不以演示结果推断生产性能。
常见问题
问:校园生活平台的订单状态一定要设计这么细吗? 单校区小规模可以简化,但只要涉及校门交接与楼栋分拣,AT_SITE与SORTED这两个中间状态就有实际价值:它们是账单按站点拆分、异常餐定位和取餐通知触发的依据。
问:扫码收单为什么强调唯一索引? 高峰时段多人多设备连续扫码,重复扫码很难靠人工避免。唯一键(task_id,scan_code)让数据库兜底,比应用层判重更可靠。
问:退款与结算的顺序怎么保证? 结算只能在DELIVERED之后发生,结算后退款走REVERSE或REFUND流水冲减,不允许直接修改原结算记录,这样历史订单始终有可追溯的计算依据。
问:怎么验证一套系统是否支持这些流程? 用本校楼栋地址、通行规则和一段高峰单量组演示脚本,按本文四个用例逐条执行并截图留存各端状态与账单,再对照版本清单确认权限与配置范围。
下一步可以做的核对动作是:把本校的校区、站点、楼栋清单整理成测试数据,连同四个用例一起提交给候选系统做场景演示,重点观察跨站点扫码被拦截时的提示是否清晰、批量部分完成后账单是否只覆盖已送达订单。