Compare commits

..

18 Commits

Author SHA1 Message Date
dzl
bca4b412c9 新增返回实付总额 2025-10-24 16:16:21 +08:00
dzl
2217ccf30c bug调整 2025-10-23 17:26:42 +08:00
dzl
be8b182e82 bug修复 2025-10-22 17:26:50 +08:00
dzl
27a3f564ed 添加支付接口 2025-10-21 17:29:58 +08:00
dzl
4774262178 修bug 2025-10-20 17:21:37 +08:00
dzl
e535e8a9c5 完善订单相关逻辑和商品推荐逻辑,添加接口注释 2025-10-17 17:26:37 +08:00
dzl
f9a757046c 新增优惠券的选择 2025-10-15 17:26:20 +08:00
dzl
69a445cebd 新增优惠卷管理和图片上传 2025-10-14 16:25:16 +08:00
dzl
486d3179d3 更新推荐商品逻辑 2025-10-14 09:17:47 +08:00
dzl
b512c6479d 优惠券 2025-10-13 17:28:29 +08:00
dzl
3abcd5e46a 接口更改 2025-10-11 17:33:00 +08:00
dzl
2a16a7fd97 数据更新 2025-10-10 17:33:09 +08:00
dzl
62630ef750 新增优惠券领取相关接口 2025-10-09 17:26:34 +08:00
dzl
69d2719eeb 去掉控制台输出 2025-09-28 11:51:56 +08:00
dzl
077686a0fc Merge branch 'master' of http://49.232.99.129:3000/admin/jurong_circle_black 2025-09-28 11:49:46 +08:00
dzl
305ac5dde6 调整秒杀和热销的算法 2025-09-28 11:49:36 +08:00
5d50c42e3e Merge remote-tracking branch 'origin/master' 2025-09-26 14:40:09 +08:00
8491be681b 提交 2025-09-26 14:40:02 +08:00
17 changed files with 2814 additions and 1876 deletions

View File

@@ -2,7 +2,10 @@
<project version="4">
<component name="DataSourcePerFileMappings">
<file url="file://$PROJECT_DIR$/database.js" value="5c67c46f-1d09-4751-a201-e01d3162c9fe" />
<file url="file://$PROJECT_DIR$/routes/matching.js" value="5c67c46f-1d09-4751-a201-e01d3162c9fe" />
<file url="file://$PROJECT_DIR$/routes/transfers.js" value="5c67c46f-1d09-4751-a201-e01d3162c9fe" />
<file url="file://$PROJECT_DIR$/routes/users.js" value="5c67c46f-1d09-4751-a201-e01d3162c9fe" />
<file url="file://$PROJECT_DIR$/services/matchingService.js" value="5c67c46f-1d09-4751-a201-e01d3162c9fe" />
<file url="file://$PROJECT_DIR$/services/transferService.js" value="5c67c46f-1d09-4751-a201-e01d3162c9fe" />
</component>
</project>

6
.idea/sqldialects.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="PROJECT" dialect="MySQL" />
</component>
</project>

View File

@@ -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
View 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
View 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;

View File

