Files
jurong_circle_black/routes/products.js
2025-08-26 10:06:23 +08:00

451 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;