363 lines
12 KiB
JavaScript
363 lines
12 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const {getDB} = require('../database');
|
|
const {agentAuth} = require('../middleware/agentAuth');
|
|
const {logger} = require('../config/logger');
|
|
const bcrypt = require('bcryptjs');
|
|
|
|
/**
|
|
* 获取代理下级用户列表
|
|
* GET /api/users
|
|
*/
|
|
router.get('/', agentAuth, async (req, res) => {
|
|
try {
|
|
const agentId = req.agent.id;
|
|
const {
|
|
page = 1,
|
|
limit = 20,
|
|
search,
|
|
role,
|
|
sort_by = 'created_at',
|
|
sort_order = 'desc',
|
|
city,
|
|
district
|
|
} = req.query;
|
|
|
|
const pageNum = parseInt(page) || 1;
|
|
const limitNum = parseInt(limit) || 20;
|
|
const offset = (pageNum - 1) * limitNum;
|
|
|
|
// 构建查询条件
|
|
let whereConditions = ['am.agent_id = ?'];
|
|
let queryParams = [agentId];
|
|
|
|
if (search) {
|
|
whereConditions.push('(u.username LIKE ? OR u.real_name LIKE ? OR u.phone LIKE ?)');
|
|
queryParams.push(`%${search}%`, `%${search}%`, `%${search}%`);
|
|
}
|
|
|
|
if (role) {
|
|
whereConditions.push('u.role = ?');
|
|
queryParams.push(role);
|
|
}
|
|
|
|
if (city) {
|
|
whereConditions.push('u.city = ?');
|
|
queryParams.push(city);
|
|
}
|
|
|
|
if (district) {
|
|
whereConditions.push('u.district_id = ?');
|
|
queryParams.push(district);
|
|
}
|
|
|
|
const whereClause = whereConditions.join(' AND ');
|
|
|
|
// 验证排序字段
|
|
const allowedSortFields = ['created_at', 'updated_at', 'balance', 'username', 'real_name'];
|
|
const sortBy = allowedSortFields.includes(sort_by) ? sort_by : 'created_at';
|
|
const sortOrder = sort_order.toLowerCase() === 'asc' ? 'ASC' : 'DESC';
|
|
|
|
// 查询用户列表
|
|
const usersQuery = `
|
|
SELECT u.id,
|
|
u.username,
|
|
u.real_name,
|
|
u.phone,
|
|
u.email,
|
|
u.avatar,
|
|
u.role,
|
|
u.city,
|
|
u.balance,
|
|
u.points,
|
|
u.created_at,
|
|
u.updated_at,
|
|
am.created_at as join_date,
|
|
(SELECT CAST(COALESCE(SUM(amount), 0) AS DECIMAL(10, 2))
|
|
FROM transfers
|
|
WHERE from_user_id = u.id
|
|
AND DATE(created_at) = CURDATE()) as today_transfer_out,
|
|
(SELECT CAST(COALESCE(SUM(amount), 0) AS DECIMAL(10, 2))
|
|
FROM transfers
|
|
WHERE to_user_id = u.id
|
|
AND DATE(created_at) = CURDATE()) as today_transfer_in
|
|
FROM agent_merchants am
|
|
LEFT JOIN users u ON am.merchant_id = u.id
|
|
WHERE ${whereClause}
|
|
ORDER BY u.${sortBy} ${sortOrder}
|
|
LIMIT ${limitNum} OFFSET ${offset}
|
|
`;
|
|
|
|
const [users] = await getDB().execute(usersQuery, queryParams);
|
|
|
|
// 查询总数
|
|
const countQuery = `
|
|
SELECT COUNT(*) as total
|
|
FROM agent_merchants am
|
|
LEFT JOIN users u ON am.merchant_id = u.id
|
|
WHERE ${whereClause}
|
|
`;
|
|
|
|
const [countResult] = await getDB().execute(countQuery, queryParams);
|
|
const total = countResult[0]?.total || 0;
|
|
|
|
// 查询统计信息
|
|
const [statsResult] = await getDB().execute(`
|
|
SELECT COUNT(*) as total_users,
|
|
COUNT(CASE WHEN u.audit_status = 'approved' THEN 1 END) as active_users,
|
|
CAST(COALESCE(SUM(u.balance), 0) AS DECIMAL(10, 2)) as total_balance,
|
|
COUNT(CASE WHEN DATE(am.created_at) = CURDATE() THEN 1 END) as today_new_users
|
|
FROM agent_merchants am
|
|
LEFT JOIN users u ON am.merchant_id = u.id
|
|
WHERE am.agent_id = ?
|
|
`, [agentId]);
|
|
|
|
const stats = statsResult[0] || {
|
|
total_users: 0,
|
|
active_users: 0,
|
|
total_balance: '0.00',
|
|
today_new_users: 0
|
|
};
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
users,
|
|
pagination: {
|
|
current_page: pageNum,
|
|
per_page: limitNum,
|
|
total,
|
|
total_pages: Math.ceil(total / limitNum)
|
|
},
|
|
stats
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
logger.error('获取用户列表失败', {
|
|
error: error.message,
|
|
stack: error.stack,
|
|
agentId: req.agent?.id
|
|
});
|
|
|
|
res.status(500).json({
|
|
success: false,
|
|
message: '获取用户列表失败'
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* 获取直营列表
|
|
*
|
|
*/
|
|
router.get('/directly_operated', agentAuth, async (req, res) => {
|
|
try {
|
|
const agentId = req.agent.userId; // 修正为正确的字段名
|
|
const {
|
|
page = 1,
|
|
size = 20,
|
|
search,
|
|
sort_by = 'created_at',
|
|
sort_order = 'desc',
|
|
} = req.query;
|
|
|
|
// 转换分页参数
|
|
const pageNum = parseInt(page);
|
|
const pageSize = parseInt(size);
|
|
const offset = (pageNum - 1) * pageSize;
|
|
|
|
// 构建查询条件
|
|
let whereConditions = ['u.inviter = ?'];
|
|
let queryParams = [agentId];
|
|
whereConditions.push('u.user_type = ?')
|
|
queryParams.push('directly_operated');
|
|
// 添加搜索条件
|
|
if (search) {
|
|
whereConditions.push('(u.username LIKE ? OR u.real_name LIKE ? OR u.phone LIKE ?)');
|
|
const searchPattern = `%${search}%`;
|
|
queryParams.push(searchPattern, searchPattern, searchPattern);
|
|
}
|
|
|
|
const whereClause = whereConditions.join(' AND ');
|
|
|
|
// 验证排序字段和顺序
|
|
const validSortFields = ['id', 'username', 'real_name', 'created_at', 'updated_at', 'balance', 'points'];
|
|
const validSortOrders = ['asc', 'desc'];
|
|
const sortField = validSortFields.includes(sort_by) ? sort_by : 'created_at';
|
|
const sortOrder = validSortOrders.includes(sort_order.toLowerCase()) ? sort_order.toUpperCase() : 'DESC';
|
|
|
|
// 查询总数
|
|
const [countResult] = await getDB().execute(`
|
|
SELECT COUNT(*) as total
|
|
FROM users u
|
|
WHERE ${whereClause}
|
|
`, queryParams);
|
|
const total = countResult[0].total;
|
|
|
|
// 查询用户列表
|
|
const [users] = await getDB().execute(`
|
|
SELECT u.*
|
|
FROM users u
|
|
WHERE ${whereClause}
|
|
ORDER BY u.${sortField} ${sortOrder}
|
|
LIMIT ${pageSize} OFFSET ${offset}
|
|
`, [...queryParams]);
|
|
|
|
// 计算分页信息
|
|
const totalPages = Math.ceil(total / pageSize);
|
|
const hasNextPage = pageNum < totalPages;
|
|
const hasPrevPage = pageNum > 1;
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
users,
|
|
pagination: {
|
|
current_page: pageNum,
|
|
per_page: pageSize,
|
|
total,
|
|
total_pages: totalPages,
|
|
has_next_page: hasNextPage,
|
|
has_prev_page: hasPrevPage
|
|
}
|
|
}
|
|
});
|
|
} catch (error) {
|
|
logger.error('获取直营用户列表失败', {
|
|
error: error.message,
|
|
stack: error.stack,
|
|
agentId: req.agent?.id
|
|
});
|
|
|
|
res.status(500).json({
|
|
success: false,
|
|
message: '获取直营用户列表失败'
|
|
});
|
|
}
|
|
})
|
|
|
|
|
|
/**
|
|
* 创建直营用户
|
|
*/
|
|
router.post('/create', agentAuth, async (req, res) => {
|
|
try {
|
|
const db = getDB();
|
|
const agentId = req.agent.userId;
|
|
const {username, real_name, phone, password, avatar} = req.body;
|
|
|
|
// 验证必填字段
|
|
if (!username || !real_name || !phone || !password) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: '请填写完整用户信息'
|
|
});
|
|
}
|
|
// 检查用户是否已存在
|
|
const [existingUsers] = await db.execute(
|
|
'SELECT id FROM users WHERE username = ? OR phone = ?',
|
|
[username, phone || null]
|
|
);
|
|
|
|
if (existingUsers.length > 0) {
|
|
return res.status(400).json({success: false, message: '用户名或手机号已存在'});
|
|
}
|
|
const hashedPassword = await bcrypt.hash(password, 10);
|
|
const [agent] = await db.execute('SELECT * FROM users WHERE id = ?', [agentId]);
|
|
console.log(agent, 'agent');
|
|
const {city, district_id, province} = agent[0];
|
|
const [result] = await db.execute(
|
|
'INSERT INTO users (username, phone, password, avatar, points, audit_status, city, district_id, payment_status, province, inviter,user_type,real_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?, "paid", ?, ?,?,?)',
|
|
[username, phone, hashedPassword, avatar, 0, 'approved', city, district_id, province, agentId, 'directly_operated',real_name]
|
|
);
|
|
const userId = result.insertId;
|
|
res.json({
|
|
success: true,
|
|
message: '直营用户创建成功',
|
|
userId
|
|
});
|
|
} catch (error) {
|
|
logger.error('创建直营用户失败', {
|
|
error: error.message,
|
|
stack: error.stack,
|
|
agentId: req.agent?.id
|
|
});
|
|
|
|
res.status(500).json({
|
|
success: false,
|
|
message: '创建直营用户失败'
|
|
});
|
|
}
|
|
})
|
|
|
|
/**
|
|
* 直营用户提现
|
|
*
|
|
*/
|
|
router.post('/withdraw', agentAuth, async (req, res) => {
|
|
const db = getDB();
|
|
try {
|
|
const agentId = req.agent.userId;
|
|
const {userId, amount} = req.body;
|
|
|
|
// 验证必填字段
|
|
if (!userId || !amount) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: '请填写完整提现信息'
|
|
});
|
|
}
|
|
let [userInfo] = await db.execute(
|
|
`SELECT *
|
|
FROM users
|
|
WHERE id = ?
|
|
AND user_type = 'directly_operated'`,
|
|
[userId]
|
|
)
|
|
|
|
if (userInfo.length > 0) {
|
|
let balance = Math.abs(userInfo[0].balance)
|
|
if (balance >= amount) {
|
|
await db.query('START TRANSACTION');
|
|
await db.execute(`UPDATE users
|
|
SET balance = balance + ?
|
|
WHERE id = ?`, [amount, userId])
|
|
await db.execute(
|
|
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
|
|
[agentId, userId, 'agent_to_operated', 'received', amount, '直营商户提现', 'withdraw']
|
|
);
|
|
// 提交事务
|
|
await db.query('COMMIT');
|
|
res.json({
|
|
success: true,
|
|
message: '提现成功'
|
|
})
|
|
} else {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: '用户余额不足'
|
|
})
|
|
}
|
|
|
|
} else {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: '无此直营用户'
|
|
})
|
|
}
|
|
} catch (error) {
|
|
await db.query('ROLLBACK');
|
|
logger.error('直营用户提现失败', {
|
|
error: error.message,
|
|
stack: error.stack,
|
|
agentId: req.agent?.id
|
|
});
|
|
res.status(500).json({
|
|
success: false,
|
|
message: '系统错误请联系管理员'
|
|
})
|
|
}
|
|
})
|
|
|
|
module.exports = router; |