做外卖点餐这个方向的时候,我一开始其实纠结过一阵。市面上现成的开源外卖系统不少,但大多数要么是PHP系、要么是Java系,前端和后台耦合得很死,真要改起来等于重写。后来接了一个课程设计和毕设辅导的项目,需求方指定要Node.js做后端、Vue做前端,数据库用MySQL,我直接把技术栈定成了Node.js + Express + MySQL + Vue + ElementUI这套组合。今天把这个项目的设计思路和实现过程完整拆一遍,包括数据库怎么建模、接口怎么设计、前端页面怎么组织、购物车和订单状态怎么流转,还有我在环境配置上踩过的那些坑。无论是正在做类似毕设的同学,还是想快速搭一套Web外卖点餐系统的开发者,都应该能从这篇文章里拿到可以直接落地的方案。
1. 技术选型的真实逻辑:这套组合到底解决了什么问题
1.1 为什么不是Java、不是PHP,而是Node.js
先聊选型。外卖点餐系统的核心场景是高频读、低频写:用户打开小程序/网页浏览菜单,选完商品,提交订单。这个业务模型天然适合Node.js的非阻塞I/O模型。Node.js单线程配合事件循环,处理大量并发读请求时表现很稳定,而且和前端同样是JavaScript,数据类型不需要来回转换,前端同学上手后端几乎零成本。
Express作为Node.js生态里最成熟的Web框架,中间件机制非常灵活。项目里要做登录鉴权、跨域处理、日志记录、参数校验,每个功能都是一个中间件函数,按顺序挂载就行。相比Koa那套基于async/await的洋葱模型,Express虽然老一点,但资料多、踩坑记录多,遇到问题基本都能搜到现成答案,对做项目来说这比框架新特性更重要。
MySQL这边没什么好争议的,订单、商品、用户这些数据都是强结构化数据,需要事务保证一致性。下单这个动作涉及扣库存、生成订单、更新购物车多个步骤,必须放在数据库事务里执行,MySQL的InnoDB引擎在这块非常成熟。
1.2 整体架构:前后端分离,接口驱动开发
项目的整体架构采用了前后端完全分离的模式:
code复制前端(Vue 2 + ElementUI + Vuex + Vue Router)
│ HTTP/JSON + JWT Token
▼
后端(Node.js + Express)
│ mysql2 连接池
▼
数据库(MySQL 5.7+)
后端只提供RESTful API,不关心页面长什么样。前端所有数据都通过axios请求后端接口获取。这样做的好处是,以后如果要把Web端迁到微信小程序,后端接口层完全不用动,只需要重写视图层。整个项目分成了user端(用户点餐页面)和admin端(商家管理页面)两个前台入口,通过路由和权限控制区分。
1.3 项目目录结构设计
项目用了monorepo管理,后端和前端放在同一个仓库下,目录清晰:
code复制ordering-system/
├── server/ # 后端服务
│ ├── app.js # Express入口
│ ├── routes/ # 路由定义
│ ├── controllers/ # 控制器(业务逻辑)
│ ├── db/ # 数据库连接配置
│ ├── middleware/ # 鉴权、错误处理中间件
│ └── utils/ # 工具函数(订单号生成等)
├── web/ # 前端工程(Vue2 + ElementUI)
│ ├── src/
│ │ ├── api/ # 后端接口封装
│ │ ├── assets/ # 静态资源
│ │ ├── components/ # 公共组件
│ │ ├── router/ # 前端路由表
│ │ ├── store/ # Vuex状态管理
│ │ └── views/ # 页面组件
│ └── package.json
└── sql/ # 数据库初始化脚本
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据库建模与Express后端:把点餐业务的骨架搭起来
2.1 核心表结构设计:从用户到订单的完整链路
数据库是外卖系统的地基,表结构设计直接决定后面业务逻辑的复杂度。我设计了一套6表方案:
用户表(user)
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | int | 主键,自增 |
| username | varchar(50) | 用户名,唯一 |
| password | varchar(255) | 密码(MD5加密) |
| phone | varchar(20) | 手机号 |
| avatar | varchar(255) | 头像地址 |
| role | tinyint | 角色:0用户,1商家管理员 |
| create_time | datetime | 注册时间 |
商品分类表(category) 和 商品表(menu)
分类表很简单:id、name、sort_order。重点是商品表,字段包括:
sql复制CREATE TABLE `menu` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL COMMENT '商品名称',
`description` varchar(255) DEFAULT '' COMMENT '商品描述',
`price` decimal(10,2) NOT NULL COMMENT '售价',
`image` varchar(255) DEFAULT '' COMMENT '图片地址',
`category_id` int(11) NOT NULL COMMENT '所属分类',
`status` tinyint(4) DEFAULT '1' COMMENT '1上架 0下架',
`stock` int(11) DEFAULT '100' COMMENT '库存',
`sales` int(11) DEFAULT '0' COMMENT '销量',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
价格字段必须用decimal(10,2),不能图省事用float——浮点数在MySQL里做比较和累加会有精度问题,订单金额差一毛钱这种问题排查起来非常痛苦。
购物车表(cart)
购物车设计成了一张关联表:user_id + menu_id + quantity。每次加购前先查一下是否已有同款商品,如果有就做数量累加,没有才插
