2025-12-18 19:07:21 +08:00
|
|
|
|
import express from 'express';
|
|
|
|
|
|
import cors from 'cors';
|
|
|
|
|
|
import path from 'path';
|
|
|
|
|
|
import { initDatabase } from './database';
|
|
|
|
|
|
import {
|
|
|
|
|
|
UserController,
|
|
|
|
|
|
QuestionController,
|
|
|
|
|
|
QuizController,
|
|
|
|
|
|
AdminController,
|
|
|
|
|
|
BackupController,
|
|
|
|
|
|
QuestionCategoryController,
|
|
|
|
|
|
ExamSubjectController,
|
|
|
|
|
|
ExamTaskController,
|
2025-12-19 16:02:38 +08:00
|
|
|
|
AdminUserController,
|
|
|
|
|
|
userGroupController as UserGroupController
|
2025-12-18 19:07:21 +08:00
|
|
|
|
} from './controllers';
|
|
|
|
|
|
import {
|
|
|
|
|
|
upload,
|
|
|
|
|
|
errorHandler,
|
|
|
|
|
|
adminAuth,
|
|
|
|
|
|
requestLogger,
|
|
|
|
|
|
responseFormatter
|
|
|
|
|
|
} from './middlewares';
|
|
|
|
|
|
|
2025-12-23 00:35:57 +08:00
|
|
|
|
export const app = express();
|
2025-12-21 01:56:54 +08:00
|
|
|
|
const PORT = process.env.PORT || 3001;
|
2025-12-18 19:07:21 +08:00
|
|
|
|
|
|
|
|
|
|
// 中间件
|
|
|
|
|
|
app.use(cors());
|
|
|
|
|
|
app.use(express.json({ limit: '10mb' }));
|
|
|
|
|
|
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
|
|
|
|
|
app.use(requestLogger);
|
|
|
|
|
|
app.use(responseFormatter);
|
|
|
|
|
|
|
|
|
|
|
|
// API路由
|
|
|
|
|
|
const apiRouter = express.Router();
|
|
|
|
|
|
|
|
|
|
|
|
// 用户相关
|
|
|
|
|
|
apiRouter.post('/users', UserController.createUser);
|
|
|
|
|
|
apiRouter.get('/users/:id', UserController.getUser);
|
|
|
|
|
|
apiRouter.post('/users/validate', UserController.validateUserInfo);
|
2025-12-19 00:58:58 +08:00
|
|
|
|
apiRouter.get('/users/name/:name', UserController.getUsersByName);
|
2025-12-18 19:07:21 +08:00
|
|
|
|
|
|
|
|
|
|
// 题库管理
|
|
|
|
|
|
apiRouter.get('/questions', QuestionController.getQuestions);
|
|
|
|
|
|
apiRouter.get('/questions/:id', QuestionController.getQuestion);
|
|
|
|
|
|
apiRouter.post('/questions', adminAuth, QuestionController.createQuestion);
|
|
|
|
|
|
apiRouter.put('/questions/:id', adminAuth, QuestionController.updateQuestion);
|
|
|
|
|
|
apiRouter.delete('/questions/:id', adminAuth, QuestionController.deleteQuestion);
|
|
|
|
|
|
apiRouter.post('/questions/import', adminAuth, upload.single('file'), QuestionController.importQuestions);
|
2025-12-25 00:15:14 +08:00
|
|
|
|
apiRouter.post('/questions/import-text', adminAuth, QuestionController.importTextQuestions);
|
2025-12-18 19:07:21 +08:00
|
|
|
|
apiRouter.get('/questions/export', adminAuth, QuestionController.exportQuestions);
|
|
|
|
|
|
|
|
|
|
|
|
// 为了兼容前端可能的错误请求,添加一个不包含 /api 前缀的路由
|
|
|
|
|
|
app.get('/questions/export', adminAuth, QuestionController.exportQuestions);
|
|
|
|
|
|
|
|
|
|
|
|
// 题目类别
|
|
|
|
|
|
apiRouter.get('/question-categories', QuestionCategoryController.getCategories);
|
|
|
|
|
|
apiRouter.post('/admin/question-categories', adminAuth, QuestionCategoryController.createCategory);
|
|
|
|
|
|
apiRouter.put('/admin/question-categories/:id', adminAuth, QuestionCategoryController.updateCategory);
|
|
|
|
|
|
apiRouter.delete('/admin/question-categories/:id', adminAuth, QuestionCategoryController.deleteCategory);
|
|
|
|
|
|
|
|
|
|
|
|
// 考试科目
|
|
|
|
|
|
apiRouter.get('/exam-subjects', ExamSubjectController.getSubjects);
|
|
|
|
|
|
apiRouter.get('/admin/subjects', adminAuth, ExamSubjectController.getSubjects);
|
|
|
|
|
|
apiRouter.post('/admin/subjects', adminAuth, ExamSubjectController.createSubject);
|
|
|
|
|
|
apiRouter.put('/admin/subjects/:id', adminAuth, ExamSubjectController.updateSubject);
|
|
|
|
|
|
apiRouter.delete('/admin/subjects/:id', adminAuth, ExamSubjectController.deleteSubject);
|
|
|
|
|
|
|
|
|
|
|
|
// 考试任务
|
|
|
|
|
|
apiRouter.get('/exam-tasks', ExamTaskController.getTasks);
|
2025-12-19 00:58:58 +08:00
|
|
|
|
apiRouter.get('/admin/tasks', adminAuth, ExamTaskController.getTasks);
|
|
|
|
|
|
apiRouter.get('/admin/tasks/:id/users', adminAuth, ExamTaskController.getTaskUsers);
|
2025-12-18 19:07:21 +08:00
|
|
|
|
apiRouter.get('/exam-tasks/user/:userId', ExamTaskController.getUserTasks);
|
|
|
|
|
|
apiRouter.post('/admin/tasks', adminAuth, ExamTaskController.createTask);
|
|
|
|
|
|
apiRouter.put('/admin/tasks/:id', adminAuth, ExamTaskController.updateTask);
|
|
|
|
|
|
apiRouter.delete('/admin/tasks/:id', adminAuth, ExamTaskController.deleteTask);
|
|
|
|
|
|
apiRouter.get('/admin/tasks/:id/report', adminAuth, ExamTaskController.getTaskReport);
|
|
|
|
|
|
|
2025-12-19 16:02:38 +08:00
|
|
|
|
// 用户组管理
|
|
|
|
|
|
apiRouter.get('/admin/user-groups', adminAuth, UserGroupController.getAll);
|
|
|
|
|
|
apiRouter.post('/admin/user-groups', adminAuth, UserGroupController.create);
|
|
|
|
|
|
apiRouter.put('/admin/user-groups/:id', adminAuth, UserGroupController.update);
|
|
|
|
|
|
apiRouter.delete('/admin/user-groups/:id', adminAuth, UserGroupController.delete);
|
|
|
|
|
|
apiRouter.get('/admin/user-groups/:id/members', adminAuth, UserGroupController.getMembers);
|
|
|
|
|
|
|
2025-12-18 19:07:21 +08:00
|
|
|
|
// 用户管理
|
|
|
|
|
|
apiRouter.get('/admin/users', adminAuth, AdminUserController.getUsers);
|
2025-12-19 16:02:38 +08:00
|
|
|
|
apiRouter.post('/admin/users', adminAuth, AdminUserController.createUser);
|
2025-12-19 00:58:58 +08:00
|
|
|
|
apiRouter.put('/admin/users/:id', adminAuth, AdminUserController.updateUser);
|
2025-12-18 19:07:21 +08:00
|
|
|
|
apiRouter.delete('/admin/users', adminAuth, AdminUserController.deleteUser);
|
|
|
|
|
|
apiRouter.get('/admin/users/export', adminAuth, AdminUserController.exportUsers);
|
|
|
|
|
|
apiRouter.post('/admin/users/import', adminAuth, upload.single('file'), AdminUserController.importUsers);
|
|
|
|
|
|
apiRouter.get('/admin/users/:userId/records', adminAuth, AdminUserController.getUserRecords);
|
|
|
|
|
|
apiRouter.get('/admin/quiz/records/detail/:recordId', adminAuth, AdminUserController.getRecordDetail);
|
|
|
|
|
|
|
|
|
|
|
|
// 答题相关
|
|
|
|
|
|
apiRouter.post('/quiz/generate', QuizController.generateQuiz);
|
|
|
|
|
|
apiRouter.post('/quiz/submit', QuizController.submitQuiz);
|
|
|
|
|
|
apiRouter.get('/quiz/records/:userId', QuizController.getUserRecords);
|
|
|
|
|
|
apiRouter.get('/quiz/records/detail/:recordId', QuizController.getRecordDetail);
|
|
|
|
|
|
apiRouter.get('/quiz/records', adminAuth, QuizController.getAllRecords);
|
|
|
|
|
|
|
|
|
|
|
|
// 管理员相关
|
|
|
|
|
|
apiRouter.post('/admin/login', AdminController.login);
|
|
|
|
|
|
apiRouter.get('/admin/statistics', adminAuth, AdminController.getStatistics);
|
2025-12-22 21:58:18 +08:00
|
|
|
|
apiRouter.get('/admin/statistics/users', adminAuth, AdminController.getUserStats);
|
|
|
|
|
|
apiRouter.get('/admin/statistics/subjects', adminAuth, AdminController.getSubjectStats);
|
|
|
|
|
|
apiRouter.get('/admin/statistics/tasks', adminAuth, AdminController.getTaskStats);
|
2025-12-19 00:58:58 +08:00
|
|
|
|
apiRouter.get('/admin/active-tasks', adminAuth, AdminController.getActiveTasksStats);
|
2025-12-23 00:35:57 +08:00
|
|
|
|
apiRouter.get('/admin/dashboard/overview', adminAuth, AdminController.getDashboardOverview);
|
|
|
|
|
|
apiRouter.get('/admin/tasks/history-stats', adminAuth, AdminController.getHistoryTaskStats);
|
|
|
|
|
|
apiRouter.get('/admin/tasks/upcoming-stats', adminAuth, AdminController.getUpcomingTaskStats);
|
2025-12-25 00:15:14 +08:00
|
|
|
|
apiRouter.get('/admin/tasks/all-stats', adminAuth, AdminController.getAllTaskStats);
|
2025-12-19 00:58:58 +08:00
|
|
|
|
apiRouter.put('/admin/config', adminAuth, AdminController.updateQuizConfig);
|
|
|
|
|
|
apiRouter.get('/admin/config', adminAuth, AdminController.getQuizConfig);
|
2025-12-18 19:07:21 +08:00
|
|
|
|
apiRouter.put('/admin/password', adminAuth, AdminController.updatePassword);
|
|
|
|
|
|
apiRouter.get('/admin/configs', adminAuth, AdminController.getAllConfigs);
|
|
|
|
|
|
|
|
|
|
|
|
// 数据备份和恢复
|
|
|
|
|
|
apiRouter.get('/admin/export/users', adminAuth, BackupController.exportUsers);
|
|
|
|
|
|
apiRouter.get('/admin/export/questions', adminAuth, BackupController.exportQuestions);
|
|
|
|
|
|
apiRouter.get('/admin/export/records', adminAuth, BackupController.exportRecords);
|
|
|
|
|
|
apiRouter.get('/admin/export/answers', adminAuth, BackupController.exportAnswers);
|
|
|
|
|
|
apiRouter.post('/admin/restore', adminAuth, BackupController.restoreData);
|
|
|
|
|
|
|
|
|
|
|
|
// 应用API路由
|
|
|
|
|
|
app.use('/api', apiRouter);
|
|
|
|
|
|
|
|
|
|
|
|
// 静态文件服务
|
|
|
|
|
|
app.use(express.static(path.join(process.cwd(), 'dist')));
|
|
|
|
|
|
|
|
|
|
|
|
// 前端路由(SPA支持)
|
|
|
|
|
|
app.get('*', (req, res) => {
|
|
|
|
|
|
res.sendFile(path.join(process.cwd(), 'dist', 'index.html'));
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 错误处理
|
|
|
|
|
|
app.use(errorHandler);
|
|
|
|
|
|
|
|
|
|
|
|
// 启动服务器
|
2025-12-23 00:35:57 +08:00
|
|
|
|
export async function startServer() {
|
2025-12-18 19:07:21 +08:00
|
|
|
|
try {
|
2025-12-21 01:56:54 +08:00
|
|
|
|
// 初始化数据库
|
|
|
|
|
|
console.log('开始数据库初始化...');
|
2025-12-18 19:07:21 +08:00
|
|
|
|
await initDatabase();
|
2025-12-21 01:56:54 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('数据库初始化失败,将继续启动服务器:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 无论数据库初始化是否成功,都启动服务器
|
|
|
|
|
|
try {
|
2025-12-18 19:07:21 +08:00
|
|
|
|
app.listen(PORT, () => {
|
|
|
|
|
|
console.log(`服务器运行在端口 ${PORT}`);
|
|
|
|
|
|
console.log(`API文档: http://localhost:${PORT}/api`);
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('启动服务器失败:', error);
|
|
|
|
|
|
process.exit(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-23 00:35:57 +08:00
|
|
|
|
if (process.env.NODE_ENV !== 'test') {
|
|
|
|
|
|
startServer();
|
|
|
|
|
|
}
|