159-基于Python的订餐系统的设计与实现

基于 Python 的订餐系统:从数据爬取到个性化推荐与可视化的一站式实践

面向学习者与实践者的一篇完整技术分享,覆盖项目架构、目录结构、技术栈、部署运行、核心代码、推荐算法、数据可视化与扩展方向。文末附联系渠道。


目录

  • 项目简介与功能概览
  • 目录结构与核心模块
  • 技术栈说明(后端/前端/数据库/数据分析/可视化)
  • 快速开始(环境、数据库、启动)
  • 核心模块详解与代码
    • 用户认证与安全(验证码、登录、注册、密码重置)
    • 数据模型与关系
    • 推荐系统(相似度与个性化推荐)
    • 数据爬取(大众点评示例)
    • 项目可视化
  • 数据库设计要点
  • 扩展方向与最佳实践
  • 常见问题(FAQ)
  • 预留可视化展示位
  • 版权与联系方式

项目简介与功能概览

本项目是一个基于 Flask + MySQL 的订餐系统,提供从店铺/菜品浏览、购物车和订单流程,到评论、收藏、礼品积分、数据爬取、可视化与推荐系统的一整套能力。面向课程设计、毕设与实际小型项目落地均可快速上手与扩展。

已实现的核心能力包括:

  • 用户体系:注册、登录、验证码校验、退出、个人资料编辑、密码找回/重置、积分记录。
  • 店铺与菜品:分类/区域筛选、店铺详情、菜品详情、评论与评分、收藏与统计。
  • 购物与订单:购物车增删改、订单创建/状态跟踪、历史记录与评价。
  • 礼品中心:礼品分类与兑换、积分扣减、礼品订单与核销。
  • 消息互动:用户留言与管理员回复。
  • 数据分析:数据爬取、可视化图表展示、基于收藏行为的个性化推荐。

目录结构与核心模块

以下为关键目录与文件(节选):

food-add/
  app.py                    # 应用入口,蓝图注册、验证码、登录/注册、个人中心等
  config.py                 # 数据库与全局配置(MySQL + PyMySQL)
  ext.py                    # SQLAlchemy 初始化
  models.py                 # 核心业务模型(用户/店铺/菜品/订单/评论/收藏/礼品…)
  blueprints/               # 业务蓝图:前台、用户、后台等
  templates/                # Jinja2 模板页面(首页、详情、下单、后台管理等)
  static/                   # 前端静态资源(JS/CSS/图片/图表库)
  spider/                   # 数据爬取脚本(requests + lxml + pandas)
  util/                     # 推荐算法与分析工具(NumPy + scikit-learn)
  dependency.txt            # 依赖安装命令清单
  design_159_order.sql      # 数据库结构与示例数据(建议导入)

技术栈说明

  • 后端
    • Flask 3.x(Web 框架)
    • Flask-SQLAlchemy(ORM)、Flask-Migrate(迁移)
    • Flask-WTF(表单)、Flask-CORS(跨域)
    • PyMySQL(MySQL 驱动)
  • 数据库
    • MySQL(业务数据存储)
  • 数据处理与推荐
    • NumPy(数值计算)
    • Pandas(数据处理/CSV 读写,用于爬取结果存档)
    • scikit-learn(余弦相似度计算,店铺相似度与推荐)
  • 前端/可视化
    • Bootstrap、jQuery
    • ECharts(主要图表库,含词云/liquidfill 等插件)
    • Chart.js(辅助图表库)

是否使用了 NumPy/Pandas:项目实际使用了二者,分别见 util/analyze.py(NumPy)与 spider/food.py(Pandas)。


快速开始

  1. 环境准备
# Python 3.8+ 建议
python -m venv .venv
. .venv/bin/activate   # Windows: .venv\\Scripts\\activate

# 安装依赖(逐条执行 dependency.txt 内命令,或自行转为 requirements.txt)
pip install flask==3.0.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install flask-migrate==4.0.4 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install flask-sqlalchemy==3.0.3 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install flask-wtf==1.2.1 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install pandas==1.1.5 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install pymysql==1.0.3 -i https://pypi.tuna.tsinghua.edu.cn/simple
  1. 数据库配置与初始化
  • 本地 MySQL 新建数据库(例如:design_159_order1)。
  • 修改 config.py 连接串以匹配本机账号密码(生产环境切勿使用弱口令)。
  • 导入 design_159_order.sql 的表结构与示例数据。
  1. 运行项目
