news 2026/10/10 6:44:34

微信小程序商城毕业设计:PHP+MySQL完整可部署系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
微信小程序商城毕业设计:PHP+MySQL完整可部署系统

简介:本资源是一套完整的微信小程序多店铺网上购物商城系统毕业设计项目,面向计算机专业本科生及小程序开发初学者,提供从客户端到后台管理的全栈实现方案。项目基于微信小程序 + .NET Core + layui 技术栈构建,涵盖小程序前端、Web管理后台、WebApi接口层及插件扩展模块,支持多商户入驻与基础电商核心功能。压缩包共1915个文件,含798个C#后端逻辑文件(如OrderController.cs、GoodsController.cs)、180个JS交互脚本、148个CSHTML视图页、42个WXML/WXSS小程序页面组件,以及SQL数据库脚本、JSON配置文件和插件源码等,整体大小为10.34MB。目前已有3592人学习下载,资源附带在线演示地址(demo.urshop.cn)及详细安装说明,包含MySQL 5.7数据库配置、插件加载机制解析与目录结构注释,便于快速部署、二次开发与毕业答辩支撑。

1. 微信小程序网上购物商城系统:毕业设计能直接跑通的完整闭环,不是Demo而是可部署的最小可行系统

你是不是也经历过——翻了十几套“微信小程序商城源码”,点开一看,app.js里写着// TODO: 登录逻辑待补全,数据库 SQL 文件里user表缺字段、order表没索引,README.md最后一行是“后续接入支付功能(未实现)”?这套基于微信小程序的在线网上购物商城系统,不是那种只渲染首页轮播图的空壳 Demo,而是一个从用户注册→浏览商品→加购下单→订单管理→后台基础数据维护,全部走通的真实闭环。它包含完整 MySQL 数据库结构(含初始化数据)、前后端分离式小程序源码(非云开发伪本地)、带注释的安装部署说明文档,甚至预留了微信支付回调接口占位和管理员登录入口。适合计算机类本科毕业设计、课程设计快速落地,也适合作为初学者理解“小程序 + Node.js/PHP 后端 + MySQL”标准三层架构的实操切片——不炫技、不堆栈,就用最稳的 wx.request + RESTful API + 原生 SQL,把一件事做透。

2. 项目结构与技术选型:为什么用原生小程序 + PHP/MySQL 组合而不是云开发或 uni-app?

2.1 源码包真实目录结构解析:看清“完整”二字落在哪里

拿到压缩包后,先别急着 npm install 或导入开发者工具。解压后你会看到三个核心文件夹:

shopping-mall-miniprogram/ ├── miniprogram/ # 小程序前端源码(WXML/WXSS/JS) ├── backend/ # 后端服务(PHP 版本,含 API 接口与数据库操作) └── docs/ # 项目安装使用说明(含数据库导入步骤、域名配置、HTTPS 要求)

提示:这不是一个“单文件小程序”,也不是纯前端静态页面。miniprogram/下的pages/目录里有index/(首页)、product/(商品详情)、cart/(购物车)、order/(订单列表)、user/(个人中心)等 7 个完整页面;backend/下的api/目录提供/login,/products,/add-to-cart,/create-order等 12 个标准 REST 接口,全部返回 JSON 格式,无任何微信云函数封装层。

这个结构决定了它不是“拿来即用”的一键部署包,但恰恰是毕业设计最需要的:你能清晰看到每个请求从wx.request()发出,到 PHP$_POST接收,再到mysqli_query()执行 SQL 的完整链路。对答辩老师来说,这比“云开发自动建表”更有解释空间;对你自己来说,改一个价格字段、加一个商品分类筛选,改三处代码就能上线——没有黑匣子。

2.2 技术栈选择逻辑:为什么坚持 PHP + MySQL 而非 Node.js 或云开发?

