项目背景
客户是武汉户部巷的一家老字号热干面连锁品牌,在武汉有8家门店。需求:
- 顾客可通过小程序浏览菜单、下单支付、查看订单状态
- 支持堂食(扫码点餐)和外带/外卖两种模式
- 会员积分、储值、优惠券等功能
- 门店管理后台(接单、出餐、打票)
- 数据看板(销售、客流、菜品排行)
预算:15000元 | 周期:4周
技术选型
| 模块 | 技术方案 | 原因 |
|---|---|---|
| 前端框架 | Uni-app (Vue3) | 一套代码发布微信+支付宝+抖音小程序 |
| 后端框架 | Node.js + Koa2 | 轻量高效,适合中小项目 |
| 数据库 | MySQL 8.0 | 稳定可靠,成本低 |
| 缓存 | Redis | 热点数据缓存、分布式锁 |
| 消息队列 | Bull (基于Redis) | 订单异步处理、延迟任务 |
| 支付 | 微信支付JSAPI | 小程序原生支付能力 |
| 打印 | 58打印机SDK | 厨房小票+顾客小票 |
| 部署 | 阿里云ECS + RDS | 武汉节点,延迟低 |
系统架构
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 用户小程序 │ │ 店员端PAD │ │ 管理后台PC │
│ (Uni-app) │ │ (Uni-app) │ │ (Vue3+AntD) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└────────┬────────┴────────┬─┘
▼ ▼
┌─────────────────────────────┐
│ Nginx 反向代理 │
└──────────────┬──────────────┘
▼
┌─────────────────────────────┐
│ Koa2 API Server (集群) │
│ - 订单服务 │
│ - 商品服务 │
│ - 会员服务 │
│ - 支付服务 │
│ - 打印服务 │
└──────────────┬──────────────┘
▼
┌─────────────────────────────┐
│ MySQL + Redis │
└─────────────────────────────┘
核心功能实现
🛒 扫码点餐流程
- 顾客扫描桌角的二维码(携带 tableId 参数)
- 小程序自动定位到对应门店和餐桌
- 加载该门店菜单(分类→菜品→规格→加料)
- 顾客选择菜品加入购物车
- 确认订单 → 选择支付方式(微信支付/会员余额/储值)
- 支付成功 → 订单推送至厨房打印机
- 厨房制作完成 → 出餐 → 顾客取餐
💳 支付实现
// 微信统一下单
async function createWxOrder(orderId, totalFee, openid) {
const params = {
appid: config.wxAppId,
mch_id: config.wxMchId,
nonce_str: randomStr(),
body: `户部巷XX店-订单${orderId}`,
out_trade_no: orderId,
total_fee: Math.round(totalFee * 100), // 单位:分
spbill_create_ip: ctx.ip,
notify_url: config.wxNotifyUrl,
trade_type: 'JSAPI',
openid: openid
}
params.sign = sign(params, config.wxApiKey)
const res = await unifiedOrder(params)
return res // 返回 prepay_id 用于前端调起支付
}
🖨️ 小票打印
// 厨房打印(58打印机)
async function printKitchenOrder(order) {
const printer = new Printer({
printerIp: order.store.kitchenPrinterIp
})
printer.init()
printer.setAlignment('CT') // 居中
printer.println(`【${order.store.name}】`)
printer.println(`桌号: ${order.tableNo} 订单: ${order.id}`)
printer.println('--------------------------------')
order.items.forEach(item => {
printer.print(`${item.name} x${item.quantity}`)
if (item.remarks) printer.print(` 备注: ${item.remarks}`)
})
printer.println('--------------------------------')
printer.println(`下单时间: ${formatTime(order.createdAt)}`)
printer.cut() // 切纸
await printer.execute()
}
👤 会员系统
// 会员积分规则
const POINTS_RULES = {
consume: 1, // 消费1元=1积分
signup: 100, // 注册送100积分
birthday: 500, // 生日当天消费双倍积分
invite: 200 // 邀请新用户注册送200积分
}
// 会员等级
const LEVEL_RULES = [{ name: '普通会员', minPoints: 0, discount: 1 },
{ name: '银卡会员', minPoints: 1000, discount: 0.95 },
{ name: '金卡会员', minPoints: 5000, discount: 0.9 },
{ name: '钻石会员', minPoints: 20000, discount: 0.85 }]
性能优化
- 菜单数据缓存:门店菜单变化不频繁,Redis 缓存 5 分钟
- 商品图片 CDN:菜品图片上传至阿里云 OSS 并开启 CDN
- 请求节流:防抖防止重复下单
- 数据库索引:订单表的 store_id、status、created_at 字段建立复合索引
- 连接池:MySQL 连接池大小设为 20
上线运营数据
小程序上线 3 个月后的运营数据:
| 指标 | 数值 |
|---|---|
| 注册用户 | 12,680 人 |
| 月活用户 | 8,520 人 |
| 日均订单 | 320 单 |
| 小程序转化率 | 18.5% |
| 会员复购率 | 42% |
| 人均消费 | 18.5 元 |
| 储值总额 | 28.6 万元 |
踩坑记录
- 微信支付回调:一定要处理重复通知(幂等性检查),否则可能出现重复发货
- 小程序审核:涉及餐饮需要《食品经营许可证》,提前准备好
- 打印机兼容:不同品牌打印机指令集不同,建议统一硬件型号
- 并发处理:高峰期(中午11:30-12:30)订单量暴增 5 倍,消息队列必不可少
技术总结
这个项目证明了:用15000元预算、4周时间,完全可以做出一个功能完善的餐饮小程序。关键是:
- 选对技术栈(Uni-app 多端发布节省大量时间)
- MVP 思维(先做核心功能,迭代优化)
- 善用第三方 SDK(支付、打印、地图等不要自己造轮子)
- 关注业务逻辑而非炫酷技术