265 lines
6.6 KiB
JavaScript
265 lines
6.6 KiB
JavaScript
|
|
const express = require('express');
|
||
|
|
const dbConnector = require('../utils/dbConnector');
|
||
|
|
const { adminRequired } = require('../middlewares/auth');
|
||
|
|
const { asyncHandler } = require('../middlewares/errorHandler');
|
||
|
|
|
||
|
|
const router = express.Router();
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取系统统计概览
|
||
|
|
*/
|
||
|
|
router.get('/statistics/overview', adminRequired, asyncHandler(async (req, res) => {
|
||
|
|
// 获取总用户数
|
||
|
|
const totalUsersResult = await dbConnector.query(
|
||
|
|
'SELECT COUNT(*) as total FROM users WHERE status = 1'
|
||
|
|
);
|
||
|
|
|
||
|
|
// 获取今日新增用户数
|
||
|
|
const newUsersTodayResult = await dbConnector.query(
|
||
|
|
'SELECT COUNT(*) as total FROM users WHERE status = 1 AND DATE(created_at) = CURDATE()'
|
||
|
|
);
|
||
|
|
|
||
|
|
// 获取总订单数
|
||
|
|
const totalOrdersResult = await dbConnector.query(
|
||
|
|
'SELECT COUNT(*) as total FROM orders'
|
||
|
|
);
|
||
|
|
|
||
|
|
// 获取总收入
|
||
|
|
const totalRevenueResult = await dbConnector.query(
|
||
|
|
'SELECT COALESCE(SUM(total_amount), 0) as total FROM orders WHERE payment_status = 1'
|
||
|
|
);
|
||
|
|
|
||
|
|
// 获取总识别次数
|
||
|
|
const totalIdentificationsResult = await dbConnector.query(
|
||
|
|
'SELECT COUNT(*) as total FROM identifications'
|
||
|
|
);
|
||
|
|
|
||
|
|
// 获取今日活跃用户数
|
||
|
|
const activeUsersTodayResult = await dbConnector.query(
|
||
|
|
`SELECT COUNT(DISTINCT user_id) as total FROM (
|
||
|
|
SELECT user_id FROM orders WHERE DATE(created_at) = CURDATE()
|
||
|
|
UNION
|
||
|
|
SELECT user_id FROM identifications WHERE DATE(created_at) = CURDATE()
|
||
|
|
) as active_users`
|
||
|
|
);
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
code: 200,
|
||
|
|
message: '获取成功',
|
||
|
|
data: {
|
||
|
|
total_users: totalUsersResult[0].total,
|
||
|
|
new_users_today: newUsersTodayResult[0].total,
|
||
|
|
total_orders: totalOrdersResult[0].total,
|
||
|
|
total_revenue: parseFloat(totalRevenueResult[0].total),
|
||
|
|
total_identifications: totalIdentificationsResult[0].total,
|
||
|
|
active_users_today: activeUsersTodayResult[0].total
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}));
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取趋势数据
|
||
|
|
*/
|
||
|
|
router.get('/statistics/trend', adminRequired, asyncHandler(async (req, res) => {
|
||
|
|
const { days = 7, type = 'users' } = req.query;
|
||
|
|
const dayCount = parseInt(days);
|
||
|
|
|
||
|
|
let query = '';
|
||
|
|
let dataKey = '';
|
||
|
|
|
||
|
|
switch (type) {
|
||
|
|
case 'users':
|
||
|
|
query = `
|
||
|
|
SELECT DATE(created_at) as date, COUNT(*) as count
|
||
|
|
FROM users
|
||
|
|
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
|
||
|
|
GROUP BY DATE(created_at)
|
||
|
|
ORDER BY date
|
||
|
|
`;
|
||
|
|
dataKey = 'new_users';
|
||
|
|
break;
|
||
|
|
case 'orders':
|
||
|
|
query = `
|
||
|
|
SELECT DATE(created_at) as date, COUNT(*) as count
|
||
|
|
FROM orders
|
||
|
|
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
|
||
|
|
GROUP BY DATE(created_at)
|
||
|
|
ORDER BY date
|
||
|
|
`;
|
||
|
|
dataKey = 'new_orders';
|
||
|
|
break;
|
||
|
|
case 'revenue':
|
||
|
|
query = `
|
||
|
|
SELECT DATE(created_at) as date, COALESCE(SUM(total_amount), 0) as amount
|
||
|
|
FROM orders
|
||
|
|
WHERE payment_status = 1 AND created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
|
||
|
|
GROUP BY DATE(created_at)
|
||
|
|
ORDER BY date
|
||
|
|
`;
|
||
|
|
dataKey = 'revenue';
|
||
|
|
break;
|
||
|
|
case 'identifications':
|
||
|
|
query = `
|
||
|
|
SELECT DATE(created_at) as date, COUNT(*) as count
|
||
|
|
FROM identifications
|
||
|
|
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
|
||
|
|
GROUP BY DATE(created_at)
|
||
|
|
ORDER BY date
|
||
|
|
`;
|
||
|
|
dataKey = 'identifications';
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
return res.status(400).json({
|
||
|
|
code: 400,
|
||
|
|
message: '不支持的统计类型',
|
||
|
|
data: null
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
const trendData = await dbConnector.query(query, [dayCount]);
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
code: 200,
|
||
|
|
message: '获取成功',
|
||
|
|
data: {
|
||
|
|
type,
|
||
|
|
days: dayCount,
|
||
|
|
trend: trendData,
|
||
|
|
total: trendData.reduce((sum, item) => sum + (item.count || item.amount || 0), 0)
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}));
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取系统配置列表
|
||
|
|
*/
|
||
|
|
router.get('/config', adminRequired, asyncHandler(async (req, res) => {
|
||
|
|
const configs = await dbConnector.query(
|
||
|
|
'SELECT config_key, config_value, config_type, description FROM system_config ORDER BY config_key'
|
||
|
|
);
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
code: 200,
|
||
|
|
message: '获取成功',
|
||
|
|
data: configs
|
||
|
|
});
|
||
|
|
}));
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取单个系统配置
|
||
|
|
*/
|
||
|
|
router.get('/config/:key', adminRequired, asyncHandler(async (req, res) => {
|
||
|
|
const { key } = req.params;
|
||
|
|
|
||
|
|
const configs = await dbConnector.query(
|
||
|
|
'SELECT config_key, config_value, config_type, description FROM system_config WHERE config_key = ?',
|
||
|
|
[key]
|
||
|
|
);
|
||
|
|
|
||
|
|
if (configs.length === 0) {
|
||
|
|
return res.status(404).json({
|
||
|
|
code: 404,
|
||
|
|
message: '配置项不存在',
|
||
|
|
data: null
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
code: 200,
|
||
|
|
message: '获取成功',
|
||
|
|
data: configs[0]
|
||
|
|
});
|
||
|
|
}));
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 更新系统配置
|
||
|
|
*/
|
||
|
|
router.put('/config/:key', adminRequired, asyncHandler(async (req, res) => {
|
||
|
|
const { key } = req.params;
|
||
|
|
const { value } = req.body;
|
||
|
|
|
||
|
|
if (value === undefined) {
|
||
|
|
return res.status(400).json({
|
||
|
|
code: 400,
|
||
|
|
message: '配置值不能为空',
|
||
|
|
data: null
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
const result = await dbConnector.query(
|
||
|
|
'UPDATE system_config SET config_value = ?, updated_at = CURRENT_TIMESTAMP WHERE config_key = ?',
|
||
|
|
[value, key]
|
||
|
|
);
|
||
|
|
|
||
|
|
if (result.affectedRows === 0) {
|
||
|
|
return res.status(404).json({
|
||
|
|
code: 404,
|
||
|
|
message: '配置项不存在',
|
||
|
|
data: null
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
code: 200,
|
||
|
|
message: '更新成功',
|
||
|
|
data: null
|
||
|
|
});
|
||
|
|
}));
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取操作日志
|
||
|
|
*/
|
||
|
|
router.get('/logs', adminRequired, asyncHandler(async (req, res) => {
|
||
|
|
const { page = 1, limit = 20, module, action, username } = req.query;
|
||
|
|
const offset = (page - 1) * limit;
|
||
|
|
|
||
|
|
let whereClause = 'WHERE 1=1';
|
||
|
|
let queryParams = [];
|
||
|
|
|
||
|
|
if (module) {
|
||
|
|
whereClause += ' AND module = ?';
|
||
|
|
queryParams.push(module);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (action) {
|
||
|
|
whereClause += ' AND action = ?';
|
||
|
|
queryParams.push(action);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (username) {
|
||
|
|
whereClause += ' AND username LIKE ?';
|
||
|
|
queryParams.push(`%${username}%`);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取日志列表
|
||
|
|
const logs = await dbConnector.query(
|
||
|
|
`SELECT id, user_id, username, user_type, module, action, target_id,
|
||
|
|
description, ip_address, user_agent, created_at
|
||
|
|
FROM operation_logs ${whereClause}
|
||
|
|
ORDER BY created_at DESC
|
||
|
|
LIMIT ? OFFSET ?`,
|
||
|
|
[...queryParams, parseInt(limit), offset]
|
||
|
|
);
|
||
|
|
|
||
|
|
// 获取总数
|
||
|
|
const totalResult = await dbConnector.query(
|
||
|
|
`SELECT COUNT(*) as total FROM operation_logs ${whereClause}`,
|
||
|
|
queryParams
|
||
|
|
);
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
code: 200,
|
||
|
|
message: '获取成功',
|
||
|
|
data: {
|
||
|
|
logs,
|
||
|
|
pagination: {
|
||
|
|
page: parseInt(page),
|
||
|
|
limit: parseInt(limit),
|
||
|
|
total: totalResult[0].total,
|
||
|
|
pages: Math.ceil(totalResult[0].total / limit)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}));
|
||
|
|
|
||
|
|
module.exports = router;
|