344 lines
9.1 KiB
JavaScript
344 lines
9.1 KiB
JavaScript
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.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: '获取用户列表失败'
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* 获取单个用户详情
|
|
* 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.balance,
|
|
u.points,
|
|
u.id_card,
|
|
u.business_license,
|
|
u.payment_qr_code,
|
|
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.balance,
|
|
u.points,
|
|
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; |