Compare commits
	
		
			16 Commits
		
	
	
		
			5d50c42e3e
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| bca4b412c9 | |||
| 2217ccf30c | |||
| be8b182e82 | |||
| 27a3f564ed | |||
| 4774262178 | |||
| e535e8a9c5 | |||
| f9a757046c | |||
| 69a445cebd | |||
| 486d3179d3 | |||
| b512c6479d | |||
| 3abcd5e46a | |||
| 2a16a7fd97 | |||
| 62630ef750 | |||
| 69d2719eeb | |||
| 077686a0fc | |||
| 305ac5dde6 | 
							
								
								
									
										132
									
								
								routes/cart.js
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								routes/cart.js
									
									
									
									
									
								
							| @@ -473,71 +473,6 @@ router.put('/:id', auth, async (req, res) => { | ||||
|   } | ||||
| }); | ||||
|  | ||||
| /** | ||||
|  * @swagger | ||||
|  * /api/cart/{id}: | ||||
|  *   delete: | ||||
|  *     summary: 删除购物车商品 | ||||
|  *     tags: [Cart] | ||||
|  *     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 | ||||
|  *       401: | ||||
|  *         description: 未授权 | ||||
|  *       404: | ||||
|  *         description: 购物车项不存在 | ||||
|  *       500: | ||||
|  *         description: 服务器错误 | ||||
|  */ | ||||
| router.delete('/:id', auth, async (req, res) => { | ||||
|   try { | ||||
|     const cartItemId = req.params.id; | ||||
|     const userId = req.user.id; | ||||
|      | ||||
|     // 检查购物车项是否存在且属于当前用户 | ||||
|     const [cartItems] = await getDB().execute( | ||||
|       'SELECT id FROM cart_items WHERE id = ? AND user_id = ?', | ||||
|       [cartItemId, userId] | ||||
|     ); | ||||
|      | ||||
|     if (cartItems.length === 0) { | ||||
|       return res.status(404).json({ success: false, message: '购物车项不存在' }); | ||||
|     } | ||||
|      | ||||
|     // 删除购物车项 | ||||
|     await getDB().execute( | ||||
|       'DELETE FROM cart_items WHERE id = ?', | ||||
|       [cartItemId] | ||||
|     ); | ||||
|      | ||||
|     res.json({ | ||||
|       success: true, | ||||
|       message: '删除购物车商品成功' | ||||
|     }); | ||||
|   } catch (error) { | ||||
|     console.error('删除购物车商品失败:', error); | ||||
|     res.status(500).json({ success: false, message: '删除购物车商品失败' }); | ||||
|   } | ||||
| }); | ||||
|  | ||||
| /** | ||||
|  * @swagger | ||||
|  * /api/cart/batch: | ||||
| @@ -654,6 +589,7 @@ router.delete('/clear', auth, async (req, res) => { | ||||
|       success: true, | ||||
|       message: '清空购物车成功' | ||||
|     }); | ||||
|     console.log(11111111111111) | ||||
|   } catch (error) { | ||||
|     console.error('清空购物车失败:', error); | ||||
|     res.status(500).json({ success: false, message: '清空购物车失败' }); | ||||
| @@ -932,4 +868,70 @@ router.post('/checkout', auth, async (req, res) => { | ||||
|   } | ||||
| }); | ||||
|  | ||||
| /** | ||||
|  * @swagger | ||||
|  * /api/cart/{id}: | ||||
|  *   delete: | ||||
|  *     summary: 删除购物车商品 | ||||
|  *     tags: [Cart] | ||||
|  *     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 | ||||
|  *       401: | ||||
|  *         description: 未授权 | ||||
|  *       404: | ||||
|  *         description: 购物车项不存在 | ||||
|  *       500: | ||||
|  *         description: 服务器错误 | ||||
|  */ | ||||
| router.delete('/:id', auth, async (req, res) => { | ||||
|   try { | ||||
|     console.log(111111111) | ||||
|     const cartItemId = req.params.id; | ||||
|     const userId = req.user.id; | ||||
|      | ||||
|     // 检查购物车项是否存在且属于当前用户 | ||||
|     const [cartItems] = await getDB().execute( | ||||
|       'SELECT id FROM cart_items WHERE id = ? AND user_id = ?', | ||||
|       [cartItemId, userId] | ||||
|     ); | ||||
|      | ||||
|     if (cartItems.length === 0) { | ||||
|       return res.status(404).json({ success: false, message: '购物车项不存在' }); | ||||
|     } | ||||
|      | ||||
|     // 删除购物车项 | ||||
|     await getDB().execute( | ||||
|       'DELETE FROM cart_items WHERE id = ?', | ||||
|       [cartItemId] | ||||
|     ); | ||||
|      | ||||
|     res.json({ | ||||
|       success: true, | ||||
|       message: '删除购物车商品成功' | ||||
|     }); | ||||
|   } catch (error) { | ||||
|     console.error('删除购物车商品失败:', error); | ||||
|     res.status(500).json({ success: false, message: '删除购物车商品失败' }); | ||||
|   } | ||||
| }); | ||||
|  | ||||
| module.exports = router; | ||||
							
								
								
									
										68
									
								
								routes/category.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								routes/category.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| const express = require('express'); | ||||
| const { getDB } = require('../database'); | ||||
| const router = express.Router(); | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * @swagger | ||||
|  * /api/category: | ||||
|  *   get: | ||||
|  *     summary: 获取一级分类及其二级分类 | ||||
|  *     description: 返回所有一级分类,每个一级分类包含其下的二级分类 | ||||
|  *     tags: [category] | ||||
|  *     responses: | ||||
|  *       200: | ||||
|  *         description: 成功返回一级分类及其二级分类 | ||||
|  *         content: | ||||
|  *           application/json: | ||||
|  *             schema: | ||||
|  *               type: object | ||||
|  *               properties: | ||||
|  *                 success: | ||||
|  *                   type: boolean | ||||
|  *                   example: true | ||||
|  *                 data: | ||||
|  *                   type: array | ||||
|  *                   items: | ||||
|  *                     type: object | ||||
|  *                     properties: | ||||
|  *                       name: | ||||
|  *                         type: string | ||||
|  *                         example: "一级分类1" | ||||
|  *                       relative: | ||||
|  *                         type: array | ||||
|  *                         items: | ||||
|  *                           type: object | ||||
|  *                           properties: | ||||
|  *                             name: | ||||
|  *                               type: string | ||||
|  *                               example: "二级分类1-1" | ||||
|  *                             img: | ||||
|  *                               type: string | ||||
|  *                               example: "https://example.com/image.jpg" | ||||
|  */ | ||||
| router.get('/', async (req, res) => { | ||||
|   try { | ||||
|     const db = await getDB(); | ||||
|     const [firstCategory] = await db.query('SELECT * FROM category WHERE level = 1'); | ||||
|  | ||||
|  | ||||
|     for (const category of firstCategory) { | ||||
|       const [secondCategories] = await db.query('SELECT * FROM category WHERE parent_id = ?', [category.id]); | ||||
|       category.relative = secondCategories; | ||||
|     } | ||||
|  | ||||
|     res.json({ success: true, data: firstCategory.map(category => ({ | ||||
|       name: category.category_name, | ||||
|       relative: category.relative.map(secondCategory => ({ | ||||
|         name: secondCategory.category_name, | ||||
|         img: secondCategory.image, | ||||
|       })) | ||||
|     }))}); | ||||
|   } catch (error) { | ||||
|     console.error('Error fetching categories:', error); | ||||
|     res.status(500).json({ success: false, error: 'Internal server error' }); | ||||
|   } | ||||
| }); | ||||
|  | ||||
| module.exports = router; | ||||
							
								
								
									
										248
									
								
								routes/coupon.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								routes/coupon.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,248 @@ | ||||