python app.py
# 浏览器访问 http://127.0.0.1:5000/

核心模块详解与代码

1. 用户认证与安全

验证码、登录、注册、找回/重置密码等逻辑集中在 app.py。示例:

# 生成四位随机数字验证码
def generate_captcha():
    return ''.join(random.choices('0123456789', k=4))

# 验证码图片路由
@app.route('/captcha')
def captcha():
    captcha_text = generate_captcha()
    session['captcha'] = captcha_text
    image_buffer = generate_captcha_image(captcha_text)
    response = make_response(image_buffer.getvalue())
    response.headers['Content-Type'] = 'image/png'
    return response
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        user_captcha = request.form.get('captcha')

        if 'captcha' not in session or user_captcha != session['captcha']:
            return render_template('login.html', message="温馨提示:验证码错误")

        user = User.query.filter_by(username=username).first()
        if user and check_password_hash(user.password, password):
            session['user_id'] = user.id
            session['username'] = username
            user.login_time = datetime.now()
            db.session.commit()
            return redirect(url_for('index.index'))
        else:
            return render_template('login.html', message="温馨提示:密码错误或用户不存在")
    return render_template('login.html')
@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        email = request.form.get('email')
        phone = request.form.get('phone')
        address = request.form.get('address')
        # ... 参数校验与重复用户检查 ...
        new_user = User(
            username=username,
            password=generate_password_hash(password),
            email=email,
            phone=phone,
            address=address,
        )
        db.session.add(new_user)
        db.session.commit()
        return redirect(url_for('login'))
    return render_template('register.html')
2. 数据模型与关系(节选)

models.py 定义了用户、店铺、菜品、评论、收藏、礼品等表及关系:

class Store(db.Model):
    __tablename__ = 'store'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), nullable=False)
    address = db.Column(db.String(255), nullable=False)
    phone = db.Column(db.String(20), nullable=False)
    average_price = db.Column(db.Float, nullable=True)
    monthly_sales = db.Column(db.Integer, nullable=False, default=0)
    rating = db.Column(db.Float, nullable=True)
    category = db.Column(db.String(100), nullable=True)
    districts = db.Column(db.String(100), nullable=True)
    dishes = db.relationship('Dish', backref='store_dishes', lazy=True)
    reviews = db.relationship('StoreReview', backref='store_reviews', lazy=True)
class Dish(db.Model):
    __tablename__ = 'dish'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), nullable=False)
    description = db.Column(db.Text, nullable=True)
    original_price = db.Column(db.Float, nullable=False)
    promotional_price = db.Column(db.Float, nullable=True)
    monthly_sales = db.Column(db.Integer, nullable=False, default=0)
    store_id = db.Column(db.Integer, db.ForeignKey('store.id'), nullable=False)
    image = db.Column(db.String(255), nullable=False)
3. 推荐系统(收藏驱动的相似度推荐)

推荐逻辑位于 util/analyze.py,通过店铺评分构建向量,利用余弦相似度计算店铺相似度,并结合用户收藏生成推荐列表:

from sklearn.metrics.pairwise import cosine_similarity
import numpy as np

def calculate_store_similarities():
    stores = Store.query.all()
    store_data = {s.id: s.rating for s in stores}
    store_ids = list(store_data.keys())
    if not store_ids:
        return {}
    ratings_matrix = np.array([store_data[store_id] for store_id in store_ids]).reshape(-1, 1)
    similarity_matrix = cosine_similarity(ratings_matrix)
    store_similarity_dict = {}
    for i, store_id in enumerate(store_ids):
        store_similarity_dict[store_id] = {
            other_id: similarity
            for other_id, similarity in zip(store_ids, similarity_matrix[i])
            if other_id != store_id
        }
    return store_similarity_dict
