ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Flask后端+微信小程序订餐系统:从源码解析到毕业设计改造指南

Flask后端+微信小程序订餐系统:从源码解析到毕业设计改造指南 简介面向毕业设计的Python Flask微信小程序订餐系统源码与配套数据库已打包为zip压缩包适合计算机相关专业学生用于课程设计、毕业设计或小程序开发入门。源码均已本地编译可运行评审分达到95分以上整体难度适中并经助教老师审定能满足学习、演示和二次开发需求。压缩包共641个文件大小约3.94MB核心后端由65个Python文件实现前端包含HTML、CSS、JavaScript页面小程序端采用WXML、WXSS结构另附JSON配置、SQL数据库脚本、Markdown说明和大量GIF操作演示可帮助快速理解订餐下单、菜品管理、订单处理等业务流程。当前已有164人学习浏览文件目录清晰下载后可直接运行并对照学习这套资料除完整源码和数据库外还提供接口配置和演示素材既能支撑论文撰写与答辩展示也便于在此基础上扩展功能或改造界面。1. 毕业设计级微信小程序订餐系统Flask 后端与小程序前端是怎么凑到一起的拿到一个毕业设计基于python-flask的微信小程序订餐系统源码数据库.zip这样的压缩包第一反应别急着解压。这套东西本质上是一条完整的技术链路微信小程序负责用户点餐、购物车、订单展示Flask 负责提供 HTTP 接口、处理业务逻辑MySQL 或者 SQLite 负责存菜品、用户、订单数据。三方之间通过 JSON 格式的数据交互小程序发起请求Flask 接收后操作数据库再把结果回传给小程序渲染。任何一个环节断了系统就跑不起来这也是毕业设计答辩时老师最喜欢问的地方。这套方案适合两类人一类是拿它当毕业设计基座准备在源码基础上改功能、换 UI、加模块另一类是想快速理解“小程序 Flask 数据库”三者如何协作的开发者。它的价值不在于代码多复杂而在于它把前后端分离、RESTful API 设计、数据库建模、会话保持这些真实项目的核心问题压缩到了一个足够小的闭环里。接下来我会从项目结构、后端接口、小程序调用、数据库设计四个层面拆开讲最后给出一套可操作的改造和验证清单。2. 从 zip 到跑通先看懂 Flask 项目的骨架与启动方式解压后第一件事不是看代码而是先理清目录。一个典型的 Flask 小程序项目解压后通常长这样wechat-order-system/ ├── app.py # Flask 入口文件 ├── config.py # 配置文件数据库连接、密钥等 ├── requirements.txt # Python 依赖列表 ├── models.py # 数据库模型 ├── api/ # 蓝图目录 │ ├── __init__.py │ ├── user.py # 用户相关接口 │ ├── menu.py # 菜单相关接口 │ ├── cart.py # 购物车接口 │ └── order.py # 订单接口 ├── utils/ # 工具函数 │ └── response.py # 统一返回格式 ├── miniprogram/ # 微信小程序前端代码 │ ├── app.js │ ├── app.json │ ├── pages/ │ │ ├── index/ # 首页展示菜品 │ │ ├── cart/ # 购物车页 │ │ ├── order/ # 订单页 │ │ └── mine/ # 个人中心 │ └── utils/ │ └── request.js # 封装 wx.request └── database/ ├── init.sql # 建库建表脚本 └── db.sqlite3 # 可能存在的 SQLite 数据库文件requirements.txt里通常躺着flask、flask-cors、pymysql或者flask-sqlalchemy这几个核心库。在动手装依赖之前先确认 Python 版本和虚拟环境是否就绪。Python 版本建议 3.8 以上太低会导致部分依赖出现兼容问题。2.1 用 python 安装教程的方式快速搭建 Flask 开发环境创建独立虚拟环境避免系统级 Python 环境被污染这是 Flask 开发约定俗成的第一道防线。具体命令如下# 创建虚拟环境 python -m venv venv # 激活虚拟环境Windows 用 venv\Scripts\activatemacOS/Linux 用 source venv/bin/activate source venv/bin/activate # 安装依赖 pip install -r requirements.txt # 启动 Flask 开发服务器 python app.py如果requirements.txt缺失或者内容不完整也可以手动安装常用依赖pip install flask flask-cors flask-sqlalchemy pymysql启动成功后终端会显示类似下面的输出标明监听端口。默认是5000端口* Running on http://127.0.0.1:5000提示如果启动时报ModuleNotFoundError: No module named flask说明当前激活的虚拟环境里没装 Flask检查是否重复激活了系统 Python。2.2 Flask 入口文件的重点配置跨域、JSON 序列化与蓝图注册app.py是整个后端的起点我一般会把跨域处理、蓝图注册、配置加载这三件事放最前面。跨域是个高频坑。浏览器里访问不会触发跨域但微信小程序里wx.request发请求时如果后端没有正确返回 CORS 头请求会在协商阶段直接被拦下。Flask 里处理跨域最省事的方式是安装flask-cors# app.py from flask import Flask from flask_cors import CORS from api import user_api, menu_api, cart_api, order_api app Flask(__name__) # 允许所有域名跨域毕业设计阶段够用 CORS(app, supports_credentialsTrue) app.config.from_pyfile(config.py) # 注册蓝图url_prefix 决定了接口的访问前缀 app.register_blueprint(user_api, url_prefix/api/user) app.register_blueprint(menu_api, url_prefix/api/menu) app.register_blueprint(cart_api, url_prefix/api/cart) app.register_blueprint(order_api, url_prefix/api/order) if __name__ __main__: # 生产环境别用 debugTrue会暴露调试器 app.run(host0.0.0.0, port5000, debugTrue)host0.0.0.0意味着监听本机所有网络接口。真机调试时小程序请求地址需要填电脑的局域网 IP比如http://192.168.1.100:5000能不能通就靠这一行。2.3 蓝图的拆分逻辑为什么毕业设计也要按模块拆路由很多新手喜欢把所有路由塞进app.py后端一长就不可维护。毕业设计答辩时老师大概率会问“你的项目里为什么用蓝图”这是一个加分项。蓝图的作用是让路由按业务模块分组代码清晰职责单一。以订单接口为例蓝图的定义通常长这样# api/order.py from flask import Blueprint, request, jsonify from utils.response import success, error order_api Blueprint(order, __name__) order_api.route(/create, methods[POST]) def create_order(): # 接收小程序传来的订单数据 data request.get_json() # 校验参数、落库、扣库存等逻辑 return success({order_id: 10086})Blueprint(order, __name__)的第一个参数是蓝图名称第二个参数是导入路径。注册蓝图时的url_prefix/api/order意味着create_order的完整路径是POST /api/order/create。小程序端要请求的 URL就是拼接后的完整路径。3. 不需要小程序编译器也能在浏览器里验证的 Flask 请求链路很多人以为调试 Flask 必须要打开微信开发者工具其实不是。Flask 接口完全可以用curl或 Postman 调试等接口稳定了再回小程序端联调效率会高很多。这也是“前后端分离开发”的核心工作方式。3.1 用 curl 命令模拟小程序端发起的请求先启动 Flask 服务然后打开另一个终端窗口用curl发起一条带 JSON 体的 POST 请求模拟小程序提交购物车数据curl -X POST http://127.0.0.1:5000/api/cart/add \ -H Content-Type: application/json \ -d {user_id: 1, menu_id: 3, quantity: 2, spec: 大份}服务端路由如果正常会返回一个 JSON 对象内容类似{ code: 0, message: ok, data: { cart_id: 17, total_price: 56.0 } }code: 0是我习惯的约定0 表示成功非 0 表示业务异常。这样小程序端判断逻辑就非常简单只需要判断code是否为 0不需要解析状态码之外的内容。3.2 前端请求封装的统一出口把 wx.request 包成 Promise微信小程序的wx.request是回调式 API在页面里用很容易出现嵌套回调。常见做法是封装成一个返回 Promise 的request.js模块页面里只需要async/await就能拿数据。这是项目里最值得抄的一段代码直接决定了后面所有页面的代码风格。// miniprogram/utils/request.js const BASE_URL http://192.168.1.100:5000; // 改成你的 Flask 服务器地址 function request(path, method GET, data {}) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL path, method: method, data: data, header: { Content-Type: application/json }, success(res) { // 后端约定的业务码 if (res.statusCode 200 res.data.code 0) { resolve(res.data.data); } else { wx.showToast({ title: res.data.message || 请求失败, icon: none }); reject(res.data); } }, fail(err) { console.error(网络异常, err); reject(err); } }); }); } module.exports { request, BASE_URL };常见的错误主要集中在这一层localhost指向的是开发者工具所在的电脑真机预览时小程序运行在手机上必须把BASE_URL改成电脑的局域网 IP。另一个常被忽略的点是微信开发者工具里需要勾选“不校验合法域名”否则连本机地址都会被拦截。header里的Content-Type如果和后端request.get_json()的解析方式不一致服务端拿到的可能是字符串而不是对象。3.3 页面层调用后端接口的最小可用示例以首页点餐为例页面的index.js里先引入封装好的请求模块然后按菜品分类拉取列表// miniprogram/pages/index/index.js const { request } require(../../utils/request.js); Page({ data: { menuList: [], loading: false }, async onLoad() { this.loadMenu(); }, async loadMenu() { this.setData({ loading: true }); try { const data await request(/api/menu/list, GET); this.setData({ menuList: data.list }); } catch (err) { console.error(菜单加载失败, err); } finally { this.setData({ loading: false }); } }, async addToCart(e) { const { id, price } e.currentTarget.dataset; try { await request(/api/cart/add, POST, { menu_id: id, quantity: 1, price: price }); wx.showToast({ title: 已加入购物车 }); } catch (err) { console.error(加购失败, err); } } });setData是小程序更新视图的唯一入口loading状态用来控制加载动画。请求路径里的/api/menu/list必须和后端蓝图中定义的路由严格一致一个斜杠错了就是 404。加购按钮里的dataset是 WXML 里>-- 用户表 CREATE TABLE user ( id INTEGER PRIMARY KEY AUTOINCREMENT, openid VARCHAR(64) UNIQUE NOT NULL, nickname VARCHAR(50), phone VARCHAR(20), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 菜品表 CREATE TABLE menu ( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, image_url TEXT, category VARCHAR(30), stock INTEGER DEFAULT 100, status TINYINT DEFAULT 1 ); -- 购物车表 CREATE TABLE cart ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, menu_id INTEGER NOT NULL, quantity INTEGER DEFAULT 1, selected TINYINT DEFAULT 1, FOREIGN KEY (user_id) REFERENCES user(id), FOREIGN KEY (menu_id) REFERENCES menu(id) ); -- 订单主表 CREATE TABLE order_main ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_no VARCHAR(32) UNIQUE NOT NULL, user_id INTEGER NOT NULL, total_amount DECIMAL(10,2) NOT NULL, status TINYINT DEFAULT 0, address TEXT, remark TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES user(id) ); -- 订单明细表 CREATE TABLE order_item ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_id INTEGER NOT NULL, menu_id INTEGER NOT NULL, menu_name VARCHAR(100), price DECIMAL(10,2), quantity INTEGER, FOREIGN KEY (order_id) REFERENCES order_main(id), FOREIGN KEY (menu_id) REFERENCES menu(id) );status字段是订单流程的开关常见取值0表示待支付1表示已支付待出餐2表示制作中3表示已出餐4表示已完成-1表示已取消。这个是毕业设计里数据库课程设计容易得分的设计点。4.2 查订单数据的不同写法原生 SQL 与 ORM 之间的取舍源码里如果用的是flask-sqlalchemy那么订单查询会走 ORM 的query语法如果是原生 SQL则直接用pymysql执行拼接字符串。两种方式是常见的数据库课程设计考察点。用 ORM 示例# models.py from flask_sqlalchemy import SQLAlchemy db SQLAlchemy() class Order(db.Model): __tablename__ order_main id db.Column(db.Integer, primary_keyTrue) order_no db.Column(db.String(32), uniqueTrue) status db.Column(db.SmallInteger, default0) total_amount db.Column(db.Numeric(10, 2))查询一个用户的所有订单只取状态为“已完成”的orders Order.query.filter( Order.user_id user_id, Order.status 4 ).order_by(Order.created_at.desc()).all()对应原生 SQL 写法是SELECT * FROM order_main WHERE user_id 1 AND status 4 ORDER BY created_at DESC;两个写法的性能差异在这个数据量级上几乎体现不出来重点是理解 ORM 帮你生成了 SQL 之后filter的条件是如何映射成WHERE子句的。Numeric类型在返回时会变成DecimalJSON 序列化前如果不转float会把后端直接卡在TypeError: Object of type Decimal is not JSON serializable。4.3 购物车表的selected字段一个决定结算金额的小细节购物车表里有个容易被忽略的字段selected它标记了购物车中某一项是否被勾选。结算时只算selected 1的项这个逻辑小程序端和后端必须保持一致。后端计算订单金额时不能信任前端传过来的total_price必须根据购物车明细重新算一遍。# api/order.py def calc_order_amount(user_id): items ( db.session.query(Cart, Menu) .join(Menu, Cart.menu_id Menu.id) .filter( Cart.user_id user_id, Cart.selected 1 ) .all() ) total sum(float(item.Menu.price) * item.Cart.quantity for item in items) return round(total, 2)这个逻辑有两个要点一是total必须用后端算出来的金额覆盖前端传来的值防止有心人改价格二是round(total, 2)避免浮点误差把订单金额变成56.00000000001。这个函数就是典型的“后端不可信原则”落地。5. 源码到手后怎么改造成自己的毕设三处必改与一个验证技巧改造源码前先把原始压缩包备份一份因为改坏了随时能回滚。接下来按优先级处理三件事改库表、换接口、换前端样式。5.1 清理原始数据与扩展菜品字段原始数据库里大概率带着作者的测试数据直接用会导致你的需求文档和演示数据对不上。清理数据用 SQLDELETE FROM order_item; DELETE FROM order_main; DELETE FROM cart; DELETE FROM menu; DELETE FROM user;然后重置自增主键让新数据的 ID 从 1 开始DELETE FROM sqlite_sequence;想加字段直接ALTER TABLE比如菜品表加一个monthly_sales字段用于展示销量ALTER TABLE menu ADD COLUMN monthly_sales INTEGER DEFAULT 0;如果用的是 MySQL重置自增主键的语句是ALTER TABLE menu AUTO_INCREMENT 1跟 SQLite 的语法不同这一步很容易混。5.2 拦截器与登录态最容易被答辩老师追问的点毕业设计的登录功能通常用微信的wx.login拿临时code后端拿code调微信接口换openid。完整的交互过程小程序端有wx.login返回的 code后端需要把这个 code 换成 openid。这一步在网络环境正常的情况下可以跑通但关键点在于后续请求的会话保持——大多数源码会简单地把user_id存在小程序本地存储里每次请求带上。这个方案很直接但答辩时说出它的缺陷反而加分。改进方式是让后端在用户登录后返回一个 token后续请求通过请求头Authorization带上。Flask 里常见用的是itsdangerous生成签名 token代码量很小# utils/token.py from itsdangerous import TimedJSONWebSignatureSerializer as JWT serializer JWT(your-secret-key, expires_in7200) def generate_token(user_id): return serializer.dumps({user_id: user_id}).decode() def verify_token(token): try: data serializer.loads(token) return data.get(user_id) except Exception: return None加上这个逻辑用户接口里就可以通过验证 token 获得当前用户而不是从请求体里读user_id。这一步能从“能用”切到“像工程”。5.3 验证改版效果的三个命令改造完成后不要只在小程序端点点点按下面这三步验证一遍每一步对应一个检查点# 第一步检查所有 Flask 接口能否响应用 curl 随机挑 5 个接口打一轮 curl -X GET http://127.0.0.1:5000/api/menu/list -H Content-Type: application/json # 第二步查数据库里新增的订单是否完整落库 sqlite3 database/db.sqlite3 SELECT * FROM order_main ORDER BY id DESC LIMIT 3; # 第三步检查购物车某件商品的下架状态是否正确同步到小程序端 sqlite3 database/db.sqlite3 SELECT id, status FROM menu WHERE id 1;第一步通过说明网络链路通第二步通过说明数据库设计没有丢字段第三步通过说明状态同步逻辑没写反。三步全过这个小程序订餐系统就不仅跑得起来还改得动。本文还有配套的精品资源点击获取
返回列表