const express = require('express'); const { getDB } = require('../database'); const { auth, adminAuth } = require('../middleware/auth'); const router = express.Router(); // 订单管理路由 // 获取订单列表 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,o.total_rongdou, 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]); // 为每个订单获取商品详情 for (const order of orders) { const [orderItems] = await getDB().execute( `SELECT oi.id, oi.product_id, oi.quantity, oi.price, oi.points_price, oi.rongdou_price, oi.spec_combination_id, p.name as product_name, p.image_url, p.description, psc.spec_values as spec_info FROM order_items oi LEFT JOIN products p ON oi.product_id = p.id LEFT JOIN product_spec_combinations psc ON oi.spec_combination_id = psc.id WHERE oi.order_id = ?`, [order.id] ); // 处理规格信息 for (const item of orderItems) { if (item.spec_info) { try { item.spec_info = JSON.parse(item.spec_info); } catch (e) { item.spec_info = null; } } } // 处理地址信息 console.log(order.address,'order.address'); if (order.address) { try { order.address = order.address; } catch (e) { order.address = null; } } order.items = orderItems; } res.json({ success: true, data: { orders, pagination: { page: pageNum, limit: limitNum, total, pages: Math.ceil(total / limitNum) } } }); router.post('/confirm', auth, async (req, res) => { const connection = await getDB().getConnection(); try { await connection.beginTransaction(); const { pre_order_id, address } = req.body; const userId = req.user.id; // 验证必填字段 if (!pre_order_id || !address) { return res.status(400).json({ success: false, message: '预订单ID和收货地址为必填项' }); } const { recipient_name, phone, province, city, district, detail_address } = address; if (!recipient_name || !phone || !province || !city || !district || !detail_address) { return res.status(400).json({ success: false, message: '收货地址信息不完整' }); } // 获取预订单信息 const [preOrders] = await connection.execute( `SELECT id, order_no, user_id, total_amount, total_points, total_rongdou, status FROM orders WHERE id = ? AND user_id = ? AND status = 'pre_order'`, [pre_order_id, userId] ); if (preOrders.length === 0) { await connection.rollback(); return res.status(404).json({ success: false, message: '预订单不存在或已处理' }); } const preOrder = preOrders[0]; // 获取用户当前积分和融豆 const [users] = await connection.execute( 'SELECT points, rongdou FROM users WHERE id = ?', [userId] ); if (users.length === 0) { await connection.rollback(); return res.status(404).json({ success: false, message: '用户不存在' }); } const user = users[0]; // 检查积分和融豆是否足够 if (preOrder.total_points > 0 && user.points < preOrder.total_points) { await connection.rollback(); return res.status(400).json({ success: false, message: '积分不足' }); } if (preOrder.total_rongdou > 0 && user.rongdou < preOrder.total_rongdou) { await connection.rollback(); return res.status(400).json({ success: false, message: '融豆不足' }); } // 扣除积分 if (preOrder.total_points > 0) { await connection.execute( 'UPDATE users SET points = points - ? WHERE id = ?', [preOrder.total_points, userId] ); // 记录积分变动历史 await connection.execute( `INSERT INTO points_history (user_id, type, amount, description, order_id) VALUES (?, 'spend', ?, ?, ?)`, [userId, preOrder.total_points, `订单消费 - ${preOrder.order_no}`, pre_order_id] ); } // 扣除融豆 if (preOrder.total_rongdou > 0) { await connection.execute( 'UPDATE users SET rongdou = rongdou - ? WHERE id = ?', [preOrder.total_rongdou, userId] ); // 记录融豆变动历史 await connection.execute( `INSERT INTO rongdou_history (user_id, type, amount, description, order_id) VALUES (?, 'spend', ?, ?, ?)`, [userId, preOrder.total_rongdou, `订单消费 - ${preOrder.order_no}`, pre_order_id] ); } // 更新订单状态和收货地址 const addressStr = JSON.stringify({ recipient_name, phone, province, city, district, detail_address }); await connection.execute( `UPDATE orders SET status = 'pending', address = ?, updated_at = NOW() WHERE id = ?`, [addressStr, pre_order_id] ); await connection.commit(); res.json({ success: true, message: '订单确认成功', data: { order_id: pre_order_id, order_no: preOrder.order_no } }); } catch (error) { await connection.rollback(); console.error('确认下单失败:', error); res.status(500).json({ success: false, message: '确认下单失败' }); } finally { connection.release(); } }); router.get('/pre-order/:id', auth, async (req, res) => { try { const preOrderId = req.params.id; const userId = req.user.id; // 获取预订单基本信息 const [orders] = await getDB().execute( `SELECT id, order_no, user_id, total_amount, total_points, total_rongdou, status, created_at FROM orders WHERE id = ? AND user_id = ? AND status = 'pre_order'`, [preOrderId, userId] ); if (orders.length === 0) { return res.status(404).json({ success: false, message: '预订单不存在' }); } const order = orders[0]; // 获取预订单商品详情 const [orderItems] = await getDB().execute( `SELECT oi.id, oi.product_id, oi.quantity, oi.price, oi.points_price, oi.rongdou_price, oi.spec_combination_id, p.name as product_name, p.image_url, p.description, psc.spec_values as spec_info FROM order_items oi LEFT JOIN products p ON oi.product_id = p.id LEFT JOIN product_spec_combinations psc ON oi.spec_combination_id = psc.id WHERE oi.order_id = ?`, [preOrderId] ); // 处理规格信息 for (const item of orderItems) { if (item.spec_info) { try { item.spec_info = JSON.parse(item.spec_info); } catch (e) { item.spec_info = null; } } } res.json({ success: true, data: { ...order, items: orderItems } }); } catch (error) { console.error('获取预订单详情失败:', error); res.status(500).json({ success: false, message: '获取预订单详情失败' }); } }); } 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: '订单不存在' }); } const order = orders[0]; // 获取订单商品详情 const [orderItems] = await getDB().execute( `SELECT oi.id, oi.product_id, oi.quantity, oi.price, oi.points_price, oi.rongdou_price, oi.spec_combination_id, p.name as product_name, p.image_url, p.description, psc.spec_values as spec_info FROM order_items oi LEFT JOIN products p ON oi.product_id = p.id LEFT JOIN product_spec_combinations psc ON oi.spec_combination_id = psc.id WHERE oi.order_id = ?`, [order.id] ); // 处理规格信息 for (const item of orderItems) { if (item.spec_info) { try { item.spec_info = JSON.parse(item.spec_info); } catch (e) { item.spec_info = null; } } } // 处理地址信息 // console.log(order.address,'order.address'); if (order.address) { try { order.address = order.address; } catch (e) { order.address = null; } } order.items = orderItems; res.json({ success: true, data: { order } }); } catch (error) { console.error('获取订单详情失败:', error); res.status(500).json({ success: false, message: '获取订单详情失败' }); } }); // 创建预订单 router.post('/create-from-cart', auth, async (req, res) => { const db = getDB(); await db.query('START TRANSACTION'); try { const { cart_item_ids } = req.body; const user_id = req.user.id; // 验证必填字段 if (!cart_item_ids || !Array.isArray(cart_item_ids) || cart_item_ids.length === 0) { await db.query('ROLLBACK'); return res.status(400).json({ success: false, message: '请选择要购买的商品' }); } // 获取购物车商品信息和支付方式 const placeholders = cart_item_ids.map(() => '?').join(','); const cartQuery = ` SELECT c.id, c.product_id, c.quantity, c.specification_id, p.name, p.price, p.points_price, p.rongdou_price, p.stock, p.status, p.payment_methods, psc.price_adjustment, psc.points_adjustment, psc.rongdou_adjustment, psc.stock as spec_stock FROM cart_items c LEFT JOIN products p ON c.product_id = p.id LEFT JOIN product_spec_combinations psc ON c.specification_id = psc.id WHERE c.id IN (${placeholders}) AND c.user_id = ? `; const [cartItems] = await db.execute(cartQuery, [...cart_item_ids, user_id]); if (cartItems.length === 0) { await db.query('ROLLBACK'); return res.status(400).json({ success: false, message: '购物车商品不存在' }); } for (const item of cartItems) { const [stock] = await getDB().execute( `SELECT stock FROM product_spec_combinations WHERE id = ?`, [item.specification_id] ); if (stock[0].stock < item.quantity) { await db.query('ROLLBACK'); return res.status(400).json({ success: false, message: `商品 ${item.name} 库存不足` }); } await db.execute( `UPDATE product_spec_combinations SET stock = stock - ? WHERE id = ?`, [item.quantity, item.specification_id] ); } // 验证商品状态和库存,计算总价和支付方式 let totalAmount = 0; let totalPoints = 0; let totalRongdou = 0; let allPaymentMethods = []; for (const item of cartItems) { if (item.status !== 'active') { await db.query('ROLLBACK'); return res.status(400).json({ success: false, message: `商品 ${item.name} 已下架` }); } const availableStock = item.specification_id ? item.spec_stock : item.stock; if (availableStock < item.quantity) { await db.query('ROLLBACK'); return res.status(400).json({ success: false, message: `商品 ${item.name} 库存不足` }); } // 解析商品支付方式 let productPaymentMethods = ['rongdou']; // 默认支付方式 if (item.payment_methods) { try { productPaymentMethods = JSON.parse(item.payment_methods); } catch (e) { console.error('解析商品支付方式失败:', e); } } console.log(productPaymentMethods,'productPaymentMethods'); allPaymentMethods = allPaymentMethods.concat(productPaymentMethods); const finalPrice = item.price + (item.price_adjustment || 0); const finalRongdouPrice = item.rongdou_price + (item.rongdou_adjustment || 0); totalAmount += finalPrice * item.quantity; // 根据支付方式计算积分和融豆需求 const hasPoints = productPaymentMethods.includes('points') || productPaymentMethods.includes('points_rongdou'); const hasRongdou = productPaymentMethods.includes('rongdou') || productPaymentMethods.includes('points_rongdou'); if (hasPoints && !hasRongdou) { // 仅积分支付:按10000积分=1融豆计算 totalPoints += finalRongdouPrice * item.quantity * 10000; totalRongdou += finalRongdouPrice * item.quantity; } else if (!hasPoints && hasRongdou) { // 仅融豆支付 totalRongdou += finalRongdouPrice * item.quantity; } else { // 组合支付或默认:记录融豆价格,前端可选择支付方式 totalRongdou += finalRongdouPrice * item.quantity; } } // 去重支付方式 const uniquePaymentMethods = [...new Set(allPaymentMethods)]; console.log('订单支付方式:', uniquePaymentMethods); // 生成预订单号 const orderNumber = 'PRE' + Date.now() + Math.random().toString(36).substr(2, 5).toUpperCase(); // 创建预订单(状态为pre_order) const [orderResult] = await db.execute( `INSERT INTO orders (order_no, user_id, total_amount, total_points, total_rongdou, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, 'pre_order', NOW(), NOW())`, [orderNumber, user_id, totalAmount, totalPoints, totalRongdou] ); const preOrderId = orderResult.insertId; // 创建预订单项 for (const item of cartItems) { const finalPrice = item.price + (item.price_adjustment || 0); const finalPointsPrice = item.points_price + (item.points_adjustment || 0); const finalRongdouPrice = item.rongdou_price + (item.rongdou_adjustment || 0); await db.execute( `INSERT INTO order_items (order_id, product_id, spec_combination_id, quantity, price, points, points_price, rongdou, rongdou_price, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())`, [preOrderId, item.product_id, item.specification_id, item.quantity, finalPrice, finalPointsPrice * item.quantity, finalPointsPrice, finalRongdouPrice * item.quantity, finalRongdouPrice] ); } // 删除购物车中的商品 await db.execute( `DELETE FROM cart_items WHERE id IN (${placeholders})`, cart_item_ids ); await db.query('COMMIT'); res.status(201).json({ success: true, message: '预订单创建成功', data: { preOrderId, orderNumber, totalAmount, totalPoints, totalRongdou, paymentMethods: uniquePaymentMethods } }); } catch (error) { await db.query('ROLLBACK'); console.error('创建预订单失败:', error); res.status(500).json({ success: false, message: '创建预订单失败' }); } }); /** * @swagger * /api/orders/{id}/cancel: * put: * summary: 用户取消订单 * tags: [Orders] * security: * - bearerAuth: [] * parameters: * - in: path * name: id * required: true * schema: * type: integer * description: 订单ID * responses: * 200: * description: 订单取消成功 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * message: * type: string * 400: * description: 只能取消待处理的订单 * 401: * description: 未授权 * 404: * description: 订单不存在 * 500: * description: 服务器错误 */ 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' && order.status !== 'pre_order') { 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] ); console.log(12345,order.id); const [orderDetails] = await db.execute( 'SELECT * FROM order_items WHERE order_id = ?', [order.id] ); console.log(12345,orderDetails); // 返还库存 await db.execute( 'UPDATE product_spec_combinations SET stock = stock + ? WHERE id = ?', [orderDetails[0].quantity, orderDetails[0].spec_combination_id] ); // 更新订单状态 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: '取消订单失败' }); } }); /** * @swagger * /api/orders/{id}/confirm: * put: * summary: 确认收货 * tags: [Orders] * security: * - bearerAuth: [] * parameters: * - in: path * name: id * required: true * schema: * type: integer * description: 订单ID * responses: * 200: * description: 确认收货成功 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * message: * type: string * 400: * description: 只能确认已发货的订单 * 401: * description: 未授权 * 404: * description: 订单不存在 * 500: * description: 服务器错误 */ 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: '确认收货失败' }); } }); /** * @swagger * /api/orders/{id}/status: * put: * summary: 更新订单状态(管理员) * tags: [Orders] * security: * - bearerAuth: [] * parameters: * - in: path * name: id * required: true * schema: * type: integer * description: 订单ID * requestBody: * required: true * content: * application/json: * schema: * type: object * properties: * status: * type: string * enum: [pending, shipped, completed, cancelled] * description: 订单状态 * required: * - status * responses: * 200: * description: 订单状态更新成功 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * message: * type: string * 400: * description: 无效的订单状态 * 401: * description: 未授权 * 403: * description: 无管理员权限 * 404: * description: 订单不存在 * 500: * description: 服务器错误 */ 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, total_rongdou, 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' && order.status !== 'pre_order') { // 退还用户积分 if (order.total_points > 0) { 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.total_points] ); } // 退还用户融豆 if (order.total_rongdou > 0) { await db.execute( 'UPDATE users SET balance = balance - ? WHERE id = ?', [order.total_rongdou, order.user_id] ); // 记录融豆历史 await db.execute( `INSERT INTO rongdou_history (user_id, amount, type, description, created_at) VALUES (?, ?, 'earn', '订单取消退还融豆', NOW())`, [order.user_id, order.total_rongdou] ); } } // 更新订单状态 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: '更新订单状态失败' }); } }); /** * @swagger * /api/orders/pending-payment/{id}: * get: * summary: 获取待支付预订单详情 * tags: [Orders] * security: * - bearerAuth: [] * parameters: * - in: path * name: id * required: true * schema: * type: integer * description: 预订单ID * responses: * 200: * description: 获取预订单详情成功 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * data: * type: object * properties: * id: * type: integer * order_no: * type: string * total_amount: * type: integer * total_points: * type: integer * total_rongdou: * type: integer * status: * type: string * created_at: * type: string * items: * type: array * items: * type: object * properties: * id: * type: integer * product_id: * type: integer * product_name: * type: string * quantity: * type: integer * price: * type: integer * points_price: * type: integer * rongdou_price: * type: integer * spec_info: * type: object * 401: * description: 未授权 * 404: * description: 预订单不存在 * 500: * description: 服务器错误 */ router.get('/pending-payment/:id', auth, async (req, res) => { try { const preOrderId = req.params.id; const userId = req.user.id; // 获取预订单基本信息 const [orders] = await getDB().execute( `SELECT id, order_no, user_id, total_amount, total_points, total_rongdou, status, created_at FROM orders WHERE id = ? AND user_id = ? AND status = 'pre_order'`, [preOrderId, userId] ); if (orders.length === 0) { return res.status(404).json({ success: false, message: '预订单不存在' }); } const order = orders[0]; // 获取预订单商品详情 const [orderItems] = await getDB().execute( `SELECT oi.id, oi.product_id, oi.quantity, oi.price, oi.points_price, oi.rongdou_price, oi.spec_combination_id, p.name as product_name, p.image_url, p.description, p.payment_methods, psc.spec_values as spec_info FROM order_items oi LEFT JOIN products p ON oi.product_id = p.id LEFT JOIN product_spec_combinations psc ON oi.spec_combination_id = psc.id WHERE oi.order_id = ?`, [preOrderId] ); // 处理规格信息 for (const item of orderItems) { if (item.spec_info) { try { item.spec_info = JSON.parse(item.spec_info); } catch (e) { item.spec_info = null; } } } res.json({ success: true, data: { ...order, items: orderItems.map(item => ({ ...item, payment_methods: JSON.parse(item.payment_methods) })) } }); } catch (error) { console.error('获取预订单详情失败:', error); res.status(500).json({ success: false, message: '获取预订单详情失败' }); } }); /** * @swagger * /api/orders/confirm-payment: * post: * summary: 确认支付订单 * description: | * 根据商品支付方式确认订单支付: * - 仅积分支付:按10000积分=1融豆的比例扣除积分 * - 仅融豆支付:直接扣除融豆 * - 组合支付:优先扣除积分(按10000:1转换),不足部分扣除融豆 * tags: [Orders] * security: * - bearerAuth: [] * requestBody: * required: true * content: * application/json: * schema: * type: object * required: * - order_id * - address_id * properties: * order_id: * type: integer * description: 订单ID * example: 123 * address_id: * type: integer * description: 收货地址ID * example: 456 * responses: * 200: * description: 确认支付成功 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * example: true * message: * type: string * example: "订单支付成功" * data: * type: object * properties: * order_id: * type: integer * example: 123 * order_no: * type: string * example: "ORD20240101123456" * 400: * description: 请求参数错误或余额不足 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * example: false * message: * type: string * enum: ["订单ID和收货地址ID为必填项", "积分不足", "融豆不足", "积分和融豆余额不足", "商品支付方式配置错误"] * 401: * description: 未授权 * 404: * description: 订单或地址不存在 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * example: false * message: * type: string * enum: ["订单不存在或已处理", "收货地址不存在", "用户不存在"] * 500: * description: 服务器错误 */ router.post('/confirm-payment', auth, async (req, res) => { const connection = await getDB().getConnection(); try { await connection.beginTransaction(); const { orderId: order_id, addressId: address_id, couponRecordId, paymentMethod } = req.body; const userId = req.user.id; // 验证必填字段 if (!order_id || !address_id) { return res.status(400).json({ success: false, message: '订单ID和收货地址ID为必填项' }); } // 获取订单信息和商品支付方式 const [orders] = await connection.execute( `SELECT o.id, o.order_no, o.user_id, o.total_amount, o.total_points, o.total_rongdou, o.status, GROUP_CONCAT(DISTINCT p.payment_methods) as payment_methods_list FROM orders o JOIN order_items oi ON o.id = oi.order_id JOIN products p ON oi.product_id = p.id WHERE o.id = ? AND o.user_id = ? AND o.status = 'pre_order' GROUP BY o.id`, [order_id, userId] ); if (orders.length === 0) { await connection.rollback(); return res.status(404).json({ success: false, message: '订单不存在或已处理' }); } const order = orders[0]; // 解析支付方式 // let allPaymentMethods = []; // // console.log(typeof order.payment_methods_list); // if (order.payment_methods_list) { // try { // // 数据库中存储的是序列化的JSON字符串,直接解析 // allPaymentMethods = JSON.parse(JSON.parse(order.payment_methods_list)); // } catch (e) { // console.error('解析支付方式失败:', e, 'raw data:', order.payment_methods_list); // allPaymentMethods = []; // } // } // // 去重支付方式 // allPaymentMethods = [...new Set(allPaymentMethods)]; // // 判断支付方式类型 // const hasPoints = allPaymentMethods.includes('points') || allPaymentMethods.includes('points_rongdou'); // const hasRongdou = allPaymentMethods.includes('rongdou') || allPaymentMethods.includes('points_rongdou'); // const isComboPayment = allPaymentMethods.includes('points_rongdou'); // console.log('订单支付方式:', allPaymentMethods, { hasPoints, hasRongdou, isComboPayment }); // 获取收货地址信息 const [addresses] = await connection.execute( 'SELECT id, receiver_name, receiver_phone, province, city, district, detailed_address as detail_address FROM user_addresses WHERE id = ? AND user_id = ?', [address_id, userId] ); if (addresses.length === 0) { await connection.rollback(); return res.status(404).json({ success: false, message: '收货地址不存在' }); } const address = addresses[0]; // 获取用户当前积分和融豆 const [users] = await connection.execute( 'SELECT points, balance FROM users WHERE id = ?', [userId] ); if (users.length === 0) { await connection.rollback(); return res.status(404).json({ success: false, message: '用户不存在' }); } const user = users[0]; if (user.balance > 0) { return res.status(400).json({ success: false, message: '融豆不足' }); } user.balance = Math.abs(user.balance); // 开始扣钱 switch (paymentMethod) { case 'points': // 积分支付逻辑 if (user.points < order.total_points) { await connection.rollback(); return res.status(400).json({ success: false, message: '积分不足' }); } await connection.execute( 'UPDATE users SET points = points - ? WHERE id = ?', [order.total_points, userId] ); // 记录积分变动历史 await connection.execute( `INSERT INTO points_history (user_id, type, amount, description, order_id) VALUES (?, 'spend', ?, ?, ?)`, [userId, order.total_points, `订单支付 - ${order.order_no}`, order_id] ); // 供应商分佣 // await connection.execute( // 'UPDATE users SET points = points + ? WHERE id = ?', // [order.total_points * 0.1, order.shop_id] // ); // console.log(123,order) break; case 'beans': // 融豆支付逻辑 if (user.balance < order.total_rongdou) { await connection.rollback(); return res.status(400).json({ success: false, message: '融豆不足' }); } await connection.execute( 'UPDATE users SET balance = balance + ? WHERE id = ?', [order.total_rongdou, userId] ); // 记录融豆变动历史 await connection.execute( `INSERT INTO points_history (user_id, type, amount, description, order_id) VALUES (?, 'spend', ?, ?, ?)`, [userId, order.total_rongdou, `订单支付 - ${order.order_no}`, order_id] ); break; case 'mixed': // 积分和融豆组合支付逻辑 if(user.points < order.total_points) { const needPoints = (user.points/10000).floor(0) * 10000; const needBeans = order.total_rongdou - needPoints/10000; if(user.balance < needBeans) { await connection.rollback(); return res.status(400).json({ success: false, message: '融豆不足' }); } await connection.execute( 'UPDATE users SET points = points - ? WHERE id = ?', [needPoints, userId] ); // 记录积分变动历史 await connection.execute( `INSERT INTO points_history (user_id, type, amount, description, order_id) VALUES (?, 'spend', ?, ?, ?)`, [userId, needPoints, `订单支付 - ${order.order_no}`, order_id] ); await connection.execute( 'UPDATE users SET balance = balance + ? WHERE id = ?', [needBeans, userId] ); // 记录融豆变动历史 await connection.execute( `INSERT INTO points_history (user_id, type, amount, description, order_id) VALUES (?, 'spend', ?, ?, ?)`, [userId, needBeans, `订单支付 - ${order.order_no}`, order_id] ); } else { await connection.execute( 'UPDATE users SET points = points - ? WHERE id = ?', [order.total_points, userId] ); // 记录积分变动历史 await connection.execute( `INSERT INTO points_history (user_id, type, amount, description, order_id) VALUES (?, 'spend', ?, ?, ?)`, [userId, order.total_points, `订单支付 - ${order.order_no}`, order_id] ); } break; default: await connection.rollback(); return res.status(400).json({ success: false, message: '支付方式配置错误' }); } // // 根据支付方式处理扣费逻辑 // let totalRongdouNeeded = order.total_rongdou; // 需要的融豆总数 // let pointsToDeduct = 0; // 需要扣除的积分 // let rongdouToDeduct = 0; // 需要扣除的融豆 // if (!hasRongdou && !hasPoints) { // await connection.rollback(); // return res.status(400).json({ success: false, message: '商品支付方式配置错误' }); // } // if (hasPoints && !hasRongdou) { // // 只支持积分支付,按10000积分=1融豆转换 // const pointsNeeded = totalRongdouNeeded * 10000; // if (user.points < pointsNeeded) { // await connection.rollback(); // return res.status(400).json({ success: false, message: '积分不足' }); // } // pointsToDeduct = pointsNeeded; // rongdouToDeduct = 0; // } else if (!hasPoints && hasRongdou) { // // 只支持融豆支付 // if (user.balance < totalRongdouNeeded) { // await connection.rollback(); // return res.status(400).json({ success: false, message: '融豆不足' }); // } // pointsToDeduct = 0; // rongdouToDeduct = totalRongdouNeeded; // } else if (hasPoints && hasRongdou) { // // 组合支付:先扣积分,不足部分用融豆 // const availablePointsInRongdou = Math.floor(user.points / 10000); // 积分可转换的融豆数 // if (availablePointsInRongdou >= totalRongdouNeeded) { // // 积分足够支付全部 // pointsToDeduct = totalRongdouNeeded * 10000; // rongdouToDeduct = 0; // } else { // // 积分不够,需要组合支付 // pointsToDeduct = availablePointsInRongdou * 10000; // rongdouToDeduct = totalRongdouNeeded - availablePointsInRongdou; // if (user.balance < rongdouToDeduct) { // await connection.rollback(); // return res.status(400).json({ success: false, message: '积分和融豆余额不足' }); // } // } // } // console.log('扣费计算:', { totalRongdouNeeded, pointsToDeduct, rongdouToDeduct, userPoints: user.points, userBalance: user.balance }); // 扣除积分 // if (pointsToDeduct > 0) { // await connection.execute( // 'UPDATE users SET points = points - ? WHERE id = ?', // [pointsToDeduct, userId] // ); // // 记录积分变动历史 // await connection.execute( // `INSERT INTO points_history (user_id, type, amount, description, order_id) // VALUES (?, 'spend', ?, ?, ?)`, // [userId, pointsToDeduct, `订单支付 - ${order.order_no}`, order_id] // ); // } // // 扣除融豆 // if (rongdouToDeduct > 0) { // await connection.execute( // 'UPDATE users SET balance = balance + ? WHERE id = ?', // [rongdouToDeduct, userId] // ); // // 记录融豆变动历史 // await connection.execute( // `INSERT INTO rongdou_history (user_id, type, amount, description, order_id) // VALUES (?, 'spend', ?, ?, ?)`, // [userId, rongdouToDeduct, `订单支付 - ${order.order_no}`, order_id] // ); // } // 更新优惠券记录 if (couponRecordId) { await connection.execute( 'UPDATE coupon_use SET use_time = NOW(), order_id = ? WHERE id = ?', [order_id, couponRecordId] ); } // 更新订单状态和收货地址 const addressStr = JSON.stringify({ recipient_name: address.receiver_name, phone: address.receiver_phone, province: address.province, city: address.city, district: address.district, detail_address: address.detail_address }); await connection.execute( `UPDATE orders SET status = 'pending', address = ?, updated_at = NOW() WHERE id = ?`, [addressStr, order_id] ); // 减组合库存 // await connection.execute( // 'UPDATE product_spec_combinations SET stock = stock - 1 WHERE id = ?', // [order.product_combination_id] // ); await connection.commit(); res.json({ success: true, message: '订单支付成功', data: { order_id: order_id, order_no: order.order_no } }); } catch (error) { await connection.rollback(); console.error('确认支付失败:', error); res.status(500).json({ success: false, message: '确认支付失败' }); } finally { connection.release(); } }); /** * @swagger * /api/orders/stats: * get: * summary: 获取订单统计信息(管理员权限) * tags: [Orders] * security: * - bearerAuth: [] * responses: * 200: * description: 成功获取订单统计信息 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * data: * type: object * properties: * totalOrders: * type: integer * description: 总订单数 * pendingOrders: * type: integer * description: 待发货订单数 * completedOrders: * type: integer * description: 已完成订单数 * monthOrders: * type: integer * description: 本月新增订单数 * monthGrowthRate: * type: number * description: 月增长率 * totalPointsConsumed: * type: number * description: 总积分消费 * 401: * description: 未授权 * 403: * description: 无管理员权限 * 500: * description: 服务器错误 */ 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;