1. 项目背景与核心价值
最近在整理毕设资料时,翻出了当年做的餐饮管理系统项目。这个基于Django框架开发的系统,不仅顺利通过了答辩,后来还被本地几家餐厅实际采用。现在回想起来,这个项目最值得分享的,不是那些标准化的增删改查功能,而是如何针对餐饮行业的特殊需求做个性化定制。
餐饮管理系统看似常见,但真正要落地使用,会遇到很多教科书上没写的实际问题。比如高峰期并发订单处理、菜品口味偏好记录、桌台动态调整等。这个系统从最初的学生作业,逐步迭代成为能支撑日均300+订单的商业系统,中间踩过的坑和积累的经验,对准备做类似项目的同学应该会很有帮助。
2. 系统架构设计解析
2.1 技术选型决策过程
选择Django作为主要框架,主要基于以下几个实际考量:
- ORM层优势:餐饮业务涉及大量关联数据操作。比如一个订单关联桌台、菜品、会员等多个实体。Django的ORM让我们能用Python类的方式操作这些关系,比直接写SQL效率高很多。实际开发中,类似这样的复杂查询:
python复制Order.objects.filter(
table__status='occupied',
create_time__gte=today
).select_related('member').prefetch_related('dishes')
-
Admin后台的快速原型:毕设周期短,Django自带的Admin后台让我们在48小时内就搭建出了可演示的雏形。后期通过定制Admin模板和添加插件,逐步满足了餐厅的实际管理需求。
-
灵活的APP划分:将系统拆分为
menu、order、member等独立APP,这种模块化设计在后期的功能扩展中体现出巨大价值。比如后来新增的供应链管理模块,可以很干净地作为独立APP集成进来。
2.2 数据库设计关键点
餐饮业务的数据关系比想象中复杂。经过多次迭代,最终的核心模型设计如下:
菜品模型示例:
python复制class Dish(models.Model):
SPICY_LEVEL = [(i, f"{i}星") for i in range(6)]
name = models.CharField(max_length=100, verbose_name="菜品名称")
category = models.ForeignKey('Category', on_delete=models.PROTECT)
price = models.DecimalField(max_digits=8, decimal_places=2)
cost = models.DecimalField(max_digits=8, decimal_places=2) # 成本价,用于毛利分析
spicy_level = models.PositiveSmallIntegerField(choices=SPICY_LEVEL)
is_active = models.BooleanField(default=True) # 软删除标记
# 重要:记录菜品被点的次数,用于推荐算法
order_count = models.PositiveIntegerField(default=0)
class Meta:
indexes = [
models.Index(fields=['category', 'is_active']),
models.Index(fields=['order_count']),
]
特别要注意的几个设计细节:
- 使用
DecimalField而非FloatField处理金额,避免浮点精度问题 - 为频繁查询的字段添加数据库索引
- 实现软删除而非物理删除,保留历史数据
- 提前预留分析字段(如order_count)
3. 核心功能实现细节
3.1 高并发订单处理
在实际运营中发现,午晚高峰时段会出现短时间内大量下单的情况。最初的朴素实现方式直接导致数据库锁争用。最终采用的解决方案:
- 异步任务队列:使用Celery处理订单创建流程
python复制@app.task(bind=True, max_retries=3)
def create_order_task(self, order_data):
try:
with transaction.atomic():
order = Order.objects.create(**order_data['base_info'])
OrderItem.objects.bulk_create([
OrderItem(order=order, **item)
for item in order_data['items']
])
# 更新菜品热度
Dish.objects.filter(
id__in=[item['dish_id'] for item in order_data['items']]
).update(order_count=F('order_count') + 1)
except Exception as exc:
self.retry(exc=exc)
-
数据库优化:
- 将MySQL的隔离级别改为READ COMMITTED
- 为订单表做了水平分表(按月份拆分)
- 使用
select_for_update(nowait=True)处理库存扣减
-
前端防重复提交:
- 提交后禁用按钮
- 使用唯一请求ID幂等处理
3.2 个性化推荐实现
会员系统的推荐功能经历了三个版本的迭代:
初始版本:基于菜品销量排行
python复制def recommend_dishes_v1(member=None):
return Dish.objects.filter(is_active=True).order_by('-order_count')[:10]
改进版本:结合会员口味偏好
python复制def recommend_dishes_v2(member):
if not member or not member.taste_preference:
return recommend_dishes_v1()
pref = member.taste_preference # 例如 {'spicy': 4, 'sour': 3}
return Dish.objects.filter(
is_active=True,
spicy_level__lte=pref.get('spicy', 5)
).order_by('-order_count')[:10]
当前版本:引入协同过滤算法
python复制def recommend_dishes_v3(member_id):
# 使用django-recommends库实现
from recommends.storages.djangoorm import DjangoORMStorage
storage = DjangoORMStorage()
return storage.recommend_for_user(
user_id=member_id,
model=Dish,
count=10
)
4. 关键问题与解决方案
4.1 桌台状态同步问题
餐厅现场经常出现这些情况:
- 服务员A刚安排客人到8号桌
- 服务员B的手机界面还没刷新,又把新客人带到8号桌
- 结果引发冲突
解决方案:
- 使用WebSocket实现实时状态推送
- 在桌台模型添加版本号字段
python复制class Table(models.Model):
STATUS_CHOICES = [
('vacant', '空闲'),
('occupied', '已占用'),
('reserved', '预定'),
]
version = models.PositiveIntegerField(default=0)
def save(self, *args, **kwargs):
self.version += 1
super().save(*args, **kwargs)
- 前端每次操作前校验版本号
4.2 报表生成性能优化
最初的日报表生成要跑30+秒(数据量约5万条记录)。通过以下优化降到3秒内:
- 添加汇总表:每天凌晨跑定时任务预计算关键指标
- 使用CTE代替子查询:
python复制from django.db import connection
def generate_daily_report(date):
with connection.cursor() as cursor:
cursor.execute("""
WITH daily_sales AS (
SELECT
dish_id,
SUM(quantity) as total_quantity,
SUM(quantity*price) as total_amount
FROM orders_orderitem
WHERE DATE(create_time) = %s
GROUP BY dish_id
)
SELECT
d.name,
ds.total_quantity,
ds.total_amount,
(ds.total_amount - ds.total_quantity*d.cost) as profit
FROM daily_sales ds
JOIN menu_dish d ON ds.dish_id = d.id
ORDER BY ds.total_amount DESC
""", [date])
return cursor.fetchall()
- 使用Django的缓存框架:
python复制from django.core.cache import cache
def get_daily_report(date):
cache_key = f'daily_report_{date}'
report = cache.get(cache_key)
if not report:
report = generate_daily_report(date)
cache.set(cache_key, report, timeout=3600*24)
return report
5. 部署与运维实战
5.1 生产环境部署方案
经过多次调整,最终稳定的部署架构如下:
code复制Nginx (负载均衡)
├── Django (Gunicorn) x3
├── Celery Worker x2
├── Redis (缓存/消息队列)
└── MySQL (主从复制)
关键配置示例(Gunicorn):
bash复制# gunicorn.conf.py
workers = 3
threads = 2
max_requests = 500
max_requests_jitter = 50
timeout = 120
5.2 监控与日志处理
使用Sentry捕获异常,配合自定义的中间件记录业务日志:
python复制import logging
from django.utils.deprecation import MiddlewareMixin
logger = logging.getLogger('operation')
class OperationLogMiddleware(MiddlewareMixin):
def process_response(self, request, response):
if request.method in ('POST', 'PUT', 'DELETE'):
logger.info(
"%s %s %s %s",
request.user,
request.method,
request.path,
response.status_code
)
return response
日志分割配置(logrotate):
code复制/var/log/django-app/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 640 root adm
sharedscripts
postrotate
systemctl reload gunicorn >/dev/null 2>&1 || true
endscript
}
6. 项目演进与定制经验
6.1 从毕设到商用系统的转变
最初版本只实现了基础CRUD,商用过程中新增的重要功能:
- 扫码点餐(使用django-rest-framework开发API)
- 库存预警(基于信号机制实现)
- 员工绩效统计
- 会员积分体系
6.2 定制开发心得
-
需求沟通技巧:
- 用真实数据演示代替口头描述
- 先做纸质原型确认流程
- 每周交付可见成果
-
代码组织建议:
- 业务逻辑放在models层
- 视图保持精简
- 表单验证使用Django Form
-
测试策略:
- 核心业务流程的测试覆盖率要100%
- 使用Factory Boy创建测试数据
- 定期运行性能测试
这个项目最大的体会是:好的系统不是一次设计出来的,而是在解决实际问题的过程中不断演进出来的。建议刚开始做类似项目的同学,不要追求一次性做出完美系统,而是先做出最小可用版本,然后根据真实反馈持续迭代。