| const express = require('express'); | ||||
| const router = express.Router(); | ||||
| const { getDB } = require('../database'); | ||||
|  | ||||
| /** | ||||
|  * @swagger | ||||
|  * /api/coupon: | ||||
|  *   get: | ||||
|  *     summary: 获取用户优惠券 | ||||
|  *     description: 返回用户所有优惠券,包含是否已领取状态 | ||||
|  *     tags: [coupon] | ||||
|  *     parameters: | ||||
|  *       - in: query | ||||
|  *         name: user_id | ||||
|  *         schema: | ||||
|  *           type: string | ||||
|  *         required: true | ||||
|  *         description: 用户ID | ||||
|  *     responses: | ||||
|  *       200: | ||||
|  *         description: 成功返回用户优惠券 | ||||
|  *         content: | ||||
|  *           application/json: | ||||
|  *             schema: | ||||
|  *               type: object | ||||
|  *               properties: | ||||
|  *                 success: | ||||
|  *                   type: boolean | ||||
|  *                   example: true | ||||
|  *                 data: | ||||
|  *                   type: array | ||||
|  *                   items: | ||||
|  *                     type: object | ||||
|  *                     properties: | ||||
|  *                       id: | ||||
|  *                         type: string | ||||
|  *                         example: "1" | ||||
|  *                       name: | ||||
|  *                         type: string | ||||
|  *                         example: "满减优惠券" | ||||
|  *                       discount: | ||||
|  *                         type: number | ||||
|  *                         example: 100 | ||||
|  *                       remain: | ||||
|  *                         type: number | ||||
|  *                         example: 10 | ||||
|  *                       got: | ||||
|  *                         type: boolean | ||||
|  *                         example: false | ||||
|  *                         description: 是否已领取 | ||||
|  *  | ||||
|  *       400: | ||||
|  *         description: 缺少用户ID参数 | ||||
|  *       500: | ||||
|  *         description: 服务器内部错误 | ||||
|  */ | ||||
| router.get('/', async (req, res) => { | ||||
|   try { | ||||
|     const useId = req.query.user_id; | ||||
|     const db = getDB(); | ||||
|     const query = ` | ||||
|       SELECT * FROM coupon_products | ||||
|     ` | ||||
|     const [coupon] = await db.query(query); | ||||
|  | ||||
|     if (coupon.length === 0) { | ||||
|       res.json({ message: '暂无优惠券' }); | ||||
|     } | ||||
|  | ||||
|     for(const item of coupon) { | ||||
|       const query = ` | ||||
|         SELECT * FROM coupon_use WHERE user_id = ? AND coupon_id = ? | ||||
|       ` | ||||
|       const [couponExist] = await db.query(query, [useId, item.id]); | ||||
|       if (couponExist.length === 0) { | ||||
|         item.got = false; | ||||
|       } else { | ||||
|         item.got = true; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     res.json({coupon, success: true}); | ||||
|   } catch (error) { | ||||
|     res.status(500).json({ error: '获取优惠券失败' }); | ||||
|   } | ||||
| }); | ||||
|  | ||||
| /** | ||||
|  * @swagger | ||||
|  * /api/coupon/{id}: | ||||
|  *   get: | ||||
|  *     summary: 用户领取优惠券 | ||||
|  *     description: 用户通过优惠券ID领取优惠券,优惠券数量减一 | ||||
|  *     tags: [coupon] | ||||
|  *     parameters: | ||||
|  *       - in: path | ||||
|  *         name: id | ||||
|  *         schema: | ||||
|  *           type: string | ||||
|  *         required: true | ||||
|  *         description: 用户ID | ||||
|  *       - in: query | ||||
|  *         name: coupon_id | ||||
|  *         schema: | ||||
|  *           type: string | ||||
|  *         required: true | ||||
|  *         description: 优惠券ID | ||||
|  *     responses: | ||||
|  *       200: | ||||
|  *         description: 成功领取优惠券 | ||||
|  *         content: | ||||
|  *           application/json: | ||||
|  *             schema: | ||||
|  *               type: object | ||||
|  *               properties: | ||||
|  *                 success: | ||||
|  *                   type: boolean | ||||
|  *                   example: true | ||||
|  *       400: | ||||
|  *         description: 缺少用户ID或优惠券ID参数 | ||||
|  *       500: | ||||
|  *         description: 服务器内部错误 | ||||
|  */ | ||||
| router.get('/:id', async (req, res) => { | ||||
|   try { | ||||
|     const db = getDB(); | ||||
|  | ||||
|     const userId = req.params.id; | ||||
|     const receiveCouponId = req.query.coupon_id; | ||||
|  | ||||
|     const query = ` | ||||
|       SELECT * FROM coupon_use WHERE user_id = ? AND coupon_id = ? | ||||
|     ` | ||||
|     const [couponExist] = await db.query(query, [userId, receiveCouponId]); | ||||
|  | ||||
|     if (couponExist.length === 0) { | ||||
|  | ||||
|       const insertQuery = ` | ||||
|         INSERT INTO coupon_use (user_id, coupon_id, get_time) VALUES (?, ?, now()) | ||||
|       ` | ||||
|       await db.query(insertQuery, [userId, receiveCouponId]); | ||||
|  | ||||
|       const updateQuery = ` | ||||
|         UPDATE coupon_products SET remain = remain - 1 WHERE id = ? | ||||
|       ` | ||||
|       await db.query(updateQuery, [receiveCouponId]); | ||||
|  | ||||
|     } else { | ||||
|       return res.status(500).json({ error: '已有该优惠券' }); | ||||
|     } | ||||
|  | ||||
|     res.json({success: true}); | ||||
|   } catch (error) { | ||||
|     console.log(error); | ||||
|     res.status(500).json({ error: '获取优惠券失败' }); | ||||
|   } | ||||
| }); | ||||
|  | ||||
| /** | ||||
|  * @swagger | ||||
|  * /api/coupon/user/{id}: | ||||
|  *   get: | ||||
|  *     summary: 获取用户优惠券 | ||||
|  *     description: 返回用户领取的优惠券,包括优惠券信息和商品信息 | ||||
|  *     tags: [coupon] | ||||
|  *     parameters: | ||||
|  *       - in: path | ||||
|  *         name: id | ||||
|  *         schema: | ||||
|  *           type: string | ||||
|  *         required: true | ||||
|  *         description: 用户ID | ||||
|  *     responses: | ||||
|  *       200: | ||||
|  *         description: 成功返回用户优惠券 | ||||
|  *         content: | ||||
|  *           application/json: | ||||
|  *             schema: | ||||
|  *               type: object | ||||
|  *               properties: | ||||
|  *                 success: | ||||
|  *                   type: boolean | ||||
|  *                   example: true | ||||
|  *                 data: | ||||
|  *                   type: array | ||||
|  *                   items: | ||||
|  *                     type: object | ||||
|  *                     properties: | ||||
|  *                       id: | ||||
|  *                         type: string | ||||
|  *                         example: "1" | ||||
|  *                       name: | ||||
|  *                         type: string | ||||
|  *                         example: "满减优惠券" | ||||
|  *                       discount: | ||||
|  *                         type: number | ||||
|  *                         example: 100 | ||||
|  *                       products_id: | ||||
|  *                         type: array | ||||
|  *                         items: | ||||
|  *                           type: string | ||||
|  *                           example: "1" | ||||
|  *                       remain: | ||||
|  *                         type: number | ||||
|  *                         example: 100 | ||||
|  *                       get_time: | ||||
|  *                         type: string | ||||
|  *                         format: date-time | ||||
|  *                         example: "2023-01-01T00:00:00.000Z" | ||||
|  *       400: | ||||
|  *         description: 缺少用户ID参数 | ||||
|  *       500: | ||||
|  *         description: 服务器内部错误 | ||||
|  */ | ||||
| router.get('/user/:id', async (req, res) => { | ||||
|   try { | ||||
|     const db = getDB(); | ||||
|     const userId = req.params.id; | ||||
|  | ||||
|     const query = ` | ||||
|       SELECT * FROM coupon_use WHERE user_id = ? | ||||
|     ` | ||||
|     const [coupon] = await db.query(query, [userId]); | ||||
|  | ||||
|     for (const item of coupon) { | ||||
|       const query = ` | ||||
|         SELECT * FROM coupon_products WHERE id = ? | ||||
|       ` | ||||
|       const [couponInfo] = await db.query(query, [item.coupon_id]); | ||||
|       item.couponInfo = couponInfo[0]; | ||||
|       item.couponInfo.products = []; | ||||
|       for (const product of item.couponInfo.products_id) { | ||||
|         const query = ` | ||||
|           SELECT name FROM products WHERE id = ? | ||||
|         ` | ||||
|         const [productInfo] = await db.query(query, [product]); | ||||
|         item.couponInfo.products.push(productInfo[0]); | ||||
|       } | ||||
|     } | ||||
|      | ||||
|     res.json({success: true, coupon}); | ||||
|   } catch (error) { | ||||
|     console.log(error); | ||||
|     res.status(500).json({ error: '加载优惠券失败' }); | ||||
|   } | ||||
| }) | ||||
|  | ||||
| module.exports = router; | ||||
							
								
								
									
										411
									
								
								routes/orders.js
									
									
									
									
									
								
							
							
						
						
									
										411
									
								
								routes/orders.js
									
									
									
									
									
								
							| @@ -77,7 +77,7 @@ router.get('/', auth, async (req, res) => { | ||||
|       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 | ||||
|         u.username, o.real_rongdou, o.real_points | ||||
|       FROM orders o | ||||
|       LEFT JOIN users u ON o.user_id = u.id | ||||
|       ${whereClause} | ||||
| @@ -114,7 +114,7 @@ router.get('/', auth, async (req, res) => { | ||||
|       } | ||||
|  | ||||
|       // 处理地址信息 | ||||
|       console.log(order.address,'order.address'); | ||||
|       // console.log(order.address,'order.address'); | ||||
|        | ||||
|       if (order.address) { | ||||
|         try { | ||||
| @@ -344,7 +344,8 @@ router.get('/:id', auth, async (req, res) => { | ||||
|     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.status, o.address, o.created_at, o.updated_at, o.total_rongdou, | ||||
|         o.real_rongdou, o.real_points, | ||||
|         u.username, u.phone | ||||
|       FROM orders o | ||||
|       LEFT JOIN users u ON o.user_id = u.id | ||||
| @@ -385,7 +386,7 @@ router.get('/:id', auth, async (req, res) => { | ||||
|     } | ||||
|  | ||||
|     // 处理地址信息 | ||||
|     console.log(order.address,'order.address'); | ||||
|     // console.log(order.address,'order.address'); | ||||
|      | ||||
|     if (order.address) { | ||||
|       try { | ||||
| @@ -442,6 +443,27 @@ router.post('/create-from-cart', auth, async (req, res) => { | ||||
|       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] | ||||
|       ); | ||||
|       await db.execute( | ||||
|         `UPDATE products SET stock = stock - ? WHERE id = ?`, | ||||
|         [item.quantity, item.product_id] | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     // 验证商品状态和库存,计算总价和支付方式 | ||||
|     let totalAmount = 0; | ||||
|     let totalPoints = 0; | ||||
| @@ -479,20 +501,23 @@ router.post('/create-from-cart', auth, async (req, res) => { | ||||
|       totalAmount += finalPrice * item.quantity; | ||||
|        | ||||
|       // 根据支付方式计算积分和融豆需求 | ||||
|       const hasPoints = productPaymentMethods.includes('points') || productPaymentMethods.includes('points_rongdou'); | ||||
|       const hasRongdou = productPaymentMethods.includes('rongdou') || productPaymentMethods.includes('points_rongdou'); | ||||
|       // 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; | ||||
|       } | ||||
|       // 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; | ||||
|       // } | ||||
|       totalPoints += item.points_price * item.quantity; | ||||
|       totalRongdou += item.rongdou_price * item.quantity; | ||||
|       // console.log(1111,item) | ||||
|     } | ||||
|      | ||||
|     // 去重支付方式 | ||||
| @@ -612,9 +637,9 @@ router.put('/:id/cancel', auth, async (req, res) => { | ||||
|  | ||||
|     const order = orders[0]; | ||||
|  | ||||
|     if (order.status !== 'pending') { | ||||
|     if (order.status !== 'pending' && order.status !== 'pre_order') { | ||||
|       await db.query('ROLLBACK'); | ||||
|       return res.status(400).json({ success: false, message: '只能取消待处理的订单' }); | ||||
|       return res.status(400).json({ success: false, message: '只能取消待处理或待支付的订单' }); | ||||
|     } | ||||
|  | ||||
|     // 退还用户积分 | ||||
| @@ -630,6 +655,24 @@ router.put('/:id/cancel', auth, async (req, res) => { | ||||
|       [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 products SET stock = stock + ? WHERE id = ?', | ||||
|       [orderDetails[0].quantity, orderDetails[0].product_id] | ||||
|     ); | ||||
|      | ||||
|  | ||||
|     // 更新订单状态 | ||||
|     await db.execute( | ||||
|       'UPDATE orders SET status = "cancelled", updated_at = NOW() WHERE id = ?', | ||||
| @@ -689,7 +732,7 @@ router.put('/:id/confirm', auth, async (req, res) => { | ||||
|  | ||||
|     // 检查订单是否存在且属于当前用户 | ||||
|     const [orders] = await getDB().execute( | ||||
|       'SELECT id, status FROM orders WHERE id = ? AND user_id = ?', | ||||
|       'SELECT * FROM orders WHERE id = ? AND user_id = ?', | ||||
|       [orderId, userId] | ||||
|     ); | ||||
|  | ||||
| @@ -703,6 +746,28 @@ router.put('/:id/confirm', auth, async (req, res) => { | ||||
|       return res.status(400).json({ success: false, message: '只能确认已发货的订单' }); | ||||
|     } | ||||
|  | ||||
|     // 佣金分配 | ||||
|     const [products] = await getDB().execute( | ||||
|       'SELECT * FROM order_items WHERE order_id = ?', | ||||
|       [orderId] | ||||
|     ); | ||||
|     for (const product of products) { | ||||
|       const [producersResult] = await getDB().execute( | ||||
|         'SELECT * FROM products WHERE id = ?', | ||||
|         [product.product_id] | ||||
|       ); | ||||
|       await getDB().execute( | ||||
|         'UPDATE products SET sales = sales + ? WHERE id = ?', | ||||
|         [product.quantity, product.product_id] | ||||
|       ); | ||||
|       if (producersResult[0].shop_name) { | ||||
|         await getDB().execute( | ||||
|           'UPDATE users SET income = income + ? WHERE id = ?', | ||||
|           [producersResult[0].price * product.quantity, parseInt(producersResult[0].shop_name)] | ||||
|         ); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // 更新订单状态 | ||||
|     await getDB().execute( | ||||
|       'UPDATE orders SET status = "completed", updated_at = NOW() WHERE id = ?', | ||||
| @@ -937,7 +1002,7 @@ router.get('/pending-payment/:id', auth, async (req, res) => { | ||||
|       `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.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 | ||||
| @@ -961,7 +1026,10 @@ router.get('/pending-payment/:id', auth, async (req, res) => { | ||||
|       success: true, | ||||
|       data: { | ||||
|         ...order, | ||||
|         items: orderItems | ||||
|         items: orderItems.map(item => ({ | ||||
|           ...item, | ||||
|           payment_methods: JSON.parse(item.payment_methods) | ||||
|         })) | ||||
|       } | ||||
|     }); | ||||
|   } catch (error) { | ||||
| @@ -1061,7 +1129,7 @@ router.post('/confirm-payment', auth, async (req, res) => { | ||||
|   try { | ||||
|     await connection.beginTransaction(); | ||||
|  | ||||
|     const { orderId: order_id, addressId: address_id } = req.body; | ||||
|     const { orderId: order_id, addressId: address_id, couponRecordId, paymentMethod, beansAmount, pointsAmount } = req.body; | ||||
|     const userId = req.user.id; | ||||
|  | ||||
|     // 验证必填字段 | ||||
| @@ -1089,29 +1157,29 @@ router.post('/confirm-payment', auth, async (req, res) => { | ||||
|     const order = orders[0]; | ||||
|      | ||||
|     // 解析支付方式 | ||||
|     let allPaymentMethods = []; | ||||
|     console.log(typeof order.payment_methods_list); | ||||
|     // let allPaymentMethods = []; | ||||
|     // // console.log(typeof order.payment_methods_list); | ||||
|      | ||||
|     if (order.payment_methods_list) { | ||||
|       try { | ||||
|         // 数据库中存储的是序列化的JSON字符串,直接解析 | ||||
|     // 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 = JSON.parse(JSON.parse(order.payment_methods_list)); | ||||
|     //   } catch (e) { | ||||
|     //     console.error('解析支付方式失败:', e, 'raw data:', order.payment_methods_list); | ||||
|     //     allPaymentMethods = []; | ||||
|     //   } | ||||
|     // } | ||||
|      | ||||
|     // 去重支付方式 | ||||
|     allPaymentMethods = [...new Set(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'); | ||||
|     // // 判断支付方式类型 | ||||
|     // 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 }); | ||||
|     // console.log('订单支付方式:', allPaymentMethods, { hasPoints, hasRongdou, isComboPayment }); | ||||
|  | ||||
|     // 获取收货地址信息 | ||||
|     const [addresses] = await connection.execute( | ||||
| @@ -1142,83 +1210,210 @@ router.post('/confirm-payment', auth, async (req, res) => { | ||||
|       return res.status(400).json({ success: false, message: '融豆不足' }); | ||||
|     } | ||||
|     user.balance = Math.abs(user.balance); | ||||
|      | ||||
|     // 根据支付方式处理扣费逻辑 | ||||
|     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) { | ||||
|  | ||||
|     console.log(123456,paymentMethod) | ||||
|  | ||||
|     // 开始扣钱 | ||||
|     switch (paymentMethod) { | ||||
|       case 'points': | ||||
|         // 积分支付逻辑 | ||||
|         if (user.points < order.total_points) { | ||||
|           await connection.rollback(); | ||||
|           return res.status(400).json({ success: false, message: '积分和融豆余额不足' }); | ||||
|           return res.status(400).json({ success: false, message: '积分不足' }); | ||||
|         } | ||||
|       } | ||||
|         console.log(1234567,paymentMethod,userId,order.total_points) | ||||
|         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; | ||||
|       case 'alipay_wap': | ||||
|         console.log(123465) | ||||
|         break; | ||||
|       default: | ||||
|         await connection.rollback(); | ||||
|         return res.status(400).json({ success: false, message: '支付方式配置错误' }); | ||||
|     } | ||||
|      | ||||
|     console.log('扣费计算:', { totalRongdouNeeded, pointsToDeduct, rongdouToDeduct, userPoints: user.points, userBalance: user.balance }); | ||||
|     // // 根据支付方式处理扣费逻辑 | ||||
|     // 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] | ||||
|       ); | ||||
|     // 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( | ||||
|         `INSERT INTO points_history (user_id, type, amount, description, order_id)  | ||||
|          VALUES (?, 'spend', ?, ?, ?)`, | ||||
|         [userId, pointsToDeduct, `订单支付 - ${order.order_no}`, order_id] | ||||
|         'UPDATE coupon_use SET use_time = NOW(), order_id = ? WHERE id = ?', | ||||
|         [order_id, couponRecordId] | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     // 扣除融豆 | ||||
|     if (rongdouToDeduct > 0) { | ||||
|     if (beansAmount > 0) { | ||||
|       await connection.execute( | ||||
|         'UPDATE users SET balance = balance + ? WHERE id = ?', | ||||
|         [rongdouToDeduct, userId] | ||||
|         'UPDATE orders SET real_rongdou = ? WHERE id = ?', | ||||
|         [beansAmount, order_id] | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|       // 记录融豆变动历史 | ||||
|     if (pointsAmount > 0) { | ||||
|       await connection.execute( | ||||
|         `INSERT INTO rongdou_history (user_id, type, amount, description, order_id)  | ||||
|          VALUES (?, 'spend', ?, ?, ?)`, | ||||
|         [userId, rongdouToDeduct, `订单支付 - ${order.order_no}`, order_id] | ||||
|         'UPDATE orders SET real_points = ? WHERE id = ?', | ||||
|         [pointsAmount, order_id] | ||||
|       ); | ||||
|     } | ||||
|  | ||||
| @@ -1233,11 +1428,17 @@ router.post('/confirm-payment', auth, async (req, res) => { | ||||
|     }); | ||||
|  | ||||
|     await connection.execute( | ||||
|       `UPDATE orders SET status = 'pending', address = ?, updated_at = NOW()  | ||||
|       `UPDATE orders SET status = 'pending', address = ?, updated_at = NOW(), coupon_record_id = ?, payment_method = ? | ||||
|        WHERE id = ?`, | ||||
|       [addressStr, order_id] | ||||
|       [addressStr, couponRecordId === undefined ? null : couponRecordId, paymentMethod, order_id] | ||||
|     ); | ||||
|  | ||||
|     // 减组合库存 | ||||
|     // await connection.execute( | ||||
|     //   'UPDATE product_spec_combinations SET stock = stock - 1 WHERE id = ?', | ||||
|     //   [order.product_combination_id] | ||||
|     // ); | ||||
|  | ||||
|     await connection.commit(); | ||||
|  | ||||
|     res.json({ | ||||
|   | ||||
| @@ -187,6 +187,10 @@ router.get('/query-status/:outTradeNo', paymentAuth, async (req, res) => { | ||||
|   } | ||||
| }); | ||||
|  | ||||
| router.get('/pay-product/test', async (req, res) => { | ||||
|   console.log(123) | ||||
| }) | ||||
|  | ||||
| /** | ||||
|  * 获取用户支付记录 | ||||
|  * GET /api/payment/orders | ||||
|   | ||||
| @@ -4,15 +4,113 @@ const { auth, adminAuth } = require('../middleware/auth'); | ||||
|  | ||||
| const router = express.Router(); | ||||
|  | ||||
| /** | ||||
|  * @swagger | ||||
|  * /api/products: | ||||
|  *   get: | ||||
|  *     summary: 获取商品列表 | ||||
|  *     description: 返回商品列表,支持分页、搜索、分类、状态过滤 | ||||
|  *     tags: [products] | ||||
|  *     parameters: | ||||
|  *       - in: query | ||||
|  *         name: page | ||||
|  *         schema: | ||||
|  *           type: integer | ||||
|  *         description: 页码,默认1 | ||||
|  *       - in: query | ||||
|  *         name: limit | ||||
|  *         schema: | ||||
|  *           type: integer | ||||
|  *         description: 每页数量,默认10,最大100 | ||||
|  *       - in: query | ||||
|  *         name: search | ||||
|  *         schema: | ||||
|  *           type: string | ||||
|  *         description: 搜索商品名称 | ||||
|  *       - in: query | ||||
|  *         name: category | ||||
|  *         schema: | ||||
|  *           type: string | ||||
|  *         description: 分类名称 | ||||
|  *       - in: query | ||||
|  *         name: status | ||||
|  *         schema: | ||||
|  *           type: string | ||||
|  *         description: 商品状态(active/inactive) | ||||
|  *     responses: | ||||
|  *       200: | ||||
|  *         description: 成功返回商品列表 | ||||
|  *         content: | ||||
|  *           application/json: | ||||
|  *             schema: | ||||
|  *               type: object | ||||
|  *               properties: | ||||
|  *                 success: | ||||
|  *                   type: boolean | ||||
|  *                   example: true | ||||
|  *                 data: | ||||
|  *                   type: array | ||||
|  *                   items: | ||||
|  *                     type: object | ||||
|  *                     properties: | ||||
|  *                       id: | ||||
|  *                         type: string | ||||
|  *                         example: "1" | ||||
|  *                       name: | ||||
|  *                         type: string | ||||
|  *                         example: "商品A" | ||||
|  *                       rongdou_price: | ||||
|  *                         type: number | ||||
|  *                         example: 100 | ||||
|  *                       points_price: | ||||
|  *                         type: number | ||||
|  *                         example: 1000 | ||||
|  *                       stock: | ||||
|  *                         type: integer | ||||
|  *                         example: 100 | ||||
|  *                       image: | ||||
|  *                         type: string | ||||
|  *                         example: "https://example.com/image.jpg" | ||||
|  *                       description: | ||||
|  *                         type: string | ||||
|  *                         example: "这是一个商品" | ||||
|  *                       status: | ||||
|  *                         type: string | ||||
|  *                         example: "active" | ||||
|  *                       category_id: | ||||
|  *                         type: string | ||||
|  *                         example: "1" | ||||
|  *                       created_at: | ||||
|  *                         type: string | ||||
|  *                         format: date-time | ||||
|  *                         example: "2023-01-01T00:00:00Z" | ||||
|  *                       updated_at: | ||||
|  *                         type: string | ||||
|  *                         format: date-time | ||||
|  *                         example: "2023-01-01T00:00:00Z" | ||||
|  *                       sales: | ||||
|  *                         type: integer | ||||
|  *                         example: 100 | ||||
|  *                       images: | ||||
|  *                         type: array | ||||
|  *                         items: | ||||
|  *                           type: string | ||||
|  *                           example: "https://example.com/image.jpg" | ||||
|  *     400: | ||||
|  *       description: 无效的分页参数 | ||||
|  *     500: | ||||
|  *       description: 服务器内部错误 | ||||
|  */ | ||||
| // 商品管理路由 | ||||
| router.get('/', async (req, res) => { | ||||
|   try { | ||||
|     const { page = 1, limit = 10, search = '', category = '', status = '' } = req.query; | ||||
|     const { page = 1, limit = 10, search = '', category = '', status = '', sort } = 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); | ||||
|     let filteredProducts = [] | ||||
|      | ||||
|     console.log('分页参数:', { pageNum, limitNum, offset, search, category, status }); | ||||
|      | ||||
| @@ -24,17 +122,27 @@ router.get('/', async (req, res) => { | ||||
|       params.push(`%${search}%`); | ||||
|     } | ||||
|      | ||||
|     if (category) { | ||||
|       whereClause += ' AND category = ?'; | ||||
|       params.push(category); | ||||
|     } | ||||
|      | ||||
|     if (status) { | ||||
|       whereClause += ' AND status = ?'; | ||||
|       params.push(status); | ||||
|     } else { | ||||
|       whereClause += ' AND status = "active"'; | ||||
|     } | ||||
|  | ||||
|     switch (sort) { | ||||
|       case 'price_desc': | ||||
|         whereClause += ' ORDER BY sale_price DESC' | ||||
|         break; | ||||
|       case 'price_asc': | ||||
|         whereClause += ' ORDER BY sale_price ASC' | ||||
|         break; | ||||
|       case 'sales_desc': | ||||
|         whereClause += ' ORDER BY sales DESC' | ||||
|         break; | ||||
|       default: | ||||
|         whereClause += ' ORDER BY created_at DESC' | ||||
|         break; | ||||
|     } | ||||
|      | ||||
|     // 获取总数 | ||||
|     const countQuery = `SELECT COUNT(*) as total FROM products ${whereClause}`; | ||||
| @@ -43,24 +151,53 @@ router.get('/', async (req, res) => { | ||||
|      | ||||
|     // 获取商品列表 | ||||
|     const query = ` | ||||
|       SELECT id, name, rongdou_price, category, points_price, stock, image_url as image, description, status, payment_methods, created_at, updated_at | ||||
|       SELECT id, name, rongdou_price, points_price, stock, image_url as image, description, status, payment_methods, created_at, updated_at, sales, images | ||||
|       FROM products  | ||||
|       ${whereClause} | ||||
|       ORDER BY created_at DESC | ||||
|       LIMIT ${limitNum} OFFSET ${offset} | ||||
|     `; | ||||
|      | ||||
|     // 确保参数数组正确传递 | ||||
|     const queryParams = [...params]; | ||||
|     console.log('Query params:', queryParams, 'Query:', query); | ||||
|     // console.log('Query params:', queryParams, 'Query:', query); | ||||
|     const [products] = await getDB().execute(query, queryParams); | ||||
|  | ||||
|     products.forEach(item=>{ | ||||
|       item.payment_methods = JSON.parse(item.payment_methods) | ||||
|       item.images = JSON.parse(item.images) | ||||
|     }) | ||||
|  | ||||
|  | ||||
|     if (category) { | ||||
|       // 先根据分类名称获取分类ID | ||||
|       const query = `SELECT * FROM category WHERE category_name = ?` | ||||
|       const [getCategory] = await getDB().execute(query, [category]) | ||||
|       const [getSecondCategory] = await getDB().execute('SELECT * FROM category WHERE parent_id = ?', [getCategory[0].id]) | ||||
|  | ||||
|       const sumCategory = getCategory.concat(getSecondCategory).map(item=>item.id) | ||||
|  | ||||
|       // 再根据分类ID获取商品ID | ||||
|       const getProductCategory = [] | ||||
|       for (const item of sumCategory) { | ||||
|         const [getProductCategoryItem] = await getDB().execute('SELECT * FROM products_category WHERE category_id = ?', [item]) | ||||
|         getProductCategory.push(...getProductCategoryItem) | ||||
|       } | ||||
|  | ||||
|       const productIds = [] | ||||
|       for (const item of getProductCategory) { | ||||
|         productIds.push(item.product_id) | ||||
|       } | ||||
|  | ||||
|       filteredProducts = products.filter(item=>productIds.includes(item.id)) | ||||
|     } else { | ||||
|       filteredProducts = products | ||||
|     } | ||||
|  | ||||
|     res.json({ | ||||
|       success: true, | ||||
|       data: { | ||||
|         products, | ||||
|         products: filteredProducts, | ||||
|         // products: products, | ||||
|         pagination: { | ||||
|           page: pageNum, | ||||
|           limit: limitNum, | ||||
| @@ -75,36 +212,36 @@ router.get('/', async (req, res) => { | ||||
|   } | ||||
| }); | ||||
|  | ||||
| // 获取商品分类列表 | ||||
| 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' | ||||
|     ); | ||||
| // // 获取商品分类列表 | ||||
| // 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: '获取商品分类失败' }); | ||||
|   } | ||||
| }); | ||||
| //     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('/hot', async (req, res) => { | ||||
|   try { | ||||
|     // 从活跃商品中随机获取2个商品 | ||||
|     const [products] = await getDB().execute( | ||||
|       `SELECT id, name, category, price, points_price, rongdou_price, stock,  | ||||
|       `SELECT id, name, price, points_price, rongdou_price, stock,  | ||||
|              image_url, images, description, shop_name, shop_avatar,  | ||||
|              payment_methods, sales, rating, status, created_at, updated_at | ||||
|        FROM products  | ||||
|        WHERE status = 'active' AND stock > 0 | ||||
|        ORDER BY RAND()  | ||||
|        ORDER BY sales DESC | ||||
|        LIMIT 2` | ||||
|     ); | ||||
|      | ||||
| @@ -156,36 +293,42 @@ router.get('/hot', async (req, res) => { | ||||
|  */ | ||||
| router.get('/cheap', async (req, res) => { | ||||
|   try { | ||||
|     // 从活跃商品中随机获取2个商品作为秒杀商品 | ||||
|  | ||||
|     const [products] = await getDB().execute( | ||||
|       `SELECT id, name, category, price, points_price, rongdou_price, stock,  | ||||
|              image_url, images, description, shop_name, shop_avatar,  | ||||
|              payment_methods, sales, rating, status, created_at, updated_at | ||||
|        FROM products  | ||||
|        WHERE status = 'active' AND stock > 0 | ||||
|       `SELECT id, start_time, end_time, flash_stock, flash_price, products_id | ||||
|        FROM flash_product | ||||
|        WHERE end_time > NOW() AND flash_stock > 0 | ||||
|        ORDER BY RAND()  | ||||
|        LIMIT 2` | ||||
|     ); | ||||
|      | ||||
|     // 格式化商品数据,为秒杀商品添加特殊标识 | ||||
|     const formattedProducts = products.map(product => ({ | ||||
|       ...product, | ||||
|       images: product.images ? JSON.parse(product.images) : (product.image_url ? [product.image_url] : []), | ||||
|       payment_methods: product.payment_methods ? JSON.parse(product.payment_methods) : ['points'], | ||||
|       // 秒杀商品特殊处理:价格打8折 | ||||
|       flash_sale_price: Math.floor(product.price * 0.8), | ||||
|       flash_sale_points: Math.floor(product.points_price * 0.8), | ||||
|       flash_sale_rongdou: Math.floor(product.rongdou_price * 0.8), | ||||
|       is_flash_sale: true, | ||||
|       // 保持向后兼容 | ||||
|       points: product.points_price, | ||||
|       image: product.image_url | ||||
|     })); | ||||
|  | ||||
|     const tempProducts = await Promise.all(products.map(async item=>{ | ||||
|  | ||||
|       const [product] = await getDB().execute( | ||||
|         `SELECT id, name, price, points_price, rongdou_price, stock,  | ||||
|                image_url, images, description, shop_name, shop_avatar,  | ||||
|                payment_methods, sales, rating, status, created_at, updated_at | ||||
|          FROM products  | ||||
|          WHERE id = ?`, | ||||
|         [item.products_id] | ||||
|       ) | ||||
|  | ||||
|       item = { | ||||
|         ...product[0], | ||||
|         images: product.images ? JSON.parse(product.images) : (product.image_url ? [product.image_url] : []), | ||||
|         payment_methods: product.payment_methods ? JSON.parse(product.payment_methods) : ['points'],  | ||||
|         ...item, | ||||
|         points: product.points_price, | ||||
|         image: product.image_url, | ||||
|         id: product[0].id, | ||||
|       } | ||||
|       return item | ||||
|     })) | ||||
|      | ||||
|     res.json({ | ||||
|       success: true, | ||||
|       data: { | ||||
|         products: formattedProducts | ||||
|         products: tempProducts | ||||
|       } | ||||
|     }); | ||||
|   } catch (error) { | ||||
| @@ -336,7 +479,7 @@ router.get('/:id', async (req, res) => { | ||||
|     const userId = req.user?.id; // 可选的用户ID,用于检查收藏状态 | ||||
|      | ||||
|     const query = ` | ||||
|       SELECT id, name, category, price, points_price, rongdou_price, stock,  | ||||
|       SELECT id, name, price, points_price, rongdou_price, stock,  | ||||
|              image_url, images, videos, description, details, shop_name, shop_avatar, | ||||
|              payment_methods, sales, rating, status, created_at, updated_at | ||||
|       FROM products  | ||||
| @@ -364,6 +507,8 @@ router.get('/:id', async (req, res) => { | ||||
|        ORDER BY psc.combination_key`, | ||||
|       [id] | ||||
|     ); | ||||
|  | ||||
|     // console.log(123,specCombinations); | ||||
|      | ||||
|     // 为每个规格组合获取详细的规格值信息 | ||||
|     const enhancedSpecifications = []; | ||||
| @@ -436,6 +581,8 @@ router.get('/:id', async (req, res) => { | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // console.log(123,enhancedSpecifications); | ||||
|      | ||||
|     // 获取商品属性 | ||||
|     const [attributes] = await getDB().execute( | ||||
| @@ -525,7 +672,7 @@ router.get('/:id', async (req, res) => { | ||||
|       // 保持向后兼容 | ||||
|       points: product.points_price, | ||||
|       image: product.image_url, | ||||
|       tags: product.category ? [product.category] : [] | ||||
|       // tags: product.category ? [product.category] : [] | ||||
|     }; | ||||
|      | ||||
|     res.json({ | ||||
| @@ -888,39 +1035,123 @@ router.get('/:id/reviews', async (req, res) => { | ||||
| // 获取推荐商品 | ||||
| router.get('/:id/recommended', async (req, res) => { | ||||
|   try { | ||||
|     // 获取商品id | ||||
|     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 | ||||
|  | ||||
|     // 通过商品id获取该商品的分类id | ||||
|     const categoryQuery = ` | ||||
|       SELECT category_id FROM products_category WHERE product_id = ? | ||||
|     `; | ||||
|     const [currentCategory] = await getDB().execute(categoryQuery, [id]); | ||||
|      | ||||
|     const [recommendedProducts] = await getDB().execute(query, [id, id]); | ||||
|     let parentId = null; | ||||
|     let categoryIds = currentCategory.map(item => item.category_id); | ||||
|      | ||||
|     // 如果同类别商品不足,补充其他热门商品 | ||||
|     if (recommendedProducts.length < 6) { | ||||
|       const remainingCount = 6 - recommendedProducts.length; | ||||
|       if (remainingCount > 0) { | ||||
|         const additionalQuery = ` | ||||
|           SELECT id, name, category, price, points_price as points,  | ||||
|     if(currentCategory.length > 0 && currentCategory[0] !== null) { | ||||
|       // 获取该商品的一级分类id | ||||
|       const item = currentCategory[0]; | ||||
|       const levelQuery = ` | ||||
|         SELECT * FROM category WHERE id = ? AND level = 1 | ||||
|       `; | ||||
|       const [fatherCategory] = await getDB().execute(levelQuery, [item.category_id]); | ||||
|        | ||||
|       if(fatherCategory.length > 0) { | ||||
|         // 该商品为一级分类商品,不存在二级分类 | ||||
|         parentId = fatherCategory[0].id; | ||||
|         categoryIds.push(parentId); | ||||
|       } else { | ||||
|         // 获取该商品的二级分类id | ||||
|         const secondlevelQuery = ` | ||||
|           SELECT * FROM category WHERE id = ? AND level = 2 | ||||
|         `; | ||||
|         const [secondlevelCategory] = await getDB().execute(secondlevelQuery, [item.category_id]); | ||||
|         if(secondlevelCategory.length > 0) { | ||||
|           // 通过二级分类获取一级分类id | ||||
|           parentId = secondlevelCategory[0].parent_id; | ||||
|           categoryIds.push(parentId); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // categoryIds目前存储该商品的一级分类id及其目录下所有二级分类的id | ||||
|     const allSecondCategoryIdsQuery = ` | ||||
|       SELECT id FROM category WHERE parent_id = ? AND level = 2 | ||||
|     `; | ||||
|     const [secondCategoryIds] = await getDB().execute(allSecondCategoryIdsQuery, [parentId]); | ||||
|     categoryIds.push(...secondCategoryIds.map(item => item.id)); | ||||
|  | ||||
|     let recommendedProducts = []; | ||||
|     let filteredRecommendProductIds = []; | ||||
|  | ||||
|     // 如果有分类ID,先获取同类商品 | ||||
|     if (categoryIds.length > 0) { | ||||
|       const recommendId = ` | ||||
|         SELECT * FROM products_category WHERE category_id IN (${categoryIds.map(() => '?').join(',')}) | ||||
|       `; | ||||
|       const [recommendProductIds] = await getDB().execute(recommendId, categoryIds); | ||||
|  | ||||
|       filteredRecommendProductIds = [...new Set(recommendProductIds.map(item => item.product_id))]; | ||||
|  | ||||
|       // 获取同类别的其他商品作为推荐 | ||||
|       if (filteredRecommendProductIds.length > 0) { | ||||
|         const query = ` | ||||
|           SELECT id, name, price, points_price as points,  | ||||
|                  stock, image_url as image, description | ||||
|           FROM products  | ||||
|           WHERE id != ? AND status = 'active' | ||||
|           ORDER BY RAND() | ||||
|           LIMIT ${remainingCount} | ||||
|           WHERE id IN (${filteredRecommendProductIds.map(() => '?').join(',')}) AND id != ? | ||||
|         `; | ||||
|          | ||||
|         const [categoryProducts] = await getDB().execute(query, [...filteredRecommendProductIds, id]); | ||||
|         recommendedProducts = categoryProducts; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // 如果同类别商品不足,补充其他热门商品 | ||||
|     if (recommendedProducts.length < 6) { | ||||
|  | ||||
|       let recommendQuery = ` | ||||
|         SELECT products_id FROM recommend_product | ||||
|         WHERE 1 = 1 | ||||
|       `; | ||||
|  | ||||
|       if (filteredRecommendProductIds.length > 0) { | ||||
|         recommendQuery += ` AND products_id NOT IN (${filteredRecommendProductIds.map(() => '?').join(',')})`; | ||||
|       } | ||||
|  | ||||
|       recommendQuery += ` ORDER BY RAND() LIMIT ${6 - recommendedProducts.length}`; | ||||
|  | ||||
|       // 根据是否有排除ID来传递参数 | ||||
|       const queryParams = filteredRecommendProductIds.length > 0  | ||||
|         ? [...filteredRecommendProductIds]  | ||||
|         : []; | ||||
|  | ||||
|       const [recommendProductIds] = await getDB().execute(recommendQuery, queryParams); | ||||
|       filteredRecommendProductIds.push(...recommendProductIds.map(item => item.products_id)); | ||||
|  | ||||
|       for (const item of recommendProductIds) { | ||||
|         const recommendQuery = ` | ||||
|           SELECT id, name, price, points_price as points,  | ||||
|                  stock, image_url as image, description | ||||
|           FROM products  | ||||
|           WHERE id = ? | ||||
|         `; | ||||
|         const [recommendProduct] = await getDB().execute(recommendQuery, [item.products_id]); | ||||
|         recommendedProducts.push(recommendProduct[0]); | ||||
|       } | ||||
|       if (recommendProductIds.length < 6) { | ||||
|         // 补充其他热门商品 | ||||
|         const additionalQuery = ` | ||||
|           SELECT id, name, price, points_price as points,  | ||||
|                  stock, image_url as image, description | ||||
|           FROM products | ||||
|           WHERE id NOT IN (${filteredRecommendProductIds.map(() => '?').join(',')}) | ||||
|           ORDER BY RAND() | ||||
|           LIMIT ${6 - recommendedProducts.length} | ||||
|         `; | ||||
|         const [additionalProducts] = await getDB().execute( | ||||
|           additionalQuery,  | ||||
|           [id] | ||||
|           additionalQuery, | ||||
|           filteredRecommendProductIds | ||||
|         ); | ||||
|          | ||||
|         recommendedProducts.push(...additionalProducts); | ||||
|       } | ||||
|     } | ||||
|   | ||||
| @@ -163,7 +163,7 @@ router.get('/provinces', async (req, res) => { | ||||
|             }); | ||||
|             global.provinces = regionsByLevel[1]; | ||||
|         }else { | ||||
|             console.log('1111') | ||||
|             // console.log('1111') | ||||
|             regionsByLevel[1] = global.provinces; | ||||
|         } | ||||
|  | ||||
|   | ||||
							
								
								
									
										96
									
								
								server.js
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								server.js
									
									
									
									
									
								
							| @@ -88,6 +88,95 @@ const limiter = rateLimit({ | ||||
| }); | ||||
| app.use('/api', limiter); | ||||
|  | ||||
|  | ||||
|  | ||||
| // ============ 添加 Swagger 文档路由 ============ | ||||
| const swaggerUi = require('swagger-ui-express'); | ||||
| const specs = require('./swagger'); | ||||
|  | ||||
| // 创建自定义的 HTML 页面来解决兼容性问题 | ||||
| const swaggerHtml = ` | ||||
| <!DOCTYPE html> | ||||
| <html lang="zh-CN"> | ||||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <title>融豆商城 API文档</title> | ||||
|     <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@4.15.5/swagger-ui.css" /> | ||||
|     <style> | ||||
|         html { | ||||
|             box-sizing: border-box; | ||||
|             overflow: -moz-scrollbars-vertical; | ||||
|             overflow-y: scroll; | ||||
|         } | ||||
|         *, | ||||
|         *:before, | ||||
|         *:after { | ||||
|             box-sizing: inherit; | ||||
|         } | ||||
|         body { | ||||
|             margin: 0; | ||||
|             background: #fafafa; | ||||
|         } | ||||
|         .swagger-ui .topbar { | ||||
|             display: none; | ||||
|         } | ||||
|         .swagger-ui .info .title { | ||||
|             color: #3b4151; | ||||
|             font-family: sans-serif; | ||||
|             font-size: 36px; | ||||
|             margin: 0; | ||||
|         } | ||||
|     </style> | ||||
| </head> | ||||
| <body> | ||||
|     <div id="swagger-ui"></div> | ||||
|     <script src="https://unpkg.com/swagger-ui-dist@4.15.5/swagger-ui-bundle.js"></script> | ||||
|     <script src="https://unpkg.com/swagger-ui-dist@4.15.5/swagger-ui-standalone-preset.js"></script> | ||||
|     <script> | ||||
|         // 兼容性处理 | ||||
|         if (!Object.hasOwn) { | ||||
|             Object.hasOwn = function(obj, prop) { | ||||
|                 return Object.prototype.hasOwnProperty.call(obj, prop); | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         window.onload = function() { | ||||
|             const ui = SwaggerUIBundle({ | ||||
|                 url: '/api-docs.json', | ||||
|                 dom_id: '#swagger-ui', | ||||
|                 deepLinking: true, | ||||
|                 presets: [ | ||||
|                     SwaggerUIBundle.presets.apis, | ||||
|                     SwaggerUIStandalonePreset | ||||
|                 ], | ||||
|                 plugins: [ | ||||
|                     SwaggerUIBundle.plugins.DownloadUrl | ||||
|                 ], | ||||
|                 layout: "StandaloneLayout", | ||||
|                 persistAuthorization: true | ||||
|             }); | ||||
|              | ||||
|             window.ui = ui; | ||||
|         } | ||||
|     </script> | ||||
| </body> | ||||
| </html> | ||||
| `; | ||||
|  | ||||
| // 自定义 Swagger 路由 | ||||
| app.get('/api-docs', (req, res) => { | ||||
|     res.send(swaggerHtml); | ||||
| }); | ||||
|  | ||||
| // 提供 JSON 格式的文档 | ||||
| app.get('/api-docs.json', (req, res) => { | ||||
|     res.setHeader('Content-Type', 'application/json'); | ||||
|     res.send(specs); | ||||
| }); | ||||
| // ============ Swagger 配置结束 ============ | ||||
|  | ||||
|  | ||||
|  | ||||
| // 静态文件服务 - 必须在API路由之前 | ||||
| // 为uploads路径配置CORS和静态文件服务 | ||||
| app.use('/uploads', express.static(path.join(__dirname, 'uploads'), { | ||||
| @@ -249,6 +338,13 @@ app.use('/api/announcements', require('./routes/announcements')); // 通知公 | ||||
| app.use('/api/wechat-pay', require('./routes/wechatPay')); // 只保留微信支付 | ||||
| app.use('/api/payment', require('./routes/payment')); | ||||
|  | ||||
| // 优惠券路由 | ||||
| app.use('/api/coupon', require('./routes/coupon')); | ||||
|  | ||||
| // 分类路由 | ||||
| app.use('/api/category', require('./routes/category')); | ||||
|  | ||||
|  | ||||
| // 前端路由 - 必须在最后,作为fallback | ||||
| app.get('/', (req, res) => { | ||||
|   res.removeHeader('Origin-Agent-Cluster'); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user