1. 项目背景与核心需求
外卖系统作为典型的O2O(Online to Offline)应用,已经成为计算机专业毕业设计的热门选题方向。基于Django框架开发外卖系统具有多重优势:首先,Django作为Python生态中最成熟的Web框架,其"开箱即用"的特性特别适合毕业设计这类有时间限制的项目;其次,外卖系统业务逻辑清晰但又不失复杂度,能够充分展示学生的全栈开发能力。
从技术架构角度看,一个完整的外卖系统需要包含以下核心模块:
- 用户系统(顾客、商家、骑手三类角色)
- 商品管理与分类系统
- 订单处理流程(创建、支付、接单、配送、评价)
- 地理位置服务(店铺定位、配送路线)
- 支付系统集成
- 后台管理系统
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发环境搭建与Django项目初始化
2.1 Python环境配置
推荐使用Python 3.8+版本,这是目前Django 4.x官方支持最稳定的Python版本。使用虚拟环境是必须的:
bash复制python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
2.2 Django安装与项目创建
安装最新稳定版Django:
bash复制pip install django==4.2.3
创建项目骨架:
bash复制django-admin startproject takeaway
cd takeaway
python manage.py startapp user # 用户模块
python manage.py startapp shop # 商家模块
python manage.py startapp order # 订单模块
2.3 数据库选型与配置
虽然Django默认使用SQLite,但对于外卖系统建议使用MySQL:
python复制# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'takeaway_db',
'USER': 'root',
'PASSWORD': 'yourpassword',
'HOST': 'localhost',
'PORT': '3306',
}
}
注意:需要先安装mysqlclient包:
pip install mysqlclient
3. 核心数据模型设计
3.1 用户系统模型
python复制# user/models.py
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
USER_TYPE_CHOICES = (
(1, 'customer'),
(2, 'merchant'),
(3, 'rider')
)
user_type = models.PositiveSmallIntegerField(choices=USER_TYPE_CHOICES)
phone = models.CharField(max_length=15)
avatar = models.ImageField(upload_to='avatars/', null=True)
class Meta:
db_table = 'user'
3.2 商家与商品模型
python复制# shop/models.py
class Merchant(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
shop_name = models.CharField(max_length=100)
address = models.TextField()
longitude = models.FloatField()
latitude = models.FloatField()
business_hours = models.CharField(max_length=100)
minimum_order = models.DecimalField(max_digits=8, decimal_places=2)
delivery_fee = models.DecimalField(max_digits=8, decimal_places=2)
class Meta:
db_table = 'merchant'
class FoodCategory(models.Model):
merchant = models.ForeignKey(Merchant, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
class Meta:
db_table = 'food_category'
class Food(models.Model):
category = models.ForeignKey(FoodCategory, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=8, decimal_places=2)
description = models.TextField(blank=True)
image = models.ImageField(upload_to='foods/')
is_active = models.BooleanField(default=True)
class Meta:
db_table = 'food'
3.3 订单系统模型
python复制# order/models.py
class Order(models.Model):
STATUS_CHOICES = (
(1, 'Pending'),
(2, 'Paid'),
(3, 'Preparing'),
(4, 'Ready'),
(5, 'On Delivery'),
(6, 'Completed'),
(7, 'Cancelled')
)
customer = models.ForeignKey(User, on_delete=models.CASCADE, related_name='customer_orders')
merchant = models.ForeignKey(User, on_delete=models.CASCADE, related_name='merchant_orders')
rider = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='rider_orders')
order_number = models.CharField(max_length=20, unique=True)
status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, default=1)
total_amount = models.DecimalField(max_digits=10, decimal_places=2)
delivery_address = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'order'
class OrderItem(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items')
food = models.ForeignKey(Food, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField()
price = models.DecimalField(max_digits=8, decimal_places=2)
class Meta:
db_table = 'order_item'
4. 核心功能实现
4.1 用户认证系统
扩展Django内置的认证系统:
python复制# user/views.py
from django.contrib.auth import login, logout
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from .forms import UserRegistrationForm, UserLoginForm
def register(request):
if request.method == 'POST':
form = UserRegistrationForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
return redirect('home')
else:
form = UserRegistrationForm()
return render(request, 'user/register.html', {'form': form})
def user_login(request):
if request.method == 'POST':
form = UserLoginForm(data=request.POST)
if form.is_valid():
user = form.get_user()
login(request, user)
return redirect('home')
else:
form = UserLoginForm()
return render(request, 'user/login.html', {'form': form})
@login_required
def user_logout(request):
logout(request)
return redirect('login')
4.2 商家后台商品管理
python复制# shop/views.py
@login_required
@user_passes_test(lambda u: u.user_type == 2)
def food_list(request):
merchant = request.user.merchant
foods = Food.objects.filter(category__merchant=merchant)
return render(request, 'shop/food_list.html', {'foods': foods})
@login_required
@user_passes_test(lambda u: u.user_type == 2)
def add_food(request):
merchant = request.user.merchant
if request.method == 'POST':
form = FoodForm(merchant, request.POST, request.FILES)
if form.is_valid():
food = form.save(commit=False)
food.category = form.cleaned_data['category']
food.save()
return redirect('food_list')
else:
form = FoodForm(merchant)
return render(request, 'shop/add_food.html', {'form': form})
4.3 订单处理流程
python复制# order/views.py
@login_required
def create_order(request):
cart = request.session.get('cart', {})
if not cart:
return redirect('menu')
if request.method == 'POST':
form = OrderForm(request.POST)
if form.is_valid():
order = form.save(commit=False)
order.customer = request.user
order.total_amount = calculate_total(cart)
order.order_number = generate_order_number()
order.save()
for food_id, item in cart.items():
food = Food.objects.get(id=food_id)
OrderItem.objects.create(
order=order,
food=food,
quantity=item['quantity'],
price=food.price
)
request.session['cart'] = {}
return redirect('order_detail', order_id=order.id)
else:
form = OrderForm()
return render(request, 'order/create_order.html', {'form': form})
def generate_order_number():
return f'ORD{timezone.now().strftime("%Y%m%d%H%M%S")}{random.randint(1000, 9999)}'
5. 高级功能实现
5.1 地理位置服务集成
使用高德地图API实现店铺定位和配送范围检查:
python复制# shop/utils.py
import requests
def check_delivery_range(merchant, address):
# 获取商家坐标
merchant_lng = merchant.longitude
merchant_lat = merchant.latitude
# 调用高德地理编码API获取地址坐标
geo_url = 'https://restapi.amap.com/v3/geocode/geo'
params = {
'key': 'your_amap_key',
'address': address
}
response = requests.get(geo_url, params=params)
data = response.json()
if data['status'] == '1' and data['count'] > 0:
location = data['geocodes'][0]['location']
customer_lng, customer_lat = map(float, location.split(','))
# 计算距离(使用Haversine公式)
from math import radians, sin, cos, sqrt, atan2
R = 6371.0 # 地球半径(km)
lat1 = radians(merchant_lat)
lon1 = radians(merchant_lng)
lat2 = radians(customer_lat)
lon2 = radians(customer_lng)
dlon = lon2 - lon1
dlat = lat2 - lat1
a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
c = 2 * atan2(sqrt(a), sqrt(1 - a))
distance = R * c # 单位:公里
return distance <= 5 # 假设配送范围为5公里
return False
5.2 支付系统集成(支付宝示例)
python复制# order/payment.py
from datetime import datetime
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
import base64
import urllib.parse
def create_alipay_trade(order):
params = {
'app_id': 'your_app_id',
'method': 'alipay.trade.page.pay',
'charset': 'utf-8',
'sign_type': 'RSA2',
'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'version': '1.0',
'notify_url': 'your_notify_url',
'biz_content': {
'out_trade_no': order.order_number,
'product_code': 'FAST_INSTANT_TRADE_PAY',
'total_amount': str(order.total_amount),
'subject': f'外卖订单-{order.order_number}',
'body': '外卖订单支付'
}
}
# 排序参数
ordered_params = sorted(params.items(), key=lambda x: x[0])
# 拼接签名字符串
sign_str = '&'.join([f'{k}={v}' for k, v in ordered_params if v])
# 读取私钥
with open('alipay_private_key.pem') as f:
private_key = RSA.importKey(f.read())
# 签名
signer = PKCS1_v1_5.new(private_key)
digest = SHA256.new(sign_str.encode('utf-8'))
signature = signer.sign(digest)
sign = base64.b64encode(signature).decode('utf-8')
params['sign'] = sign
# 生成最终请求URL
base_url = 'https://openapi.alipay.com/gateway.do'
query_string = urllib.parse.urlencode(params)
return f'{base_url}?{query_string}'
6. 系统优化与部署
6.1 性能优化
使用Django缓存框架提升系统性能:
python复制# settings.py
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}
# 视图缓存示例
from django.views.decorators.cache import cache_page
@cache_page(60 * 15) # 缓存15分钟
def menu(request, merchant_id):
merchant = get_object_or_404(Merchant, id=merchant_id)
categories = FoodCategory.objects.filter(merchant=merchant).prefetch_related('food_set')
return render(request, 'shop/menu.html', {'merchant': merchant, 'categories': categories})
6.2 安全加固
python复制# settings.py
# 安全相关配置
SECURE_SSL_REDIRECT = True # 强制HTTPS
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'
SECURE_HSTS_SECONDS = 31536000 # 1年
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
6.3 生产环境部署
使用Nginx + Gunicorn部署Django项目:
bash复制# 安装Gunicorn
pip install gunicorn
# 启动Gunicorn
gunicorn --workers 3 --bind unix:/tmp/takeaway.sock takeaway.wsgi:application
Nginx配置示例:
nginx复制server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://unix:/tmp/takeaway.sock;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /static/ {
alias /path/to/your/static/files/;
}
location /media/ {
alias /path/to/your/media/files/;
}
}
7. 毕业设计扩展建议
-
数据分析模块:使用Django的ORM结合Pandas实现销售数据分析功能,生成商家销售报表和热门商品统计。
-
实时通知系统:集成WebSocket实现订单状态实时更新,使用Django Channels框架。
-
推荐系统:基于用户历史订单实现简单的协同过滤推荐算法,推荐可能喜欢的商品。
-
多平台支持:开发RESTful API接口,为移动端APP提供数据支持,使用Django REST framework。
-
压力测试:使用Locust等工具模拟高并发场景,测试系统性能并给出优化方案。
在实际开发中,我发现外卖系统最关键的三个技术点是:订单状态机的正确实现、地理位置服务的精准计算、以及支付系统的安全集成。特别是订单状态转换,必须考虑所有可能的路径和异常情况,比如用户取消订单时,需要检查当前订单状态是否允许取消,并正确处理退款流程。
