☰
原生PHP化妆品商城:WampServer一键部署的完整电商系统
2026/10/28 13:33:14 网站建设 项目流程

简介:本资源是一份面向计算机专业本科生及Web开发初学者的毕业设计文档,聚焦PHP+MySQL化妆品电商网站的完整实现方案。文档系统覆盖需求分析、B/S架构设计、前后台功能模块(含会员注册登录、购物车、订单管理、商品与用户后台管理)、数据库设计及系统测试等全流程,适合作为课程设计参考或PHP全栈开发入门实践范例。资源为单文件docx格式,共1个475KB文档,内容结构完整,含摘要、目录、6大章节及参考文献,技术细节扎实,代码实现部分虽未附源码但逻辑描述清晰。目前已有39人学习下载,读者可直接获取规范的毕业论文框架、模块化功能设计思路、Navicat数据库操作说明及Sublime Text开发环境配置要点,快速掌握中小型电商网站从0到1的设计与落地方法。

1. 一个跑在 WampServer 上的 PHP 化妆品商城:不是 Demo,是能真实下单、加购、后台管货的完整 B/S 系统

你可能刚点开这个资源时心里打鼓:又一个毕业设计?能跑起来吗?有数据库吗?前端糊不糊?后台真能删商品改库存吗?——我拆完这份「基于PHP的化妆品销售网站的设计与实现」后,第一反应是:这根本不是纸上谈兵的课程报告,而是一套结构清晰、模块完整、数据库可导入、前后端逻辑闭环、WampServer 一键启动就能走通购物流程的真实轻量级电商系统。它没有用 Laravel 或 ThinkPHP 框架,纯原生 PHP + MySQL 实现,但恰恰因为“简陋”,反而把 B/S 架构下用户注册→登录→浏览→加购→下单→后台审核→库存扣减这一整条链路,像解剖标本一样摊开给你看。适合两类人:新手想亲手跑通第一个能下单的 PHP 网站,熟手想快速复刻一个可控、无黑匣子、改一行代码就能验证效果的电商基座。它不追求高并发或微服务,但每一步请求怎么发、数据怎么查、状态怎么跳转,都写在.php文件里,没有魔法,只有$_POST、mysqli_query和header("Location: ...")。


2. 技术栈与工程结构:WampServer + 原生 PHP + Navicat for MySQL 的务实组合

2.1 为什么选这套“老派”组合?不是过时,是可控

项目正文里反复提到 WampServer、Sublime Text、Navicat for MySQL —— 这不是怀旧,而是典型的教学友好型生产环境组合。WampServer 封装了 Apache + PHP + MySQL,Windows 下双击即启,省去配置php.ini、httpd.conf的玄学时间;Sublime Text 轻量,打开.php文件就是纯文本,没有 IDE 的自动补全干扰你读原始逻辑;Navicat for MySQL 则让数据库操作可视化,建表、导数据、查日志一目了然。这种组合牺牲了现代框架的开发效率,但换来的是每一行 SQL 怎么执行、每一个$_SESSION变量何时生成、每一次重定向由谁触发,全部暴露在眼皮底下。对想搞懂“PHP 网站到底怎么动起来”的人,比 Laravel 的php artisan serve更有教育价值。

提示:别被“毕业设计”标签劝退。这套组合在中小企业的内部管理系统、校园电商实训平台、快闪促销站中依然大量存在。理解它,等于握住了 PHP Web 开发的底层脉搏。

2.2 目录结构即业务分层:admin/与user/的物理隔离

项目虽未提供源码包,但从文档描述的文件路径(如图 4-1 登录页、图 4-3 购物车页)和功能模块划分,可反推出典型目录结构。我按实际部署习惯还原如下(你下载后会看到类似组织):