很多人会疑惑:现在都 2024 年了,为什么不用 Express + MySQL 或直接上云开发?答案很实在:稳定、可调试、易答辩、零额外服务依赖。

  • PHP 版本(推荐使用):backend/下的config.php明确指定数据库连接参数,所有 SQL 查询均使用mysqli面向过程写法(非 PDO),api/login.php中密码校验采用password_verify(),符合毕业设计对“安全基础”的基本要求。更重要的是,它不依赖 npm run dev、pm2 进程守护或 nginx 反向代理——你只需把backend/放进 Apache/PHPStudy 的www目录,浏览器访问http://localhost/api/products.php就能看到 JSON 返回,调试时var_dump()直接打屏,毫无玄学。

  • Node.js 版本(备选):压缩包内backend-node/是 Express 实现,但需自行npm install并npm start,且默认监听localhost:3000,小程序要求request合法域名必须是 HTTPS,本地调试需配合ngrok或自签证书——这对答辩前两天还在调登录态的同学,就是翻车高发区。

  • 云开发版本(明确排除):项目中无cloudfunctions/目录,无wx.cloud.callFunction()调用。原因很直白:云开发虽快,但答辩时老师问“订单状态如何原子更新”,你答“用数据库事务”,他点头;你答“用云数据库的 transaction”,他可能追问“那底层是 MySQL 还是 MongoDB?事务隔离级别怎么设?”——瞬间失分。而本项目create-order.php中START TRANSACTION→INSERT INTO order_main→INSERT INTO order_item→UPDATE product_stock→COMMIT的完整事务块,就是教科书级答案。

所以,如果你的目标是“两周内完成可演示、可讲清楚、可改可调”的毕业设计,PHP 版就是最优解。它不酷,但够厚;它不新,但够稳。

2.3 数据库设计要点:5 张核心表如何支撑真实购物流程

数据库文件为shopping_mall.sql,UTF8mb4 编码,共 9 张表,其中 5 张为核心业务表:

表名字段数关键字段说明是否含初始数据
user11id,username,password_hash,mobile,avatar_url,created_at✅ 含 3 条测试账号(含管理员admin/123456)
product12id,name,price,stock,category_id,cover_image,status(0下架/1上架)✅ 含 15 款商品(含手机、耳机、充电宝等真实品类)
category4id,name,parent_id,sort_order✅ 含 4 级分类(数码→手机→iPhone→iPhone 14)
cart6id,user_id,product_id,quantity,created_at,updated_at❌ 空表(用户首次加购自动生成)
order_main13id,order_no,user_id,total_amount,status(0待支付/1已支付/2已发货/3已完成),pay_time,ship_no❌ 空表(下单时插入)

注意:product.status和order_main.status是双状态机设计,避免“已支付但库存扣减失败”的脏数据。cart表无外键约束(为兼容低版本 MySQL),但 PHP 层通过JOIN和事务保证一致性。这种“不过度设计、恰到好处”的数据库方案,正是毕业设计该有的分寸感——既体现范式意识(如category.parent_id支持无限级分类),又不陷入“为微服务而分库”的过度设计陷阱。

3. 安装部署全流程:从数据库导入到小程序真机调试的六步实操

3.1 第一步:本地环境准备(Apache + PHP 7.2+ + MySQL 5.7+)

不要跳过这步。很多同学卡在第一步,不是代码问题,而是环境没对齐。

  • 推荐组合:PHPStudy Pro(Windows)或 XAMPP(macOS/Linux),关闭自带 MySQL 8.0,改用其内置的 MySQL 5.7(因项目 SQL 文件中CREATE TABLE语句含TYPE=MyISAM,MySQL 8.0 已废弃该语法);
  • PHP 版本必须 ≥7.2:因password_hash()和password_verify()在 7.2+ 才默认启用PASSWORD_ARGON2I,而项目中register.php使用PASSWORD_DEFAULT,低版本会报错;
  • 扩展开启:确保php.ini中extension=mysqli和extension=curl已取消注释(;去掉),否则wx.request调用后端将返回空白页。

验证方式:启动 PHPStudy,浏览器访问http://localhost,点击“探针”,确认 PHP 版本、MySQL 连接状态、curl 是否 enabled。这一步花 10 分钟,能省去后续 3 小时排查“接口 500”的时间。

