后台仪表盘重新设计---待测试
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { SystemConfigModel } from '../models';
|
||||
import { query } from '../database';
|
||||
|
||||
export class AdminController {
|
||||
// 管理员登录
|
||||
@@ -126,6 +127,149 @@ export class AdminController {
|
||||
}
|
||||
}
|
||||
|
||||
static async getUserStats(req: Request, res: Response) {
|
||||
try {
|
||||
const rows = await query(
|
||||
`
|
||||
SELECT
|
||||
u.id as userId,
|
||||
u.name as userName,
|
||||
COUNT(qr.id) as totalRecords,
|
||||
COALESCE(ROUND(AVG(qr.total_score), 2), 0) as averageScore,
|
||||
COALESCE(MAX(qr.total_score), 0) as highestScore,
|
||||
COALESCE(MIN(qr.total_score), 0) as lowestScore
|
||||
FROM users u
|
||||
LEFT JOIN quiz_records qr ON u.id = qr.user_id
|
||||
GROUP BY u.id
|
||||
ORDER BY totalRecords DESC, u.created_at DESC
|
||||
`,
|
||||
);
|
||||
|
||||
const data = (rows as any[]).map((row) => ({
|
||||
userId: row.userId,
|
||||
userName: row.userName,
|
||||
totalRecords: Number(row.totalRecords) || 0,
|
||||
averageScore: Number(row.averageScore) || 0,
|
||||
highestScore: Number(row.highestScore) || 0,
|
||||
lowestScore: Number(row.lowestScore) || 0,
|
||||
}));
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data,
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error('获取用户统计失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || '获取用户统计失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static async getSubjectStats(req: Request, res: Response) {
|
||||
try {
|
||||
const rows = await query(
|
||||
`
|
||||
SELECT
|
||||
s.id as subjectId,
|
||||
s.name as subjectName,
|
||||
COUNT(qr.id) as totalRecords,
|
||||
COALESCE(ROUND(AVG(qr.total_score), 2), 0) as averageScore,
|
||||
COALESCE(ROUND(AVG(
|
||||
CASE
|
||||
WHEN qr.total_count > 0 THEN (qr.correct_count * 100.0 / qr.total_count)
|
||||
ELSE 0
|
||||
END
|
||||
), 2), 0) as averageCorrectRate
|
||||
FROM exam_subjects s
|
||||
LEFT JOIN quiz_records qr ON s.id = qr.subject_id
|
||||
GROUP BY s.id
|
||||
ORDER BY totalRecords DESC, s.created_at DESC
|
||||
`,
|
||||
);
|
||||
|
||||
const data = (rows as any[]).map((row) => ({
|
||||
subjectId: row.subjectId,
|
||||
subjectName: row.subjectName,
|
||||
totalRecords: Number(row.totalRecords) || 0,
|
||||
averageScore: Number(row.averageScore) || 0,
|
||||
averageCorrectRate: Number(row.averageCorrectRate) || 0,
|
||||
}));
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data,
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error('获取科目统计失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || '获取科目统计失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static async getTaskStats(req: Request, res: Response) {
|
||||
try {
|
||||
const rows = await query(
|
||||
`
|
||||
SELECT
|
||||
t.id as taskId,
|
||||
t.name as taskName,
|
||||
COALESCE(rs.totalRecords, 0) as totalRecords,
|
||||
COALESCE(rs.averageScore, 0) as averageScore,
|
||||
COALESCE(us.totalUsers, 0) as totalUsers,
|
||||
COALESCE(rs.completedUsers, 0) as completedUsers
|
||||
FROM exam_tasks t
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
task_id,
|
||||
COUNT(*) as totalRecords,
|
||||
ROUND(AVG(total_score), 2) as averageScore,
|
||||
COUNT(DISTINCT user_id) as completedUsers
|
||||
FROM quiz_records
|
||||
WHERE task_id IS NOT NULL
|
||||
GROUP BY task_id
|
||||
) rs ON rs.task_id = t.id
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
task_id,
|
||||
COUNT(DISTINCT user_id) as totalUsers
|
||||
FROM exam_task_users
|
||||
GROUP BY task_id
|
||||
) us ON us.task_id = t.id
|
||||
ORDER BY t.created_at DESC
|
||||
`,
|
||||
);
|
||||
|
||||
const data = rows.map((row: any) => {
|
||||
const totalUsers = Number(row.totalUsers) || 0;
|
||||
const completedUsers = Number(row.completedUsers) || 0;
|
||||
const completionRate = totalUsers > 0 ? (completedUsers / totalUsers) * 100 : 0;
|
||||
|
||||
return {
|
||||
taskId: row.taskId,
|
||||
taskName: row.taskName,
|
||||
totalRecords: Number(row.totalRecords) || 0,
|
||||
averageScore: Number(row.averageScore) || 0,
|
||||
completionRate,
|
||||
};
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data,
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error('获取任务统计失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || '获取任务统计失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 修改管理员密码
|
||||
static async updatePassword(req: Request, res: Response) {
|
||||
try {
|
||||
@@ -180,4 +324,4 @@ export class AdminController {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,9 @@ apiRouter.get('/quiz/records', adminAuth, QuizController.getAllRecords);
|
||||
// 管理员相关
|
||||
apiRouter.post('/admin/login', AdminController.login);
|
||||
apiRouter.get('/admin/statistics', adminAuth, AdminController.getStatistics);
|
||||
apiRouter.get('/admin/statistics/users', adminAuth, AdminController.getUserStats);
|
||||
apiRouter.get('/admin/statistics/subjects', adminAuth, AdminController.getSubjectStats);
|
||||
apiRouter.get('/admin/statistics/tasks', adminAuth, AdminController.getTaskStats);
|
||||
apiRouter.get('/admin/active-tasks', adminAuth, AdminController.getActiveTasksStats);
|
||||
apiRouter.put('/admin/config', adminAuth, AdminController.updateQuizConfig);
|
||||
apiRouter.get('/admin/config', adminAuth, AdminController.getQuizConfig);
|
||||
@@ -154,4 +157,4 @@ async function startServer() {
|
||||
|
||||
// 启动服务器
|
||||
// 重启触发
|
||||
startServer();
|
||||
startServer();
|
||||
|
||||
Reference in New Issue
Block a user