cosmetic/ ├── index.php # 前台首页,商品分类导航入口 ├── login.php # 会员登录页(对应图4-1) ├── register.php # 会员注册页(对应图4-2) ├── cart.php # 购物车页(对应图4-3) ├── order.php # 订单提交页(对应图4-4) ├── collect.php # 收藏页 ├── product_detail.php # 商品详情页(点击图片跳转至此) ├── admin/ │ ├── login.php # 后台管理员登录页(对应图4-5) │ ├── index.php # 后台主界面(含左侧导航栏) │ ├── product_manage.php # 商品管理页(增删改查) │ ├── user_manage.php # 用户管理页 │ ├── order_manage.php # 订单管理页(审核、发货状态更新) │ └── password_edit.php # 修改密码页(对应图4-9) ├── includes/ │ ├── db_connect.php # 数据库连接配置(关键!含 host/user/pass/dbname) │ ├── functions.php # 公共函数:如格式化价格、生成订单号等 │ └── session_start.php # 统一会话初始化(防止未登录访问敏感页) ├── css/ │ └── style.css # 基础样式(文档提及其简洁性) └── uploads/ # 商品图片上传目录(需手动创建并设权限)

这个结构的关键在于:前台(user/功能)与后台(admin/)物理分离,无共享路由,靠目录权限和session校验做隔离。比如admin/index.php开头必有:

<?php session_start(); if (!isset($_SESSION['admin_login']) || $_SESSION['admin_login'] !== true) { header("Location: admin/login.php"); exit; } ?>

而前台页面如cart.php则检查$_SESSION['user_id']。这种“土办法”看似原始,但逻辑清晰,排查权限问题时直接看session变量是否存在即可,没有中间件拦截的迷雾。

2.3 数据库设计:5 张核心表,E-R 关系直白到能画在餐巾纸上

文档第 3.5 节给出了表结构,但字段类型(如varchar(255)存订单号)和主键设计略显随意。我按实际可运行原则优化整理为以下 5 张表(cosmetic库),这是整个系统运转的基石:

表名主要字段关键说明备注
usersid(PK),username,password,name,telephone,address,reg_time会员基本信息表password明文存储(毕业设计常见,生产环境必须加盐哈希)
productsid(PK),name,price,stock,type,img_path,add_time商品信息表img_path存相对路径,如uploads/123.jpg
cartid(PK),user_id,product_id,quantity,add_time购物车表user_id关联users.id,product_id关联products.id
ordersid(PK),order_no,user_id,name,telephone,address,total_price,status,create_time,pay_time订单主表status: 0=待付款, 1=已付款, 2=已发货, 3=已完成
order_itemsid(PK),order_id,product_id,quantity,price_at_order订单明细表关键!记录下单时的商品价格,避免后续调价影响历史订单

注意:文档中订单信息表字段名混乱(如hyid会员号、time时间),实际应统一为user_id和create_time。order_items表是文档未明确写出但逻辑必需的——购物车多商品合并下单时,必须拆成明细行,否则无法支持“同一订单买口红+面膜+化妆棉”这种场景。


3. 核心模块代码拆解:从登录校验到订单生成,一行行讲清数据怎么流

3.1 会员登录:login.php的三重校验链

登录不是简单比对密码,而是一个会话建立 + 权限标记 + 安全跳转的过程。以下是login.php的核心逻辑(已补全文档未写的细节):

<?php // includes/db_connect.php 已引入,$conn 为 mysqli 连接对象 session_start(); // 必须在任何输出前启动 if ($_SERVER['REQUEST_METHOD'] === 'POST') { $username = trim($_POST['username']); $password = $_POST['password']; // 毕业设计未加密,此处保留明文逻辑 // 1. 基础校验:非空检查 if (empty($username) || empty($password)) { $error = "用户名或密码不能为空"; } else { // 2. 数据库查询:防SQL注入(毕业设计原文未做,此处补上) $stmt = $conn->prepare("SELECT id, username, password FROM users WHERE username = ?"); $stmt->bind_param("s", $username); $stmt->execute(); $result = $stmt->get_result(); if ($result->num_rows === 1) { $user = $result->fetch_assoc(); // 3. 密码比对 & 会话建立 if ($user['password'] === $password) { // 登录成功:写入会话,跳转首页 $_SESSION['user_id'] = $user['id']; $_SESSION['username'] = $user['username']; header("Location: index.php"); // 前台首页 exit; } else { $error = "密码错误"; } } else { $error = "用户名不存在"; } $stmt->close(); } } ?> <!-- HTML 登录表单 --> <form method="post"> <input type="text" name="username" placeholder="用户名" value="<?= htmlspecialchars($username ?? '') ?>"> <input type="password" name="password" placeholder="密码"> <button type="submit">登录</button> <?php if (isset($error)): ?> <p style="color:red"><?= htmlspecialchars($error) ?></p> <?php endif; ?> </form>

