1892 lines
72 KiB
JavaScript
1892 lines
72 KiB
JavaScript
const express = require('express');
|
||
const bcrypt = require('bcryptjs');
|
||
const {getDB} = require('../database');
|
||
const {auth, adminAuth} = require('../middleware/auth');
|
||
const dayjs = require('dayjs');
|
||
|
||
const router = express.Router();
|
||
|
||
/**
|
||
* @swagger
|
||
* tags:
|
||
* name: Users
|
||
* description: 用户管理API
|
||
*/
|
||
|
||
router.post('/', auth, adminAuth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
await db.query('START TRANSACTION');
|
||
|
||
const {
|
||
username,
|
||
password,
|
||
role = 'user',
|
||
isSystemAccount = false, // 是否为虚拟商户
|
||
realName,
|
||
idCard,
|
||
wechatQr,
|
||
alipayQr,
|
||
bankCard,
|
||
unionpayQr,
|
||
province,
|
||
city,
|
||
districtId,
|
||
phone,
|
||
avatar,
|
||
user_type = 'directly_operated',
|
||
inviter = null
|
||
} = req.body;
|
||
|
||
if (!username || !password) {
|
||
return res.status(400).json({success: false, message: '用户名和密码不能为空'});
|
||
}
|
||
|
||
if (!realName || !idCard) {
|
||
return res.status(400).json({success: false, message: '姓名和身份证号不能为空'});
|
||
}
|
||
if (!city || !districtId || !province) {
|
||
return res.status(400).json({success: false, message: '请选择城市和区县'});
|
||
}
|
||
|
||
// 验证身份证号格式
|
||
const idCardRegex = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
|
||
if (!idCardRegex.test(idCard)) {
|
||
return res.status(400).json({success: false, message: '身份证号格式不正确'});
|
||
}
|
||
|
||
// 检查用户是否已存在
|
||
const [existingUsers] = await db.execute(
|
||
'SELECT id FROM users WHERE username = ? OR id_card = ? OR (phone IS NOT NULL AND phone = ?)',
|
||
[username, idCard, phone || null]
|
||
);
|
||
|
||
if (existingUsers.length > 0) {
|
||
return res.status(400).json({success: false, message: '用户名、身份证号或手机号已存在'});
|
||
}
|
||
|
||
// 加密密码
|
||
const hashedPassword = await bcrypt.hash(password, 10);
|
||
|
||
// 创建用户
|
||
console.log([username, hashedPassword, role, isSystemAccount, 0, realName, idCard, wechatQr, alipayQr, bankCard, unionpayQr, phone, province, city, districtId, user_type, inviter], 'info');
|
||
|
||
const [result] = await db.execute(
|
||
'INSERT INTO users (username, password, role, is_system_account, points, real_name, id_card, wechat_qr, alipay_qr, bank_card, unionpay_qr, phone, province, city, district_id, user_type, inviter,avatar) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?,?)',
|
||
[username, hashedPassword, role, isSystemAccount, 0, realName, idCard, wechatQr, alipayQr, bankCard, unionpayQr, phone, province, city, districtId, user_type, inviter, avatar]
|
||
);
|
||
|
||
const userId = result.insertId;
|
||
if (user_type === 'agent_directly') {
|
||
const agentCode = 'AG' + Date.now().toString().slice(-8);
|
||
await db.execute(
|
||
'INSERT INTO regional_agents (user_id, region_id,status,agent_code) VALUES (?, ?,?,?)',
|
||
[userId, districtId, 'active', agentCode]
|
||
);
|
||
await db.execute(
|
||
`UPDATE users
|
||
SET payment_status='paid'
|
||
WHERE id = ${userId}`
|
||
)
|
||
await db.execute(`
|
||
INSERT INTO distribution (user_id, amount, is_offline, type)
|
||
VALUES (${userId}, 2980, 1, 'system')
|
||
`)
|
||
}
|
||
|
||
// 用户余额已在创建用户时设置为默认值0.00,无需额外操作
|
||
|
||
await db.query('COMMIT');
|
||
|
||
// 返回创建的用户信息(不包含密码)
|
||
const [newUser] = await db.execute(
|
||
'SELECT id, username, role, avatar, points, real_name, phone, created_at, updated_at FROM users WHERE id = ?',
|
||
[userId]
|
||
);
|
||
|
||
res.status(201).json({
|
||
success: true,
|
||
message: '用户创建成功',
|
||
user: newUser[0]
|
||
});
|
||
} catch (error) {
|
||
try {
|
||
await getDB().query('ROLLBACK');
|
||
} catch (rollbackError) {
|
||
console.error('回滚错误:', rollbackError);
|
||
}
|
||
console.error('创建用户错误:', error);
|
||
res.status(500).json({success: false, message: '创建用户失败'});
|
||
}
|
||
});
|
||
|
||
|
||
router.get('/pending-audit', auth, adminAuth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
const {page = 1, limit = 10} = req.query;
|
||
const pageNum = parseInt(page) || 1;
|
||
const limitNum = parseInt(limit) || 10;
|
||
const offset = (pageNum - 1) * limitNum;
|
||
|
||
// 获取待审核用户总数
|
||
const [countResult] = await db.execute(
|
||
'SELECT COUNT(*) as total FROM users WHERE audit_status = ?',
|
||
['pending']
|
||
);
|
||
const total = countResult[0].total;
|
||
|
||
// 获取待审核用户列表
|
||
const [users] = await db.execute(
|
||
`SELECT id,
|
||
username,
|
||
phone,
|
||
real_name,
|
||
business_license,
|
||
id_card_front,
|
||
id_card_back,
|
||
wechat_qr,
|
||
alipay_qr,
|
||
unionpay_qr,
|
||
bank_card,
|
||
audit_status,
|
||
created_at
|
||
FROM users
|
||
WHERE audit_status = ?
|
||
ORDER BY created_at ASC
|
||
LIMIT ${limitNum} OFFSET ${offset}`,
|
||
['pending']
|
||
);
|
||
|
||
res.json({
|
||
success: true,
|
||
data: {
|
||
users,
|
||
pagination: {
|
||
page: pageNum,
|
||
limit: limitNum,
|
||
total,
|
||
pages: Math.ceil(total / limitNum)
|
||
}
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('获取待审核用户列表错误:', error);
|
||
res.status(500).json({success: false, message: '获取待审核用户列表失败'});
|
||
}
|
||
});
|
||
|
||
// 获取用户列表用于转账(普通用户权限)
|
||
router.get('/for-transfer', auth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
|
||
// 获取所有用户的基本信息(用于转账选择)
|
||
const [users] = await db.execute(
|
||
'SELECT id, username, real_name FROM users WHERE id != ? ORDER BY username',
|
||
[req.user.id]
|
||
);
|
||
|
||
res.json({
|
||
success: true,
|
||
data: users
|
||
});
|
||
} catch (error) {
|
||
console.error('获取转账用户列表错误:', error);
|
||
res.status(500).json({success: false, message: '获取用户列表失败'});
|
||
}
|
||
});
|
||
|
||
// 获取用户列表(管理员权限)
|
||
router.get('/', auth, adminAuth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
const {
|
||
page = 1,
|
||
limit = 10,
|
||
search = '',
|
||
role = '',
|
||
city = '',
|
||
district = '',
|
||
province = '',
|
||
sort = 'created_at',
|
||
order = 'desc'
|
||
} = req.query;
|
||
|
||
// 确保参数为有效数字
|
||
const pageNum = Math.max(1, parseInt(page) || 1);
|
||
const limitNum = Math.max(1, parseInt(limit) || 10);
|
||
const offset = Math.max(0, (pageNum - 1) * limitNum);
|
||
|
||
let whereConditions = [];
|
||
let countParams = [];
|
||
let listParams = [];
|
||
|
||
// 构建查询条件
|
||
if (search) {
|
||
whereConditions.push('(u.username LIKE ? OR u.real_name LIKE ?)');
|
||
countParams.push(`%${search}%`, `%${search}%`);
|
||
listParams.push(`%${search}%`, `%${search}%`);
|
||
}
|
||
|
||
if (role && role !== 'all') {
|
||
whereConditions.push('u.role = ?');
|
||
countParams.push(role);
|
||
listParams.push(role);
|
||
}
|
||
|
||
if (city) {
|
||
whereConditions.push('u.city = ?');
|
||
countParams.push(city);
|
||
listParams.push(city);
|
||
}
|
||
|
||
if (province) {
|
||
whereConditions.push('u.province = ?');
|
||
countParams.push(province);
|
||
listParams.push(province);
|
||
}
|
||
|
||
if (district) {
|
||
whereConditions.push('u.district_id = ?');
|
||
countParams.push(district);
|
||
listParams.push(district);
|
||
}
|
||
|
||
const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : '';
|
||
|
||
// 添加分页参数
|
||
listParams.push(limitNum.toString(), offset.toString());
|
||
|
||
|
||
// 获取总数
|
||
const [countResult] = await db.execute(
|
||
`SELECT COUNT(*) as total
|
||
FROM users u
|
||
LEFT JOIN zhejiang_regions r ON u.district_id = r.id
|
||
${whereClause}`,
|
||
countParams
|
||
);
|
||
|
||
// 验证排序字段,防止SQL注入
|
||
const validSortFields = ['id', 'username', 'role', 'points', 'balance', 'created_at', 'updated_at'];
|
||
const sortField = validSortFields.includes(sort) ? sort : 'created_at';
|
||
|
||
// 验证排序方向
|
||
const sortOrder = (order && (order.toUpperCase() === 'ASC' || order.toUpperCase() === 'DESC'))
|
||
? order.toUpperCase()
|
||
: 'DESC';
|
||
|
||
// 获取用户列表,关联地区信息和转账统计
|
||
const [users] = await db.execute(
|
||
`SELECT u.id,
|
||
u.username,
|
||
u.role,
|
||
u.avatar,
|
||
u.points,
|
||
u.balance,
|
||
u.real_name,
|
||
u.id_card,
|
||
u.phone,
|
||
u.wechat_qr,
|
||
u.alipay_qr,
|
||
u.bank_card,
|
||
u.unionpay_qr,
|
||
u.audit_status,
|
||
u.is_system_account,
|
||
u.created_at,
|
||
u.updated_at,
|
||
u.province,
|
||
u.city,
|
||
u.district_id,
|
||
u.id_card_front,
|
||
u.id_card_back,
|
||
u.business_license,
|
||
u.is_distribute,
|
||
u.user_type,
|
||
u.inviter,
|
||
p.name as province_name,
|
||
c.name as city_name,
|
||
d.name as district_name,
|
||
COALESCE(yesterday_out.amount, 0) as yesterday_transfer_amount,
|
||
COALESCE(today_in.amount, 0) as today_received_amount
|
||
FROM users u
|
||
LEFT JOIN china_regions p ON u.province = p.code
|
||
LEFT JOIN china_regions c ON u.city = c.code
|
||
LEFT JOIN china_regions d ON u.district_id = d.code
|
||
LEFT JOIN (SELECT from_user_id, SUM(amount) as amount
|
||
FROM transfers
|
||
WHERE created_at >= DATE(DATE_SUB(NOW(), INTERVAL 1 DAY))
|
||
AND created_at < DATE(NOW())
|
||
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 >= DATE(NOW())
|
||
AND created_at < DATE(DATE_ADD(NOW(), INTERVAL 1 DAY))
|
||
AND status IN ('confirmed', 'received')
|
||
GROUP BY to_user_id) today_in ON u.id = today_in.to_user_id
|
||
${whereClause}
|
||
ORDER BY u.${sortField} ${sortOrder}
|
||
LIMIT ${limitNum} OFFSET ${offset}`,
|
||
listParams.slice(0, -2)
|
||
);
|
||
users.forEach(user => {
|
||
user.region = [user.province, user.city, user.district_id]
|
||
})
|
||
|
||
res.json({
|
||
success: true,
|
||
users,
|
||
total: countResult[0].total,
|
||
page: pageNum,
|
||
limit: limitNum,
|
||
totalPages: Math.ceil(countResult[0].total / limitNum)
|
||
});
|
||
} catch (error) {
|
||
console.error('获取用户列表错误:', error);
|
||
res.status(500).json({success: false, message: '获取用户列表失败'});
|
||
}
|
||
});
|
||
|
||
// 获取当前用户的个人资料
|
||
router.get('/profile', auth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
const userId = req.user.id;
|
||
|
||
const [users] = await db.execute(
|
||
'SELECT * FROM users WHERE id = ?',
|
||
[userId]
|
||
);
|
||
const [distribution] = await db.execute(`
|
||
SELECT count(*) as total
|
||
FROM distribution
|
||
WHERE user_id = ?`, [userId])
|
||
users[0].distribution = distribution[0].total > 0 ? true : false;
|
||
if (users.length === 0) {
|
||
return res.status(404).json({success: false, message: '用户不存在'});
|
||
}
|
||
const profile = {
|
||
...users[0],
|
||
nickname: users[0].username, // 添加nickname字段,映射到username
|
||
realName: users[0].real_name,
|
||
idCard: users[0].id_card,
|
||
wechatQr: users[0].wechat_qr,
|
||
alipayQr: users[0].alipay_qr,
|
||
bankCard: users[0].bank_card,
|
||
unionpayQr: users[0].unionpay_qr,
|
||
businessLicense: users[0].business_license,
|
||
idCardFront: users[0].id_card_front,
|
||
idCardBack: users[0].id_card_back
|
||
};
|
||
res.json({success: true, user: profile});
|
||
} catch (error) {
|
||
console.error('获取用户资料错误:', error);
|
||
res.status(500).json({success: false, message: '获取用户资料失败'});
|
||
}
|
||
});
|
||
|
||
/**
|
||
* 获取当前用户的收款码状态
|
||
* 用于检查用户是否已上传微信、支付宝、云闪付收款码
|
||
*/
|
||
router.get('/payment-codes-status', auth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
const userId = req.user.id;
|
||
|
||
const [users] = await db.execute(
|
||
'SELECT wechat_qr, alipay_qr, unionpay_qr FROM users WHERE id = ?',
|
||
[userId]
|
||
);
|
||
|
||
if (users.length === 0) {
|
||
return res.status(404).json({success: false, message: '用户不存在'});
|
||
}
|
||
|
||
const paymentCodes = users[0];
|
||
|
||
res.json({
|
||
success: true,
|
||
data: {
|
||
wechat_qr: paymentCodes.wechat_qr || '',
|
||
alipay_qr: paymentCodes.alipay_qr || '',
|
||
unionpay_qr: paymentCodes.unionpay_qr || ''
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('获取收款码状态错误:', error);
|
||
res.status(500).json({success: false, message: '获取收款码状态失败'});
|
||
}
|
||
});
|
||
|
||
// 获取用户收款信息
|
||
router.get('/payment-info/:userId', auth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
const targetUserId = req.params.userId;
|
||
|
||
const [users] = await db.execute(
|
||
'SELECT id, username, wechat_qr, alipay_qr, unionpay_qr, bank_card FROM users WHERE id = ?',
|
||
[targetUserId]
|
||
);
|
||
|
||
if (users.length === 0) {
|
||
return res.status(404).json({success: false, message: '用户不存在'});
|
||
}
|
||
|
||
const user = users[0];
|
||
res.json({
|
||
success: true,
|
||
data: {
|
||
id: user.id,
|
||
username: user.username,
|
||
wechat_qr: user.wechat_qr,
|
||
alipay_qr: user.alipay_qr,
|
||
unionpay_qr: user.unionpay_qr,
|
||
bank_card: user.bank_card
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('获取用户收款信息错误:', error);
|
||
res.status(500).json({success: false, message: '获取用户收款信息失败'});
|
||
}
|
||
});
|
||
|
||
// 获取当前用户的统计信息
|
||
router.get('/stats', auth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
const userId = req.user.id;
|
||
|
||
// 如果是管理员,返回全局统计
|
||
if (req.user.role === 'admin') {
|
||
// 总用户数
|
||
const [totalUsers] = await db.execute('SELECT COUNT(*) as count FROM users');
|
||
|
||
// 直营数量
|
||
const [directly_operated] = await db.execute('SELECT COUNT(*) as count FROM users WHERE user_type = "directly_operated"');
|
||
// 代理数量
|
||
const [agent] = await db.execute('SELECT COUNT(*) as count FROM users WHERE user_type = "agent"');
|
||
// 直营代理数量
|
||
const [agent_directly] = await db.execute('SELECT COUNT(*) as count FROM users WHERE user_type = "agent_directly"');
|
||
// 普通用户数量
|
||
const [regularUsers] = await db.execute('SELECT COUNT(*) as count FROM users WHERE user_type = "user"');
|
||
|
||
// 本月新增用户
|
||
const [monthUsers] = await db.execute(
|
||
'SELECT COUNT(*) as count FROM users WHERE YEAR(created_at) = YEAR(NOW()) AND MONTH(created_at) = MONTH(NOW())'
|
||
);
|
||
|
||
// 上月新增用户
|
||
const [lastMonthUsers] = await db.execute(
|
||
'SELECT COUNT(*) as count FROM users WHERE YEAR(created_at) = YEAR(DATE_SUB(NOW(), INTERVAL 1 MONTH)) AND MONTH(created_at) = MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH))'
|
||
);
|
||
|
||
// 计算月增长率
|
||
const monthGrowthRate = lastMonthUsers[0].count > 0
|
||
? ((monthUsers[0].count - lastMonthUsers[0].count) / lastMonthUsers[0].count * 100).toFixed(2)
|
||
: 0;
|
||
|
||
// 用户总积分
|
||
const [totalPoints] = await db.execute('SELECT COALESCE(SUM(points), 0) as total FROM users');
|
||
|
||
// 今日新增用户
|
||
const [todayUsers] = await db.execute(
|
||
'SELECT COUNT(*) as count FROM users WHERE DATE(created_at) = CURDATE()'
|
||
);
|
||
|
||
// 昨日新增用户
|
||
const [yesterdayUsers] = await db.execute(
|
||
'SELECT COUNT(*) as count FROM users WHERE DATE(created_at) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)'
|
||
);
|
||
|
||
// 活跃用户数(有订单的用户)
|
||
const [activeUsers] = await db.execute(
|
||
`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
|
||
FROM transfers
|
||
WHERE DATE(created_at) = DATE_SUB(CURDATE(), INTERVAL 7 DAY)
|
||
`)
|
||
res.json({
|
||
success: true,
|
||
stats: {
|
||
totalUsers: totalUsers[0].count,
|
||
directly_operated: directly_operated[0].count,
|
||
agent: agent[0].count,
|
||
agent_directly: agent_directly[0].count,
|
||
regularUsers: regularUsers[0].count,
|
||
monthNewUsers: monthUsers[0].count,
|
||
todayUsers: todayUsers[0].count,
|
||
yesterdayUsers: yesterdayUsers[0].count,
|
||
monthlyGrowth: parseFloat(monthGrowthRate),
|
||
totalPoints: totalPoints[0].total,
|
||
activeUsers: activeUsers[0].count,
|
||
activeRate: (weekUsers[0].count / totalUsers[0].count * 100).toFixed(2)
|
||
}
|
||
});
|
||
} else {
|
||
// 普通用户返回个人统计
|
||
// 用户订单数
|
||
const [orderCount] = await db.execute(
|
||
'SELECT COUNT(*) as count FROM orders WHERE user_id = ?',
|
||
[userId]
|
||
);
|
||
|
||
// 用户总消费
|
||
const [totalSpent] = await db.execute(
|
||
'SELECT COALESCE(SUM(total_amount), 0) as total FROM orders WHERE user_id = ? AND status = "completed"',
|
||
[userId]
|
||
);
|
||
|
||
// 用户积分历史
|
||
const [pointsEarned] = await db.execute(
|
||
'SELECT COALESCE(SUM(amount), 0) as total FROM points_history WHERE user_id = ? AND type = "earn"',
|
||
[userId]
|
||
);
|
||
|
||
const [pointsSpent] = await db.execute(
|
||
'SELECT COALESCE(SUM(amount), 0) as total FROM points_history WHERE user_id = ? AND type = "spend"',
|
||
[userId]
|
||
);
|
||
|
||
res.json({
|
||
success: true,
|
||
stats: {
|
||
orderCount: orderCount[0].count,
|
||
totalSpent: totalSpent[0].total,
|
||
pointsEarned: pointsEarned[0].total,
|
||
pointsSpent: pointsSpent[0].total,
|
||
currentPoints: req.user.points || 0
|
||
}
|
||
});
|
||
}
|
||
} catch (error) {
|
||
console.error('获取用户统计错误:', error);
|
||
res.status(500).json({success: false, message: '获取用户统计失败'});
|
||
}
|
||
});
|
||
|
||
// 获取用户统计信息(管理员权限)- 必须在/:id路由之前定义
|
||
router.get('/admin/stats', auth, adminAuth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
// 总用户数
|
||
const [totalUsers] = await db.execute('SELECT COUNT(*) as count FROM users');
|
||
|
||
// 管理员数量
|
||
const [adminUsers] = await db.execute('SELECT COUNT(*) as count FROM users WHERE role = "admin"');
|
||
|
||
// 普通用户数量
|
||
const [regularUsers] = await db.execute('SELECT COUNT(*) as count FROM users WHERE role = "user"');
|
||
|
||
// 本月新增用户
|
||
const [monthUsers] = await db.execute(
|
||
'SELECT COUNT(*) as count FROM users WHERE YEAR(created_at) = YEAR(NOW()) AND MONTH(created_at) = MONTH(NOW())'
|
||
);
|
||
|
||
// 上月新增用户
|
||
const [lastMonthUsers] = await db.execute(
|
||
'SELECT COUNT(*) as count FROM users WHERE YEAR(created_at) = YEAR(DATE_SUB(NOW(), INTERVAL 1 MONTH)) AND MONTH(created_at) = MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH))'
|
||
);
|
||
|
||
// 计算月增长率
|
||
const monthGrowthRate = lastMonthUsers[0].count > 0
|
||
? ((monthUsers[0].count - lastMonthUsers[0].count) / lastMonthUsers[0].count * 100).toFixed(2)
|
||
: 0;
|
||
|
||
// 用户总积分
|
||
const [totalPoints] = await db.execute('SELECT COALESCE(SUM(points), 0) as total FROM users');
|
||
|
||
// 今日新增用户
|
||
const [todayUsers] = await db.execute(
|
||
'SELECT COUNT(*) as count FROM users WHERE DATE(created_at) = CURDATE()'
|
||
);
|
||
|
||
// 昨日新增用户
|
||
const [yesterdayUsers] = await db.execute(
|
||
'SELECT COUNT(*) as count FROM users WHERE DATE(created_at) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)'
|
||
);
|
||
|
||
// 活跃用户数(有订单的用户)
|
||
const [activeUsers] = await db.execute(
|
||
'SELECT COUNT(DISTINCT user_id) as count FROM orders'
|
||
);
|
||
|
||
res.json({
|
||
success: true,
|
||
stats: {
|
||
totalUsers: totalUsers[0].count,
|
||
adminUsers: adminUsers[0].count,
|
||
regularUsers: regularUsers[0].count,
|
||
monthNewUsers: monthUsers[0].count,
|
||
todayUsers: todayUsers[0].count,
|
||
yesterdayUsers: yesterdayUsers[0].count,
|
||
monthlyGrowth: parseFloat(monthGrowthRate),
|
||
totalPoints: totalPoints[0].total,
|
||
activeUsers: activeUsers[0].count
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('获取用户统计错误:', error);
|
||
res.status(500).json({success: false, message: '获取用户统计失败'});
|
||
}
|
||
});
|
||
|
||
// 获取当前用户积分
|
||
router.get('/points', auth, async (req, res) => {
|
||
try {
|
||
const userId = req.user.id;
|
||
|
||
const [users] = await getDB().execute(
|
||
'SELECT points FROM users WHERE id = ?',
|
||
[userId]
|
||
);
|
||
|
||
if (users.length === 0) {
|
||
return res.status(404).json({success: false, message: '用户不存在'});
|
||
}
|
||
|
||
res.json({
|
||
success: true,
|
||
points: users[0].points
|
||
});
|
||
} catch (error) {
|
||
console.error('获取用户积分错误:', error);
|
||
res.status(500).json({success: false, message: '获取用户积分失败'});
|
||
}
|
||
});
|
||
|
||
// 获取用户积分历史记录
|
||
router.get('/points/history', auth, async (req, res) => {
|
||
try {
|
||
const userId = req.user.id;
|
||
const {page = 1, limit = 20, type} = req.query;
|
||
|
||
// 确保参数为有效数字
|
||
const pageNum = Math.max(1, parseInt(page) || 1);
|
||
const limitNum = Math.max(1, Math.min(100, parseInt(limit) || 20));
|
||
const offset = Math.max(0, (pageNum - 1) * limitNum);
|
||
|
||
let whereClause = 'WHERE user_id = ?';
|
||
let queryParams = [userId];
|
||
|
||
if (type && ['earn', 'spend'].includes(type)) {
|
||
whereClause += ' AND type = ?';
|
||
queryParams.push(type);
|
||
}
|
||
|
||
// 获取总数
|
||
const [countResult] = await getDB().execute(
|
||
`SELECT COUNT(*) as total
|
||
FROM points_history ${whereClause}`,
|
||
queryParams
|
||
);
|
||
|
||
// 获取历史记录
|
||
const [records] = await getDB().execute(
|
||
`SELECT id, type, amount, description, order_id, created_at
|
||
FROM points_history ${whereClause}
|
||
ORDER BY created_at DESC
|
||
LIMIT ${limitNum} OFFSET ${offset}`,
|
||
queryParams
|
||
);
|
||
|
||
res.json({
|
||
success: true,
|
||
data: {
|
||
records,
|
||
pagination: {
|
||
page: pageNum,
|
||
limit: limitNum,
|
||
total: countResult[0].total,
|
||
totalPages: Math.ceil(countResult[0].total / limitNum)
|
||
}
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('获取积分历史失败:', error);
|
||
res.status(500).json({success: false, message: '获取积分历史失败'});
|
||
}
|
||
});
|
||
|
||
// 获取用户增长趋势数据(管理员权限)
|
||
router.get('/growth-trend', auth, adminAuth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
const {days = 7} = req.query;
|
||
const daysNum = Math.min(90, Math.max(1, parseInt(days) || 7));
|
||
|
||
// 获取指定天数内的用户注册趋势
|
||
const [trendData] = await db.execute(`
|
||
SELECT DATE(created_at) as date,
|
||
COUNT(*) as count
|
||
FROM users
|
||
WHERE created_at >= DATE_SUB(NOW(), INTERVAL ? DAY)
|
||
GROUP BY DATE(created_at)
|
||
ORDER BY date ASC
|
||
`, [daysNum]);
|
||
|
||
// 填充缺失的日期(注册数为0)
|
||
const result = [];
|
||
|
||
for (let i = daysNum - 1; i >= 0; i--) {
|
||
const date = dayjs().subtract(i, 'day');
|
||
const dateStr = date.format('YYYY-MM-DD');
|
||
|
||
// 修复日期比较:将数据库返回的Date对象转换为字符串进行比较
|
||
const existingData = trendData.find(item => {
|
||
const itemDateStr = dayjs(item.date).format('YYYY-MM-DD');
|
||
return itemDateStr === dateStr;
|
||
});
|
||
|
||
result.push({
|
||
date: date.format('MM-DD'),
|
||
count: existingData ? existingData.count : 0
|
||
});
|
||
}
|
||
|
||
res.json({
|
||
success: true,
|
||
data: result
|
||
});
|
||
} catch (error) {
|
||
console.error('获取用户增长趋势错误:', error);
|
||
res.status(500).json({success: false, message: '获取用户增长趋势失败'});
|
||
}
|
||
});
|
||
|
||
// 获取日收入统计数据(管理员权限)
|
||
router.get('/daily-revenue', auth, adminAuth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
const {days = 30} = req.query;
|
||
const daysNum = Math.min(90, Math.max(1, parseInt(days) || 30));
|
||
|
||
// 获取指定天数内的用户注册数据,按天统计
|
||
const [dailyData] = await db.execute(`
|
||
SELECT DATE(created_at) as date,
|
||
COUNT(*) as user_count
|
||
FROM users
|
||
WHERE created_at >= DATE_SUB(NOW(), INTERVAL ? DAY)
|
||
GROUP BY DATE(created_at)
|
||
ORDER BY date ASC
|
||
`, [daysNum]);
|
||
const [dailyDataTransfers] = await db.execute(`
|
||
SELECT DATE(created_at) as date,
|
||
SUM(CASE WHEN source_type IN ('system') THEN amount END) as amount
|
||
FROM transfers
|
||
WHERE created_at >= DATE_SUB(NOW(), INTERVAL ? DAY)
|
||
GROUP BY DATE(created_at)
|
||
ORDER BY date ASC
|
||
`, [daysNum]);
|
||
// 填充缺失的日期(注册数为0)
|
||
const result = [];
|
||
for (let i = daysNum - 1; i >= 0; i--) {
|
||
const date = dayjs().subtract(i, 'day');
|
||
const dateStr = date.format('YYYY-MM-DD'); // YYYY-MM-DD格式
|
||
const dateDisplay = date.format('M/D'); // 显示格式
|
||
|
||
const existingData = dailyData.find(item => {
|
||
const itemDateStr = dayjs(item.date).format('YYYY-MM-DD');
|
||
return itemDateStr === dateStr;
|
||
});
|
||
const existingDataTransfers = dailyDataTransfers.find(item => {
|
||
const itemDateStr = dayjs(item.date).format('YYYY-MM-DD');
|
||
return itemDateStr === dateStr;
|
||
});
|
||
const userCount = existingData ? existingData.user_count : 0;
|
||
const revenue = existingDataTransfers && existingDataTransfers.amount !== null ? existingDataTransfers.amount : 0; // 每个用户398元收入
|
||
|
||
result.push({
|
||
date: dateDisplay,
|
||
userCount: userCount,
|
||
amount: revenue
|
||
});
|
||
}
|
||
|
||
res.json({
|
||
success: true,
|
||
data: result
|
||
});
|
||
} catch (error) {
|
||
console.error('获取日收入统计错误:', error);
|
||
res.status(500).json({success: false, message: '获取日收入统计失败'});
|
||
}
|
||
});
|
||
// 获取当前用户个人资料
|
||
router.get('/profile', auth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
const userId = req.user.id;
|
||
|
||
const [users] = await db.execute(
|
||
'SELECT id, username, role, avatar, points, real_name, id_card, phone, wechat_qr, alipay_qr, bank_card, unionpay_qr, business_license, id_card_front, id_card_back, audit_status, created_at, updated_at FROM users WHERE id = ?',
|
||
[userId]
|
||
);
|
||
|
||
if (users.length === 0) {
|
||
return res.status(404).json({success: false, message: '用户不存在'});
|
||
}
|
||
|
||
// 转换字段名以匹配前端
|
||
const user = users[0];
|
||
const profile = {
|
||
...user,
|
||
nickname: user.username, // 添加nickname字段,映射到username
|
||
realName: user.real_name,
|
||
idCard: user.id_card,
|
||
wechatQr: user.wechat_qr,
|
||
alipayQr: user.alipay_qr,
|
||
bankCard: user.bank_card,
|
||
unionpayQr: user.unionpay_qr,
|
||
businessLicense: user.business_license,
|
||
idCardFront: user.id_card_front,
|
||
idCardBack: user.id_card_back,
|
||
auditStatus: user.audit_status
|
||
};
|
||
|
||
res.json({success: true, user: profile});
|
||
} catch (error) {
|
||
console.error('获取用户个人资料错误:', error);
|
||
res.status(500).json({success: false, message: '获取用户个人资料失败'});
|
||
}
|
||
});
|
||
|
||
// 更新当前用户个人资料
|
||
router.put('/profile', auth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
const userId = req.user.id;
|
||
const {
|
||
username,
|
||
nickname,
|
||
avatar,
|
||
realName,
|
||
idCard,
|
||
phone,
|
||
wechatQr,
|
||
alipayQr,
|
||
bankCard,
|
||
unionpayQr,
|
||
businessLicense,
|
||
idCardFront,
|
||
idCardBack,
|
||
city,
|
||
districtId
|
||
} = req.body;
|
||
|
||
// 处理nickname字段,如果提供了nickname,则使用nickname作为username
|
||
const finalUsername = nickname || username;
|
||
|
||
// 检查用户名、身份证号和手机号是否已被其他用户使用
|
||
if (finalUsername || idCard || phone) {
|
||
const conditions = [];
|
||
const checkValues = [];
|
||
|
||
if (finalUsername) {
|
||
conditions.push('username = ?');
|
||
checkValues.push(finalUsername);
|
||
}
|
||
if (idCard) {
|
||
conditions.push('id_card = ?');
|
||
checkValues.push(idCard);
|
||
}
|
||
if (phone) {
|
||
conditions.push('phone = ?');
|
||
checkValues.push(phone);
|
||
}
|
||
|
||
if (conditions.length > 0) {
|
||
const [existingUsers] = await db.execute(
|
||
`SELECT id
|
||
FROM users
|
||
WHERE (${conditions.join(' OR ')})
|
||
AND id != ?`,
|
||
[...checkValues, userId]
|
||
);
|
||
|
||
if (existingUsers.length > 0) {
|
||
return res.status(400).json({success: false, message: '用户名、身份证号或手机号已被使用'});
|
||
}
|
||
}
|
||
}
|
||
|
||
// 验证身份证号格式
|
||
if (idCard) {
|
||
const idCardRegex = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
|
||
if (!idCardRegex.test(idCard)) {
|
||
return res.status(400).json({success: false, message: '身份证号格式不正确'});
|
||
}
|
||
}
|
||
|
||
// 构建更新字段
|
||
const updateFields = [];
|
||
const updateValues = [];
|
||
|
||
if (finalUsername !== undefined) {
|
||
updateFields.push('username = ?');
|
||
updateValues.push(finalUsername);
|
||
}
|
||
|
||
if (avatar !== undefined) {
|
||
updateFields.push('avatar = ?');
|
||
updateValues.push(avatar);
|
||
}
|
||
|
||
if (realName !== undefined) {
|
||
updateFields.push('real_name = ?');
|
||
updateValues.push(realName);
|
||
}
|
||
|
||
if (idCard !== undefined) {
|
||
updateFields.push('id_card = ?');
|
||
updateValues.push(idCard);
|
||
}
|
||
|
||
if (phone !== undefined) {
|
||
updateFields.push('phone = ?');
|
||
updateValues.push(phone);
|
||
}
|
||
|
||
// 添加城市和地区字段更新
|
||
if (city !== undefined) {
|
||
updateFields.push('city = ?');
|
||
updateValues.push(city);
|
||
}
|
||
|
||
if (districtId !== undefined) {
|
||
updateFields.push('district_id = ?');
|
||
updateValues.push(districtId);
|
||
}
|
||
|
||
// 检查是否更新了需要重新审核的关键信息
|
||
let needsReaudit = false;
|
||
|
||
if (wechatQr !== undefined) {
|
||
updateFields.push('wechat_qr = ?');
|
||
updateValues.push(wechatQr);
|
||
needsReaudit = true;
|
||
}
|
||
|
||
if (alipayQr !== undefined) {
|
||
updateFields.push('alipay_qr = ?');
|
||
updateValues.push(alipayQr);
|
||
needsReaudit = true;
|
||
}
|
||
|
||
if (bankCard !== undefined) {
|
||
updateFields.push('bank_card = ?');
|
||
updateValues.push(bankCard);
|
||
needsReaudit = true;
|
||
}
|
||
|
||
if (unionpayQr !== undefined) {
|
||
updateFields.push('unionpay_qr = ?');
|
||
updateValues.push(unionpayQr);
|
||
needsReaudit = true;
|
||
}
|
||
|
||
if (city !== undefined) {
|
||
updateFields.push('city = ?');
|
||
updateValues.push(city);
|
||
}
|
||
|
||
if (districtId !== undefined) {
|
||
updateFields.push('district_id = ?');
|
||
updateValues.push(districtId);
|
||
}
|
||
|
||
if (businessLicense !== undefined) {
|
||
updateFields.push('business_license = ?');
|
||
updateValues.push(businessLicense);
|
||
needsReaudit = true;
|
||
}
|
||
|
||
if (idCardFront !== undefined) {
|
||
updateFields.push('id_card_front = ?');
|
||
updateValues.push(idCardFront);
|
||
needsReaudit = true;
|
||
}
|
||
|
||
if (idCardBack !== undefined) {
|
||
updateFields.push('id_card_back = ?');
|
||
updateValues.push(idCardBack);
|
||
needsReaudit = true;
|
||
}
|
||
|
||
// 如果更新了关键信息且用户不是管理员,则重置审核状态为待审核
|
||
if (needsReaudit && req.user.role !== 'admin') {
|
||
updateFields.push('audit_status = ?');
|
||
updateValues.push('pending');
|
||
}
|
||
|
||
if (updateFields.length === 0) {
|
||
return res.status(400).json({success: false, message: '没有要更新的字段'});
|
||
}
|
||
|
||
updateValues.push(userId);
|
||
|
||
await db.execute(
|
||
`UPDATE users
|
||
SET ${updateFields.join(', ')}
|
||
WHERE id = ?`,
|
||
updateValues
|
||
);
|
||
|
||
// 返回更新后的用户信息
|
||
const [updatedUsers] = await db.execute(
|
||
'SELECT id, username, role, avatar, points, real_name, id_card, phone, wechat_qr, alipay_qr, bank_card, unionpay_qr, business_license, id_card_front, id_card_back, audit_status, is_system_account, created_at, updated_at FROM users WHERE id = ?',
|
||
[userId]
|
||
);
|
||
|
||
// 转换字段名以匹配前端
|
||
const user = updatedUsers[0];
|
||
const profile = {
|
||
...user,
|
||
nickname: user.username, // 添加nickname字段,映射到username
|
||
realName: user.real_name,
|
||
idCard: user.id_card,
|
||
wechatQr: user.wechat_qr,
|
||
alipayQr: user.alipay_qr,
|
||
bankCard: user.bank_card,
|
||
unionpayQr: user.unionpay_qr,
|
||
businessLicense: user.business_license,
|
||
idCardFront: user.id_card_front,
|
||
idCardBack: user.id_card_back,
|
||
auditStatus: user.audit_status
|
||
};
|
||
|
||
res.json({
|
||
success: true,
|
||
message: '个人资料更新成功',
|
||
data: profile
|
||
});
|
||
} catch (error) {
|
||
console.error('更新个人资料错误:', error);
|
||
res.status(500).json({success: false, message: '更新个人资料失败'});
|
||
}
|
||
});
|
||
// 获取用户详情
|
||
router.get('/:id', auth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
const userId = req.params.id;
|
||
|
||
// 只有管理员或用户本人可以查看详情
|
||
if (req.user.role !== 'admin' && req.user.id != userId) {
|
||
return res.status(403).json({success: false, message: '权限不足'});
|
||
}
|
||
|
||
const [users] = await db.execute(
|
||
'SELECT id, username, role, avatar, points, real_name, id_card, phone, wechat_qr, alipay_qr, bank_card, unionpay_qr, created_at, updated_at FROM users WHERE id = ?',
|
||
[userId]
|
||
);
|
||
|
||
if (users.length === 0) {
|
||
return res.status(404).json({success: false, message: '用户不存在'});
|
||
}
|
||
|
||
res.json({success: true, user: users[0]});
|
||
} catch (error) {
|
||
console.error('获取用户详情错误:', error);
|
||
res.status(500).json({success: false, message: '获取用户详情失败'});
|
||
}
|
||
});
|
||
|
||
// 更新用户信息
|
||
router.put('/:id', auth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
const userId = req.params.id;
|
||
const {
|
||
username,
|
||
password,
|
||
role,
|
||
isSystemAccount,
|
||
avatar,
|
||
realName,
|
||
idCard,
|
||
phone,
|
||
wechatQr,
|
||
alipayQr,
|
||
bankCard,
|
||
unionpayQr,
|
||
idCardFront,
|
||
idCardBack,
|
||
businessLicense,
|
||
province,
|
||
city,
|
||
districtId,
|
||
user_type,
|
||
inviter,
|
||
} = req.body;
|
||
|
||
// 只有管理员或用户本人可以更新信息
|
||
if (req.user.role !== 'admin' && req.user.id != userId) {
|
||
return res.status(403).json({success: false, message: '权限不足'});
|
||
}
|
||
|
||
// 非管理员不能修改角色
|
||
if (req.user.role !== 'admin' && role) {
|
||
return res.status(403).json({success: false, message: '无权限修改用户角色'});
|
||
}
|
||
|
||
// 检查用户名、身份证号和手机号是否已被其他用户使用
|
||
if (username || idCard || phone) {
|
||
const conditions = [];
|
||
const checkValues = [];
|
||
|
||
if (username) {
|
||
conditions.push('username = ?');
|
||
checkValues.push(username);
|
||
}
|
||
if (idCard) {
|
||
conditions.push('id_card = ?');
|
||
checkValues.push(idCard);
|
||
}
|
||
if (phone) {
|
||
conditions.push('phone = ?');
|
||
checkValues.push(phone);
|
||
}
|
||
|
||
if (conditions.length > 0) {
|
||
const [existingUsers] = await db.execute(
|
||
`SELECT id
|
||
FROM users
|
||
WHERE (${conditions.join(' OR ')})
|
||
AND id != ?`,
|
||
[...checkValues, userId]
|
||
);
|
||
|
||
if (existingUsers.length > 0) {
|
||
return res.status(400).json({success: false, message: '用户名、身份证号或手机号已被使用'});
|
||
}
|
||
}
|
||
}
|
||
|
||
// 验证身份证号格式
|
||
if (idCard) {
|
||
const idCardRegex = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
|
||
if (!idCardRegex.test(idCard)) {
|
||
return res.status(400).json({success: false, message: '身份证号格式不正确'});
|
||
}
|
||
}
|
||
|
||
// 构建更新字段
|
||
const updateFields = [];
|
||
const updateValues = [];
|
||
|
||
if (username) {
|
||
updateFields.push('username = ?');
|
||
updateValues.push(username);
|
||
}
|
||
|
||
// 处理密码更新
|
||
if (password && password.trim() !== '') {
|
||
const hashedPassword = await bcrypt.hash(password, 10);
|
||
updateFields.push('password = ?');
|
||
updateValues.push(hashedPassword);
|
||
}
|
||
|
||
if (role && req.user.role === 'admin') {
|
||
updateFields.push('role = ?');
|
||
updateValues.push(role);
|
||
}
|
||
|
||
// 只有管理员可以修改账户类型
|
||
if (isSystemAccount !== undefined && req.user.role === 'admin') {
|
||
updateFields.push('is_system_account = ?');
|
||
updateValues.push(isSystemAccount);
|
||
}
|
||
|
||
if (avatar !== undefined) {
|
||
updateFields.push('avatar = ?');
|
||
updateValues.push(avatar);
|
||
}
|
||
|
||
if (realName !== undefined) {
|
||
updateFields.push('real_name = ?');
|
||
updateValues.push(realName);
|
||
}
|
||
|
||
if (idCard !== undefined) {
|
||
updateFields.push('id_card = ?');
|
||
updateValues.push(idCard);
|
||
}
|
||
|
||
if (phone !== undefined) {
|
||
updateFields.push('phone = ?');
|
||
updateValues.push(phone);
|
||
}
|
||
if (city !== undefined) {
|
||
updateFields.push('city = ?');
|
||
updateValues.push(city);
|
||
}
|
||
|
||
if (districtId !== undefined) {
|
||
updateFields.push('district_id = ?');
|
||
updateValues.push(districtId);
|
||
}
|
||
// 检查是否更新了需要重新审核的关键信息
|
||
let needsReaudit = false;
|
||
|
||
if (wechatQr !== undefined) {
|
||
updateFields.push('wechat_qr = ?');
|
||
updateValues.push(wechatQr);
|
||
needsReaudit = true;
|
||
}
|
||
|
||
if (alipayQr !== undefined) {
|
||
updateFields.push('alipay_qr = ?');
|
||
updateValues.push(alipayQr);
|
||
needsReaudit = true;
|
||
}
|
||
|
||
if (bankCard !== undefined) {
|
||
updateFields.push('bank_card = ?');
|
||
updateValues.push(bankCard);
|
||
needsReaudit = true;
|
||
}
|
||
|
||
if (unionpayQr !== undefined) {
|
||
updateFields.push('unionpay_qr = ?');
|
||
updateValues.push(unionpayQr);
|
||
needsReaudit = true;
|
||
}
|
||
if (idCardFront !== undefined) {
|
||
updateFields.push('id_card_front = ?');
|
||
updateValues.push(idCardFront);
|
||
needsReaudit = true;
|
||
}
|
||
if (idCardBack !== undefined) {
|
||
updateFields.push('id_card_back = ?');
|
||
updateValues.push(idCardBack);
|
||
needsReaudit = true;
|
||
}
|
||
if (province !== undefined) {
|
||
updateFields.push('province = ?');
|
||
updateValues.push(province);
|
||
needsReaudit = true;
|
||
}
|
||
if (businessLicense !== undefined) {
|
||
updateFields.push('business_license = ?');
|
||
updateValues.push(businessLicense);
|
||
needsReaudit = true;
|
||
}
|
||
if (user_type !== undefined) {
|
||
updateFields.push('user_type = ?');
|
||
updateValues.push(user_type);
|
||
needsReaudit = true;
|
||
}
|
||
if (inviter !== undefined) {
|
||
updateFields.push('inviter = ?');
|
||
updateValues.push(inviter);
|
||
needsReaudit = true;
|
||
}
|
||
|
||
// 如果更新了关键信息且用户不是管理员,则重置审核状态为待审核
|
||
if (needsReaudit && req.user.role !== 'admin') {
|
||
updateFields.push('audit_status = ?');
|
||
updateValues.push('pending');
|
||
}
|
||
|
||
if (updateFields.length === 0) {
|
||
return res.status(400).json({success: false, message: '没有要更新的字段'});
|
||
}
|
||
|
||
updateValues.push(userId);
|
||
|
||
await db.execute(
|
||
`UPDATE users
|
||
SET ${updateFields.join(', ')}
|
||
WHERE id = ?`,
|
||
updateValues
|
||
);
|
||
|
||
// 返回更新后的用户信息
|
||
const [updatedUsers] = await db.execute(
|
||
'SELECT id, username, role, avatar, points, real_name, id_card, phone, wechat_qr, alipay_qr, bank_card, unionpay_qr, city, district_id, province, created_at, updated_at FROM users WHERE id = ?',
|
||
[userId]
|
||
);
|
||
|
||
res.json({
|
||
success: true,
|
||
message: '用户信息更新成功',
|
||
user: updatedUsers[0]
|
||
});
|
||
} catch (error) {
|
||
console.error('更新用户信息错误:', error);
|
||
res.status(500).json({success: false, message: '更新用户信息失败'});
|
||
}
|
||
});
|
||
|
||
|
||
// 删除用户(管理员权限)
|
||
router.delete('/:id', auth, adminAuth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
const userId = req.params.id;
|
||
|
||
// 不能删除自己
|
||
if (req.user.id == userId) {
|
||
return res.status(400).json({success: false, message: '不能删除自己的账户'});
|
||
}
|
||
|
||
// 检查用户是否存在
|
||
const [users] = await db.execute(
|
||
'SELECT id FROM users WHERE id = ?',
|
||
[userId]
|
||
);
|
||
|
||
if (users.length === 0) {
|
||
return res.status(404).json({success: false, message: '用户不存在'});
|
||
}
|
||
|
||
// 删除用户
|
||
await db.execute('DELETE FROM users WHERE id = ?', [userId]);
|
||
|
||
res.json({success: true, message: '用户删除成功'});
|
||
} catch (error) {
|
||
console.error('删除用户错误:', error);
|
||
res.status(500).json({success: false, message: '删除用户失败'});
|
||
}
|
||
});
|
||
|
||
/**
|
||
* 审核用户(管理员权限)
|
||
*/
|
||
router.put('/:id/audit', auth, adminAuth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
const userId = req.params.id;
|
||
const {action, note} = req.body; // action: 'approve' 或 'reject'
|
||
|
||
if (!action || !['approve', 'reject'].includes(action)) {
|
||
return res.status(400).json({success: false, message: '审核操作无效'});
|
||
}
|
||
|
||
// 检查用户是否存在且为待审核状态
|
||
const [users] = await db.execute(
|
||
'SELECT id, username, audit_status FROM users WHERE id = ?',
|
||
[userId]
|
||
);
|
||
|
||
if (users.length === 0) {
|
||
return res.status(404).json({success: false, message: '用户不存在'});
|
||
}
|
||
|
||
const user = users[0];
|
||
if (user.audit_status !== 'pending') {
|
||
return res.status(400).json({success: false, message: '该用户不是待审核状态'});
|
||
}
|
||
|
||
// 更新审核状态
|
||
const auditStatus = action === 'approve' ? 'approved' : 'rejected';
|
||
await db.execute(
|
||
`UPDATE users
|
||
SET audit_status = ?,
|
||
audit_note = ?,
|
||
audited_by = ?,
|
||
audited_at = NOW()
|
||
WHERE id = ?`,
|
||
[auditStatus, note || null, req.user.id, userId]
|
||
);
|
||
|
||
const message = action === 'approve' ? '用户审核通过' : '用户审核拒绝';
|
||
res.json({success: true, message});
|
||
} catch (error) {
|
||
console.error('审核用户错误:', error);
|
||
res.status(500).json({success: false, message: '审核用户失败'});
|
||
}
|
||
});
|
||
|
||
/**
|
||
* 获取用户审核详情(管理员权限)
|
||
*/
|
||
router.get('/:id/audit-detail', auth, adminAuth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
const userId = req.params.id;
|
||
|
||
// 获取用户详细信息
|
||
const [users] = await db.execute(
|
||
`SELECT u.id,
|
||
u.username,
|
||
u.phone,
|
||
u.real_name,
|
||
u.business_license,
|
||
u.id_card_front,
|
||
u.id_card_back,
|
||
u.audit_status,
|
||
u.audit_note,
|
||
u.audited_at,
|
||
u.created_at,
|
||
auditor.username as auditor_name
|
||
FROM users u
|
||
LEFT JOIN users auditor ON u.audited_by = auditor.id
|
||
WHERE u.id = ?`,
|
||
[userId]
|
||
);
|
||
|
||
if (users.length === 0) {
|
||
return res.status(404).json({success: false, message: '用户不存在'});
|
||
}
|
||
|
||
res.json({
|
||
success: true,
|
||
data: users[0]
|
||
});
|
||
} catch (error) {
|
||
console.error('获取用户审核详情错误:', error);
|
||
res.status(500).json({success: false, message: '获取用户审核详情失败'});
|
||
}
|
||
});
|
||
|
||
router.put('/:id/distribute', auth, async (req, res) => {
|
||
try {
|
||
const db = getDB();
|
||
const userId = req.params.id;
|
||
const {is_distribute} = req.body;
|
||
|
||
if (typeof is_distribute !== 'boolean') {
|
||
return res.status(400).json({success: false, message: '分发状态无效'});
|
||
}
|
||
|
||
|
||
// 检查用户是否存在
|
||
const [users] = await db.execute(
|
||
'SELECT id,user_type FROM users WHERE id = ?',
|
||
[userId]
|
||
);
|
||
|
||
if (users.length === 0) {
|
||
return res.status(404).json({success: false, message: '用户不存在'});
|
||
}
|
||
if (users[0].user_type === 'directly_operated') {
|
||
return res.status(400).json({success: false, message: '直营用户不允许开启委托出售'});
|
||
}
|
||
let [isServiceFee] = await db.execute('SELECT COUNT(*) AS total FROM distribution WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 YEAR) AND user_id = ?', [userId]);
|
||
if (isServiceFee[0].total > 0) {
|
||
// 更新分发状态
|
||
await db.execute(
|
||
'UPDATE users SET is_distribute = ? WHERE id = ?',
|
||
[is_distribute, userId]
|
||
);
|
||
res.json({
|
||
success: true,
|
||
message: '分发状态更新成功',
|
||
is_distribute
|
||
});
|
||
} else {
|
||
return res.json({success: false, message: '请缴纳2980融豆服务费'});
|
||
}
|
||
} catch (error) {
|
||
|
||
}
|
||
})
|
||
/**
|
||
* 扣除2980融豆服务费
|
||
*/
|
||
router.post('/:id/deduct-service-fee', auth, async (req, res) => {
|
||
const db = getDB();
|
||
try {
|
||
const userId = req.params.id;
|
||
const serviceFee = 2980; // 服务费金额
|
||
|
||
// 开始事务
|
||
await db.query('START TRANSACTION');
|
||
|
||
// 使用行级锁定查询用户信息,只锁定当前用户记录
|
||
const [users] = await db.execute(
|
||
'SELECT id, balance, username,district_id FROM users WHERE id = ? FOR UPDATE',
|
||
[userId]
|
||
);
|
||
|
||
if (users.length === 0) {
|
||
await db.query('ROLLBACK');
|
||
return res.status(404).json({success: false, message: '用户不存在'});
|
||
}
|
||
//判断今年是否已扣款
|
||
let [isServiceFee] = await db.execute('SELECT COUNT(*) AS total FROM distribution WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 YEAR) AND user_id = ?', [userId]);
|
||
if (isServiceFee[0].total > 0) {
|
||
return res.status(400).json({success: false, message: '已缴纳2980融豆服务费'});
|
||
}
|
||
const user = users[0];
|
||
const currentBalance = Math.abs(user.balance); // 将负数转为正数处理
|
||
|
||
// 检查融豆余额是否足够
|
||
if (currentBalance < serviceFee) {
|
||
await db.query('ROLLBACK');
|
||
return res.status(400).json({
|
||
success: false,
|
||
message: `融豆余额不足,当前余额:${currentBalance},需要:${serviceFee}`
|
||
});
|
||
}
|
||
|
||
// 扣除融豆(balance字段为负数,所以减去服务费实际是增加负数)
|
||
await db.execute(
|
||
'UPDATE users SET balance = balance + ? WHERE id = ?',
|
||
[serviceFee, userId]
|
||
);
|
||
//查找上级分销
|
||
let [distribute] = await db.execute(
|
||
'SELECT inviter FROM users WHERE id = ?',
|
||
[userId]
|
||
);
|
||
distribute = distribute[0]
|
||
//如果有上级分销
|
||
if (distribute.inviter) {
|
||
// 查找上级分销
|
||
let [distributeUser] = await db.execute(
|
||
'SELECT id, balance,user_type,inviter FROM users WHERE id = ?',
|
||
[distribute.inviter]
|
||
);
|
||
distributeUser = distributeUser[0]
|
||
//分销是代理
|
||
if (distributeUser.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.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 (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
|
||
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.3, '用户服务费返现', 'system']
|
||
);
|
||
//记录服务费
|
||
await db.execute(
|
||
'INSERT INTO distribution (user_id,agent_id, amount, type) VALUES (?, ?, ?,?)',
|
||
[userId, distributeUser.id, serviceFee, 'agent']
|
||
)
|
||
}
|
||
//如果不是代理,查看是否是直营代理
|
||
if (distributeUser.user_type == 'agent_directly') {
|
||
//给直营代理50%融豆给平台50%融豆
|
||
await db.execute(
|
||
'UPDATE users SET balance = balance - ? WHERE id = ?',
|
||
[serviceFee * 0.5, 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.5, '用户服务费返现', 'agent_operated']
|
||
);
|
||
//记录平台利润
|
||
await db.execute(
|
||
'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']
|
||
);
|
||
//记录服务费
|
||
await db.execute(
|
||
'INSERT INTO distribution (user_id,agent_id, amount, type) VALUES (?, ?, ?,?)',
|
||
[userId, distributeUser.id, serviceFee, 'direct_agent']
|
||
)
|
||
}
|
||
//是否是直营
|
||
if (distributeUser.user_type == 'directly_operated') {
|
||
//查询这个月直营做了多少单
|
||
let [orderCount] = await db.execute(
|
||
`SELECT COUNT(*) AS total
|
||
FROM distribution
|
||
WHERE agent_id = ?
|
||
AND created_at >= DATE_FORMAT(NOW(), '%Y-%m-01')`,
|
||
[distributeUser.id]
|
||
);
|
||
orderCount = orderCount[0]
|
||
if (orderCount.total <= 5) {
|
||
//给直营代理20%融豆给平台50% 融豆给用户30%
|
||
await db.execute(
|
||
'UPDATE users SET balance = balance - ? WHERE id = ?',
|
||
[serviceFee * 0.2, distributeUser.inviter]
|
||
);
|
||
//给直营添加30%融豆
|
||
await db.execute(
|
||
'UPDATE users SET balance = balance - ? WHERE id = ?',
|
||
[serviceFee * 0.3, distributeUser.id]
|
||
);
|
||
//记录转账记录
|
||
await db.execute(
|
||
'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 (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 (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
|
||
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.5, '用户服务费返现', 'system']
|
||
);
|
||
}
|
||
if (orderCount.total > 5 && orderCount.total <= 15) {
|
||
//给直营代理20%融豆给平台50%融豆给用户30%
|
||
await db.execute(
|
||
'UPDATE users SET balance = balance - ? WHERE id = ?',
|
||
[serviceFee * 0.15, distributeUser.inviter]
|
||
);
|
||
//给直营添加30%融豆
|
||
await db.execute(
|
||
'UPDATE users SET balance = balance - ? WHERE id = ?',
|
||
[serviceFee * 0.35, distributeUser.id]
|
||
);
|
||
//记录转账记录
|
||
await db.execute(
|
||
'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 (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 (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
|
||
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.5, '用户服务费返现', 'system']
|
||
);
|
||
}
|
||
if (orderCount.total > 15) {
|
||
//给直营代理20%融豆给平台50%融豆给用户30%
|
||
await db.execute(
|
||
'UPDATE users SET balance = balance - ? WHERE id = ?',
|
||
[serviceFee * 0.1, distributeUser.inviter]
|
||
);
|
||
//给直营添加30%融豆
|
||
await db.execute(
|
||
'UPDATE users SET balance = balance - ? WHERE id = ?',
|
||
[serviceFee * 0.4, distributeUser.id]
|
||
);
|
||
//记录转账记录
|
||
await db.execute(
|
||
'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']
|
||
);
|
||
//记录直营利润
|
||
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_operated', 'received', serviceFee * 0.4, '用户服务费返现', 'directly_operated']
|
||
);
|
||
//记录平台利润
|
||
await db.execute(
|
||
'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']
|
||
);
|
||
}
|
||
//记录服务费
|
||
await db.execute(
|
||
'INSERT INTO distribution (user_id,agent_id, amount, type) VALUES (?, ?, ?,?)',
|
||
[userId, distributeUser.id, serviceFee, 'direct_agent']
|
||
)
|
||
}
|
||
//要是用户之间分销
|
||
if (distributeUser.user_type == 'user') {
|
||
//查询用户是否有上级
|
||
let [userUpInfo] = await db.execute(
|
||
`SELECT *
|
||
FROM users
|
||
WHERE id = ?`,
|
||
[distributeUser.inviter]
|
||
)
|
||
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(
|
||
'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 (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 {
|
||
//用户没有上级
|
||
await db.execute(
|
||
'UPDATE users SET balance = balance - ? WHERE id = ?',
|
||
[serviceFee * 0.2, 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_user', 'received', serviceFee * 0.2, '用户服务费返现', 'operated']
|
||
);
|
||
//记录平台利润
|
||
await db.execute(
|
||
'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']
|
||
);
|
||
|
||
}
|
||
//记录服务费
|
||
await db.execute(
|
||
'INSERT INTO distribution (user_id,agent_id, amount, type) VALUES (?, ?, ?,?)',
|
||
[userId, distributeUser.id, serviceFee, 'user']
|
||
)
|
||
}
|
||
|
||
} else {
|
||
//判断用户此区域是否有代理
|
||
let [agentUser] = await db.execute(`
|
||
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 (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 (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 (to_user_id ,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
|
||
[userId, 3512, 'user_to_system', 'received', serviceFee, '用户服务费返现', 'system']
|
||
);
|
||
|
||
}
|
||
await db.execute(
|
||
'INSERT INTO distribution (user_id,agent_id, amount, type) VALUES (?, ?, ?,?)',
|
||
[userId, 3512, serviceFee, 'user']
|
||
)
|
||
|
||
}
|
||
await db.execute(
|
||
'UPDATE users SET is_distribute = ? WHERE id = ?',
|
||
[true, userId]
|
||
);
|
||
// 提交事务
|
||
await db.query('COMMIT');
|
||
|
||
res.json({
|
||
success: true,
|
||
message: '服务费扣除成功',
|
||
data: {
|
||
user_id: userId,
|
||
username: user.username,
|
||
deducted_amount: serviceFee,
|
||
remaining_balance: currentBalance - serviceFee
|
||
}
|
||
});
|
||
|
||
} catch (error) {
|
||
try {
|
||
// 发生错误时回滚事务
|
||
await db.query('ROLLBACK');
|
||
} catch (rollbackError) {
|
||
console.error('回滚失败:', rollbackError);
|
||
}
|
||
console.error('扣除服务费失败:', error);
|
||
res.status(500).json({success: false, message: '扣除服务费失败'});
|
||
}
|
||
});
|
||
|
||
module.exports = router; |