@@ -310,12 +310,12 @@ router.post('/confirm-allocation/:allocationId', auth, async (req, res) => {
}
// 调用服务层方法,传递完整的转账信息
const transferId = await matchingService.confirmAllocation(
allocationId,
userId,
transferAmount,
description,
voucher
const transferId = matchingService.confirmAllocation(
allocationId,
userId,
transferAmount,
description,
voucher
);
res.json({
@@ -324,11 +324,11 @@ router.post('/confirm-allocation/:allocationId', auth, async (req, res) => {
data: { transferId }
});
axios.post('http://localhost:5000/ocr',{
id: allocationId
}).then(res => {
console.log(res.data)
})
// axios.post('http://localhost:5000/ocr',{
// id: allocationId
// }).then(res => {
// console.log(res.data)
// })
} catch (error) {
console.error('确认分配失败:', error);

View File

@@ -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(
@@ -1143,82 +1211,209 @@ router.post('/confirm-payment', auth, async (req, res) => {
}
user.balance = Math.abs(user.balance);
// 根据支付方式处理扣费逻辑
let totalRongdouNeeded = order.total_rongdou; // 需要的融豆总数
let pointsToDeduct = 0; // 需要扣除的积分
let rongdouToDeduct = 0; // 需要扣除的融豆
console.log(123456,paymentMethod)
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) {
// 开始扣钱
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({

View File

@@ -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

View File

@@ -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,11 +122,6 @@ router.get('/', async (req, res) => {
params.push(`%${search}%`);
}
if (category) {
whereClause += ' AND category = ?';
params.push(category);
}
if (status) {
whereClause += ' AND status = ?';
params.push(status);
@@ -36,6 +129,21 @@ router.get('/', async (req, res) => {
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 [countResult] = await getDB().execute(countQuery, params);
@@ -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
@@ -365,6 +508,8 @@ router.get('/:id', async (req, res) => {
[id]
);
// console.log(123,specCombinations);
// 为每个规格组合获取详细的规格值信息
const enhancedSpecifications = [];
for (const combination of specCombinations) {
@@ -437,6 +582,8 @@ router.get('/:id', async (req, res) => {
}
}
// console.log(123,enhancedSpecifications);
// 获取商品属性
const [attributes] = await getDB().execute(
'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,
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(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 IN (${filteredRecommendProductIds.map(() => '?').join(',')}) AND id != ?
`;
const [categoryProducts] = await getDB().execute(query, [...filteredRecommendProductIds, id]);
recommendedProducts = categoryProducts;
}
}
// 如果同类别商品不足,补充其他热门商品
if (recommendedProducts.length < 6) {
const remainingCount = 6 - recommendedProducts.length;
if (remainingCount > 0) {
const additionalQuery = `
SELECT id, name, category, price, points_price as points,
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 != ? AND status = 'active'
ORDER BY RAND()
LIMIT ${remainingCount}
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]
filteredRecommendProductIds
);
recommendedProducts.push(...additionalProducts);
}
}

View File

@@ -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;
}

View File

@@ -141,11 +141,6 @@ router.post('/send', async (req, res) => {
// 生产环境发送真实短信
try {
console.log(code);
res.json({
success: true,
message: '验证码发送成功'
})
return
const sendSmsRequest = new Dysmsapi20170525.SendSmsRequest({
phoneNumbers: phone,
signName: SMS_CONFIG.signName,

View File

@@ -12,6 +12,48 @@ const dayjs = require('dayjs');
const router = express.Router();
// router.get('/tmp', async (req, res) => {
// const db = getDB();
// // 1. 查询所有转账记录,按 id 升序
// let [transfers] = await db.execute(`
// SELECT *
// FROM transfers
// WHERE status='received' or status='confirmed'
// ORDER BY id ASC`);
//
// // 2. 用对象维护每个用户的最新余额
// const userBalances = {};
//
// // 3. 遍历每条转账记录,计算余额
// for (const trx of transfers) {
// const fromId = trx.from_user_id;
// const toId = trx.to_user_id;
// const amount = Number(trx.amount);
//
// if (!(fromId in userBalances)) userBalances[fromId] = 0;
// if (!(toId in userBalances)) userBalances[toId] = 0;
//
// const fromBalance = userBalances[fromId] - amount;
// const toBalance = userBalances[toId] + amount;
//
// userBalances[fromId] = fromBalance;
// userBalances[toId] = toBalance;
//
// const balanceDiff = toBalance - fromBalance;
//
// // 4. 更新当前行的字段
// await db.execute(
// `UPDATE transfers
// SET from_user_balance = ?,
// to_user_balance = ?,
// balance_diff = ?
// WHERE id = ?`,
// [fromBalance, toBalance, amount, trx.id]
// );
//
// }
// res.json('更新成功')
// })
router.get('/',
authenticateToken,
validateQuery(transferSchemas.query),
@@ -342,7 +384,6 @@ router.post('/confirm-not-received',
);
// 获取用户转账记录
router.get('/user/:userId', authenticateToken, async (req, res) => {
try {
@@ -386,8 +427,9 @@ router.get('/user/:userId', authenticateToken, async (req, res) => {
LEFT JOIN users to_user ON t.to_user_id = to_user.id
${whereClause}
ORDER BY t.created_at
DESC
LIMIT ${limitNum} OFFSET ${offset}
DESC
LIMIT ${limitNum}
OFFSET ${offset}
`, countParams);
const [countResult] = await db.execute(`
@@ -425,37 +467,37 @@ router.get('/stats', authenticateToken, async (req, res) => {
if (isAdmin) {
// 管理员可以查看全局统计
const [totalStats] = await db.execute(`
SELECT COUNT(*) as total_transfers,
SUM(amount) as total_flow_amount,
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_count,
SUM(CASE WHEN status = 'confirmed' THEN 1 ELSE 0 END) as confirmed_count,
SUM(CASE WHEN status = 'received' THEN 1 ELSE 0 END) as received_count,
SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) as rejected_count,
SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) as cancelled_count,
SUM(CASE WHEN status = 'not_received' THEN 1 ELSE 0 END) as not_received_count,
SUM(CASE WHEN is_overdue = 1 THEN 1 ELSE 0 END) as overdue_count,
SUM(CASE WHEN is_bad_debt = 1 THEN 1 ELSE 0 END) as bad_debt_count,
SUM(CASE WHEN status = 'confirmed' THEN amount ELSE 0 END) as total_amount,
SUM(CASE WHEN is_bad_debt = 1 THEN amount ELSE 0 END) as bad_debt_amount,
SELECT COUNT(*) as total_transfers,
SUM(amount) as total_flow_amount,
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_count,
SUM(CASE WHEN status = 'confirmed' THEN 1 ELSE 0 END) as confirmed_count,
SUM(CASE WHEN status = 'received' THEN 1 ELSE 0 END) as received_count,
SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) as rejected_count,
SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) as cancelled_count,
SUM(CASE WHEN status = 'not_received' THEN 1 ELSE 0 END) as not_received_count,
SUM(CASE WHEN is_overdue = 1 THEN 1 ELSE 0 END) as overdue_count,
SUM(CASE WHEN is_bad_debt = 1 THEN 1 ELSE 0 END) as bad_debt_count,
SUM(CASE WHEN status = 'confirmed' THEN amount ELSE 0 END) as total_amount,
SUM(CASE WHEN is_bad_debt = 1 THEN amount ELSE 0 END) as bad_debt_amount,
SUM(CASE
WHEN transfer_type = 'initial' AND status = 'confirmed' THEN amount
ELSE 0 END) as initial_amount,
ELSE 0 END) as initial_amount,
SUM(CASE
WHEN transfer_type = 'return' AND status = 'confirmed' THEN amount
ELSE 0 END) as return_amount,
ELSE 0 END) as return_amount,
SUM(CASE
WHEN transfer_type = 'user_to_user' AND status = 'confirmed' THEN amount
ELSE 0 END) as user_to_user_amount,
ELSE 0 END) as user_to_user_amount,
(SELECT SUM(balance)
FROM users
WHERE role = 'user'
AND is_system_account = 1) as total_merchant_balance,
AND is_system_account = 1) as total_merchant_balance,
(SELECT SUM(balance)
FROM users
WHERE role = 'user'
AND is_system_account != 1) as total_user_balance,
SUM(CASE WHEN source_type IN ('system') THEN amount END) as participated_transfers,
SUM(CASE WHEN source_type IN ('agent') THEN amount END) as agent_total,
SUM(CASE WHEN source_type IN ('system') THEN amount END) as participated_transfers,
SUM(CASE WHEN source_type IN ('agent') THEN amount END) as agent_total,
SUM(CASE WHEN source_type IN ('operated_agent') THEN amount END) as operated_agent_total
FROM transfers
`);
@@ -470,26 +512,26 @@ router.get('/stats', authenticateToken, async (req, res) => {
(
COALESCE((SELECT SUM(amount)
FROM transfers
WHERE DATE(created_at) = ?
AND to_user_id IN (SELECT id FROM users WHERE is_system_account = 1)
AND status = 'received'), 0) -
WHERE DATE (created_at) = ?
AND to_user_id IN (SELECT id FROM users WHERE is_system_account = 1)
AND status = 'received'), 0) -
COALESCE((SELECT SUM(amount)
FROM transfers
WHERE DATE(created_at) = ?
AND from_user_id IN (SELECT id FROM users WHERE is_system_account = 1)
AND status = 'received'), 0)
WHERE DATE (created_at) = ?
AND from_user_id IN (SELECT id FROM users WHERE is_system_account = 1)
AND status = 'received'), 0)
) as today_amount
FROM transfers
WHERE DATE(created_at) = ?
WHERE DATE (created_at) = ?
`, [todayStr, todayStr, todayStr]);
const [monthlyStats] = await db.execute(`
SELECT COUNT(*) as monthly_transfers,
SUM(CASE WHEN status = 'received' THEN amount ELSE 0 END) as monthly_amount,
SUM(CASE WHEN source_type IN ('system') THEN amount END) as monthly_participated_transfers
SELECT COUNT(*) as monthly_transfers,
SUM(CASE WHEN status = 'received' THEN amount ELSE 0 END) as monthly_amount,
SUM(CASE WHEN source_type IN ('system') THEN amount END) as monthly_participated_transfers
FROM transfers
WHERE YEAR(created_at) = ?
AND MONTH(created_at) = ?
WHERE YEAR (created_at) = ?
AND MONTH (created_at) = ?
`, [currentYear, currentMonth]);
// 获取上月统计数据用于对比
@@ -497,12 +539,12 @@ router.get('/stats', authenticateToken, async (req, res) => {
const lastMonthYear = currentMonth === 1 ? currentYear - 1 : currentYear;
const [lastMonthStats] = await db.execute(`
SELECT COUNT(*) as last_monthly_transfers,
SUM(CASE WHEN status = 'confirmed' THEN amount ELSE 0 END) as last_monthly_amount,
SUM(CASE WHEN source_type IN ('system') THEN amount END) as last_monthly_participated_transfers
SELECT COUNT(*) as last_monthly_transfers,
SUM(CASE WHEN status = 'confirmed' THEN amount ELSE 0 END) as last_monthly_amount,
SUM(CASE WHEN source_type IN ('system') THEN amount END) as last_monthly_participated_transfers
FROM transfers
WHERE YEAR(created_at) = ?
AND MONTH(created_at) = ?
WHERE YEAR (created_at) = ?
AND MONTH (created_at) = ?
`, [lastMonthYear, lastMonth]);
stats = {
@@ -524,9 +566,9 @@ router.get('/stats', authenticateToken, async (req, res) => {
return_amount: parseFloat(totalStats[0].return_amount || 0),
user_to_user_amount: parseFloat(totalStats[0].user_to_user_amount || 0),
participated_transfers: totalStats[0].participated_transfers || 0,
total_user_balance:totalStats[0].total_user_balance || 0,
agent_total:totalStats[0].agent_total || 0,//代理收入
operated_agent_total:totalStats[0].operated_agent_total || 0,//直营代理收入
total_user_balance: totalStats[0].total_user_balance || 0,
agent_total: totalStats[0].agent_total || 0,//代理收入
operated_agent_total: totalStats[0].operated_agent_total || 0,//直营代理收入
},
today: {
transfers: todayStats[0].today_transfers || 0,
@@ -565,7 +607,7 @@ router.get('/stats', authenticateToken, async (req, res) => {
SUM(CASE WHEN status = 'confirmed' AND to_user_id = ? THEN amount ELSE 0 END) as today_received
FROM transfers
WHERE (from_user_id = ? OR to_user_id = ?)
AND DATE(created_at) = ?
AND DATE (created_at) = ?
`, [userId, userId, userId, userId, todayStr]);
stats = {
@@ -706,9 +748,9 @@ router.get('/trend', authenticateToken, async (req, res) => {
// 首先获取数据库中最早和最晚的转账日期
const [dateRange] = await db.execute(`
SELECT MIN(DATE(created_at)) as min_date,
MAX(DATE(created_at)) as max_date,
COUNT(*) as total_count
SELECT MIN(DATE (created_at)) as min_date,
MAX(DATE (created_at)) as max_date,
COUNT(*) as total_count
FROM transfers
`);
@@ -737,13 +779,13 @@ router.get('/trend', authenticateToken, async (req, res) => {
// 获取指定天数内的转账趋势(从最大日期往前推)
const [trendData] = await db.execute(`
SELECT DATE(created_at) as date,
COUNT(*) as count,
SUM(amount) as amount
SELECT DATE (created_at) as date, COUNT (*) as count, SUM (amount) as amount
FROM transfers
WHERE DATE(created_at) >= DATE_SUB(?, INTERVAL ? DAY)
AND status IN ('confirmed', 'received')
GROUP BY DATE(created_at)
WHERE DATE (created_at) >= DATE_SUB(?
, INTERVAL ? DAY)
AND status IN ('confirmed'
, 'received')
GROUP BY DATE (created_at)
ORDER BY date ASC
`, [dateRange[0].max_date, daysNum - 1]);
@@ -1024,7 +1066,8 @@ router.get('/pending-allocations',
JOIN matching_orders mo ON oa.id = mo.id
${whereClause}
ORDER BY oa.${sortField} ${sortOrder}
LIMIT ${parseInt(limit)} OFFSET ${parseInt(offset)}
LIMIT ${parseInt(limit)}
OFFSET ${parseInt(offset)}
`;
const [allocations] = queryParams.length > 0
@@ -1140,19 +1183,16 @@ router.get('/daily-stats',
// 获取所有用户的昨日转出和今日入账统计
let [userStats] = await db.execute(`
SELECT u.id as user_id,
SELECT u.id as user_id,
u.username,
u.real_name,
u.phone,
u.balance,
COALESCE(yesterday_out.amount, 0) as yesterday_out_amount,
COALESCE(today_in.amount, 0) as today_in_amount,
COALESCE(confirmed_from.confirmed_amount, 0) as confirmed_from_amount,
CASE
WHEN (COALESCE(u.balance, 0) + COALESCE(confirmed_from.confirmed_amount, 0)) > ABS(u.balance)
THEN ABS(u.balance)
ELSE (COALESCE(u.balance, 0) + COALESCE(confirmed_from.confirmed_amount, 0))
END as balance_needed
COALESCE(yesterday_out.amount, 0) -
COALESCE(yesterday_system_num.amount, 0) as yesterday_out_amount,
COALESCE(today_in.amount, 0) as today_in_amount,
COALESCE(confirmed_from.confirmed_amount, 0) as confirmed_from_amount,
COALESCE(u.balance, 0) + COALESCE(confirmed_from.confirmed_amount, 0) as balance_needed
FROM users u
LEFT JOIN (SELECT from_user_id,
SUM(amount) as amount
@@ -1161,6 +1201,15 @@ router.get('/daily-stats',
AND created_at <= ?
AND status IN ('confirmed', 'received')
GROUP BY from_user_id) yesterday_out ON u.id = yesterday_out.from_user_id
LEFT JOIN (SELECT to_user_id,
SUM(amount) as amount
FROM transfers
WHERE created_at >= ?
AND created_at <= ?
AND transfer_type != 'user_to_user'
AND status IN ('received')
GROUP BY to_user_id) yesterday_system_num
ON u.id = yesterday_system_num.to_user_id
LEFT JOIN (SELECT to_user_id,
SUM(amount) as amount
FROM transfers
@@ -1171,17 +1220,17 @@ router.get('/daily-stats',
left join (select from_user_id,
sum(amount) as confirmed_amount
from transfers
where status = 'received'
and created_at >= ?
and created_at <= ?
group by from_user_id) as confirmed_from on u.id = confirmed_from.from_user_id
WHERE u.role != 'admin'
WHERE status IN ('confirmed', 'received')
AND created_at >= ?
AND created_at <= ?
GROUP BY from_user_id) as confirmed_from on u.id = confirmed_from.from_user_id
WHERE u.role != 'admin' AND u.user_type !='directly_operated'
AND u.is_system_account != 1
AND yesterday_out.amount > 0
AND u.balance < 0
ORDER BY balance_needed DESC, yesterday_out_amount DESC
`, [yesterdayStartStr, yesterdayEndStr, todayStartStr, todayEndStr, todayStartStr, todayEndStr]);
// userStats = userStats.filter(item=>item.balance_needed >= 100)
`, [yesterdayStartStr, yesterdayEndStr, yesterdayStartStr, yesterdayEndStr,todayStartStr, todayEndStr, todayStartStr, todayEndStr]);
// userStats = userStats.filter(item=>item.balance_needed > 100)
userStats.forEach(item => {
item.balance_needed = Math.abs(item.balance_needed)
})

View File

@@ -51,54 +51,6 @@ const multiUpload = multer({
}
});
/**
* @swagger
* /upload/image:
* post:
* summary: 上传图片
* tags: [Upload]
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* multipart/form-data:
* schema:
* type: object
* properties:
* file:
* type: string
* format: binary
* description: 要上传的图片文件
* type:
* type: string
* enum: [avatar, product, document]
* default: document
* description: 上传文件类型
* responses:
* 200:
* description: 图片上传成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* url:
* type: string
* description: 上传后的文件URL
* filename:
* type: string
* description: 上传后的文件名
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.post('/image', authenticateToken, (req, res) => {
upload.single('file')(req, res, async (err) => {
if (err instanceof multer.MulterError) {

View File

@@ -215,7 +215,7 @@ router.get('/', auth, adminAuth, async (req, res) => {
// 确保参数为有效数字
const pageNum = Math.max(1, parseInt(page) || 1);
const limitNum = Math.max(1, Math.min(100, parseInt(limit) || 10));
const limitNum = Math.max(1, parseInt(limit) || 10);
const offset = Math.max(0, (pageNum - 1) * limitNum);
let whereConditions = [];
@@ -504,7 +504,10 @@ router.get('/stats', auth, async (req, res) => {
// 活跃用户数(有订单的用户)
const [activeUsers] = await db.execute(
'SELECT COUNT(DISTINCT from_user_id) as count FROM transfers'
`SELECT
COUNT(DISTINCT from_user_id) AS count
FROM transfers
WHERE YEARWEEK(created_at, 1) = YEARWEEK(CURDATE() - INTERVAL 1 WEEK, 1)`
);
const [weekUsers] = await db.execute(`
SELECT COUNT(DISTINCT from_user_id) as count
@@ -1546,20 +1549,52 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
[distribute.inviter]
);
distributeUser = distributeUser[0]
//分销是代理
if (distributeUser.user_type == 'agent') {
//给代理添加2980融豆的70% 增加区域保护
await db.execute(
'UPDATE users SET balance = balance - ? WHERE id = ?',
[serviceFee * 0.7, distributeUser.id]
);
//记录转账记录
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_agent', 'received', serviceFee * 0.7, '用户服务费返现', 'agent']
);
let [agentUser] = await db.execute(`
SELECT r.*
FROM regional_agents as r
LEFT JOIN users au on r.user_id = au.id
WHERE au.user_type = 'agent'
AND r.status = 'active'
AND r.region_id = ${user.district_id}
`)
if (agentUser.length > 0 && agentUser[0].user_id !== distributeUser.id) {
//增加区域保护
await db.execute(`
UPDATE users
SET balance = balance - ?
WHERE id = ?
`, [serviceFee * 0.05, agentUser[0].user_id])
await db.execute(
'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, agentUser[0].user_id, 'user_to_agent', 'received', serviceFee * 0.05, '区域保护服务费返现', 'agent']
);
await db.execute(
'UPDATE users SET balance = balance - ? WHERE id = ?',
[serviceFee * 0.65, distributeUser.id]
);
//记录转账记录
await db.execute(
'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_agent', 'received', serviceFee * 0.65, '用户服务费返现', 'agent']
);
} else {
//给代理添加2980融豆的70% 增加区域保护
await db.execute(
'UPDATE users SET balance = balance - ? WHERE id = ?',
[serviceFee * 0.7, distributeUser.id]
);
//记录转账记录
await db.execute(
'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_agent', 'received', serviceFee * 0.7, '用户服务费返现', 'agent']
);
}
//记录平台利润
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.3, '用户服务费返现', 'system']
);
//记录服务费
@@ -1577,12 +1612,12 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
);
//记录转账记录
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_agent', 'received', serviceFee * 0.5, '用户服务费返现', 'agent_operated']
);
//记录平台利润
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.5, '用户服务费返现', 'system']
);
//记录服务费
@@ -1615,17 +1650,17 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
);
//记录转账记录
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.inviter, 'user_to_agent', 'received', serviceFee * 0.2, '用户服务费返现', 'operated_agent']
);
//记录直营利润
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id, from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_operated', 'received', serviceFee * 0.3, '用户服务费返现', 'directly_operated']
);
//记录平台利润
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.5, '用户服务费返现', 'system']
);
}
@@ -1642,17 +1677,17 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
);
//记录转账记录
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.inviter, 'user_to_agent', 'received', serviceFee * 0.15, '用户服务费返现', 'agent_operated']
);
//记录直营利润
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_operated', 'received', serviceFee * 0.35, '用户服务费返现', 'directly_operated']
);
//记录平台利润
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.5, '用户服务费返现', 'system']
);
}
@@ -1669,7 +1704,7 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
);
//记录转账记录
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id ,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.inviter, 'user_to_agent', 'received', serviceFee * 0.1, '用户服务费返现', 'agent_operated']
);
//记录直营利润
@@ -1679,7 +1714,7 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
);
//记录平台利润
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.5, '用户服务费返现', 'system']
);
}
@@ -1701,29 +1736,61 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
userUpInfo = userUpInfo[0]
//判断用户上级是否是代理
if (userUpInfo && userUpInfo.user_type === 'agent') {
let [agentUser] = await db.execute(`
SELECT r.*
FROM regional_agents as r
LEFT JOIN users au on r.user_id = au.id
WHERE au.user_type = 'agent'
AND r.status = 'active'
AND r.region_id = ${user.district_id}
`)
if (agentUser.length > 0 && agentUser[0].user_id !== distributeUser.id) {
//增加区域保护
await db.execute(`
UPDATE users
SET balance = balance - ?
WHERE id = ?
`, [serviceFee * 0.05, agentUser[0].user_id])
await db.execute(
'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, agentUser[0].user_id, 'user_to_agent', 'received', serviceFee * 0.05, '区域保护服务费返现', 'agent']
);
//给代理分配
await db.execute(
'UPDATE users SET balance = balance - ? WHERE id = ?',
[serviceFee * 0.45, userUpInfo.id]
);
//记录代理转账信息
await db.execute(
'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, userUpInfo.id, 'user_to_agent', 'received', serviceFee * 0.45, '用户服务费返现', 'agent']
);
}else {
//给代理分配
await db.execute(
'UPDATE users SET balance = balance - ? WHERE id = ?',
[serviceFee * 0.5, userUpInfo.id]
);
//记录代理转账信息
await db.execute(
'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, userUpInfo.id, 'user_to_agent', 'received', serviceFee * 0.5, '用户服务费返现', 'agent']
);
}
//给用户分配
await db.execute(
'UPDATE users SET balance = balance - ? WHERE id = ?',
[serviceFee * 0.2, distributeUser.id]
);
//给代理分配
await db.execute(
'UPDATE users SET balance = balance - ? WHERE id = ?',
[serviceFee * 0.5, userUpInfo.id]
);
//记录用户转账信息
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id ,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_user', 'received', serviceFee * 0.2, '用户服务费返现', 'operated']
);
//记录代理转账信息
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, userUpInfo.id, 'user_to_agent', 'received', serviceFee * 0.5, '用户服务费返现', 'agent']
);
//记录平台利润
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.3, '用户服务费返现', 'system']
);
} else {
@@ -1734,12 +1801,12 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
);
//记录转账记录
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_user', 'received', serviceFee * 0.2, '用户服务费返现', 'operated']
);
//记录平台利润
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id ,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.8, '用户服务费返现', 'system']
);
@@ -1754,25 +1821,34 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
} else {
//判断用户此区域是否有代理
let [agentUser] = await db.execute(`
SELECT user_id
FROM regional_agents
WHERE region_id = ?
`, [users[0].district_id])
SELECT rg.user_id
FROM regional_agents as rg
LEFT JOIN users ag ON ag.id = rg.user_id
WHERE rg.region_id = ?
AND rg.status = 'active'
AND ag.user_type = 'agent'
`, [user.district_id])
if (agentUser.length > 0) {
//给区域代理分区域保护
await db.execute(`
UPDATE users
SET balance = balance - ?
WHERE id = ?
`, [serviceFee * 0.05, agentUser[0].user_id])
//给代理分成5%
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id ,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, agentUser[0].user_id, 'user_to_regional', 'received', serviceFee * 0.05, '区域保护服务费返现', 'agent']
)
//记录平台利润
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.95, '用户服务费返现', 'system']
);
} else {
//记录平台利润
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
'INSERT INTO transfers (to_user_id ,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, 3512, 'user_to_system', 'received', serviceFee, '用户服务费返现', 'system']
);

View File

@@ -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');

File diff suppressed because it is too large Load Diff

View File

@@ -241,7 +241,7 @@ class TransferService {
// 获取转账列表
async getTransfers(filters = {}, pagination = {}, user_type = 'user_to_user') {
const db = getDB();
const {page = 1, limit = 10, sort = 'created_at', order = 'desc'} = pagination;
const {page = 1, limit = 10, sort = 'id', order = 'desc'} = pagination;
const pageNum = parseInt(page, 10) || 1;
const limitNum = parseInt(limit, 10) || 10;
const offset = (pageNum - 1) * limitNum;
@@ -283,7 +283,7 @@ class TransferService {
// 构建排序子句
const validSortFields = ['id', 'amount', 'created_at', 'updated_at', 'status'];
const sortField = validSortFields.includes(sort) ? sort : 'created_at';
const sortField = validSortFields.includes(sort) ? sort : 'id';
const sortOrder = order && order.toLowerCase() === 'asc' ? 'ASC' : 'DESC';
const orderClause = `ORDER BY t.${sortField} ${sortOrder}`;
@@ -304,6 +304,8 @@ class TransferService {
`SELECT t.*,
fu.username as from_username,
fu.real_name as from_real_name,
fu.balance as from_balance,
tu.balance as to_balance,
tu.username as to_username,
tu.real_name as to_real_name,
f_p.name as from_province,
@@ -322,7 +324,7 @@ class TransferService {
LEFT JOIN china_regions t_c ON t_c.code = tu.city
LEFT JOIN china_regions t_d ON t_d.code = tu.district_id
${whereClause} ${orderClause}
LIMIT ${limitNum}`,
LIMIT ${limitNum} OFFSET ${offset}`,
params
);