ARTICLE DETAIL

资讯详情

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

PHP+MySQL服装商城购物系统课程设计:从环境搭建到下单事务的完整拆包

PHP+MySQL服装商城购物系统课程设计:从环境搭建到下单事务的完整拆包 简介本资源为基于PHPMySQL实现的服装商城购物系统课程设计项目编号100013121面向计算机相关专业学生及Web开发初学者可用于课程设计参考、PHP与数据库综合实践或毕业设计选题。系统涵盖用户管理、商品展示、购物车与后台订单管理等模块涉及表单处理、数据库设计、SQL查询优化、事务处理及SQL注入防护等知识点并采用响应式布局兼顾多端体验。压缩包共260个文件约5.54MB包含10个PHP核心页面、1个SQL数据库脚本、9个HTML与9个CSS样式文件、21个JavaScript脚本以及大量jpg、png、gif图片素材和字体文件另附项目说明书文档便于理解整体架构与运行流程。目前已有393人学习下载适合需要完整项目源码、数据库脚本与说明文档的读者参考复用。1. 服装商城购物系统拆包一份能跑起来的 PHPMySQL 课程设计长什么样很多同学做课程设计时最头疼的不是写代码而是找不到一个结构完整、能跑通、还带数据库的参考项目。这份编号 100013121 的服装商城购物系统就是典型的 PHPMySQL 组合前端用原生 PHP 渲染页面后端用 MySQL 存商品、订单、用户数据功能覆盖商品浏览、购物车、下单、后台管理这条主线。它适合正在做 Web 课程设计、想快速理解电商系统数据流的学生也适合刚接触 PHP 全栈、需要一个完整 CRUD 案例练手的开发者。拿到源码后你要做的不是通读每一行而是先把它跑起来再顺着「用户下单」这条链路去拆表结构和业务逻辑。2. 环境搭建与数据库初始化从 MySQL 安装到导入 SQL 文件2.1 选型理由为什么这套系统用原生 PHP 而不是框架这套系统没有用 Laravel、ThinkPHP 这类框架而是原生 PHP 加 MySQL。原因很直接课程设计场景下框架会引入 Composer 依赖、路由配置、模板引擎等额外概念反而增加理解成本。原生 PHP 的请求流程是「浏览器访问 .php 文件 → PHP 解析执行 → 查询 MySQL → 返回 HTML」每一步都看得见适合用来理解 Web 请求的完整生命周期。数据库层面MySQL 是这类项目最稳妥的选择。它安装简单、社区资料多、和 PHP 的 mysqli/PDO 扩展配合成熟。你不需要考虑分布式或高并发单机 MySQL 足够支撑课程设计的演示和答辩。常见做法是本地用 phpStudy 或 XAMPP 一键集成环境省去单独配置 PHP 和 MySQL 的麻烦。但如果你想真正搞懂环境依赖我建议手动装一遍踩过的坑才是自己的。2.2 手动搭建 PHPMySQL 运行环境先确认版本匹配。PHP 7.4 或 8.0 都可以MySQL 用 5.7 或 8.0。PHP 8.0 配 MySQL 8.0 时mysqli 默认认证插件是 caching_sha2_password老代码可能连不上后面避坑章节会细说。Windows 下安装 MySQL 8 的典型步骤# 1. 下载 MySQL 8 的 ZIP 包解压到 D:\mysql8 # 2. 初始化数据目录 mysqld --initialize-insecure --basedirD:\mysql8 --datadirD:\mysql8\data # 3. 注册为系统服务 mysqld --install MySQL8 --defaults-fileD:\mysql8\my.ini # 4. 启动服务 net start MySQL8 # 5. 登录并设置 root 密码 mysql -u root --skip-password ALTER USER rootlocalhost IDENTIFIED BY 你的密码;这段命令的逻辑是--initialize-insecure创建一个空密码的 root 账户方便首次登录--install把 MySQL 注册成 Windows 服务开机自启最后用ALTER USER设置正式密码。参数--basedir和--datadir必须指向你实际解压的路径写错会导致服务启动失败。Linux 下更简单Ubuntu 用apt install mysql-serverCentOS 用yum install mysql-server。安装完执行mysql_secure_installation做安全初始化。PHP 这边确认php.ini里这两行没有被注释extensionmysqli extensionpdo_mysql改完重启 Apache 或 Nginx。写一个info.php内容为?php phpinfo(); ?浏览器访问能看到 mysqli 模块就说明环境通了。2.3 导入数据库并配置连接参数源码包里一般有一个.sql文件比如shop.sql或database.sql。导入方式有两种# 方式一命令行导入 mysql -u root -p 你的密码 shop.sql # 方式二先建库再导入 mysql -u root -p CREATE DATABASE shop DEFAULT CHARACTER SET utf8mb4; USE shop; SOURCE D:/project/shop.sql;推荐用第二种因为可以显式指定字符集为utf8mb4避免中文商品名变成乱码。导入完成后用SHOW TABLES;确认表都建好了通常会有user、goods、orders、cart、admin这几张核心表。接下来找到 PHP 的连接配置文件通常叫conn.php、db.php或config.php改成你自己的数据库信息?php $host 127.0.0.1; // 数据库地址本地用 127.0.0.1 比 localhost 更稳 $user root; // 数据库用户名 $pass 你的密码; // 数据库密码 $db shop; // 数据库名和导入时建库名一致 $port 3306; // MySQL 默认端口 $conn new mysqli($host, $user, $pass, $db, $port); if ($conn-connect_error) { die(连接失败: . $conn-connect_error); } $conn-set_charset(utf8mb4); // 必须设置否则中文乱码 ?这里用127.0.0.1而不是localhost是有原因的Linux 下localhost会走 Unix socket 而不是 TCP如果 MySQL 的 socket 路径和 PHP 默认不一致就会报error 2002 (HY000): cant connect to local MySQL server through socket。用127.0.0.1强制走 TCP能绕开这个玄学问题。set_charset(utf8mb4)这行不能省它决定了 PHP 和 MySQL 之间传输中文时用什么编码。3. 核心功能链路拆解商品、购物车、订单三张表的联动3.1 商品列表与详情页的数据查询逻辑商品列表页通常对应index.php或goods_list.php。核心逻辑是从goods表查出所有上架商品分页展示。典型查询?php $page isset($_GET[page]) ? intval($_GET[page]) : 1; $page max(1, $page); // 防止页码为 0 或负数 $size 12; // 每页显示 12 件商品 $offset ($page - 1) * $size; $sql SELECT id, name, price, img, stock FROM goods WHERE status 1 ORDER BY id DESC LIMIT ?, ?; $stmt $conn-prepare($sql); $stmt-bind_param(ii, $offset, $size); $stmt-execute(); $result $stmt-get_result(); while ($row $result-fetch_assoc()) { // 渲染商品卡片 } ?这段代码的关键点有三个。第一intval($_GET[page])把用户输入的页码强制转成整数防止 SQL 注入。第二用preparebind_param而不是直接拼接 SQL这是防注入的标准做法。第三LIMIT ?, ?的两个参数都是整数所以bind_param的类型字符串是ii写错类型会导致查询结果异常。商品详情页对应goods_detail.php通过 URL 参数id查询单条记录?php $id intval($_GET[id]); $sql SELECT * FROM goods WHERE id ? AND status 1; $stmt $conn-prepare($sql); $stmt-bind_param(i, $id); $stmt-execute(); $goods $stmt-get_result()-fetch_assoc(); if (!$goods) { die(商品不存在或已下架); } ?这里加了status 1的条件确保已下架商品即使知道 ID 也访问不到。fetch_assoc()返回关联数组字段名就是表里的列名模板里直接用$goods[name]、$goods[price]取值。3.2 购物车Session 存储与数据库持久化的取舍购物车是这套系统里最能体现设计思路的模块。常见有两种实现Session 存储和数据库存储。Session 方案把购物车数据存在$_SESSION[cart]里结构类似[goods_id quantity]。优点是实现简单、不写数据库缺点是换浏览器或清 Cookie 后购物车丢失且无法做「购物车商品降价提醒」这类功能。数据库方案建一张cart表字段包括id、user_id、goods_id、num、add_time。优点是持久化、可跨设备缺点是多一次数据库查询。这套课程设计项目通常用 Session 方案因为不需要登录就能加购降低演示门槛。核心代码?php session_start(); // 加入购物车 function addToCart($goodsId, $num 1) { $goodsId intval($goodsId); $num max(1, intval($num)); if (!isset($_SESSION[cart])) { $_SESSION[cart] []; } if (isset($_SESSION[cart][$goodsId])) { $_SESSION[cart][$goodsId] $num; // 已存在则累加数量 } else { $_SESSION[cart][$goodsId] $num; // 不存在则新增 } } // 从购物车读取商品详情 function getCartItems($conn) { if (empty($_SESSION[cart])) { return []; } $ids array_keys($_SESSION[cart]); $idStr implode(,, array_map(intval, $ids)); // 强制转整数再拼接 $sql SELECT id, name, price, img FROM goods WHERE id IN ($idStr); $result $conn-query($sql); $items []; while ($row $result-fetch_assoc()) { $row[num] $_SESSION[cart][$row[id]]; $row[subtotal] $row[price] * $row[num]; $items[] $row; } return $items; } ?array_map(intval, $ids)这一步是安全关键。虽然$ids来自 Session 理论上可控但养成「拼 SQL 前先强制转整数」的习惯能避免很多意外。subtotal是小计模板里直接用来显示每行商品的总价。购物车数量修改和删除也是操作$_SESSION[cart]数组改完重新计算总价即可。注意每次操作后要header(Location: cart.php)跳转回购物车页避免刷新时重复提交。3.3 下单流程从购物车到订单表的事务处理下单是整个系统里唯一需要事务的地方。因为要同时做三件事写orders表、写order_items表、扣减goods表库存。任何一步失败都会导致数据不一致。?php session_start(); $conn-begin_transaction(); try { $userId intval($_SESSION[user_id]); $total 0; $items getCartItems($conn); if (empty($items)) { throw new Exception(购物车为空); } // 1. 计算总价并检查库存 foreach ($items as $item) { $stockSql SELECT stock FROM goods WHERE id ? FOR UPDATE; $stmt $conn-prepare($stockSql); $stmt-bind_param(i, $item[id]); $stmt-execute(); $stock $stmt-get_result()-fetch_assoc()[stock]; if ($stock $item[num]) { throw new Exception(商品 . $item[name] . 库存不足); } $total $item[subtotal]; } // 2. 写入订单主表 $orderNo date(YmdHis) . rand(1000, 9999); $stmt $conn-prepare(INSERT INTO orders (order_no, user_id, total, status, add_time) VALUES (?, ?, ?, 0, NOW())); $stmt-bind_param(sid, $orderNo, $userId, $total); $stmt-execute(); $orderId $conn-insert_id; // 3. 写入订单明细并扣库存 foreach ($items as $item) { $stmt $conn-prepare(INSERT INTO order_items (order_id, goods_id, price, num) VALUES (?, ?, ?, ?)); $stmt-bind_param(iidi, $orderId, $item[id], $item[price], $item[num]); $stmt-execute(); $stmt $conn-prepare(UPDATE goods SET stock stock - ? WHERE id ?); $stmt-bind_param(ii, $item[num], $item[id]); $stmt-execute(); } $conn-commit(); unset($_SESSION[cart]); // 清空购物车 header(Location: order_success.php?order_no . $orderNo); } catch (Exception $e) { $conn-rollback(); die(下单失败 . $e-getMessage()); } ?begin_transaction()开启事务commit()提交rollback()回滚。SELECT ... FOR UPDATE是行级锁防止两个用户同时买最后一件商品时超卖。insert_id拿到刚插入的订单主键用于关联订单明细。orderNo用时间戳加随机数生成保证唯一性。整个流程要么全成功要么全回滚不会出现「订单写了但库存没扣」的情况。4. 后台管理与权限控制管理员登录与商品 CRUD4.1 管理员登录与 Session 鉴权后台入口通常是/admin/login.php。管理员表admin存用户名和密码哈希。登录逻辑?php session_start(); if ($_SERVER[REQUEST_METHOD] POST) { $username trim($_POST[username]); $password $_POST[password]; $stmt $conn-prepare(SELECT id, username, password FROM admin WHERE username ?); $stmt-bind_param(s, $username); $stmt-execute(); $admin $stmt-get_result()-fetch_assoc(); if ($admin password_verify($password, $admin[password])) { $_SESSION[admin_id] $admin[id]; $_SESSION[admin_name] $admin[username]; header(Location: index.php); exit; } else { $error 用户名或密码错误; } } ?password_verify是 PHP 内置的密码校验函数配合password_hash使用。如果源码里存的是 MD5 密码建议改成password_hash因为 MD5 已经不安全了。登录成功后把admin_id写进 Session后续每个后台页面开头都要检查?php session_start(); if (!isset($_SESSION[admin_id])) { header(Location: login.php); exit; } ?这段代码要放在后台每个 PHP 文件的最顶部session_start()之前不能有任何输出否则会报headers already sent错误。4.2 商品管理新增、编辑、删除与图片上传商品 CRUD 是后台的核心。新增商品涉及表单提交和图片上传?php // 处理图片上传 if (isset($_FILES[img]) $_FILES[img][error] UPLOAD_ERR_OK) { $allowTypes [image/jpeg, image/png, image/gif]; $maxSize 2 * 1024 * 1024; // 2MB if (!in_array($_FILES[img][type], $allowTypes)) { die(只允许上传 JPG/PNG/GIF 图片); } if ($_FILES[img][size] $maxSize) { die(图片不能超过 2MB); } $ext pathinfo($_FILES[img][name], PATHINFO_EXTENSION); $newName uniqid(goods_, true) . . . $ext; $uploadDir __DIR__ . /../uploads/; if (!is_dir($uploadDir)) { mkdir($uploadDir, 0755, true); } if (move_uploaded_file($_FILES[img][tmp_name], $uploadDir . $newName)) { $imgPath uploads/ . $newName; } else { die(图片上传失败); } } // 写入数据库 $stmt $conn-prepare(INSERT INTO goods (name, price, stock, img, status, add_time) VALUES (?, ?, ?, ?, 1, NOW())); $stmt-bind_param(sdis, $_POST[name], $_POST[price], $_POST[stock], $imgPath); $stmt-execute(); ?$_FILES[img][type]是浏览器传来的 MIME 类型可以被伪造更严格的做法是用finfo_file()检测真实类型。uniqid(goods_, true)生成唯一文件名避免同名覆盖。move_uploaded_file把临时文件移到目标目录目标目录必须存在且有写权限。编辑和删除逻辑类似删除时要注意如果商品已经被订单引用直接删会导致订单明细查不到商品名。常见做法是软删除把status改为 0 而不是物理删除。4.3 订单管理与状态流转后台订单列表从orders表查关联user表拿用户名。订单状态一般用数字表示0 待付款、1 已付款、2 已发货、3 已完成、4 已取消。管理员可以修改状态?php $orderId intval($_POST[order_id]); $newStatus intval($_POST[status]); $allowStatus [0, 1, 2, 3, 4]; if (!in_array($newStatus, $allowStatus)) { die(非法状态); } $stmt $conn-prepare(UPDATE orders SET status ? WHERE id ?); $stmt-bind_param(ii, $newStatus, $orderId); $stmt-execute(); header(Location: order_list.php); ?状态流转要有约束比如已完成的订单不能再改回待付款。可以在更新前先查当前状态判断是否允许这次变更。课程设计里不做这么细也能过但答辩时老师可能会问「如果用户已经付款了管理员误点取消怎么办」提前想好这个问题的答案。5. 避坑与排查从连接失败到中文乱码的五个血泪经验5.1 数据库连接报 error 2002现象PHP 页面报error 2002 (HY000): cant connect to local MySQL server through socket /tmp/mysql.sock。原因PHP 配置里mysqli.default_socket指向的路径和 MySQL 实际 socket 路径不一致或者 MySQL 服务根本没启动。解决先systemctl status mysql确认服务在跑。如果服务正常把连接配置里的localhost改成127.0.0.1强制走 TCP 而不是 socket。或者找到 MySQL 真实的 socket 路径在php.ini里设置mysqli.default_socket /var/run/mysqld/mysqld.sock。5.2 中文商品名显示为问号或乱码现象数据库里中文正常但页面上显示???或æœ?è£?。原因字符集在三个环节中有一个不一致——数据库建库字符集、表字符集、PHP 连接字符集。解决建库时用utf8mb4连接后立即执行$conn-set_charset(utf8mb4)HTML 头部加meta charsetutf-8。三处统一后乱码消失。如果数据已经乱码需要重新导入 SQL 文件导入时指定--default-character-setutf8mb4。5.3 图片上传成功但页面不显示现象uploads/目录里能看到图片文件但商品列表页图片是裂图。原因数据库里存的路径是相对路径但页面所在的目录层级不同相对路径解析错误。解决统一用相对于网站根目录的路径比如/uploads/goods_xxx.jpg前面加斜杠。或者在模板里用__DIR__或$_SERVER[DOCUMENT_ROOT]拼绝对路径。检查浏览器控制台的 Network 面板看图片请求的 URL 是什么就能定位路径问题。5.4 后台页面报 headers already sent现象登录成功后跳转报Warning: Cannot modify header information - headers already sent。原因header()函数之前有输出哪怕是一个空格或 BOM 头。解决检查?php之前有没有空行或空格PHP 文件保存时选「UTF-8 无 BOM」格式。session_start()和header()必须放在文件最顶部前面不能有任何 HTML 或 echo 输出。5.5 下单时库存扣成负数现象两个用户同时买最后一件商品库存变成 -1。原因没有用事务和行级锁两个请求同时读到库存为 1都判断通过都执行了扣减。解决用SELECT ... FOR UPDATE在事务内锁定行或者用UPDATE goods SET stock stock - ? WHERE id ? AND stock ?这种带条件的更新检查affected_rows是否为 1为 0 说明库存不足。6. 二次开发与验证用一条 SQL 确认数据一致性跑通之后怎么验证这套系统的数据是可靠的我一般会写一条对账 SQL检查订单总金额和订单明细汇总是否一致-- 检查订单主表总价与明细汇总是否一致 SELECT o.id, o.order_no, o.total AS order_total, SUM(oi.price * oi.num) AS items_total FROM orders o LEFT JOIN order_items oi ON oi.order_id o.id GROUP BY o.id HAVING ABS(o.total - IFNULL(SUM(oi.price * oi.num), 0)) 0.01;这条 SQL 的逻辑是按订单分组把明细表里每条商品的「单价 × 数量」加起来和订单主表的total对比。如果差值的绝对值大于 0.01浮点数精度容差说明这单数据有问题。正常情况下应该返回空结果集。如果查出记录就去检查下单时的事务逻辑看是不是某次扣减或插入失败了。二次开发时最常见的需求是加一个「商品搜索」功能。在goods表上建索引然后改查询ALTER TABLE goods ADD INDEX idx_name (name);?php $keyword trim($_GET[keyword] ?? ); if ($keyword ! ) { $like % . $keyword . %; $stmt $conn-prepare(SELECT id, name, price, img FROM goods WHERE status 1 AND name LIKE ? ORDER BY id DESC LIMIT 20); $stmt-bind_param(s, $like); $stmt-execute(); $result $stmt-get_result(); } ?LIKE %关键词%会导致索引失效但课程设计数据量小性能影响可以忽略。如果数据量上万就要换成全文索引或搜索引擎。还有一个容易忽略的点config.php里的数据库密码不要提交到公开仓库。我习惯在项目根目录加一个.gitignore把config.php排除掉同时提供一个config.example.php模板。这样别人拿到源码后复制一份改密码就能跑不会泄露自己的数据库信息。从那以后我每次拿到一个 PHP 项目都强制先跑一遍「导入 SQL → 改连接配置 → 访问首页 → 走一遍下单流程」这四步确认数据链路通了再去看代码细节。希望帮到你。本文还有配套的精品资源点击获取
返回列表