初次提交
This commit is contained in:
354
routes/users.js
Normal file
354
routes/users.js
Normal file
@@ -0,0 +1,354 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { getDB } = require('../database');
|
||||
const { agentAuth } = require('../middleware/agentAuth');
|
||||
const { logger } = require('../config/logger');
|
||||
|
||||
/**
|
||||
* 获取代理下级用户列表
|
||||
* 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 = ?');
|
||||
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.district,
|
||||
u.account_type,
|
||||
u.balance,
|
||||
u.points,
|
||||
u.status,
|
||||
u.last_login_at,
|
||||
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.status = 'active' 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: '获取用户列表失败'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取单个用户详情
|
||||
* GET /api/users/:id
|
||||
*/
|
||||
router.get('/:id', agentAuth, async (req, res) => {
|
||||
try {
|
||||
const agentId = req.agent.id;
|
||||
const userId = req.params.id;
|
||||
|
||||
// 验证用户是否属于当前代理
|
||||
const [users] = await getDB().execute(`
|
||||
SELECT
|
||||
u.id,
|
||||
u.username,
|
||||
u.real_name,
|
||||
u.phone,
|
||||
u.email,
|
||||
u.avatar,
|
||||
u.role,
|
||||
u.city,
|
||||
u.district,
|
||||
u.account_type,
|
||||
u.balance,
|
||||
u.points,
|
||||
u.status,
|
||||
u.id_card,
|
||||
u.business_license,
|
||||
u.payment_qr_code,
|
||||
u.last_login_at,
|
||||
u.created_at,
|
||||
u.updated_at,
|
||||
am.created_at as join_date
|
||||
FROM agent_merchants am
|
||||
LEFT JOIN users u ON am.merchant_id = u.id
|
||||
WHERE am.agent_id = ? AND u.id = ?
|
||||
`, [agentId, userId]);
|
||||
|
||||
if (users.length === 0) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '用户不存在或不属于当前代理'
|
||||
});
|
||||
}
|
||||
|
||||
const user = users[0];
|
||||
|
||||
// 获取用户转账统计
|
||||
const [transferStats] = await getDB().execute(`
|
||||
SELECT
|
||||
COUNT(*) as total_transfers,
|
||||
CAST(COALESCE(SUM(CASE WHEN from_user_id = ? THEN amount ELSE 0 END), 0) AS DECIMAL(10,2)) as total_transfer_out,
|
||||
CAST(COALESCE(SUM(CASE WHEN to_user_id = ? THEN amount ELSE 0 END), 0) AS DECIMAL(10,2)) as total_transfer_in,
|
||||
COUNT(CASE WHEN DATE(created_at) = CURDATE() THEN 1 END) as today_transfers,
|
||||
CAST(COALESCE(SUM(CASE WHEN from_user_id = ? AND DATE(created_at) = CURDATE() THEN amount ELSE 0 END), 0) AS DECIMAL(10,2)) as today_transfer_out,
|
||||
CAST(COALESCE(SUM(CASE WHEN to_user_id = ? AND DATE(created_at) = CURDATE() THEN amount ELSE 0 END), 0) AS DECIMAL(10,2)) as today_transfer_in
|
||||
FROM transfers
|
||||
WHERE from_user_id = ? OR to_user_id = ?
|
||||
`, [userId, userId, userId, userId, userId, userId]);
|
||||
|
||||
user.transfer_stats = transferStats[0] || {
|
||||
total_transfers: 0,
|
||||
total_transfer_out: '0.00',
|
||||
total_transfer_in: '0.00',
|
||||
today_transfers: 0,
|
||||
today_transfer_out: '0.00',
|
||||
today_transfer_in: '0.00'
|
||||
};
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: user
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('获取用户详情失败', {
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
agentId: req.agent?.id,
|
||||
userId: req.params.id
|
||||
});
|
||||
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取用户详情失败'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 导出用户数据
|
||||
* GET /api/users/export
|
||||
*/
|
||||
router.get('/export/data', agentAuth, async (req, res) => {
|
||||
try {
|
||||
const agentId = req.agent.id;
|
||||
const { format = 'json', search, role, city, district } = req.query;
|
||||
|
||||
// 构建查询条件
|
||||
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 = ?');
|
||||
queryParams.push(district);
|
||||
}
|
||||
|
||||
const whereClause = whereConditions.join(' AND ');
|
||||
|
||||
// 查询用户数据
|
||||
const [users] = await getDB().execute(`
|
||||
SELECT
|
||||
u.id,
|
||||
u.username,
|
||||
u.real_name,
|
||||
u.phone,
|
||||
u.email,
|
||||
u.role,
|
||||
u.city,
|
||||
u.district,
|
||||
u.account_type,
|
||||
u.balance,
|
||||
u.points,
|
||||
u.status,
|
||||
u.created_at,
|
||||
am.created_at as join_date
|
||||
FROM agent_merchants am
|
||||
LEFT JOIN users u ON am.merchant_id = u.id
|
||||
WHERE ${whereClause}
|
||||
ORDER BY u.created_at DESC
|
||||
`, queryParams);
|
||||
|
||||
if (format === 'csv') {
|
||||
// 生成CSV格式
|
||||
const csvHeader = 'ID,用户名,真实姓名,手机号,邮箱,角色,城市,地区,账户类型,余额,积分,状态,注册时间,加入时间\n';
|
||||
const csvData = users.map(user => {
|
||||
return [
|
||||
user.id,
|
||||
user.username || '',
|
||||
user.real_name || '',
|
||||
user.phone || '',
|
||||
user.email || '',
|
||||
user.role || '',
|
||||
user.city || '',
|
||||
user.district || '',
|
||||
user.account_type || '',
|
||||
user.balance || '0.00',
|
||||
user.points || 0,
|
||||
user.status || '',
|
||||
user.created_at || '',
|
||||
user.join_date || ''
|
||||
].join(',');
|
||||
}).join('\n');
|
||||
|
||||
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
||||
res.setHeader('Content-Disposition', `attachment; filename="users_${Date.now()}.csv"`);
|
||||
res.send(csvHeader + csvData);
|
||||
} else {
|
||||
// 默认JSON格式
|
||||
res.json({
|
||||
success: true,
|
||||
data: users,
|
||||
exported_at: new Date().toISOString(),
|
||||
total: users.length
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.error('导出用户数据失败', {
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
agentId: req.agent?.id
|
||||
});
|
||||
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '导出用户数据失败'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user