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
|
* @swagger
|
||||||
* /api/cart/batch:
|
* /api/cart/batch:
|
||||||
@@ -654,6 +589,7 @@ router.delete('/clear', auth, async (req, res) => {
|
|||||||
success: true,
|
success: true,
|
||||||
message: '清空购物车成功'
|
message: '清空购物车成功'
|
||||||
});
|
});
|
||||||
|
console.log(11111111111111)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('清空购物车失败:', error);
|
console.error('清空购物车失败:', error);
|
||||||
res.status(500).json({ success: false, message: '清空购物车失败' });
|
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;
|
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;
|
||||||
397
routes/orders.js
397
routes/orders.js
@@ -77,7 +77,7 @@ router.get('/', auth, async (req, res) => {
|
|||||||
SELECT
|
SELECT
|
||||||
o.id, o.order_no, o.user_id, o.total_amount, o.total_points,
|
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,
|
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
|
FROM orders o
|
||||||
LEFT JOIN users u ON o.user_id = u.id
|
LEFT JOIN users u ON o.user_id = u.id
|
||||||
${whereClause}
|
${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) {
|
if (order.address) {
|
||||||
try {
|
try {
|
||||||
@@ -344,7 +344,8 @@ router.get('/:id', auth, async (req, res) => {
|
|||||||
const query = `
|
const query = `
|
||||||
SELECT
|
SELECT
|
||||||
o.id, o.order_no, o.user_id, o.total_amount, o.total_points,
|
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
|
u.username, u.phone
|
||||||
FROM orders o
|
FROM orders o
|
||||||
LEFT JOIN users u ON o.user_id = u.id
|
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) {
|
if (order.address) {
|
||||||
try {
|
try {
|
||||||
@@ -442,6 +443,27 @@ router.post('/create-from-cart', auth, async (req, res) => {
|
|||||||
return res.status(400).json({ success: false, message: '购物车商品不存在' });
|
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 totalAmount = 0;
|
||||||
let totalPoints = 0;
|
let totalPoints = 0;
|
||||||
@@ -479,20 +501,23 @@ router.post('/create-from-cart', auth, async (req, res) => {
|
|||||||
totalAmount += finalPrice * item.quantity;
|
totalAmount += finalPrice * item.quantity;
|
||||||
|
|
||||||
// 根据支付方式计算积分和融豆需求
|
// 根据支付方式计算积分和融豆需求
|
||||||
const hasPoints = productPaymentMethods.includes('points') || productPaymentMethods.includes('points_rongdou');
|
// const hasPoints = productPaymentMethods.includes('points') || productPaymentMethods.includes('points_rongdou');
|
||||||
const hasRongdou = productPaymentMethods.includes('rongdou') || productPaymentMethods.includes('points_rongdou');
|
// const hasRongdou = productPaymentMethods.includes('rongdou') || productPaymentMethods.includes('points_rongdou');
|
||||||
|
|
||||||
if (hasPoints && !hasRongdou) {
|
// if (hasPoints && !hasRongdou) {
|
||||||
// 仅积分支付:按10000积分=1融豆计算
|
// // 仅积分支付:按10000积分=1融豆计算
|
||||||
totalPoints += finalRongdouPrice * item.quantity * 10000;
|
// totalPoints += finalRongdouPrice * item.quantity * 10000;
|
||||||
totalRongdou += finalRongdouPrice * item.quantity;
|
// totalRongdou += finalRongdouPrice * item.quantity;
|
||||||
} else if (!hasPoints && hasRongdou) {
|
// } else if (!hasPoints && hasRongdou) {
|
||||||
// 仅融豆支付
|
// // 仅融豆支付
|
||||||
totalRongdou += finalRongdouPrice * item.quantity;
|
// totalRongdou += finalRongdouPrice * item.quantity;
|
||||||
} else {
|
// } else {
|
||||||
// 组合支付或默认:记录融豆价格,前端可选择支付方式
|
// // 组合支付或默认:记录融豆价格,前端可选择支付方式
|
||||||
totalRongdou += finalRongdouPrice * item.quantity;
|
// 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];
|
const order = orders[0];
|
||||||
|
|
||||||
if (order.status !== 'pending') {
|
if (order.status !== 'pending' && order.status !== 'pre_order') {
|
||||||
await db.query('ROLLBACK');
|
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]
|
[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(
|
await db.execute(
|
||||||
'UPDATE orders SET status = "cancelled", updated_at = NOW() WHERE id = ?',
|
'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(
|
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]
|
[orderId, userId]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -703,6 +746,28 @@ router.put('/:id/confirm', auth, async (req, res) => {
|
|||||||
return res.status(400).json({ success: false, message: '只能确认已发货的订单' });
|
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(
|
await getDB().execute(
|
||||||
'UPDATE orders SET status = "completed", updated_at = NOW() WHERE id = ?',
|
'UPDATE orders SET status = "completed", updated_at = NOW() WHERE id = ?',
|
||||||
@@ -937,7 +1002,7 @@ router.get('/pending-payment/:id', auth, async (req, res) => {
|
|||||||
`SELECT
|
`SELECT
|
||||||
oi.id, oi.product_id, oi.quantity, oi.price, oi.points_price, oi.rongdou_price,
|
oi.id, oi.product_id, oi.quantity, oi.price, oi.points_price, oi.rongdou_price,
|
||||||
oi.spec_combination_id,
|
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
|
psc.spec_values as spec_info
|
||||||
FROM order_items oi
|
FROM order_items oi
|
||||||
LEFT JOIN products p ON oi.product_id = p.id
|
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,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
...order,
|
...order,
|
||||||
items: orderItems
|
items: orderItems.map(item => ({
|
||||||
|
...item,
|
||||||
|
payment_methods: JSON.parse(item.payment_methods)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -1061,7 +1129,7 @@ router.post('/confirm-payment', auth, async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
await connection.beginTransaction();
|
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;
|
const userId = req.user.id;
|
||||||
|
|
||||||
// 验证必填字段
|
// 验证必填字段
|
||||||
@@ -1089,29 +1157,29 @@ router.post('/confirm-payment', auth, async (req, res) => {
|
|||||||
const order = orders[0];
|
const order = orders[0];
|
||||||
|
|
||||||
// 解析支付方式
|
// 解析支付方式
|
||||||
let allPaymentMethods = [];
|
// let allPaymentMethods = [];
|
||||||
console.log(typeof order.payment_methods_list);
|
// // console.log(typeof order.payment_methods_list);
|
||||||
|
|
||||||
if (order.payment_methods_list) {
|
// if (order.payment_methods_list) {
|
||||||
try {
|
// try {
|
||||||
// 数据库中存储的是序列化的JSON字符串,直接解析
|
// // 数据库中存储的是序列化的JSON字符串,直接解析
|
||||||
|
|
||||||
allPaymentMethods = JSON.parse(JSON.parse(order.payment_methods_list));
|
// allPaymentMethods = JSON.parse(JSON.parse(order.payment_methods_list));
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
console.error('解析支付方式失败:', e, 'raw data:', order.payment_methods_list);
|
// console.error('解析支付方式失败:', e, 'raw data:', order.payment_methods_list);
|
||||||
allPaymentMethods = [];
|
// allPaymentMethods = [];
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 去重支付方式
|
// // 去重支付方式
|
||||||
allPaymentMethods = [...new Set(allPaymentMethods)];
|
// allPaymentMethods = [...new Set(allPaymentMethods)];
|
||||||
|
|
||||||
// 判断支付方式类型
|
// // 判断支付方式类型
|
||||||
const hasPoints = allPaymentMethods.includes('points') || allPaymentMethods.includes('points_rongdou');
|
// const hasPoints = allPaymentMethods.includes('points') || allPaymentMethods.includes('points_rongdou');
|
||||||
const hasRongdou = allPaymentMethods.includes('rongdou') || allPaymentMethods.includes('points_rongdou');
|
// const hasRongdou = allPaymentMethods.includes('rongdou') || allPaymentMethods.includes('points_rongdou');
|
||||||
const isComboPayment = 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(
|
const [addresses] = await connection.execute(
|
||||||
@@ -1143,82 +1211,209 @@ router.post('/confirm-payment', auth, async (req, res) => {
|
|||||||
}
|
}
|
||||||
user.balance = Math.abs(user.balance);
|
user.balance = Math.abs(user.balance);
|
||||||
|
|
||||||
// 根据支付方式处理扣费逻辑
|
console.log(123456,paymentMethod)
|
||||||
let totalRongdouNeeded = order.total_rongdou; // 需要的融豆总数
|
|
||||||
let pointsToDeduct = 0; // 需要扣除的积分
|
|
||||||
let rongdouToDeduct = 0; // 需要扣除的融豆
|
|
||||||
|
|
||||||
if (!hasRongdou && !hasPoints) {
|
// 开始扣钱
|
||||||
await connection.rollback();
|
switch (paymentMethod) {
|
||||||
return res.status(400).json({ success: false, message: '商品支付方式配置错误' });
|
case 'points':
|
||||||
}
|
// 积分支付逻辑
|
||||||
|
if (user.points < order.total_points) {
|
||||||
if (hasPoints && !hasRongdou) {
|
|
||||||
// 只支持积分支付,按10000积分=1融豆转换
|
|
||||||
const pointsNeeded = totalRongdouNeeded * 10000;
|
|
||||||
if (user.points < pointsNeeded) {
|
|
||||||
await connection.rollback();
|
await connection.rollback();
|
||||||
return res.status(400).json({ success: false, message: '积分不足' });
|
return res.status(400).json({ success: false, message: '积分不足' });
|
||||||
}
|
}
|
||||||
pointsToDeduct = pointsNeeded;
|
console.log(1234567,paymentMethod,userId,order.total_points)
|
||||||
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(
|
await connection.execute(
|
||||||
'UPDATE users SET points = points - ? WHERE id = ?',
|
'UPDATE users SET points = points - ? WHERE id = ?',
|
||||||
[pointsToDeduct, userId]
|
[order.total_points, userId]
|
||||||
);
|
);
|
||||||
|
|
||||||
// 记录积分变动历史
|
// 记录积分变动历史
|
||||||
await connection.execute(
|
await connection.execute(
|
||||||
`INSERT INTO points_history (user_id, type, amount, description, order_id)
|
`INSERT INTO points_history (user_id, type, amount, description, order_id)
|
||||||
VALUES (?, 'spend', ?, ?, ?)`,
|
VALUES (?, 'spend', ?, ?, ?)`,
|
||||||
[userId, pointsToDeduct, `订单支付 - ${order.order_no}`, order_id]
|
[userId, order.total_points, `订单支付 - ${order.order_no}`, order_id]
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// 扣除融豆
|
// 供应商分佣
|
||||||
if (rongdouToDeduct > 0) {
|
// 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(
|
await connection.execute(
|
||||||
'UPDATE users SET balance = balance + ? WHERE id = ?',
|
'UPDATE users SET balance = balance + ? WHERE id = ?',
|
||||||
[rongdouToDeduct, userId]
|
[order.total_rongdou, userId]
|
||||||
);
|
);
|
||||||
|
|
||||||
// 记录融豆变动历史
|
// 记录融豆变动历史
|
||||||
await connection.execute(
|
await connection.execute(
|
||||||
`INSERT INTO rongdou_history (user_id, type, amount, description, order_id)
|
`INSERT INTO points_history (user_id, type, amount, description, order_id)
|
||||||
VALUES (?, 'spend', ?, ?, ?)`,
|
VALUES (?, 'spend', ?, ?, ?)`,
|
||||||
[userId, rongdouToDeduct, `订单支付 - ${order.order_no}`, order_id]
|
[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: '支付方式配置错误' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// // 根据支付方式处理扣费逻辑
|
||||||
|
// 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]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (beansAmount > 0) {
|
||||||
|
await connection.execute(
|
||||||
|
'UPDATE orders SET real_rongdou = ? WHERE id = ?',
|
||||||
|
[beansAmount, order_id]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pointsAmount > 0) {
|
||||||
|
await connection.execute(
|
||||||
|
'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(
|
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 = ?`,
|
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();
|
await connection.commit();
|
||||||
|
|
||||||
res.json({
|
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
|
* GET /api/payment/orders
|
||||||
|
|||||||
@@ -4,15 +4,113 @@ const { auth, adminAuth } = require('../middleware/auth');
|
|||||||
|
|
||||||
const router = express.Router();
|
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) => {
|
router.get('/', async (req, res) => {
|
||||||
try {
|
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 pageNum = Math.max(1, parseInt(page) || 1);
|
||||||
const limitNum = Math.max(1, Math.min(100, parseInt(limit) || 10)); // 限制最大100条
|
const limitNum = Math.max(1, Math.min(100, parseInt(limit) || 10)); // 限制最大100条
|
||||||
const offset = Math.max(0, (pageNum - 1) * limitNum);
|
const offset = Math.max(0, (pageNum - 1) * limitNum);
|
||||||
|
let filteredProducts = []
|
||||||
|
|
||||||
console.log('分页参数:', { pageNum, limitNum, offset, search, category, status });
|
console.log('分页参数:', { pageNum, limitNum, offset, search, category, status });
|
||||||
|
|
||||||
@@ -24,11 +122,6 @@ router.get('/', async (req, res) => {
|
|||||||
params.push(`%${search}%`);
|
params.push(`%${search}%`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (category) {
|
|
||||||
whereClause += ' AND category = ?';
|
|
||||||
params.push(category);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
whereClause += ' AND status = ?';
|
whereClause += ' AND status = ?';
|
||||||
params.push(status);
|
params.push(status);
|
||||||
@@ -36,6 +129,21 @@ router.get('/', async (req, res) => {
|
|||||||
whereClause += ' AND status = "active"';
|
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}`;
|
const countQuery = `SELECT COUNT(*) as total FROM products ${whereClause}`;
|
||||||
const [countResult] = await getDB().execute(countQuery, params);
|
const [countResult] = await getDB().execute(countQuery, params);
|
||||||
@@ -43,24 +151,53 @@ router.get('/', async (req, res) => {
|
|||||||
|
|
||||||
// 获取商品列表
|
// 获取商品列表
|
||||||
const query = `
|
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
|
FROM products
|
||||||
${whereClause}
|
${whereClause}
|
||||||
ORDER BY created_at DESC
|
|
||||||
LIMIT ${limitNum} OFFSET ${offset}
|
LIMIT ${limitNum} OFFSET ${offset}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// 确保参数数组正确传递
|
// 确保参数数组正确传递
|
||||||
const queryParams = [...params];
|
const queryParams = [...params];
|
||||||
console.log('Query params:', queryParams, 'Query:', query);
|
// console.log('Query params:', queryParams, 'Query:', query);
|
||||||
const [products] = await getDB().execute(query, queryParams);
|
const [products] = await getDB().execute(query, queryParams);
|
||||||
|
|
||||||
products.forEach(item=>{
|
products.forEach(item=>{
|
||||||
item.payment_methods = JSON.parse(item.payment_methods)
|
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({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
products,
|
products: filteredProducts,
|
||||||
|
// products: products,
|
||||||
pagination: {
|
pagination: {
|
||||||
page: pageNum,
|
page: pageNum,
|
||||||
limit: limitNum,
|
limit: limitNum,
|
||||||
@@ -75,36 +212,36 @@ router.get('/', async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 获取商品分类列表
|
// // 获取商品分类列表
|
||||||
router.get('/categories', async (req, res) => {
|
// router.get('/categories', async (req, res) => {
|
||||||
try {
|
// try {
|
||||||
const [categories] = await getDB().execute(
|
// const [categories] = await getDB().execute(
|
||||||
'SELECT DISTINCT category FROM products WHERE status = "active" AND category IS NOT NULL'
|
// 'SELECT DISTINCT category FROM products WHERE status = "active" AND category IS NOT NULL'
|
||||||
);
|
// );
|
||||||
|
|
||||||
res.json({
|
// res.json({
|
||||||
success: true,
|
// success: true,
|
||||||
data: {
|
// data: {
|
||||||
categories: categories.map(item => item.category)
|
// categories: categories.map(item => item.category)
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
console.error('获取商品分类失败:', error);
|
// console.error('获取商品分类失败:', error);
|
||||||
res.status(500).json({ success: false, message: '获取商品分类失败' });
|
// res.status(500).json({ success: false, message: '获取商品分类失败' });
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
// 获取热销商品
|
// 获取热销商品
|
||||||
router.get('/hot', async (req, res) => {
|
router.get('/hot', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// 从活跃商品中随机获取2个商品
|
// 从活跃商品中随机获取2个商品
|
||||||
const [products] = await getDB().execute(
|
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,
|
image_url, images, description, shop_name, shop_avatar,
|
||||||
payment_methods, sales, rating, status, created_at, updated_at
|
payment_methods, sales, rating, status, created_at, updated_at
|
||||||
FROM products
|
FROM products
|
||||||
WHERE status = 'active' AND stock > 0
|
WHERE status = 'active' AND stock > 0
|
||||||
ORDER BY RAND()
|
ORDER BY sales DESC
|
||||||
LIMIT 2`
|
LIMIT 2`
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -156,36 +293,42 @@ router.get('/hot', async (req, res) => {
|
|||||||
*/
|
*/
|
||||||
router.get('/cheap', async (req, res) => {
|
router.get('/cheap', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// 从活跃商品中随机获取2个商品作为秒杀商品
|
|
||||||
const [products] = await getDB().execute(
|
const [products] = await getDB().execute(
|
||||||
`SELECT id, name, category, price, points_price, rongdou_price, stock,
|
`SELECT id, start_time, end_time, flash_stock, flash_price, products_id
|
||||||
image_url, images, description, shop_name, shop_avatar,
|
FROM flash_product
|
||||||
payment_methods, sales, rating, status, created_at, updated_at
|
WHERE end_time > NOW() AND flash_stock > 0
|
||||||
FROM products
|
|
||||||
WHERE status = 'active' AND stock > 0
|
|
||||||
ORDER BY RAND()
|
ORDER BY RAND()
|
||||||
LIMIT 2`
|
LIMIT 2`
|
||||||
);
|
);
|
||||||
|
|
||||||
// 格式化商品数据,为秒杀商品添加特殊标识
|
const tempProducts = await Promise.all(products.map(async item=>{
|
||||||
const formattedProducts = products.map(product => ({
|
|
||||||
...product,
|
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] : []),
|
images: product.images ? JSON.parse(product.images) : (product.image_url ? [product.image_url] : []),
|
||||||
payment_methods: product.payment_methods ? JSON.parse(product.payment_methods) : ['points'],
|
payment_methods: product.payment_methods ? JSON.parse(product.payment_methods) : ['points'],
|
||||||
// 秒杀商品特殊处理:价格打8折
|
...item,
|
||||||
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,
|
points: product.points_price,
|
||||||
image: product.image_url
|
image: product.image_url,
|
||||||
}));
|
id: product[0].id,
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
}))
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
products: formattedProducts
|
products: tempProducts
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -336,7 +479,7 @@ router.get('/:id', async (req, res) => {
|
|||||||
const userId = req.user?.id; // 可选的用户ID,用于检查收藏状态
|
const userId = req.user?.id; // 可选的用户ID,用于检查收藏状态
|
||||||
|
|
||||||
const query = `
|
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,
|
image_url, images, videos, description, details, shop_name, shop_avatar,
|
||||||
payment_methods, sales, rating, status, created_at, updated_at
|
payment_methods, sales, rating, status, created_at, updated_at
|
||||||
FROM products
|
FROM products
|
||||||
@@ -365,6 +508,8 @@ router.get('/:id', async (req, res) => {
|
|||||||
[id]
|
[id]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// console.log(123,specCombinations);
|
||||||
|
|
||||||
// 为每个规格组合获取详细的规格值信息
|
// 为每个规格组合获取详细的规格值信息
|
||||||
const enhancedSpecifications = [];
|
const enhancedSpecifications = [];
|
||||||
for (const combination of specCombinations) {
|
for (const combination of specCombinations) {
|
||||||
@@ -437,6 +582,8 @@ router.get('/:id', async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// console.log(123,enhancedSpecifications);
|
||||||
|
|
||||||
// 获取商品属性
|
// 获取商品属性
|
||||||
const [attributes] = await getDB().execute(
|
const [attributes] = await getDB().execute(
|
||||||
'SELECT * FROM product_attributes WHERE product_id = ? ORDER BY sort_order, id',
|
'SELECT * FROM product_attributes WHERE product_id = ? ORDER BY sort_order, id',
|
||||||
@@ -525,7 +672,7 @@ router.get('/:id', async (req, res) => {
|
|||||||
// 保持向后兼容
|
// 保持向后兼容
|
||||||
points: product.points_price,
|
points: product.points_price,
|
||||||
image: product.image_url,
|
image: product.image_url,
|
||||||
tags: product.category ? [product.category] : []
|
// tags: product.category ? [product.category] : []
|
||||||
};
|
};
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
@@ -888,39 +1035,123 @@ router.get('/:id/reviews', async (req, res) => {
|
|||||||
// 获取推荐商品
|
// 获取推荐商品
|
||||||
router.get('/:id/recommended', async (req, res) => {
|
router.get('/:id/recommended', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
// 获取商品id
|
||||||
const id = parseInt(req.params.id);
|
const id = parseInt(req.params.id);
|
||||||
|
|
||||||
|
// 通过商品id获取该商品的分类id
|
||||||
|
const categoryQuery = `
|
||||||
|
SELECT category_id FROM products_category WHERE product_id = ?
|
||||||
|
`;
|
||||||
|
const [currentCategory] = await getDB().execute(categoryQuery, [id]);
|
||||||
|
|
||||||
|
let parentId = null;
|
||||||
|
let categoryIds = currentCategory.map(item => item.category_id);
|
||||||
|
|
||||||
|
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 = `
|
const query = `
|
||||||
SELECT p2.id, p2.name, p2.category, p2.price, p2.points_price as points,
|
SELECT id, name, price, points_price as points,
|
||||||
p2.stock, p2.image_url as image, p2.description
|
stock, image_url as image, description
|
||||||
FROM products p1
|
FROM products
|
||||||
JOIN products p2 ON p1.category = p2.category
|
WHERE id IN (${filteredRecommendProductIds.map(() => '?').join(',')}) AND id != ?
|
||||||
WHERE p1.id = ? AND p2.id != ? AND p2.status = 'active'
|
|
||||||
ORDER BY RAND()
|
|
||||||
LIMIT 6
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const [recommendedProducts] = await getDB().execute(query, [id, id]);
|
const [categoryProducts] = await getDB().execute(query, [...filteredRecommendProductIds, id]);
|
||||||
|
recommendedProducts = categoryProducts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 如果同类别商品不足,补充其他热门商品
|
// 如果同类别商品不足,补充其他热门商品
|
||||||
if (recommendedProducts.length < 6) {
|
if (recommendedProducts.length < 6) {
|
||||||
const remainingCount = 6 - recommendedProducts.length;
|
|
||||||
if (remainingCount > 0) {
|
let recommendQuery = `
|
||||||
const additionalQuery = `
|
SELECT products_id FROM recommend_product
|
||||||
SELECT id, name, category, price, points_price as points,
|
WHERE 1 = 1
|
||||||
stock, image_url as image, description
|
|
||||||
FROM products
|
|
||||||
WHERE id != ? AND status = 'active'
|
|
||||||
ORDER BY RAND()
|
|
||||||
LIMIT ${remainingCount}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
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(
|
const [additionalProducts] = await getDB().execute(
|
||||||
additionalQuery,
|
additionalQuery,
|
||||||
[id]
|
filteredRecommendProductIds
|
||||||
);
|
);
|
||||||
|
|
||||||
recommendedProducts.push(...additionalProducts);
|
recommendedProducts.push(...additionalProducts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ router.get('/provinces', async (req, res) => {
|
|||||||
});
|
});
|
||||||
global.provinces = regionsByLevel[1];
|
global.provinces = regionsByLevel[1];
|
||||||
}else {
|
}else {
|
||||||
console.log('1111')
|
// console.log('1111')
|
||||||
regionsByLevel[1] = global.provinces;
|
regionsByLevel[1] = global.provinces;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
96
server.js
96
server.js
@@ -88,6 +88,95 @@ const limiter = rateLimit({
|
|||||||
});
|
});
|
||||||
app.use('/api', limiter);
|
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路由之前
|
// 静态文件服务 - 必须在API路由之前
|
||||||
// 为uploads路径配置CORS和静态文件服务
|
// 为uploads路径配置CORS和静态文件服务
|
||||||
app.use('/uploads', express.static(path.join(__dirname, 'uploads'), {
|
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/wechat-pay', require('./routes/wechatPay')); // 只保留微信支付
|
||||||
app.use('/api/payment', require('./routes/payment'));
|
app.use('/api/payment', require('./routes/payment'));
|
||||||
|
|
||||||
|
// 优惠券路由
|
||||||
|
app.use('/api/coupon', require('./routes/coupon'));
|
||||||
|
|
||||||
|
// 分类路由
|
||||||
|
app.use('/api/category', require('./routes/category'));
|
||||||
|
|
||||||
|
|
||||||
// 前端路由 - 必须在最后,作为fallback
|
// 前端路由 - 必须在最后,作为fallback
|
||||||
app.get('/', (req, res) => {
|
app.get('/', (req, res) => {
|
||||||
res.removeHeader('Origin-Agent-Cluster');
|
res.removeHeader('Origin-Agent-Cluster');
|
||||||
|
|||||||
Reference in New Issue
Block a user