3.2 第二步:数据库导入与权限配置(含常见字符集坑)

解压得到shopping_mall.sql,用 PHPMyAdmin 或命令行导入:

# 命令行方式(推荐,可控性强) mysql -u root -p > CREATE DATABASE shopping_mall DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; > USE shopping_mall; > SOURCE /path/to/shopping_mall.sql;

逻辑说明:必须显式指定utf8mb4字符集,因为商品名称含 emoji(如 📱)、用户昵称含生僻字,utf8(实际是 utf8mb3)无法存储。若跳过此步直接CREATE DATABASE shopping_mall;,导入后中文变???,且ALTER DATABASE无法彻底修复已有表字段编码。

导入后,检查user表中admin账号密码是否可登录:

SELECT id, username, SUBSTRING(password_hash, 1, 20) AS hash_preview FROM user WHERE username = 'admin'; -- 应返回类似:1, 'admin', '$2y$10$Zz...'

$2y$开头表示 bcrypt 加密,说明密码哈希正确。若为$2a$或明文,说明导入时password_hash()函数未生效,需检查 PHP 版本。

3.3 第三步:后端 API 配置与域名绑定(HTTPS 是硬门槛)

进入backend/config.php,修改以下三项:

<?php define('DB_HOST', 'localhost'); // 数据库地址(若用 Docker 则填宿主机 IP) define('DB_USER', 'root'); // 数据库用户名 define('DB_PASS', ''); // 数据库密码(PHPStudy 默认为空) define('API_BASE_URL', 'http://localhost/backend/api/'); // ⚠️ 关键!小程序 request 的 base URL ?>

参数说明:API_BASE_URL必须以/结尾,且不能是http://127.0.0.1。微信小程序调试器允许localhost,但真机调试强制要求https或127.0.0.1(仅限 iOS 模拟器)。因此,开发阶段请统一用localhost,上线前再替换为你的 HTTPS 域名。

验证 API 是否就绪:浏览器访问http://localhost/backend/api/products.php,应返回 JSON 数组,形如:

[ { "id": 1, "name": "iPhone 14 Pro", "price": "7999.00", "cover_image": "https://example.com/images/iphone14.jpg", "stock": 23 } ]

若返回{"error":"Database connection failed"},检查config.php中数据库密码或 MySQL 服务是否启动;若返回空白页,检查php.ini中display_errors = On是否开启,并查看 Apache 错误日志。

3.4 第四步:小程序开发者工具导入与 AppID 替换

打开微信开发者工具(v1.06.2308040 及以上),选择“导入项目” → 选中miniprogram/目录。

关键配置在project.config.json和app.js:

  • project.config.json中"appid"字段改为你的测试 AppID(无则填wx0000000000000000,仅限开发调试);
  • app.js中globalData的apiUrl必须与后端API_BASE_URL一致:
