以下是一个基于 PHP+MySQL 的简易电商商品评论系统实现方案,包含数据库设计、API 接口及前端展示代码:
一、数据库设计(MySQL)
-- 商品表
CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL COMMENT '商品名称',
`price` decimal(10,2) NOT NULL COMMENT '价格',
`category_id` int(11) DEFAULT NULL COMMENT '分类ID',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
);
-- 评论表
CREATE TABLE `product_reviews` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) NOT NULL COMMENT '商品ID',
`user_id` int(11) DEFAULT NULL COMMENT '用户ID(可为空,匿名用户)',
`user_name` varchar(100) DEFAULT NULL COMMENT '用户名(匿名时显示)',
`content` text NOT NULL COMMENT '评论内容',
`rating` tinyint(1) NOT NULL COMMENT '评分(1-5星)',
`status` tinyint(1) DEFAULT 1 COMMENT '状态(0=待审核,1=已通过,2=已拒绝)',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_product_id` (`product_id`),
KEY `idx_status` (`status`)
);
-- 评论图片表
CREATE TABLE `review_images` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`review_id` int(11) NOT NULL COMMENT '评论ID',
`image_url` varchar(255) NOT NULL COMMENT '图片URL',
`sort` int(11) DEFAULT 0 COMMENT '排序',
PRIMARY KEY (`id`),
KEY `idx_review_id` (`review_id`)
);
二、PHP 后端实现
1. 数据库连接(db_connect.php)
<?php
$host = 'localhost';
$dbname = 'ecommerce';
$username = 'root';
$password = 'password';
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
die("数据库连接失败: " . $e->getMessage());
}
2. 获取商品评论 API(get_reviews.php)
<?php
require 'db_connect.php';
// 参数验证
$product_id = isset($_GET['product_id']) ? intval($_GET['product_id']) : 0;
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
$page_size = isset($_GET['page_size']) ? min(intval($_GET['page_size']), 100) : 20;
if (!$product_id) {
die(json_encode(['code' => 400, 'message' => '缺少商品ID']));
}
// 查询评论
$offset = ($page - 1) * $page_size;
$stmt = $pdo->prepare("
SELECT r.*, GROUP_CONCAT(i.image_url) as images
FROM product_reviews r
LEFT JOIN review_images i ON r.id = i.review_id
WHERE r.product_id = ? AND r.status = 1
GROUP BY r.id
ORDER BY r.created_at DESC
LIMIT ? OFFSET ?
");
$stmt->execute([$product_id, $page_size, $offset]);
$reviews = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 处理图片
foreach ($reviews as &$review) {
$review['images'] = $review['images'] ? explode(',', $review['images']) : [];
}
// 返回结果
echo json_encode([
'code' => 200,
'data' => $reviews,
'page' => $page,
'page_size' => $page_size,
'total' => $pdo->query("SELECT COUNT(*) FROM product_reviews WHERE product_id = $product_id AND status = 1")->fetchColumn()
]);
3. 提交评论 API(submit_review.php)
<?php
require 'db_connect.php';
// 参数验证
$product_id = isset($_POST['product_id']) ? intval($_POST['product_id']) : 0;
$user_id = isset($_POST['user_id']) ? intval($_POST['user_id']) : 0;
$user_name = isset($_POST['user_name']) ? htmlspecialchars($_POST['user_name']) : '匿名用户';
$content = isset($_POST['content']) ? htmlspecialchars($_POST['content']) : '';
$rating = isset($_POST['rating']) ? min(max(intval($_POST['rating']), 1), 5) : 5;
if (!$product_id || empty($content)) {
die(json_encode(['code' => 400, 'message' => '参数不完整']));
}
// 插入评论
$stmt = $pdo->prepare("
INSERT INTO product_reviews (product_id, user_id, user_name, content, rating, status)
VALUES (?, ?, ?, ?, ?, 0)
");
$stmt->execute([$product_id, $user_id, $user_name, $content, $rating]);
$review_id = $pdo->lastInsertId();
// 处理图片上传(示例代码,实际需处理文件上传逻辑)
if (isset($_FILES['images']) && $_FILES['images']['error'][0] === 0) {
foreach ($_FILES['images']['tmp_name'] as $key => $tmp_name) {
$image_path = '/uploads/reviews/' . uniqid() . '.jpg';
move_uploaded_file($tmp_name, __DIR__ . $image_path);
$pdo->prepare("
INSERT INTO review_images (review_id, image_url, sort)
VALUES (?, ?, ?)
")->execute([$review_id, $image_path, $key]);
}
}
echo json_encode(['code' => 200, 'message' => '评论提交成功,等待审核']);
三、前端展示(HTML+JavaScript)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>商品评论</title>
<style>
.review-list {
max-width: 800px;
margin: 0 auto;
}
.review-item {
border-bottom: 1px solid #eee;
padding: 15px 0;
}
.review-header {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
}
.review-rating {
color: #ff9900;
}
.review-images {
display: flex;
gap: 10px;
margin-top: 10px;
}
.review-images img {
max-height: 100px;
border-radius: 4px;
}
.review-form {
margin-top: 30px;
padding: 20px;
background-color: #f9f9f9;
border-radius: 8px;
}
.rating-stars {
font-size: 24px;
color: #ccc;
cursor: pointer;
}
.rating-stars .star.active {
color: #ff9900;
}
</style>
</head>
<body>
<div class="review-list">
<h2>商品评论</h2>
<div id="reviews-container"></div>
<div class="review-form">
<h3>发表评论</h3>
<div class="rating-stars">
<span class="star" data-rating="1">★</span>
<span class="star" data-rating="2">★</span>
<span class="star" data-rating="3">★</span>
<span class="star" data-rating="4">★</span>
<span class="star" data-rating="5">★</span>
<input type="hidden" id="rating" value="5">
</div>
<textarea id="review-content" placeholder="请输入评论内容" rows="4" style="width: 100%; margin: 10px 0;"></textarea>
<input type="file" id="review-images" multiple accept="image/*">
<button id="submit-review">提交评论</button>
</div>
</div>
<script>
const productId = 123; // 替换为实际商品ID
let currentPage = 1;
// 加载评论
function loadReviews(page = 1) {
fetch(`get_reviews.php?product_id=${productId}&page=${page}`)
.then(response => response.json())
.then(data => {
renderReviews(data.data);
currentPage = page;
})
.catch(error => console.error('加载评论失败:', error));
}
// 渲染评论
function renderReviews(reviews) {
const container = document.getElementById('reviews-container');
container.innerHTML = '';
if (reviews.length === 0) {
container.innerHTML = '<p>暂无评论</p>';
return;
}
reviews.forEach(review => {
const reviewEl = document.createElement('div');
reviewEl.className = 'review-item';
reviewEl.innerHTML = `
<div class="review-header">
<span>${review.user_name}</span>
<span class="review-rating">${'★'.repeat(review.rating)}</span>
</div>
<p>${review.content}</p>
<div class="review-date">${review.created_at}</div>
${review.images.length > 0 ? `
<div class="review-images">
${review.images.map(img => `<img src="${img}" alt="评论图片">`).join('')}
</div>
` : ''}
`;
container.appendChild(reviewEl);
});
}
// 评分选择
document.querySelectorAll('.rating-stars .star').forEach(star => {
star.addEventListener('click', () => {
const rating = parseInt(star.getAttribute('data-rating'));
document.getElementById('rating').value = rating;
document.querySelectorAll('.rating-stars .star').forEach(s => {
if (parseInt(s.getAttribute('data-rating')) <= rating) {
s.classList.add('active');
} else {
s.classList.remove('active');
}
});
});
});
// 提交评论
document.getElementById('submit-review').addEventListener('click', () => {
const content = document.getElementById('review-content').value.trim();
const rating = document.getElementById('rating').value;
const images = document.getElementById('review-images').files;
if (!content) {
alert('请输入评论内容');
return;
}
const formData = new FormData();
formData.append('product_id', productId);
formData.append('content', content);
formData.append('rating', rating);
for (let i = 0; i < images.length; i++) {
formData.append('images[]', images[i]);
}
fetch('submit_review.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
alert(data.message);
if (data.code === 200) {
document.getElementById('review-content').value = '';
document.getElementById('review-images').value = '';
}
})
.catch(error => console.error('提交评论失败:', error));
});
// 初始化加载
loadReviews();
</script>
</body>
</html>
四、部署与优化建议
-
安全优化:
- 使用 PDO 预处理语句防止 SQL 注入
- 对用户输入进行 XSS 过滤
- 图片上传需验证文件类型和大小
-
性能优化:
- 添加数据库索引(如评论表的
product_id和status) - 评论列表采用分页加载
- 图片使用 CDN 加速
- 添加数据库索引(如评论表的
-
功能扩展:
- 添加评论审核机制
- 支持评论点赞 / 踩功能
- 实现评论分类筛选(好评 / 中评 / 差评)
-
用户体验:
-
添加评论加载动画
-
实现评论提交成功提示
-
支持图片预览功能
-
此方案提供了电商评论系统的基础架构,可根据实际需求进行扩展和优化。编辑分享