459 lines
14 KiB
JavaScript
459 lines
14 KiB
JavaScript
const express = require('express');
|
|
const { getDB } = require('../database');
|
|
const { auth, adminAuth } = require('../middleware/auth');
|
|
|
|
const router = express.Router();
|
|
|
|
// 生成订单号
|
|
function generateOrderNo() {
|
|
const timestamp = Date.now().toString();
|
|
const random = Math.random().toString(36).substr(2, 5);
|
|
return `ORD${timestamp}${random}`.toUpperCase();
|
|
}
|
|
|
|
// 获取订单列表
|
|
router.get('/', auth, async (req, res) => {
|
|
try {
|
|
const { page = 1, limit = 10, search = '', orderNumber = '', username = '', status = '', startDate = '', endDate = '' } = req.query;
|
|
|
|
|
|
|
|
// 确保参数为有效数字
|
|
const pageNum = parseInt(page) || 1;
|
|
const limitNum = parseInt(limit) || 10;
|
|
const offset = (pageNum - 1) * limitNum;
|
|
const isAdmin = req.user.role === 'admin';
|
|
|
|
let whereClause = 'WHERE 1=1';
|
|
const params = [];
|
|
|
|
// 非管理员只能查看自己的订单
|
|
if (!isAdmin) {
|
|
whereClause += ' AND o.user_id = ?';
|
|
params.push(req.user.id);
|
|
}
|
|
|
|
if (search) {
|
|
whereClause += ' AND (o.order_no LIKE ? OR u.username LIKE ?)';
|
|
params.push(`%${search}%`, `%${search}%`);
|
|
}
|
|
|
|
if (orderNumber) {
|
|
whereClause += ' AND o.order_no LIKE ?';
|
|
params.push(`%${orderNumber}%`);
|
|
}
|
|
|
|
if (username ) {
|
|
whereClause += ' AND u.username LIKE ?';
|
|
params.push(`%${username}%`);
|
|
}
|
|
|
|
if (status && status.trim()) {
|
|
whereClause += ' AND o.status = ?';
|
|
params.push(status);
|
|
}
|
|
|
|
if (startDate && startDate.trim()) {
|
|
whereClause += ' AND DATE(o.created_at) >= ?';
|
|
params.push(startDate);
|
|
}
|
|
|
|
if (endDate && endDate.trim()) {
|
|
whereClause += ' AND DATE(o.created_at) <= ?';
|
|
params.push(endDate);
|
|
}
|
|
|
|
// 获取总数
|
|
const countQuery = `
|
|
SELECT COUNT(*) as total
|
|
FROM orders as o
|
|
LEFT JOIN users u ON o.user_id = u.id
|
|
${whereClause}
|
|
`;
|
|
console.log(countQuery,params);
|
|
|
|
const [countResult] = await getDB().execute(countQuery, params);
|
|
const total = countResult[0].total;
|
|
console.log(total,'数量');
|
|
|
|
// 获取订单列表
|
|
const query = `
|
|
SELECT
|
|
o.id, o.order_no, o.user_id, o.total_amount, o.total_points,
|
|
o.status, o.address, o.created_at, o.updated_at,
|
|
u.username
|
|
FROM orders o
|
|
LEFT JOIN users u ON o.user_id = u.id
|
|
${whereClause}
|
|
ORDER BY o.created_at DESC
|
|
LIMIT ${limitNum} OFFSET ${offset}
|
|
`;
|
|
|
|
const [orders] = await getDB().execute(query, [...params]);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
orders,
|
|
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('/:id', auth, async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const isAdmin = req.user.role === 'admin';
|
|
|
|
let whereClause = 'WHERE o.id = ?';
|
|
const params = [id];
|
|
|
|
// 非管理员只能查看自己的订单
|
|
if (!isAdmin) {
|
|
whereClause += ' AND o.user_id = ?';
|
|
params.push(req.user.id);
|
|
}
|
|
|
|
const query = `
|
|
SELECT
|
|
o.id, o.order_no, o.user_id, o.total_amount, o.total_points,
|
|
o.status, o.address, o.created_at, o.updated_at,
|
|
u.username, u.phone
|
|
FROM orders o
|
|
LEFT JOIN users u ON o.user_id = u.id
|
|
${whereClause}
|
|
`;
|
|
|
|
const [orders] = await getDB().execute(query, params);
|
|
|
|
if (orders.length === 0) {
|
|
return res.status(404).json({ success: false, message: '订单不存在' });
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: { order: orders[0] }
|
|
});
|
|
} catch (error) {
|
|
console.error('获取订单详情失败:', error);
|
|
res.status(500).json({ success: false, message: '获取订单详情失败' });
|
|
}
|
|
});
|
|
|
|
// 创建订单
|
|
router.post('/', auth, async (req, res) => {
|
|
const db = getDB();
|
|
await db.query('START TRANSACTION');
|
|
|
|
try {
|
|
|
|
|
|
const { product_id, quantity, shipping_address } = req.body;
|
|
const user_id = req.user.id;
|
|
|
|
// 验证必填字段
|
|
if (!product_id || !quantity || !shipping_address) {
|
|
return res.status(400).json({ success: false, message: '请填写所有必填字段' });
|
|
}
|
|
|
|
// 检查商品是否存在且有效
|
|
const [products] = await db.execute(
|
|
'SELECT id, name, points_price, stock, status FROM products WHERE id = ?',
|
|
[product_id]
|
|
);
|
|
|
|
if (products.length === 0 || products[0].status !== 'active') {
|
|
await db.query('ROLLBACK');
|
|
return res.status(404).json({ success: false, message: '商品不存在或已下架' });
|
|
}
|
|
|
|
const product = products[0];
|
|
|
|
// 检查库存
|
|
if (product.stock < quantity) {
|
|
await db.query('ROLLBACK');
|
|
return res.status(400).json({ success: false, message: '库存不足' });
|
|
}
|
|
|
|
// 计算总积分
|
|
const totalPoints = product.points_price * quantity;
|
|
|
|
// 检查用户积分是否足够
|
|
const [users] = await db.execute(
|
|
'SELECT id, username, points FROM users WHERE id = ?',
|
|
[user_id]
|
|
);
|
|
|
|
if (users.length === 0) {
|
|
await db.query('ROLLBACK');
|
|
return res.status(404).json({ success: false, message: '用户不存在' });
|
|
}
|
|
|
|
const user = users[0];
|
|
|
|
if (user.points < totalPoints) {
|
|
await db.query('ROLLBACK');
|
|
return res.status(400).json({ success: false, message: '积分不足' });
|
|
}
|
|
|
|
// 生成订单号
|
|
const orderNumber = 'ORD' + Date.now() + Math.random().toString(36).substr(2, 4).toUpperCase();
|
|
|
|
// 创建订单
|
|
const [orderResult] = await db.execute(
|
|
`INSERT INTO orders (order_no, user_id, total_amount, total_points, status, address, created_at, updated_at)
|
|
VALUES (?, ?, ?, ?, 'pending', ?, NOW(), NOW())`,
|
|
[orderNumber, user_id, 0, totalPoints, shipping_address]
|
|
);
|
|
|
|
// 扣除用户积分
|
|
await db.execute(
|
|
'UPDATE users SET points = points - ? WHERE id = ?',
|
|
[totalPoints, user_id]
|
|
);
|
|
|
|
// 减少商品库存
|
|
await db.execute(
|
|
'UPDATE products SET stock = stock - ? WHERE id = ?',
|
|
[quantity, product_id]
|
|
);
|
|
|
|
// 记录积分历史
|
|
await db.execute(
|
|
`INSERT INTO points_history (user_id, amount, type, description, created_at)
|
|
VALUES (?, ?, 'spend', ?, NOW())`,
|
|
[user_id, -totalPoints, `购买商品:${product.name}`]
|
|
);
|
|
|
|
await db.query('COMMIT');
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
message: '订单创建成功',
|
|
data: {
|
|
orderId: orderResult.insertId,
|
|
orderNumber,
|
|
pointsUsed: totalPoints
|
|
}
|
|
});
|
|
} catch (error) {
|
|
await db.query('ROLLBACK');
|
|
console.error('创建订单失败:', error);
|
|
res.status(500).json({ success: false, message: '创建订单失败' });
|
|
}
|
|
});
|
|
|
|
// 用户取消订单
|
|
router.put('/:id/cancel', auth, async (req, res) => {
|
|
const db = getDB();
|
|
await db.query('START TRANSACTION');
|
|
|
|
try {
|
|
|
|
const orderId = req.params.id;
|
|
const userId = req.user.id;
|
|
|
|
// 检查订单是否存在且属于当前用户
|
|
const [orders] = await db.execute(
|
|
'SELECT id, user_id, total_points, status FROM orders WHERE id = ? AND user_id = ?',
|
|
[orderId, userId]
|
|
);
|
|
|
|
if (orders.length === 0) {
|
|
await db.query('ROLLBACK');
|
|
return res.status(404).json({ success: false, message: '订单不存在' });
|
|
}
|
|
|
|
const order = orders[0];
|
|
|
|
if (order.status !== 'pending') {
|
|
await db.query('ROLLBACK');
|
|
return res.status(400).json({ success: false, message: '只能取消待处理的订单' });
|
|
}
|
|
|
|
// 退还用户积分
|
|
await db.execute(
|
|
'UPDATE users SET points = points + ? WHERE id = ?',
|
|
[order.total_points, userId]
|
|
);
|
|
|
|
// 记录积分历史
|
|
await db.execute(
|
|
`INSERT INTO points_history (user_id, amount, type, description, created_at)
|
|
VALUES (?, ?, 'refund', '订单取消退还积分', NOW())`,
|
|
[userId, order.total_points]
|
|
);
|
|
|
|
// 更新订单状态
|
|
await db.execute(
|
|
'UPDATE orders SET status = "cancelled", updated_at = NOW() WHERE id = ?',
|
|
[orderId]
|
|
);
|
|
|
|
await db.query('COMMIT');
|
|
|
|
res.json({ success: true, message: '订单已取消' });
|
|
} catch (error) {
|
|
await db.query('ROLLBACK');
|
|
console.error('取消订单失败:', error);
|
|
res.status(500).json({ success: false, message: '取消订单失败' });
|
|
}
|
|
});
|
|
|
|
// 确认收货
|
|
router.put('/:id/confirm', auth, async (req, res) => {
|
|
try {
|
|
const orderId = req.params.id;
|
|
const userId = req.user.id;
|
|
|
|
// 检查订单是否存在且属于当前用户
|
|
const [orders] = await getDB().execute(
|
|
'SELECT id, status FROM orders WHERE id = ? AND user_id = ?',
|
|
[orderId, userId]
|
|
);
|
|
|
|
if (orders.length === 0) {
|
|
return res.status(404).json({ success: false, message: '订单不存在' });
|
|
}
|
|
|
|
const order = orders[0];
|
|
|
|
if (order.status !== 'shipped') {
|
|
return res.status(400).json({ success: false, message: '只能确认已发货的订单' });
|
|
}
|
|
|
|
// 更新订单状态
|
|
await getDB().execute(
|
|
'UPDATE orders SET status = "completed", updated_at = NOW() WHERE id = ?',
|
|
[orderId]
|
|
);
|
|
|
|
res.json({ success: true, message: '确认收货成功' });
|
|
} catch (error) {
|
|
console.error('确认收货失败:', error);
|
|
res.status(500).json({ success: false, message: '确认收货失败' });
|
|
}
|
|
});
|
|
|
|
// 更新订单状态(管理员)
|
|
router.put('/:id/status', auth, adminAuth, async (req, res) => {
|
|
const db = getDB();
|
|
await db.query('START TRANSACTION');
|
|
|
|
try {
|
|
|
|
const orderId = req.params.id;
|
|
const { status } = req.body;
|
|
|
|
const validStatuses = ['pending', 'shipped', 'completed', 'cancelled'];
|
|
if (!validStatuses.includes(status)) {
|
|
await db.query('ROLLBACK');
|
|
return res.status(400).json({ success: false, message: '无效的订单状态' });
|
|
}
|
|
|
|
// 检查订单是否存在
|
|
const [orders] = await db.execute(
|
|
'SELECT id, user_id, total_points, status FROM orders WHERE id = ?',
|
|
[orderId]
|
|
);
|
|
|
|
if (orders.length === 0) {
|
|
await db.query('ROLLBACK');
|
|
return res.status(404).json({ success: false, message: '订单不存在' });
|
|
}
|
|
|
|
const order = orders[0];
|
|
|
|
// 如果是取消订单,需要退还积分
|
|
if (status === 'cancelled' && order.status !== 'cancelled') {
|
|
// 退还用户积分
|
|
await db.execute(
|
|
'UPDATE users SET points = points + ? WHERE id = ?',
|
|
[order.total_points, order.user_id]
|
|
);
|
|
|
|
// 记录积分历史
|
|
await db.execute(
|
|
`INSERT INTO points_history (user_id, amount, type, description, created_at)
|
|
VALUES (?, ?, 'earn', '订单取消退还积分', NOW())`,
|
|
[order.user_id, order.points_cost]
|
|
);
|
|
}
|
|
|
|
// 更新订单状态
|
|
await db.execute(
|
|
'UPDATE orders SET status = ?, updated_at = NOW() WHERE id = ?',
|
|
[status, orderId]
|
|
);
|
|
|
|
await db.query('COMMIT');
|
|
|
|
res.json({ success: true, message: '订单状态已更新' });
|
|
} catch (error) {
|
|
await db.query('ROLLBACK');
|
|
console.error('更新订单状态失败:', error);
|
|
res.status(500).json({ success: false, message: '更新订单状态失败' });
|
|
}
|
|
});
|
|
|
|
// 获取订单统计信息(管理员权限)
|
|
router.get('/stats', auth, adminAuth, async (req, res) => {
|
|
try {
|
|
// 总订单数
|
|
const [totalOrders] = await getDB().execute('SELECT COUNT(*) as count FROM orders');
|
|
|
|
// 待发货订单数
|
|
const [pendingOrders] = await getDB().execute('SELECT COUNT(*) as count FROM orders WHERE status = "pending"');
|
|
|
|
// 已完成订单数
|
|
const [completedOrders] = await getDB().execute('SELECT COUNT(*) as count FROM orders WHERE status = "completed"');
|
|
|
|
// 本月新增订单
|
|
const [monthOrders] = await getDB().execute(
|
|
'SELECT COUNT(*) as count FROM orders WHERE YEAR(created_at) = YEAR(NOW()) AND MONTH(created_at) = MONTH(NOW())'
|
|
);
|
|
|
|
// 上月订单数(用于计算增长率)
|
|
const [lastMonthOrders] = await getDB().execute(
|
|
'SELECT COUNT(*) as count FROM orders WHERE YEAR(created_at) = YEAR(DATE_SUB(NOW(), INTERVAL 1 MONTH)) AND MONTH(created_at) = MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH))'
|
|
);
|
|
|
|
// 计算月增长率
|
|
const lastMonthCount = lastMonthOrders[0].count;
|
|
const currentMonthCount = monthOrders[0].count;
|
|
let monthGrowthRate = 0;
|
|
if (lastMonthCount > 0) {
|
|
monthGrowthRate = ((currentMonthCount - lastMonthCount) / lastMonthCount * 100).toFixed(1);
|
|
}
|
|
|
|
// 总积分消费
|
|
const [totalPointsConsumed] = await getDB().execute('SELECT SUM(points_cost) as total FROM orders WHERE status != "cancelled"');
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
totalOrders: totalOrders[0].count,
|
|
pendingOrders: pendingOrders[0].count,
|
|
completedOrders: completedOrders[0].count,
|
|
monthOrders: monthOrders[0].count,
|
|
monthGrowthRate: parseFloat(monthGrowthRate),
|
|
totalPointsConsumed: totalPointsConsumed[0].total || 0
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('获取订单统计失败:', error);
|
|
res.status(500).json({ success: false, message: '获取订单统计失败' });
|
|
}
|
|
});
|
|
|
|
module.exports = router; |