App({ globalData: { userInfo: null, apiUrl: 'http://localhost/backend/api/' // ⚠️ 必须与 config.php 中完全一致 } })

注意:此处apiUrl是字符串拼接基础,pages/index/index.js中wx.request({ url: app.globalData.apiUrl + 'products.php' })才构成完整请求地址。若少写/,会变成http://localhost/backend/apiproducts.php(404)。

导入后,点击“编译”,控制台无 red error 即表示前端结构正常。此时点击“预览”生成二维码,用真机微信扫码——若提示“网络错误”,大概率是apiUrl配置错误或后端未启动。

3.5 第五步:真机调试与登录态打通(Cookie vs Token)

小程序登录流程为:前端wx.login()获取 code → 传给后端/login.php→ 后端用 code 换取openid→ 查库匹配user表 → 返回token(JWT)→ 前端存入wx.setStorageSync('token')。

关键点在于:后端不依赖 session,全程用 token 鉴权。/cart/list.php开头有:

$token = $_SERVER['HTTP_AUTHORIZATION'] ?? ''; if (empty($token) || !validateToken($token)) { echo json_encode(['error' => 'Unauthorized']); exit; }

而前端每次请求都带上:

wx.request({ url: app.globalData.apiUrl + 'cart/list.php', header: { 'Authorization': wx.getStorageSync('token') // ⚠️ 注意 key 名是 Authorization,不是 token }, success: res => { ... } })

提示:真机调试时,wx.login()在 iOS 上可能因隐私设置返回fail auth deny,此时需引导用户手动授权。但毕业设计演示阶段,用测试账号admin/123456直接调用/login.php?username=admin&password=123456更稳妥——login.php同时支持 code 登录和账号密码登录,后者专为答辩场景保留。

3.6 第六步:管理员后台访问与订单状态流转验证

项目未提供独立后台网页,但所有管理功能均通过小程序内“管理员入口”触发:

  • 使用admin/123456登录;
  • 进入“我的” → “设置” → “进入管理后台”(隐藏按钮,长按 3 秒出现);
  • 可执行:商品上下架(改product.status)、订单发货(改order_main.status为 2 并填ship_no)、查看用户列表。

验证订单状态机:

  1. 用户下单 →order_main.status = 0(待支付)
  2. 管理员后台点击“发货” →status = 2(已发货),ship_no写入快递单号
  3. 小程序“我的订单”中该订单显示“已发货,预计 2 天后送达”

若状态不更新,检查backend/api/update-order-status.php中 SQL 是否执行成功,以及wx.request的header.Authorization是否携带有效 token。

4. 避坑指南:五个血泪经验总结,覆盖 90% 的部署失败场景

4.1 现象:小程序编译成功,但首页空白,控制台报Failed to load resource: the server responded with a status of 404 ()

原因:app.js中apiUrl配置错误,或后端backend/目录未放在 Web 服务器根目录下。例如,你把backend/放在D:\project\,但 Apache 的DocumentRoot是D:\phpstudy_pro\WWW,导致http://localhost/backend/api/products.php根本无法路由。
解决:将整个backend/文件夹复制到 Apache 的www目录(如D:\phpstudy_pro\WWW\backend),并确保http://localhost/backend/api/products.php在浏览器可直接访问。用 Postman 测试该 URL,而非只信小程序控制台。

4.2 现象:登录时提示{"error":"Invalid credentials"},但数据库中账号密码明明存在

原因:login.php中密码校验逻辑为password_verify($password, $hashed_password),而$hashed_password来自数据库user.password_hash字段。若你手动修改过该字段为明文(如'123456'),password_verify()永远返回 false。
解决:不要手改数据库密码字段。重置密码请用reset-password.php(项目含此脚本),或执行 SQL:

UPDATE user SET password_hash = '$2y$10$Zz...真实bcrypt哈希...' WHERE username = 'admin';

哈希值可用在线工具生成(搜索“bcrypt generator”),输入123456,选择 cost=10。

4.3 现象:添加商品到购物车后,cart/list.php返回空数组,但数据库cart表有数据

原因:cart/list.php查询语句为SELECT c.*, p.name, p.price, p.cover_image FROM cart c JOIN product p ON c.product_id = p.id WHERE c.user_id = ?,若product表中对应id的商品status = 0(已下架),JOIN结果为空,导致返回空。
解决:检查商品状态。在管理员后台将商品设为“上架”,或修改 SQL 为LEFT JOIN并增加AND p.status = 1条件。这是业务逻辑坑,不是代码 Bug。

4.4 现象:真机扫码后提示request:fail net::ERR_CONNECTION_REFUSED

原因:真机无法访问localhost。iOS/Android 微信认为localhost指向手机自身,而非电脑。
解决:

  • 方案 A(推荐):用 PHPStudy 的“虚拟域名”功能,将mall.test指向127.0.0.1,然后在config.php和app.js中将localhost全部替换为mall.test;
  • 方案 B:电脑和手机连同一 WiFi,查电脑 IP(如192.168.1.100),后端config.php改为DB_HOST = '192.168.1.100',API_BASE_URL = 'http://192.168.1.100/backend/api/',小程序apiUrl同步修改。
    ⚠️ 注意:Windows 防火墙需放行 80 端口,否则手机无法访问。

4.5 现象:下单成功,但order_main表中total_amount为 0,或order_item表无数据

原因:create-order.php中事务未正确提交。代码中有mysqli_query($conn, "START TRANSACTION");,但后续某条INSERT失败(如product.stock < quantity)时,未执行mysqli_query($conn, "ROLLBACK");,导致部分数据写入、部分回滚失败。
解决:检查create-order.php末尾是否有if ($success) { mysqli_query($conn, "COMMIT"); } else { mysqli_query($conn, "ROLLBACK"); }。若无,请补全。更稳妥做法是:在INSERT INTO order_main后,立即SELECT LAST_INSERT_ID()获取order_id,再用该 ID 插入order_item,避免主键冲突。

5. 毕业设计答辩加分技巧:三个可现场演示的“小而美”改造点

5.1 改造一:为商品列表增加“销量排序”按钮(30 行代码,10 分钟上线)

答辩时老师常问:“如果我想按销量排序,怎么加?” 别说“后期迭代”,现场改给他看。

步骤:

  1. 在miniprogram/pages/index/index.wxml的<view class="filter-bar">内加按钮:
<view class="sort-btn" bindtap="switchSort">
  • 在index.js的data中加:
  • sortType: 'default', // default / sales / price sortText: '综合排序'
    1. 在index.js的onLoad中,请求时带上sort参数:
    wx.request({ url: app.globalData.apiUrl + 'products.php?sort=' + this.data.sortType, success: res => { this.setData({ products: res.data }) } })
    1. 修改backend/api/products.php,在 SQL 查询前加:
    $sort = $_GET['sort'] ?? 'default'; switch($sort) { case 'sales': $order_sql = 'ORDER BY sales_count DESC'; break; case 'price': $order_sql = 'ORDER BY price ASC'; break; default: $order_sql = 'ORDER BY created_at DESC'; } $sql = "SELECT * FROM product WHERE status = 1 $order_sql";

    效果:点击按钮切换排序,无需重启服务。这展示了你对前后端数据流的掌控力,比讲“MVC 分层”直观十倍。

    5.2 改造二:在订单详情页增加“复制快递单号”功能(提升用户体验细节)

    真机演示时,长按快递单号自动复制,老师会觉得“这孩子考虑得很细”。

    步骤:

    1. miniprogram/pages/order/detail.wxml中,快递单号区域加bindlongtap:
    <view class="ship-info" bindlongtap="copyShipNo">
  • detail.js中加方法:
  • copyShipNo(e) { const shipNo = e.currentTarget.dataset.shipno; wx.setClipboardData({ data: shipNo, success: () => wx.showToast({ title: '已复制', icon: 'none' }) }); }

    注意:bindlongtap是微信原生事件,无需引入组件。这个改动不涉及后端,纯粹前端交互,却能让演示环节多一个“哇”点。

    5.3 改造三:用数据库视图简化管理员查询(展示数据库设计能力)

    老师若问“后台查销量TOP10商品怎么写SQL?”,你答“SELECT * FROM product ORDER BY sales_count DESC LIMIT 10”太浅。现场建个视图,体现工程思维。

    步骤(在 PHPMyAdmin 执行):

    CREATE VIEW top_sales_products AS SELECT p.id, p.name, p.price, p.cover_image, COALESCE(SUM(oi.quantity), 0) as total_sales FROM product p LEFT JOIN order_item oi ON p.id = oi.product_id JOIN order_main om ON oi.order_id = om.id AND om.status >= 2 GROUP BY p.id ORDER BY total_sales DESC LIMIT 10;

    然后在管理员后台接口中新增api/top-sales.php,内容仅为:

    <?php require_once 'config.php'; $result = mysqli_query($conn, "SELECT * FROM top_sales_products"); echo json_encode(mysqli_fetch_all($result, MYSQLI_ASSOC)); ?>

    价值:视图将复杂 JOIN 和聚合逻辑封装,后台接口变单表查询,性能更好,也方便未来扩展(如加时间范围筛选)。这比“我用了 Redis 缓存”更扎实——因为 Redis 你未必真配,但视图,现场就能建。

    从那以后我每次帮同学看毕业设计,第一件事就是让他打开backend/api/products.php,删掉最上面的header('Access-Control-Allow-Origin: *');,再问:“如果去掉这行,你的小程序还能请求吗?” 答不上来,就先回去把 CORS 原理搞懂。因为真正的工程能力,不在堆了多少框架,而在删掉一行代码后,你知道系统哪会断、为什么断、怎么修。希望帮到你。

    本文还有配套的精品资源,点击获取

    版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
    网站建设 2026/10/10 6:44:33

    Linux权限管理全面解析:从rwx到ACL的安全实践

    1. 一场权限事故&#xff0c;让我把Linux权限管理重新学了一遍前阵子半夜被电话叫醒&#xff0c;生产环境一台服务器上跑着的定时任务突然大面积报错&#xff0c;报的全是"Permission denied"。登录上去一看&#xff0c;某个数据目录的属主和权限全乱了&#xff0c;原…

    作者头像 李华
    网站建设 2026/10/10 6:44:25

    Finalshell连接Ubuntu虚拟机反复提示输入密码?从SSH到网络全链路排查

    Finalshell连接VMware里的Ubuntu虚拟机&#xff0c;密码输了一遍又一遍&#xff0c;界面上始终是那句"请输入密码"&#xff0c;这种问题我帮人远程看过很多次。表面看是密码不对&#xff0c;实际原因五花八门&#xff0c;十次里有八次甚至跟密码本身一点关系都没有。…

    作者头像 李华
    网站建设 2026/10/10 6:44:16

    内存映射与按需加载:LogViewPro如何秒开超大日志文件

    简介&#xff1a;LogViewPro中文版是一款面向系统管理员、运维工程师与软件开发者的日志文本查看工具&#xff0c;专为解决超大文本文件打开卡慢、全文检索困难等痛点而设计。压缩包为zip格式&#xff0c;整体仅1.54MB&#xff0c;轻量免安装&#xff0c;解压后即可运行主程序使…

    作者头像 李华
    网站建设 2026/10/10 6:44:07

    Linux depmod命令详解:内核模块依赖关系与加载实战指南

    1. 认识depmod&#xff1a;内核模块系统里那个“劳碌命”的幕后管家1.1 先搞明白它在Linux系统里到底做什么经常折腾内核模块、驱动编译或者嵌入式Linux的朋友&#xff0c;肯定绕不开这么一条命令&#xff1a;depmod -a看名字就知道&#xff0c;depmod dependency module&…

    作者头像 李华
    网站建设 2026/10/10 6:43:32

    CentOS 7虚拟机网络配置全攻略:NAT模式与固定IP实战

    CentOS 7虚拟机装好了&#xff0c;进入系统第一件事就是配置网络。很多人卡在第一步&#xff1a;ping www.baidu.com死活不通&#xff0c;报错信息换来换去&#xff0c;就是找不着原因。这种场景我见了太多次&#xff0c;几乎每个刚接触虚拟机装Linux的人都会在"配置网络&…

    作者头像 李华
    网站建设 2026/10/10 6:42:44

    LeetCode 27 移除元素:从暴力到双指针的原地操作详解

    Day 24了&#xff0c;今天刷到LeetCode第27题“移除元素”。这道题很多初学者一看就觉得简单&#xff1a;不就是把数组里等于某个值的元素删掉吗&#xff1f;但实际动手写的时候&#xff0c;问题就来了——原地操作不允许开新数组、返回的是新长度而不是新数组、删除元素后下标…

    作者头像 李华

    关于博客

    这是一个专注于编程技术分享的极简博客,旨在为开发者提供高质量的技术文章和教程。

    订阅更新

    输入您的邮箱,获取最新文章更新。

    © 2025 极简编程博客. 保留所有权利.