随着本地生活服务不断向线上发展,外卖、代购、代送、帮取、同城配送等服务逐渐形成较为完整的即时服务体系。对于想要进入这一领域的开发者来说,外卖跑腿小程序是一种比较适合快速搭建服务平台的产品形态。
一个完整的外卖跑腿小程序,并不只是实现“商品下单”这么简单,还需要围绕用户、商家、骑手、平台管理建立完整的业务流程。本文将从0开始,结合实际代码,介绍如何设计和搭建一个基础的外卖跑腿小程序。
一、外卖跑腿小程序需要哪些端?
从系统架构来看,可以将平台划分为四个主要部分:
- 用户端:浏览商家、选择商品、提交订单、查看配送进度。
- 商家端:管理商品、处理订单、修改订单状态。
- 骑手端:查看配送订单、抢单、取货、配送、完成订单。
- 管理后台:管理用户、商家、骑手、订单以及平台数据。
基本业务流程可以设计成:
用户
↓
浏览商家/选择跑腿服务
↓
提交订单
↓
支付
↓
商家接单
↓
骑手抢单
↓
取货
↓
配送
↓
用户收货
↓
订单完成
如果是纯跑腿订单,则可以省略商家制作环节:
用户发布跑腿订单
↓
平台生成订单
↓
骑手抢单
↓
骑手取件
↓
骑手配送
↓
订单完成
这种设计可以让外卖和跑腿两种业务共用订单体系。
二、先设计数据库结构
从0开始开发时,不建议一开始就写大量页面代码,而应该先确定核心数据结构。
例如用户表:
CREATE TABLE user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
nickname VARCHAR(50) NOT NULL,
mobile VARCHAR(20),
avatar VARCHAR(255),
balance DECIMAL(10,2) DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
商家表:
CREATE TABLE merchant (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
logo VARCHAR(255),
address VARCHAR(255),
status TINYINT DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
商品表:
CREATE TABLE goods (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
merchant_id BIGINT NOT NULL,
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL,
stock INT DEFAULT 0,
image VARCHAR(255),
status TINYINT DEFAULT 1
);
订单表则需要同时考虑外卖和跑腿:
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_no VARCHAR(50) UNIQUE NOT NULL,
user_id BIGINT NOT NULL,
merchant_id BIGINT,
rider_id BIGINT,
order_type TINYINT NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
delivery_fee DECIMAL(10,2) DEFAULT 0,
status VARCHAR(30) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
其中:
order_type = 1 外卖订单
order_type = 2 跑腿订单
通过订单类型进行区分,可以避免为了两种业务建立完全独立的订单系统。
三、小程序首页怎么设计?
用户进入小程序后,可以将首页划分为几个核心区域:
┌──────────────────────┐
│ 定位 / 搜索 │
├──────────────────────┤
│ 外卖 │ 跑腿 │ 商城 │ 服务 │
├──────────────────────┤
│ 活动 Banner │
├──────────────────────┤
│ 推荐商家 │
│ ┌──────────────────┐ │
│ │ 商家Logo 商家名称 │ │
│ │ 商品信息 ¥25 │ │
│ └──────────────────┘ │
│ │
│ 更多商家 │
└──────────────────────┘
小程序页面可以使用微信小程序原生 WXML:
<view class="page">
<view class="search-box">
<input
placeholder="搜索商家、商品或服务"
bindinput="handleSearch"
/>
</view>
<view class="service-menu">
<view bindtap="goTakeaway">
<text>外卖</text>
</view>
<view bindtap="goErrand">
<text>跑腿</text>
</view>
<view bindtap="goMall">
<text>商城</text>
</view>
<view bindtap="goService">
<text>其他服务</text>
</view>
</view>
<view class="merchant-list">
<block wx:for="{
{merchantList}}" wx:key="id">
<view class="merchant-item">
<image src="{
{item.logo}}" />
<view>
<text>{
{item.name}}</text>
<text>{
{item.address}}</text>
</view>
</view>
</block>
</view>
</view>
对应的 JavaScript:
Page({
data: {
merchantList: []
},
onLoad() {
this.loadMerchantList();
},
loadMerchantList() {
wx.request({
url: 'https://api.example.com/merchant/list',
method: 'GET',
success: (res) => {
this.setData({
merchantList: res.data.data
});
}
});
},
goTakeaway() {
wx.navigateTo({
url: '/pages/takeaway/index'
});
},
goErrand() {
wx.navigateTo({
url: '/pages/errand/index'
});
}
});
实际项目中,接口地址应该替换成自己的后端 API。
四、外卖订单怎么实现?
用户选择商品之后,可以形成购物车数据:
const cart = [
{
goodsId: 1001,
name: '招牌炒饭',
price: 18,
quantity: 2
},
{
goodsId: 1002,
name: '可乐',
price: 5,
quantity: 1
}
];
然后计算商品金额:
function calculateAmount(cart) {
return cart.reduce((total, item) => {
return total + item.price * item.quantity;
}, 0);
}
const goodsAmount = calculateAmount(cart);
console.log(goodsAmount);
如果商品金额为:
18 × 2 + 5 × 1 = 41元
再加上配送费:
const deliveryFee = 5;
const orderAmount = goodsAmount + deliveryFee;
console.log(orderAmount);
最终订单金额就是:
46元
五、跑腿订单需要哪些信息?
跑腿业务和普通外卖最大的区别,是用户购买的并不是固定商品,而是一项配送服务。
例如用户可以填写:
取货地址
送货地址
联系人
联系电话
物品类型
物品说明
配送备注
配送费用
小程序提交订单时,可以向后端发送:
wx.request({
url: 'https://api.example.com/errand/create',
method: 'POST',
data: {
pickupAddress: '人民路100号',
deliveryAddress: '解放路200号',
contactName: '张先生',
contactMobile: '13800000000',
itemDescription: '文件资料',
remark: '请轻拿轻放'
},
success(res) {
console.log('订单创建成功', res.data);
}
});
后端接收到数据后创建跑腿订单。
六、后端如何创建订单?
以 Node.js + Express 为例,可以简单设计一个订单接口:
const express = require('express');
const app = express();
app.use(express.json());
app.post('/api/errand/create', async (req, res) => {
const {
pickupAddress,
deliveryAddress,
contactName,
contactMobile,
itemDescription,
remark
} = req.body;
if (!pickupAddress || !deliveryAddress) {
return res.json({
code: 400,
message: '取货地址和送货地址不能为空'
});
}
const orderNo =
'PS' + Date.now();
// 实际项目这里应该写入数据库
const order = {
orderNo,
pickupAddress,
deliveryAddress,
contactName,
contactMobile,
itemDescription,
remark,
status: 'WAITING_PAY'
};
return res.json({
code: 200,
message: '订单创建成功',
data: order
});
});
app.listen(3000, () => {
console.log('server started');
});
这里暂时使用内存对象模拟订单,正式项目则应该写入 MySQL、PostgreSQL 等数据库。
七、订单状态是整个系统的核心
外卖跑腿系统一定要提前规划好订单状态。
例如外卖订单:
待支付
↓
待接单
↓
制作中
↓
待取餐
↓
配送中
↓
已完成
异常情况下还可能出现:
已取消
已退款
跑腿订单可以设计为:
待支付
↓
待接单
↓
配送中
↓
已完成
骑手抢单之后,将订单更新为配送状态:
async function grabOrder(orderId, riderId) {
const order = await getOrder(orderId);
if (!order) {
throw new Error('订单不存在');
}
if (order.status !== 'WAITING_RIDER') {
throw new Error('订单已经被其他骑手抢走');
}
await updateOrder(orderId, {
riderId: riderId,
status: 'DELIVERING'
});
return true;
}
这里有一个非常重要的逻辑:
骑手抢单必须进行订单状态校验。
否则两个骑手同时点击抢单,就可能出现一个订单被两个骑手同时抢到的问题。
八、如何避免骑手同时抢到一个订单?
数据库层面可以通过条件更新解决。
例如:
UPDATE orders
SET
rider_id = ?,
status = 'DELIVERING'
WHERE
id = ?
AND status = 'WAITING_RIDER';
执行之后,再判断影响行数:
const result = await db.execute(sql, [
riderId,
orderId
]);
if (result.affectedRows === 0) {
return {
success: false,
message: '订单已被其他骑手抢走'
};
}
return {
success: true,
message: '抢单成功'
};
这种方式比单纯在前端判断更加可靠,因为最终的订单状态必须由服务端控制。
九、骑手端需要实现什么?
骑手端可以围绕三个核心页面设计:
抢单大厅
附近订单
订单A
取货:XX路
送达:XX小区
配送费:¥8
[立即抢单]
订单B
取货:XX商场
送达:XX写字楼
配送费:¥12
[立即抢单]
我的订单
待取货
配送中
已完成
我的收入
今日收入:¥128
配送订单:16单
待结算:¥35
累计收入:¥8,620
这样就可以形成一个相对完整的骑手工作流程。
十、商家端需要解决订单处理
商家收到订单后,需要及时看到:
新订单
↓
确认接单
↓
开始制作
↓
制作完成
↓
等待骑手取餐
例如后端可以提供订单状态更新接口:
app.put('/api/order/:id/status', async (req, res) => {
const orderId = req.params.id;
const {
status } = req.body;
const allowStatus = [
'ACCEPTED',
'MAKING',
'READY',
'COMPLETED',
'CANCELLED'
];
if (!allowStatus.includes(status)) {
return res.json({
code: 400,
message: '非法订单状态'
});
}
await updateOrderStatus(orderId, status);
res.json({
code: 200,
message: '订单状态更新成功'
});
});
正式开发时,还应该根据当前状态限制下一步状态,避免出现:
待接单 → 已完成
这种不符合业务流程的情况。
十一、管理后台如何搭建?
总管理后台可以围绕以下模块建设:
工作台
├── 用户管理
├── 商家管理
├── 骑手管理
├── 商品管理
├── 外卖订单
├── 跑腿订单
├── 配送管理
├── 财务管理
├── 营销管理
└── 系统设置
后台首页则可以展示:
今日订单 今日营业额 配送订单
368 ¥28,650 126
待处理订单 配送中订单 入驻商家
26 48 156
通过这些数据,平台运营人员能够快速了解当前业务运行情况。
十二、从0搭建时应该按照什么顺序开发?
如果是小白或者第一次开发,不建议同时开发所有模块。
可以按照下面的顺序推进:
第一阶段:基础架构
先完成:
数据库
+
后端 API
+
小程序基础框架
第二阶段:用户功能
完成:
登录
首页
商家列表
商品列表
购物车
订单
个人中心
第三阶段:外卖业务
完成:
商品下单
支付
商家接单
商品制作
骑手抢单
配送
订单完成
第四阶段:跑腿业务
增加:
发布跑腿订单
取货地址
送货地址
配送费用
骑手抢单
配送
完成订单
第五阶段:后台管理
最后完善:
用户管理
商家管理
骑手管理
订单管理
财务统计
平台设置
这样的开发方式更加容易控制项目复杂度。
十三、外卖和跑腿最好共用一套订单体系
从长期维护的角度来看,不建议将外卖系统和跑腿系统完全拆成两个独立平台。
可以设计统一订单模型:
const order = {
orderNo: 'PS202608180001',
orderType: 'TAKEAWAY',
userId: 10001,
merchantId: 20001,
riderId: 30001,
amount: 38,
deliveryFee: 5,
status: 'DELIVERING'
};
跑腿订单则:
const order = {
orderNo: 'PS202608180002',
orderType: 'ERRAND',
userId: 10001,
merchantId: null,
riderId: 30002,
amount: 15,
deliveryFee: 15,
status: 'DELIVERING'
};
这样订单、骑手、配送、支付、售后等基础能力都可以复用。

十四、总结
对于刚开始接触外卖跑腿小程序开发的开发者来说,最重要的不是一开始就把所有功能全部做出来,而是先梳理清楚业务角色、订单流程和数据关系。
一个基础的外卖跑腿小程序可以按照:
用户端
↓
商品/跑腿服务
↓
统一订单中心
↓
商家处理 / 骑手抢单
↓
配送
↓
订单完成
↓
后台统计
进行搭建。
技术实现上,可以采用微信小程序 + Node.js/Java/PHP后端 + MySQL + Redis等常见技术组合。随着业务发展,再逐步增加优惠券、会员、评价、配送区域、商家结算、骑手收入统计等功能。
对于真正从0开始做项目的人来说,先完成“用户下单—商家接单—骑手抢单—配送完成”这一条最核心的业务链路,再逐步扩展其他功能,通常比一开始堆叠大量功能更加稳妥。