const express = require('express'); const cors = require('cors'); const mysql = require('mysql2/promise'); const jwt = require('jsonwebtoken'); const bcrypt = require('bcryptjs'); const app = express(); // 中间件配置 app.use(cors({ origin: ['http://localhost:5173', 'http://localhost:3000'], credentials: true })); app.use(express.json({ limit: '50mb' })); app.use(express.urlencoded({ extended: true, limit: '50mb' })); // 数据库连接配置 const dbConfig = { host: '127.0.0.1', user: 'root', password: '', database: 'tg_manage', acquireTimeout: 60000, timeout: 60000, reconnect: true }; // JWT配置 const JWT_SECRET = 'tg-management-system-jwt-secret-2025'; // 数据库连接池 let pool; async function initDB() { pool = mysql.createPool({ ...dbConfig, waitForConnections: true, connectionLimit: 10, queueLimit: 0 }); console.log('📊 数据库连接池初始化成功'); } // JWT中间件 const authenticateToken = (req, res, next) => { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; if (!token) { return res.status(401).json({ success: false, code: 401, msg: '未提供访问令牌' }); } jwt.verify(token, JWT_SECRET, (err, user) => { if (err) { return res.status(403).json({ success: false, code: 403, msg: '令牌无效或已过期' }); } req.user = user; next(); }); }; // 统一错误处理 const handleError = (error, req, res, next) => { console.error('API错误:', error); res.status(500).json({ success: false, code: 500, msg: '服务器内部错误: ' + error.message, data: null }); }; // ==================== 认证相关接口 ==================== // 登录接口 app.post('/auth/login', async (req, res) => { try { const { username, password } = req.body || {}; if (!username || !password) { return res.status(400).json({ success: false, code: 400, msg: '用户名和密码不能为空', data: null }); } // 简单验证 - 生产环境应该从数据库验证 if (username === 'admin' && password === '111111') { const token = jwt.sign( { id: 1, username: 'admin', role: 'admin' }, JWT_SECRET, { expiresIn: '24h' } ); res.json({ success: true, code: 200, msg: '登录成功', data: { token, user: { id: 1, username: 'admin', role: 'admin', nickname: '系统管理员' } } }); } else { res.status(401).json({ success: false, code: 401, msg: '用户名或密码错误', data: null }); } } catch (error) { handleError(error, req, res); } }); // 获取用户信息接口 app.get('/auth/user', authenticateToken, (req, res) => { res.json({ success: true, code: 200, msg: '获取用户信息成功', data: { id: req.user.id, username: req.user.username, role: req.user.role, nickname: '系统管理员', avatar: null, permissions: ['*'] } }); }); // ==================== TG账号管理接口 ==================== // TG账号列表接口 app.post('/tgAccount/list', async (req, res) => { try { const connection = await pool.getConnection(); const page = (req.body && req.body.page) || 1; const size = (req.body && req.body.size) || 10; const phone = (req.body && req.body.phone) || ''; const status = req.body && req.body.status; const usageId = req.body && req.body.usageId; let whereConditions = []; let params = []; if (phone) { whereConditions.push('phone LIKE ?'); params.push(`%${phone}%`); } if (status !== undefined && status !== null && status !== '') { whereConditions.push('status = ?'); params.push(status); } if (usageId !== undefined && usageId !== null && usageId !== '') { whereConditions.push('usageId = ?'); params.push(usageId); } const whereClause = whereConditions.length > 0 ? 'WHERE ' + whereConditions.join(' AND ') : ''; // 查询总数 const [countResult] = await connection.execute( `SELECT COUNT(*) as total FROM accounts ${whereClause}`, params ); const total = countResult[0].total; // 查询数据 const offset = (page - 1) * size; const [rows] = await connection.execute( `SELECT id, phone, firstname, lastname, status, usageId, createdAt, updatedAt, lastOnline, isBan, about FROM accounts ${whereClause} ORDER BY id DESC LIMIT ${size} OFFSET ${offset}`, params ); // 统计数据 const [stats] = await connection.execute( `SELECT COUNT(*) as total, SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as active, SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) as inactive, SUM(CASE WHEN isBan = 1 THEN 1 ELSE 0 END) as banned FROM accounts` ); connection.release(); res.json({ success: true, code: 200, data: { list: rows, total: total, page: page, size: size, totalPages: Math.ceil(total / size), stats: stats[0] }, msg: '查询成功' }); } catch (error) { console.error('TG账号列表API错误:', error); res.status(500).json({ success: false, code: 500, data: null, msg: '服务器错误: ' + error.message }); } }); // TG账号详情接口 app.get('/tgAccount/:id', async (req, res) => { try { const connection = await pool.getConnection(); const { id } = req.params; const [rows] = await connection.execute( 'SELECT * FROM accounts WHERE id = ?', [id] ); connection.release(); if (rows.length === 0) { return res.status(404).json({ success: false, code: 404, msg: '账号不存在', data: null }); } res.json({ success: true, code: 200, msg: '获取成功', data: rows[0] }); } catch (error) { handleError(error, req, res); } }); // 创建TG账号接口 app.post('/tgAccount/add', async (req, res) => { try { const connection = await pool.getConnection(); const { phone, firstname, lastname, password, usageId } = req.body; if (!phone || !usageId) { return res.status(400).json({ success: false, code: 400, msg: '手机号和用途不能为空', data: null }); } const [result] = await connection.execute( `INSERT INTO accounts (phone, firstname, lastname, password, usageId, status, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, 1, NOW(), NOW())`, [phone, firstname || '', lastname || '', password || '', usageId] ); connection.release(); res.json({ success: true, code: 200, msg: '添加成功', data: { id: result.insertId } }); } catch (error) { if (error.code === 'ER_DUP_ENTRY') { res.status(400).json({ success: false, code: 400, msg: '手机号已存在', data: null }); } else { handleError(error, req, res); } } }); // 更新TG账号接口 app.put('/tgAccount/:id', async (req, res) => { try { const connection = await pool.getConnection(); const { id } = req.params; const { firstname, lastname, usageId, status, about } = req.body; await connection.execute( `UPDATE accounts SET firstname = ?, lastname = ?, usageId = ?, status = ?, about = ?, updatedAt = NOW() WHERE id = ?`, [firstname, lastname, usageId, status, about, id] ); connection.release(); res.json({ success: true, code: 200, msg: '更新成功', data: null }); } catch (error) { handleError(error, req, res); } }); // 删除TG账号接口 app.delete('/tgAccount/:id', async (req, res) => { try { const connection = await pool.getConnection(); const { id } = req.params; await connection.execute('DELETE FROM accounts WHERE id = ?', [id]); connection.release(); res.json({ success: true, code: 200, msg: '删除成功', data: null }); } catch (error) { handleError(error, req, res); } }); // 批量操作TG账号接口 app.post('/tgAccount/batch', async (req, res) => { try { const connection = await pool.getConnection(); const { ids, action } = req.body; if (!ids || !Array.isArray(ids) || ids.length === 0) { return res.status(400).json({ success: false, code: 400, msg: '请选择要操作的账号', data: null }); } const placeholders = ids.map(() => '?').join(','); switch (action) { case 'enable': await connection.execute( `UPDATE accounts SET status = 1, updatedAt = NOW() WHERE id IN (${placeholders})`, ids ); break; case 'disable': await connection.execute( `UPDATE accounts SET status = 0, updatedAt = NOW() WHERE id IN (${placeholders})`, ids ); break; case 'delete': await connection.execute( `DELETE FROM accounts WHERE id IN (${placeholders})`, ids ); break; default: return res.status(400).json({ success: false, code: 400, msg: '不支持的操作类型', data: null }); } connection.release(); res.json({ success: true, code: 200, msg: '批量操作成功', data: null }); } catch (error) { handleError(error, req, res); } }); // ==================== 账号用途管理接口 ==================== // 账号用途列表接口 app.post('/accountUsage/list', async (req, res) => { try { const connection = await pool.getConnection(); const pageIndex = (req.body && req.body.pageIndex) || 1; const pageSize = (req.body && req.body.pageSize) || 10; const type = (req.body && req.body.type) || ''; let whereClause = ''; let params = []; if (type) { whereClause = 'WHERE type LIKE ?'; params.push(`%${type}%`); } // 查询总数 const [countResult] = await connection.execute( `SELECT COUNT(*) as total FROM accounts_usage ${whereClause}`, params ); const total = countResult[0].total; // 查询数据 const offset = (pageIndex - 1) * pageSize; const [rows] = await connection.execute( `SELECT id, type, createdAt, updatedAt FROM accounts_usage ${whereClause} ORDER BY id DESC LIMIT ${pageSize} OFFSET ${offset}`, params ); connection.release(); res.json({ success: true, code: '200', message: '查询成功', data: { list: rows, totalRow: total, pageIndex: pageIndex, pageSize: pageSize } }); } catch (error) { console.error('账号用途API错误:', error); res.status(500).json({ success: false, code: '500', message: '服务器错误: ' + error.message, data: null }); } }); // 添加账号用途接口 app.post('/accountUsage/add', async (req, res) => { try { const connection = await pool.getConnection(); const { type } = req.body; if (!type) { return res.status(400).json({ success: false, code: '400', message: '用途名称不能为空', data: null }); } const [result] = await connection.execute( 'INSERT INTO accounts_usage (type, createdAt, updatedAt) VALUES (?, NOW(), NOW())', [type] ); connection.release(); res.json({ success: true, code: '200', message: '添加成功', data: { id: result.insertId } }); } catch (error) { console.error('添加账号用途错误:', error); res.status(500).json({ success: false, code: '500', message: '添加失败: ' + error.message, data: null }); } }); // 更新账号用途接口 app.post('/accountUsage/update', async (req, res) => { try { const connection = await pool.getConnection(); const { id, type } = req.body; if (!type) { return res.status(400).json({ success: false, code: '400', message: '用途名称不能为空', data: null }); } await connection.execute( 'UPDATE accounts_usage SET type = ?, updatedAt = NOW() WHERE id = ?', [type, id] ); connection.release(); res.json({ success: true, code: '200', message: '更新成功', data: null }); } catch (error) { console.error('更新账号用途错误:', error); res.status(500).json({ success: false, code: '500', message: '更新失败: ' + error.message, data: null }); } }); // 删除账号用途接口 app.delete('/accountUsage/:id', async (req, res) => { try { const connection = await pool.getConnection(); const { id } = req.params; await connection.execute('DELETE FROM accounts_usage WHERE id = ?', [id]); connection.release(); res.json({ success: true, code: '200', message: '删除成功', data: null }); } catch (error) { console.error('删除账号用途错误:', error); res.status(500).json({ success: false, code: '500', message: '删除失败: ' + error.message, data: null }); } }); // ==================== 统计分析接口 ==================== // 系统概览数据接口 app.get('/telegram/statistics/overview', async (req, res) => { try { const connection = await pool.getConnection(); // 账号统计 const [accountStats] = await connection.execute( `SELECT COUNT(*) as totalAccounts, SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as activeAccounts, SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) as inactiveAccounts, SUM(CASE WHEN isBan = 1 THEN 1 ELSE 0 END) as bannedAccounts FROM accounts` ); // 用途统计 const [usageStats] = await connection.execute( 'SELECT COUNT(*) as totalUsages FROM accounts_usage' ); // 在线统计(最近24小时) const [onlineStats] = await connection.execute( `SELECT COUNT(*) as onlineAccounts FROM accounts WHERE lastOnline >= DATE_SUB(NOW(), INTERVAL 24 HOUR)` ); connection.release(); res.json({ success: true, code: 200, msg: '获取成功', data: { accounts: accountStats[0], usages: usageStats[0].totalUsages, online: onlineStats[0].onlineAccounts, timestamp: new Date().toISOString() } }); } catch (error) { handleError(error, req, res); } }); // 账号统计数据接口 app.get('/telegram/statistics/account', async (req, res) => { try { const connection = await pool.getConnection(); // 按用途分组统计 const [usageStats] = await connection.execute( `SELECT u.type as usageName, COUNT(a.id) as count, SUM(CASE WHEN a.status = 1 THEN 1 ELSE 0 END) as active, SUM(CASE WHEN a.isBan = 1 THEN 1 ELSE 0 END) as banned FROM accounts_usage u LEFT JOIN accounts a ON u.id = a.usageId GROUP BY u.id, u.type ORDER BY count DESC` ); // 日活统计(最近7天) const [dailyStats] = await connection.execute( `SELECT DATE(lastOnline) as date, COUNT(*) as count FROM accounts WHERE lastOnline >= DATE_SUB(NOW(), INTERVAL 7 DAY) GROUP BY DATE(lastOnline) ORDER BY date DESC` ); connection.release(); res.json({ success: true, code: 200, msg: '获取成功', data: { usageStats, dailyStats } }); } catch (error) { handleError(error, req, res); } }); // ==================== 菜单权限接口 ==================== // 获取菜单权限接口 app.get('/telegram/permission/menus', authenticateToken, (req, res) => { const menus = [ { id: 1, title: '仪表板', path: '/dashboard', icon: 'DashboardOutlined', children: [] }, { id: 2, title: '账号管理', path: '/account-manage', icon: 'UserOutlined', children: [ { id: 21, title: 'TG账号列表', path: '/account-manage/list', icon: 'UnorderedListOutlined' }, { id: 22, title: 'TG账号用途', path: '/account-manage/usage', icon: 'TagsOutlined' } ] } ]; res.json({ success: true, code: 200, msg: '获取菜单成功', data: menus }); }); // ==================== 健康检查接口 ==================== app.get('/health', (req, res) => { res.json({ success: true, code: 200, msg: 'API服务运行正常', data: { timestamp: new Date().toISOString(), uptime: process.uptime() } }); }); // 全局错误处理 app.use(handleError); // 启动服务 const PORT = 3002; async function startServer() { try { await initDB(); app.listen(PORT, () => { console.log(`🚀 完整版API服务启动成功,端口: ${PORT}`); console.log(`📊 健康检查: GET http://localhost:${PORT}/health`); console.log(`🔐 登录接口: POST http://localhost:${PORT}/auth/login`); console.log(`👤 用户信息: GET http://localhost:${PORT}/auth/user`); console.log(`📱 TG账号列表: POST http://localhost:${PORT}/tgAccount/list`); console.log(`📋 账号用途管理: POST http://localhost:${PORT}/accountUsage/list`); console.log(`📈 系统概览: GET http://localhost:${PORT}/telegram/statistics/overview`); console.log(`📊 账号统计: GET http://localhost:${PORT}/telegram/statistics/account`); console.log(`🏠 菜单权限: GET http://localhost:${PORT}/telegram/permission/menus`); console.log(''); console.log('✅ 完整版API服务器已就绪,支持全功能!'); }); } catch (error) { console.error('❌ 服务启动失败:', error); process.exit(1); } } startServer();