1. 项目背景与核心需求
餐饮预订系统作为连接顾客与餐厅的重要桥梁,在现代餐饮服务中扮演着关键角色。传统电话预订方式存在信息记录不完整、高峰期占线、人工调度效率低等问题。我们采用Python+Django后端与Vue前端的全栈方案,通过Pycharm这一专业IDE进行开发,实现了一个具备完整预订流程的数字化解决方案。
这个系统需要解决三个核心痛点:
- 实时桌位状态可视化(避免重复预订)
- 多条件复合查询(日期/时段/人数/区域)
- 订单状态的即时更新与通知
在技术选型上,Django的ORM层能高效处理餐厅桌位、预订记录等结构化数据,Vue的响应式特性则完美适配动态表单和实时状态展示的需求。Pycharm的专业版提供了完整的Django模板支持和Vue语法提示,大幅提升全栈开发效率。
2. 开发环境搭建与项目初始化
2.1 工具链配置要点
推荐使用Pycharm Professional 2023.2+版本,它原生支持Django项目和Vue.js插件。关键配置步骤:
- Python环境:
bash复制# 创建专用虚拟环境
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate.bat # Windows
pip install django==4.2 django-rest-framework
- 前端依赖:
bash复制npm install -g @vue/cli
vue create frontend --preset default
cd frontend && npm install axios vue-router vuex
- Pycharm特殊配置:
- 启用Django支持:File > Settings > Languages & Frameworks > Django
- 配置Vue插件:Plugins中搜索并安装Vue.js
- 设置JavaScript版本为ECMAScript 6
注意:社区版Pycharm缺少Django模板支持,建议使用专业版。如果使用社区版,需要手动配置运行配置(Run/Debug Configuration)
2.2 项目结构设计
采用前后端分离架构,目录结构如下:
code复制restaurant_booking/
├── backend/ # Django项目
│ ├── config/ # 主配置
│ ├── apps/ # 各应用模块
│ └── manage.py
└── frontend/ # Vue项目
├── public/
├── src/
│ ├── api/ # 接口封装
│ ├── store/ # Vuex状态
│ └── views/ # 页面组件
└── package.json
这种结构在Pycharm中需要特殊处理:
- 右键项目根目录 > Mark Directory as > Sources Root
- 对frontend目录 > Mark Directory as > Resource Root
3. 数据模型设计与Django ORM实现
3.1 核心数据模型
在Django的models.py中定义五个核心实体:
python复制from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
phone = models.CharField(max_length=20)
vip_level = models.IntegerField(default=0)
class RestaurantTable(models.Model):
TABLE_TYPES = (
('W', 'Window'),
('H', 'Hall'),
('P', 'Private')
)
code = models.CharField(max_length=10, unique=True)
capacity = models.IntegerField()
table_type = models.CharField(max_length=1, choices=TABLE_TYPES)
is_active = models.BooleanField(default=True)
class Reservation(models.Model):
STATUS_CHOICES = (
('P', 'Pending'),
('C', 'Confirmed'),
('X', 'Cancelled')
)
user = models.ForeignKey(User, on_delete=models.CASCADE)
table = models.ForeignKey(RestaurantTable, on_delete=models.CASCADE)
date = models.DateField()
time_slot = models.CharField(max_length=5) # 如 "18:00"
special_requests = models.TextField(blank=True)
status = models.CharField(max_length=1, choices=STATUS_CHOICES, default='P')
created_at = models.DateTimeField(auto_now_add=True)
3.2 高级ORM技巧
- 复杂查询优化:
python复制# 获取某时段可用桌位
available_tables = RestaurantTable.objects.filter(
is_active=True,
capacity__gte=guest_count
).exclude(
id__in=Reservation.objects.filter(
date=target_date,
time_slot=target_slot,
status__in=['P', 'C']
).values('table_id')
)
- 信号机制使用:
python复制from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=Reservation)
def send_reservation_notification(sender, instance, created, **kwargs):
if created:
# 发送短信/邮件通知
pass
4. Vue前端架构与关键交互实现
4.1 预订流程组件设计
采用Vue 3的组合式API,核心组件结构:
code复制BookingFlow/
├── TableSelector.vue # 桌位选择
├── DateTimePicker.vue # 日期时间选择
├── GuestInfo.vue # 客人信息
└── PaymentMethod.vue # 支付方式
动态表单验证示例:
javascript复制// 在DateTimePicker.vue中
const rules = {
date: [
v => !!v || '请选择日期',
v => new Date(v) >= new Date() || '不能选择过去日期'
],
timeSlot: [
v => !!v || '请选择时段',
v => ['11:00', '13:00', '18:00'].includes(v) || '无效时段'
]
}
4.2 状态管理方案
使用Vuex管理全局状态:
javascript复制// store/modules/booking.js
export default {
state: () => ({
selectedDate: null,
selectedTime: null,
guestCount: 2,
availableTables: []
}),
mutations: {
UPDATE_AVAILABILITY(state, tables) {
state.availableTables = tables
}
},
actions: {
async fetchAvailability({ commit }, { date, time }) {
const res = await api.get(`/api/tables/available?date=${date}&time=${time}`)
commit('UPDATE_AVAILABILITY', res.data)
}
}
}
5. 前后端交互与API设计
5.1 Django REST Framework配置
序列化器定义:
python复制from rest_framework import serializers
class TableSerializer(serializers.ModelSerializer):
class Meta:
model = RestaurantTable
fields = ['id', 'code', 'capacity', 'table_type']
class ReservationSerializer(serializers.ModelSerializer):
table = TableSerializer(read_only=True)
class Meta:
model = Reservation
fields = '__all__'
extra_kwargs = {
'user': {'read_only': True},
'status': {'read_only': True}
}
视图集配置:
python复制from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticated
class ReservationViewSet(ModelViewSet):
queryset = Reservation.objects.all()
serializer_class = ReservationSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
return super().get_queryset().filter(user=self.request.user)
5.2 跨域与安全配置
- 安装CORS中间件:
bash复制pip install django-cors-headers
- 配置settings.py:
python复制INSTALLED_APPS = [
...,
'corsheaders'
]
MIDDLEWARE = [
...,
'corsheaders.middleware.CorsMiddleware'
]
CORS_ALLOWED_ORIGINS = [
"http://localhost:8080",
"http://127.0.0.1:8080"
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication'
]
}
6. 系统部署与性能优化
6.1 生产环境部署方案
推荐使用Docker Compose部署:
dockerfile复制# backend/Dockerfile
FROM python:3.10
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000"]
dockerfile复制# frontend/Dockerfile
FROM node:16
WORKDIR /app
COPY package*.json .
RUN npm install
COPY . .
RUN npm run build
CMD ["npm", "run", "serve"]
docker-compose.yml配置:
yaml复制version: '3'
services:
db:
image: postgres:13
environment:
POSTGRES_PASSWORD: restaurant123
backend:
build: ./backend
ports: ["8000:8000"]
depends_on: [db]
frontend:
build: ./frontend
ports: ["8080:8080"]
6.2 缓存与性能优化
- Redis缓存配置:
python复制CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://redis:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
- 查询优化示例:
python复制# 使用select_related减少查询次数
reservations = Reservation.objects.select_related(
'table', 'user'
).filter(date=target_date)
- Vue组件懒加载:
javascript复制const TableMap = () => import('./components/TableMap.vue')
7. 实际开发中的经验总结
- 时间处理陷阱:
- 前端使用day.js处理日期格式化
- 后端统一使用UTC时间存储
- 关键代码示例:
javascript复制// 前端时区转换
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
dayjs.extend(utc)
const localTime = dayjs.utc(apiTime).local().format('YYYY-MM-DD HH:mm')
- 并发预订冲突处理:
python复制# 使用select_for_update实现悲观锁
from django.db import transaction
@transaction.atomic
def create_reservation(user, table_id, date, time_slot):
table = RestaurantTable.objects.select_for_update().get(pk=table_id)
if Reservation.objects.filter(table=table, date=date, time_slot=time_slot).exists():
raise ValueError("该时段已被预订")
# 创建预订逻辑...
- Pycharm调试技巧:
- 配置Django Server和JavaScript Debug双运行配置
- 使用Vue Devtools与Django Debug Toolbar联动调试
- 对复杂查询使用Pycharm的Database工具直接查看ORM生成的SQL
这个项目让我深刻体会到,一个看似简单的预订系统背后需要考虑的细节远超预期。特别是时间处理时区问题,我们最初版本就因为没有统一时区标准导致预订时间错乱的严重bug。后来通过强制后端只接受ISO格式时间字符串,并在数据库统一存储UTC时间才彻底解决。
