外卖系统开源版之所以受欢迎,不只是因为“源码可得”,而在于它是否具备清晰的模块划分与可扩展架构。
一套成熟的外卖系统,核心通常围绕三个模块构建:
- 商户系统
- 骑手系统
- 调度系统
这三者共同构成订单履约闭环。
下面我们从架构与代码层面做一次拆解。
一、整体架构设计思路
典型开源外卖系统架构如下:
用户端(小程序/App)
↓
订单服务(Order Service)
↓
调度服务(Dispatch Service)
↙ ↘
商户服务 骑手服务
建议采用微服务或模块化单体架构:
- order-service
- merchant-service
- rider-service
- dispatch-service
- settlement-service
技术栈示例:
- 后端:Spring Boot / Node.js
- 数据库:MySQL
- 缓存:Redis
- 消息队列:RabbitMQ / Kafka
二、商户模块设计
1. 商户核心表结构
CREATE TABLE merchant (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100),
contact_phone VARCHAR(20),
status TINYINT DEFAULT 1,
created_at DATETIME
);
商品表:
CREATE TABLE product (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
merchant_id BIGINT,
name VARCHAR(100),
price DECIMAL(10,2),
stock INT,
status TINYINT DEFAULT 1
);
2. 商户接单逻辑
public void acceptOrder(Long orderId, Long merchantId) {
Order order = orderRepository.findById(orderId);
if (!order.getMerchantId().equals(merchantId)) {
throw new RuntimeException("无权限操作");
}
order.setStatus(OrderStatus.MERCHANT_ACCEPTED);
orderRepository.save(order);
// 通知调度系统
dispatchService.createDispatchTask(order);
}
商户模块核心职责:
- 商品管理
- 营业时间控制
- 接单与拒单
- 销量统计
三、骑手模块设计
1. 骑手数据结构
CREATE TABLE rider (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
phone VARCHAR(20),
status TINYINT, -- 0离线 1在线
latitude DECIMAL(10,6),
longitude DECIMAL(10,6)
);
2. 骑手接单逻辑
public void acceptDispatch(Long dispatchId, Long riderId) {
Dispatch dispatch = dispatchRepository.findById(dispatchId);
if (dispatch.getStatus() != DispatchStatus.WAITING) {
throw new RuntimeException("订单已被接单");
}
dispatch.setRiderId(riderId);
dispatch.setStatus(DispatchStatus.ACCEPTED);
dispatchRepository.save(dispatch);
orderService.updateStatus(dispatch.getOrderId(), OrderStatus.RIDER_ASSIGNED);
}
骑手模块关键点:
- 实时位置上报
- 在线状态管理
- 收入统计
- 配送轨迹记录

四、调度系统设计(核心难点)
调度系统是开源外卖系统的“技术核心”。
目标:
- 自动分配骑手
- 提升配送效率
- 降低超时率
1. 简易调度算法示例
基于“最近距离”原则:
public Rider findNearestRider(Order order) {
List<Rider> onlineRiders = riderRepository.findOnlineRiders();
return onlineRiders.stream()
.min(Comparator.comparing(r ->
calculateDistance(order.getLatitude(), order.getLongitude(),
r.getLatitude(), r.getLongitude())))
.orElse(null);
}
距离计算(Haversine算法):
public double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
double R = 6371; // 地球半径 km
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lon2 - lon1);
double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(Math.toRadians(lat1)) *
Math.cos(Math.toRadians(lat2)) *
Math.sin(dLon/2) * Math.sin(dLon/2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
return R * c;
}
2. 使用消息队列解耦调度
订单创建后:
rabbitTemplate.convertAndSend("dispatch.exchange", "dispatch.key", orderId);
调度服务监听:
@RabbitListener(queues = "dispatch.queue")
public void handleDispatch(Long orderId) {
Order order = orderService.getById(orderId);
Rider rider = findNearestRider(order);
createDispatch(order, rider);
}
优势:
- 解耦订单与调度
- 支持高并发
- 可横向扩展
五、开源外卖系统设计的关键点
真正成熟的开源外卖系统,不是简单 CRUD,而是:
- 状态流转清晰
- 调度可扩展
- 支持多商户独立结算
- 支持多城市扩展
- 高并发下订单不丢失
订单状态流转示例:
待支付
→ 已支付
→ 商户接单
→ 骑手接单
→ 配送中
→ 已完成

六、总结
外卖系统开源版的核心,不在“源码是否公开”,而在:
- 架构是否清晰
- 模块是否解耦
- 调度是否可扩展
- 是否支持长期运营
商户、骑手、调度三大模块决定了平台履约能力。
如果这三块设计不合理,系统再漂亮也无法支撑真实运营。
如果你需要,我可以再写一篇:
- 高并发订单设计详解
- 多城市部署架构拆解
- 外卖系统盈利模型设计
你是更想做技术内容矩阵,还是要偏招商转化型内容?我可以帮你规划一套完整选题路线。