def recommend_stores(user_id, num_recommendations=5):
    user_favorites = StoreFavorite.query.filter_by(user_id=user_id).all()
    followed_store_ids = [f.store_id for f in user_favorites]
    if not followed_store_ids:
        return get_top_favorite_stores(num_recommendations)
    store_similarity_dict = calculate_store_similarities()
    recommendation_scores = {}
    for store_id in followed_store_ids:
        for similar_store_id, similarity in store_similarity_dict.get(store_id, {}).items():
            if similar_store_id not in followed_store_ids:
                recommendation_scores[similar_store_id] = recommendation_scores.get(similar_store_id, 0) + similarity
    recommended_store_ids = sorted(recommendation_scores, key=recommendation_scores.get, reverse=True)[:num_recommendations]
    return Store.query.filter(Store.id.in_(recommended_store_ids)).all()

可扩展建议:

  • 引入更多画像特征(均价、销量、品类向量、地理位置 embedding)。
  • 使用矩阵分解/隐语义模型或轻量级深度召回(如 LightFM、YouTube DNN 思路)。
4. 数据爬取(大众点评示例)

spider/food.py 展示了 requests + lxml + pandas 的爬取流程,抓取店铺信息并写入 CSV:

def crawl(url, city):
    response = requests.get(url=url, cookies=cookies1, headers=headers)
    txt = etree.HTML(response.text)
    categories = [("小吃快餐", 'g112'), ("火锅", 'g110'), ("自助餐", 'g111')]
    districts = [("渝中区", 'r42'), ("江北区", 'r44')]
    for category_name, category_code in categories:
        for district_name, district_code in districts:
            fl = f'https://www.dianping.com/chongqing/ch10/{category_code}{district_code}'
            List_index(fl, category_name, district_name, city, start_page=1, start_index=1)
            time.sleep(3)
def down_load(wkb):
    columns = ['city','food_category','districts','name','src','pj','price','main','business','recommend','link']
    df = pd.DataFrame(wkb, columns=columns)
    if not os.path.exists('大众点评.csv'):
        df.to_csv('大众点评.csv', index=False, encoding='utf-8-sig')
    else:
        df.to_csv('大众点评.csv', index=False, header=False, mode='a', encoding='utf-8-sig')

合规提示:请遵守目标网站 Robots 协议与使用条款,合理设置频率与缓存,禁止用于商业抓取。

5. 项目可视化

👾 项目源码获取,码界筑梦坊各平台同名,博客底部含联系方式卡片,欢迎咨询!

前端主要使用 ECharts 构建交互式图表,并提供 Chart.js 作为补充。

基于Python的订餐系统

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


数据库设计要点(概述)

  • 用户(User)关联订单、评论、收藏、积分与礼品兑换记录。
  • 店铺(Store)关联菜品、评论与收藏统计;保留均价、销量、评分、分类、区域等特征。
  • 菜品(Dish)关联价格、库存、月销量与评论。
  • 订单与支付(Order/Payment)管理全流程状态(表定义见 models.py)。
  • 礼品与分类(Gift/GiftCategory)以及兑换流水(GiftRedeem、GiftOrder)。

建议:为高频查询列建立索引(店铺分类/区域、订单状态、用户外键等),订单编号唯一键,分表分库与归档策略可按数据量演进。


扩展方向与最佳实践

  • 安全:开启 CSRF、完善权限校验、密码强度与多因子认证(Flask-Login/Flask-Principal 可引入)。
  • 性能:读写分离、查询缓存、异步任务(Celery/RQ)处理图片/爬取/推荐批计算。
  • 推荐:多特征融合、召回+排序两阶段、AB 测试与效果监控。
  • 可视化:接入可视化大屏、时序数据库/OLAP(如 ClickHouse)做多维分析。
  • 工程化:Docker 化、CI/CD、灰度发布与线上监控(Prometheus + Grafana)。

常见问题(FAQ)

  • 看不到验证码/中文混乱?请确保 Pillow 字体加载正常,模板使用 UTF-8 编码;Windows 下可回退默认字体。
  • 登录一直失败?检查 session/secret_key、验证码校验、数据库连接与用户密码是否使用 generate_password_hash 存储。
  • 图表不展示?确认页面引用了 echarts.min.jsChart.min.js,并在 DOM 加载后初始化。
  • 爬取失败/数据为空?检查 Cookies/Headers 与反爬策略,降低频率并增加重试与代理策略。

版权与联系方式

本文与项目仅用于学习与技术交流,数据抓取请遵守相关法律法规与网站条款,严禁用于非法用途与商业侵权。

交流与合作:码界筑梦坊各大平台同名

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码界筑梦坊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值