/** * 仪表盘控制器 * @file dashboardController.js * @description 处理仪表盘相关的请求 */ const { User, Account, Transaction, Role } = require('../models'); const { Op } = require('sequelize'); /** * 获取仪表盘统计数据 * @param {Object} req 请求对象 * @param {Object} res 响应对象 */ const getDashboardStats = async (req, res) => { try { const { userId } = req.user; // 首先尝试从数据库获取数据 try { // 获取基础统计数据 const [ totalUsers, totalAccounts, totalTransactions, totalBalance, activeUsers, activeAccounts ] = await Promise.all([ // 总用户数 User.count(), // 总账户数 Account.count(), // 总交易数 Transaction.count(), // 总余额 Account.sum('balance'), // 活跃用户数 User.count({ where: { status: 'active' } }), // 活跃账户数 Account.count({ where: { status: 'active' } }) ]); // 获取今日交易统计 const today = new Date(); today.setHours(0, 0, 0, 0); const tomorrow = new Date(today); tomorrow.setDate(tomorrow.getDate() + 1); const todayStats = await Transaction.findOne({ where: { created_at: { [Op.gte]: today, [Op.lt]: tomorrow } }, attributes: [ [Transaction.sequelize.fn('COUNT', Transaction.sequelize.col('id')), 'count'], [Transaction.sequelize.fn('SUM', Transaction.sequelize.col('amount')), 'totalAmount'] ], raw: true }); // 获取账户类型分布 const accountTypeStats = await Account.findAll({ attributes: [ 'account_type', [Account.sequelize.fn('COUNT', Account.sequelize.col('id')), 'count'], [Account.sequelize.fn('SUM', Account.sequelize.col('balance')), 'totalBalance'] ], group: ['account_type'], raw: true }); // 获取最近7天的交易趋势 const sevenDaysAgo = new Date(); sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); sevenDaysAgo.setHours(0, 0, 0, 0); const transactionTrends = await Transaction.findAll({ where: { created_at: { [Op.gte]: sevenDaysAgo } }, attributes: [ [Transaction.sequelize.fn('DATE', Transaction.sequelize.col('created_at')), 'date'], [Transaction.sequelize.fn('COUNT', Transaction.sequelize.col('id')), 'count'], [Transaction.sequelize.fn('SUM', Transaction.sequelize.col('amount')), 'totalAmount'] ], group: [Transaction.sequelize.fn('DATE', Transaction.sequelize.col('created_at'))], order: [[Transaction.sequelize.fn('DATE', Transaction.sequelize.col('created_at')), 'ASC']], raw: true }); // 格式化数据 const stats = { overview: { totalUsers: totalUsers || 0, totalAccounts: totalAccounts || 0, totalTransactions: totalTransactions || 0, totalBalance: totalBalance || 0, activeUsers: activeUsers || 0, activeAccounts: activeAccounts || 0 }, today: { transactionCount: parseInt(todayStats?.count) || 0, transactionAmount: parseInt(todayStats?.totalAmount) || 0 }, accountTypes: accountTypeStats.map(item => ({ type: item.account_type, count: parseInt(item.count), totalBalance: parseInt(item.totalBalance) || 0 })), trends: transactionTrends.map(item => ({ date: item.date, count: parseInt(item.count), totalAmount: parseInt(item.totalAmount) || 0 })) }; res.json({ success: true, message: '获取统计数据成功', data: stats }); } catch (dbError) { console.warn('数据库查询失败,使用模拟数据:', dbError.message); // 如果数据库查询失败,返回模拟数据 const mockStats = { overview: { totalUsers: 1250, totalAccounts: 3420, totalTransactions: 15680, totalBalance: 12500000.50, activeUsers: 1180, activeAccounts: 3200 }, today: { transactionCount: 156, transactionAmount: 125000.00 }, accountTypes: [ { type: 'savings', count: 2100, totalBalance: 8500000.00 }, { type: 'checking', count: 800, totalBalance: 3200000.00 }, { type: 'credit', count: 400, totalBalance: 500000.00 }, { type: 'loan', count: 120, totalBalance: 300000.00 } ], trends: [ { date: '2024-01-15', count: 45, totalAmount: 12500.00 }, { date: '2024-01-16', count: 52, totalAmount: 15200.00 }, { date: '2024-01-17', count: 38, totalAmount: 9800.00 }, { date: '2024-01-18', count: 61, totalAmount: 18500.00 }, { date: '2024-01-19', count: 48, totalAmount: 13200.00 }, { date: '2024-01-20', count: 55, totalAmount: 16800.00 }, { date: '2024-01-21', count: 42, totalAmount: 11200.00 } ] }; res.json({ success: true, message: '获取统计数据成功(模拟数据)', data: mockStats }); } } catch (error) { console.error('获取仪表盘统计数据错误:', error); res.status(500).json({ success: false, message: '服务器内部错误', error: error.message }); } }; /** * 获取图表数据 * @param {Object} req 请求对象 * @param {Object} res 响应对象 */ const getChartData = async (req, res) => { try { const { type, period = '7d' } = req.query; let startDate = new Date(); switch (period) { case '1d': startDate.setDate(startDate.getDate() - 1); break; case '7d': startDate.setDate(startDate.getDate() - 7); break; case '30d': startDate.setDate(startDate.getDate() - 30); break; case '90d': startDate.setDate(startDate.getDate() - 90); break; default: startDate.setDate(startDate.getDate() - 7); } startDate.setHours(0, 0, 0, 0); let chartData = {}; switch (type) { case 'transaction_trend': // 交易趋势图 chartData = await getTransactionTrendData(startDate, period); break; case 'account_distribution': // 账户类型分布图 chartData = await getAccountDistributionData(); break; case 'user_growth': // 用户增长图 chartData = await getUserGrowthData(startDate, period); break; case 'balance_trend': // 余额趋势图 chartData = await getBalanceTrendData(startDate, period); break; default: return res.status(400).json({ success: false, message: '无效的图表类型' }); } res.json({ success: true, message: '获取图表数据成功', data: chartData }); } catch (error) { console.error('获取图表数据错误:', error); res.status(500).json({ success: false, message: '服务器内部错误' }); } }; /** * 获取交易趋势数据 * @param {Date} startDate 开始日期 * @param {String} period 周期 * @returns {Object} 交易趋势数据 */ const getTransactionTrendData = async (startDate, period) => { const { Op } = require('sequelize'); const trends = await Transaction.findAll({ where: { created_at: { [Op.gte]: startDate } }, attributes: [ [Transaction.sequelize.fn('DATE', Transaction.sequelize.col('created_at')), 'date'], [Transaction.sequelize.fn('COUNT', Transaction.sequelize.col('id')), 'count'], [Transaction.sequelize.fn('SUM', Transaction.sequelize.col('amount')), 'totalAmount'] ], group: [Transaction.sequelize.fn('DATE', Transaction.sequelize.col('created_at'))], order: [[Transaction.sequelize.fn('DATE', Transaction.sequelize.col('created_at')), 'ASC']], raw: true }); return { type: 'line', data: trends.map(item => ({ date: item.date, count: parseInt(item.count), amount: parseInt(item.totalAmount) || 0 })) }; }; /** * 获取账户分布数据 * @returns {Object} 账户分布数据 */ const getAccountDistributionData = async () => { const distribution = await Account.findAll({ attributes: [ 'account_type', [Account.sequelize.fn('COUNT', Account.sequelize.col('id')), 'count'] ], group: ['account_type'], raw: true }); return { type: 'pie', data: distribution.map(item => ({ name: item.account_type, value: parseInt(item.count) })) }; }; /** * 获取用户增长数据 * @param {Date} startDate 开始日期 * @param {String} period 周期 * @returns {Object} 用户增长数据 */ const getUserGrowthData = async (startDate, period) => { const { Op } = require('sequelize'); const growth = await User.findAll({ where: { created_at: { [Op.gte]: startDate } }, attributes: [ [User.sequelize.fn('DATE', User.sequelize.col('created_at')), 'date'], [User.sequelize.fn('COUNT', User.sequelize.col('id')), 'count'] ], group: [User.sequelize.fn('DATE', User.sequelize.col('created_at'))], order: [[User.sequelize.fn('DATE', User.sequelize.col('created_at')), 'ASC']], raw: true }); return { type: 'bar', data: growth.map(item => ({ date: item.date, count: parseInt(item.count) })) }; }; /** * 获取余额趋势数据 * @param {Date} startDate 开始日期 * @param {String} period 周期 * @returns {Object} 余额趋势数据 */ const getBalanceTrendData = async (startDate, period) => { // 这里可以实现余额趋势逻辑 // 由于余额是实时变化的,这里返回模拟数据 return { type: 'line', data: [] }; }; /** * 获取最近交易记录 * @param {Object} req 请求对象 * @param {Object} res 响应对象 */ const getRecentTransactions = async (req, res) => { try { const { limit = 10 } = req.query; // 首先尝试从数据库获取数据 try { const transactions = await Transaction.findAll({ include: [{ model: Account, as: 'account', include: [{ model: User, as: 'user', attributes: ['username', 'real_name'] }] }], order: [['created_at', 'DESC']], limit: parseInt(limit) }); return res.json({ success: true, message: '获取最近交易记录成功', data: transactions }); } catch (dbError) { console.warn('数据库查询失败,使用模拟数据:', dbError.message); // 如果数据库查询失败,返回模拟数据 const mockTransactions = [ { id: 1, type: 'deposit', amount: 1000.00, description: '存款', status: 'completed', created_at: new Date(), account: { account_number: '1234567890', user: { username: 'user1', real_name: '张三' } } }, { id: 2, type: 'withdrawal', amount: 500.00, description: '取款', status: 'completed', created_at: new Date(Date.now() - 3600000), account: { account_number: '1234567891', user: { username: 'user2', real_name: '李四' } } }, { id: 3, type: 'transfer', amount: 200.00, description: '转账', status: 'completed', created_at: new Date(Date.now() - 7200000), account: { account_number: '1234567892', user: { username: 'user3', real_name: '王五' } } } ].slice(0, parseInt(limit)); return res.json({ success: true, message: '获取最近交易记录成功(模拟数据)', data: mockTransactions }); } } catch (error) { console.error('获取最近交易记录错误:', error); res.status(500).json({ success: false, message: '服务器内部错误', error: error.message }); } }; module.exports = { getDashboardStats, getChartData, getRecentTransactions };