外卖跑腿配送系统本质上是一个以多角色协同、实时调度和地理位置驱动为核心的同城即时履约平台。它连接用户、商家与骑手,并通过管理后台实现全局运营管控。
本文将从业务流程到技术架构,再到关键代码实现,全方位拆解一套可落地的外卖跑腿配送系统设计方案。
(图:外卖跑腿配送系统架构示意)
典型的外卖跑腿系统通常采用分层或微服务/模块化架构。业务层面,用户端支持下单、支付与订单追踪;商家端负责接单、出餐及订单管理;骑手端围绕抢单、配送与签收;管理后台则统筹订单调度、风控和数据分析。
技术选型上,前端常采用Vue、React,或直接开发小程序与App;后端以Java Spring Boot、Node.js、Go为主;数据库使用MySQL与Redis;地图服务集成高德或Google Maps;消息推送通过WebSocket或MQTT;定位则基于GPS与LBS。
核心业务流程详解
系统主链路清晰:用户下单→商家接单→系统派单(或骑手抢单)→骑手取餐→配送→完成订单→结算评价。其中派单逻辑是整个流程的核心节点。
核心模块拆解
1. 订单模块
订单是系统的核心数据实体。一个简化版的订单表设计如下:
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT,
merchant_id BIGINT,
rider_id BIGINT,
status VARCHAR(20),
total_amount DECIMAL(10,2),
create_time DATETIME,
update_time DATETIME
);
2. 下单接口(后端示例)
以下Node.js示例展示了下单接口的核心逻辑:先计算总金额,然后创建订单,最后将订单ID放入Redis队列等待派单系统处理。
app.post("/order/create", async (req, res) => {
const { userId, merchantId, items } = req.body;
// 第一步:计算订单总金额
const total = items.reduce((sum, item) => {
return sum + item.price * item.count;
}, 0);
// 第二步:创建订单记录
const order = await db.orders.create({
user_id: userId,
merchant_id: merchantId,
total_amount: total,
status: "PENDING"
});
// 第三步:写入Redis队列用于派单调度
await redis.lpush("order_queue", order.id);
res.json({ success: true, orderId: order.id });
});
3. 派单核心逻辑(重点)
派单是外卖系统智能化的体现。如何将订单分配给最近的骑手?思路清晰:获取订单位置,通过LBS查找附近骑手,计算距离,最后分配给距离最近的一位。
具体实现可借助Redis的GEO功能管理骑手位置:
// 骑手上线时存储其位置信息到Redis GEO
await redis.geoadd("riders_location", longitude, latitude, riderId);
查询附近可用骑手:
const riders = await redis.georadius(
"riders_location",
orderLng,
orderLat,
3, // 3公里范围
"km",
"WITHDIST",
"COUNT",
5
);
完整的派单逻辑封装如下:
async function dispatchOrder(orderId, orderLng, orderLat) {
const riders = await redis.georadius(
"riders_location",
orderLng,
orderLat,
5,
"km",
"WITHDIST"
);
if (!riders.length) {
throw new Error("暂无可用骑手");
}
// 按距离升序排序
riders.sort((a, b) => a[1] - b[1]);
const selectedRider = riders[0][0];
// 更新订单信息
await db.orders.update({
where: { id: orderId },
data: { rider_id: selectedRider, status: "DISPATCHED" }
});
// 通过WebSocket/MQTT通知骑手
notifyRider(selectedRider, orderId);
return selectedRider;
}
4. 实时订单推送(WebSocket)
骑手需在秒级收到订单通知,WebSocket是实现实时推送的关键:
const WebSocket = require("ws");
const wss = new WebSocket.Server({ port: 8080 });
wss.on("connection", (ws) => {
ws.on("message", (msg) => {
const data = JSON.parse(msg);
if (data.type === "rider_login") {
ws.riderId = data.riderId;
}
});
});
// 推送新订单给指定骑手
function notifyRider(riderId, orderId) {
wss.clients.forEach(client => {
if (client.riderId === riderId) {
client.send(JSON.stringify({ type: "NEW_ORDER", orderId }));
}
});
}
5. 骑手接单逻辑
接单时需进行状态判断,防止多人同时抢单:
app.post("/rider/accept", async (req, res) => {
const { riderId, orderId } = req.body;
const order = await db.orders.findById(orderId);
// 检查订单是否仍处于可接单状态
if (order.status !== "DISPATCHED") {
return res.json({ success: false, msg: "订单已被接单" });
}
await db.orders.update({
where: { id: orderId },
data: { status: "ACCEPTED", rider_id: riderId }
});
res.json({ success: true });
});
6. 定位更新(骑手端)
骑手位置需持续上报,常见频率为每5秒一次:
setInterval(() => {
na vigator.geolocation.getCurrentPosition(async (pos) => {
await fetch("/rider/location/update", {
method: "POST",
body: JSON.stringify({
riderId,
lng: pos.coords.longitude,
lat: pos.coords.latitude
})
});
});
}, 5000);
后端接收更新后写入Redis:
app.post("/rider/location/update", async (req, res) => {
const { riderId, lng, lat } = req.body;
await redis.geoadd("riders_location", lng, lat, riderId);
res.json({ success: true });
});
关键技术难点
实战中,有四个常见坑位需要重点关注。
第一是高并发订单处理。解决方案成熟:使用Redis队列削峰,配合MQ异步派单,必要时引入分库分表。
第二是实时性。WebSocket已替代HTTP轮询,MQTT在移动端连接优化方面表现更优。
第三是距离计算。Redis GEO是首选方案,Haversine公式可作为备选。
第四是防止抢单冲突。分布式锁是标配,利用Redis的SETNX可干净利落地解决:
const lock = await redis.set("order_lock_" + orderId, riderId, "NX", "EX", 10);
if (!lock) {
return "订单已被抢";
}
系统升级方向
若目标为商业级别的系统,以下几项几乎是刚需:
- 智能派单:引入AI算法,综合距离、负载和骑手评分;
- 动态调度:支持订单合并配送,提升整体效率;
- 路径规划优化:采用A*或Dijkstra算法;
- 风控系统:专门用于检测异常骑手行为;
- 大数据分析:进行热区预测,提前调度运力。
(图:外卖跑腿配送系统升级方向)
总结
归根结底,外卖跑腿配送系统的核心并非“下单”动作本身。真正的技术难点集中在三大块:派单算法、实时通信、高并发处理。这三块打得扎实,系统才真正具备商业落地的底气。
