1. 项目概述
最近在开发一个餐厅点餐管理系统,采用了前后端分离的架构设计。后端使用Python的Flask框架构建RESTful API,前端则基于Vue.js实现用户交互界面。这个系统主要解决传统餐厅纸质菜单点餐效率低、易出错的问题,同时为餐厅管理者提供数据统计和分析功能。
系统支持三种角色:顾客可以浏览菜单、下单支付;服务员可以处理订单、查看桌台状态;管理员则能管理菜单、查看经营报表。整个开发过程涉及数据库设计、API开发、前端交互等多个环节,下面我会详细分享每个环节的实现细节和踩过的坑。
2. 技术选型与架构设计
2.1 后端技术栈选择
选择Flask作为后端框架主要基于以下几点考虑:
- 轻量灵活:相比Django,Flask更适合中小型项目快速开发
- RESTful支持:通过Flask-RESTful扩展可以方便构建API
- 生态完善:有丰富的扩展库支持各种功能需求
核心依赖库包括:
- Flask-SQLAlchemy:ORM工具,简化数据库操作
- Flask-JWT-Extended:实现JWT认证
- Flask-Migrate:数据库迁移管理
- Flask-CORS:解决跨域问题
提示:在开发初期就确定好依赖库版本,避免后期兼容性问题。我使用的是Flask 2.0.1和Python 3.8.5的组合,稳定性较好。
2.2 前端技术栈选择
Vue 3作为前端框架的优势:
- 响应式编程:数据驱动视图,简化DOM操作
- 组件化开发:提高代码复用性和可维护性
- 丰富的生态:Vue Router、Vuex等官方库完善
主要技术组合:
- Vue 3 + Composition API
- Vue Router:页面导航
- Vuex:状态管理
- Axios:HTTP请求
- Element Plus:UI组件库
2.3 数据库选型
对比了MySQL和PostgreSQL后选择了MySQL,原因:
- 部署简单:云服务支持完善
- 性能足够:餐厅场景数据量不大
- 运维成本低:有成熟的GUI工具如Navicat
3. 系统模块设计与实现
3.1 后端核心模块
3.1.1 用户认证模块
采用JWT(JSON Web Token)实现无状态认证,关键代码:
python复制from flask_jwt_extended import JWTManager, create_access_token
app = Flask(__name__)
app.config['JWT_SECRET_KEY'] = 'your-strong-secret-key' # 生产环境要更复杂
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1)
jwt = JWTManager(app)
@app.route('/login', methods=['POST'])
def login():
username = request.json.get('username')
password = request.json.get('password')
# 验证逻辑...
access_token = create_access_token(identity=username)
return {'access_token': access_token}
注意事项:
- JWT密钥要足够复杂且定期更换
- 设置合理的token过期时间
- 敏感接口需要添加@jwt_required()装饰器
3.1.2 菜单管理模块
数据库表设计:
sql复制CREATE TABLE dishes (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL,
category VARCHAR(50),
description TEXT,
image_url VARCHAR(255),
stock INT DEFAULT 0,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CRUD接口实现要点:
- 使用Flask-SQLAlchemy简化数据库操作
- 添加参数验证,防止非法输入
- 实现软删除而非物理删除
3.1.3 订单处理模块
订单表设计考虑:
sql复制CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
table_number VARCHAR(20) NOT NULL,
status ENUM('pending', 'confirmed', 'preparing', 'ready', 'completed', 'cancelled') DEFAULT 'pending',
total_amount DECIMAL(10,2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE order_items (
id INT AUTO_INCREMENT PRIMARY KEY,
order_id INT NOT NULL,
dish_id INT NOT NULL,
quantity INT NOT NULL,
unit_price DECIMAL(10,2) NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(id),
FOREIGN KEY (dish_id) REFERENCES dishes(id)
);
3.2 前端核心模块
3.2.1 菜单展示页面
关键实现:
- 使用Vue3的setup语法
- 通过Axios获取菜单数据
- 实现分类筛选和搜索功能
javascript复制import { ref, onMounted } from 'vue'
import axios from 'axios'
export default {
setup() {
const dishes = ref([])
const categories = ref([])
const activeCategory = ref('all')
const fetchDishes = async () => {
try {
const res = await axios.get('/api/dishes')
dishes.value = res.data
// 提取分类...
} catch (error) {
console.error('获取菜单失败:', error)
}
}
onMounted(fetchDishes)
return { dishes, categories, activeCategory }
}
}
3.2.2 购物车功能
使用Vuex管理购物车状态:
javascript复制// store/cart.js
export default {
state: () => ({
items: [],
total: 0
}),
mutations: {
addItem(state, dish) {
const existingItem = state.items.find(item => item.id === dish.id)
if (existingItem) {
existingItem.quantity++
} else {
state.items.push({ ...dish, quantity: 1 })
}
state.total += dish.price
},
// 其他操作...
}
}
4. 开发流程详解
4.1 环境搭建
后端环境:
bash复制# 创建虚拟环境
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
# 安装依赖
pip install flask flask-sqlalchemy flask-migrate flask-jwt-extended flask-cors
前端环境:
bash复制# 安装Vue CLI
npm install -g @vue/cli
# 创建项目
vue create restaurant-frontend
cd restaurant-frontend
npm install axios vuex vue-router element-plus
4.2 数据库设计与迁移
使用Flask-Migrate管理数据库变更:
python复制# 初始化迁移仓库
flask db init
# 生成迁移脚本
flask db migrate -m "initial migration"
# 应用迁移
flask db upgrade
4.3 前后端联调
解决跨域问题:
python复制from flask_cors import CORS
app = Flask(__name__)
CORS(app, resources={r"/api/*": {"origins": "*"}})
前端Axios配置:
javascript复制axios.defaults.baseURL = 'http://localhost:5000/api'
axios.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
5. 部署上线
5.1 后端部署
使用Gunicorn+Nginx部署Flask应用:
bash复制# 安装Gunicorn
pip install gunicorn
# 启动服务
gunicorn -w 4 -b 0.0.0.0:5000 wsgi:app
Nginx配置示例:
nginx复制server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
5.2 前端部署
Vue项目打包:
bash复制npm run build
Nginx配置静态文件服务:
nginx复制server {
listen 80;
server_name yourdomain.com;
location / {
root /path/to/dist;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend;
}
}
6. 关键问题与解决方案
6.1 并发订单处理
问题:高峰期可能出现订单冲突
解决方案:
- 使用数据库事务保证数据一致性
- 添加乐观锁控制库存扣减
python复制@app.route('/api/orders', methods=['POST'])
@jwt_required()
def create_order():
try:
db.session.begin()
# 检查库存
for item in order_items:
dish = Dish.query.with_for_update().get(item.dish_id)
if dish.stock < item.quantity:
raise Exception(f"{dish.name}库存不足")
dish.stock -= item.quantity
# 创建订单...
db.session.commit()
return jsonify(order.to_dict()), 201
except Exception as e:
db.session.rollback()
return jsonify({"error": str(e)}), 400
6.2 性能优化
-
数据库层面:
- 为常用查询字段添加索引
- 合理设计表关联
- 使用缓存减轻数据库压力
-
前端层面:
- 图片懒加载
- 组件按需加载
- 使用keep-alive缓存组件
6.3 安全防护
- 输入验证:
python复制from flask import request, abort
from marshmallow import Schema, fields, ValidationError
class DishSchema(Schema):
name = fields.Str(required=True)
price = fields.Decimal(required=True)
# 其他字段...
@app.route('/api/dishes', methods=['POST'])
def create_dish():
try:
data = DishSchema().load(request.json)
except ValidationError as err:
abort(400, str(err.messages))
# 处理逻辑...
- 其他安全措施:
- 使用HTTPS加密传输
- 定期备份数据库
- 实现操作日志审计
7. 扩展功能建议
- 实时通知:使用WebSocket实现订单状态实时更新
- 扫码点餐:生成桌台专属二维码,顾客扫码直接点餐
- 数据分析:集成可视化图表展示销售趋势
- 多端适配:开发微信小程序版本
我在实际开发中发现,餐厅业务流程的梳理比技术实现更重要。建议在编码前充分与餐厅工作人员沟通,了解他们的实际工作流程和痛点,这样开发出来的系统才能真正解决问题。