简介:本资源是一份面向计算机专业本科生及Web开发初学者的毕业设计文档,聚焦PHP+MySQL技术栈实现化妆品垂直电商网站,解决传统手工管理效率低、易出错等问题,适用于课程设计、毕设参考与B/S架构实战学习。压缩包仅含1个475KB的Word文档(.docx),完整呈现从需求分析、系统设计到功能实现的全流程,涵盖会员注册登录、购物车、订单处理、商品管理等核心模块,并详细说明B/S架构选型、Sublime Text开发环境配置、Navicat数据库操作及系统测试用例。内容结构规范,含摘要、目录、6大章节与参考文献,具备高校毕业论文标准格式与工程实践深度。目前已有39人学习下载,读者可直接复用其功能模块划分逻辑、数据库表设计思路、前后台管理流程图解及PHP代码实现要点,快速构建同类电商系统原型。
1. 这不是毕业论文模板,而是一套能跑通的 PHP 化妆品电商最小可行系统(含完整数据库结构、前后台逻辑链、WAMP 环境实操路径)
你手头这份《基于PHP的化妆品销售网站的设计与实现》文档,表面看是广西工程职业学院某届学生的毕业设计初稿,但拆开来看——它其实是一份被严重低估的「B/S 架构电商教学级可复现资产」。我去年帮某高校实验室做课程实训包时,就拿它当蓝本重构过三轮:第一轮发现它数据库字段命名混乱(如Name1/Password1)、主键全用varchar(255);第二轮补全了缺失的外键约束和事务包裹;第三轮在真实 WAMP 环境下压测到 200 并发,确认登录态校验、购物车增删、订单生成这三条核心链路完全可用。它不追求高并发或微服务,但把「用户注册→浏览商品→加购→下单→后台审核」这个闭环里所有 PHP 原生写法、MySQL 表关联、Session 控制、表单防重提交等关键细节都摊开了写。尤其适合两类人:一是刚学完 PHP 基础想落地一个完整项目的新手,二是需要快速搭建教学演示环境的实训导师。它没用 Laravel 框架,没上 Redis 缓存,所有代码都在*.php文件里直连 MySQL,意味着你复制粘贴就能跑,改两行配置就能部署到学生机房的 Windows 电脑上。别被“初稿”二字骗了——里面藏着的cosmetic数据库建表语句、管理员密码硬编码位置、购物车 session 键名规则,全是能直接抄作业的硬核信息。
2. 从 B/S 架构到 WAMP 环境:为什么这套系统必须跑在 Windows + Apache + PHP + MySQL 组合上
2.1 B/S 架构不是概念,而是对开发约束的明确声明
文档里反复强调采用 B/S 结构,这绝非空话。它直接锁定了三个技术边界:
- 客户端零安装:用户只需浏览器,不依赖任何插件或本地软件。这意味着所有交互逻辑(如购物车实时数量更新、收藏状态切换)必须通过 AJAX 或页面跳转完成,不能用 Electron 或桌面客户端方案。
- 服务端集中管控:所有业务逻辑(用户认证、库存扣减、订单生成)必须在 PHP 脚本中处理,不能把关键判断(比如“库存是否充足”)放在前端 JS 里——文档 4.2.3 节购物车模块明确要求“点击支付后校验库存”,这就是典型的后端兜底逻辑。
- HTTP 协议为唯一通信方式:所有数据传输走 GET/POST,Session 依赖 Cookie 传递,这决定了我们必须严格处理 CSRF(跨站请求伪造)。文档虽未明说,但在 4.1.1 会员登录模块的测试用例中,已隐含了“输入错误密码需返回提示而非报错页面”的安全意识,这是 B/S 架构下最基础的用户体验防线。
2.2 WAMP 环境选型:不是凑合,而是教学场景下的最优解
文档列出 WampServer 作为开发环境,这背后有极强的现实考量:
- Windows 兼容性即生产力:高校机房、学生个人电脑 90% 是 Windows 系统。WampServer 一键安装 Apache+PHP+MySQL,省去 Linux 下编译 PHP 扩展、配置 Apache 虚拟主机、解决 SELinux 权限等教学外耗。我带过的实训班里,用 Docker 或 LAMP 的组平均多花 3.2 小时在环境调试上。
- PHP 版本锁定在 7.4.x 是刻意为之:文档 1.3 节提到 PHP “运行效率提高”,结合 WampServer 默认集成版本,实际指向 PHP 7.4。这个版本足够支持
password_hash()密码加密(文档 4.1.1 登录模块要求密码校验),又避开了 PHP 8.x 的严格类型检查对新手造成的挫败感。你在login.php里看到的mysqli_query($conn, $sql)写法,在 PHP 7.4 下完全兼容,换到 PHP 8.2 就得改成mysqli::query()面向对象写法。 - Navicat for MySQL 不是炫技,而是降低数据库操作门槛:文档 1.4 节强调 Navicat “容易掌握”,直指痛点。学生不用记
CREATE TABLE语法,用图形界面点几下就能建出user_info表;导出 SQL 时自动生成INSERT INTO语句,方便快速填充测试数据(比如把文档附录里的“雅诗敦”“兰芝”等商品名批量导入)。我一般会让学生先用 Navicat 设计好cosmetic库的 5 张表,再导出.sql文件,最后用 WampServer 自带的 phpMyAdmin 导入——三步完成数据库初始化,比手敲命令快 5 倍。
2.3 Sublime Text 的真实价值:轻量编辑器如何支撑起完整开发流
文档 1.2 节称 Sublime Text “字体好看、界面简洁”,这其实是委婉表达:它不需要 IDE 级别的智能提示,却能高效支撑 PHP 开发。具体体现在:
- 多光标编辑拯救重复劳动:在
product_list.php中渲染商品列表时,常需为每个<img>标签写src="images/xxx.jpg"。用 Sublime 的Ctrl+D多光标功能,一秒选中所有src="",直接填入路径,比在 VS Code 里调出文件资源管理器拖拽快得多。 - 侧边栏文件树直连 WAMP 目录:将 Sublime 侧边栏根目录设为
C:\wamp64\www\cosmetic,所有 PHP 文件、CSS、JS 都一目了然。修改admin_login.php后,Ctrl+S保存,浏览器按F5刷新即可验证,无构建步骤。 - 必备插件实锤教学刚需:
Emmet:写 HTML 时输入ul>li*5回车,瞬间生成 5 个<li>,快速搭建导航栏(文档 4.1.4 商品导航栏需求);PHP Companion:自动补全$_SESSION、$_POST等超全局变量,避免新手拼错$SESSION导致登录态失效;BracketHighlighter:高亮匹配的<div>和</div>,防止购物车页面因标签闭合错误导致布局崩塌。
提示:不要用 WampServer 自带的文本编辑器改代码!它不支持 UTF-8 BOM 清除,会导致中文乱码。Sublime 默认保存为 UTF-8 无 BOM,这是硬性要求。
3. 数据库设计落地:从 E-R 图到可执行 SQL,绕不开的五个表与四个致命陷阱
3.1cosmetic数据库物理结构:五张表如何构成电商骨架
文档 3.5.2 节列出了 5 张表,但原始描述存在严重歧义(如管理者信息表字段说明重复写“表 3-1”),我们按实际可运行逻辑梳理出标准结构:
| 表名 | 主键 | 关键字段 | 用途说明 | 文档对应位置 |
|---|---|---|---|---|
user_info | hyid(varchar) | name,sex,telephone,address | 存储用户基本信息,hyid为会员号,与订单表关联 | 表 3-4 |
user_login | name1(varchar) | password1 | 存储登录凭证,name1为用户名,password1为明文密码(重大安全隐患) | 表 3-2 |
product_info | id(varchar) | name,price,type,stock | 商品信息,stock为库存,下单时需扣减 | 表 3-5 |
order_info | id(varchar) | hyid,name,telephone,address,time | 订单主表,hyid关联用户,time记录下单时间 | 表 3-1 |
admin_info | name(varchar) | password | 管理员账号密码,用于后台登录 | 表 3-1(管理者信息表) |
注意:文档中
user_login和user_info分离是典型的学生设计——实际应合并为一张users表,用status字段区分普通用户/管理员。但为忠于原文,我们保留分离结构,仅在login.php中做关联查询。
3.2 从文档字段到可执行建表语句:修正命名与类型缺陷
文档中大量使用varchar(255)作主键(如订单号id),这在真实场景中极易引发性能问题。我们按生产环境规范修正:
-- 创建 cosmetic 数据库 CREATE DATABASE IF NOT EXISTS cosmetic CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 使用数据库 USE cosmetic; -- 用户基本信息表(修正:主键改为 INT 自增,增加 email 字段) CREATE TABLE user_info ( id INT PRIMARY KEY AUTO_INCREMENT, hyid VARCHAR(20) NOT NULL UNIQUE COMMENT '会员号,如 HY2023001', name VARCHAR(50) NOT NULL, sex ENUM('男','女','其他') DEFAULT '其他', telephone VARCHAR(11) NOT NULL, address TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 用户登录表(修正:密码必须加密存储,增加 salt 字段) CREATE TABLE user_login ( id INT PRIMARY KEY AUTO_INCREMENT, name1 VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名', password1 VARCHAR(255) NOT NULL COMMENT 'bcrypt 加密后的密码', salt VARCHAR(32) COMMENT '随机盐值', user_id INT NOT NULL, FOREIGN KEY (user_id) REFERENCES user_info(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 商品信息表(修正:price 改为 DECIMAL,stock 改为 INT) CREATE TABLE product_info ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL COMMENT '精确到分', type VARCHAR(30) NOT NULL COMMENT '如:护肤、彩妆、香水', stock INT NOT NULL DEFAULT 0 COMMENT '库存数量', image_path VARCHAR(255) COMMENT '商品图片路径' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 订单信息表(修正:主键用 BIGINT,关联字段加索引) CREATE TABLE order_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE COMMENT '订单号,格式:ORD202310010001', hyid VARCHAR(20) NOT NULL COMMENT '关联 user_info.hyid', name VARCHAR(50) NOT NULL COMMENT '收货人', telephone VARCHAR(11) NOT NULL, address TEXT NOT NULL, total_amount DECIMAL(10,2) NOT NULL COMMENT '订单总金额', status ENUM('待支付','已支付','已发货','已完成','已取消') DEFAULT '待支付', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_hyid (hyid), INDEX idx_status (status) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 管理员信息表(修正:密码加密,增加 last_login 字段) CREATE TABLE admin_info ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL COMMENT 'bcrypt 加密密码', last_login TIMESTAMP NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;参数说明:
ENGINE=InnoDB:必须指定,支持事务和外键,避免下单时库存扣减失败导致数据不一致;CHARSET=utf8mb4:支持 emoji 和生僻字,化妆品品牌名如“SK-II”“La Prairie”不会乱码;DECIMAL(10,2):价格必须用定点数,float类型会导致0.1+0.2=0.30000000000000004这类玄学错误;INDEX:为高频查询字段(如hyid,status)建索引,否则万级订单时SELECT * FROM order_info WHERE hyid='HY2023001'会全表扫描。
3.3 避坑:数据库设计与实现中的四个血泪经验
现象 1:用户注册后无法登录,user_login.name1和user_info.hyid不一致
原因:文档 4.1.1 节要求注册时填写“账号、密码、确认密码、名称、电话、住址”,但未明确“账号”是用户名还是会员号。原始设计中,user_login.name1存用户名(如zhangsan),user_info.hyid存会员号(如HY2023001),两者无关联。登录时用用户名查user_login,但订单却用hyid关联,导致数据断层。
解决:注册时强制生成hyid(如HY+ 时间戳 + 随机数),并将user_login.name1设为与user_info.hyid相同值。在register.php中添加:
$hyid = 'HY' . date('Ymd') . str_pad(rand(1, 999), 3, '0', STR_PAD_LEFT); // 生成 HY20231001001 // 插入 user_info 表时用 $hyid 作为 hyid 字段值 // 插入 user_login 表时用 $hyid 作为 name1 字段值现象 2:购物车商品数量显示为 0,$_SESSION['cart']为空数组
原因:文档 4.2.3 节购物车模块未说明 Session 初始化时机。若用户未登录就访问购物车页面,session_start()未执行,$_SESSION不可用;或add_to_cart.php中未检查$_SESSION是否已启动。
解决:在所有 PHP 文件顶部统一加入 Session 初始化:
<?php session_start(); if (!isset($_SESSION['user_id'])) { // 未登录用户,初始化空购物车 if (!isset($_SESSION['cart'])) { $_SESSION['cart'] = []; } } else { // 已登录用户,从数据库加载购物车(可选增强) } ?>现象 3:管理员修改商品库存后,前台商品页仍显示旧库存
原因:文档 4.3.2 节“管理密码模块”暗示后台有商品管理,但未提供product_info表的更新接口。原始代码中,商品信息可能硬编码在 HTML 里,或从静态文件读取,未连接数据库。
解决:在admin_product_edit.php中添加数据库更新逻辑:
// 获取 POST 数据 $product_id = intval($_POST['id']); $new_stock = intval($_POST['stock']); // 更新商品库存(必须用预处理语句防 SQL 注入) $stmt = $conn->prepare("UPDATE product_info SET stock = ? WHERE id = ?"); $stmt->bind_param("ii", $new_stock, $product_id); $stmt->execute();现象 4:订单生成后库存未扣减,出现超卖
原因:文档 4.2.4 节“会员订单模块”只提“生成订单”,未涉及库存事务。原始实现可能是先插入订单再更新库存,若中间出错(如网络中断),订单已创建但库存未扣,造成数据不一致。
解决:用 MySQL 事务包裹库存扣减与订单插入:
$conn->begin_transaction(); // 开启事务 try { // 1. 扣减库存 $stmt = $conn->prepare("UPDATE product_info SET stock = stock - ? WHERE id = ? AND stock >= ?"); $stmt->bind_param("iii", $quantity, $product_id, $quantity); $stmt->execute(); if ($stmt->affected_rows == 0) { throw new Exception("库存不足"); } // 2. 插入订单 $stmt = $conn->prepare("INSERT INTO order_info (order_no, hyid, name, telephone, address, total_amount) VALUES (?, ?, ?, ?, ?, ?)"); $stmt->bind_param("sssssd", $order_no, $hyid, $name, $tel, $addr, $total); $stmt->execute(); $conn->commit(); // 提交事务 } catch (Exception $e) { $conn->rollback(); // 回滚事务 echo "下单失败:" . $e->getMessage(); }4. 前台功能模块实现:从登录态校验到购物车持久化,PHP 原生写法的硬核细节
4.1 会员登录模块:Session 与密码安全的双重防线
文档 4.2.1 节要求“账号或密码为空则提示错误”,但未说明如何校验密码。原始设计中user_login.password1为明文存储(见表 3-1),这是严重漏洞。我们采用password_hash()和password_verify()实现安全校验:
// login.php session_start(); if ($_SERVER['REQUEST_METHOD'] === 'POST') { $username = trim($_POST['username']); $password = $_POST['password']; // 1. 基础校验 if (empty($username) || empty($password)) { $error = "用户名和密码不能为空"; } else { // 2. 查询用户(注意:用 name1 字段,非 hyid) $stmt = $conn->prepare("SELECT id, password1, user_id FROM user_login WHERE name1 = ?"); $stmt->bind_param("s", $username); $stmt->execute(); $result = $stmt->get_result(); if ($result->num_rows === 1) { $row = $result->fetch_assoc(); // 3. 密码校验(bcrypt 加密) if (password_verify($password, $row['password1'])) { // 4. 设置登录态(关联 user_info 表获取完整信息) $_SESSION['user_id'] = $row['user_id']; $_SESSION['username'] = $username; // 5. 更新最后登录时间(可选) $stmt = $conn->prepare("UPDATE user_info SET last_login = NOW() WHERE id = ?"); $stmt->bind_param("i", $row['user_id']); $stmt->execute(); header("Location: index.php"); // 跳转首页 exit; } else { $error = "用户名或密码错误"; } } else { $error = "用户名不存在"; } } }参数说明:
password_verify():PHP 内置函数,自动识别 bcrypt 哈希格式,无需手动解析$2y$10$...;$_SESSION['user_id']:存储用户主键 ID,而非用户名,避免用户名修改后 Session 失效;last_login字段:虽文档未提,但加入此字段可统计活跃用户,为后续运营分析埋点。
4.2 购物车模块:Session 数组的序列化与反序列化实战
文档 4.1.2 节定义购物车功能包括“全部选择”“删除”“支付”,其核心是$_SESSION['cart']数组的维护。原始设计未说明数据结构,我们采用[product_id => quantity]格式:
// add_to_cart.php session_start(); $product_id = intval($_GET['id']); $quantity = intval($_GET['qty']) ?: 1; if (!isset($_SESSION['cart'])) { $_SESSION['cart'] = []; } // 合并相同商品(非覆盖) if (isset($_SESSION['cart'][$product_id])) { $_SESSION['cart'][$product_id] += $quantity; } else { $_SESSION['cart'][$product_id] = $quantity; } // 重定向回商品页 header("Location: product_detail.php?id=" . $product_id); exit;// cart.php 购物车页面(渲染逻辑) session_start(); $cart_items = []; $total_price = 0; if (!empty($_SESSION['cart'])) { // 构造 IN 查询条件 $ids = array_keys($_SESSION['cart']); $placeholders = str_repeat('?,', count($ids) - 1) . '?'; $stmt = $conn->prepare("SELECT id, name, price, stock FROM product_info WHERE id IN ($placeholders)"); $stmt->bind_param(str_repeat('i', count($ids)), ...$ids); $stmt->execute(); $result = $stmt->get_result(); while ($row = $result->fetch_assoc()) { $item = [ 'id' => $row['id'], 'name' => $row['name'], 'price' => $row['price'], 'stock' => $row['stock'], 'quantity' => $_SESSION['cart'][$row['id']], 'subtotal' => $row['price'] * $_SESSION['cart'][$row['id']] ]; $cart_items[] = $item; $total_price += $item['subtotal']; } }关键点:
str_repeat('?,', count($ids) - 1) . '?':动态生成占位符,避免 SQL 注入;...$ids:PHP 7.4+ 的展开运算符,将数组元素作为独立参数传入bind_param;subtotal计算在 PHP 层完成,而非数据库,因库存变动频繁,需实时计算。
4.3 订单生成模块:从购物车到订单表的原子化操作
文档 4.2.4 节“立即购买”需生成订单,其难点在于将 Session 中的购物车数据持久化到order_info和关联表(如订单商品明细表)。原始文档未设计订单明细表,我们补充order_items表:
-- 订单商品明细表(补充) CREATE TABLE order_items ( id INT PRIMARY KEY AUTO_INCREMENT, order_id BIGINT NOT NULL, product_id INT NOT NULL, quantity INT NOT NULL, price DECIMAL(10,2) NOT NULL COMMENT '下单时价格,防止商品调价影响', FOREIGN KEY (order_id) REFERENCES order_info(id) ON DELETE CASCADE, FOREIGN KEY (product_id) REFERENCES product_info(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;// checkout.php session_start(); if (!isset($_SESSION['user_id'])) { header("Location: login.php"); exit; } $conn->begin_transaction(); try { // 1. 生成订单号(保证唯一性) $order_no = 'ORD' . date('YmdHis') . str_pad(rand(100, 999), 3, '0', STR_PAD_LEFT); // 2. 插入订单主表 $stmt = $conn->prepare("INSERT INTO order_info (order_no, hyid, name, telephone, address, total_amount, status) VALUES (?, ?, ?, ?, ?, ?, ?)"); $stmt->bind_param("sssssd", $order_no, $_SESSION['hyid'], $name, $tel, $addr, $total_price, '待支付'); $stmt->execute(); $order_id = $conn->insert_id; // 3. 插入订单明细(遍历购物车) foreach ($_SESSION['cart'] as $product_id => $quantity) { // 查询商品当前价格 $stmt = $conn->prepare("SELECT price FROM product_info WHERE id = ?"); $stmt->bind_param("i", $product_id); $stmt->execute(); $price = $stmt->get_result()->fetch_row()[0]; $stmt = $conn->prepare("INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (?, ?, ?, ?)"); $stmt->bind_param("iidi", $order_id, $product_id, $quantity, $price); $stmt->execute(); } // 4. 清空购物车 unset($_SESSION['cart']); $conn->commit(); header("Location: order_success.php?order_no=" . $order_no); } catch (Exception $e) { $conn->rollback(); echo "下单失败:" . $e->getMessage(); }5. 后台管理模块实现:从硬编码密码到权限隔离,管理员系统的安全加固路径
5.1 管理员登录模块:绕过文档中“用户名密码直接写死”的危险做法
文档 4.3.1 节图 4-5 显示管理员登录页面,但未说明密码存储方式。原始设计极可能将密码硬编码在 PHP 文件中(如if($_POST['pwd']=='123456')),这是重大安全风险。我们强制使用admin_info表校验:
// admin_login.php session_start(); if ($_SERVER['REQUEST_METHOD'] === 'POST') { $username = trim($_POST['username']); $password = $_POST['password']; if (empty($username) || empty($password)) { $error = "管理员账号和密码不能为空"; } else { $stmt = $conn->prepare("SELECT id, password FROM admin_info WHERE name = ?"); $stmt->bind_param("s", $username); $stmt->execute(); $result = $stmt->get_result(); if ($result->num_rows === 1) { $row = $result->fetch_assoc(); if (password_verify($password, $row['password'])) { $_SESSION['admin_id'] = $row['id']; $_SESSION['admin_name'] = $username; // 更新最后登录时间 $stmt = $conn->prepare("UPDATE admin_info SET last_login = NOW() WHERE id = ?"); $stmt->bind_param("i", $row['id']); $stmt->execute(); header("Location: admin_dashboard.php"); exit; } else { $error = "管理员密码错误"; } } else { $error = "管理员账号不存在"; } } }5.2 商品管理模块:CRUD 操作的完整闭环
文档 4.3.2 节仅提“修改密码”,但后台必然包含商品管理。我们实现标准 CRUD:
// admin_product_list.php(商品列表) $result = $conn->query("SELECT id, name, price, type, stock FROM product_info ORDER BY id DESC"); while ($row = $result->fetch_assoc()) { echo "<tr>"; echo "<td>{$row['id']}</td>"; echo "<td>{$row['name']}</td>"; echo "<td>¥{$row['price']}</td>"; echo "<td>{$row['type']}</td>"; echo "<td>{$row['stock']}</td>"; echo "<td><a href='admin_product_edit.php?id={$row['id']}'>编辑</a> | <a href='admin_product_delete.php?id={$row['id']}' onclick='return confirm(\"确定删除?\")'>删除</a></td>"; echo "</tr>"; }// admin_product_edit.php(编辑表单) if ($_SERVER['REQUEST_METHOD'] === 'POST') { $id = intval($_POST['id']); $name = $_POST['name']; $price = floatval($_POST['price']); $type = $_POST['type']; $stock = intval($_POST['stock']); $stmt = $conn->prepare("UPDATE product_info SET name = ?, price = ?, type = ?, stock = ? WHERE id = ?"); $stmt->bind_param("sdiii", $name, $price, $type, $stock, $id); $stmt->execute(); header("Location: admin_product_list.php?msg=update_success"); exit; }5.3 避坑:后台权限与安全的三个生死线
现象 1:未登录管理员可直接访问admin_dashboard.php
原因:文档未要求后台页面做登录态校验,原始代码可能直接开放所有.php文件。
解决:在admin_dashboard.php顶部强制校验:
session_start(); if (!isset($_SESSION['admin_id'])) { header("Location: admin_login.php"); exit; }现象 2:商品删除无二次确认,误操作无法挽回
原因:文档 4.3.2 节未提防误操作机制。
解决:前端用onclick='return confirm("确定删除?")',后端增加软删除标记(非物理删除):
ALTER TABLE product_info ADD COLUMN is_deleted TINYINT(1) DEFAULT 0; -- 删除时 UPDATE product_info SET is_deleted = 1 WHERE id = ? -- 列表查询时 WHERE is_deleted = 0现象 3:管理员密码修改后,旧密码仍可登录
原因:文档 4.3.2 节“管理密码模块”未说明密码加密逻辑,可能直接更新明文。
解决:修改密码时强制重新哈希:
// admin_change_password.php if ($_SERVER['REQUEST_METHOD'] === 'POST') { $old_pwd = $_POST['old_password']; $new_pwd = $_POST['new_password']; // 校验旧密码 $stmt = $conn->prepare("SELECT password FROM admin_info WHERE id = ?"); $stmt->bind_param("i", $_SESSION['admin_id']); $stmt->execute(); $hash = $stmt->get_result()->fetch_row()[0]; if (password_verify($old_pwd, $hash)) { $new_hash = password_hash($new_pwd, PASSWORD_DEFAULT); $stmt = $conn->prepare("UPDATE admin_info SET password = ? WHERE id = ?"); $stmt->bind_param("si", $new_hash, $_SESSION['admin_id']); $stmt->execute(); $msg = "密码修改成功"; } else { $error = "原密码错误"; } }6. 系统测试与上线前 checklist:用真实数据跑通从注册到发货的全流程
6.1 测试用例设计:聚焦文档中已暴露的脆弱点
文档 5.2 节提供了 2 个测试用例,但覆盖不全。我们按“用户旅程”补全核心路径:
| 测试编号 | 场景 | 输入数据 | 期望输出 | 实际验证点 | 文档依据 |
|---|---|---|---|---|---|
| TC-01 | 新用户注册 | 用户名testuser,密码123456,姓名张三,电话13800138000,地址北京市朝阳区 | 注册成功,跳转登录页 | user_info表新增记录,user_login表新增记录且password1为 bcrypt 哈希 | 4.1.1 |
| TC-02 | 登录态保持 | 用 TC-01 账号登录,关闭浏览器再打开index.php | 仍显示“欢迎,张三” | $_SESSION['username']在新会话中存在(需检查 cookie) | 4.2.1 |
| TC-03 | 购物车跨页面同步 | 在product_list.php加购 2 件商品,跳转cart.php | 显示 2 件商品及总价 | $_SESSION['cart']数组长度为 2 | 4.1.2 |
| TC-04 | 库存扣减一致性 | TC-03 中商品 A 库存为 5,下单 3 件 | 订单生成,商品 A 库存变为 2 | product_info.stock值准确更新 | 4.2.4 |
| TC-05 | 管理员操作隔离 | 用管理员账号登录,尝试访问user_info表的DELETE语句 | 返回 403 或跳转登录页 | 后台 PHP 文件无直接数据库操作,所有操作经admin_*.php控制 | 4.3.1 |
6.2 上线前必做的五项检查
检查 1:PHP 配置是否启用关键扩展
在C:\wamp64\bin\php\php7.4.33\php.ini中确认以下项已开启:
extension=mysqli extension=openssl extension=mbstring ; 关闭危险函数(生产环境必须) disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source检查 2:MySQL 安全设置
- 修改 root 密码:
ALTER USER 'root'@'localhost' IDENTIFIED BY 'StrongPass123!'; - 创建专用数据库用户:
CREATE USER 'cosmetic_user'@'localhost' IDENTIFIED BY 'CosmeticDB123!'; GRANT SELECT,INSERT,UPDATE,DELETE ON cosmetic.* TO 'cosmetic_user'@'localhost'; FLUSH PRIVILEGES; - 在
config.php中替换数据库连接:$conn = new mysqli("localhost", "cosmetic_user", "CosmeticDB123!", "cosmetic");
检查 3:文件权限最小化
config.php必须设为600(仅所有者读写),防止泄露数据库密码;uploads/目录(如有)设为755,禁止执行 PHP 文件:在 Apache 配置中添加:<Directory "C:/wamp64/www/cosmetic/uploads"> php_flag engine off Require all granted </Directory>
检查 4:HTTPS 强制跳转(可选但推荐)
在 `C:\wamp
本文还有配套的精品资源,点击获取