游乐游手机版
首页/AI教程/文章详情

从零搭建外卖跑腿配送系统完整流程解析

时间:2026-07-03 15:55
外卖跑腿配送系统是围绕多角色、实时调度和地理位置驱动的同城即时履约体系。核心业务流程包括用户下单、商家接单、智能派单、骑手配送及结算。关键技术涉及RedisGEO实现就近派单、WebSocket保障实时推送、分布式锁防止抢单冲突,并需应对高并发与实时性挑战。

外卖跑腿配送系统本质上是一个以多角色协同、实时调度和地理位置驱动为核心的同城即时履约平台。它连接用户、商家与骑手,并通过管理后台实现全局运营管控。

本文将从业务流程到技术架构,再到关键代码实现,全方位拆解一套可落地的外卖跑腿配送系统设计方案。

外卖跑腿配送系统(图:外卖跑腿配送系统架构示意)

典型的外卖跑腿系统通常采用分层或微服务/模块化架构。业务层面,用户端支持下单、支付与订单追踪;商家端负责接单、出餐及订单管理;骑手端围绕抢单、配送与签收;管理后台则统筹订单调度、风控和数据分析。

技术选型上,前端常采用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算法;
  • 风控系统:专门用于检测异常骑手行为;
  • 大数据分析:进行热区预测,提前调度运力。

外卖跑腿配送系统(图:外卖跑腿配送系统升级方向)

总结

归根结底,外卖跑腿配送系统的核心并非“下单”动作本身。真正的技术难点集中在三大块:派单算法、实时通信、高并发处理。这三块打得扎实,系统才真正具备商业落地的底气。

来源:https://cloud.tencent.com.cn/developer/article/2701951
上一篇WorkBuddy从入门到精通全场景落地高效指南 下一篇Opus 4.8 GPT5.5 Gemini3.1 Pro同任务输赢结果复杂
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
批处理BAT入门教程第一篇
AI教程 · 2026-07-03

批处理BAT入门教程第一篇

提供13个批处理实战技巧,覆盖全盘查找并删除文件夹或文件、拷贝移动文件、创建畸形文件夹及设置隐藏属性等场景,可一键完成系统维护与文件管理工作,极大提升自动化操作效率和便捷性。

从零开始批处理命令For循环详解与实战案例
AI教程 · 2026-07-03

从零开始批处理命令For循环详解与实战案例

批处理For命令支持 d、 l、 r、 f四个参数。 d仅列出当前目录下的目录名; r递归搜索指定路径及其子目录中的文件; l生成数值序列; f可解析文件、字符串或命令输出,通过delims、tokens、skip、eol等选项灵活处理内容。

批评你的人是你生命中的贵人
AI教程 · 2026-07-03

批评你的人是你生命中的贵人

批评你的人往往最值得珍惜,因为他们关注你、助你成长。面对批评应包容反思,用行动改进而非辩解。接受批评是自我完善的过程,能让人少走弯路,避免重复犯错。这样的人正是生命中的贵人,值得感恩与珍惜。

测试人员角色定位与职责详解
AI教程 · 2026-07-03

测试人员角色定位与职责详解

测试人员角色经历了从找问题、保证质量到分析风险的转变,最终核心职责是提供关键信息,协助团队创造优秀产品。这包括识别问题、评估风险及帮助团队了解项目状态,而非单纯把关或追求完美。

经营成功测试生涯的实用方法与策略
AI教程 · 2026-07-03

经营成功测试生涯的实用方法与策略

一、测试生涯的起点 1989年,我在田纳西大学攻读研究生时,意外地从软件开发人员转行成为一名软件测试工程师。这并非我主动选择,说起来还有些戏剧性——某个早晨,教授质问我为何缺席那么多开发会议,我解释说这些会议总是安排在周末早上,对我这个第一次离家、刚入学的学生来说实在不便。结果呢?等待我的不是解聘通