Files
jurong_circle_black/routes/users.js

1892 lines
72 KiB
JavaScript
Raw Normal View History

2025-08-26 10:06:23 +08:00
const express = require('express');
const bcrypt = require('bcryptjs');
2025-09-15 17:27:13 +08:00
const {getDB} = require('../database');
const {auth, adminAuth} = require('../middleware/auth');
2025-08-26 10:06:23 +08:00
const dayjs = require('dayjs');
const router = express.Router();
2025-08-28 09:14:56 +08:00
/**
* @swagger
* tags:
* name: Users
* description: 用户管理API
*/
2025-08-26 10:06:23 +08:00
router.post('/', auth, adminAuth, async (req, res) => {
2025-09-15 17:27:13 +08:00
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: '用户名和密码不能为空'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (!realName || !idCard) {
return res.status(400).json({success: false, message: '姓名和身份证号不能为空'});
}
if (!city || !districtId || !province) {
return res.status(400).json({success: false, message: '请选择城市和区县'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 验证身份证号格式
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: '身份证号格式不正确'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 检查用户是否已存在
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]
);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (existingUsers.length > 0) {
return res.status(400).json({success: false, message: '用户名、身份证号或手机号已存在'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 加密密码
const hashedPassword = await bcrypt.hash(password, 10);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 创建用户
console.log([username, hashedPassword, role, isSystemAccount, 0, realName, idCard, wechatQr, alipayQr, bankCard, unionpayQr, phone, province, city, districtId, user_type, inviter], 'info');
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
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]
);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
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')
`)
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 用户余额已在创建用户时设置为默认值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: '创建用户失败'});
2025-08-26 10:06:23 +08:00
}
});
2025-09-15 17:27:13 +08:00
2025-08-26 10:06:23 +08:00
router.get('/pending-audit', auth, adminAuth, async (req, res) => {
2025-09-15 17:27:13 +08:00
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: '获取待审核用户列表失败'});
}
2025-08-26 10:06:23 +08:00
});
// 获取用户列表用于转账(普通用户权限)
router.get('/for-transfer', auth, async (req, res) => {
2025-09-15 17:27:13 +08:00
try {
const db = getDB();
// 获取所有用户的基本信息(用于转账选择)
const [users] = await db.execute(
'SELECT id, username, real_name FROM users WHERE id != ? ORDER BY username',
[req.user.id]
);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
res.json({
success: true,
data: users
});
} catch (error) {
console.error('获取转账用户列表错误:', error);
res.status(500).json({success: false, message: '获取用户列表失败'});
}
2025-08-26 10:06:23 +08:00
});
// 获取用户列表(管理员权限)
router.get('/', auth, adminAuth, async (req, res) => {
2025-09-15 17:27:13 +08:00
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);
2025-09-26 14:40:02 +08:00
const limitNum = Math.max(1, parseInt(limit) || 10);
2025-09-15 17:27:13 +08:00
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}%`);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (role && role !== 'all') {
whereConditions.push('u.role = ?');
countParams.push(role);
listParams.push(role);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (city) {
whereConditions.push('u.city = ?');
countParams.push(city);
listParams.push(city);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (province) {
whereConditions.push('u.province = ?');
countParams.push(province);
listParams.push(province);
}
2025-09-10 18:10:40 +08:00
2025-09-15 17:27:13 +08:00
if (district) {
whereConditions.push('u.district_id = ?');
countParams.push(district);
listParams.push(district);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
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: '获取用户列表失败'});
}
2025-08-26 10:06:23 +08:00
});
// 获取当前用户的个人资料
router.get('/profile', auth, async (req, res) => {
2025-09-15 17:27:13 +08:00
try {
const db = getDB();
const userId = req.user.id;
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
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: '获取用户资料失败'});
2025-08-26 10:06:23 +08:00
}
});
/**
* 获取当前用户的收款码状态
* 用于检查用户是否已上传微信支付宝云闪付收款码
*/
router.get('/payment-codes-status', auth, async (req, res) => {
2025-09-15 17:27:13 +08:00
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]
);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (users.length === 0) {
return res.status(404).json({success: false, message: '用户不存在'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
const paymentCodes = users[0];
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
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: '获取收款码状态失败'});
}
2025-08-26 10:06:23 +08:00
});
// 获取用户收款信息
router.get('/payment-info/:userId', auth, async (req, res) => {
2025-09-15 17:27:13 +08:00
try {
const db = getDB();
const targetUserId = req.params.userId;
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
const [users] = await db.execute(
'SELECT id, username, wechat_qr, alipay_qr, unionpay_qr, bank_card FROM users WHERE id = ?',
[targetUserId]
);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (users.length === 0) {
return res.status(404).json({success: false, message: '用户不存在'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
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: '获取用户收款信息失败'});
}
2025-08-26 10:06:23 +08:00
});
// 获取当前用户的统计信息
router.get('/stats', auth, async (req, res) => {
2025-09-15 17:27:13 +08:00
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(
2025-09-26 14:40:02 +08:00
`SELECT
COUNT(DISTINCT from_user_id) AS count
FROM transfers
WHERE YEARWEEK(created_at, 1) = YEARWEEK(CURDATE() - INTERVAL 1 WEEK, 1)`
2025-09-15 17:27:13 +08:00
);
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: '获取用户统计失败'});
2025-08-26 10:06:23 +08:00
}
});
// 获取用户统计信息(管理员权限)- 必须在/:id路由之前定义
router.get('/admin/stats', auth, adminAuth, async (req, res) => {
2025-09-15 17:27:13 +08:00
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: '获取用户统计失败'});
}
2025-08-26 10:06:23 +08:00
});
// 获取当前用户积分
router.get('/points', auth, async (req, res) => {
2025-09-15 17:27:13 +08:00
try {
const userId = req.user.id;
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
const [users] = await getDB().execute(
'SELECT points FROM users WHERE id = ?',
[userId]
);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (users.length === 0) {
return res.status(404).json({success: false, message: '用户不存在'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
res.json({
success: true,
points: users[0].points
});
} catch (error) {
console.error('获取用户积分错误:', error);
res.status(500).json({success: false, message: '获取用户积分失败'});
}
2025-08-26 10:06:23 +08:00
});
// 获取用户积分历史记录
router.get('/points/history', auth, async (req, res) => {
2025-09-15 17:27:13 +08:00
try {
const userId = req.user.id;
const {page = 1, limit = 20, type} = req.query;
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 确保参数为有效数字
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);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
let whereClause = 'WHERE user_id = ?';
let queryParams = [userId];
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
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
);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
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: '获取积分历史失败'});
}
2025-08-26 10:06:23 +08:00
});
// 获取用户增长趋势数据(管理员权限)
router.get('/growth-trend', auth, adminAuth, async (req, res) => {
2025-09-15 17:27:13 +08:00
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
});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
res.json({
success: true,
data: result
});
} catch (error) {
console.error('获取用户增长趋势错误:', error);
res.status(500).json({success: false, message: '获取用户增长趋势失败'});
}
2025-08-26 10:06:23 +08:00
});
// 获取日收入统计数据(管理员权限)
router.get('/daily-revenue', auth, adminAuth, async (req, res) => {
2025-09-15 17:27:13 +08:00
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
});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
res.json({
success: true,
data: result
});
} catch (error) {
console.error('获取日收入统计错误:', error);
res.status(500).json({success: false, message: '获取日收入统计失败'});
}
2025-08-26 10:06:23 +08:00
});
// 获取当前用户个人资料
router.get('/profile', auth, async (req, res) => {
2025-09-15 17:27:13 +08:00
try {
const db = getDB();
const userId = req.user.id;
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
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]
);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (users.length === 0) {
return res.status(404).json({success: false, message: '用户不存在'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 转换字段名以匹配前端
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: '获取用户个人资料失败'});
}
2025-08-26 10:06:23 +08:00
});
// 更新当前用户个人资料
router.put('/profile', auth, async (req, res) => {
2025-09-15 17:27:13 +08:00
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: '用户名、身份证号或手机号已被使用'});
}
}
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 验证身份证号格式
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: '身份证号格式不正确'});
}
2025-08-26 10:06:23 +08:00
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 构建更新字段
const updateFields = [];
const updateValues = [];
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (finalUsername !== undefined) {
updateFields.push('username = ?');
updateValues.push(finalUsername);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (avatar !== undefined) {
updateFields.push('avatar = ?');
updateValues.push(avatar);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (realName !== undefined) {
updateFields.push('real_name = ?');
updateValues.push(realName);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (idCard !== undefined) {
updateFields.push('id_card = ?');
updateValues.push(idCard);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (phone !== undefined) {
updateFields.push('phone = ?');
updateValues.push(phone);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 添加城市和地区字段更新
if (city !== undefined) {
updateFields.push('city = ?');
updateValues.push(city);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (districtId !== undefined) {
updateFields.push('district_id = ?');
updateValues.push(districtId);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 检查是否更新了需要重新审核的关键信息
let needsReaudit = false;
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (wechatQr !== undefined) {
updateFields.push('wechat_qr = ?');
updateValues.push(wechatQr);
needsReaudit = true;
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (alipayQr !== undefined) {
updateFields.push('alipay_qr = ?');
updateValues.push(alipayQr);
needsReaudit = true;
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (bankCard !== undefined) {
updateFields.push('bank_card = ?');
updateValues.push(bankCard);
needsReaudit = true;
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (unionpayQr !== undefined) {
updateFields.push('unionpay_qr = ?');
updateValues.push(unionpayQr);
needsReaudit = true;
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (city !== undefined) {
updateFields.push('city = ?');
updateValues.push(city);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (districtId !== undefined) {
updateFields.push('district_id = ?');
updateValues.push(districtId);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (businessLicense !== undefined) {
updateFields.push('business_license = ?');
updateValues.push(businessLicense);
needsReaudit = true;
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (idCardFront !== undefined) {
updateFields.push('id_card_front = ?');
updateValues.push(idCardFront);
needsReaudit = true;
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (idCardBack !== undefined) {
updateFields.push('id_card_back = ?');
updateValues.push(idCardBack);
needsReaudit = true;
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 如果更新了关键信息且用户不是管理员,则重置审核状态为待审核
if (needsReaudit && req.user.role !== 'admin') {
updateFields.push('audit_status = ?');
updateValues.push('pending');
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (updateFields.length === 0) {
return res.status(400).json({success: false, message: '没有要更新的字段'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
updateValues.push(userId);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
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: '更新个人资料失败'});
}
2025-08-26 10:06:23 +08:00
});
// 获取用户详情
router.get('/:id', auth, async (req, res) => {
2025-09-15 17:27:13 +08:00
try {
const db = getDB();
const userId = req.params.id;
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 只有管理员或用户本人可以查看详情
if (req.user.role !== 'admin' && req.user.id != userId) {
return res.status(403).json({success: false, message: '权限不足'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
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]
);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (users.length === 0) {
return res.status(404).json({success: false, message: '用户不存在'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
res.json({success: true, user: users[0]});
} catch (error) {
console.error('获取用户详情错误:', error);
res.status(500).json({success: false, message: '获取用户详情失败'});
}
2025-08-26 10:06:23 +08:00
});
// 更新用户信息
router.put('/:id', auth, async (req, res) => {
2025-09-15 17:27:13 +08:00
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: '权限不足'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 非管理员不能修改角色
if (req.user.role !== 'admin' && role) {
return res.status(403).json({success: false, message: '无权限修改用户角色'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 检查用户名、身份证号和手机号是否已被其他用户使用
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: '用户名、身份证号或手机号已被使用'});
}
}
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 验证身份证号格式
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: '身份证号格式不正确'});
}
2025-08-26 10:06:23 +08:00
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 构建更新字段
const updateFields = [];
const updateValues = [];
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (username) {
updateFields.push('username = ?');
updateValues.push(username);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 处理密码更新
if (password && password.trim() !== '') {
const hashedPassword = await bcrypt.hash(password, 10);
updateFields.push('password = ?');
updateValues.push(hashedPassword);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (role && req.user.role === 'admin') {
updateFields.push('role = ?');
updateValues.push(role);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 只有管理员可以修改账户类型
if (isSystemAccount !== undefined && req.user.role === 'admin') {
updateFields.push('is_system_account = ?');
updateValues.push(isSystemAccount);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (avatar !== undefined) {
updateFields.push('avatar = ?');
updateValues.push(avatar);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (realName !== undefined) {
updateFields.push('real_name = ?');
updateValues.push(realName);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (idCard !== undefined) {
updateFields.push('id_card = ?');
updateValues.push(idCard);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (phone !== undefined) {
updateFields.push('phone = ?');
updateValues.push(phone);
}
if (city !== undefined) {
updateFields.push('city = ?');
updateValues.push(city);
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (districtId !== undefined) {
updateFields.push('district_id = ?');
updateValues.push(districtId);
}
// 检查是否更新了需要重新审核的关键信息
let needsReaudit = false;
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (wechatQr !== undefined) {
updateFields.push('wechat_qr = ?');
updateValues.push(wechatQr);
needsReaudit = true;
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (alipayQr !== undefined) {
updateFields.push('alipay_qr = ?');
updateValues.push(alipayQr);
needsReaudit = true;
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (bankCard !== undefined) {
updateFields.push('bank_card = ?');
updateValues.push(bankCard);
needsReaudit = true;
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
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;
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 如果更新了关键信息且用户不是管理员,则重置审核状态为待审核
if (needsReaudit && req.user.role !== 'admin') {
updateFields.push('audit_status = ?');
updateValues.push('pending');
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (updateFields.length === 0) {
return res.status(400).json({success: false, message: '没有要更新的字段'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
updateValues.push(userId);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
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]
);
2025-08-26 10:06:23 +08:00
2025-09-15 17:27:13 +08:00
res.json({
success: true,
message: '用户信息更新成功',
user: updatedUsers[0]
});
} catch (error) {
console.error('更新用户信息错误:', error);
res.status(500).json({success: false, message: '更新用户信息失败'});
}
});
2025-08-26 10:06:23 +08:00
// 删除用户(管理员权限)
router.delete('/:id', auth, adminAuth, async (req, res) => {
2025-09-15 17:27:13 +08:00
try {
const db = getDB();
const userId = req.params.id;
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 不能删除自己
if (req.user.id == userId) {
return res.status(400).json({success: false, message: '不能删除自己的账户'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 检查用户是否存在
const [users] = await db.execute(
'SELECT id FROM users WHERE id = ?',
[userId]
);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (users.length === 0) {
return res.status(404).json({success: false, message: '用户不存在'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 删除用户
await db.execute('DELETE FROM users WHERE id = ?', [userId]);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
res.json({success: true, message: '用户删除成功'});
} catch (error) {
console.error('删除用户错误:', error);
res.status(500).json({success: false, message: '删除用户失败'});
}
2025-08-26 10:06:23 +08:00
});
/**
* 审核用户管理员权限
*/
router.put('/:id/audit', auth, adminAuth, async (req, res) => {
2025-09-15 17:27:13 +08:00
try {
const db = getDB();
const userId = req.params.id;
const {action, note} = req.body; // action: 'approve' 或 'reject'
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (!action || !['approve', 'reject'].includes(action)) {
return res.status(400).json({success: false, message: '审核操作无效'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 检查用户是否存在且为待审核状态
const [users] = await db.execute(
'SELECT id, username, audit_status FROM users WHERE id = ?',
[userId]
);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (users.length === 0) {
return res.status(404).json({success: false, message: '用户不存在'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
const user = users[0];
if (user.audit_status !== 'pending') {
return res.status(400).json({success: false, message: '该用户不是待审核状态'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 更新审核状态
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: '审核用户失败'});
}
2025-08-26 10:06:23 +08:00
});
/**
* 获取用户审核详情管理员权限
*/
router.get('/:id/audit-detail', auth, adminAuth, async (req, res) => {
2025-09-15 17:27:13 +08:00
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: '用户不存在'});
}
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
res.json({
success: true,
data: users[0]
});
} catch (error) {
console.error('获取用户审核详情错误:', error);
res.status(500).json({success: false, message: '获取用户审核详情失败'});
}
2025-08-26 10:06:23 +08:00
});
2025-09-10 18:10:40 +08:00
2025-09-02 09:29:20 +08:00
router.put('/:id/distribute', auth, async (req, res) => {
2025-09-15 17:27:13 +08:00
try {
const db = getDB();
const userId = req.params.id;
const {is_distribute} = req.body;
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
if (typeof is_distribute !== 'boolean') {
return res.status(400).json({success: false, message: '分发状态无效'});
}
2025-09-02 09:29:20 +08:00
2025-09-10 18:10:40 +08:00
2025-09-15 17:27:13 +08:00
// 检查用户是否存在
const [users] = await db.execute(
'SELECT id,user_type FROM users WHERE id = ?',
[userId]
);
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
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) {
2025-09-10 18:10:40 +08:00
2025-09-15 17:27:13 +08:00
}
2025-09-10 18:10:40 +08:00
})
/**
* 扣除2980融豆服务费
2025-09-15 17:27:13 +08:00
*/
2025-09-10 18:10:40 +08:00
router.post('/:id/deduct-service-fee', auth, async (req, res) => {
2025-09-15 17:27:13 +08:00
const db = getDB();
try {
const userId = req.params.id;
const serviceFee = 2980; // 服务费金额
2025-09-02 09:29:20 +08:00
2025-09-15 17:27:13 +08:00
// 开始事务
await db.query('START TRANSACTION');
// 使用行级锁定查询用户信息,只锁定当前用户记录
const [users] = await db.execute(
'SELECT id, balance, username,district_id FROM users WHERE id = ? FOR UPDATE',
[userId]
2025-09-10 18:10:40 +08:00
);
2025-09-15 17:27:13 +08:00
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字段为负数所以减去服务费实际是增加负数
2025-09-10 18:10:40 +08:00
await db.execute(
2025-09-15 17:27:13 +08:00
'UPDATE users SET balance = balance + ? WHERE id = ?',
[serviceFee, userId]
2025-09-10 18:10:40 +08:00
);
2025-09-15 17:27:13 +08:00
//查找上级分销
let [distribute] = await db.execute(
'SELECT inviter FROM users WHERE id = ?',
[userId]
2025-09-10 18:10:40 +08:00
);
2025-09-15 17:27:13 +08:00
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]
2025-09-26 14:40:02 +08:00
//分销是代理
2025-09-15 17:27:13 +08:00
if (distributeUser.user_type == 'agent') {
2025-09-26 14:40:02 +08:00
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']
);
}
2025-09-15 17:27:13 +08:00
//记录平台利润
await db.execute(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-15 17:27:13 +08:00
[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(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-16 17:39:51 +08:00
[userId, distributeUser.id, 'user_to_agent', 'received', serviceFee * 0.5, '用户服务费返现', 'agent_operated']
2025-09-15 17:27:13 +08:00
);
//记录平台利润
await db.execute(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-15 17:27:13 +08:00
[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) {
2025-09-16 17:39:51 +08:00
//给直营代理20%融豆给平台50% 融豆给用户30%
2025-09-15 17:27:13 +08:00
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(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-15 17:27:13 +08:00
[userId, distributeUser.inviter, 'user_to_agent', 'received', serviceFee * 0.2, '用户服务费返现', 'operated_agent']
);
//记录直营利润
await db.execute(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id, from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-15 17:27:13 +08:00
[userId, distributeUser.id, 'user_to_operated', 'received', serviceFee * 0.3, '用户服务费返现', 'directly_operated']
);
//记录平台利润
await db.execute(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-15 17:27:13 +08:00
[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(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-16 17:39:51 +08:00
[userId, distributeUser.inviter, 'user_to_agent', 'received', serviceFee * 0.15, '用户服务费返现', 'agent_operated']
2025-09-15 17:27:13 +08:00
);
//记录直营利润
await db.execute(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-16 17:39:51 +08:00
[userId, distributeUser.id, 'user_to_operated', 'received', serviceFee * 0.35, '用户服务费返现', 'directly_operated']
2025-09-15 17:27:13 +08:00
);
//记录平台利润
await db.execute(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-15 17:27:13 +08:00
[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(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id ,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-16 17:39:51 +08:00
[userId, distributeUser.inviter, 'user_to_agent', 'received', serviceFee * 0.1, '用户服务费返现', 'agent_operated']
2025-09-15 17:27:13 +08:00
);
//记录直营利润
await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-16 17:39:51 +08:00
[userId, distributeUser.id, 'user_to_operated', 'received', serviceFee * 0.4, '用户服务费返现', 'directly_operated']
2025-09-15 17:27:13 +08:00
);
//记录平台利润
await db.execute(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-15 17:27:13 +08:00
[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') {
2025-09-26 14:40:02 +08:00
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']
);
}
2025-09-15 17:27:13 +08:00
//给用户分配
await db.execute(
'UPDATE users SET balance = balance - ? WHERE id = ?',
[serviceFee * 0.2, distributeUser.id]
);
//记录用户转账信息
await db.execute(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id ,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-16 17:39:51 +08:00
[userId, distributeUser.id, 'user_to_user', 'received', serviceFee * 0.2, '用户服务费返现', 'operated']
2025-09-15 17:27:13 +08:00
);
2025-09-26 14:40:02 +08:00
2025-09-15 17:27:13 +08:00
//记录平台利润
await db.execute(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-15 17:27:13 +08:00
[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(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-16 17:39:51 +08:00
[userId, distributeUser.id, 'user_to_user', 'received', serviceFee * 0.2, '用户服务费返现', 'operated']
2025-09-15 17:27:13 +08:00
);
//记录平台利润
await db.execute(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id ,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-15 17:27:13 +08:00
[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']
)
}
2025-09-10 18:10:40 +08:00
} else {
2025-09-15 17:27:13 +08:00
//判断用户此区域是否有代理
let [agentUser] = await db.execute(`
2025-09-26 14:40:02 +08:00
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])
2025-09-15 17:27:13 +08:00
if (agentUser.length > 0) {
2025-09-26 14:40:02 +08:00
//给区域代理分区域保护
await db.execute(`
UPDATE users
SET balance = balance - ?
WHERE id = ?
`, [serviceFee * 0.05, agentUser[0].user_id])
2025-09-15 17:27:13 +08:00
//给代理分成5%
await db.execute(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id ,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-16 17:39:51 +08:00
[userId, agentUser[0].user_id, 'user_to_regional', 'received', serviceFee * 0.05, '区域保护服务费返现', 'agent']
2025-09-15 17:27:13 +08:00
)
//记录平台利润
await db.execute(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id , from_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-15 17:27:13 +08:00
[userId, 3512, 'user_to_system', 'received', serviceFee * 0.95, '用户服务费返现', 'system']
);
} else {
//记录平台利润
await db.execute(
2025-09-26 14:40:02 +08:00
'INSERT INTO transfers (to_user_id ,from_user_id , transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
2025-09-15 17:27:13 +08:00
[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']
)
}
2025-09-10 18:10:40 +08:00
await db.execute(
2025-09-15 17:27:13 +08:00
'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: '扣除服务费失败'});
2025-09-10 18:10:40 +08:00
}
});
2025-08-26 10:06:23 +08:00
module.exports = router;