简介:这份资源面向计算机相关专业的毕业生及需要完成课程设计的学生,提供一套基于Python+Vue+MySQL的前后端分离线上购物系统完整方案,可用于毕业设计选题、项目实战练习或技术栈学习。压缩包共676个文件,约15.76MB,涵盖39个py后端源码、35个vue前端组件、151个js脚本、42个css样式及162个svg图标等,另含2个sql数据库文件、论文文档与答辩pptx,并附安装、运行、构建等bat脚本,便于快速部署调试。系统区分用户与管理员角色,用户可浏览商品、下单购买、评价收藏及管理订单,管理员负责商品与订单等后台管理,功能链路完整。已有249人学习下载,适合希望掌握Python后端与Vue前端分离开发、需要完整源码与论文参考的读者,可据此理解项目结构、接口设计与数据库建模,并在此基础上进行二次开发或功能扩展。
1. 从零到答辩:一套 Python+Vue+MySQL 线上购物系统到底要交付什么
很多同学做毕业设计时,第一反应是去搜「免费 Python 源码大全」,下载一个压缩包改改名字就交差。但真正到了答辩现场,老师问一句「你的前后端分离是怎么体现的」「MySQL 的数据库连接池配了多大」,立刻就露馅了。这套基于 Python+Vue+MySQL 的线上购物系统,核心不是「能跑起来」,而是你要能讲清楚每一层在干什么、为什么这么选、数据怎么流转。它适合正在准备计算机相关专业毕业设计、需要一套完整可演示可答辩项目的同学,也适合想通过一个真实项目把 Python 后端、Vue 前端、MySQL 数据库串起来的入门开发者。整篇文章我会按「先跑通、再讲透、最后避坑」的顺序,把环境搭建、后端接口、前端页面、数据库设计、论文与答辩要点全部拆开讲,每一步都给可复现的命令和代码。
2. 环境搭建:Python、Vue、MySQL 三件套的安装与配置
2.1 Python 安装与环境隔离
Windows 上安装 Python,去官网下载 3.8 以上版本即可,安装时务必勾选「Add Python to PATH」,否则后面在命令行敲python会提示找不到命令。安装完成后验证:
python --version pip --version如果两条命令都能正常输出版本号,说明基础环境没问题。接下来做环境隔离,不要把所有依赖装到全局,否则换一台电脑或者换一个项目就冲突。推荐用 venv:
# 在项目根目录创建虚拟环境 python -m venv venv # Windows 激活 venv\Scripts\activate # macOS / Linux 激活 source venv/bin/activate激活后命令行前面会出现(venv)标识。这一步的参数说明:venv是虚拟环境目录名,可以改成别的,但建议保持默认,因为很多教程和脚本都按这个名字写。激活之后所有pip install都只影响这个环境,卸载重装不会污染系统 Python。
后端框架我一般选 Flask 或 Django。毕业设计场景下 Flask 更轻,路由和扩展一目了然,答辩时也好讲。安装核心依赖:
pip install flask flask-cors flask-sqlalchemy pymysqlflask-cors用来解决前后端分离时的跨域问题,flask-sqlalchemy是 ORM,pymysql是 MySQL 驱动。这四个包基本覆盖了后端主要需求。
2.2 Vue 安装及环境配置
前端需要 Node.js,去官网下载 LTS 版本,安装后验证:
node -v npm -v然后安装 Vue CLI 或者直接用 Vite 创建项目。Vue 3 是当前主流,建议用 Vite:
npm create vite@latest shop-frontend -- --template vue cd shop-frontend npm install npm run dev执行完最后一条命令,浏览器打开http://localhost:5173能看到 Vue 默认页面,说明前端环境通了。这里有个常见坑:npm install卡住不动,大概率是网络源的问题,可以换成国内镜像:
npm config set registry https://registry.npmmirror.com换完再执行npm install,速度会明显提升。另外建议装一个 Vue Devtools 浏览器插件,调试组件状态和路由参数非常方便,能省掉大量console.log。
2.3 MySQL 安装配置与建库
MySQL 推荐 8.0 版本,Windows 下下载安装包一路下一步即可,注意设置 root 密码时要记牢。安装完成后验证:
mysql -u root -p输入密码后进入 MySQL 命令行,执行SELECT VERSION();能看到版本号就说明安装成功。接下来建库建表:
CREATE DATABASE shop_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE shop_db; CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, phone VARCHAR(20) DEFAULT '', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL DEFAULT 0.00, stock INT NOT NULL DEFAULT 0, category_id INT, image VARCHAR(255) DEFAULT '', description TEXT ); CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, total_price DECIMAL(10,2) NOT NULL DEFAULT 0.00, status TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP );参数说明:utf8mb4字符集支持 emoji 和生僻字,比utf8更完整;DECIMAL(10,2)用于金额,避免浮点误差;status用 TINYINT 表示订单状态,0 待付款、1 已付款、2 已发货、3 已完成。建表时字段默认值要设好,比如stock默认 0、price默认 0.00,这样插入数据时即使不传也不会报错。
3. 后端接口:用 Flask 把商品、订单、用户三块业务串起来
3.1 数据库连接池与 SQLAlchemy 配置
前后端分离项目里,后端要频繁读写数据库,如果每次请求都新建连接,性能会很差。常见做法是配置连接池。Flask-SQLAlchemy 自带连接池参数:
from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_cors import CORS app = Flask(__name__) CORS(app, supports_credentials=True) app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:你的密码@localhost:3306/shop_db?charset=utf8mb4' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_ENGINE_OPTIONS'] = { 'pool_size': 10, 'max_overflow': 20, 'pool_recycle': 3600, 'pool_pre_ping': True } db = SQLAlchemy(app)参数说明:pool_size是连接池常驻连接数,max_overflow是高峰期允许临时超出的连接数,pool_recycle=3600表示连接一小时后自动回收,避免 MySQL 主动断开导致的error 2002或连接失效。pool_pre_ping=True会在每次取连接前先 ping 一下,确保连接可用。这几个参数在答辩时是加分项,能说明你考虑过生产环境。
3.2 商品列表与详情接口
商品接口是购物系统最核心的部分,前端首页、分类页、详情页都依赖它。先定义模型:
class Product(db.Model): __tablename__ = 'product' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) price = db.Column(db.Numeric(10, 2), default=0.00) stock = db.Column(db.Integer, default=0) category_id = db.Column(db.Integer) image = db.Column(db.String(255), default='') description = db.Column(db.Text) def to_dict(self): return { 'id': self.id, 'name': self.name, 'price': float(self.price), 'stock': self.stock, 'category_id': self.category_id, 'image': self.image, 'description': self.description }然后写接口:
@app.route('/api/products', methods=['GET']) def get_products(): page = request.args.get('page', 1, type=int) size = request.args.get('size', 10, type=int) keyword = request.args.get('keyword', '', type=str) query = Product.query if keyword: query = query.filter(Product.name.like(f'%{keyword}%')) pagination = query.paginate(page=page, per_page=size, error_out=False) return jsonify({ 'code': 200, 'data': [p.to_dict() for p in pagination.items], 'total': pagination.total, 'page': page, 'size': size }) @app.route('/api/products/<int:pid>', methods=['GET']) def get_product_detail(pid): product = Product.query.get(pid) if not product: return jsonify({'code': 404, 'msg': '商品不存在'}), 404 return jsonify({'code': 200, 'data': product.to_dict()})逻辑说明:列表接口支持分页和关键词搜索,paginate是 SQLAlchemy 自带的分页方法,返回items和total。详情接口用路径参数pid查询单条记录,查不到返回 404。参数方面,page默认 1,size默认 10,前端传参时如果不传就用默认值,避免报错。to_dict方法把模型对象转成字典,price用float转换是因为Numeric类型返回的是Decimal,JSON 序列化会报错。
3.3 订单创建与库存扣减
订单接口要处理事务,下单时同时写订单表和扣库存,任何一步失败都要回滚:
@app.route('/api/orders', methods=['POST']) def create_order(): data = request.get_json() user_id = data.get('user_id') items = data.get('items', []) if not user_id or not items: return jsonify({'code': 400, 'msg': '参数不完整'}), 400 try: total = 0 for item in items: product = Product.query.get(item['product_id']) if not product or product.stock < item['quantity']: raise Exception(f'商品 {item["product_id"]} 库存不足') product.stock -= item['quantity'] total += float(product.price) * item['quantity'] order = Order(user_id=user_id, total_price=total, status=0) db.session.add(order) db.session.commit() return jsonify({'code': 200, 'data': {'order_id': order.id, 'total': total}}) except Exception as e: db.session.rollback() return jsonify({'code': 500, 'msg': str(e)}), 500逻辑说明:先遍历购物车条目,检查库存是否充足,充足则扣减并累加总价,然后创建订单记录,最后统一commit。如果中间任何一步抛异常,rollback回滚所有操作,保证数据一致性。参数说明:items是数组,每个元素包含product_id和quantity;status=0表示待付款。这里有个血泪经验:库存扣减一定要在事务里做,否则并发下单会出现超卖,答辩时老师很可能问这个问题。
4. 前端页面:Vue 路由、组件与接口联调
4.1 Vue 路由参数与页面结构
前端页面按功能拆成首页、商品列表、商品详情、购物车、订单、个人中心几个路由。用 Vue Router 配置:
import { createRouter, createWebHistory } from 'vue-router' const routes = [ { path: '/', component: () => import('../views/Home.vue') }, { path: '/products', component: () => import('../views/ProductList.vue') }, { path: '/product/:id', component: () => import('../views/ProductDetail.vue') }, { path: '/cart', component: () => import('../views/Cart.vue') }, { path: '/orders', component: () => import('../views/OrderList.vue') }, { path: '/login', component: () => import('../views/Login.vue') } ] const router = createRouter({ history: createWebHistory(), routes }) export default router参数说明:/product/:id是动态路由,详情页通过route.params.id拿到商品 ID。createWebHistory是 history 模式,URL 不带#,更美观,但部署时需要服务端配置 fallback,否则刷新页面会 404。如果不想折腾,可以用createWebHashHistory,URL 带#但刷新不会丢。
4.2 用 Axios 封装请求与跨域处理
前端调后端接口统一用 Axios,封装一个 request 工具:
import axios from 'axios' const request = axios.create({ baseURL: 'http://localhost:5000/api', timeout: 10000, withCredentials: true }) request.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }) request.interceptors.response.use( response => response.data, error => { console.error('请求失败', error) return Promise.reject(error) } ) export default request逻辑说明:baseURL指向后端地址,timeout设 10 秒,超时自动中断。请求拦截器自动带上 token,响应拦截器统一处理返回数据。跨域问题在后端用flask-cors解决,前端不需要额外配置。如果后端没配 CORS,浏览器会报Access-Control-Allow-Origin错误,这是前后端分离项目最常见的翻车点之一。
4.3 商品列表页与详情页联调
商品列表页核心逻辑:
import { ref, onMounted } from 'vue' import request from '../utils/request' const products = ref([]) const page = ref(1) const total = ref(0) const fetchProducts = async () => { const res = await request.get('/products', { params: { page: page.value, size: 10 } }) products.value = res.data total.value = res.total } onMounted(() => { fetchProducts() })逻辑说明:ref定义响应式数据,onMounted在组件挂载后调用接口。params传分页参数,后端返回的data和total分别赋值给列表和总数。详情页类似,只是把page换成路由参数id。联调时如果数据不显示,先看浏览器 Network 面板,确认请求是否发出、返回状态码是否 200、返回结构是否和前端取值一致。常见问题是后端返回{code, data},前端直接取res而不是res.data,导致列表为空。
5. 避坑与排查:毕业设计项目最容易翻车的 5 个地方
5.1 跨域报错但后端明明配了 CORS
现象:浏览器控制台报Access-Control-Allow-Origin错误,但后端代码里已经写了CORS(app)。原因:CORS 配置顺序不对,或者前端请求带了自定义 header 导致预检请求失败。解决:确保CORS(app, supports_credentials=True)在app创建后立即调用,不要放在路由注册之后。如果前端带了Authorizationheader,后端还要允许对应 header。
5.2 MySQL 连接报 error 2002
现象:启动后端时报error 2002 (hy000): can't connect to local mysql server through socket。原因:MySQL 服务没启动,或者连接地址写成了 socket 方式。解决:Windows 下在服务管理器里启动 MySQL 服务,Linux 下执行systemctl start mysql。连接字符串用localhost:3306而不是 socket 路径。
5.3 Vue 打包后刷新页面 404
现象:npm run build后部署到服务器,首页能打开,刷新子页面就 404。原因:history 模式需要服务端把所有路径 fallback 到index.html。解决:Nginx 配置try_files $uri $uri/ /index.html;,或者改用 hash 模式。答辩演示时如果只在本地npm run dev,不会遇到这个问题,但部署后必现。
5.4 订单接口并发下单超卖
现象:两个用户同时买最后一件商品,都下单成功,库存变成 -1。原因:库存检查和扣减不在同一个事务里,或者没有加锁。解决:把检查和扣减放在同一个事务中,必要时用SELECT ... FOR UPDATE加行锁。毕业设计演示时可以用 JMeter 模拟并发,答辩时主动提这一点很加分。
5.5 论文里数据库设计和代码对不上
现象:论文里写的表结构和实际代码里的模型字段不一致,答辩时被老师发现。原因:先写代码后补论文,或者改代码没同步改论文。解决:以代码为准反向更新论文,表名、字段名、类型、默认值逐一核对。建议在论文里放一张 ER 图和一张表结构说明表,和代码保持一一对应。
6. 答辩加分项:把论文和演示串成一条线
答辩不是念论文,而是用 10 分钟让老师相信这套系统是你做的、你懂。我的习惯是提前准备三条线:第一条是数据流,从用户点击商品到订单写入数据库,每一步经过哪个文件、哪个函数;第二条是技术选型,为什么用 Flask 不用 Django、为什么用 Vue3 不用 React、为什么用 MySQL 不用 MongoDB,每个问题准备一句话回答;第三条是边界,主动说清楚哪些没做,比如支付没接真实接口、秒杀没做限流,但你知道怎么做。
演示时按「注册登录 → 浏览商品 → 加入购物车 → 下单 → 查看订单」走一遍完整流程,提前把数据准备好,不要现场录数据。如果老师问「前后端分离体现在哪」,你就打开浏览器 Network 面板,指着 API 请求说前端只负责渲染,数据全部通过 JSON 接口获取,后端不返回 HTML。如果问「数据库连接池怎么配的」,把第 3 章的配置参数讲一遍,重点说pool_pre_ping和pool_recycle的作用。
论文方面,摘要和绪论不要抄模板,用自己的话写清楚做了什么、解决了什么问题。系统设计章节放架构图和 ER 图,实现章节贴关键代码并加注释,测试章节放几张接口测试截图。最后附上源码和数据库 SQL 文件,确保老师拿到手能跑起来。我踩过最大的坑是论文里的截图和最终代码不一致,改代码后忘了换图,答辩时被追问了很久。希望帮到你。
本文还有配套的精品资源,点击获取