随着本地生活业务快速发展,自研外卖平台的需求持续增多。一套完整外卖系统,除了用户点餐下单基础能力之外,商户入驻、订单调度是平台能够规模化运营的两大核心模块。商户入驻实现多商家快速接入平台,订单调度负责将订单高效分配给骑手,共同支撑整个履约业务闭环。

本文将从系统整体架构、商户入驻模块、订单调度模块、数据表设计、关键代码实现、支付处理、数据统计、技术选型、功能扩展几个方面,讲解本地外卖配送系统的开发要点。
整套系统分为用户小程序、商户端、骑手端、平台管理后台四端,全部复用同一套API服务层,保障多端数据实时同步。
整体架构示意:
用户小程序
|
商户端 ---- API服务层 ---- 平台管理后台
|
骑手端
|
MySQL + Redis商户入驻是多商家外卖平台的入口,完整流程:商户提交入驻资料 → 平台审核资质 → 审核通过开通店铺账号 → 商户登录后台配置店铺与商品。
CREATE TABLE merchant (
id INT PRIMARY KEY AUTO_INCREMENT,
shop_name VARCHAR(100) COMMENT '店铺名称',
contact_name VARCHAR(50) COMMENT '联系人',
phone VARCHAR(20) COMMENT '联系电话',
license_img VARCHAR(255) COMMENT '营业执照图片',
food_license_img VARCHAR(255) COMMENT '食品经营许可证',
shop_address VARCHAR(255) COMMENT '门店地址',
lat DECIMAL(10,6) COMMENT '门店纬度',
lng DECIMAL(10,6) COMMENT '门店经度',
commission_rate DECIMAL(5,2) COMMENT '平台佣金比例',
apply_status TINYINT DEFAULT 0 COMMENT '0待审核 1审核通过 2审核驳回',
status TINYINT DEFAULT 0 COMMENT '0未营业 1营业中',
create_time DATETIME
);商户提交入驻申请接口(Node.js)
router.post('/merchant/apply', async(req,res)=>{
const merchant = await Merchant.create({
shop_name:req.body.shopName,
contact_name:req.body.contactName,
phone:req.body.phone,
license_img:req.body.licenseImg,
food_license_img:req.body.foodLicenseImg,
shop_address:req.body.shopAddress,
lat:req.body.lat,
lng:req.body.lng,
apply_status:0
})
res.json({
code:200,
message:"入驻申请提交成功,等待平台审核",
data:merchant
})
})平台审核商户接口示例:
router.post('/merchant/audit',async(req,res)=>{
let merchantId = req.body.merchantId;
let applyStatus = req.body.applyStatus;
await Merchant.update({
apply_status:applyStatus
},{
where:{
id:merchantId
}
})
res.json({
message:applyStatus===1?"审核通过":"审核驳回"
})
})用户选购商品加入购物车,系统计算商品金额、配送费、平台佣金相关数据,生成平台订单。
购物车金额计算函数:
function calculateCartAmount(cart){
let total = 0;
cart.forEach(item=>{
total += item.price * item.number;
})
return total;
}创建订单,同时记录商户分佣基数:
async function createOrder(userId,cart,shopId){
const goodsAmount = calculateCartAmount(cart);
//查询商户佣金比例
const merchantInfo = await Merchant.findByPk(shopId);
const commission = goodsAmount * merchantInfo.commission_rate /100;
const order = await Order.create({
user_id:userId,
shop_id:shopId,
goods_amount:goodsAmount,
commission:commission,
status:'待支付'
});
return order;
}基础订单状态流转:
待支付
|
已支付
|
商户接单
|
调度分配骑手
|
配送中
|
订单完成订单调度分为自动派单、骑手抢单两种模式,平台后台可以切换模式。商户接单之后,订单进入调度池,进行骑手匹配。
订单调度状态常量:
const dispatchStatus={
WAIT_DISPATCH:1, //待调度
AUTO_ASSIGN:2, //已自动派单
GRAB_ORDER:3, //抢单中
RIDER_ACCEPT:4, //骑手已接单
DELIVERING:5, //配送中
FINISH:6 //已完成
}订单进入抢单池,附近骑手可以主动接单:
router.post('/rider/grabOrder',async(req,res)=>{
let orderId = req.body.orderId;
let riderId = req.body.riderId;
const orderInfo = await Order.findByPk(orderId);
if(orderInfo.dispatch_status !== dispatchStatus.GRAB_ORDER){
return res.json({code:500,message:"该订单不可抢单"})
}
await Order.update({
rider_id:riderId,
dispatch_status:dispatchStatus.RIDER_ACCEPT
},{
where:{id:orderId}
})
res.json({message:"抢单成功"})
})筛选在线、距离门店较近的空闲骑手,优先分配订单。实际项目中还需要叠加骑手负载、准时率权重。
// 获取门店周边在线骑手,简化版自动派单
async function autoDispatchOrder(orderId,shopLat,shopLng){
//查询周边在线空闲骑手(位置数据存储在Redis)
const nearRiderList = await getNearOnlineRider(shopLat,shopLng);
if(nearRiderList.length ===0){
//无可用骑手,切换到抢单模式
await Order.update({dispatch_status:dispatchStatus.GRAB_ORDER},{where:{id:orderId}});
return false;
}
//选取距离最近骑手
const targetRider = nearRiderList[0];
await Order.update({
rider_id:targetRider.id,
dispatch_status:dispatchStatus.RIDER_ACCEPT
},{where:{id:orderId}});
return true;
}业务说明:当自动派单找不到合适骑手时,订单自动降级到抢单池,避免订单长时间无人承接。
调度系统强依赖地图接口,主要能力:
路线请求示例:
function getDeliveryRoute(start,end){
return axios.get(
"[https://api.map.com/direction](https://api.map.com/direction)",
{
params:{
origin:start,
destination:end
}
}
)
}用户完成下单,调用微信支付,支付回调更新订单状态,订单进入商户接单环节。
支付业务流程:
用户提交订单
↓
生成业务订单记录
↓
调用微信统一下单
↓
小程序调起微信支付
↓
接收微信回调通知
↓
更新订单状态为【已支付】,推送消息给商户支付回调示例代码:
router.post('/pay/notify', async(req,res)=>{
let orderId = req.body.orderId;
await Order.update({
status:"已支付"
},{
where:{
id:orderId
}
});
res.send("success");
})平台需要统计商户入驻情况、订单量、平台佣金收入等指标。
统计每日订单数量SQL:
SELECT
DATE(create_time) AS day, COUNT(*) AS order_count
FROM orders
GROUP BY day;统计平台佣金总收入:
SELECT
SUM(commission) AS total_commission
FROM orders
WHERE status='已完成';统计商户订单与营收:
SELECT shop_id,COUNT(*) AS order_num,SUM(goods_amount) AS shop_sales
FROM orders
WHERE status='已完成'
GROUP BY shop_id;SpringBoot项目目录参考
local‑food‑system
├── user 用户模块
├── merchant 商户入驻与商户管理
├── goods 商品模块
├── order 订单基础模块
├── dispatch 订单调度派单核心
├── rider 骑手管理模块
├── payment 支付模块
└── admin 平台后台基础商户入驻与订单调度完成后,还可以迭代扩展更多业务能力。
本地外卖配送系统中,商户入驻模块解决多商家接入平台的问题,订单调度模块解决订单履约分配的问题,两者共同决定平台是否能够规模化运营。
开发时不仅要完成基础CRUD接口,还需要处理入驻审核流程、调度模式切换、派单降级兜底等业务边界场景。结合数据表合理设计、Redis缓存地理位置数据,可以实现一套稳定的本地外卖平台,后续还可以基于业务需求叠加营销、结算、报表等扩展功能。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。