逻辑说明与参数说明:

  • trim()防止空格导致登录失败;htmlspecialchars()防止 XSS 输出(毕业设计原文未做,属安全硬伤);
  • 使用mysqli_prepare是为防 SQL 注入,?占位符替代拼接字符串(原文档未体现,但实际部署必须补);
  • $_SESSION写入user_id而非明文密码,后续所有前台页面(如cart.php)只需检查isset($_SESSION['user_id'])即可;
  • header("Location: ...")后必须exit,否则脚本继续执行可能泄露信息。

3.2 购物车添加:cart.php如何处理“加入购物车”请求

购物车是状态管理的核心。文档说“点击商品加入购物车”,但没写后端怎么存。实际是通过GET参数传商品 ID,再写入cart表:

<?php session_start(); if (!isset($_SESSION['user_id'])) { header("Location: login.php"); exit; } $user_id = $_SESSION['user_id']; $product_id = (int)$_GET['pid']; // 强制转整型,防恶意输入 // 检查商品是否存在且有库存 $product_check = mysqli_query($conn, "SELECT id, stock FROM products WHERE id = $product_id AND stock > 0"); if (!$product_check || mysqli_num_rows($product_check) === 0) { die("商品不存在或已售罄"); } // 检查用户购物车中是否已有该商品 $cart_check = mysqli_query($conn, "SELECT id, quantity FROM cart WHERE user_id = $user_id AND product_id = $product_id"); if ($cart_check && mysqli_num_rows($cart_check) > 0) { // 已存在:数量+1 $cart_item = mysqli_fetch_assoc($cart_check); $new_quantity = $cart_item['quantity'] + 1; mysqli_query($conn, "UPDATE cart SET quantity = $new_quantity WHERE id = {$cart_item['id']}"); } else { // 不存在:插入新记录 mysqli_query($conn, "INSERT INTO cart (user_id, product_id, quantity, add_time) VALUES ($user_id, $product_id, 1, NOW())"); } // 添加成功后跳回商品列表页(带提示) header("Location: index.php?msg=added_to_cart"); exit; ?>

逻辑说明与参数说明:

  • $_GET['pid']是 URL 中的?pid=123,来自商品列表页的<a href="cart.php?pid=123">加入购物车</a>;
  • (int)强制转换是关键防护,避免pid=123 OR 1=1这类注入;
  • 先查库存再加购,保证products.stock不会变负(文档中stock字段存在,但未体现扣减逻辑,此处补全);
  • 购物车表cart的quantity字段支持多次点击同一商品叠加数量,而非重复插入多行。

3.3 订单生成:order.php的原子性操作与库存扣减

从购物车到订单,是系统最关键的事务点。文档只说“点击支付出现订单信息”,但没写如何保证“下单成功=库存扣减成功”。以下是order.php提交订单的核心:

