Files
jurong_circle_agent_black/routes/users.js
2025-09-15 17:28:12 +08:00

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;