451 lines
13 KiB
JavaScript
451 lines
13 KiB
JavaScript
const express = require('express');
|
||
const { getDB } = require('../database');
|
||
const { auth, adminAuth } = require('../middleware/auth');
|
||
|
||
const router = express.Router();
|
||
|
||
// 获取商品列表
|
||
router.get('/', async (req, res) => {
|
||
try {
|
||
const { page = 1, limit = 10, search = '', category = '', status = '' } = req.query;
|
||
|
||
// 确保参数为有效数字
|
||
const pageNum = Math.max(1, parseInt(page) || 1);
|
||
const limitNum = Math.max(1, Math.min(100, parseInt(limit) || 10)); // 限制最大100条
|
||
const offset = Math.max(0, (pageNum - 1) * limitNum);
|
||
|
||
console.log('分页参数:', { pageNum, limitNum, offset, search, category, status });
|
||
|
||
let whereClause = 'WHERE 1=1';
|
||
const params = [];
|
||
|
||
if (search) {
|
||
whereClause += ' AND name LIKE ?';
|
||
params.push(`%${search}%`);
|
||
}
|
||
|
||
if (category) {
|
||
whereClause += ' AND category = ?';
|
||
params.push(category);
|
||
}
|
||
|
||
if (status) {
|
||
whereClause += ' AND status = ?';
|
||
params.push(status);
|
||
} else {
|
||
whereClause += ' AND status = "active"';
|
||
}
|
||
|
||
// 获取总数
|
||
const countQuery = `SELECT COUNT(*) as total FROM products ${whereClause}`;
|
||
const [countResult] = await getDB().execute(countQuery, params);
|
||
const total = countResult[0].total;
|
||
|
||
// 获取商品列表
|
||
const query = `
|
||
SELECT id, name, category, points_price as points, stock, image_url as image, description, status, created_at, updated_at
|
||
FROM products
|
||
${whereClause}
|
||
ORDER BY created_at DESC
|
||
LIMIT ${limitNum} OFFSET ${offset}
|
||
`;
|
||
|
||
// 确保参数数组正确传递
|
||
const queryParams = [...params];
|
||
console.log('Query params:', queryParams, 'Query:', query);
|
||
const [products] = await getDB().execute(query, queryParams);
|
||
|
||
res.json({
|
||
success: true,
|
||
data: {
|
||
products,
|
||
pagination: {
|
||
page: pageNum,
|
||
limit: limitNum,
|
||
total,
|
||
pages: Math.ceil(total / limitNum)
|
||
}
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('获取商品列表失败:', error);
|
||
res.status(500).json({ success: false, message: '获取商品列表失败' });
|
||
}
|
||
});
|
||
|
||
// 获取商品分类列表
|
||
router.get('/categories', async (req, res) => {
|
||
try {
|
||
const [categories] = await getDB().execute(
|
||
'SELECT DISTINCT category FROM products WHERE status = "active" AND category IS NOT NULL'
|
||
);
|
||
|
||
res.json({
|
||
success: true,
|
||
data: {
|
||
categories: categories.map(item => item.category)
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('获取商品分类失败:', error);
|
||
res.status(500).json({ success: false, message: '获取商品分类失败' });
|
||
}
|
||
});
|
||
|
||
// 获取单个商品详情
|
||
router.get('/:id', async (req, res) => {
|
||
try {
|
||
const { id } = req.params;
|
||
|
||
const query = `
|
||
SELECT id, name, category, price, points_price as points, stock, image_url as image, description, details, status, created_at, updated_at
|
||
FROM products
|
||
WHERE id = ?
|
||
`;
|
||
|
||
const [products] = await getDB().execute(query, [id]);
|
||
|
||
if (products.length === 0) {
|
||
return res.status(404).json({ success: false, message: '商品不存在' });
|
||
}
|
||
|
||
// 增强商品数据,添加前端需要的字段
|
||
const product = products[0];
|
||
const enhancedProduct = {
|
||
...product,
|
||
images: product.image ? [product.image] : ['/imgs/default-product.png'], // 将单个图片转为数组
|
||
tags: product.category ? [product.category] : [], // 将分类作为标签
|
||
sales: Math.floor(Math.random() * 1000) + 100, // 模拟销量数据
|
||
rating: (Math.random() * 2 + 3).toFixed(1), // 模拟评分 3-5分
|
||
originalPoints: product.points + Math.floor(Math.random() * 100), // 模拟原价
|
||
discount: Math.floor(Math.random() * 3 + 7) // 模拟折扣 7-9折
|
||
};
|
||
|
||
res.json({
|
||
success: true,
|
||
data: { product: enhancedProduct }
|
||
});
|
||
} catch (error) {
|
||
console.error('获取商品详情失败:', error);
|
||
res.status(500).json({ success: false, message: '获取商品详情失败' });
|
||
}
|
||
});
|
||
|
||
// 创建商品(管理员权限)
|
||
router.post('/', auth, adminAuth, async (req, res) => {
|
||
try {
|
||
|
||
const { name, description, price, points_price, stock, category, image_url, details, status = 'active' } = req.body;
|
||
|
||
if (!name || !price || !points_price || stock === undefined) {
|
||
return res.status(400).json({ message: '商品名称、原价、积分价格和库存不能为空' });
|
||
}
|
||
|
||
const [result] = await getDB().execute(
|
||
`INSERT INTO products (name, description, price, points_price, stock, category, image_url, details, status, created_at, updated_at)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`,
|
||
[name, description, price, points_price, stock, category || null, image_url, details, status]
|
||
);
|
||
|
||
res.status(201).json({
|
||
success: true,
|
||
message: '商品创建成功',
|
||
data: { productId: result.insertId }
|
||
});
|
||
} catch (error) {
|
||
console.error('创建商品错误:', error);
|
||
res.status(500).json({ message: '创建商品失败' });
|
||
}
|
||
});
|
||
|
||
// 更新商品(管理员权限)
|
||
router.put('/:id', auth, adminAuth, async (req, res) => {
|
||
try {
|
||
|
||
const productId = req.params.id;
|
||
const { name, description, price, points_price, stock, category, image_url, details, status } = req.body;
|
||
|
||
// 检查商品是否存在
|
||
const [products] = await getDB().execute(
|
||
'SELECT id FROM products WHERE id = ?',
|
||
[productId]
|
||
);
|
||
|
||
if (products.length === 0) {
|
||
return res.status(404).json({ message: '商品不存在' });
|
||
}
|
||
|
||
// 构建更新字段
|
||
const updateFields = [];
|
||
const updateValues = [];
|
||
|
||
if (name) {
|
||
updateFields.push('name = ?');
|
||
updateValues.push(name);
|
||
}
|
||
|
||
if (description !== undefined) {
|
||
updateFields.push('description = ?');
|
||
updateValues.push(description);
|
||
}
|
||
|
||
if (price !== undefined) {
|
||
updateFields.push('price = ?');
|
||
updateValues.push(price);
|
||
}
|
||
|
||
if (points_price !== undefined) {
|
||
updateFields.push('points_price = ?');
|
||
updateValues.push(points_price);
|
||
}
|
||
|
||
if (stock !== undefined) {
|
||
updateFields.push('stock = ?');
|
||
updateValues.push(stock);
|
||
}
|
||
|
||
if (category !== undefined) {
|
||
updateFields.push('category = ?');
|
||
updateValues.push(category);
|
||
}
|
||
|
||
if (image_url !== undefined) {
|
||
updateFields.push('image_url = ?');
|
||
updateValues.push(image_url);
|
||
}
|
||
|
||
if (details !== undefined) {
|
||
updateFields.push('details = ?');
|
||
updateValues.push(details);
|
||
}
|
||
|
||
if (status) {
|
||
updateFields.push('status = ?');
|
||
updateValues.push(status);
|
||
}
|
||
|
||
if (updateFields.length === 0) {
|
||
return res.status(400).json({ message: '没有要更新的字段' });
|
||
}
|
||
|
||
updateFields.push('updated_at = NOW()');
|
||
updateValues.push(productId);
|
||
|
||
await getDB().execute(
|
||
`UPDATE products SET ${updateFields.join(', ')} WHERE id = ?`,
|
||
updateValues
|
||
);
|
||
|
||
res.json({
|
||
success: true,
|
||
message: '商品更新成功'
|
||
});
|
||
} catch (error) {
|
||
console.error('更新商品错误:', error);
|
||
res.status(500).json({ message: '更新商品失败' });
|
||
}
|
||
});
|
||
|
||
// 删除商品(管理员权限)
|
||
router.delete('/:id', auth, adminAuth, async (req, res) => {
|
||
try {
|
||
const { id } = req.params;
|
||
|
||
// 检查商品是否存在
|
||
const checkQuery = 'SELECT id FROM products WHERE id = ?';
|
||
const [existing] = await getDB().execute(checkQuery, [id]);
|
||
|
||
if (existing.length === 0) {
|
||
return res.status(404).json({ success: false, message: '商品不存在' });
|
||
}
|
||
|
||
// 检查是否有相关订单
|
||
const orderCheckQuery = 'SELECT id FROM orders WHERE product_id = ? LIMIT 1';
|
||
const [orders] = await getDB().execute(orderCheckQuery, [id]);
|
||
|
||
if (orders.length > 0) {
|
||
return res.status(400).json({ success: false, message: '该商品存在相关订单,无法删除' });
|
||
}
|
||
|
||
const query = 'DELETE FROM products WHERE id = ?';
|
||
await getDB().execute(query, [id]);
|
||
|
||
res.json({
|
||
success: true,
|
||
message: '商品删除成功'
|
||
});
|
||
} catch (error) {
|
||
console.error('删除商品失败:', error);
|
||
res.status(500).json({ success: false, message: '删除商品失败' });
|
||
}
|
||
});
|
||
|
||
// 获取商品统计信息(管理员权限)
|
||
router.get('/stats', auth, adminAuth, async (req, res) => {
|
||
try {
|
||
// 获取商品总数
|
||
const totalQuery = 'SELECT COUNT(*) as total FROM products';
|
||
const [totalResult] = await getDB().execute(totalQuery);
|
||
const totalProducts = totalResult[0].total;
|
||
|
||
// 获取活跃商品数
|
||
const activeQuery = 'SELECT COUNT(*) as total FROM products WHERE status = "active"';
|
||
const [activeResult] = await getDB().execute(activeQuery);
|
||
const activeProducts = activeResult[0].total;
|
||
|
||
// 获取库存不足商品数(库存小于10)
|
||
const lowStockQuery = 'SELECT COUNT(*) as total FROM products WHERE stock < 10';
|
||
const [lowStockResult] = await getDB().execute(lowStockQuery);
|
||
const lowStockProducts = lowStockResult[0].total;
|
||
|
||
// 获取本月新增商品数
|
||
const monthlyQuery = `
|
||
SELECT COUNT(*) as total
|
||
FROM products
|
||
WHERE YEAR(created_at) = YEAR(CURDATE()) AND MONTH(created_at) = MONTH(CURDATE())
|
||
`;
|
||
const [monthlyResult] = await getDB().execute(monthlyQuery);
|
||
const monthlyProducts = monthlyResult[0].total;
|
||
|
||
// 计算月增长率
|
||
const lastMonthQuery = `
|
||
SELECT COUNT(*) as total
|
||
FROM products
|
||
WHERE YEAR(created_at) = YEAR(DATE_SUB(CURDATE(), INTERVAL 1 MONTH))
|
||
AND MONTH(created_at) = MONTH(DATE_SUB(CURDATE(), INTERVAL 1 MONTH))
|
||
`;
|
||
const [lastMonthResult] = await getDB().execute(lastMonthQuery);
|
||
const lastMonthProducts = lastMonthResult[0].total;
|
||
|
||
const monthlyGrowth = lastMonthProducts > 0
|
||
? ((monthlyProducts - lastMonthProducts) / lastMonthProducts * 100).toFixed(1)
|
||
: 0;
|
||
|
||
res.json({
|
||
success: true,
|
||
data: {
|
||
stats: {
|
||
totalProducts,
|
||
activeProducts,
|
||
lowStockProducts,
|
||
monthlyProducts,
|
||
monthlyGrowth: parseFloat(monthlyGrowth)
|
||
}
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('获取商品统计失败:', error);
|
||
res.status(500).json({ success: false, message: '获取商品统计失败' });
|
||
}
|
||
});
|
||
|
||
// 获取商品评论
|
||
router.get('/:id/reviews', async (req, res) => {
|
||
try {
|
||
const { id } = req.params;
|
||
|
||
// 这里可以从数据库获取真实的评论数据
|
||
// 目前返回模拟数据,格式匹配前端期望
|
||
const mockReviews = [
|
||
{
|
||
id: 1,
|
||
user: {
|
||
name: '用户1',
|
||
avatar: null
|
||
},
|
||
rating: 5,
|
||
content: '商品质量很好,非常满意!',
|
||
createdAt: '2024-01-15 10:30:00',
|
||
images: null
|
||
},
|
||
{
|
||
id: 2,
|
||
user: {
|
||
name: '用户2',
|
||
avatar: null
|
||
},
|
||
rating: 4,
|
||
content: '性价比不错,值得购买。',
|
||
createdAt: '2024-01-14 15:20:00',
|
||
images: null
|
||
},
|
||
{
|
||
id: 3,
|
||
user: {
|
||
name: '用户3',
|
||
avatar: null
|
||
},
|
||
rating: 5,
|
||
content: '发货速度快,包装精美。',
|
||
createdAt: '2024-01-13 09:45:00',
|
||
images: null
|
||
}
|
||
];
|
||
|
||
res.json({
|
||
success: true,
|
||
data: {
|
||
reviews: mockReviews,
|
||
total: mockReviews.length,
|
||
averageRating: 4.7
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('获取商品评论失败:', error);
|
||
res.status(500).json({ success: false, message: '获取商品评论失败' });
|
||
}
|
||
});
|
||
|
||
// 获取推荐商品
|
||
router.get('/:id/recommended', async (req, res) => {
|
||
try {
|
||
const id = parseInt(req.params.id);
|
||
|
||
// 获取同类别的其他商品作为推荐
|
||
const query = `
|
||
SELECT p2.id, p2.name, p2.category, p2.price, p2.points_price as points,
|
||
p2.stock, p2.image_url as image, p2.description
|
||
FROM products p1
|
||
JOIN products p2 ON p1.category = p2.category
|
||
WHERE p1.id = ? AND p2.id != ? AND p2.status = 'active'
|
||
ORDER BY RAND()
|
||
LIMIT 6
|
||
`;
|
||
|
||
const [recommendedProducts] = await getDB().execute(query, [id, id]);
|
||
|
||
// 如果同类别商品不足,补充其他热门商品
|
||
if (recommendedProducts.length < 6) {
|
||
const remainingCount = 6 - recommendedProducts.length;
|
||
if (remainingCount > 0) {
|
||
const additionalQuery = `
|
||
SELECT id, name, category, price, points_price as points,
|
||
stock, image_url as image, description
|
||
FROM products
|
||
WHERE id != ? AND status = 'active'
|
||
ORDER BY RAND()
|
||
LIMIT ${remainingCount}
|
||
`;
|
||
|
||
const [additionalProducts] = await getDB().execute(
|
||
additionalQuery,
|
||
[id]
|
||
);
|
||
|
||
recommendedProducts.push(...additionalProducts);
|
||
}
|
||
}
|
||
|
||
res.json({
|
||
success: true,
|
||
data: {
|
||
products: recommendedProducts
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('获取推荐商品失败:', error);
|
||
res.status(500).json({ success: false, message: '获取推荐商品失败' });
|
||
}
|
||
});
|
||
|
||
module.exports = router; |