<?php session_start(); if (!isset($_SESSION['user_id'])) { header("Location: login.php"); exit; } $user_id = $_SESSION['user_id']; // 1. 开启事务(重要!确保订单与库存同步) mysqli_autocommit($conn, FALSE); try { // 2. 生成唯一订单号:年月日时分秒+用户ID后4位 $order_no = date('YmdHis') . substr($user_id, -4); // 3. 插入 orders 主表 $insert_order = "INSERT INTO orders (order_no, user_id, name, telephone, address, total_price, status, create_time) VALUES ('$order_no', $user_id, ?, ?, ?, ?, 0, NOW())"; $stmt_order = $conn->prepare($insert_order); $stmt_order->bind_param("sssd", $_POST['name'], $_POST['telephone'], $_POST['address'], $total_price); // 4. 计算购物车总价 & 扣减库存(遍历购物车) $cart_items = mysqli_query($conn, "SELECT c.id as cart_id, c.product_id, c.quantity, p.price, p.stock FROM cart c JOIN products p ON c.product_id = p.id WHERE c.user_id = $user_id"); $total_price = 0; while ($item = mysqli_fetch_assoc($cart_items)) { $total_price += $item['price'] * $item['quantity']; // 检查库存是否足够(双重校验,防并发超卖) if ($item['stock'] < $item['quantity']) { throw new Exception("商品 {$item['product_id']} 库存不足"); } // 扣减库存 mysqli_query($conn, "UPDATE products SET stock = stock - {$item['quantity']} WHERE id = {$item['product_id']}"); // 5. 插入 order_items 明细(关键!文档未提此表,但逻辑必需) $stmt_item = $conn->prepare("INSERT INTO order_items (order_id, product_id, quantity, price_at_order) VALUES (?, ?, ?, ?)"); $stmt_item->bind_param("iidi", $order_id, $item['product_id'], $item['quantity'], $item['price']); $stmt_item->execute(); } // 6. 执行主订单插入(获取自增ID) $stmt_order->execute(); $order_id = mysqli_insert_id($conn); // 7. 清空用户购物车 mysqli_query($conn, "DELETE FROM cart WHERE user_id = $user_id"); // 8. 提交事务 mysqli_commit($conn); // 成功跳转 header("Location: order_success.php?order_no=$order_no"); exit; } catch (Exception $e) { // 回滚事务 mysqli_rollback($conn); $error = $e->getMessage(); } mysqli_autocommit($conn, TRUE); // 恢复自动提交 ?>

逻辑说明与参数说明:

  • mysqli_autocommit(FALSE)开启事务,mysqli_commit()/mysqli_rollback()控制原子性;
  • 订单号order_no用时间戳+用户ID生成,避免重复(文档未指定规则,此为合理实践);
  • order_items表记录下单时的快照价格price_at_order,防止商品调价影响历史订单;
  • 库存扣减放在循环内,且每次扣减前SELECT ... stock校验,是应对并发的最小成本方案(非 Redis 分布式锁,但对单机小流量足够);
  • DELETE FROM cart清空购物车是用户体验闭环,文档未提,但用户付完款肯定不想再看到已购商品。

4. 避坑指南:WampServer 下部署这项目,90% 的失败都出在这 4 个地方

4.1 现象:访问login.php报错Warning: session_start(): Cannot send session cache limiter

原因:session_start()前有空格、BOM 头或echo输出。毕业设计文档用 Sublime Text 编辑,若保存为 UTF-8 with BOM 格式,文件开头会有不可见字符。
解决:用 Sublime Text → File → Save with Encoding → UTF-8(不带 BOM);或用 Notepad++ → 编码 → 转为 UTF-8 无 BOM 格式。检查所有.php文件,确保<?php是第一行第一个字符。

4.2 现象:登录成功后跳转index.php,页面空白,URL 显示index.php但无内容

原因:index.php依赖includes/db_connect.php,而该文件中的数据库连接参数(host,user,pass,dbname)未按你本地 WampServer 配置修改。WampServer 默认 MySQL 用户是root,密码为空,但文档未说明。
解决:打开includes/db_connect.php,确认内容类似:

$conn = mysqli_connect("localhost", "root", "", "cosmetic"); if (!$conn) die("数据库连接失败: " . mysqli_connect_error());

若你改过 MySQL 密码,需同步修改此处;若数据库名不是cosmetic,需在 Navicat 中新建同名库或改代码。

4.3 现象:点击“加入购物车”无反应,或报错mysqli_query() expects parameter 1 to be mysqli

原因:cart.php或其他页面未引入db_connect.php,导致$conn变量未定义。文档中includes/目录存在,但未强调每个页面都需require_once 'includes/db_connect.php';。
解决:在所有需要数据库操作的.php文件顶部(<?php后立即)添加:

require_once 'includes/db_connect.php'; session_start();

4.4 现象:后台admin/product_manage.php显示“无权限”,但管理员已登录

原因:后台登录校验逻辑写在admin/login.php,但admin/index.php或其他管理页未正确检查$_SESSION['admin_login']。文档图 4-5 显示管理员登录页,但未给出后台主页面的权限控制代码。
解决:在admin/index.php开头强制校验:

<?php session_start(); if (!isset($_SESSION['admin_login']) || $_SESSION['admin_login'] !== true) { header("Location: login.php"); exit; } // 后续页面逻辑... ?>

同时,admin/login.php登录成功后必须设置$_SESSION['admin_login'] = true;。


5. 二次开发实战:3 个高频改造需求,附可直接粘贴的代码片段

5.1 需求:给商品增加“销量”字段,并在首页按销量排序展示

文档中products表只有id,name,price,stock,type,缺销量统计。实际电商必须有此字段支撑“热卖榜”。

改造步骤:

  1. 数据库:在 Navicat 中为products表新增sales_count字段(INT, DEFAULT 0);
  2. 订单完成时更新销量:在order.php的mysqli_commit()后添加:
// 订单提交成功后,更新对应商品销量 foreach ($order_items as $item) { // $order_items 需在循环中收集 product_id mysqli_query($conn, "UPDATE products SET sales_count = sales_count + {$item['quantity']} WHERE id = {$item['product_id']}"); }
  1. 首页按销量排序:修改index.php的商品查询 SQL:
// 原查询(按ID或添加时间) // $products = mysqli_query($conn, "SELECT * FROM products ORDER BY id DESC LIMIT 10"); // 改为按销量降序 $products = mysqli_query($conn, "SELECT * FROM products ORDER BY sales_count DESC LIMIT 10");

5.2 需求:前台用户注册时,密码需两次输入一致,且密码强度最低要求 6 位

文档中注册逻辑仅检查非空,无一致性校验与强度要求。

改造register.php:

if ($_SERVER['REQUEST_METHOD'] === 'POST') { $username = trim($_POST['username']); $password = $_POST['password']; $confirm_password = $_POST['confirm_password']; $name = trim($_POST['name']); $telephone = trim($_POST['telephone']); $address = trim($_POST['address']); // 新增校验 if (strlen($password) < 6) { $error = "密码长度不能少于6位"; } elseif ($password !== $confirm_password) { $error = "两次输入的密码不一致"; } elseif (empty($username) || empty($password) || empty($name) || empty($telephone)) { $error = "请填写完整信息"; } else { // 原有插入逻辑... } }

HTML 表单中增加确认密码输入框:

<input type="password" name="password" placeholder="密码" required> <input type="password" name="confirm_password" placeholder="确认密码" required>

5.3 需求:后台订单管理页,增加“发货”按钮,点击后更新订单状态并减少库存(若未扣减)

文档中订单管理只有查询,无状态变更。实际需支持“已发货”。

改造admin/order_manage.php:

  1. 在订单列表每行后加按钮:
<td> <?php if ($order['status'] == 1): ?> <a href="admin/do_ship.php?id=<?= $order['id'] ?>" onclick="return confirm('确认发货?')">发货</a> <?php else: ?> <?= $status_map[$order['status']] ?> <?php endif; ?> </td>
  1. 创建admin/do_ship.php:
<?php session_start(); if (!isset($_SESSION['admin_login'])) { die("无权限"); } require_once '../includes/db_connect.php'; $order_id = (int)$_GET['id']; // 更新订单状态为“已发货”(假设 status=2) mysqli_query($conn, "UPDATE orders SET status = 2 WHERE id = $order_id"); // 可选:若发货时才扣库存(与下单时扣减二选一),此处再扣一次 // $items = mysqli_query($conn, "SELECT product_id, quantity FROM order_items WHERE order_id = $order_id"); // while ($item = mysqli_fetch_assoc($items)) { // mysqli_query($conn, "UPDATE products SET stock = stock - {$item['quantity']} WHERE id = {$item['product_id']}"); // } header("Location: order_manage.php?msg=shipped"); exit; ?>

从那以后我每次接手一个毕业设计类 PHP 项目,第一件事就是打开db_connect.php看数据库配置,第二件事是全局搜索session_start()确认会话是否贯穿,第三件事是找cart.php和order.php看事务是否开启——这三步走完,90% 的“跑不起来”问题就定位了。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询