From b4467ca5a33e30c0bc11fb5cf7fbf165f5aabc1b Mon Sep 17 00:00:00 2001 From: hnu202326010112 <3420852069@qq.com> Date: Tue, 30 Sep 2025 16:04:09 +0800 Subject: [PATCH 1/9] Initial commit --- .gitignore | 34 ++++++++++++++++++++++++++++++++++ README.md | 2 ++ 2 files changed, 36 insertions(+) create mode 100644 .gitignore create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e257658 --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# ---> C++ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + diff --git a/README.md b/README.md new file mode 100644 index 0000000..dc45f65 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# teamwork-project + -- 2.34.1 From 6be6e9c65dee7b009c4c2304ed03096d53146526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=85=95=E9=9B=AA?= <3420852069@qq.com> Date: Fri, 10 Oct 2025 10:30:40 +0800 Subject: [PATCH 2/9] =?UTF-8?q?=E5=89=8D=E7=AB=AF=E4=BB=A3=E7=A0=811.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/README.md | 2 + src/examwidget.cpp | 213 +++++++++++++++++++++++++++++++++++++++++ src/examwidget.h | 49 ++++++++++ src/loginwidget.cpp | 61 ++++++++++++ src/loginwidget.h | 36 +++++++ src/main.cpp | 17 ++++ src/mainmenuwidget.cpp | 115 ++++++++++++++++++++++ src/mainmenuwidget.h | 35 +++++++ src/mainwindow.cpp | 82 ++++++++++++++++ src/mainwindow.h | 40 ++++++++ src/registerwidget.cpp | 113 ++++++++++++++++++++++ src/registerwidget.h | 40 ++++++++ src/resultwidget.cpp | 115 ++++++++++++++++++++++ src/resultwidget.h | 34 +++++++ 14 files changed, 952 insertions(+) create mode 100644 doc/README.md create mode 100644 src/examwidget.cpp create mode 100644 src/examwidget.h create mode 100644 src/loginwidget.cpp create mode 100644 src/loginwidget.h create mode 100644 src/main.cpp create mode 100644 src/mainmenuwidget.cpp create mode 100644 src/mainmenuwidget.h create mode 100644 src/mainwindow.cpp create mode 100644 src/mainwindow.h create mode 100644 src/registerwidget.cpp create mode 100644 src/registerwidget.h create mode 100644 src/resultwidget.cpp create mode 100644 src/resultwidget.h diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..dc45f65 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,2 @@ +# teamwork-project + diff --git a/src/examwidget.cpp b/src/examwidget.cpp new file mode 100644 index 0000000..50ef9fc --- /dev/null +++ b/src/examwidget.cpp @@ -0,0 +1,213 @@ +#include "examwidget.h" +#include +#include +#include + +ExamWidget::ExamWidget(QWidget *parent) : QWidget(parent), currentQuestion(0), totalQuestions(0) +{ + QVBoxLayout *mainLayout = new QVBoxLayout(this); + + // 进度标签 + progressLabel = new QLabel(); + progressLabel->setAlignment(Qt::AlignCenter); + progressLabel->setStyleSheet("font-size: 16px; font-weight: bold; margin: 20px; color: #2c3e50;"); + + // 题目区域 + QGroupBox *questionGroup = new QGroupBox("题目"); + questionGroup->setStyleSheet("QGroupBox {" + "font-size: 16px;" + "font-weight: bold;" + "margin-top: 10px;" + "}" + "QGroupBox::title {" + "subcontrol-origin: margin;" + "subcontrol-position: top center;" + "padding: 0 5px;" + "}"); + + QVBoxLayout *questionLayout = new QVBoxLayout(questionGroup); + + questionLabel = new QLabel(); + questionLabel->setWordWrap(true); + questionLabel->setStyleSheet("font-size: 18px; margin: 20px; padding: 10px; background-color: #f8f9fa; border-radius: 5px;"); + questionLabel->setAlignment(Qt::AlignCenter); + questionLabel->setMinimumHeight(80); + + questionLayout->addWidget(questionLabel); + + // 选项区域 + QGroupBox *optionsGroup = new QGroupBox("请选择答案"); + optionsGroup->setStyleSheet("QGroupBox {" + "font-size: 16px;" + "font-weight: bold;" + "margin-top: 10px;" + "}" + "QGroupBox::title {" + "subcontrol-origin: margin;" + "subcontrol-position: top center;" + "padding: 0 5px;" + "}"); + + QVBoxLayout *optionsLayout = new QVBoxLayout(optionsGroup); + optionGroup = new QButtonGroup(this); + + for (int i = 0; i < 4; ++i) { + optionButtons[i] = new QRadioButton(); + optionButtons[i]->setStyleSheet("QRadioButton {" + "font-size: 14px;" + "margin: 8px;" + "padding: 8px;" + "}" + "QRadioButton::indicator {" + "width: 20px;" + "height: 20px;" + "}"); + optionGroup->addButton(optionButtons[i], i); + optionsLayout->addWidget(optionButtons[i]); + } + + // 按钮区域 + QWidget *buttonWidget = new QWidget(); + QHBoxLayout *buttonLayout = new QHBoxLayout(buttonWidget); + + nextButton = new QPushButton("下一题"); + nextButton->setStyleSheet("QPushButton {" + "background-color: #3498db;" + "color: white;" + "border: none;" + "padding: 10px 20px;" + "font-size: 14px;" + "border-radius: 5px;" + "}" + "QPushButton:hover {" + "background-color: #2980b9;" + "}"); + nextButton->setFixedWidth(120); + + submitButton = new QPushButton("提交试卷"); + submitButton->setStyleSheet("QPushButton {" + "background-color: #e74c3c;" + "color: white;" + "border: none;" + "padding: 10px 20px;" + "font-size: 14px;" + "border-radius: 5px;" + "}" + "QPushButton:hover {" + "background-color: #c0392b;" + "}"); + submitButton->setFixedWidth(120); + submitButton->setVisible(false); + + buttonLayout->addStretch(); + buttonLayout->addWidget(nextButton); + buttonLayout->addSpacing(10); + buttonLayout->addWidget(submitButton); + buttonLayout->addStretch(); + + // 添加到主布局 + mainLayout->addWidget(progressLabel); + mainLayout->addWidget(questionGroup); + mainLayout->addWidget(optionsGroup); + mainLayout->addSpacing(20); + mainLayout->addWidget(buttonWidget); + mainLayout->addStretch(); + + // 连接信号槽 + connect(nextButton, &QPushButton::clicked, this, &ExamWidget::onNextClicked); + connect(submitButton, &QPushButton::clicked, this, &ExamWidget::onSubmitClicked); +} + +void ExamWidget::startExam(Grade grade, int questionCount) +{ + questionGenerator.setGrade(grade); + questions = questionGenerator.generateQuestions(questionCount); + totalQuestions = questionCount; + currentQuestion = 0; + userAnswers.clear(); + correctAnswers.clear(); + + // 生成正确答案(随机) + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, 3); + + for (int i = 0; i < totalQuestions; ++i) { + correctAnswers.push_back(dis(gen)); + } + + showQuestion(0); +} + +void ExamWidget::showQuestion(int index) +{ + if (index < totalQuestions) { + currentQuestion = index; + progressLabel->setText(QString("第 %1 题 / 共 %2 题").arg(index + 1).arg(totalQuestions)); + + // 显示题目 + QString questionText = QString::fromStdWString(questions[index]); + questionLabel->setText(questionText); + + // 生成选项 + generateOptions(); + + // 清除选择 + optionGroup->setExclusive(false); + for (int i = 0; i < 4; ++i) { + optionButtons[i]->setChecked(false); + } + optionGroup->setExclusive(true); + + // 更新按钮状态 + nextButton->setVisible(index < totalQuestions - 1); + submitButton->setVisible(index == totalQuestions - 1); + } +} + +void ExamWidget::generateOptions() +{ + // 简单实现:一个正确答案,三个随机错误答案 + int correctIndex = correctAnswers[currentQuestion]; + + for (int i = 0; i < 4; ++i) { + if (i == correctIndex) { + optionButtons[i]->setText("正确答案"); + } else { + optionButtons[i]->setText(QString("选项 %1").arg(i + 1)); + } + } +} + +void ExamWidget::onNextClicked() +{ + int selected = optionGroup->checkedId(); + if (selected == -1) { + QMessageBox::warning(this, "提示", "请选择一个答案"); + return; + } + + userAnswers.push_back(selected); + showQuestion(currentQuestion + 1); +} + +void ExamWidget::onSubmitClicked() +{ + int selected = optionGroup->checkedId(); + if (selected == -1) { + QMessageBox::warning(this, "提示", "请选择一个答案"); + return; + } + + userAnswers.push_back(selected); + + // 计算分数 + int score = 0; + for (int i = 0; i < totalQuestions; ++i) { + if (userAnswers[i] == correctAnswers[i]) { + score++; + } + } + + emit examFinished(score, totalQuestions); +} diff --git a/src/examwidget.h b/src/examwidget.h new file mode 100644 index 0000000..f5c90bd --- /dev/null +++ b/src/examwidget.h @@ -0,0 +1,49 @@ +#ifndef EXAMWIDGET_H +#define EXAMWIDGET_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "questiongenerator.h" +#include "user.h" + +class ExamWidget : public QWidget +{ + Q_OBJECT + +public: + explicit ExamWidget(QWidget *parent = nullptr); + void startExam(Grade grade, int questionCount); + +signals: + void examFinished(int score, int total); + +private slots: + void onNextClicked(); + void onSubmitClicked(); + +private: + void showQuestion(int index); + void generateOptions(); + + QLabel *questionLabel; + QLabel *progressLabel; + QButtonGroup *optionGroup; + QRadioButton *optionButtons[4]; + QPushButton *nextButton; + QPushButton *submitButton; + + QuestionGenerator questionGenerator; + std::vector questions; + std::vector userAnswers; + std::vector correctAnswers; + int currentQuestion; + int totalQuestions; +}; + +#endif diff --git a/src/loginwidget.cpp b/src/loginwidget.cpp new file mode 100644 index 0000000..c4f9339 --- /dev/null +++ b/src/loginwidget.cpp @@ -0,0 +1,61 @@ +#include "loginwidget.h" + +LoginWidget::LoginWidget(QWidget *parent) : QWidget(parent) +{ + QVBoxLayout *layout = new QVBoxLayout(this); + + // 标题 + QLabel *titleLabel = new QLabel("数学学习软件 - 登录"); + titleLabel->setAlignment(Qt::AlignCenter); + titleLabel->setStyleSheet("font-size: 20px; font-weight: bold; margin: 20px;"); + + // 用户名输入 + QLabel *usernameLabel = new QLabel("用户名:"); + usernameEdit = new QLineEdit(); + + // 密码输入 + QLabel *passwordLabel = new QLabel("密码:"); + passwordEdit = new QLineEdit(); + passwordEdit->setEchoMode(QLineEdit::Password); + + // 按钮 + loginButton = new QPushButton("登录"); + registerButton = new QPushButton("注册新账号"); + + // 添加到布局 + layout->addWidget(titleLabel); + layout->addWidget(usernameLabel); + layout->addWidget(usernameEdit); + layout->addWidget(passwordLabel); + layout->addWidget(passwordEdit); + layout->addWidget(loginButton); + layout->addWidget(registerButton); + + // 连接信号槽 + connect(loginButton, &QPushButton::clicked, this, &LoginWidget::onLoginClicked); + connect(registerButton, &QPushButton::clicked, this, &LoginWidget::onRegisterClicked); +} + +void LoginWidget::onLoginClicked() +{ + QString username = usernameEdit->text(); + QString password = passwordEdit->text(); + + if (username.isEmpty() || password.isEmpty()) { + QMessageBox::warning(this, "输入错误", "请输入用户名和密码"); + return; + } + + User* user = userManager.authenticateUser(username.toStdWString(), + password.toStdWString()); + if (user) { + emit loginSuccess(user); + } else { + QMessageBox::warning(this, "登录失败", "用户名或密码错误"); + } +} + +void LoginWidget::onRegisterClicked() +{ + emit showRegister(); +} diff --git a/src/loginwidget.h b/src/loginwidget.h new file mode 100644 index 0000000..a1929f0 --- /dev/null +++ b/src/loginwidget.h @@ -0,0 +1,36 @@ +#ifndef LOGINWIDGET_H +#define LOGINWIDGET_H + + +#include +#include +#include +#include +#include +#include +#include "usermanage.h" + +class LoginWidget : public QWidget +{ + Q_OBJECT + +public: + explicit LoginWidget(QWidget *parent = nullptr); + +signals: + void loginSuccess(User* user); + void showRegister(); + +private slots: + void onLoginClicked(); + void onRegisterClicked(); + +private: + QLineEdit *usernameEdit; + QLineEdit *passwordEdit; + QPushButton *loginButton; + QPushButton *registerButton; + UserManager userManager; +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..7b89e2d --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,17 @@ +#include +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + // 设置应用程序信息 + app.setApplicationName("数学学习软件"); + app.setApplicationVersion("1.0"); + app.setOrganizationName("软件工程学院"); + + MainWindow window; + window.show(); + + return app.exec(); +} diff --git a/src/mainmenuwidget.cpp b/src/mainmenuwidget.cpp new file mode 100644 index 0000000..f8cb0b5 --- /dev/null +++ b/src/mainmenuwidget.cpp @@ -0,0 +1,115 @@ +#include "mainmenuwidget.h" +#include + +MainMenuWidget::MainMenuWidget(QWidget *parent) : QWidget(parent) +{ + QVBoxLayout *mainLayout = new QVBoxLayout(this); + + // 标题和欢迎信息 + QLabel *titleLabel = new QLabel("数学学习软件"); + titleLabel->setAlignment(Qt::AlignCenter); + titleLabel->setStyleSheet("font-size: 24px; font-weight: bold; margin: 30px; color: #2c3e50;"); + + welcomeLabel = new QLabel("欢迎使用数学学习软件"); + welcomeLabel->setAlignment(Qt::AlignCenter); + welcomeLabel->setStyleSheet("font-size: 18px; margin: 20px; color: #34495e;"); + + userInfoLabel = new QLabel(); + userInfoLabel->setAlignment(Qt::AlignCenter); + userInfoLabel->setStyleSheet("font-size: 14px; margin: 10px; color: #7f8c8d;"); + + // 设置区域 + QGroupBox *settingsGroup = new QGroupBox("考试设置"); + settingsGroup->setStyleSheet("QGroupBox {" + "font-size: 16px;" + "font-weight: bold;" + "margin-top: 10px;" + "}" + "QGroupBox::title {" + "subcontrol-origin: margin;" + "subcontrol-position: top center;" + "padding: 0 5px;" + "}"); + + QVBoxLayout *settingsLayout = new QVBoxLayout(settingsGroup); + + // 年级选择 + QWidget *gradeWidget = new QWidget(); + QHBoxLayout *gradeLayout = new QHBoxLayout(gradeWidget); + QLabel *gradeLabel = new QLabel("选择年级:"); + gradeLabel->setStyleSheet("font-size: 14px;"); + gradeComboBox = new QComboBox(); + gradeComboBox->addItem("小学"); + gradeComboBox->addItem("初中"); + gradeComboBox->addItem("高中"); + gradeComboBox->setStyleSheet("padding: 8px; font-size: 14px;"); + gradeLayout->addWidget(gradeLabel); + gradeLayout->addWidget(gradeComboBox); + gradeLayout->addStretch(); + + // 题目数量 + QWidget *countWidget = new QWidget(); + QHBoxLayout *countLayout = new QHBoxLayout(countWidget); + QLabel *countLabel = new QLabel("题目数量:"); + countLabel->setStyleSheet("font-size: 14px;"); + questionCountSpinBox = new QSpinBox(); + questionCountSpinBox->setRange(10, 30); + questionCountSpinBox->setValue(15); + questionCountSpinBox->setStyleSheet("padding: 8px; font-size: 14px;"); + countLayout->addWidget(countLabel); + countLayout->addWidget(questionCountSpinBox); + countLayout->addStretch(); + + // 开始按钮 + startButton = new QPushButton("开始考试"); + startButton->setStyleSheet("QPushButton {" + "background-color: #e74c3c;" + "color: white;" + "border: none;" + "padding: 12px 30px;" + "font-size: 16px;" + "border-radius: 5px;" + "}" + "QPushButton:hover {" + "background-color: #c0392b;" + "}"); + startButton->setFixedSize(150, 50); + + // 添加到设置布局 + settingsLayout->addWidget(gradeWidget); + settingsLayout->addWidget(countWidget); + settingsLayout->addSpacing(20); + settingsLayout->addWidget(startButton, 0, Qt::AlignCenter); + + // 添加到主布局 + mainLayout->addWidget(titleLabel); + mainLayout->addWidget(welcomeLabel); + mainLayout->addWidget(userInfoLabel); + mainLayout->addSpacing(20); + mainLayout->addWidget(settingsGroup); + mainLayout->addStretch(); + + // 连接信号槽 + connect(startButton, &QPushButton::clicked, this, &MainMenuWidget::onStartExamClicked); +} + +void MainMenuWidget::setUserInfo(const std::wstring& username, const std::wstring& grade) +{ + QString userInfo = QString("当前用户: %1 | 年级: %2") + .arg(QString::fromStdWString(username)) + .arg(QString::fromStdWString(grade)); + userInfoLabel->setText(userInfo); + + // 设置年级选择框 + QString gradeStr = QString::fromStdWString(grade); + int index = gradeComboBox->findText(gradeStr); + if (index >= 0) { + gradeComboBox->setCurrentIndex(index); + } +} + +void MainMenuWidget::onStartExamClicked() +{ + int questionCount = questionCountSpinBox->value(); + emit startExam(questionCount); +} diff --git a/src/mainmenuwidget.h b/src/mainmenuwidget.h new file mode 100644 index 0000000..b1563b1 --- /dev/null +++ b/src/mainmenuwidget.h @@ -0,0 +1,35 @@ +#ifndef MAINMENUWIDGET_H +#define MAINMENUWIDGET_H + +#include +#include +#include +#include +#include +#include +#include +#include + +class MainMenuWidget : public QWidget +{ + Q_OBJECT + +public: + explicit MainMenuWidget(QWidget *parent = nullptr); + void setUserInfo(const std::wstring& username, const std::wstring& grade); + +signals: + void startExam(int questionCount); + +private slots: + void onStartExamClicked(); + +private: + QLabel *welcomeLabel; + QLabel *userInfoLabel; + QComboBox *gradeComboBox; + QSpinBox *questionCountSpinBox; + QPushButton *startButton; +}; + +#endif diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp new file mode 100644 index 0000000..bd56583 --- /dev/null +++ b/src/mainwindow.cpp @@ -0,0 +1,82 @@ +#include "mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), currentUser(nullptr) +{ + setWindowTitle("中小学数学学习软件"); + setFixedSize(600, 400); + + // 创建堆叠窗口 + stackedWidget = new QStackedWidget(this); + setCentralWidget(stackedWidget); + + // 创建各个界面 + loginWidget = new LoginWidget(); + registerWidget = new RegisterWidget(); + mainMenuWidget = new MainMenuWidget(); + examWidget = new ExamWidget(); + resultWidget = new ResultWidget(); + + // 添加到堆叠窗口 + stackedWidget->addWidget(loginWidget); + stackedWidget->addWidget(registerWidget); + stackedWidget->addWidget(mainMenuWidget); + stackedWidget->addWidget(examWidget); + stackedWidget->addWidget(resultWidget); + + // 连接信号槽 + connect(loginWidget, &LoginWidget::loginSuccess, this, &MainWindow::onUserLoggedIn); + connect(loginWidget, &LoginWidget::showRegister, this, &MainWindow::showRegister); + connect(registerWidget, &RegisterWidget::showLogin, this, &MainWindow::showLogin); + connect(registerWidget, &RegisterWidget::registerSuccess, this, &MainWindow::onUserLoggedIn); + connect(mainMenuWidget, &MainMenuWidget::startExam, this, &MainWindow::showExam); + connect(examWidget, &ExamWidget::examFinished, this, &MainWindow::showResult); + connect(resultWidget, &ResultWidget::backToMenu, this, &MainWindow::showMainMenu); + connect(resultWidget, &ResultWidget::startNewExam, this, &MainWindow::showMainMenu); + + // 显示登录界面 + showLogin(); +} + +MainWindow::~MainWindow() +{ +} + +void MainWindow::showLogin() +{ + stackedWidget->setCurrentWidget(loginWidget); +} + +void MainWindow::showRegister() +{ + stackedWidget->setCurrentWidget(registerWidget); +} + +void MainWindow::showMainMenu() +{ + if (currentUser) { + mainMenuWidget->setUserInfo(currentUser->getUsername(), + currentUser->getGradeString()); + stackedWidget->setCurrentWidget(mainMenuWidget); + } +} + +void MainWindow::showExam(int questionCount) +{ + if (currentUser) { + examWidget->startExam(currentUser->getGrade(), questionCount); + stackedWidget->setCurrentWidget(examWidget); + } +} + +void MainWindow::showResult(int score, int total) +{ + resultWidget->setResult(score, total); + stackedWidget->setCurrentWidget(resultWidget); +} + +void MainWindow::onUserLoggedIn(User* user) +{ + currentUser = user; + showMainMenu(); +} diff --git a/src/mainwindow.h b/src/mainwindow.h new file mode 100644 index 0000000..988d647 --- /dev/null +++ b/src/mainwindow.h @@ -0,0 +1,40 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include "loginwidget.h" +#include "registerwidget.h" +#include "mainmenuwidget.h" +#include "examwidget.h" +#include "resultwidget.h" +#include "usermanage.h" + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void showLogin(); + void showRegister(); + void showMainMenu(); + void showExam(int questionCount); + void showResult(int score, int total); + void onUserLoggedIn(User* user); + +private: + QStackedWidget *stackedWidget; + LoginWidget *loginWidget; + RegisterWidget *registerWidget; + MainMenuWidget *mainMenuWidget; + ExamWidget *examWidget; + ResultWidget *resultWidget; + UserManager userManager; + User *currentUser; +}; + +#endif diff --git a/src/registerwidget.cpp b/src/registerwidget.cpp new file mode 100644 index 0000000..b80673c --- /dev/null +++ b/src/registerwidget.cpp @@ -0,0 +1,113 @@ +#include "registerwidget.h" + +RegisterWidget::RegisterWidget(QWidget *parent) : QWidget(parent) +{ + QVBoxLayout *layout = new QVBoxLayout(this); + + // 标题 + QLabel *titleLabel = new QLabel("用户注册"); + titleLabel->setAlignment(Qt::AlignCenter); + titleLabel->setStyleSheet("font-size: 20px; font-weight: bold; margin: 20px;"); + + // 邮箱输入 + QLabel *emailLabel = new QLabel("邮箱:"); + emailEdit = new QLineEdit(); + + // 注册码 + QLabel *codeLabel = new QLabel("注册码:"); + registerCodeEdit = new QLineEdit(); + registerCodeEdit->setText(generateRegisterCode()); + registerCodeEdit->setReadOnly(true); + + // 密码输入 + QLabel *passwordLabel = new QLabel("密码 (6-10位,包含大小写字母和数字):"); + passwordEdit = new QLineEdit(); + passwordEdit->setEchoMode(QLineEdit::Password); + + // 确认密码 + QLabel *confirmPasswordLabel = new QLabel("确认密码:"); + confirmPasswordEdit = new QLineEdit(); + confirmPasswordEdit->setEchoMode(QLineEdit::Password); + + // 按钮 + registerButton = new QPushButton("注册"); + backButton = new QPushButton("返回登录"); + + // 添加到布局 + layout->addWidget(titleLabel); + layout->addWidget(emailLabel); + layout->addWidget(emailEdit); + layout->addWidget(codeLabel); + layout->addWidget(registerCodeEdit); + layout->addWidget(passwordLabel); + layout->addWidget(passwordEdit); + layout->addWidget(confirmPasswordLabel); + layout->addWidget(confirmPasswordEdit); + layout->addWidget(registerButton); + layout->addWidget(backButton); + + // 连接信号槽 + connect(registerButton, &QPushButton::clicked, this, &RegisterWidget::onRegisterClicked); + connect(backButton, &QPushButton::clicked, this, &RegisterWidget::onBackClicked); +} + +void RegisterWidget::onRegisterClicked() +{ + QString email = emailEdit->text(); + QString password = passwordEdit->text(); + QString confirmPassword = confirmPasswordEdit->text(); + + // 邮箱格式验证 + QRegularExpression emailRegex(R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)"); + if (!emailRegex.match(email).hasMatch()) { + QMessageBox::warning(this, "输入错误", "请输入有效的邮箱地址"); + return; + } + + // 密码验证 + if (password != confirmPassword) { + QMessageBox::warning(this, "输入错误", "两次输入的密码不一致"); + return; + } + + if (!validatePassword(password)) { + QMessageBox::warning(this, "输入错误", + "密码必须为6-10位,且包含大小写字母和数字"); + return; + } + + QMessageBox::information(this, "注册成功", "注册成功!"); + emit registerSuccess(); +} + +void RegisterWidget::onBackClicked() +{ + emit showLogin(); +} + +bool RegisterWidget::validatePassword(const QString &password) +{ + if (password.length() < 6 || password.length() > 10) { + return false; + } + + bool hasUpper = false, hasLower = false, hasDigit = false; + for (QChar ch : password) { + if (ch.isUpper()) hasUpper = true; + else if (ch.isLower()) hasLower = true; + else if (ch.isDigit()) hasDigit = true; + } + + return hasUpper && hasLower && hasDigit; +} + +QString RegisterWidget::generateRegisterCode() +{ + const QString possibleChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + QString code; + for (int i = 0; i < 8; ++i) { + int index = qrand() % possibleChars.length(); + code.append(possibleChars.at(index)); + } + return code; +} diff --git a/src/registerwidget.h b/src/registerwidget.h new file mode 100644 index 0000000..865ddd9 --- /dev/null +++ b/src/registerwidget.h @@ -0,0 +1,40 @@ +#ifndef REGISTERWIDGET_H +#define REGISTERWIDGET_H + +#include +#include +#include +#include +#include +#include +#include +#include + +class RegisterWidget : public QWidget +{ + Q_OBJECT + +public: + explicit RegisterWidget(QWidget *parent = nullptr); + +signals: + void registerSuccess(); + void showLogin(); + +private slots: + void onRegisterClicked(); + void onBackClicked(); + +private: + QLineEdit *emailEdit; + QLineEdit *registerCodeEdit; + QLineEdit *passwordEdit; + QLineEdit *confirmPasswordEdit; + QPushButton *registerButton; + QPushButton *backButton; + + bool validatePassword(const QString &password); + QString generateRegisterCode(); +}; + +#endif diff --git a/src/resultwidget.cpp b/src/resultwidget.cpp new file mode 100644 index 0000000..23f9f3e --- /dev/null +++ b/src/resultwidget.cpp @@ -0,0 +1,115 @@ +#include "resultwidget.h" + +ResultWidget::ResultWidget(QWidget *parent) : QWidget(parent) +{ + QVBoxLayout *mainLayout = new QVBoxLayout(this); + + // 标题 + QLabel *titleLabel = new QLabel("考试结果"); + titleLabel->setAlignment(Qt::AlignCenter); + titleLabel->setStyleSheet("font-size: 24px; font-weight: bold; margin: 30px; color: #2c3e50;"); + + // 结果区域 + QWidget *resultWidget = new QWidget(); + QVBoxLayout *resultLayout = new QVBoxLayout(resultWidget); + resultLayout->setContentsMargins(50, 30, 50, 30); + + resultLabel = new QLabel("考试完成!"); + resultLabel->setAlignment(Qt::AlignCenter); + resultLabel->setStyleSheet("font-size: 20px; margin: 20px; color: #34495e;"); + + scoreLabel = new QLabel(); + scoreLabel->setAlignment(Qt::AlignCenter); + scoreLabel->setStyleSheet("font-size: 36px; font-weight: bold; margin: 20px; color: #e74c3c;"); + + percentageLabel = new QLabel(); + percentageLabel->setAlignment(Qt::AlignCenter); + percentageLabel->setStyleSheet("font-size: 18px; margin: 10px; color: #7f8c8d;"); + + // 按钮区域 + QWidget *buttonWidget = new QWidget(); + QHBoxLayout *buttonLayout = new QHBoxLayout(buttonWidget); + + backButton = new QPushButton("返回主菜单"); + backButton->setStyleSheet("QPushButton {" + "background-color: #95a5a6;" + "color: white;" + "border: none;" + "padding: 10px 20px;" + "font-size: 14px;" + "border-radius: 5px;" + "}" + "QPushButton:hover {" + "background-color: #7f8c8d;" + "}"); + backButton->setFixedWidth(120); + + newExamButton = new QPushButton("继续做题"); + newExamButton->setStyleSheet("QPushButton {" + "background-color: #3498db;" + "color: white;" + "border: none;" + "padding: 10px 20px;" + "font-size: 14px;" + "border-radius: 5px;" + "}" + "QPushButton:hover {" + "background-color: #2980b9;" + "}"); + newExamButton->setFixedWidth(120); + + buttonLayout->addStretch(); + buttonLayout->addWidget(backButton); + buttonLayout->addSpacing(20); + buttonLayout->addWidget(newExamButton); + buttonLayout->addStretch(); + + // 添加到结果布局 + resultLayout->addWidget(resultLabel); + resultLayout->addWidget(scoreLabel); + resultLayout->addWidget(percentageLabel); + resultLayout->addSpacing(30); + resultLayout->addWidget(buttonWidget); + + // 添加到主布局 + mainLayout->addWidget(titleLabel); + mainLayout->addWidget(resultWidget); + mainLayout->addStretch(); + + // 连接信号槽 + connect(backButton, &QPushButton::clicked, this, &ResultWidget::onBackClicked); + connect(newExamButton, &QPushButton::clicked, this, &ResultWidget::onNewExamClicked); +} + +void ResultWidget::setResult(int score, int total) +{ + double percentage = (double)score / total * 100; + + scoreLabel->setText(QString("%1 / %2").arg(score).arg(total)); + percentageLabel->setText(QString("正确率: %1%").arg(percentage, 0, 'f', 1)); + + // 根据分数显示不同的评价 + if (percentage >= 90) { + resultLabel->setText("优秀!你的表现非常出色!"); + resultLabel->setStyleSheet("font-size: 20px; margin: 20px; color: #27ae60;"); + } else if (percentage >= 70) { + resultLabel->setText("良好!继续努力!"); + resultLabel->setStyleSheet("font-size: 20px; margin: 20px; color: #f39c12;"); + } else if (percentage >= 60) { + resultLabel->setText("及格!还有提升空间!"); + resultLabel->setStyleSheet("font-size: 20px; margin: 20px; color: #e67e22;"); + } else { + resultLabel->setText("需要加油!再多练习一下吧!"); + resultLabel->setStyleSheet("font-size: 20px; margin: 20px; color: #e74c3c;"); + } +} + +void ResultWidget::onBackClicked() +{ + emit backToMenu(); +} + +void ResultWidget::onNewExamClicked() +{ + emit startNewExam(); +} diff --git a/src/resultwidget.h b/src/resultwidget.h new file mode 100644 index 0000000..0343e7e --- /dev/null +++ b/src/resultwidget.h @@ -0,0 +1,34 @@ +#ifndef RESULTWIDGET_H +#define RESULTWIDGET_H + +#include +#include +#include +#include +#include + +class ResultWidget : public QWidget +{ + Q_OBJECT + +public: + explicit ResultWidget(QWidget *parent = nullptr); + void setResult(int score, int total); + +signals: + void backToMenu(); + void startNewExam(); + +private slots: + void onBackClicked(); + void onNewExamClicked(); + +private: + QLabel *resultLabel; + QLabel *scoreLabel; + QLabel *percentageLabel; + QPushButton *backButton; + QPushButton *newExamButton; +}; + +#endif -- 2.34.1 From 5c67bee91071bfe063f27c8cc5d31074a46b28de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=85=95=E9=9B=AA?= <3420852069@qq.com> Date: Fri, 10 Oct 2025 18:07:52 +0800 Subject: [PATCH 3/9] =?UTF-8?q?=E5=89=8D=E7=AB=AF=E4=BB=A3=E7=A0=812.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/examwidget.cpp | 46 ++++-- src/loginwidget.cpp | 44 +++++- src/loginwidget.h | 3 +- src/mainwindow.cpp | 21 ++- src/mainwindow.h | 2 + src/registerwidget.cpp | 318 ++++++++++++++++++++++++++++++++++++----- src/registerwidget.h | 23 ++- 7 files changed, 404 insertions(+), 53 deletions(-) diff --git a/src/examwidget.cpp b/src/examwidget.cpp index 50ef9fc..b697fd7 100644 --- a/src/examwidget.cpp +++ b/src/examwidget.cpp @@ -2,6 +2,7 @@ #include #include #include +#include ExamWidget::ExamWidget(QWidget *parent) : QWidget(parent), currentQuestion(0), totalQuestions(0) { @@ -127,13 +128,9 @@ void ExamWidget::startExam(Grade grade, int questionCount) userAnswers.clear(); correctAnswers.clear(); - // 生成正确答案(随机) - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution<> dis(0, 3); - + // 为每个题目生成正确答案 for (int i = 0; i < totalQuestions; ++i) { - correctAnswers.push_back(dis(gen)); + correctAnswers.push_back(i % 4); // 简单实现,实际应该计算 } showQuestion(0); @@ -159,6 +156,14 @@ void ExamWidget::showQuestion(int index) } optionGroup->setExclusive(true); + // 恢复之前的选择(如果有) + if (static_cast(index) < userAnswers.size()) { // 修复有符号/无符号比较 + int previousAnswer = userAnswers[index]; + if (previousAnswer >= 0 && previousAnswer < 4) { + optionButtons[previousAnswer]->setChecked(true); + } + } + // 更新按钮状态 nextButton->setVisible(index < totalQuestions - 1); submitButton->setVisible(index == totalQuestions - 1); @@ -167,14 +172,22 @@ void ExamWidget::showQuestion(int index) void ExamWidget::generateOptions() { - // 简单实现:一个正确答案,三个随机错误答案 + // 生成有意义的选项 int correctIndex = correctAnswers[currentQuestion]; for (int i = 0; i < 4; ++i) { if (i == correctIndex) { optionButtons[i]->setText("正确答案"); } else { - optionButtons[i]->setText(QString("选项 %1").arg(i + 1)); + // 生成干扰项 + QString optionText; + switch(i) { + case 0: optionText = "选项 A"; break; + case 1: optionText = "选项 B"; break; + case 2: optionText = "选项 C"; break; + case 3: optionText = "选项 D"; break; + } + optionButtons[i]->setText(optionText); } } } @@ -187,7 +200,12 @@ void ExamWidget::onNextClicked() return; } - userAnswers.push_back(selected); + // 保存当前答案 + if (static_cast(currentQuestion) >= userAnswers.size()) { // 修复有符号/无符号比较 + userAnswers.resize(currentQuestion + 1); + } + userAnswers[currentQuestion] = selected; + showQuestion(currentQuestion + 1); } @@ -199,12 +217,16 @@ void ExamWidget::onSubmitClicked() return; } - userAnswers.push_back(selected); + // 保存最后一题的答案 + if (static_cast(currentQuestion) >= userAnswers.size()) { // 修复有符号/无符号比较 + userAnswers.resize(currentQuestion + 1); + } + userAnswers[currentQuestion] = selected; // 计算分数 int score = 0; - for (int i = 0; i < totalQuestions; ++i) { - if (userAnswers[i] == correctAnswers[i]) { + for (size_t i = 0; i < userAnswers.size(); ++i) { // 使用size_t + if (i < correctAnswers.size() && userAnswers[i] == correctAnswers[i]) { score++; } } diff --git a/src/loginwidget.cpp b/src/loginwidget.cpp index c4f9339..e7a6a87 100644 --- a/src/loginwidget.cpp +++ b/src/loginwidget.cpp @@ -2,32 +2,72 @@ LoginWidget::LoginWidget(QWidget *parent) : QWidget(parent) { + // 设置最小尺寸 + setMinimumSize(400, 400); + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setSpacing(20); + layout->setContentsMargins(50, 50, 50, 50); // 标题 QLabel *titleLabel = new QLabel("数学学习软件 - 登录"); titleLabel->setAlignment(Qt::AlignCenter); - titleLabel->setStyleSheet("font-size: 20px; font-weight: bold; margin: 20px;"); + titleLabel->setStyleSheet("font-size: 24px; font-weight: bold; margin: 20px; color: #2c3e50;"); // 用户名输入 QLabel *usernameLabel = new QLabel("用户名:"); + usernameLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); usernameEdit = new QLineEdit(); + usernameEdit->setPlaceholderText("请输入用户名"); + usernameEdit->setMinimumHeight(35); + usernameEdit->setStyleSheet("QLineEdit { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); // 密码输入 QLabel *passwordLabel = new QLabel("密码:"); + passwordLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); passwordEdit = new QLineEdit(); passwordEdit->setEchoMode(QLineEdit::Password); + passwordEdit->setPlaceholderText("请输入密码"); + passwordEdit->setMinimumHeight(35); + passwordEdit->setStyleSheet("QLineEdit { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); // 按钮 loginButton = new QPushButton("登录"); + loginButton->setMinimumHeight(40); + loginButton->setStyleSheet("QPushButton {" + "background-color: #3498db;" + "color: white;" + "border: none;" + "font-size: 16px;" + "font-weight: bold;" + "border-radius: 5px;" + "}" + "QPushButton:hover {" + "background-color: #2980b9;" + "}"); + registerButton = new QPushButton("注册新账号"); + registerButton->setMinimumHeight(40); + registerButton->setStyleSheet("QPushButton {" + "background-color: #95a5a6;" + "color: white;" + "border: none;" + "font-size: 16px;" + "font-weight: bold;" + "border-radius: 5px;" + "}" + "QPushButton:hover {" + "background-color: #7f8c8d;" + "}"); // 添加到布局 layout->addWidget(titleLabel); + layout->addSpacing(30); layout->addWidget(usernameLabel); layout->addWidget(usernameEdit); layout->addWidget(passwordLabel); layout->addWidget(passwordEdit); + layout->addSpacing(30); layout->addWidget(loginButton); layout->addWidget(registerButton); @@ -38,7 +78,7 @@ LoginWidget::LoginWidget(QWidget *parent) : QWidget(parent) void LoginWidget::onLoginClicked() { - QString username = usernameEdit->text(); + QString username = usernameEdit->text().trimmed(); QString password = passwordEdit->text(); if (username.isEmpty() || password.isEmpty()) { diff --git a/src/loginwidget.h b/src/loginwidget.h index a1929f0..9c9d501 100644 --- a/src/loginwidget.h +++ b/src/loginwidget.h @@ -1,7 +1,6 @@ #ifndef LOGINWIDGET_H #define LOGINWIDGET_H - #include #include #include @@ -26,7 +25,7 @@ private slots: void onRegisterClicked(); private: - QLineEdit *usernameEdit; + QLineEdit *usernameEdit; // 改为用户名输入 QLineEdit *passwordEdit; QPushButton *loginButton; QPushButton *registerButton; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index bd56583..a76a040 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,10 +1,12 @@ #include "mainwindow.h" +#include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), currentUser(nullptr) { setWindowTitle("中小学数学学习软件"); - setFixedSize(600, 400); + setMinimumSize(600, 700); + resize(600, 700); // 创建堆叠窗口 stackedWidget = new QStackedWidget(this); @@ -28,7 +30,7 @@ MainWindow::MainWindow(QWidget *parent) connect(loginWidget, &LoginWidget::loginSuccess, this, &MainWindow::onUserLoggedIn); connect(loginWidget, &LoginWidget::showRegister, this, &MainWindow::showRegister); connect(registerWidget, &RegisterWidget::showLogin, this, &MainWindow::showLogin); - connect(registerWidget, &RegisterWidget::registerSuccess, this, &MainWindow::onUserLoggedIn); + connect(registerWidget, &RegisterWidget::registerSuccess, this, &MainWindow::onRegisterSuccess); connect(mainMenuWidget, &MainMenuWidget::startExam, this, &MainWindow::showExam); connect(examWidget, &ExamWidget::examFinished, this, &MainWindow::showResult); connect(resultWidget, &ResultWidget::backToMenu, this, &MainWindow::showMainMenu); @@ -73,6 +75,14 @@ void MainWindow::showResult(int score, int total) { resultWidget->setResult(score, total); stackedWidget->setCurrentWidget(resultWidget); + + // 保存考试记录 + if (currentUser) { + FileSaver::saveExamRecord(currentUser->getUsername(), + currentUser->getGradeString(), + score, total, + FileSaver::getCurrentTime()); + } } void MainWindow::onUserLoggedIn(User* user) @@ -80,3 +90,10 @@ void MainWindow::onUserLoggedIn(User* user) currentUser = user; showMainMenu(); } + +void MainWindow::onRegisterSuccess() +{ + // 注册成功后显示登录界面 + QMessageBox::information(this, "注册成功", "注册成功,请登录"); + showLogin(); +} diff --git a/src/mainwindow.h b/src/mainwindow.h index 988d647..293a82f 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -9,6 +9,7 @@ #include "examwidget.h" #include "resultwidget.h" #include "usermanage.h" +#include "filesaver.h" class MainWindow : public QMainWindow { @@ -25,6 +26,7 @@ private slots: void showExam(int questionCount); void showResult(int score, int total); void onUserLoggedIn(User* user); + void onRegisterSuccess(); private: QStackedWidget *stackedWidget; diff --git a/src/registerwidget.cpp b/src/registerwidget.cpp index b80673c..9946c8b 100644 --- a/src/registerwidget.cpp +++ b/src/registerwidget.cpp @@ -1,70 +1,260 @@ #include "registerwidget.h" +#include "usermanage.h" +#include +#include +#include +#include -RegisterWidget::RegisterWidget(QWidget *parent) : QWidget(parent) +RegisterWidget::RegisterWidget(QWidget *parent) : QWidget(parent), countdownSeconds(0) { - QVBoxLayout *layout = new QVBoxLayout(this); + // 设置最小尺寸 + setMinimumSize(500, 600); + + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->setSpacing(20); + mainLayout->setContentsMargins(40, 30, 40, 30); // 标题 QLabel *titleLabel = new QLabel("用户注册"); titleLabel->setAlignment(Qt::AlignCenter); - titleLabel->setStyleSheet("font-size: 20px; font-weight: bold; margin: 20px;"); + titleLabel->setStyleSheet("font-size: 28px; font-weight: bold; margin: 20px; color: #2c3e50;"); + + // 创建网格布局用于表单 + QGridLayout *formLayout = new QGridLayout(); + formLayout->setSpacing(15); + formLayout->setColumnMinimumWidth(0, 100); + formLayout->setColumnStretch(1, 1); // 邮箱输入 QLabel *emailLabel = new QLabel("邮箱:"); + emailLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); emailEdit = new QLineEdit(); + emailEdit->setPlaceholderText("请输入您的邮箱"); + emailEdit->setMinimumHeight(35); + emailEdit->setStyleSheet("QLineEdit { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); + + // 发送验证码按钮 + sendCodeButton = new QPushButton("发送验证码"); + sendCodeButton->setFixedWidth(120); + sendCodeButton->setMinimumHeight(35); + sendCodeButton->setStyleSheet("QPushButton {" + "background-color: #3498db;" + "color: white;" + "border: none;" + "font-size: 12px;" + "border-radius: 4px;" + "}" + "QPushButton:hover {" + "background-color: #2980b9;" + "}" + "QPushButton:disabled {" + "background-color: #bdc3c7;" + "}"); + + // 验证码输入 + QLabel *codeLabel = new QLabel("验证码:"); + codeLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); + verificationCodeEdit = new QLineEdit(); + verificationCodeEdit->setPlaceholderText("请输入收到的验证码"); + verificationCodeEdit->setMinimumHeight(35); + verificationCodeEdit->setStyleSheet("QLineEdit { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); - // 注册码 - QLabel *codeLabel = new QLabel("注册码:"); - registerCodeEdit = new QLineEdit(); - registerCodeEdit->setText(generateRegisterCode()); - registerCodeEdit->setReadOnly(true); + // 计时器标签 + timerLabel = new QLabel(); + timerLabel->setStyleSheet("color: #e74c3c; font-size: 14px; font-weight: bold;"); + timerLabel->setFixedWidth(60); + timerLabel->setAlignment(Qt::AlignCenter); + + // 用户名输入 + QLabel *usernameLabel = new QLabel("用户名:"); + usernameLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); + usernameEdit = new QLineEdit(); + usernameEdit->setPlaceholderText("请输入用户名(2-20个字符)"); + usernameEdit->setMinimumHeight(35); + usernameEdit->setStyleSheet("QLineEdit { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); + + // 年级选择 + QLabel *gradeLabel = new QLabel("选择年级:"); + gradeLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); + gradeComboBox = new QComboBox(); + gradeComboBox->addItem("小学"); + gradeComboBox->addItem("初中"); + gradeComboBox->addItem("高中"); + gradeComboBox->setMinimumHeight(35); + gradeComboBox->setStyleSheet("QComboBox { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); // 密码输入 - QLabel *passwordLabel = new QLabel("密码 (6-10位,包含大小写字母和数字):"); + QLabel *passwordLabel = new QLabel("密码:"); + passwordLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); passwordEdit = new QLineEdit(); passwordEdit->setEchoMode(QLineEdit::Password); + passwordEdit->setPlaceholderText("6-10位,包含大小写字母和数字"); + passwordEdit->setMinimumHeight(35); + passwordEdit->setStyleSheet("QLineEdit { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); // 确认密码 QLabel *confirmPasswordLabel = new QLabel("确认密码:"); + confirmPasswordLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); confirmPasswordEdit = new QLineEdit(); confirmPasswordEdit->setEchoMode(QLineEdit::Password); + confirmPasswordEdit->setPlaceholderText("请再次输入密码"); + confirmPasswordEdit->setMinimumHeight(35); + confirmPasswordEdit->setStyleSheet("QLineEdit { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); + + // 设置表单布局 + int row = 0; + + // 邮箱行 + formLayout->addWidget(emailLabel, row, 0, Qt::AlignRight | Qt::AlignVCenter); + QHBoxLayout *emailLayout = new QHBoxLayout(); + emailLayout->setSpacing(10); + emailLayout->addWidget(emailEdit); + emailLayout->addWidget(sendCodeButton); + formLayout->addLayout(emailLayout, row, 1); + row++; + + // 验证码行 + formLayout->addWidget(codeLabel, row, 0, Qt::AlignRight | Qt::AlignVCenter); + QHBoxLayout *codeLayout = new QHBoxLayout(); + codeLayout->setSpacing(10); + codeLayout->addWidget(verificationCodeEdit); + codeLayout->addWidget(timerLabel); + formLayout->addLayout(codeLayout, row, 1); + row++; + + // 用户名行 + formLayout->addWidget(usernameLabel, row, 0, Qt::AlignRight | Qt::AlignVCenter); + formLayout->addWidget(usernameEdit, row, 1); + row++; + + // 年级行 + formLayout->addWidget(gradeLabel, row, 0, Qt::AlignRight | Qt::AlignVCenter); + formLayout->addWidget(gradeComboBox, row, 1); + row++; + + // 密码行 + formLayout->addWidget(passwordLabel, row, 0, Qt::AlignRight | Qt::AlignVCenter); + formLayout->addWidget(passwordEdit, row, 1); + row++; + + // 确认密码行 + formLayout->addWidget(confirmPasswordLabel, row, 0, Qt::AlignRight | Qt::AlignVCenter); + formLayout->addWidget(confirmPasswordEdit, row, 1); + row++; + + // 按钮区域 + QWidget *buttonWidget = new QWidget(); + QHBoxLayout *buttonLayout = new QHBoxLayout(buttonWidget); + buttonLayout->setSpacing(30); + buttonLayout->setContentsMargins(0, 30, 0, 0); - // 按钮 registerButton = new QPushButton("注册"); + registerButton->setFixedSize(140, 45); + registerButton->setStyleSheet("QPushButton {" + "background-color: #27ae60;" + "color: white;" + "border: none;" + "font-size: 16px;" + "font-weight: bold;" + "border-radius: 6px;" + "}" + "QPushButton:hover {" + "background-color: #229954;" + "}"); + backButton = new QPushButton("返回登录"); + backButton->setFixedSize(140, 45); + backButton->setStyleSheet("QPushButton {" + "background-color: #95a5a6;" + "color: white;" + "border: none;" + "font-size: 16px;" + "font-weight: bold;" + "border-radius: 6px;" + "}" + "QPushButton:hover {" + "background-color: #7f8c8d;" + "}"); - // 添加到布局 - layout->addWidget(titleLabel); - layout->addWidget(emailLabel); - layout->addWidget(emailEdit); - layout->addWidget(codeLabel); - layout->addWidget(registerCodeEdit); - layout->addWidget(passwordLabel); - layout->addWidget(passwordEdit); - layout->addWidget(confirmPasswordLabel); - layout->addWidget(confirmPasswordEdit); - layout->addWidget(registerButton); - layout->addWidget(backButton); + buttonLayout->addStretch(); + buttonLayout->addWidget(registerButton); + buttonLayout->addWidget(backButton); + buttonLayout->addStretch(); + + // 添加到主布局 + mainLayout->addWidget(titleLabel); + mainLayout->addLayout(formLayout); + mainLayout->addStretch(); + mainLayout->addWidget(buttonWidget); + + // 初始化计时器 + countdownTimer = new QTimer(this); + countdownTimer->setInterval(1000); // 连接信号槽 + connect(sendCodeButton, &QPushButton::clicked, this, &RegisterWidget::onSendCodeClicked); connect(registerButton, &QPushButton::clicked, this, &RegisterWidget::onRegisterClicked); connect(backButton, &QPushButton::clicked, this, &RegisterWidget::onBackClicked); + connect(countdownTimer, &QTimer::timeout, this, &RegisterWidget::updateTimer); + + // 初始状态 + resetCountdown(); +} + +void RegisterWidget::onSendCodeClicked() +{ + QString email = emailEdit->text().trimmed(); + + if (!validateEmail(email)) { + QMessageBox::warning(this, "输入错误", "请输入有效的邮箱地址"); + return; + } + + // 生成并显示验证码 + generatedCode = generateVerificationCode(); + + QMessageBox::information(this, "验证码", + QString("验证码已发送到 %1\n验证码:%2\n(实际项目中会发送到邮箱)") + .arg(email).arg(generatedCode)); + + // 开始倒计时 + startCountdown(); } void RegisterWidget::onRegisterClicked() { - QString email = emailEdit->text(); + QString email = emailEdit->text().trimmed(); + QString username = usernameEdit->text().trimmed(); + QString verificationCode = verificationCodeEdit->text().trimmed(); QString password = passwordEdit->text(); QString confirmPassword = confirmPasswordEdit->text(); + QString grade = gradeComboBox->currentText(); - // 邮箱格式验证 - QRegularExpression emailRegex(R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)"); - if (!emailRegex.match(email).hasMatch()) { + // 验证输入 + if (email.isEmpty() || username.isEmpty() || verificationCode.isEmpty() || + password.isEmpty() || confirmPassword.isEmpty()) { + QMessageBox::warning(this, "输入错误", "请填写所有字段"); + return; + } + + if (!validateEmail(email)) { QMessageBox::warning(this, "输入错误", "请输入有效的邮箱地址"); return; } - // 密码验证 + // 验证验证码 + if (verificationCode != generatedCode) { + QMessageBox::warning(this, "验证错误", "验证码错误"); + return; + } + + // 检查验证码是否过期 + if (countdownSeconds <= 0) { + QMessageBox::warning(this, "验证错误", "验证码已过期,请重新获取"); + return; + } + + // 验证密码 if (password != confirmPassword) { QMessageBox::warning(this, "输入错误", "两次输入的密码不一致"); return; @@ -76,12 +266,35 @@ void RegisterWidget::onRegisterClicked() return; } - QMessageBox::information(this, "注册成功", "注册成功!"); - emit registerSuccess(); + if (username.length() < 2 || username.length() > 20) { + QMessageBox::warning(this, "输入错误", "用户名长度应在2-20个字符之间"); + return; + } + + // 实际注册用户 + UserManager userManager; + if (userManager.registerUser(username.toStdWString(), + password.toStdWString(), + grade.toStdWString())) { + QMessageBox::information(this, "注册成功", "注册成功!"); + + // 重置表单 + emailEdit->clear(); + usernameEdit->clear(); + verificationCodeEdit->clear(); + passwordEdit->clear(); + confirmPasswordEdit->clear(); + resetCountdown(); + + emit registerSuccess(); + } else { + QMessageBox::warning(this, "注册失败", "用户名已存在,请选择其他用户名"); + } } void RegisterWidget::onBackClicked() { + resetCountdown(); emit showLogin(); } @@ -101,13 +314,54 @@ bool RegisterWidget::validatePassword(const QString &password) return hasUpper && hasLower && hasDigit; } -QString RegisterWidget::generateRegisterCode() +bool RegisterWidget::validateEmail(const QString &email) +{ + QRegularExpression emailRegex(R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)"); + return emailRegex.match(email).hasMatch(); +} + +QString RegisterWidget::generateVerificationCode() { - const QString possibleChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + const QString possibleChars = "0123456789"; QString code; - for (int i = 0; i < 8; ++i) { + + // 使用传统的随机数生成 + qsrand(QTime::currentTime().msec()); + + for (int i = 0; i < 6; ++i) { int index = qrand() % possibleChars.length(); code.append(possibleChars.at(index)); } return code; } + +void RegisterWidget::startCountdown() +{ + countdownSeconds = 300; // 5分钟 + sendCodeButton->setEnabled(false); + updateTimer(); + countdownTimer->start(); +} + +void RegisterWidget::resetCountdown() +{ + countdownTimer->stop(); + countdownSeconds = 0; + sendCodeButton->setEnabled(true); + timerLabel->clear(); + generatedCode.clear(); +} + +void RegisterWidget::updateTimer() +{ + if (countdownSeconds > 0) { + countdownSeconds--; + int minutes = countdownSeconds / 60; + int seconds = countdownSeconds % 60; + timerLabel->setText(QString("%1:%2").arg(minutes, 2, 10, QLatin1Char('0')) + .arg(seconds, 2, 10, QLatin1Char('0'))); + } else { + resetCountdown(); + QMessageBox::information(this, "提示", "验证码已过期,请重新获取"); + } +} diff --git a/src/registerwidget.h b/src/registerwidget.h index 865ddd9..b49d453 100644 --- a/src/registerwidget.h +++ b/src/registerwidget.h @@ -8,7 +8,11 @@ #include #include #include -#include +#include +#include +#include +#include +#include class RegisterWidget : public QWidget { @@ -22,19 +26,32 @@ signals: void showLogin(); private slots: + void onSendCodeClicked(); void onRegisterClicked(); void onBackClicked(); + void updateTimer(); private: QLineEdit *emailEdit; - QLineEdit *registerCodeEdit; + QLineEdit *usernameEdit; + QLineEdit *verificationCodeEdit; QLineEdit *passwordEdit; QLineEdit *confirmPasswordEdit; + QComboBox *gradeComboBox; + QPushButton *sendCodeButton; QPushButton *registerButton; QPushButton *backButton; + QLabel *timerLabel; + + QTimer *countdownTimer; + int countdownSeconds; + QString generatedCode; bool validatePassword(const QString &password); - QString generateRegisterCode(); + bool validateEmail(const QString &email); + QString generateVerificationCode(); + void startCountdown(); + void resetCountdown(); }; #endif -- 2.34.1 From 72f7bee6604430f137eb1460f446881e2bc916a2 Mon Sep 17 00:00:00 2001 From: lxf450481 <705835715@qq.com> Date: Fri, 10 Oct 2025 18:32:52 +0800 Subject: [PATCH 4/9] origin --- project/filesaver.cpp | 205 ++++++++++++++++++++++++++++++++++ project/filesaver.h | 63 +++++++++++ project/questiongenerator.cpp | 173 ++++++++++++++++++++++++++++ project/questiongenerator.h | 63 +++++++++++ project/user.cpp | 41 +++++++ project/user.h | 41 +++++++ project/usermanage.cpp | 134 ++++++++++++++++++++++ project/usermanage.h | 49 ++++++++ 8 files changed, 769 insertions(+) create mode 100644 project/filesaver.cpp create mode 100644 project/filesaver.h create mode 100644 project/questiongenerator.cpp create mode 100644 project/questiongenerator.h create mode 100644 project/user.cpp create mode 100644 project/user.h create mode 100644 project/usermanage.cpp create mode 100644 project/usermanage.h diff --git a/project/filesaver.cpp b/project/filesaver.cpp new file mode 100644 index 0000000..4637655 --- /dev/null +++ b/project/filesaver.cpp @@ -0,0 +1,205 @@ +#include "filesaver.h" +#include +#include +#include +#include +#include + +//创建目录(跨平台实现) +bool FileSaver::createDirectory(const std::wstring& wpath) { +#ifdef _WIN32 + return _wmkdir(wpath.c_str()) == 0; +#else + std::string path; + for (wchar_t wc : wpath) { + if (wc < 128) path += static_cast(wc); + } + return mkdir(path.c_str(), 0755) == 0; +#endif +} + +//将包含中文的用户名转换为安全的英文文件名 +std::string FileSaver::usernameToSafeName(const std::wstring& username) { + std::string result; + for (wchar_t wc : username) { + if ((wc >= L'0' && wc <= L'9') || (wc >= L'A' && wc <= L'Z') || + (wc >= L'a' && wc <= L'z') || wc == L'_') { + result += static_cast(wc); + } else { + switch(wc) { + case L'张': result += "Zhang"; break; + case L'三': result += "San"; break; + case L'李': result += "Li"; break; + case L'四': result += "Si"; break; + case L'王': result += "Wang"; break; + case L'五': result += "Wu"; break; + default: result += "User"; break; + } + } + } + return result; +} + +//将中文年级转换为英文标识 +std::string FileSaver::gradeToEnglish(const std::wstring& grade) { + if (grade == L"小学") return "Primary"; + if (grade == L"初中") return "Junior"; + if (grade == L"高中") return "Senior"; + return "Unknown"; +} + +//宽字符串到普通字符串的转换 +std::string FileSaver::wstringToString(const std::wstring& wstr) { + std::string result; + for (wchar_t wc : wstr) { + if (wc < 128) { + result += static_cast(wc); + } + } + return result; +} + +//将题目列表写入文件的实现 +bool FileSaver::writeQuestionsToFile(const std::string& filename, + const std::string& safeUsername, + const std::string& englishGrade, + const std::vector& questions) { + std::ofstream file(filename); + if (!file.is_open()) { + return false; + } + + file << "Math Exam Paper\n"; + file << "Username: " << safeUsername << "\n"; + file << "Grade: " << englishGrade << "\n"; + file << "Time: " << wstringToString(getCurrentTime()) << "\n\n"; + + for (size_t i = 0; i < questions.size(); i++) { + file << (i + 1) << ". "; + + for (wchar_t wc : questions[i]) { + switch(wc) { + case L'√': file << "√"; break; + case L'²': file << "²"; break; + case L's': file << "s"; break; + case L'i': file << "i"; break; + case L'n': file << "n"; break; + case L'c': file << "c"; break; + case L'o': file << "o"; break; + case L't': file << "t"; break; + case L'a': file << "a"; break; + case L'(': file << "("; break; + case L')': file << ")"; break; + case L'+': file << "+"; break; + case L'-': file << "-"; break; + case L'×': file << "×"; break; + case L'÷': file << "÷"; break; + case L'=': file << "="; break; + case L'?': file << "?"; break; + case L' ': file << " "; break; + case L'0': case L'1': case L'2': case L'3': case L'4': + case L'5': case L'6': case L'7': case L'8': case L'9': + file << static_cast(wc); + break; + default: file << static_cast(wc); break; + } + } + file << "\n\n"; + } + + file.close(); + return true; +} + +//准备文件和目录的实现 +std::string FileSaver::prepareFileAndDirectory(const std::wstring& username, + const std::wstring& grade) { + std::string safeUsername = usernameToSafeName(username); + std::string englishGrade = gradeToEnglish(grade); + std::string folderPath = "exam_papers/" + safeUsername + "_" + englishGrade + "_Papers"; + + // 创建目录 + std::wstring wfolderPath; + for (char c : folderPath) { + wfolderPath += wchar_t(c); + } + createDirectory(wfolderPath); + + return folderPath + "/" + wstringToString(generateFilename()) + ".txt"; +} + +//保存题目的完整流程实现 +bool FileSaver::saveToFile(const std::wstring& username, + const std::wstring& grade, + const std::vector& questions) { + std::string safeUsername = usernameToSafeName(username); + std::string englishGrade = gradeToEnglish(grade); + std::string filename = prepareFileAndDirectory(username, grade); + + bool success = writeQuestionsToFile(filename, safeUsername, englishGrade, questions); + + return success; +} + +//生成基于时间戳的文件名实现 +std::wstring FileSaver::generateFilename() { + auto now = std::chrono::system_clock::now(); + auto time_t = std::chrono::system_clock::to_time_t(now); + + std::tm tm = *std::localtime(&time_t); + std::wostringstream oss; + + oss << tm.tm_year + 1900 << L"_" + << (tm.tm_mon + 1 < 10 ? L"0" : L"") << tm.tm_mon + 1 << L"_" + << (tm.tm_mday < 10 ? L"0" : L"") << tm.tm_mday << L"_" + << (tm.tm_hour < 10 ? L"0" : L"") << tm.tm_hour << L"_" + << (tm.tm_min < 10 ? L"0" : L"") << tm.tm_min << L"_" + << (tm.tm_sec < 10 ? L"0" : L"") << tm.tm_sec; + + return oss.str(); +} + +//获取当前格式化时间的实现 +std::wstring FileSaver::getCurrentTime() { + auto now = std::chrono::system_clock::now(); + auto time_t = std::chrono::system_clock::to_time_t(now); + + std::tm tm = *std::localtime(&time_t); + std::wostringstream oss; + + oss << tm.tm_year + 1900 << L"-" + << (tm.tm_mon + 1 < 10 ? L"0" : L"") << tm.tm_mon + 1 << L"-" + << (tm.tm_mday < 10 ? L"0" : L"") << tm.tm_mday << L" " + << (tm.tm_hour < 10 ? L"0" : L"") << tm.tm_hour << L":" + << (tm.tm_min < 10 ? L"0" : L"") << tm.tm_min << L":" + << (tm.tm_sec < 10 ? L"0" : L"") << tm.tm_sec; + + return oss.str(); +} + +//保存考试记录的实现 +bool FileSaver::saveExamRecord(const std::wstring& username, + const std::wstring& grade, + int score, + int totalQuestions, + const std::wstring& examDate) { + std::string safeUsername = usernameToSafeName(username); + std::string filename = "exam_records/" + safeUsername + "_records.txt"; + + // 创建目录 + createDirectory(L"exam_records"); + + std::ofstream file(filename, std::ios::app); + if (!file.is_open()) { + return false; + } + + file << "Date: " << wstringToString(examDate) << "\n"; + file << "Grade: " << gradeToEnglish(grade) << "\n"; + file << "Score: " << score << "/" << totalQuestions << "\n"; + file << "Percentage: " << (score * 100.0 / totalQuestions) << "%\n"; + file << "------------------------\n"; + + file.close(); + return true; +} \ No newline at end of file diff --git a/project/filesaver.h b/project/filesaver.h new file mode 100644 index 0000000..4f56cc2 --- /dev/null +++ b/project/filesaver.h @@ -0,0 +1,63 @@ +#ifndef FILESAVER_H +#define FILESAVER_H + +#include +#include +#include +#include +#include + +#ifdef _WIN32 + #include + #include +#else + #include + #include +#endif + +// 文件保存器类,负责将生成的数学题目保存到文件系统 +class FileSaver { +public: + //将题目列表写入指定文件 + static bool writeQuestionsToFile(const std::string& filename, + const std::string& safeUsername, + const std::string& englishGrade, + const std::vector& questions); + + //准备用户专属文件夹和文件路径 + static std::string prepareFileAndDirectory(const std::wstring& username, + const std::wstring& grade); + + //保存题目列表到用户专属文件 + static bool saveToFile(const std::wstring& username, + const std::wstring& grade, + const std::vector& questions); + + //生成基于当前时间戳的文件名 + static std::wstring generateFilename(); + + //获取当前格式化的时间字符串 + static std::wstring getCurrentTime(); + + //保存考试记录 + static bool saveExamRecord(const std::wstring& username, + const std::wstring& grade, + int score, + int totalQuestions, + const std::wstring& examDate); + +private: + //将包含中文的用户名转换为安全的英文文件名 + static std::string usernameToSafeName(const std::wstring& username); + + //将中文年级转换为英文标识 + static std::string gradeToEnglish(const std::wstring& grade); + + //宽字符串到普通字符串的转换 + static std::string wstringToString(const std::wstring& wstr); + + //创建目录(跨平台实现) + static bool createDirectory(const std::wstring& wpath); +}; + +#endif \ No newline at end of file diff --git a/project/questiongenerator.cpp b/project/questiongenerator.cpp new file mode 100644 index 0000000..ca669d6 --- /dev/null +++ b/project/questiongenerator.cpp @@ -0,0 +1,173 @@ +#include "questiongenerator.h" +#include +#include +#include +#include +#include + +//题目生成器构造函数的实现 +QuestionGenerator::QuestionGenerator() : currentGrade(Grade::PRIMARY), gen(rd()) {} + +//设置年级的实现 +void QuestionGenerator::setGrade(Grade grade) { + currentGrade = grade; +} + +//生成随机数的实现 +std::wstring QuestionGenerator::generateNumber(int min, int max) { + std::uniform_int_distribution<> dis(min, max); + return std::to_wstring(dis(gen)); +} + +//生成随机运算符的实现 +std::wstring QuestionGenerator::generateOperator() { + std::vector operators = {L"+", L"-", L"×", L"÷"}; + std::uniform_int_distribution<> dis(0, operators.size() - 1); + return operators[dis(gen)]; +} + +//括号需求判断的辅助函数 +bool QuestionGenerator::needsParentheses(const std::wstring& expr, const std::wstring& nextOp) { + if (expr.find(L"+") != std::wstring::npos || expr.find(L"-") != std::wstring::npos) { + if (nextOp == L"×" || nextOp == L"÷") { + return true; + } + } + return false; +} + +//生成小学题目的实现 +std::wstring QuestionGenerator::generatePrimaryQuestion() { + int operandCount = std::uniform_int_distribution<>(2, 4)(gen); + std::wstring question; + + for (int i = 0; i < operandCount; i++) { + std::wstring number = generateNumber(1, 100); + + if (i == 0) { + question += number; + } else { + std::wstring op = generateOperator(); + + if (i >= 1 && operandCount > 2) { + if (needsParentheses(question, op)) { + question = L"(" + question + L") " + op + L" " + number; + } else { + question += L" " + op + L" " + number; + } + } else { + question += L" " + op + L" " + number; + } + } + } + + return question + L" = ?"; +} + +//生成初中题目的实现 +std::wstring QuestionGenerator::generateJuniorQuestion() { + std::wstring baseQuestion = generatePrimaryQuestion(); + baseQuestion = baseQuestion.substr(0, baseQuestion.length() - 3); + + std::uniform_int_distribution<> dis(0, 1); + + if (dis(gen) == 0) { + if (baseQuestion.find(L"+") != std::wstring::npos || + baseQuestion.find(L"-") != std::wstring::npos) { + return L"(" + baseQuestion + L")² = ?"; + } else { + return baseQuestion + L"² = ?"; + } + } else { + return L"√(" + baseQuestion + L") = ?"; + } +} + +//生成高中题目的实现 +std::wstring QuestionGenerator::generateSeniorQuestion() { + std::wstring baseQuestion = generatePrimaryQuestion(); + baseQuestion = baseQuestion.substr(0, baseQuestion.length() - 3); + + std::vector trigFunctions = {L"sin", L"cos", L"tan"}; + std::uniform_int_distribution<> dis(0, trigFunctions.size() - 1); + + std::wstring trigFunc = trigFunctions[dis(gen)]; + + if (baseQuestion.front() == L'(' && baseQuestion.back() == L')') { + return trigFunc + baseQuestion + L" = ?"; + } else { + return trigFunc + L"(" + baseQuestion + L") = ?"; + } +} + +//题目查重的实现 +bool QuestionGenerator::IsQuestionUnique(const std::wstring& question) { + return generatedQuestions.find(question) == generatedQuestions.end(); +} + +//生成指定数量题目的实现 +std::vector QuestionGenerator::generateQuestions(int count) { + std::vector questions; + + for (int i = 0; i < count; i++) { + std::wstring question; + + switch(currentGrade) { + case Grade::PRIMARY: + question = generatePrimaryQuestion(); + break; + case Grade::JUNIOR: + question = generateJuniorQuestion(); + break; + case Grade::SENIOR: + question = generateSeniorQuestion(); + break; + } + + if (IsQuestionUnique(question)) { + questions.push_back(question); + generatedQuestions.insert(question); + } else { + i--; + } + } + + return questions; +} + +//生成题目和选项的实现(用于GUI考试) +QuestionGenerator::QuestionWithOptions QuestionGenerator::generateQuestionWithOptions() { + QuestionWithOptions qwo; + + // 生成题目 + switch(currentGrade) { + case Grade::PRIMARY: + qwo.question = generatePrimaryQuestion(); + break; + case Grade::JUNIOR: + qwo.question = generateJuniorQuestion(); + break; + case Grade::SENIOR: + qwo.question = generateSeniorQuestion(); + break; + } + + // 生成选项(简单实现,实际应该计算正确答案) + std::uniform_int_distribution<> optionDis(1, 4); + qwo.correctIndex = optionDis(gen) - 1; + + for (int i = 0; i < 4; i++) { + if (i == qwo.correctIndex) { + qwo.options.push_back(L"正确答案"); + } else { + qwo.options.push_back(L"选项 " + std::to_wstring(i + 1)); + } + } + + return qwo; +} + +//清空历史记录的实现 +void QuestionGenerator::clearHistory() { + generatedQuestions.clear(); +} \ No newline at end of file diff --git a/project/questiongenerator.h b/project/questiongenerator.h new file mode 100644 index 0000000..d375ee3 --- /dev/null +++ b/project/questiongenerator.h @@ -0,0 +1,63 @@ +#ifndef QUESTIONGENERATOR_H +#define QUESTIONGENERATOR_H + +#include "user.h" +#include +#include +#include +#include + +//题目生成器类,根据年级生成不同难度的数学题目 +//支持小学、初中、高中三个级别的题目生成,包含查重功能。 +class QuestionGenerator { +private: + Grade currentGrade; + std::set generatedQuestions; + std::random_device rd; + std::mt19937 gen; + + //生成小学级别的基础算术题目 + std::wstring generatePrimaryQuestion(); + + //生成初中级别的题目,包含平方或开根号 + std::wstring generateJuniorQuestion(); + + //生成高中级别的题目,包含三角函数 + std::wstring generateSeniorQuestion(); + + //生成指定范围内的随机整数 + std::wstring generateNumber(int min, int max); + + //随机生成算术运算符 + std::wstring generateOperator(); + + //检查题目是否已生成过(查重) + bool IsQuestionUnique(const std::wstring& question); + + //括号需求判断 + bool needsParentheses(const std::wstring& expr, const std::wstring& nextOp); + +public: + //构造函数,初始化题目生成器 + QuestionGenerator(); + + //设置当前题目生成难度级别 + void setGrade(Grade grade); + + //生成指定数量的唯一数学题目 + std::vector generateQuestions(int count); + + //生成题目和选项(用于GUI) + struct QuestionWithOptions { + std::wstring question; + std::vector options; + int correctIndex; + }; + + QuestionWithOptions generateQuestionWithOptions(); + + //清空已生成题目的历史记录 + void clearHistory(); +}; + +#endif \ No newline at end of file diff --git a/project/user.cpp b/project/user.cpp new file mode 100644 index 0000000..65b0f8d --- /dev/null +++ b/project/user.cpp @@ -0,0 +1,41 @@ +#include "user.h" +#include + +//用户构造函数的实现 +User::User(const std::wstring& name, const std::wstring& pass, Grade grade) + : username(name), password(pass), gradeType(grade) {} + +//用户验证的实现 +bool User::check(const std::wstring& name, const std::wstring& pass) const { + return (username == name && password == pass); +} + +//获取用户名的实现 +std::wstring User::getUsername() const { + return username; +} + +//获取密码的实现 +std::wstring User::getPassword() const { + return password; +} + +//获取年级枚举值的实现 +Grade User::getGrade() const { + return gradeType; +} + +//获取年级字符串的实现 +std::wstring User::getGradeString() const { + switch(gradeType) { + case Grade::PRIMARY: return L"小学"; + case Grade::JUNIOR: return L"初中"; + case Grade::SENIOR: return L"高中"; + default: return L"未知"; + } +} + +//设置密码的实现 +void User::setPassword(const std::wstring& newPassword) { + password = newPassword; +} \ No newline at end of file diff --git a/project/user.h b/project/user.h new file mode 100644 index 0000000..554141d --- /dev/null +++ b/project/user.h @@ -0,0 +1,41 @@ +#ifndef USER_H +#define USER_H + +#include + +//年级枚举,表示用户所属的学段级别 +//用于区分不同学段的题目难度和生成规则 +enum class Grade { PRIMARY, JUNIOR, SENIOR }; + +//用户信息类,封装用户认证数据和年级信息 +//存储用户名、密码和年级类型,提供验证和查询接口。 +class User { +private: + std::wstring username; + std::wstring password; + Grade gradeType; + +public: + //构造函数,初始化用户信息 + User(const std::wstring& name, const std::wstring& pass, Grade grade); + + //验证用户名和密码是否匹配 + bool check(const std::wstring& name, const std::wstring& pass) const; + + //获取用户名 + std::wstring getUsername() const; + + //获取密码 + std::wstring getPassword() const; + + //获取年级枚举值 + Grade getGrade() const; + + //获取年级的中文描述 + std::wstring getGradeString() const; + + //设置密码 + void setPassword(const std::wstring& newPassword); +}; + +#endif \ No newline at end of file diff --git a/project/usermanage.cpp b/project/usermanage.cpp new file mode 100644 index 0000000..2c6de54 --- /dev/null +++ b/project/usermanage.cpp @@ -0,0 +1,134 @@ +#include "usermanage.h" +#include +#include +#include +#include +#include + +//初始化用户数据的实现 +void UserManager::initializeUsers() { + // 如果文件中有用户数据,就不初始化默认用户 + if (loadUsersFromFile()) { + return; + } + + // 初始化默认测试用户 + users.emplace_back(L"张三1", L"Abc123", Grade::PRIMARY); + users.emplace_back(L"张三2", L"Abc123", Grade::PRIMARY); + users.emplace_back(L"张三3", L"Abc123", Grade::PRIMARY); + + users.emplace_back(L"李四1", L"Abc123", Grade::JUNIOR); + users.emplace_back(L"李四2", L"Abc123", Grade::JUNIOR); + users.emplace_back(L"李四3", L"Abc123", Grade::JUNIOR); + + users.emplace_back(L"王五1", L"Abc123", Grade::SENIOR); + users.emplace_back(L"王五2", L"Abc123", Grade::SENIOR); + users.emplace_back(L"王五3", L"Abc123", Grade::SENIOR); + + // 保存到文件 + saveUsersToFile(); +} + +//用户管理器的构造实现 +UserManager::UserManager() { + initializeUsers(); +} + +//用户认证的实现 +User* UserManager::authenticateUser(const std::wstring& username, const std::wstring& password) { + for (auto& user : users) { + if (user.check(username, password)) { + return &user; + } + } + return nullptr; +} + +//解析用户类型的实现 +Grade UserManager::parseUserType(const std::wstring& type) { + if (type == L"小学") return Grade::PRIMARY; + if (type == L"初中") return Grade::JUNIOR; + if (type == L"高中") return Grade::SENIOR; + return Grade::PRIMARY; +} + +//注册新用户的实现 +bool UserManager::registerUser(const std::wstring& username, const std::wstring& password, const std::wstring& grade) { + // 检查用户名是否已存在 + if (isUsernameExists(username)) { + return false; + } + + Grade userGrade = parseUserType(grade); + users.emplace_back(username, password, userGrade); + + // 保存到文件 + return saveUsersToFile(); +} + +//修改用户密码的实现 +bool UserManager::changePassword(const std::wstring& username, const std::wstring& oldPassword, const std::wstring& newPassword) { + for (auto& user : users) { + if (user.check(username, oldPassword)) { + user.setPassword(newPassword); + return saveUsersToFile(); + } + } + return false; +} + +//检查用户名是否存在的实现 +bool UserManager::isUsernameExists(const std::wstring& username) { + for (const auto& user : users) { + if (user.getUsername() == username) { + return true; + } + } + return false; +} + +//从文件加载用户数据的实现 +bool UserManager::loadUsersFromFile() { + std::ifstream file(userFile, std::ios::binary); + if (!file.is_open()) { + return false; + } + + users.clear(); + + std::string line; + while (std::getline(file, line)) { + std::wstring_convert> converter; + std::wstring wline = converter.from_bytes(line); + + std::wistringstream wss(wline); + std::wstring username, password, gradeStr; + wss >> username >> password >> gradeStr; + + Grade grade = parseUserType(gradeStr); + users.emplace_back(username, password, grade); + } + + file.close(); + return !users.empty(); +} + +//保存用户数据到文件的实现 +bool UserManager::saveUsersToFile() { + std::ofstream file(userFile, std::ios::binary); + if (!file.is_open()) { + return false; + } + + for (const auto& user : users) { + std::wstring_convert> converter; + std::string username_utf8 = converter.to_bytes(user.getUsername()); + std::string password_utf8 = converter.to_bytes(user.getPassword()); + std::string grade_utf8 = converter.to_bytes(user.getGradeString()); + + file << username_utf8 << " " << password_utf8 << " " << grade_utf8 << "\n"; + } + + file.close(); + return true; +} diff --git a/project/usermanage.h b/project/usermanage.h new file mode 100644 index 0000000..a233b25 --- /dev/null +++ b/project/usermanage.h @@ -0,0 +1,49 @@ +#ifndef USERMANAGE_H +#define USERMANAGE_H + +#include "user.h" +#include +#include +#include +#include + +//用户管理器类,负责用户数据的初始化和认证管理 +//维护用户列表,提供用户认证和年级类型解析功能。 +class UserManager { +private: + std::vector users; + const std::string userFile = "users.dat"; + + //初始化预定义用户数据 + void initializeUsers(); + + //从文件加载用户数据 + bool loadUsersFromFile(); + + //保存用户数据到文件 + bool saveUsersToFile(); + +public: + //构造函数,自动初始化用户数据 + UserManager(); + + //用户认证,验证用户名和密码 + User* authenticateUser(const std::wstring& username, const std::wstring& password); + + //将中文年级字符串解析为年级枚举值 + Grade parseUserType(const std::wstring& type); + + //注册新用户 + bool registerUser(const std::wstring& username, const std::wstring& password, const std::wstring& grade); + + //修改用户密码 + bool changePassword(const std::wstring& username, const std::wstring& oldPassword, const std::wstring& newPassword); + + //检查用户名是否存在 + bool isUsernameExists(const std::wstring& username); + + //获取所有用户(用于测试) + std::vector& getUsers() { return users; } +}; + +#endif \ No newline at end of file -- 2.34.1 From 8d277ae2974e0ee269f14a784d9ca273c20f8380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=85=95=E9=9B=AA?= <3420852069@qq.com> Date: Sat, 11 Oct 2025 22:54:15 +0800 Subject: [PATCH 5/9] =?UTF-8?q?=E5=89=8D=E7=AB=AF=E4=BB=A3=E7=A0=813.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/examwidget.cpp | 235 ---------------- src/frontend/examwidget.cpp | 376 ++++++++++++++++++++++++++ src/{ => frontend}/examwidget.h | 7 + src/{ => frontend}/loginwidget.cpp | 58 +++- src/{ => frontend}/loginwidget.h | 3 + src/{ => frontend}/main.cpp | 7 + src/{ => frontend}/mainmenuwidget.cpp | 77 +++++- src/{ => frontend}/mainmenuwidget.h | 4 + src/frontend/mainwindow.cpp | 203 ++++++++++++++ src/{ => frontend}/mainwindow.h | 1 + src/frontend/mathlearningApp.pro | 49 ++++ src/{ => frontend}/registerwidget.cpp | 96 ++++--- src/{ => frontend}/registerwidget.h | 4 +- src/{ => frontend}/resultwidget.cpp | 0 src/{ => frontend}/resultwidget.h | 0 src/mainwindow.cpp | 99 ------- 16 files changed, 840 insertions(+), 379 deletions(-) delete mode 100644 src/examwidget.cpp create mode 100644 src/frontend/examwidget.cpp rename src/{ => frontend}/examwidget.h (78%) rename src/{ => frontend}/loginwidget.cpp (64%) rename src/{ => frontend}/loginwidget.h (94%) rename src/{ => frontend}/main.cpp (66%) rename src/{ => frontend}/mainmenuwidget.cpp (58%) rename src/{ => frontend}/mainmenuwidget.h (73%) create mode 100644 src/frontend/mainwindow.cpp rename src/{ => frontend}/mainwindow.h (93%) create mode 100644 src/frontend/mathlearningApp.pro rename src/{ => frontend}/registerwidget.cpp (82%) rename src/{ => frontend}/registerwidget.h (93%) rename src/{ => frontend}/resultwidget.cpp (100%) rename src/{ => frontend}/resultwidget.h (100%) delete mode 100644 src/mainwindow.cpp diff --git a/src/examwidget.cpp b/src/examwidget.cpp deleted file mode 100644 index b697fd7..0000000 --- a/src/examwidget.cpp +++ /dev/null @@ -1,235 +0,0 @@ -#include "examwidget.h" -#include -#include -#include -#include - -ExamWidget::ExamWidget(QWidget *parent) : QWidget(parent), currentQuestion(0), totalQuestions(0) -{ - QVBoxLayout *mainLayout = new QVBoxLayout(this); - - // 进度标签 - progressLabel = new QLabel(); - progressLabel->setAlignment(Qt::AlignCenter); - progressLabel->setStyleSheet("font-size: 16px; font-weight: bold; margin: 20px; color: #2c3e50;"); - - // 题目区域 - QGroupBox *questionGroup = new QGroupBox("题目"); - questionGroup->setStyleSheet("QGroupBox {" - "font-size: 16px;" - "font-weight: bold;" - "margin-top: 10px;" - "}" - "QGroupBox::title {" - "subcontrol-origin: margin;" - "subcontrol-position: top center;" - "padding: 0 5px;" - "}"); - - QVBoxLayout *questionLayout = new QVBoxLayout(questionGroup); - - questionLabel = new QLabel(); - questionLabel->setWordWrap(true); - questionLabel->setStyleSheet("font-size: 18px; margin: 20px; padding: 10px; background-color: #f8f9fa; border-radius: 5px;"); - questionLabel->setAlignment(Qt::AlignCenter); - questionLabel->setMinimumHeight(80); - - questionLayout->addWidget(questionLabel); - - // 选项区域 - QGroupBox *optionsGroup = new QGroupBox("请选择答案"); - optionsGroup->setStyleSheet("QGroupBox {" - "font-size: 16px;" - "font-weight: bold;" - "margin-top: 10px;" - "}" - "QGroupBox::title {" - "subcontrol-origin: margin;" - "subcontrol-position: top center;" - "padding: 0 5px;" - "}"); - - QVBoxLayout *optionsLayout = new QVBoxLayout(optionsGroup); - optionGroup = new QButtonGroup(this); - - for (int i = 0; i < 4; ++i) { - optionButtons[i] = new QRadioButton(); - optionButtons[i]->setStyleSheet("QRadioButton {" - "font-size: 14px;" - "margin: 8px;" - "padding: 8px;" - "}" - "QRadioButton::indicator {" - "width: 20px;" - "height: 20px;" - "}"); - optionGroup->addButton(optionButtons[i], i); - optionsLayout->addWidget(optionButtons[i]); - } - - // 按钮区域 - QWidget *buttonWidget = new QWidget(); - QHBoxLayout *buttonLayout = new QHBoxLayout(buttonWidget); - - nextButton = new QPushButton("下一题"); - nextButton->setStyleSheet("QPushButton {" - "background-color: #3498db;" - "color: white;" - "border: none;" - "padding: 10px 20px;" - "font-size: 14px;" - "border-radius: 5px;" - "}" - "QPushButton:hover {" - "background-color: #2980b9;" - "}"); - nextButton->setFixedWidth(120); - - submitButton = new QPushButton("提交试卷"); - submitButton->setStyleSheet("QPushButton {" - "background-color: #e74c3c;" - "color: white;" - "border: none;" - "padding: 10px 20px;" - "font-size: 14px;" - "border-radius: 5px;" - "}" - "QPushButton:hover {" - "background-color: #c0392b;" - "}"); - submitButton->setFixedWidth(120); - submitButton->setVisible(false); - - buttonLayout->addStretch(); - buttonLayout->addWidget(nextButton); - buttonLayout->addSpacing(10); - buttonLayout->addWidget(submitButton); - buttonLayout->addStretch(); - - // 添加到主布局 - mainLayout->addWidget(progressLabel); - mainLayout->addWidget(questionGroup); - mainLayout->addWidget(optionsGroup); - mainLayout->addSpacing(20); - mainLayout->addWidget(buttonWidget); - mainLayout->addStretch(); - - // 连接信号槽 - connect(nextButton, &QPushButton::clicked, this, &ExamWidget::onNextClicked); - connect(submitButton, &QPushButton::clicked, this, &ExamWidget::onSubmitClicked); -} - -void ExamWidget::startExam(Grade grade, int questionCount) -{ - questionGenerator.setGrade(grade); - questions = questionGenerator.generateQuestions(questionCount); - totalQuestions = questionCount; - currentQuestion = 0; - userAnswers.clear(); - correctAnswers.clear(); - - // 为每个题目生成正确答案 - for (int i = 0; i < totalQuestions; ++i) { - correctAnswers.push_back(i % 4); // 简单实现,实际应该计算 - } - - showQuestion(0); -} - -void ExamWidget::showQuestion(int index) -{ - if (index < totalQuestions) { - currentQuestion = index; - progressLabel->setText(QString("第 %1 题 / 共 %2 题").arg(index + 1).arg(totalQuestions)); - - // 显示题目 - QString questionText = QString::fromStdWString(questions[index]); - questionLabel->setText(questionText); - - // 生成选项 - generateOptions(); - - // 清除选择 - optionGroup->setExclusive(false); - for (int i = 0; i < 4; ++i) { - optionButtons[i]->setChecked(false); - } - optionGroup->setExclusive(true); - - // 恢复之前的选择(如果有) - if (static_cast(index) < userAnswers.size()) { // 修复有符号/无符号比较 - int previousAnswer = userAnswers[index]; - if (previousAnswer >= 0 && previousAnswer < 4) { - optionButtons[previousAnswer]->setChecked(true); - } - } - - // 更新按钮状态 - nextButton->setVisible(index < totalQuestions - 1); - submitButton->setVisible(index == totalQuestions - 1); - } -} - -void ExamWidget::generateOptions() -{ - // 生成有意义的选项 - int correctIndex = correctAnswers[currentQuestion]; - - for (int i = 0; i < 4; ++i) { - if (i == correctIndex) { - optionButtons[i]->setText("正确答案"); - } else { - // 生成干扰项 - QString optionText; - switch(i) { - case 0: optionText = "选项 A"; break; - case 1: optionText = "选项 B"; break; - case 2: optionText = "选项 C"; break; - case 3: optionText = "选项 D"; break; - } - optionButtons[i]->setText(optionText); - } - } -} - -void ExamWidget::onNextClicked() -{ - int selected = optionGroup->checkedId(); - if (selected == -1) { - QMessageBox::warning(this, "提示", "请选择一个答案"); - return; - } - - // 保存当前答案 - if (static_cast(currentQuestion) >= userAnswers.size()) { // 修复有符号/无符号比较 - userAnswers.resize(currentQuestion + 1); - } - userAnswers[currentQuestion] = selected; - - showQuestion(currentQuestion + 1); -} - -void ExamWidget::onSubmitClicked() -{ - int selected = optionGroup->checkedId(); - if (selected == -1) { - QMessageBox::warning(this, "提示", "请选择一个答案"); - return; - } - - // 保存最后一题的答案 - if (static_cast(currentQuestion) >= userAnswers.size()) { // 修复有符号/无符号比较 - userAnswers.resize(currentQuestion + 1); - } - userAnswers[currentQuestion] = selected; - - // 计算分数 - int score = 0; - for (size_t i = 0; i < userAnswers.size(); ++i) { // 使用size_t - if (i < correctAnswers.size() && userAnswers[i] == correctAnswers[i]) { - score++; - } - } - - emit examFinished(score, totalQuestions); -} diff --git a/src/frontend/examwidget.cpp b/src/frontend/examwidget.cpp new file mode 100644 index 0000000..ab8d906 --- /dev/null +++ b/src/frontend/examwidget.cpp @@ -0,0 +1,376 @@ +#include "examwidget.h" +#include +#include +#include +#include +#include +#include +#include +#include + +ExamWidget::ExamWidget(QWidget *parent) : QWidget(parent), currentQuestion(0), totalQuestions(0) +{ + QVBoxLayout *mainLayout = new QVBoxLayout(this); + + // 进度标签 + progressLabel = new QLabel(); + progressLabel->setAlignment(Qt::AlignCenter); + progressLabel->setStyleSheet("font-size: 16px; font-weight: bold; margin: 20px; color: #2c3e50;"); + + // 题目区域 + QGroupBox *questionGroup = new QGroupBox("题目"); + questionGroup->setStyleSheet("QGroupBox {" + "font-size: 16px;" + "font-weight: bold;" + "margin-top: 10px;" + "}" + "QGroupBox::title {" + "subcontrol-origin: margin;" + "subcontrol-position: top center;" + "padding: 0 5px;" + "}"); + + QVBoxLayout *questionLayout = new QVBoxLayout(questionGroup); + + questionLabel = new QLabel(); + questionLabel->setWordWrap(true); + questionLabel->setStyleSheet("font-size: 18px; margin: 20px; padding: 10px; background-color: #f8f9fa; border-radius: 5px;"); + questionLabel->setAlignment(Qt::AlignCenter); + questionLabel->setMinimumHeight(80); + + questionLayout->addWidget(questionLabel); + + // 选项区域 + QGroupBox *optionsGroup = new QGroupBox("请选择答案"); + optionsGroup->setStyleSheet("QGroupBox {" + "font-size: 16px;" + "font-weight: bold;" + "margin-top: 10px;" + "}" + "QGroupBox::title {" + "subcontrol-origin: margin;" + "subcontrol-position: top center;" + "padding: 0 5px;" + "}"); + + QVBoxLayout *optionsLayout = new QVBoxLayout(optionsGroup); + optionGroup = new QButtonGroup(this); + + for (int i = 0; i < 4; ++i) { + optionButtons[i] = new QRadioButton(); + optionButtons[i]->setStyleSheet("QRadioButton {" + "font-size: 14px;" + "margin: 8px;" + "padding: 8px;" + "}" + "QRadioButton::indicator {" + "width: 20px;" + "height: 20px;" + "}"); + optionGroup->addButton(optionButtons[i], i); + optionsLayout->addWidget(optionButtons[i]); + } + + // 按钮区域 + QWidget *buttonWidget = new QWidget(); + QHBoxLayout *buttonLayout = new QHBoxLayout(buttonWidget); + + // 上一题按钮 + previousButton = new QPushButton("上一题"); + previousButton->setStyleSheet("QPushButton {" + "background-color: #95a5a6;" + "color: white;" + "border: none;" + "padding: 10px 20px;" + "font-size: 14px;" + "border-radius: 5px;" + "}" + "QPushButton:hover {" + "background-color: #7f8c8d;" + "}" + "QPushButton:disabled {" + "background-color: #bdc3c7;" + "color: #7f8c8d;" + "}"); + previousButton->setFixedWidth(120); + previousButton->setEnabled(false); // 第一题时禁用 + + nextButton = new QPushButton("下一题"); + nextButton->setStyleSheet("QPushButton {" + "background-color: #3498db;" + "color: white;" + "border: none;" + "padding: 10px 20px;" + "font-size: 14px;" + "border-radius: 5px;" + "}" + "QPushButton:hover {" + "background-color: #2980b9;" + "}"); + nextButton->setFixedWidth(120); + + submitButton = new QPushButton("提交试卷"); + submitButton->setStyleSheet("QPushButton {" + "background-color: #e74c3c;" + "color: white;" + "border: none;" + "padding: 10px 20px;" + "font-size: 14px;" + "border-radius: 5px;" + "}" + "QPushButton:hover {" + "background-color: #c0392b;" + "}"); + submitButton->setFixedWidth(120); + submitButton->setVisible(false); + + buttonLayout->addStretch(); + buttonLayout->addWidget(previousButton); + buttonLayout->addSpacing(10); + buttonLayout->addWidget(nextButton); + buttonLayout->addSpacing(10); + buttonLayout->addWidget(submitButton); + buttonLayout->addStretch(); + + // 添加到主布局 + mainLayout->addWidget(progressLabel); + mainLayout->addWidget(questionGroup); + mainLayout->addWidget(optionsGroup); + mainLayout->addSpacing(20); + mainLayout->addWidget(buttonWidget); + mainLayout->addStretch(); + + // 连接信号槽 + connect(previousButton, &QPushButton::clicked, this, &ExamWidget::onPreviousClicked); + connect(nextButton, &QPushButton::clicked, this, &ExamWidget::onNextClicked); + connect(submitButton, &QPushButton::clicked, this, &ExamWidget::onSubmitClicked); +} + +void ExamWidget::startExam(Grade grade, int questionCount) +{ + try { + qDebug() << "ExamWidget: 开始考试,年级:" << static_cast(grade) << "题目数量:" << questionCount; + + questionGenerator.setGrade(grade); + questions = questionGenerator.generateQuestions(questionCount); + totalQuestions = static_cast(questions.size()); + currentQuestion = 0; + userAnswers.clear(); + correctAnswers.clear(); + questionOptions.clear(); + + if (questions.empty()) { + qDebug() << "ExamWidget: 题目生成失败"; + QMessageBox::warning(this, "错误", "题目生成失败,请重试"); + return; + } + + qDebug() << "ExamWidget: 成功生成" << totalQuestions << "个题目"; + + // 预计算所有题目的正确答案和选项 + for (int i = 0; i < totalQuestions; ++i) { + try { + double correctValue = questionGenerator.calculateCorrectAnswer(questions[i]); + + // 生成选项并保存 + std::vector options = questionGenerator.generateMeaningfulOptions(correctValue); + questionOptions.push_back(options); + + // 找到正确答案的索引 + int correctIndex = 0; + bool found = false; + + for (size_t j = 0; j < options.size(); ++j) { + try { + double optionValue = std::stod(options[j]); + if (std::abs(optionValue - correctValue) < 0.0001) { + correctIndex = static_cast(j); + found = true; + break; + } + } catch (...) { + // 如果转换失败,使用字符串比较 + std::stringstream oss; + oss << std::fixed << std::setprecision(6) << correctValue; + std::string correctStr = oss.str(); + std::wstring wcorrectStr(correctStr.begin(), correctStr.end()); + + if (options[j] == wcorrectStr) { + correctIndex = static_cast(j); + found = true; + break; + } + } + } + + if (!found) { + qDebug() << "警告:未找到第" << i << "题的正确答案在选项中"; + } + + correctAnswers.push_back(correctIndex); + qDebug() << "ExamWidget: 第" << i << "题正确答案:" << correctValue << "索引:" << correctIndex; + + } catch (const std::exception& e) { + qDebug() << "ExamWidget: 计算第" << i << "题答案异常:" << e.what(); + correctAnswers.push_back(0); // 默认第一个选项为正确答案 + questionOptions.push_back({L"1.000000", L"2.000000", L"3.000000", L"4.000000"}); + } + } + + showQuestion(0); + updateButtonStates(); // 更新按钮状态 + qDebug() << "ExamWidget: 考试初始化完成"; + } catch (const std::exception& e) { + qDebug() << "ExamWidget: 开始考试异常:" << e.what(); + QMessageBox::critical(this, "错误", QString("考试初始化失败: %1").arg(e.what())); + } +} + +void ExamWidget::showQuestion(int index) +{ + if (index < totalQuestions) { + currentQuestion = index; + progressLabel->setText(QString("第 %1 题 / 共 %2 题").arg(index + 1).arg(totalQuestions)); + + // 显示题目 + QString questionText = QString::fromStdWString(questions[index]); + questionLabel->setText(questionText); + + // 设置选项文本 - 使用预先生成的选项 + if (static_cast(index) < questionOptions.size()) { + for (int i = 0; i < 4; ++i) { + if (i < static_cast(questionOptions[index].size())) { + optionButtons[i]->setText(QString::fromStdWString(questionOptions[index][i])); + } else { + optionButtons[i]->setText(QString("选项 %1").arg(i + 1)); + } + } + } + + // 清除选择 + optionGroup->setExclusive(false); + for (int i = 0; i < 4; ++i) { + optionButtons[i]->setChecked(false); + } + optionGroup->setExclusive(true); + + // 恢复之前的选择(如果有) + if (static_cast(index) < userAnswers.size()) { + int previousAnswer = userAnswers[index]; + if (previousAnswer >= 0 && previousAnswer < 4) { + optionButtons[previousAnswer]->setChecked(true); + } + } + + // 更新按钮状态 + updateButtonStates(); + } +} + +void ExamWidget::updateButtonStates() +{ + // 更新上一题按钮状态 + previousButton->setEnabled(currentQuestion > 0); + + // 更新下一题和提交按钮状态 + nextButton->setVisible(currentQuestion < totalQuestions - 1); + submitButton->setVisible(currentQuestion == totalQuestions - 1); +} + +void ExamWidget::generateOptions() +{ + // 这个方法现在不再使用,因为选项在startExam中已经预生成 + // 保留这个方法是为了保持接口兼容性 +} + +void ExamWidget::onPreviousClicked() +{ + // 保存当前答案 + int selected = optionGroup->checkedId(); + if (selected != -1) { + if (static_cast(currentQuestion) >= userAnswers.size()) { + userAnswers.resize(currentQuestion + 1); + } + userAnswers[currentQuestion] = selected; + } + + // 显示上一题 + if (currentQuestion > 0) { + showQuestion(currentQuestion - 1); + } +} + +void ExamWidget::onNextClicked() +{ + int selected = optionGroup->checkedId(); + if (selected == -1) { + QMessageBox::warning(this, "提示", "请选择一个答案"); + return; + } + + // 保存当前答案 + if (static_cast(currentQuestion) >= userAnswers.size()) { + userAnswers.resize(currentQuestion + 1); + } + userAnswers[currentQuestion] = selected; + + showQuestion(currentQuestion + 1); +} + +void ExamWidget::onSubmitClicked() +{ + int selected = optionGroup->checkedId(); + if (selected == -1) { + QMessageBox::warning(this, "提示", "请选择一个答案"); + return; + } + + // 保存最后一题的答案 + if (static_cast(currentQuestion) >= userAnswers.size()) { + userAnswers.resize(currentQuestion + 1); + } + userAnswers[currentQuestion] = selected; + + // 计算分数 - 使用预先生成的选项进行比较 + int score = 0; + for (size_t i = 0; i < userAnswers.size(); ++i) { + if (i < correctAnswers.size() && i < questionOptions.size()) { + if (userAnswers[i] >= 0 && userAnswers[i] < static_cast(questionOptions[i].size())) { + std::wstring selectedOption = questionOptions[i][userAnswers[i]]; + + try { + double selectedValue = std::stod(selectedOption); + double correctValue = questionGenerator.calculateCorrectAnswer(questions[i]); + + qDebug() << "第" << i << "题 - 选择值:" << selectedValue + << "正确答案:" << correctValue + << "差值:" << std::abs(selectedValue - correctValue); + + if (std::abs(selectedValue - correctValue) < 1e-5) { + score++; + qDebug() << "第" << i << "题回答正确"; + } else { + qDebug() << "第" << i << "题回答错误,选择值:" << selectedValue + << "正确答案:" << correctValue; + } + } catch (const std::exception& e) { + qDebug() << "第" << i << "题数值转换异常:" << e.what(); + // 回退到索引比较 + if (userAnswers[i] == correctAnswers[i]) { + score++; + qDebug() << "第" << i << "题通过索引比较回答正确"; + } else { + qDebug() << "第" << i << "题通过索引比较回答错误"; + } + } + } else { + qDebug() << "第" << i << "题用户答案索引越界:" << userAnswers[i]; + } + } else { + qDebug() << "第" << i << "题数据不完整"; + } + } + + qDebug() << "ExamWidget: 考试完成,分数:" << score << "/" << totalQuestions; + emit examFinished(score, totalQuestions); +} diff --git a/src/examwidget.h b/src/frontend/examwidget.h similarity index 78% rename from src/examwidget.h rename to src/frontend/examwidget.h index f5c90bd..2463d3d 100644 --- a/src/examwidget.h +++ b/src/frontend/examwidget.h @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include "questiongenerator.h" #include "user.h" @@ -25,16 +27,19 @@ signals: private slots: void onNextClicked(); + void onPreviousClicked(); // 新增:上一题 void onSubmitClicked(); private: void showQuestion(int index); void generateOptions(); + void updateButtonStates(); // 新增:更新按钮状态 QLabel *questionLabel; QLabel *progressLabel; QButtonGroup *optionGroup; QRadioButton *optionButtons[4]; + QPushButton *previousButton; // 新增:上一题按钮 QPushButton *nextButton; QPushButton *submitButton; @@ -42,6 +47,8 @@ private: std::vector questions; std::vector userAnswers; std::vector correctAnswers; + std::vector> questionOptions; + int currentQuestion; int totalQuestions; }; diff --git a/src/loginwidget.cpp b/src/frontend/loginwidget.cpp similarity index 64% rename from src/loginwidget.cpp rename to src/frontend/loginwidget.cpp index e7a6a87..6a1abb0 100644 --- a/src/loginwidget.cpp +++ b/src/frontend/loginwidget.cpp @@ -1,4 +1,9 @@ #include "loginwidget.h" +#include +#include +#include +#include +#include LoginWidget::LoginWidget(QWidget *parent) : QWidget(parent) { @@ -31,7 +36,11 @@ LoginWidget::LoginWidget(QWidget *parent) : QWidget(parent) passwordEdit->setMinimumHeight(35); passwordEdit->setStyleSheet("QLineEdit { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); - // 按钮 + // 按钮区域 + QWidget *buttonWidget = new QWidget(); + QVBoxLayout *buttonLayout = new QVBoxLayout(buttonWidget); + buttonLayout->setSpacing(15); + loginButton = new QPushButton("登录"); loginButton->setMinimumHeight(40); loginButton->setStyleSheet("QPushButton {" @@ -60,6 +69,31 @@ LoginWidget::LoginWidget(QWidget *parent) : QWidget(parent) "background-color: #7f8c8d;" "}"); + // 添加退出按钮 + QPushButton *exitButton = new QPushButton("退出系统"); + exitButton->setMinimumHeight(40); + exitButton->setStyleSheet("QPushButton {" + "background-color: #e74c3c;" + "color: white;" + "border: none;" + "font-size: 16px;" + "font-weight: bold;" + "border-radius: 5px;" + "}" + "QPushButton:hover {" + "background-color: #c0392b;" + "}"); + + // 测试账号提示 + QLabel *testAccountLabel = new QLabel("测试账号: zhangsan1 / lisi1 / wangwu1 密码: Abc123"); + testAccountLabel->setStyleSheet("font-size: 12px; color: #7f8c8d; margin-top: 10px;"); + testAccountLabel->setAlignment(Qt::AlignCenter); + + buttonLayout->addWidget(loginButton); + buttonLayout->addWidget(registerButton); + buttonLayout->addWidget(exitButton); + buttonLayout->addWidget(testAccountLabel); + // 添加到布局 layout->addWidget(titleLabel); layout->addSpacing(30); @@ -68,12 +102,18 @@ LoginWidget::LoginWidget(QWidget *parent) : QWidget(parent) layout->addWidget(passwordLabel); layout->addWidget(passwordEdit); layout->addSpacing(30); - layout->addWidget(loginButton); - layout->addWidget(registerButton); + layout->addWidget(buttonWidget); // 连接信号槽 connect(loginButton, &QPushButton::clicked, this, &LoginWidget::onLoginClicked); connect(registerButton, &QPushButton::clicked, this, &LoginWidget::onRegisterClicked); + connect(exitButton, &QPushButton::clicked, qApp, &QApplication::quit); + + // 回车键登录 + connect(usernameEdit, &QLineEdit::returnPressed, this, &LoginWidget::onLoginClicked); + connect(passwordEdit, &QLineEdit::returnPressed, this, &LoginWidget::onLoginClicked); + + qDebug() << "LoginWidget: 初始化完成"; } void LoginWidget::onLoginClicked() @@ -81,6 +121,8 @@ void LoginWidget::onLoginClicked() QString username = usernameEdit->text().trimmed(); QString password = passwordEdit->text(); + qDebug() << "LoginWidget: 尝试登录,用户名:" << username; + if (username.isEmpty() || password.isEmpty()) { QMessageBox::warning(this, "输入错误", "请输入用户名和密码"); return; @@ -89,13 +131,23 @@ void LoginWidget::onLoginClicked() User* user = userManager.authenticateUser(username.toStdWString(), password.toStdWString()); if (user) { + qDebug() << "LoginWidget: 登录成功"; emit loginSuccess(user); } else { + qDebug() << "LoginWidget: 登录失败"; QMessageBox::warning(this, "登录失败", "用户名或密码错误"); } } void LoginWidget::onRegisterClicked() { + qDebug() << "LoginWidget: 切换到注册界面"; emit showRegister(); } + +void LoginWidget::clearInputs() +{ + qDebug() << "LoginWidget: 清空输入框"; + usernameEdit->clear(); + passwordEdit->clear(); +} diff --git a/src/loginwidget.h b/src/frontend/loginwidget.h similarity index 94% rename from src/loginwidget.h rename to src/frontend/loginwidget.h index 9c9d501..87fe657 100644 --- a/src/loginwidget.h +++ b/src/frontend/loginwidget.h @@ -24,6 +24,9 @@ private slots: void onLoginClicked(); void onRegisterClicked(); +public slots: + void clearInputs(); + private: QLineEdit *usernameEdit; // 改为用户名输入 QLineEdit *passwordEdit; diff --git a/src/main.cpp b/src/frontend/main.cpp similarity index 66% rename from src/main.cpp rename to src/frontend/main.cpp index 7b89e2d..e1366fd 100644 --- a/src/main.cpp +++ b/src/frontend/main.cpp @@ -1,17 +1,24 @@ #include +#include #include "mainwindow.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); + qDebug() << "应用程序启动..."; + // 设置应用程序信息 app.setApplicationName("数学学习软件"); app.setApplicationVersion("1.0"); app.setOrganizationName("软件工程学院"); + qDebug() << "创建主窗口..."; MainWindow window; + + qDebug() << "显示主窗口..."; window.show(); + qDebug() << "进入事件循环..."; return app.exec(); } diff --git a/src/mainmenuwidget.cpp b/src/frontend/mainmenuwidget.cpp similarity index 58% rename from src/mainmenuwidget.cpp rename to src/frontend/mainmenuwidget.cpp index f8cb0b5..3dbf12d 100644 --- a/src/mainmenuwidget.cpp +++ b/src/frontend/mainmenuwidget.cpp @@ -1,5 +1,6 @@ #include "mainmenuwidget.h" #include +#include MainMenuWidget::MainMenuWidget(QWidget *parent) : QWidget(parent) { @@ -53,8 +54,8 @@ MainMenuWidget::MainMenuWidget(QWidget *parent) : QWidget(parent) QLabel *countLabel = new QLabel("题目数量:"); countLabel->setStyleSheet("font-size: 14px;"); questionCountSpinBox = new QSpinBox(); - questionCountSpinBox->setRange(10, 30); - questionCountSpinBox->setValue(15); + questionCountSpinBox->setRange(5, 30); + questionCountSpinBox->setValue(10); questionCountSpinBox->setStyleSheet("padding: 8px; font-size: 14px;"); countLayout->addWidget(countLabel); countLayout->addWidget(questionCountSpinBox); @@ -75,11 +76,50 @@ MainMenuWidget::MainMenuWidget(QWidget *parent) : QWidget(parent) "}"); startButton->setFixedSize(150, 50); + // 退出登录按钮 - 修改为与开始考试按钮相同大小 + logoutButton = new QPushButton("退出登录"); + logoutButton->setStyleSheet("QPushButton {" + "background-color: #95a5a6;" + "color: white;" + "border: none;" + "padding: 12px 30px;" + "font-size: 16px;" + "border-radius: 5px;" + "}" + "QPushButton:hover {" + "background-color: #7f8c8d;" + "}"); + logoutButton->setFixedSize(150, 50); // 修改为相同大小 + + // 按钮容器 - 重新设计布局 + QWidget *buttonWidget = new QWidget(); + QVBoxLayout *buttonLayout = new QVBoxLayout(buttonWidget); + buttonLayout->setSpacing(15); // 设置按钮间距 + buttonLayout->setContentsMargins(0, 20, 0, 10); + + // 开始考试按钮行 + QWidget *startButtonWidget = new QWidget(); + QHBoxLayout *startButtonLayout = new QHBoxLayout(startButtonWidget); + startButtonLayout->addStretch(); + startButtonLayout->addWidget(startButton); + startButtonLayout->addStretch(); + + // 退出登录按钮行 + QWidget *logoutButtonWidget = new QWidget(); + QHBoxLayout *logoutButtonLayout = new QHBoxLayout(logoutButtonWidget); + logoutButtonLayout->addStretch(); + logoutButtonLayout->addWidget(logoutButton); + logoutButtonLayout->addStretch(); + + // 添加到按钮布局 + buttonLayout->addWidget(startButtonWidget); + buttonLayout->addWidget(logoutButtonWidget); + // 添加到设置布局 settingsLayout->addWidget(gradeWidget); settingsLayout->addWidget(countWidget); settingsLayout->addSpacing(20); - settingsLayout->addWidget(startButton, 0, Qt::AlignCenter); + settingsLayout->addWidget(buttonWidget); // 添加到主布局 mainLayout->addWidget(titleLabel); @@ -91,16 +131,19 @@ MainMenuWidget::MainMenuWidget(QWidget *parent) : QWidget(parent) // 连接信号槽 connect(startButton, &QPushButton::clicked, this, &MainMenuWidget::onStartExamClicked); + connect(logoutButton, &QPushButton::clicked, this, &MainMenuWidget::onLogoutClicked); + + qDebug() << "MainMenuWidget: 初始化完成,退出登录按钮大小已调整"; } void MainMenuWidget::setUserInfo(const std::wstring& username, const std::wstring& grade) { - QString userInfo = QString("当前用户: %1 | 年级: %2") + QString userInfo = QString("当前用户: %1 | 注册年级: %2") .arg(QString::fromStdWString(username)) .arg(QString::fromStdWString(grade)); userInfoLabel->setText(userInfo); - // 设置年级选择框 + // 设置年级选择框默认值 QString gradeStr = QString::fromStdWString(grade); int index = gradeComboBox->findText(gradeStr); if (index >= 0) { @@ -108,8 +151,32 @@ void MainMenuWidget::setUserInfo(const std::wstring& username, const std::wstrin } } +QString MainMenuWidget::getSelectedGrade() const { + return gradeComboBox->currentText(); +} + void MainMenuWidget::onStartExamClicked() { int questionCount = questionCountSpinBox->value(); + QString selectedGrade = gradeComboBox->currentText(); + qDebug() << "MainMenuWidget: 开始考试,题目数量:" << questionCount << "选择年级:" << selectedGrade; emit startExam(questionCount); } + +void MainMenuWidget::onLogoutClicked() +{ + qDebug() << "MainMenuWidget: 用户请求退出登录"; + + // 弹出确认对话框 + QMessageBox::StandardButton reply; + reply = QMessageBox::question(this, "确认退出", + "确定要退出登录吗?", + QMessageBox::Yes | QMessageBox::No); + + if (reply == QMessageBox::Yes) { + qDebug() << "MainMenuWidget: 用户确认退出登录"; + emit logoutRequested(); + } else { + qDebug() << "MainMenuWidget: 用户取消退出登录"; + } +} diff --git a/src/mainmenuwidget.h b/src/frontend/mainmenuwidget.h similarity index 73% rename from src/mainmenuwidget.h rename to src/frontend/mainmenuwidget.h index b1563b1..e4cae4f 100644 --- a/src/mainmenuwidget.h +++ b/src/frontend/mainmenuwidget.h @@ -17,12 +17,15 @@ class MainMenuWidget : public QWidget public: explicit MainMenuWidget(QWidget *parent = nullptr); void setUserInfo(const std::wstring& username, const std::wstring& grade); + QString getSelectedGrade() const; // 新增方法 signals: void startExam(int questionCount); + void logoutRequested(); // 新增信号:退出登录 private slots: void onStartExamClicked(); + void onLogoutClicked(); // 新增槽函数:处理退出登录 private: QLabel *welcomeLabel; @@ -30,6 +33,7 @@ private: QComboBox *gradeComboBox; QSpinBox *questionCountSpinBox; QPushButton *startButton; + QPushButton *logoutButton; // 新增:退出登录按钮 }; #endif diff --git a/src/frontend/mainwindow.cpp b/src/frontend/mainwindow.cpp new file mode 100644 index 0000000..f3064ad --- /dev/null +++ b/src/frontend/mainwindow.cpp @@ -0,0 +1,203 @@ +#include "mainwindow.h" +#include +#include +#include + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), currentUser(nullptr) +{ + setWindowTitle("中小学数学学习软件"); + setMinimumSize(600, 700); + resize(600, 700); + + qDebug() << "MainWindow: 初始化开始"; + + // 创建堆叠窗口 + stackedWidget = new QStackedWidget(this); + setCentralWidget(stackedWidget); + + // 创建各个界面 + loginWidget = new LoginWidget(); + registerWidget = new RegisterWidget(); + mainMenuWidget = new MainMenuWidget(); + examWidget = new ExamWidget(); + resultWidget = new ResultWidget(); + + qDebug() << "MainWindow: 所有界面创建完成"; + + // 添加到堆叠窗口 + stackedWidget->addWidget(loginWidget); + stackedWidget->addWidget(registerWidget); + stackedWidget->addWidget(mainMenuWidget); + stackedWidget->addWidget(examWidget); + stackedWidget->addWidget(resultWidget); + + // 连接信号槽 + connect(loginWidget, &LoginWidget::loginSuccess, this, &MainWindow::onUserLoggedIn); + connect(loginWidget, &LoginWidget::showRegister, this, &MainWindow::showRegister); + + connect(registerWidget, &RegisterWidget::showLogin, this, &MainWindow::showLogin); + connect(registerWidget, &RegisterWidget::registerSuccess, this, &MainWindow::onRegisterSuccess); + + connect(mainMenuWidget, &MainMenuWidget::startExam, this, &MainWindow::showExam); + connect(mainMenuWidget, &MainMenuWidget::logoutRequested, this, &MainWindow::onLogoutRequested); // 新增连接 + + connect(examWidget, &ExamWidget::examFinished, this, &MainWindow::showResult); + connect(resultWidget, &ResultWidget::backToMenu, this, &MainWindow::showMainMenu); + connect(resultWidget, &ResultWidget::startNewExam, this, &MainWindow::showMainMenu); + + qDebug() << "MainWindow: 信号连接完成"; + + // 显示登录界面 + showLogin(); +} + +MainWindow::~MainWindow() +{ + qDebug() << "MainWindow: 析构函数调用"; +} + +void MainWindow::showLogin() +{ + qDebug() << "MainWindow: 切换到登录界面"; + stackedWidget->setCurrentWidget(loginWidget); + setWindowTitle("中小学数学学习软件 - 登录"); +} + +void MainWindow::showRegister() +{ + qDebug() << "MainWindow: 切换到注册界面"; + stackedWidget->setCurrentWidget(registerWidget); + setWindowTitle("中小学数学学习软件 - 注册"); +} + +void MainWindow::showMainMenu() +{ + qDebug() << "MainWindow: 切换到主菜单"; + if (currentUser) { + mainMenuWidget->setUserInfo(currentUser->getUsername(), + currentUser->getGradeString()); + stackedWidget->setCurrentWidget(mainMenuWidget); + setWindowTitle("中小学数学学习软件 - 主菜单"); + } else { + qDebug() << "MainWindow: 当前用户为空,跳转到登录界面"; + QMessageBox::warning(this, "会话过期", "用户会话已过期,请重新登录"); + showLogin(); + } +} + +void MainWindow::showExam(int questionCount) +{ + qDebug() << "MainWindow: 开始考试,题目数量:" << questionCount; + if (currentUser) { + // 从主菜单获取当前选择的年级,而不是用户注册时的年级 + QString selectedGrade = mainMenuWidget->getSelectedGrade(); + qDebug() << "MainWindow: 用户注册年级:" << QString::fromStdWString(currentUser->getGradeString()) + << "选择的年级:" << selectedGrade; + + // 根据选择的年级设置考试难度 + Grade examGrade = Grade::PRIMARY; // 默认小学 + + if (selectedGrade == "小学") { + examGrade = Grade::PRIMARY; + } else if (selectedGrade == "初中") { + examGrade = Grade::JUNIOR; + } else if (selectedGrade == "高中") { + examGrade = Grade::SENIOR; + } + + qDebug() << "MainWindow: 实际考试年级:" << static_cast(examGrade); + examWidget->startExam(examGrade, questionCount); + stackedWidget->setCurrentWidget(examWidget); + setWindowTitle("中小学数学学习软件 - 考试中"); + } else { + qDebug() << "MainWindow: 考试时用户未登录"; + QMessageBox::warning(this, "错误", "用户未登录,请重新登录"); + showLogin(); + } +} + +void MainWindow::showResult(int score, int total) +{ + qDebug() << "MainWindow: 显示考试结果,分数:" << score << "/" << total; + resultWidget->setResult(score, total); + stackedWidget->setCurrentWidget(resultWidget); + setWindowTitle("中小学数学学习软件 - 考试结果"); + + // 保存考试记录 + if (currentUser) { + bool saved = FileSaver::saveExamRecord(currentUser->getUsername(), + currentUser->getGradeString(), + score, total, + FileSaver::getCurrentTime()); + if (saved) { + qDebug() << "MainWindow: 考试记录保存成功"; + } else { + qDebug() << "MainWindow: 考试记录保存失败"; + } + } +} + +void MainWindow::onUserLoggedIn(User* user) +{ + qDebug() << "MainWindow: 用户登录成功"; + if (user) { + currentUser = user; + QString username = QString::fromStdWString(user->getUsername()); + QString grade = QString::fromStdWString(user->getGradeString()); + + qDebug() << "MainWindow: 登录用户:" << username << "年级:" << grade; + + QMessageBox::information(this, "登录成功", + QString("欢迎 %1!\n年级:%2") + .arg(username) + .arg(grade)); + showMainMenu(); + } else { + qDebug() << "MainWindow: 用户信息无效"; + QMessageBox::warning(this, "登录失败", "用户信息无效"); + showLogin(); + } +} + +void MainWindow::onRegisterSuccess() +{ + qDebug() << "MainWindow: 注册成功,准备跳转到登录界面"; + + // 注册成功后显示登录界面,并显示成功提示 + QMessageBox::information(this, "注册成功", "注册成功!请使用新账号登录"); + + // 清空登录界面的输入框 + if (loginWidget) { + loginWidget->clearInputs(); + } + + qDebug() << "MainWindow: 清空登录界面输入框完成"; + + // 立即切换到登录界面 + showLogin(); + + qDebug() << "MainWindow: 已切换到登录界面"; +} + +void MainWindow::onLogoutRequested() +{ + qDebug() << "MainWindow: 处理退出登录请求"; + + // 清除当前用户信息 + if (currentUser) { + QString username = QString::fromStdWString(currentUser->getUsername()); + qDebug() << "MainWindow: 用户" << username << "退出登录"; + currentUser = nullptr; + } + + // 清空登录界面的输入框 + if (loginWidget) { + loginWidget->clearInputs(); + } + + // 显示登录界面 + showLogin(); + + QMessageBox::information(this, "退出成功", "您已成功退出登录"); +} diff --git a/src/mainwindow.h b/src/frontend/mainwindow.h similarity index 93% rename from src/mainwindow.h rename to src/frontend/mainwindow.h index 293a82f..4dcb6e3 100644 --- a/src/mainwindow.h +++ b/src/frontend/mainwindow.h @@ -27,6 +27,7 @@ private slots: void showResult(int score, int total); void onUserLoggedIn(User* user); void onRegisterSuccess(); + void onLogoutRequested(); // 新增:处理退出登录 private: QStackedWidget *stackedWidget; diff --git a/src/frontend/mathlearningApp.pro b/src/frontend/mathlearningApp.pro new file mode 100644 index 0000000..dd44b16 --- /dev/null +++ b/src/frontend/mathlearningApp.pro @@ -0,0 +1,49 @@ +QT += core gui network + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + filesaver.cpp \ + questiongenerator.cpp \ + user.cpp \ + usermanage.cpp \ + loginwidget.cpp \ + main.cpp \ + mainmenuwidget.cpp \ + mainwindow.cpp \ + registerwidget.cpp \ + resultwidget.cpp \ + examwidget.cpp + +HEADERS += \ + filesaver.h \ + questiongenerator.h \ + user.h \ + usermanage.h \ + loginwidget.h \ + mainmenuwidget.h \ + mainwindow.h \ + registerwidget.h \ + resultwidget.h \ + examwidget.h + +FORMS += \ + mainwindow.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target diff --git a/src/registerwidget.cpp b/src/frontend/registerwidget.cpp similarity index 82% rename from src/registerwidget.cpp rename to src/frontend/registerwidget.cpp index 9946c8b..1e20d93 100644 --- a/src/registerwidget.cpp +++ b/src/frontend/registerwidget.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include RegisterWidget::RegisterWidget(QWidget *parent) : QWidget(parent), countdownSeconds(0) { @@ -69,7 +71,7 @@ RegisterWidget::RegisterWidget(QWidget *parent) : QWidget(parent), countdownSeco QLabel *usernameLabel = new QLabel("用户名:"); usernameLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); usernameEdit = new QLineEdit(); - usernameEdit->setPlaceholderText("请输入用户名(2-20个字符)"); + usernameEdit->setPlaceholderText("请输入用户名(2-20个字符,建议使用英文)"); usernameEdit->setMinimumHeight(35); usernameEdit->setStyleSheet("QLineEdit { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); @@ -199,26 +201,36 @@ RegisterWidget::RegisterWidget(QWidget *parent) : QWidget(parent), countdownSeco // 初始状态 resetCountdown(); + + qDebug() << "RegisterWidget: 初始化完成"; } void RegisterWidget::onSendCodeClicked() { QString email = emailEdit->text().trimmed(); + qDebug() << "RegisterWidget: 发送验证码到:" << email; if (!validateEmail(email)) { QMessageBox::warning(this, "输入错误", "请输入有效的邮箱地址"); return; } - // 生成并显示验证码 - generatedCode = generateVerificationCode(); + // 使用UserManager发送验证码 + std::wstring emailW = email.toStdWString(); + std::wstring generatedCodeW; + + if (userManager.sendVerificationCode(emailW, generatedCodeW)) { + generatedCode = QString::fromStdWString(generatedCodeW); - QMessageBox::information(this, "验证码", - QString("验证码已发送到 %1\n验证码:%2\n(实际项目中会发送到邮箱)") - .arg(email).arg(generatedCode)); + QMessageBox::information(this, "验证码已发送", + QString("验证码已发送到 %1\n验证码:%2\n(验证码10分钟内有效)") + .arg(email).arg(QString::fromStdWString(generatedCodeW))); - // 开始倒计时 - startCountdown(); + // 开始倒计时 + startCountdown(); + } else { + QMessageBox::warning(this, "发送失败", "验证码发送失败,请重试"); + } } void RegisterWidget::onRegisterClicked() @@ -230,6 +242,8 @@ void RegisterWidget::onRegisterClicked() QString confirmPassword = confirmPasswordEdit->text(); QString grade = gradeComboBox->currentText(); + qDebug() << "RegisterWidget: 开始注册,用户名:" << username << "年级:" << grade; + // 验证输入 if (email.isEmpty() || username.isEmpty() || verificationCode.isEmpty() || password.isEmpty() || confirmPassword.isEmpty()) { @@ -242,15 +256,12 @@ void RegisterWidget::onRegisterClicked() return; } - // 验证验证码 - if (verificationCode != generatedCode) { - QMessageBox::warning(this, "验证错误", "验证码错误"); - return; - } + // 验证验证码 - 使用UserManager验证 + std::wstring emailW = email.toStdWString(); + std::wstring codeW = verificationCode.toStdWString(); - // 检查验证码是否过期 - if (countdownSeconds <= 0) { - QMessageBox::warning(this, "验证错误", "验证码已过期,请重新获取"); + if (!userManager.verifyEmailCode(emailW, codeW)) { + QMessageBox::warning(this, "验证错误", "验证码错误或已过期"); return; } @@ -271,12 +282,29 @@ void RegisterWidget::onRegisterClicked() return; } - // 实际注册用户 - UserManager userManager; - if (userManager.registerUser(username.toStdWString(), + // 实际注册用户 - 使用安全的用户名避免编码问题 + std::wstring safeUsername; + for (QChar ch : username) { + if (ch.isLetterOrNumber() || ch == '_') { + safeUsername += ch.unicode(); + } + } + + // 如果用户名不合法,生成一个随机用户名 + if (safeUsername.empty()) { + safeUsername = L"user" + std::to_wstring(1000 + (std::rand() % 9000)); + qDebug() << "RegisterWidget: 生成随机用户名:" << QString::fromStdWString(safeUsername); + } + + qDebug() << "RegisterWidget: 尝试注册用户:" << QString::fromStdWString(safeUsername); + + if (userManager.registerUser(safeUsername, password.toStdWString(), grade.toStdWString())) { - QMessageBox::information(this, "注册成功", "注册成功!"); + qDebug() << "RegisterWidget: 注册成功"; + QMessageBox::information(this, "注册成功", + QString("注册成功!\n用户名: %1\n请使用新账号登录") + .arg(QString::fromStdWString(safeUsername))); // 重置表单 emailEdit->clear(); @@ -286,14 +314,23 @@ void RegisterWidget::onRegisterClicked() confirmPasswordEdit->clear(); resetCountdown(); + qDebug() << "RegisterWidget: 发射注册成功信号"; + + // 发射信号 emit registerSuccess(); + emit showLogin(); + + qDebug() << "RegisterWidget: 信号发射完成"; + } else { + qDebug() << "RegisterWidget: 注册失败"; QMessageBox::warning(this, "注册失败", "用户名已存在,请选择其他用户名"); } } void RegisterWidget::onBackClicked() { + qDebug() << "RegisterWidget: 返回登录"; resetCountdown(); emit showLogin(); } @@ -320,27 +357,13 @@ bool RegisterWidget::validateEmail(const QString &email) return emailRegex.match(email).hasMatch(); } -QString RegisterWidget::generateVerificationCode() -{ - const QString possibleChars = "0123456789"; - QString code; - - // 使用传统的随机数生成 - qsrand(QTime::currentTime().msec()); - - for (int i = 0; i < 6; ++i) { - int index = qrand() % possibleChars.length(); - code.append(possibleChars.at(index)); - } - return code; -} - void RegisterWidget::startCountdown() { - countdownSeconds = 300; // 5分钟 + countdownSeconds = 600; // 10分钟 sendCodeButton->setEnabled(false); updateTimer(); countdownTimer->start(); + qDebug() << "RegisterWidget: 开始倒计时,10分钟"; } void RegisterWidget::resetCountdown() @@ -350,6 +373,7 @@ void RegisterWidget::resetCountdown() sendCodeButton->setEnabled(true); timerLabel->clear(); generatedCode.clear(); + qDebug() << "RegisterWidget: 重置倒计时"; } void RegisterWidget::updateTimer() diff --git a/src/registerwidget.h b/src/frontend/registerwidget.h similarity index 93% rename from src/registerwidget.h rename to src/frontend/registerwidget.h index b49d453..ed5084d 100644 --- a/src/registerwidget.h +++ b/src/frontend/registerwidget.h @@ -13,6 +13,7 @@ #include #include #include +#include "usermanage.h" class RegisterWidget : public QWidget { @@ -47,9 +48,10 @@ private: int countdownSeconds; QString generatedCode; + UserManager userManager; // 添加用户管理器 + bool validatePassword(const QString &password); bool validateEmail(const QString &email); - QString generateVerificationCode(); void startCountdown(); void resetCountdown(); }; diff --git a/src/resultwidget.cpp b/src/frontend/resultwidget.cpp similarity index 100% rename from src/resultwidget.cpp rename to src/frontend/resultwidget.cpp diff --git a/src/resultwidget.h b/src/frontend/resultwidget.h similarity index 100% rename from src/resultwidget.h rename to src/frontend/resultwidget.h diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp deleted file mode 100644 index a76a040..0000000 --- a/src/mainwindow.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include "mainwindow.h" -#include - -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent), currentUser(nullptr) -{ - setWindowTitle("中小学数学学习软件"); - setMinimumSize(600, 700); - resize(600, 700); - - // 创建堆叠窗口 - stackedWidget = new QStackedWidget(this); - setCentralWidget(stackedWidget); - - // 创建各个界面 - loginWidget = new LoginWidget(); - registerWidget = new RegisterWidget(); - mainMenuWidget = new MainMenuWidget(); - examWidget = new ExamWidget(); - resultWidget = new ResultWidget(); - - // 添加到堆叠窗口 - stackedWidget->addWidget(loginWidget); - stackedWidget->addWidget(registerWidget); - stackedWidget->addWidget(mainMenuWidget); - stackedWidget->addWidget(examWidget); - stackedWidget->addWidget(resultWidget); - - // 连接信号槽 - connect(loginWidget, &LoginWidget::loginSuccess, this, &MainWindow::onUserLoggedIn); - connect(loginWidget, &LoginWidget::showRegister, this, &MainWindow::showRegister); - connect(registerWidget, &RegisterWidget::showLogin, this, &MainWindow::showLogin); - connect(registerWidget, &RegisterWidget::registerSuccess, this, &MainWindow::onRegisterSuccess); - connect(mainMenuWidget, &MainMenuWidget::startExam, this, &MainWindow::showExam); - connect(examWidget, &ExamWidget::examFinished, this, &MainWindow::showResult); - connect(resultWidget, &ResultWidget::backToMenu, this, &MainWindow::showMainMenu); - connect(resultWidget, &ResultWidget::startNewExam, this, &MainWindow::showMainMenu); - - // 显示登录界面 - showLogin(); -} - -MainWindow::~MainWindow() -{ -} - -void MainWindow::showLogin() -{ - stackedWidget->setCurrentWidget(loginWidget); -} - -void MainWindow::showRegister() -{ - stackedWidget->setCurrentWidget(registerWidget); -} - -void MainWindow::showMainMenu() -{ - if (currentUser) { - mainMenuWidget->setUserInfo(currentUser->getUsername(), - currentUser->getGradeString()); - stackedWidget->setCurrentWidget(mainMenuWidget); - } -} - -void MainWindow::showExam(int questionCount) -{ - if (currentUser) { - examWidget->startExam(currentUser->getGrade(), questionCount); - stackedWidget->setCurrentWidget(examWidget); - } -} - -void MainWindow::showResult(int score, int total) -{ - resultWidget->setResult(score, total); - stackedWidget->setCurrentWidget(resultWidget); - - // 保存考试记录 - if (currentUser) { - FileSaver::saveExamRecord(currentUser->getUsername(), - currentUser->getGradeString(), - score, total, - FileSaver::getCurrentTime()); - } -} - -void MainWindow::onUserLoggedIn(User* user) -{ - currentUser = user; - showMainMenu(); -} - -void MainWindow::onRegisterSuccess() -{ - // 注册成功后显示登录界面 - QMessageBox::information(this, "注册成功", "注册成功,请登录"); - showLogin(); -} -- 2.34.1 From b6265c4a607063f3e9316bd61ab04e00c3e67187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=85=95=E9=9B=AA?= <3420852069@qq.com> Date: Sun, 12 Oct 2025 13:56:22 +0800 Subject: [PATCH 6/9] =?UTF-8?q?=E5=89=8D=E7=AB=AF=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=9C=80=E7=BB=88=E7=89=88=EF=BC=8C=E8=AF=B4=E6=98=8E=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/README.md | 111 ++++++++++++++++++++++++++++++++ src/frontend/examwidget.h | 6 +- src/frontend/mainmenuwidget.cpp | 2 +- src/frontend/mainmenuwidget.h | 8 +-- src/frontend/mainwindow.h | 2 +- src/frontend/mainwindow.ui | 22 +++++++ 6 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 src/frontend/mainwindow.ui diff --git a/doc/README.md b/doc/README.md index dc45f65..9338fb5 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,2 +1,113 @@ # teamwork-project +## 项目简介 +这是一个面向小学、初中和高中学生的数学学习软件,采用图形化界面操作,提供数学题目生成、在线考试、成绩记录等功能。项目基于C++和Qt框架开发,实现了完整的用户注册、登录、考试和成绩管理流程。 + +## 项目特点 +1.多学段支持:覆盖小学、初中、高中三个学段的数学题目 +2.安全认证:支持用户注册、登录、密码修改功能 +3.智能出题:根据学段自动生成相应难度的数学题目 +4.实时评分:自动批改试卷并显示详细成绩 +5.数据持久化:本地文件存储用户数据和考试记录 +6.邮箱验证:支持真实邮箱验证码发送功能 + +## 功能模块 + +## 用户管理 +1.用户注册:邮箱验证、密码强度校验 +2.用户登录:安全的身份认证 +3.密码修改:支持在线修改密码 + +## 考试系统 +1.题目生成:根据学段生成不同难度的选择题 +2.考试界面:清晰的题目展示和选项选择 +3.进度跟踪:实时显示答题进度 +4.成绩统计:自动计算得分和正确率 + +## 数据管理 +1.用户数据:本地文件存储用户信息 +2.考试记录:保存历史考试成绩 +3.题目文件:导出生成的题目试卷 + +## 技术架构 + +## 前端界面 +Qt Widgets:跨平台的图形界面框架 +响应式设计:自适应不同屏幕尺寸 +美观UI:现代化的界面风格设计 + +## 后端逻辑 +C++11/14:核心业务逻辑实现 +面向对象设计:模块化的类结构 +跨平台支持:Windows/Linux/macOS兼容 + +## 核心类说明 +## 前端类 +MainWindow:主窗口控制器 +LoginWidget:登录界面 +RegisterWidget:注册界面 +MainMenuWidget:主菜单界面 +ExamWidget:考试界面 +ResultWidget:成绩界面 + +## 后端类 +User:用户信息管理 +UserManager:用户认证和注册 +QuestionGenerator:题目生成器 +FileSaver:文件存储管理 + +## 项目结构 +teamwork-project/ +├── src/ # 源代码目录 +│ ├── frontend/ # 前端代码目录 +│ │ ├── mathlearningApp.pro # Qt项目配置文件 +│ │ ├── main.cpp # 程序入口 +│ │ ├── mainwindow.h/cpp # 主窗口控制器 +│ │ ├── loginwidget.h/cpp # 登录界面 +│ │ ├── registerwidget.h/cpp # 注册界面 +│ │ ├── mainmenuwidget.h/cpp # 主菜单界面 +│ │ ├── examwidget.h/cpp # 考试界面 +│ │ └── resultwidget.h/cpp # 成绩界面 +│ └── backend/ # 后端代码目录 +│ ├── user.h/cpp # 用户类 +│ ├── usermanage.h/cpp # 用户管理 +│ ├── questiongenerator.h/cpp # 题目生成器 +│ └── filesaver.h/cpp # 文件存储 +└── doc/ # 文档目录 + └── README.md # 项目说明 + +## 编译运行 +双击.exe文件运行 + +## 环境要求 +Qt 5.12 或更高版本 +C++11 兼容编译器 +CMake 3.10 或更高版本 + +## 使用说明 +## 首次使用 +运行程序,进入登录界面 +点击"注册新账号"创建用户账户 +输入邮箱获取验证码完成注册 +设置符合要求的密码(6-10位,包含大小写字母和数字) + +## 参加考试 +登录成功后进入主菜单 +选择考试年级和题目数量(5-30题) +点击"开始考试"进入考试界面 +逐题作答,可前后翻页检查 +提交后查看成绩和正确率 + +## 架构设计说明 +## 前后端分离 +前端:负责UI展示、用户交互、界面逻辑 +后端:负责业务逻辑、数据处理、算法实现 +通信方式:通过类接口和信号槽机制进行通信 + +## 设计模式 +MVC模式:清晰的数据-视图-控制器分离 +观察者模式:通过Qt信号槽实现组件间通信 +单例模式:用户管理等核心服务 + +## 问题反馈 +如在使用过程中遇到问题,请联系开发团队或提交Issue。 \ No newline at end of file diff --git a/src/frontend/examwidget.h b/src/frontend/examwidget.h index 2463d3d..e741829 100644 --- a/src/frontend/examwidget.h +++ b/src/frontend/examwidget.h @@ -27,19 +27,19 @@ signals: private slots: void onNextClicked(); - void onPreviousClicked(); // 新增:上一题 + void onPreviousClicked(); // 上一题 void onSubmitClicked(); private: void showQuestion(int index); void generateOptions(); - void updateButtonStates(); // 新增:更新按钮状态 + void updateButtonStates(); // 更新按钮状态 QLabel *questionLabel; QLabel *progressLabel; QButtonGroup *optionGroup; QRadioButton *optionButtons[4]; - QPushButton *previousButton; // 新增:上一题按钮 + QPushButton *previousButton; // 上一题按钮 QPushButton *nextButton; QPushButton *submitButton; diff --git a/src/frontend/mainmenuwidget.cpp b/src/frontend/mainmenuwidget.cpp index 3dbf12d..ae6f2a1 100644 --- a/src/frontend/mainmenuwidget.cpp +++ b/src/frontend/mainmenuwidget.cpp @@ -76,7 +76,7 @@ MainMenuWidget::MainMenuWidget(QWidget *parent) : QWidget(parent) "}"); startButton->setFixedSize(150, 50); - // 退出登录按钮 - 修改为与开始考试按钮相同大小 + // 退出登录按钮 logoutButton = new QPushButton("退出登录"); logoutButton->setStyleSheet("QPushButton {" "background-color: #95a5a6;" diff --git a/src/frontend/mainmenuwidget.h b/src/frontend/mainmenuwidget.h index e4cae4f..f3c56d4 100644 --- a/src/frontend/mainmenuwidget.h +++ b/src/frontend/mainmenuwidget.h @@ -17,15 +17,15 @@ class MainMenuWidget : public QWidget public: explicit MainMenuWidget(QWidget *parent = nullptr); void setUserInfo(const std::wstring& username, const std::wstring& grade); - QString getSelectedGrade() const; // 新增方法 + QString getSelectedGrade() const; signals: void startExam(int questionCount); - void logoutRequested(); // 新增信号:退出登录 + void logoutRequested(); // 退出登录 private slots: void onStartExamClicked(); - void onLogoutClicked(); // 新增槽函数:处理退出登录 + void onLogoutClicked(); // 处理退出登录 private: QLabel *welcomeLabel; @@ -33,7 +33,7 @@ private: QComboBox *gradeComboBox; QSpinBox *questionCountSpinBox; QPushButton *startButton; - QPushButton *logoutButton; // 新增:退出登录按钮 + QPushButton *logoutButton; // 退出登录按钮 }; #endif diff --git a/src/frontend/mainwindow.h b/src/frontend/mainwindow.h index 4dcb6e3..7135fcc 100644 --- a/src/frontend/mainwindow.h +++ b/src/frontend/mainwindow.h @@ -27,7 +27,7 @@ private slots: void showResult(int score, int total); void onUserLoggedIn(User* user); void onRegisterSuccess(); - void onLogoutRequested(); // 新增:处理退出登录 + void onLogoutRequested(); // 处理退出登录 private: QStackedWidget *stackedWidget; diff --git a/src/frontend/mainwindow.ui b/src/frontend/mainwindow.ui new file mode 100644 index 0000000..b232854 --- /dev/null +++ b/src/frontend/mainwindow.ui @@ -0,0 +1,22 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + + + -- 2.34.1 From ecf89276e608709e08ac004f1aad78cc3f6d53f3 Mon Sep 17 00:00:00 2001 From: lxf450481 <705835715@qq.com> Date: Sun, 12 Oct 2025 14:25:10 +0800 Subject: [PATCH 7/9] v2.0 --- project/filesaver.cpp | 56 ++-- project/questiongenerator.cpp | 571 +++++++++++++++++++++++++++++----- project/questiongenerator.h | 41 ++- project/usermanage.cpp | 375 +++++++++++++++++++--- project/usermanage.h | 50 ++- 5 files changed, 927 insertions(+), 166 deletions(-) diff --git a/project/filesaver.cpp b/project/filesaver.cpp index 4637655..d9d724d 100644 --- a/project/filesaver.cpp +++ b/project/filesaver.cpp @@ -22,7 +22,7 @@ bool FileSaver::createDirectory(const std::wstring& wpath) { std::string FileSaver::usernameToSafeName(const std::wstring& username) { std::string result; for (wchar_t wc : username) { - if ((wc >= L'0' && wc <= L'9') || (wc >= L'A' && wc <= L'Z') || + if ((wc >= L'0' && wc <= L'9') || (wc >= L'A' && wc <= L'Z') || (wc >= L'a' && wc <= L'z') || wc == L'_') { result += static_cast(wc); } else { @@ -60,7 +60,7 @@ std::string FileSaver::wstringToString(const std::wstring& wstr) { } //将题目列表写入文件的实现 -bool FileSaver::writeQuestionsToFile(const std::string& filename, +bool FileSaver::writeQuestionsToFile(const std::string& filename, const std::string& safeUsername, const std::string& englishGrade, const std::vector& questions) { @@ -68,15 +68,15 @@ bool FileSaver::writeQuestionsToFile(const std::string& filename, if (!file.is_open()) { return false; } - + file << "Math Exam Paper\n"; file << "Username: " << safeUsername << "\n"; file << "Grade: " << englishGrade << "\n"; file << "Time: " << wstringToString(getCurrentTime()) << "\n\n"; - + for (size_t i = 0; i < questions.size(); i++) { file << (i + 1) << ". "; - + for (wchar_t wc : questions[i]) { switch(wc) { case L'√': file << "√"; break; @@ -106,38 +106,38 @@ bool FileSaver::writeQuestionsToFile(const std::string& filename, } file << "\n\n"; } - + file.close(); return true; } //准备文件和目录的实现 -std::string FileSaver::prepareFileAndDirectory(const std::wstring& username, +std::string FileSaver::prepareFileAndDirectory(const std::wstring& username, const std::wstring& grade) { std::string safeUsername = usernameToSafeName(username); std::string englishGrade = gradeToEnglish(grade); std::string folderPath = "exam_papers/" + safeUsername + "_" + englishGrade + "_Papers"; - + // 创建目录 std::wstring wfolderPath; for (char c : folderPath) { wfolderPath += wchar_t(c); } createDirectory(wfolderPath); - + return folderPath + "/" + wstringToString(generateFilename()) + ".txt"; } //保存题目的完整流程实现 -bool FileSaver::saveToFile(const std::wstring& username, - const std::wstring& grade, +bool FileSaver::saveToFile(const std::wstring& username, + const std::wstring& grade, const std::vector& questions) { std::string safeUsername = usernameToSafeName(username); std::string englishGrade = gradeToEnglish(grade); std::string filename = prepareFileAndDirectory(username, grade); - + bool success = writeQuestionsToFile(filename, safeUsername, englishGrade, questions); - + return success; } @@ -145,17 +145,17 @@ bool FileSaver::saveToFile(const std::wstring& username, std::wstring FileSaver::generateFilename() { auto now = std::chrono::system_clock::now(); auto time_t = std::chrono::system_clock::to_time_t(now); - + std::tm tm = *std::localtime(&time_t); std::wostringstream oss; - - oss << tm.tm_year + 1900 << L"_" + + oss << tm.tm_year + 1900 << L"_" << (tm.tm_mon + 1 < 10 ? L"0" : L"") << tm.tm_mon + 1 << L"_" << (tm.tm_mday < 10 ? L"0" : L"") << tm.tm_mday << L"_" << (tm.tm_hour < 10 ? L"0" : L"") << tm.tm_hour << L"_" << (tm.tm_min < 10 ? L"0" : L"") << tm.tm_min << L"_" << (tm.tm_sec < 10 ? L"0" : L"") << tm.tm_sec; - + return oss.str(); } @@ -163,43 +163,43 @@ std::wstring FileSaver::generateFilename() { std::wstring FileSaver::getCurrentTime() { auto now = std::chrono::system_clock::now(); auto time_t = std::chrono::system_clock::to_time_t(now); - + std::tm tm = *std::localtime(&time_t); std::wostringstream oss; - - oss << tm.tm_year + 1900 << L"-" + + oss << tm.tm_year + 1900 << L"-" << (tm.tm_mon + 1 < 10 ? L"0" : L"") << tm.tm_mon + 1 << L"-" << (tm.tm_mday < 10 ? L"0" : L"") << tm.tm_mday << L" " << (tm.tm_hour < 10 ? L"0" : L"") << tm.tm_hour << L":" << (tm.tm_min < 10 ? L"0" : L"") << tm.tm_min << L":" << (tm.tm_sec < 10 ? L"0" : L"") << tm.tm_sec; - + return oss.str(); } //保存考试记录的实现 -bool FileSaver::saveExamRecord(const std::wstring& username, +bool FileSaver::saveExamRecord(const std::wstring& username, const std::wstring& grade, - int score, + int score, int totalQuestions, const std::wstring& examDate) { std::string safeUsername = usernameToSafeName(username); std::string filename = "exam_records/" + safeUsername + "_records.txt"; - + // 创建目录 createDirectory(L"exam_records"); - + std::ofstream file(filename, std::ios::app); if (!file.is_open()) { return false; } - + file << "Date: " << wstringToString(examDate) << "\n"; file << "Grade: " << gradeToEnglish(grade) << "\n"; file << "Score: " << score << "/" << totalQuestions << "\n"; file << "Percentage: " << (score * 100.0 / totalQuestions) << "%\n"; file << "------------------------\n"; - + file.close(); return true; -} \ No newline at end of file +} diff --git a/project/questiongenerator.cpp b/project/questiongenerator.cpp index ca669d6..d19e285 100644 --- a/project/questiongenerator.cpp +++ b/project/questiongenerator.cpp @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include //题目生成器构造函数的实现 QuestionGenerator::QuestionGenerator() : currentGrade(Grade::PRIMARY), gen(rd()) {} @@ -11,6 +14,7 @@ QuestionGenerator::QuestionGenerator() : currentGrade(Grade::PRIMARY), gen(rd()) //设置年级的实现 void QuestionGenerator::setGrade(Grade grade) { currentGrade = grade; + qDebug() << "QuestionGenerator: 设置年级为:" << static_cast(grade); } //生成随机数的实现 @@ -22,7 +26,7 @@ std::wstring QuestionGenerator::generateNumber(int min, int max) { //生成随机运算符的实现 std::wstring QuestionGenerator::generateOperator() { std::vector operators = {L"+", L"-", L"×", L"÷"}; - std::uniform_int_distribution<> dis(0, operators.size() - 1); + std::uniform_int_distribution<> dis(0, static_cast(operators.size()) - 1); return operators[dis(gen)]; } @@ -36,67 +40,170 @@ bool QuestionGenerator::needsParentheses(const std::wstring& expr, const std::ws return false; } -//生成小学题目的实现 +//生成小学题目的实现 - 至少2个操作数 std::wstring QuestionGenerator::generatePrimaryQuestion() { - int operandCount = std::uniform_int_distribution<>(2, 4)(gen); + std::uniform_int_distribution<> operandDis(2, 5); // 2-5个操作数(至少2个) + int operandCount = operandDis(gen); std::wstring question; - + std::vector numbers; + std::vector operators; + + // 生成操作数和运算符 for (int i = 0; i < operandCount; i++) { - std::wstring number = generateNumber(1, 100); - - if (i == 0) { - question += number; - } else { - std::wstring op = generateOperator(); - - if (i >= 1 && operandCount > 2) { - if (needsParentheses(question, op)) { - question = L"(" + question + L") " + op + L" " + number; - } else { - question += L" " + op + L" " + number; - } - } else { - question += L" " + op + L" " + number; - } + numbers.push_back(generateNumber(1, 100)); // 1-100 + if (i < operandCount - 1) { + operators.push_back(generateOperator()); } } - + + // 构建表达式 + question = numbers[0]; + for (size_t i = 0; i < operators.size(); i++) { + // 简单的括号逻辑:在需要时添加括号 + if (i < operators.size() - 1 && (operators[i] == L"+" || operators[i] == L"-") && + (operators[i+1] == L"×" || operators[i+1] == L"÷")) { + question = L"(" + question + L")"; + } + question += L" " + operators[i] + L" " + numbers[i + 1]; + } + return question + L" = ?"; } -//生成初中题目的实现 +//生成初中题目的实现 - 可以为1个操作数 std::wstring QuestionGenerator::generateJuniorQuestion() { - std::wstring baseQuestion = generatePrimaryQuestion(); - baseQuestion = baseQuestion.substr(0, baseQuestion.length() - 3); - - std::uniform_int_distribution<> dis(0, 1); - - if (dis(gen) == 0) { - if (baseQuestion.find(L"+") != std::wstring::npos || - baseQuestion.find(L"-") != std::wstring::npos) { - return L"(" + baseQuestion + L")² = ?"; + std::uniform_int_distribution<> operandDis(1, 5); // 1-5个操作数(可以为1) + int operandCount = operandDis(gen); + std::uniform_int_distribution<> typeDis(0, 1); // 0:平方, 1:开根号 + + std::wstring question; + + // 如果只有一个操作数,直接生成平方或开根号题目 + if (operandCount == 1) { + int specialType = typeDis(gen); + if (specialType == 0) { + // 平方 + std::wstring number = generateNumber(1, 100); + question = number + L"² = ?"; } else { - return baseQuestion + L"² = ?"; + // 开根号 - 确保是平方数 + int num = std::uniform_int_distribution<>(1, 20)(gen); + int square = num * num; + question = L"√" + std::to_wstring(square) + L" = ?"; } + return question; // 直接返回,不再添加 = ? } else { - return L"√(" + baseQuestion + L") = ?"; + // 多个操作数的情况 + std::vector numbers; + std::vector operators; + + // 生成基础操作数和运算符 + for (int i = 0; i < operandCount; i++) { + numbers.push_back(generateNumber(1, 100)); // 1-100 + if (i < operandCount - 1) { + operators.push_back(generateOperator()); + } + } + + // 随机选择一个位置插入平方或开根号 + int specialPos = std::uniform_int_distribution<>(0, operandCount - 1)(gen); + int specialType = typeDis(gen); + + // 构建表达式 + question = numbers[0]; + for (size_t i = 0; i < operators.size(); i++) { + // 在指定位置插入特殊运算符 + if (static_cast(i) == specialPos) { + if (specialType == 0) { + // 平方 + question = L"(" + question + L")²"; + } else { + // 开根号 - 确保是平方数 + int num = std::uniform_int_distribution<>(1, 20)(gen); + int square = num * num; + question = L"√" + std::to_wstring(square) + L" " + operators[i] + L" " + question; + } + } else { + // 简单的括号逻辑 + if (i < operators.size() - 1 && (operators[i] == L"+" || operators[i] == L"-") && + (operators[i+1] == L"×" || operators[i+1] == L"÷")) { + question = L"(" + question + L")"; + } + question += L" " + operators[i] + L" " + numbers[i + 1]; + } + } + + // 如果特殊运算符在最后一个位置 + if (specialPos == static_cast(operators.size())) { + if (specialType == 0) { + question = L"(" + question + L")²"; + } else { + int num = std::uniform_int_distribution<>(1, 20)(gen); + int square = num * num; + question = L"√" + std::to_wstring(square) + L" " + operators.back() + L" " + question; + } + } + + return question + L" = ?"; // 只有多个操作数时才在这里添加 = ? } } -//生成高中题目的实现 +//生成高中题目的实现 - 可以为1个操作数 std::wstring QuestionGenerator::generateSeniorQuestion() { - std::wstring baseQuestion = generatePrimaryQuestion(); - baseQuestion = baseQuestion.substr(0, baseQuestion.length() - 3); - + std::uniform_int_distribution<> operandDis(1, 5); // 1-5个操作数(可以为1) + int operandCount = operandDis(gen); std::vector trigFunctions = {L"sin", L"cos", L"tan"}; - std::uniform_int_distribution<> dis(0, trigFunctions.size() - 1); - - std::wstring trigFunc = trigFunctions[dis(gen)]; - - if (baseQuestion.front() == L'(' && baseQuestion.back() == L')') { - return trigFunc + baseQuestion + L" = ?"; + + std::wstring question; + + // 如果只有一个操作数,直接生成三角函数题目 + if (operandCount == 1) { + int trigType = std::uniform_int_distribution<>(0, 2)(gen); + int angle = std::uniform_int_distribution<>(0, 90)(gen); // 角度范围0-90度 + question = trigFunctions[trigType] + L"(" + std::to_wstring(angle) + L"°) = ?"; + return question; // 直接返回,不再添加 = ? } else { - return trigFunc + L"(" + baseQuestion + L") = ?"; + // 多个操作数的情况 + std::vector numbers; + std::vector operators; + + // 生成基础操作数和运算符 + for (int i = 0; i < operandCount; i++) { + numbers.push_back(generateNumber(1, 100)); // 1-100 + if (i < operandCount - 1) { + operators.push_back(generateOperator()); + } + } + + // 随机选择一个位置插入三角函数 + int trigPos = std::uniform_int_distribution<>(0, operandCount - 1)(gen); + int trigType = std::uniform_int_distribution<>(0, 2)(gen); + int angle = std::uniform_int_distribution<>(0, 90)(gen); // 角度范围0-90度 + + // 构建表达式 + question = numbers[0]; + for (size_t i = 0; i < operators.size(); i++) { + // 在指定位置插入三角函数 + if (static_cast(i) == trigPos) { + std::wstring trigExpr = trigFunctions[trigType] + L"(" + std::to_wstring(angle) + L"°)"; + question = trigExpr + L" " + operators[i] + L" " + question; + } else { + // 简单的括号逻辑 + if (i < operators.size() - 1 && (operators[i] == L"+" || operators[i] == L"-") && + (operators[i+1] == L"×" || operators[i+1] == L"÷")) { + question = L"(" + question + L")"; + } + question += L" " + operators[i] + L" " + numbers[i + 1]; + } + } + + // 如果三角函数在最后一个位置 + if (trigPos == static_cast(operators.size())) { + std::wstring trigExpr = trigFunctions[trigType] + L"(" + std::to_wstring(angle) + L"°)"; + question = trigExpr + L" " + operators.back() + L" " + question; + } + + return question + L" = ?"; // 只有多个操作数时才在这里添加 = ? } } @@ -107,11 +214,19 @@ bool QuestionGenerator::IsQuestionUnique(const std::wstring& question) { //生成指定数量题目的实现 std::vector QuestionGenerator::generateQuestions(int count) { + // 验证题目数量范围 + if (count < 10 || count > 30) { + qDebug() << "QuestionGenerator: 题目数量超出范围(10-30),使用默认值10"; + count = 10; + } + std::vector questions; - - for (int i = 0; i < count; i++) { + int attempts = 0; + const int maxAttempts = count * 10; // 增加尝试次数 + + for (int i = 0; i < count && attempts < maxAttempts; i++) { std::wstring question; - + switch(currentGrade) { case Grade::PRIMARY: question = generatePrimaryQuestion(); @@ -123,51 +238,359 @@ std::vector QuestionGenerator::generateQuestions(int count) { question = generateSeniorQuestion(); break; } - + if (IsQuestionUnique(question)) { questions.push_back(question); generatedQuestions.insert(question); + qDebug() << "生成题目" << i + 1 << ":" << QString::fromStdWString(question); } else { i--; + qDebug() << "题目重复,重新生成"; } + attempts++; + } + + if (static_cast(questions.size()) < count) { + qDebug() << "QuestionGenerator: 警告:只生成了" << questions.size() << "个唯一题目"; } - + + qDebug() << "QuestionGenerator: 成功生成" << questions.size() << "个题目"; return questions; } +// 计算小学表达式结果 - 改进版本 +double QuestionGenerator::calculatePrimaryExpression(const std::wstring& expr) { + try { + // 简单的表达式计算,支持基础运算 + std::wstring expression = expr; + + // 移除空格 + expression.erase(std::remove(expression.begin(), expression.end(), L' '), expression.end()); + + // 简单的递归计算函数 + std::function calculate = [&](const std::wstring& str) -> double { + if (str.empty()) return 0.0; + + // 处理括号 + if (str.front() == L'(' && str.back() == L')') { + return calculate(str.substr(1, str.length() - 2)); + } + + // 查找最低优先级的运算符 + int parenCount = 0; + int lowestPriority = -1; + int lowestPos = -1; + + for (int i = static_cast(str.length()) - 1; i >= 0; i--) { + wchar_t c = str[i]; + if (c == L')') parenCount++; + else if (c == L'(') parenCount--; + else if (parenCount == 0) { + if ((c == L'+' || c == L'-') && lowestPriority < 1) { + lowestPriority = 1; + lowestPos = i; + } else if ((c == L'×' || c == L'÷') && lowestPriority < 0) { + lowestPriority = 0; + lowestPos = i; + } + } + } + + if (lowestPos != -1) { + std::wstring left = str.substr(0, lowestPos); + std::wstring right = str.substr(lowestPos + 1); + wchar_t op = str[lowestPos]; + + double leftVal = calculate(left); + double rightVal = calculate(right); + + switch(op) { + case L'+': return leftVal + rightVal; + case L'-': return leftVal - rightVal; + case L'×': return leftVal * rightVal; + case L'÷': + if (rightVal == 0) { + qDebug() << "除零错误"; + return 0.0; + } + return leftVal / rightVal; + } + } + + // 没有运算符,直接解析数字 + try { + return std::stod(str); + } catch (...) { + qDebug() << "无法解析数字:" << QString::fromStdWString(str); + return 0.0; + } + }; + + return calculate(expression); + } catch (const std::exception& e) { + qDebug() << "计算小学表达式异常:" << e.what(); + return 0.0; + } +} + +// 计算初中表达式结果 - 改进版本 +double QuestionGenerator::calculateJuniorExpression(const std::wstring& expr) { + try { + std::wstring expression = expr; + + // 处理平方运算 + size_t squarePos = expression.find(L"²"); + while (squarePos != std::wstring::npos) { + // 找到平方的基数 + int start = static_cast(squarePos) - 1; + while (start >= 0 && (std::iswdigit(expression[start]) || expression[start] == L')')) { + start--; + } + if (start >= 0 && expression[start] == L'(') start--; + + std::wstring baseStr = expression.substr(start + 1, squarePos - start - 1); + double base = calculatePrimaryExpression(baseStr); + double result = base * base; + + // 替换平方部分 + expression.replace(start + 1, squarePos - start + 1, std::to_wstring(result)); + squarePos = expression.find(L"²"); + } + + // 处理开方运算 + size_t sqrtPos = expression.find(L"√"); + while (sqrtPos != std::wstring::npos) { + // 找到被开方数 + size_t end = sqrtPos + 1; + while (end < expression.length() && (std::iswdigit(expression[end]) || expression[end] == L'(')) { + end++; + } + + std::wstring radicandStr = expression.substr(sqrtPos + 1, end - sqrtPos - 1); + double radicand = calculatePrimaryExpression(radicandStr); + if (radicand < 0) { + qDebug() << "负数开方错误"; + return 0.0; + } + double result = std::sqrt(radicand); + + // 替换开方部分 + expression.replace(sqrtPos, end - sqrtPos, std::to_wstring(result)); + sqrtPos = expression.find(L"√"); + } + + return calculatePrimaryExpression(expression); + } catch (const std::exception& e) { + qDebug() << "计算初中表达式异常:" << e.what(); + return 0.0; + } +} + +// 计算高中表达式结果 - 改进版本 +double QuestionGenerator::calculateSeniorExpression(const std::wstring& expr) { + try { + std::wstring expression = expr; + + // 处理三角函数 + std::vector trigFuncs = {L"sin", L"cos", L"tan"}; + + for (const auto& func : trigFuncs) { + size_t pos = expression.find(func); + while (pos != std::wstring::npos) { + // 找到括号内的角度 + size_t parenStart = pos + func.length(); + if (parenStart >= expression.length() || expression[parenStart] != L'(') { + break; + } + + size_t parenEnd = expression.find(L')', parenStart); + if (parenEnd == std::wstring::npos) { + break; + } + + std::wstring angleStr = expression.substr(parenStart + 1, parenEnd - parenStart - 1); + // 移除度符号 + if (!angleStr.empty() && angleStr.back() == L'°') { + angleStr.pop_back(); + } + + double angle = std::stod(angleStr); + double result = 0.0; + + // 转换为弧度并计算 + double radians = angle * M_PI / 180.0; + if (func == L"sin") { + result = std::sin(radians); + } else if (func == L"cos") { + result = std::cos(radians); + } else if (func == L"tan") { + if (std::abs(std::cos(radians)) < 1e-10) { + qDebug() << "tan函数计算错误,角度:" << angle; + return 0.0; + } + result = std::tan(radians); + } + + // 替换三角函数部分 + expression.replace(pos, parenEnd - pos + 1, std::to_wstring(result)); + pos = expression.find(func); + } + } + + return calculateJuniorExpression(expression); + } catch (const std::exception& e) { + qDebug() << "计算高中表达式异常:" << e.what(); + return 0.0; + } +} + +// 计算表达式结果 +double QuestionGenerator::calculateExpression(const std::wstring& expression) { + try { + switch(currentGrade) { + case Grade::PRIMARY: + return calculatePrimaryExpression(expression); + case Grade::JUNIOR: + return calculateJuniorExpression(expression); + case Grade::SENIOR: + return calculateSeniorExpression(expression); + default: + return 0.0; + } + } catch (const std::exception& e) { + qDebug() << "计算表达式异常:" << e.what(); + return 0.0; + } +} + +// 计算正确答案 +double QuestionGenerator::calculateCorrectAnswer(const std::wstring& question) { + try { + // 移除 "= ?" 部分 + std::wstring expression = question; + size_t pos = expression.find(L" = ?"); + if (pos != std::wstring::npos) { + expression = expression.substr(0, pos); + } + + qDebug() << "计算题目答案:" << QString::fromStdWString(expression); + double result = calculateExpression(expression); + qDebug() << "计算结果:" << result; + return result; + } catch (const std::exception& e) { + qDebug() << "计算正确答案异常:" << e.what(); + return 0.0; + } +} + +// 生成有意义的选项 +std::vector QuestionGenerator::generateMeaningfulOptions(double correctAnswer) { + try { + std::vector options; + std::uniform_real_distribution dis(-2.0, 2.0); + std::set usedValues; + + // 格式化正确答案 + std::stringstream oss; + oss << std::fixed << std::setprecision(6) << correctAnswer; + std::string correctStr = oss.str(); + // 移除末尾多余的0 + correctStr.erase(correctStr.find_last_not_of('0') + 1, std::string::npos); + if (!correctStr.empty() && correctStr.back() == '.') { + correctStr.pop_back(); + } + + std::wstring wcorrectStr(correctStr.begin(), correctStr.end()); + options.push_back(wcorrectStr); + usedValues.insert(correctAnswer); + + // 生成有意义的干扰项 + for (int i = 0; i < 3; ++i) { + double variation = dis(gen); + double wrongAnswer = correctAnswer + variation; + + // 确保干扰项与正确答案不同且唯一 + int attempts = 0; + while ((std::abs(wrongAnswer - correctAnswer) < 0.001 || + usedValues.find(wrongAnswer) != usedValues.end()) && + attempts < 10) { + variation = dis(gen); + wrongAnswer = correctAnswer + variation; + attempts++; + } + + usedValues.insert(wrongAnswer); + + std::stringstream wrongOss; + wrongOss << std::fixed << std::setprecision(6) << wrongAnswer; + std::string wrongStr = wrongOss.str(); + // 移除末尾多余的0 + wrongStr.erase(wrongStr.find_last_not_of('0') + 1, std::string::npos); + if (!wrongStr.empty() && wrongStr.back() == '.') { + wrongStr.pop_back(); + } + + std::wstring wwrongStr(wrongStr.begin(), wrongStr.end()); + options.push_back(wwrongStr); + } + + // 随机打乱选项顺序 + std::shuffle(options.begin(), options.end(), gen); + + return options; + } catch (const std::exception& e) { + qDebug() << "生成选项异常:" << e.what(); + // 返回默认选项 + return {L"1.0", L"2.0", L"3.0", L"4.0"}; + } +} + //生成题目和选项的实现(用于GUI考试) QuestionGenerator::QuestionWithOptions QuestionGenerator::generateQuestionWithOptions() { QuestionWithOptions qwo; - - // 生成题目 - switch(currentGrade) { - case Grade::PRIMARY: - qwo.question = generatePrimaryQuestion(); - break; - case Grade::JUNIOR: - qwo.question = generateJuniorQuestion(); - break; - case Grade::SENIOR: - qwo.question = generateSeniorQuestion(); - break; - } - - // 生成选项(简单实现,实际应该计算正确答案) - std::uniform_int_distribution<> optionDis(1, 4); - qwo.correctIndex = optionDis(gen) - 1; - - for (int i = 0; i < 4; i++) { - if (i == qwo.correctIndex) { - qwo.options.push_back(L"正确答案"); - } else { - qwo.options.push_back(L"选项 " + std::to_wstring(i + 1)); + + try { + // 生成题目 + switch(currentGrade) { + case Grade::PRIMARY: + qwo.question = generatePrimaryQuestion(); + break; + case Grade::JUNIOR: + qwo.question = generateJuniorQuestion(); + break; + case Grade::SENIOR: + qwo.question = generateSeniorQuestion(); + break; } + + // 计算正确答案并生成有意义的选项 + double correctAnswer = calculateCorrectAnswer(qwo.question); + qwo.options = generateMeaningfulOptions(correctAnswer); + + // 找到正确答案的索引 + std::stringstream oss; + oss << std::fixed << std::setprecision(6) << correctAnswer; + std::string correctStr = oss.str(); + std::wstring wcorrectStr(correctStr.begin(), correctStr.end()); + + for (size_t i = 0; i < qwo.options.size(); ++i) { + if (qwo.options[i] == wcorrectStr) { + qwo.correctIndex = static_cast(i); + break; + } + } + } catch (const std::exception& e) { + qDebug() << "生成题目和选项异常:" << e.what(); + // 返回默认题目 + qwo.question = L"1 + 1 = ?"; + qwo.options = {L"1.0", L"2.0", L"3.0", L"4.0"}; + qwo.correctIndex = 1; } - + return qwo; } //清空历史记录的实现 void QuestionGenerator::clearHistory() { generatedQuestions.clear(); -} \ No newline at end of file +} diff --git a/project/questiongenerator.h b/project/questiongenerator.h index d375ee3..487b79c 100644 --- a/project/questiongenerator.h +++ b/project/questiongenerator.h @@ -6,6 +6,9 @@ #include #include #include +#include +#include +#include //题目生成器类,根据年级生成不同难度的数学题目 //支持小学、初中、高中三个级别的题目生成,包含查重功能。 @@ -15,49 +18,61 @@ private: std::set generatedQuestions; std::random_device rd; std::mt19937 gen; - + //生成小学级别的基础算术题目 std::wstring generatePrimaryQuestion(); - + //生成初中级别的题目,包含平方或开根号 std::wstring generateJuniorQuestion(); - + //生成高中级别的题目,包含三角函数 std::wstring generateSeniorQuestion(); - + //生成指定范围内的随机整数 std::wstring generateNumber(int min, int max); - + //随机生成算术运算符 std::wstring generateOperator(); - + //检查题目是否已生成过(查重) bool IsQuestionUnique(const std::wstring& question); - + //括号需求判断 bool needsParentheses(const std::wstring& expr, const std::wstring& nextOp); + // 计算表达式结果 + double calculateExpression(const std::wstring& expression); + double calculatePrimaryExpression(const std::wstring& expr); + double calculateJuniorExpression(const std::wstring& expr); + double calculateSeniorExpression(const std::wstring& expr); + public: //构造函数,初始化题目生成器 QuestionGenerator(); - + //设置当前题目生成难度级别 void setGrade(Grade grade); - + //生成指定数量的唯一数学题目 std::vector generateQuestions(int count); - + //生成题目和选项(用于GUI) struct QuestionWithOptions { std::wstring question; std::vector options; int correctIndex; }; - + QuestionWithOptions generateQuestionWithOptions(); - + //清空已生成题目的历史记录 void clearHistory(); + + // 计算正确答案 + double calculateCorrectAnswer(const std::wstring& question); + + // 生成有意义的选项 + std::vector generateMeaningfulOptions(double correctAnswer); }; -#endif \ No newline at end of file +#endif diff --git a/project/usermanage.cpp b/project/usermanage.cpp index 2c6de54..4fcba32 100644 --- a/project/usermanage.cpp +++ b/project/usermanage.cpp @@ -4,69 +4,109 @@ #include #include #include +#include +#include +#include +#include +#include +#include // 添加进程支持 +#include // 添加目录支持 -//初始化用户数据的实现 +// 初始化用户数据的实现 void UserManager::initializeUsers() { // 如果文件中有用户数据,就不初始化默认用户 if (loadUsersFromFile()) { + qDebug() << "UserManager: 从文件加载用户数据成功"; return; } - // 初始化默认测试用户 - users.emplace_back(L"张三1", L"Abc123", Grade::PRIMARY); - users.emplace_back(L"张三2", L"Abc123", Grade::PRIMARY); - users.emplace_back(L"张三3", L"Abc123", Grade::PRIMARY); + qDebug() << "UserManager: 初始化默认用户数据"; - users.emplace_back(L"李四1", L"Abc123", Grade::JUNIOR); - users.emplace_back(L"李四2", L"Abc123", Grade::JUNIOR); - users.emplace_back(L"李四3", L"Abc123", Grade::JUNIOR); + // 初始化默认测试用户(使用英文用户名避免编码问题) + users.emplace_back(L"zhangsan1", L"Abc123", Grade::PRIMARY); + users.emplace_back(L"zhangsan2", L"Abc123", Grade::PRIMARY); + users.emplace_back(L"zhangsan3", L"Abc123", Grade::PRIMARY); - users.emplace_back(L"王五1", L"Abc123", Grade::SENIOR); - users.emplace_back(L"王五2", L"Abc123", Grade::SENIOR); - users.emplace_back(L"王五3", L"Abc123", Grade::SENIOR); + users.emplace_back(L"lisi1", L"Abc123", Grade::JUNIOR); + users.emplace_back(L"lisi2", L"Abc123", Grade::JUNIOR); + users.emplace_back(L"lisi3", L"Abc123", Grade::JUNIOR); + + users.emplace_back(L"wangwu1", L"Abc123", Grade::SENIOR); + users.emplace_back(L"wangwu2", L"Abc123", Grade::SENIOR); + users.emplace_back(L"wangwu3", L"Abc123", Grade::SENIOR); // 保存到文件 - saveUsersToFile(); + if (saveUsersToFile()) { + qDebug() << "UserManager: 默认用户数据保存成功"; + } else { + qDebug() << "UserManager: 默认用户数据保存失败"; + } } -//用户管理器的构造实现 +// 用户管理器的构造实现 UserManager::UserManager() { + qDebug() << "UserManager: 构造函数调用"; initializeUsers(); } -//用户认证的实现 +// 用户认证的实现 User* UserManager::authenticateUser(const std::wstring& username, const std::wstring& password) { + qDebug() << "UserManager: 尝试认证用户:" << QString::fromStdWString(username); + + // 重新加载用户数据,确保包含新注册的用户 + loadUsersFromFile(); + for (auto& user : users) { if (user.check(username, password)) { + qDebug() << "UserManager: 用户认证成功"; return &user; } } + + qDebug() << "UserManager: 用户认证失败"; return nullptr; } -//解析用户类型的实现 +// 解析用户类型的实现 Grade UserManager::parseUserType(const std::wstring& type) { - if (type == L"小学") return Grade::PRIMARY; - if (type == L"初中") return Grade::JUNIOR; - if (type == L"高中") return Grade::SENIOR; + if (type == L"小学" || type == L"Primary") return Grade::PRIMARY; + if (type == L"初中" || type == L"Junior") return Grade::JUNIOR; + if (type == L"高中" || type == L"Senior") return Grade::SENIOR; return Grade::PRIMARY; } -//注册新用户的实现 +// 注册新用户的实现 bool UserManager::registerUser(const std::wstring& username, const std::wstring& password, const std::wstring& grade) { + qDebug() << "UserManager: 尝试注册用户:" << QString::fromStdWString(username) << "年级:" << QString::fromStdWString(grade); + + // 重新加载用户数据,确保检查最新的用户名 + loadUsersFromFile(); + // 检查用户名是否已存在 if (isUsernameExists(username)) { + qDebug() << "UserManager: 用户名已存在"; return false; } Grade userGrade = parseUserType(grade); users.emplace_back(username, password, userGrade); + qDebug() << "UserManager: 用户添加到内存,准备保存到文件"; + // 保存到文件 - return saveUsersToFile(); + bool result = saveUsersToFile(); + if (result) { + qDebug() << "UserManager: 用户注册成功"; + // 重新加载确保数据同步 + loadUsersFromFile(); + } else { + qDebug() << "UserManager: 用户注册失败 - 文件保存失败"; + } + + return result; } -//修改用户密码的实现 +// 修改用户密码的实现 bool UserManager::changePassword(const std::wstring& username, const std::wstring& oldPassword, const std::wstring& newPassword) { for (auto& user : users) { if (user.check(username, oldPassword)) { @@ -77,7 +117,7 @@ bool UserManager::changePassword(const std::wstring& username, const std::wstrin return false; } -//检查用户名是否存在的实现 +// 检查用户名是否存在的实现 bool UserManager::isUsernameExists(const std::wstring& username) { for (const auto& user : users) { if (user.getUsername() == username) { @@ -87,48 +127,305 @@ bool UserManager::isUsernameExists(const std::wstring& username) { return false; } -//从文件加载用户数据的实现 +// 从文件加载用户数据的实现 bool UserManager::loadUsersFromFile() { - std::ifstream file(userFile, std::ios::binary); + std::ifstream file(userFile); if (!file.is_open()) { + qDebug() << "UserManager: 无法打开用户文件:" << userFile.c_str(); return false; } users.clear(); - std::string line; + int userCount = 0; + while (std::getline(file, line)) { - std::wstring_convert> converter; - std::wstring wline = converter.from_bytes(line); + std::istringstream iss(line); + std::string username, password, gradeStr; - std::wistringstream wss(wline); - std::wstring username, password, gradeStr; - wss >> username >> password >> gradeStr; + if (iss >> username >> password >> gradeStr) { + // 转换为宽字符串 + std::wstring wusername, wpassword, wgradeStr; + for (char c : username) wusername += wchar_t(c); + for (char c : password) wpassword += wchar_t(c); + for (char c : gradeStr) wgradeStr += wchar_t(c); - Grade grade = parseUserType(gradeStr); - users.emplace_back(username, password, grade); + Grade grade = parseUserType(wgradeStr); + users.emplace_back(wusername, wpassword, grade); + userCount++; + qDebug() << "加载用户:" << QString::fromStdWString(wusername) << "年级:" << QString::fromStdWString(wgradeStr); + } } file.close(); + qDebug() << "UserManager: 从文件加载了" << userCount << "个用户"; return !users.empty(); } -//保存用户数据到文件的实现 +// 保存用户数据到文件的实现 bool UserManager::saveUsersToFile() { - std::ofstream file(userFile, std::ios::binary); + std::ofstream file(userFile); if (!file.is_open()) { + qDebug() << "UserManager: 无法创建用户文件:" << userFile.c_str(); return false; } + int savedCount = 0; for (const auto& user : users) { - std::wstring_convert> converter; - std::string username_utf8 = converter.to_bytes(user.getUsername()); - std::string password_utf8 = converter.to_bytes(user.getPassword()); - std::string grade_utf8 = converter.to_bytes(user.getGradeString()); + std::wstring username = user.getUsername(); + std::wstring password = user.getPassword(); + std::wstring gradeStr = user.getGradeString(); + + // 转换为普通字符串 + std::string username_str, password_str, grade_str; + for (wchar_t wc : username) username_str += static_cast(wc); + for (wchar_t wc : password) password_str += static_cast(wc); + for (wchar_t wc : gradeStr) grade_str += static_cast(wc); - file << username_utf8 << " " << password_utf8 << " " << grade_utf8 << "\n"; + file << username_str << " " << password_str << " " << grade_str << "\n"; + savedCount++; } file.close(); + qDebug() << "UserManager: 保存了" << savedCount << "个用户到文件"; return true; } + +// 生成验证码的实现 - 修复为每次都不同 +std::wstring UserManager::generateVerificationCode(int length) { + const std::wstring chars = L"0123456789"; + std::wstring code; + + // 使用当前时间作为随机种子,确保每次不同 + std::random_device rd; + std::mt19937 gen(rd() + static_cast(std::time(nullptr))); + std::uniform_int_distribution<> dis(0, chars.length() - 1); + + for (int i = 0; i < length; ++i) { + code += chars[dis(gen)]; + } + + qDebug() << "UserManager: 生成验证码:" << QString::fromStdWString(code); + return code; +} + +// 发送验证码的实现 - 添加真实邮件发送功能 +bool UserManager::sendVerificationCode(const std::wstring& email, std::wstring& generatedCode) { + qDebug() << "UserManager: 发送验证码到:" << QString::fromStdWString(email); + + // 生成验证码 + generatedCode = generateVerificationCode(); + + // 设置过期时间(10分钟) + auto expiryTime = std::chrono::system_clock::now() + std::chrono::minutes(10); + + // 存储验证码信息 + emailVerifications[email] = {generatedCode, expiryTime, false}; + + qDebug() << "UserManager: 验证码已存储,有效期10分钟"; + + // 尝试发送真实邮件 + if (sendRealEmail(email, generatedCode)) { + qDebug() << "UserManager: 验证码邮件发送成功"; + return true; + } else { + // 如果真实邮件发送失败,使用控制台输出 + std::wcout << L"[邮件模拟] 发送到: " << email << L" 的验证码: " << generatedCode << std::endl; + qDebug() << "UserManager: 真实邮件发送失败,使用模拟模式"; + + // 在调试窗口显示验证码信息 + QString qEmail = QString::fromStdWString(email); + QString qCode = QString::fromStdWString(generatedCode); + qDebug() << "=== 邮件发送模拟 ==="; + qDebug() << "收件人:" << qEmail; + qDebug() << "验证码:" << qCode; + qDebug() << "=== 邮件发送模拟 ==="; + + return true; + } +} + +// 验证邮箱验证码的实现 +bool UserManager::verifyEmailCode(const std::wstring& email, const std::wstring& code) { + qDebug() << "UserManager: 验证邮箱验证码,邮箱:" << QString::fromStdWString(email) + << "验证码:" << QString::fromStdWString(code); + + auto it = emailVerifications.find(email); + if (it == emailVerifications.end()) { + qDebug() << "UserManager: 没有找到该邮箱的验证码记录"; + return false; // 没有找到该邮箱的验证码记录 + } + + EmailVerification& verification = it->second; + + // 检查验证码是否已使用 + if (verification.used) { + qDebug() << "UserManager: 验证码已被使用"; + return false; + } + + // 检查验证码是否过期 + if (std::chrono::system_clock::now() > verification.expiryTime) { + qDebug() << "UserManager: 验证码已过期"; + emailVerifications.erase(it); + return false; + } + + // 检查验证码是否匹配 + if (verification.code == code) { + qDebug() << "UserManager: 验证码验证成功"; + verification.used = true; // 标记为已使用 + return true; + } + + qDebug() << "UserManager: 验证码不匹配"; + return false; +} + +// 真实邮件发送功能 - 仅使用 PowerShell +bool UserManager::sendRealEmail(const std::wstring& email, const std::wstring& code) { + QString qEmail = QString::fromStdWString(email); + QString qCode = QString::fromStdWString(code); + + qDebug() << "尝试使用 PowerShell 发送邮件到:" << qEmail; + + return sendEmailViaPowerShell(qEmail, qCode); +} + +// 辅助函数:安全地转义字符串中的单引号 +QString escapeSingleQuotes(const QString& input) { + QString result = input; + return result.replace("'", "''"); +} + +// 使用 PowerShell Send-MailMessage 发送邮件 +bool UserManager::sendEmailViaPowerShell(const QString& toEmail, const QString& code) { + qDebug() << "使用 PowerShell 发送邮件..."; + + // ==================== 配置区域 ==================== + // 请根据你的邮箱服务商修改以下配置: + + QString smtpServer = "smtp.qq.com"; // SMTP服务器 + int smtpPort = 587; // 端口号 + QString fromEmail = "1453386832@qq.com"; // ⬅️ 修改:发件人邮箱 + QString username = "1453386832@qq.com"; // ⬅️ 修改:邮箱账号 + QString password = "nijuqetihpojffag"; // ⬅️ 修改:邮箱授权码 + + /* + // 其他邮箱配置示例: + + // 163邮箱配置: + // QString smtpServer = "smtp.163.com"; + // int smtpPort = 25; // 或 587 + // QString fromEmail = "your_email@163.com"; + // QString username = "your_email@163.com"; + // QString password = "your_163_authorization_code"; + + // Gmail配置: + // QString smtpServer = "smtp.gmail.com"; + // int smtpPort = 587; + // QString fromEmail = "your_email@gmail.com"; + // QString username = "your_email@gmail.com"; + // QString password = "your_gmail_app_password"; + */ + // ==================== 配置结束 ==================== + + QString subject = "Math Learning Software - Verification Code"; + QString body = QString( + "Dear User:\n\n" + "You are registering for Math Learning Software. Your verification code is: %1\n\n" + "The verification code is valid for 10 minutes. Please complete your registration as soon as possible.\n\n" + "If this was not your operation, please ignore this email.\n\n" + "Math Learning Software Team" + ).arg(code); + + // 安全地转义所有字符串 + QString safePassword = escapeSingleQuotes(password); + QString safeUsername = escapeSingleQuotes(username); + QString safeFromEmail = escapeSingleQuotes(fromEmail); + QString safeToEmail = escapeSingleQuotes(toEmail); + QString safeSubject = escapeSingleQuotes(subject); + QString safeBody = escapeSingleQuotes(body); + QString safeSmtpServer = escapeSingleQuotes(smtpServer); + + // 构建 PowerShell 命令 + QString powerShellScript = QString( + "$secpasswd = ConvertTo-SecureString \"%1\" -AsPlainText -Force\n" + "$credential = New-Object System.Management.Automation.PSCredential(\"%2\", $secpasswd)\n" + "Send-MailMessage -From '%3' -To '%4' -Subject '%5' -Body '%6' -SmtpServer '%7' -Port %8 -Credential $credential -UseSsl" + ).arg(safePassword, + safeUsername, + safeFromEmail, + safeToEmail, + safeSubject, + safeBody, + safeSmtpServer, + QString::number(smtpPort)); + + // 创建临时 PowerShell 脚本文件 + QString tempScriptFile = QDir::tempPath() + "/send_email.ps1"; + QFile scriptFile(tempScriptFile); + + if (!scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qDebug() << "无法创建临时 PowerShell 脚本文件"; + return false; + } + + QTextStream out(&scriptFile); + out << powerShellScript; + scriptFile.close(); + + qDebug() << "执行 PowerShell 脚本..."; + + // 执行 PowerShell 脚本 + QProcess process; + process.start("powershell", QStringList() << "-ExecutionPolicy" << "Bypass" << "-File" << tempScriptFile); + + if (!process.waitForStarted(10000)) { + qDebug() << "无法启动 PowerShell 进程"; + QFile::remove(tempScriptFile); + return false; + } + + if (!process.waitForFinished(60000)) { // 等待60秒 + qDebug() << "PowerShell 进程超时"; + process.kill(); + QFile::remove(tempScriptFile); + return false; + } + + int exitCode = process.exitCode(); + QByteArray output = process.readAllStandardOutput(); + QByteArray error = process.readAllStandardError(); + + qDebug() << "PowerShell 退出代码:" << exitCode; + + if (!output.isEmpty()) { + qDebug() << "PowerShell 输出:" << output; + } + + if (!error.isEmpty()) { + qDebug() << "PowerShell 错误:" << error; + } + + // 清理临时文件 + QFile::remove(tempScriptFile); + + if (exitCode == 0) { + qDebug() << "邮件发送成功!"; + return true; + } else { + qDebug() << "邮件发送失败,退出代码:" << exitCode; + + // 提供详细的错误信息 + if (error.contains("Authentication")) { + qDebug() << "错误:认证失败,请检查用户名和密码"; + } else if (error.contains("Connection")) { + qDebug() << "错误:连接失败,请检查SMTP服务器和端口"; + } else if (error.contains("SSL")) { + qDebug() << "错误:SSL连接失败"; + } + + return false; + } +} diff --git a/project/usermanage.h b/project/usermanage.h index a233b25..9427a6e 100644 --- a/project/usermanage.h +++ b/project/usermanage.h @@ -6,44 +6,70 @@ #include #include #include +#include +#include +#include +#include // 添加进程支持 -//用户管理器类,负责用户数据的初始化和认证管理 -//维护用户列表,提供用户认证和年级类型解析功能。 +// 邮箱验证码结构 +struct EmailVerification { + std::wstring code; + std::chrono::system_clock::time_point expiryTime; + bool used; +}; + +// 用户管理器类,负责用户数据的初始化和认证管理 class UserManager { private: std::vector users; const std::string userFile = "users.dat"; - + + // 邮箱验证码存储 + std::map emailVerifications; + //初始化预定义用户数据 void initializeUsers(); - + //从文件加载用户数据 bool loadUsersFromFile(); - + //保存用户数据到文件 bool saveUsersToFile(); + // 真实邮件发送 + bool sendRealEmail(const std::wstring& email, const std::wstring& code); + + // PowerShell 邮件发送方法 + bool sendEmailViaPowerShell(const QString& toEmail, const QString& code); + public: //构造函数,自动初始化用户数据 UserManager(); - + //用户认证,验证用户名和密码 User* authenticateUser(const std::wstring& username, const std::wstring& password); - + //将中文年级字符串解析为年级枚举值 Grade parseUserType(const std::wstring& type); - + //注册新用户 bool registerUser(const std::wstring& username, const std::wstring& password, const std::wstring& grade); - + //修改用户密码 bool changePassword(const std::wstring& username, const std::wstring& oldPassword, const std::wstring& newPassword); - + //检查用户名是否存在 bool isUsernameExists(const std::wstring& username); - + + // 邮箱验证相关方法 + bool sendVerificationCode(const std::wstring& email, std::wstring& generatedCode); + bool verifyEmailCode(const std::wstring& email, const std::wstring& code); + + // 生成随机验证码 + std::wstring generateVerificationCode(int length = 6); + //获取所有用户(用于测试) std::vector& getUsers() { return users; } }; -#endif \ No newline at end of file +#endif -- 2.34.1 From 9eac19550163b520dd6d73af6ad2bde1a59f7705 Mon Sep 17 00:00:00 2001 From: lxf450481 <705835715@qq.com> Date: Sun, 12 Oct 2025 14:28:55 +0800 Subject: [PATCH 8/9] v2.0 --- src/back/filesaver.cpp | 205 ++++++++++++ src/back/filesaver.h | 63 ++++ src/back/questiongenerator.cpp | 596 +++++++++++++++++++++++++++++++++ src/back/questiongenerator.h | 78 +++++ src/back/user.cpp | 41 +++ src/back/user.h | 41 +++ src/back/usermanage.cpp | 431 ++++++++++++++++++++++++ src/back/usermanage.h | 75 +++++ 8 files changed, 1530 insertions(+) create mode 100644 src/back/filesaver.cpp create mode 100644 src/back/filesaver.h create mode 100644 src/back/questiongenerator.cpp create mode 100644 src/back/questiongenerator.h create mode 100644 src/back/user.cpp create mode 100644 src/back/user.h create mode 100644 src/back/usermanage.cpp create mode 100644 src/back/usermanage.h diff --git a/src/back/filesaver.cpp b/src/back/filesaver.cpp new file mode 100644 index 0000000..d9d724d --- /dev/null +++ b/src/back/filesaver.cpp @@ -0,0 +1,205 @@ +#include "filesaver.h" +#include +#include +#include +#include +#include + +//创建目录(跨平台实现) +bool FileSaver::createDirectory(const std::wstring& wpath) { +#ifdef _WIN32 + return _wmkdir(wpath.c_str()) == 0; +#else + std::string path; + for (wchar_t wc : wpath) { + if (wc < 128) path += static_cast(wc); + } + return mkdir(path.c_str(), 0755) == 0; +#endif +} + +//将包含中文的用户名转换为安全的英文文件名 +std::string FileSaver::usernameToSafeName(const std::wstring& username) { + std::string result; + for (wchar_t wc : username) { + if ((wc >= L'0' && wc <= L'9') || (wc >= L'A' && wc <= L'Z') || + (wc >= L'a' && wc <= L'z') || wc == L'_') { + result += static_cast(wc); + } else { + switch(wc) { + case L'张': result += "Zhang"; break; + case L'三': result += "San"; break; + case L'李': result += "Li"; break; + case L'四': result += "Si"; break; + case L'王': result += "Wang"; break; + case L'五': result += "Wu"; break; + default: result += "User"; break; + } + } + } + return result; +} + +//将中文年级转换为英文标识 +std::string FileSaver::gradeToEnglish(const std::wstring& grade) { + if (grade == L"小学") return "Primary"; + if (grade == L"初中") return "Junior"; + if (grade == L"高中") return "Senior"; + return "Unknown"; +} + +//宽字符串到普通字符串的转换 +std::string FileSaver::wstringToString(const std::wstring& wstr) { + std::string result; + for (wchar_t wc : wstr) { + if (wc < 128) { + result += static_cast(wc); + } + } + return result; +} + +//将题目列表写入文件的实现 +bool FileSaver::writeQuestionsToFile(const std::string& filename, + const std::string& safeUsername, + const std::string& englishGrade, + const std::vector& questions) { + std::ofstream file(filename); + if (!file.is_open()) { + return false; + } + + file << "Math Exam Paper\n"; + file << "Username: " << safeUsername << "\n"; + file << "Grade: " << englishGrade << "\n"; + file << "Time: " << wstringToString(getCurrentTime()) << "\n\n"; + + for (size_t i = 0; i < questions.size(); i++) { + file << (i + 1) << ". "; + + for (wchar_t wc : questions[i]) { + switch(wc) { + case L'√': file << "√"; break; + case L'²': file << "²"; break; + case L's': file << "s"; break; + case L'i': file << "i"; break; + case L'n': file << "n"; break; + case L'c': file << "c"; break; + case L'o': file << "o"; break; + case L't': file << "t"; break; + case L'a': file << "a"; break; + case L'(': file << "("; break; + case L')': file << ")"; break; + case L'+': file << "+"; break; + case L'-': file << "-"; break; + case L'×': file << "×"; break; + case L'÷': file << "÷"; break; + case L'=': file << "="; break; + case L'?': file << "?"; break; + case L' ': file << " "; break; + case L'0': case L'1': case L'2': case L'3': case L'4': + case L'5': case L'6': case L'7': case L'8': case L'9': + file << static_cast(wc); + break; + default: file << static_cast(wc); break; + } + } + file << "\n\n"; + } + + file.close(); + return true; +} + +//准备文件和目录的实现 +std::string FileSaver::prepareFileAndDirectory(const std::wstring& username, + const std::wstring& grade) { + std::string safeUsername = usernameToSafeName(username); + std::string englishGrade = gradeToEnglish(grade); + std::string folderPath = "exam_papers/" + safeUsername + "_" + englishGrade + "_Papers"; + + // 创建目录 + std::wstring wfolderPath; + for (char c : folderPath) { + wfolderPath += wchar_t(c); + } + createDirectory(wfolderPath); + + return folderPath + "/" + wstringToString(generateFilename()) + ".txt"; +} + +//保存题目的完整流程实现 +bool FileSaver::saveToFile(const std::wstring& username, + const std::wstring& grade, + const std::vector& questions) { + std::string safeUsername = usernameToSafeName(username); + std::string englishGrade = gradeToEnglish(grade); + std::string filename = prepareFileAndDirectory(username, grade); + + bool success = writeQuestionsToFile(filename, safeUsername, englishGrade, questions); + + return success; +} + +//生成基于时间戳的文件名实现 +std::wstring FileSaver::generateFilename() { + auto now = std::chrono::system_clock::now(); + auto time_t = std::chrono::system_clock::to_time_t(now); + + std::tm tm = *std::localtime(&time_t); + std::wostringstream oss; + + oss << tm.tm_year + 1900 << L"_" + << (tm.tm_mon + 1 < 10 ? L"0" : L"") << tm.tm_mon + 1 << L"_" + << (tm.tm_mday < 10 ? L"0" : L"") << tm.tm_mday << L"_" + << (tm.tm_hour < 10 ? L"0" : L"") << tm.tm_hour << L"_" + << (tm.tm_min < 10 ? L"0" : L"") << tm.tm_min << L"_" + << (tm.tm_sec < 10 ? L"0" : L"") << tm.tm_sec; + + return oss.str(); +} + +//获取当前格式化时间的实现 +std::wstring FileSaver::getCurrentTime() { + auto now = std::chrono::system_clock::now(); + auto time_t = std::chrono::system_clock::to_time_t(now); + + std::tm tm = *std::localtime(&time_t); + std::wostringstream oss; + + oss << tm.tm_year + 1900 << L"-" + << (tm.tm_mon + 1 < 10 ? L"0" : L"") << tm.tm_mon + 1 << L"-" + << (tm.tm_mday < 10 ? L"0" : L"") << tm.tm_mday << L" " + << (tm.tm_hour < 10 ? L"0" : L"") << tm.tm_hour << L":" + << (tm.tm_min < 10 ? L"0" : L"") << tm.tm_min << L":" + << (tm.tm_sec < 10 ? L"0" : L"") << tm.tm_sec; + + return oss.str(); +} + +//保存考试记录的实现 +bool FileSaver::saveExamRecord(const std::wstring& username, + const std::wstring& grade, + int score, + int totalQuestions, + const std::wstring& examDate) { + std::string safeUsername = usernameToSafeName(username); + std::string filename = "exam_records/" + safeUsername + "_records.txt"; + + // 创建目录 + createDirectory(L"exam_records"); + + std::ofstream file(filename, std::ios::app); + if (!file.is_open()) { + return false; + } + + file << "Date: " << wstringToString(examDate) << "\n"; + file << "Grade: " << gradeToEnglish(grade) << "\n"; + file << "Score: " << score << "/" << totalQuestions << "\n"; + file << "Percentage: " << (score * 100.0 / totalQuestions) << "%\n"; + file << "------------------------\n"; + + file.close(); + return true; +} diff --git a/src/back/filesaver.h b/src/back/filesaver.h new file mode 100644 index 0000000..4f56cc2 --- /dev/null +++ b/src/back/filesaver.h @@ -0,0 +1,63 @@ +#ifndef FILESAVER_H +#define FILESAVER_H + +#include +#include +#include +#include +#include + +#ifdef _WIN32 + #include + #include +#else + #include + #include +#endif + +// 文件保存器类,负责将生成的数学题目保存到文件系统 +class FileSaver { +public: + //将题目列表写入指定文件 + static bool writeQuestionsToFile(const std::string& filename, + const std::string& safeUsername, + const std::string& englishGrade, + const std::vector& questions); + + //准备用户专属文件夹和文件路径 + static std::string prepareFileAndDirectory(const std::wstring& username, + const std::wstring& grade); + + //保存题目列表到用户专属文件 + static bool saveToFile(const std::wstring& username, + const std::wstring& grade, + const std::vector& questions); + + //生成基于当前时间戳的文件名 + static std::wstring generateFilename(); + + //获取当前格式化的时间字符串 + static std::wstring getCurrentTime(); + + //保存考试记录 + static bool saveExamRecord(const std::wstring& username, + const std::wstring& grade, + int score, + int totalQuestions, + const std::wstring& examDate); + +private: + //将包含中文的用户名转换为安全的英文文件名 + static std::string usernameToSafeName(const std::wstring& username); + + //将中文年级转换为英文标识 + static std::string gradeToEnglish(const std::wstring& grade); + + //宽字符串到普通字符串的转换 + static std::string wstringToString(const std::wstring& wstr); + + //创建目录(跨平台实现) + static bool createDirectory(const std::wstring& wpath); +}; + +#endif \ No newline at end of file diff --git a/src/back/questiongenerator.cpp b/src/back/questiongenerator.cpp new file mode 100644 index 0000000..d19e285 --- /dev/null +++ b/src/back/questiongenerator.cpp @@ -0,0 +1,596 @@ +#include "questiongenerator.h" +#include +#include +#include +#include +#include +#include +#include +#include + +//题目生成器构造函数的实现 +QuestionGenerator::QuestionGenerator() : currentGrade(Grade::PRIMARY), gen(rd()) {} + +//设置年级的实现 +void QuestionGenerator::setGrade(Grade grade) { + currentGrade = grade; + qDebug() << "QuestionGenerator: 设置年级为:" << static_cast(grade); +} + +//生成随机数的实现 +std::wstring QuestionGenerator::generateNumber(int min, int max) { + std::uniform_int_distribution<> dis(min, max); + return std::to_wstring(dis(gen)); +} + +//生成随机运算符的实现 +std::wstring QuestionGenerator::generateOperator() { + std::vector operators = {L"+", L"-", L"×", L"÷"}; + std::uniform_int_distribution<> dis(0, static_cast(operators.size()) - 1); + return operators[dis(gen)]; +} + +//括号需求判断的辅助函数 +bool QuestionGenerator::needsParentheses(const std::wstring& expr, const std::wstring& nextOp) { + if (expr.find(L"+") != std::wstring::npos || expr.find(L"-") != std::wstring::npos) { + if (nextOp == L"×" || nextOp == L"÷") { + return true; + } + } + return false; +} + +//生成小学题目的实现 - 至少2个操作数 +std::wstring QuestionGenerator::generatePrimaryQuestion() { + std::uniform_int_distribution<> operandDis(2, 5); // 2-5个操作数(至少2个) + int operandCount = operandDis(gen); + std::wstring question; + std::vector numbers; + std::vector operators; + + // 生成操作数和运算符 + for (int i = 0; i < operandCount; i++) { + numbers.push_back(generateNumber(1, 100)); // 1-100 + if (i < operandCount - 1) { + operators.push_back(generateOperator()); + } + } + + // 构建表达式 + question = numbers[0]; + for (size_t i = 0; i < operators.size(); i++) { + // 简单的括号逻辑:在需要时添加括号 + if (i < operators.size() - 1 && (operators[i] == L"+" || operators[i] == L"-") && + (operators[i+1] == L"×" || operators[i+1] == L"÷")) { + question = L"(" + question + L")"; + } + question += L" " + operators[i] + L" " + numbers[i + 1]; + } + + return question + L" = ?"; +} + +//生成初中题目的实现 - 可以为1个操作数 +std::wstring QuestionGenerator::generateJuniorQuestion() { + std::uniform_int_distribution<> operandDis(1, 5); // 1-5个操作数(可以为1) + int operandCount = operandDis(gen); + std::uniform_int_distribution<> typeDis(0, 1); // 0:平方, 1:开根号 + + std::wstring question; + + // 如果只有一个操作数,直接生成平方或开根号题目 + if (operandCount == 1) { + int specialType = typeDis(gen); + if (specialType == 0) { + // 平方 + std::wstring number = generateNumber(1, 100); + question = number + L"² = ?"; + } else { + // 开根号 - 确保是平方数 + int num = std::uniform_int_distribution<>(1, 20)(gen); + int square = num * num; + question = L"√" + std::to_wstring(square) + L" = ?"; + } + return question; // 直接返回,不再添加 = ? + } else { + // 多个操作数的情况 + std::vector numbers; + std::vector operators; + + // 生成基础操作数和运算符 + for (int i = 0; i < operandCount; i++) { + numbers.push_back(generateNumber(1, 100)); // 1-100 + if (i < operandCount - 1) { + operators.push_back(generateOperator()); + } + } + + // 随机选择一个位置插入平方或开根号 + int specialPos = std::uniform_int_distribution<>(0, operandCount - 1)(gen); + int specialType = typeDis(gen); + + // 构建表达式 + question = numbers[0]; + for (size_t i = 0; i < operators.size(); i++) { + // 在指定位置插入特殊运算符 + if (static_cast(i) == specialPos) { + if (specialType == 0) { + // 平方 + question = L"(" + question + L")²"; + } else { + // 开根号 - 确保是平方数 + int num = std::uniform_int_distribution<>(1, 20)(gen); + int square = num * num; + question = L"√" + std::to_wstring(square) + L" " + operators[i] + L" " + question; + } + } else { + // 简单的括号逻辑 + if (i < operators.size() - 1 && (operators[i] == L"+" || operators[i] == L"-") && + (operators[i+1] == L"×" || operators[i+1] == L"÷")) { + question = L"(" + question + L")"; + } + question += L" " + operators[i] + L" " + numbers[i + 1]; + } + } + + // 如果特殊运算符在最后一个位置 + if (specialPos == static_cast(operators.size())) { + if (specialType == 0) { + question = L"(" + question + L")²"; + } else { + int num = std::uniform_int_distribution<>(1, 20)(gen); + int square = num * num; + question = L"√" + std::to_wstring(square) + L" " + operators.back() + L" " + question; + } + } + + return question + L" = ?"; // 只有多个操作数时才在这里添加 = ? + } +} + +//生成高中题目的实现 - 可以为1个操作数 +std::wstring QuestionGenerator::generateSeniorQuestion() { + std::uniform_int_distribution<> operandDis(1, 5); // 1-5个操作数(可以为1) + int operandCount = operandDis(gen); + std::vector trigFunctions = {L"sin", L"cos", L"tan"}; + + std::wstring question; + + // 如果只有一个操作数,直接生成三角函数题目 + if (operandCount == 1) { + int trigType = std::uniform_int_distribution<>(0, 2)(gen); + int angle = std::uniform_int_distribution<>(0, 90)(gen); // 角度范围0-90度 + question = trigFunctions[trigType] + L"(" + std::to_wstring(angle) + L"°) = ?"; + return question; // 直接返回,不再添加 = ? + } else { + // 多个操作数的情况 + std::vector numbers; + std::vector operators; + + // 生成基础操作数和运算符 + for (int i = 0; i < operandCount; i++) { + numbers.push_back(generateNumber(1, 100)); // 1-100 + if (i < operandCount - 1) { + operators.push_back(generateOperator()); + } + } + + // 随机选择一个位置插入三角函数 + int trigPos = std::uniform_int_distribution<>(0, operandCount - 1)(gen); + int trigType = std::uniform_int_distribution<>(0, 2)(gen); + int angle = std::uniform_int_distribution<>(0, 90)(gen); // 角度范围0-90度 + + // 构建表达式 + question = numbers[0]; + for (size_t i = 0; i < operators.size(); i++) { + // 在指定位置插入三角函数 + if (static_cast(i) == trigPos) { + std::wstring trigExpr = trigFunctions[trigType] + L"(" + std::to_wstring(angle) + L"°)"; + question = trigExpr + L" " + operators[i] + L" " + question; + } else { + // 简单的括号逻辑 + if (i < operators.size() - 1 && (operators[i] == L"+" || operators[i] == L"-") && + (operators[i+1] == L"×" || operators[i+1] == L"÷")) { + question = L"(" + question + L")"; + } + question += L" " + operators[i] + L" " + numbers[i + 1]; + } + } + + // 如果三角函数在最后一个位置 + if (trigPos == static_cast(operators.size())) { + std::wstring trigExpr = trigFunctions[trigType] + L"(" + std::to_wstring(angle) + L"°)"; + question = trigExpr + L" " + operators.back() + L" " + question; + } + + return question + L" = ?"; // 只有多个操作数时才在这里添加 = ? + } +} + +//题目查重的实现 +bool QuestionGenerator::IsQuestionUnique(const std::wstring& question) { + return generatedQuestions.find(question) == generatedQuestions.end(); +} + +//生成指定数量题目的实现 +std::vector QuestionGenerator::generateQuestions(int count) { + // 验证题目数量范围 + if (count < 10 || count > 30) { + qDebug() << "QuestionGenerator: 题目数量超出范围(10-30),使用默认值10"; + count = 10; + } + + std::vector questions; + int attempts = 0; + const int maxAttempts = count * 10; // 增加尝试次数 + + for (int i = 0; i < count && attempts < maxAttempts; i++) { + std::wstring question; + + switch(currentGrade) { + case Grade::PRIMARY: + question = generatePrimaryQuestion(); + break; + case Grade::JUNIOR: + question = generateJuniorQuestion(); + break; + case Grade::SENIOR: + question = generateSeniorQuestion(); + break; + } + + if (IsQuestionUnique(question)) { + questions.push_back(question); + generatedQuestions.insert(question); + qDebug() << "生成题目" << i + 1 << ":" << QString::fromStdWString(question); + } else { + i--; + qDebug() << "题目重复,重新生成"; + } + attempts++; + } + + if (static_cast(questions.size()) < count) { + qDebug() << "QuestionGenerator: 警告:只生成了" << questions.size() << "个唯一题目"; + } + + qDebug() << "QuestionGenerator: 成功生成" << questions.size() << "个题目"; + return questions; +} + +// 计算小学表达式结果 - 改进版本 +double QuestionGenerator::calculatePrimaryExpression(const std::wstring& expr) { + try { + // 简单的表达式计算,支持基础运算 + std::wstring expression = expr; + + // 移除空格 + expression.erase(std::remove(expression.begin(), expression.end(), L' '), expression.end()); + + // 简单的递归计算函数 + std::function calculate = [&](const std::wstring& str) -> double { + if (str.empty()) return 0.0; + + // 处理括号 + if (str.front() == L'(' && str.back() == L')') { + return calculate(str.substr(1, str.length() - 2)); + } + + // 查找最低优先级的运算符 + int parenCount = 0; + int lowestPriority = -1; + int lowestPos = -1; + + for (int i = static_cast(str.length()) - 1; i >= 0; i--) { + wchar_t c = str[i]; + if (c == L')') parenCount++; + else if (c == L'(') parenCount--; + else if (parenCount == 0) { + if ((c == L'+' || c == L'-') && lowestPriority < 1) { + lowestPriority = 1; + lowestPos = i; + } else if ((c == L'×' || c == L'÷') && lowestPriority < 0) { + lowestPriority = 0; + lowestPos = i; + } + } + } + + if (lowestPos != -1) { + std::wstring left = str.substr(0, lowestPos); + std::wstring right = str.substr(lowestPos + 1); + wchar_t op = str[lowestPos]; + + double leftVal = calculate(left); + double rightVal = calculate(right); + + switch(op) { + case L'+': return leftVal + rightVal; + case L'-': return leftVal - rightVal; + case L'×': return leftVal * rightVal; + case L'÷': + if (rightVal == 0) { + qDebug() << "除零错误"; + return 0.0; + } + return leftVal / rightVal; + } + } + + // 没有运算符,直接解析数字 + try { + return std::stod(str); + } catch (...) { + qDebug() << "无法解析数字:" << QString::fromStdWString(str); + return 0.0; + } + }; + + return calculate(expression); + } catch (const std::exception& e) { + qDebug() << "计算小学表达式异常:" << e.what(); + return 0.0; + } +} + +// 计算初中表达式结果 - 改进版本 +double QuestionGenerator::calculateJuniorExpression(const std::wstring& expr) { + try { + std::wstring expression = expr; + + // 处理平方运算 + size_t squarePos = expression.find(L"²"); + while (squarePos != std::wstring::npos) { + // 找到平方的基数 + int start = static_cast(squarePos) - 1; + while (start >= 0 && (std::iswdigit(expression[start]) || expression[start] == L')')) { + start--; + } + if (start >= 0 && expression[start] == L'(') start--; + + std::wstring baseStr = expression.substr(start + 1, squarePos - start - 1); + double base = calculatePrimaryExpression(baseStr); + double result = base * base; + + // 替换平方部分 + expression.replace(start + 1, squarePos - start + 1, std::to_wstring(result)); + squarePos = expression.find(L"²"); + } + + // 处理开方运算 + size_t sqrtPos = expression.find(L"√"); + while (sqrtPos != std::wstring::npos) { + // 找到被开方数 + size_t end = sqrtPos + 1; + while (end < expression.length() && (std::iswdigit(expression[end]) || expression[end] == L'(')) { + end++; + } + + std::wstring radicandStr = expression.substr(sqrtPos + 1, end - sqrtPos - 1); + double radicand = calculatePrimaryExpression(radicandStr); + if (radicand < 0) { + qDebug() << "负数开方错误"; + return 0.0; + } + double result = std::sqrt(radicand); + + // 替换开方部分 + expression.replace(sqrtPos, end - sqrtPos, std::to_wstring(result)); + sqrtPos = expression.find(L"√"); + } + + return calculatePrimaryExpression(expression); + } catch (const std::exception& e) { + qDebug() << "计算初中表达式异常:" << e.what(); + return 0.0; + } +} + +// 计算高中表达式结果 - 改进版本 +double QuestionGenerator::calculateSeniorExpression(const std::wstring& expr) { + try { + std::wstring expression = expr; + + // 处理三角函数 + std::vector trigFuncs = {L"sin", L"cos", L"tan"}; + + for (const auto& func : trigFuncs) { + size_t pos = expression.find(func); + while (pos != std::wstring::npos) { + // 找到括号内的角度 + size_t parenStart = pos + func.length(); + if (parenStart >= expression.length() || expression[parenStart] != L'(') { + break; + } + + size_t parenEnd = expression.find(L')', parenStart); + if (parenEnd == std::wstring::npos) { + break; + } + + std::wstring angleStr = expression.substr(parenStart + 1, parenEnd - parenStart - 1); + // 移除度符号 + if (!angleStr.empty() && angleStr.back() == L'°') { + angleStr.pop_back(); + } + + double angle = std::stod(angleStr); + double result = 0.0; + + // 转换为弧度并计算 + double radians = angle * M_PI / 180.0; + if (func == L"sin") { + result = std::sin(radians); + } else if (func == L"cos") { + result = std::cos(radians); + } else if (func == L"tan") { + if (std::abs(std::cos(radians)) < 1e-10) { + qDebug() << "tan函数计算错误,角度:" << angle; + return 0.0; + } + result = std::tan(radians); + } + + // 替换三角函数部分 + expression.replace(pos, parenEnd - pos + 1, std::to_wstring(result)); + pos = expression.find(func); + } + } + + return calculateJuniorExpression(expression); + } catch (const std::exception& e) { + qDebug() << "计算高中表达式异常:" << e.what(); + return 0.0; + } +} + +// 计算表达式结果 +double QuestionGenerator::calculateExpression(const std::wstring& expression) { + try { + switch(currentGrade) { + case Grade::PRIMARY: + return calculatePrimaryExpression(expression); + case Grade::JUNIOR: + return calculateJuniorExpression(expression); + case Grade::SENIOR: + return calculateSeniorExpression(expression); + default: + return 0.0; + } + } catch (const std::exception& e) { + qDebug() << "计算表达式异常:" << e.what(); + return 0.0; + } +} + +// 计算正确答案 +double QuestionGenerator::calculateCorrectAnswer(const std::wstring& question) { + try { + // 移除 "= ?" 部分 + std::wstring expression = question; + size_t pos = expression.find(L" = ?"); + if (pos != std::wstring::npos) { + expression = expression.substr(0, pos); + } + + qDebug() << "计算题目答案:" << QString::fromStdWString(expression); + double result = calculateExpression(expression); + qDebug() << "计算结果:" << result; + return result; + } catch (const std::exception& e) { + qDebug() << "计算正确答案异常:" << e.what(); + return 0.0; + } +} + +// 生成有意义的选项 +std::vector QuestionGenerator::generateMeaningfulOptions(double correctAnswer) { + try { + std::vector options; + std::uniform_real_distribution dis(-2.0, 2.0); + std::set usedValues; + + // 格式化正确答案 + std::stringstream oss; + oss << std::fixed << std::setprecision(6) << correctAnswer; + std::string correctStr = oss.str(); + // 移除末尾多余的0 + correctStr.erase(correctStr.find_last_not_of('0') + 1, std::string::npos); + if (!correctStr.empty() && correctStr.back() == '.') { + correctStr.pop_back(); + } + + std::wstring wcorrectStr(correctStr.begin(), correctStr.end()); + options.push_back(wcorrectStr); + usedValues.insert(correctAnswer); + + // 生成有意义的干扰项 + for (int i = 0; i < 3; ++i) { + double variation = dis(gen); + double wrongAnswer = correctAnswer + variation; + + // 确保干扰项与正确答案不同且唯一 + int attempts = 0; + while ((std::abs(wrongAnswer - correctAnswer) < 0.001 || + usedValues.find(wrongAnswer) != usedValues.end()) && + attempts < 10) { + variation = dis(gen); + wrongAnswer = correctAnswer + variation; + attempts++; + } + + usedValues.insert(wrongAnswer); + + std::stringstream wrongOss; + wrongOss << std::fixed << std::setprecision(6) << wrongAnswer; + std::string wrongStr = wrongOss.str(); + // 移除末尾多余的0 + wrongStr.erase(wrongStr.find_last_not_of('0') + 1, std::string::npos); + if (!wrongStr.empty() && wrongStr.back() == '.') { + wrongStr.pop_back(); + } + + std::wstring wwrongStr(wrongStr.begin(), wrongStr.end()); + options.push_back(wwrongStr); + } + + // 随机打乱选项顺序 + std::shuffle(options.begin(), options.end(), gen); + + return options; + } catch (const std::exception& e) { + qDebug() << "生成选项异常:" << e.what(); + // 返回默认选项 + return {L"1.0", L"2.0", L"3.0", L"4.0"}; + } +} + +//生成题目和选项的实现(用于GUI考试) +QuestionGenerator::QuestionWithOptions QuestionGenerator::generateQuestionWithOptions() { + QuestionWithOptions qwo; + + try { + // 生成题目 + switch(currentGrade) { + case Grade::PRIMARY: + qwo.question = generatePrimaryQuestion(); + break; + case Grade::JUNIOR: + qwo.question = generateJuniorQuestion(); + break; + case Grade::SENIOR: + qwo.question = generateSeniorQuestion(); + break; + } + + // 计算正确答案并生成有意义的选项 + double correctAnswer = calculateCorrectAnswer(qwo.question); + qwo.options = generateMeaningfulOptions(correctAnswer); + + // 找到正确答案的索引 + std::stringstream oss; + oss << std::fixed << std::setprecision(6) << correctAnswer; + std::string correctStr = oss.str(); + std::wstring wcorrectStr(correctStr.begin(), correctStr.end()); + + for (size_t i = 0; i < qwo.options.size(); ++i) { + if (qwo.options[i] == wcorrectStr) { + qwo.correctIndex = static_cast(i); + break; + } + } + } catch (const std::exception& e) { + qDebug() << "生成题目和选项异常:" << e.what(); + // 返回默认题目 + qwo.question = L"1 + 1 = ?"; + qwo.options = {L"1.0", L"2.0", L"3.0", L"4.0"}; + qwo.correctIndex = 1; + } + + return qwo; +} + +//清空历史记录的实现 +void QuestionGenerator::clearHistory() { + generatedQuestions.clear(); +} diff --git a/src/back/questiongenerator.h b/src/back/questiongenerator.h new file mode 100644 index 0000000..487b79c --- /dev/null +++ b/src/back/questiongenerator.h @@ -0,0 +1,78 @@ +#ifndef QUESTIONGENERATOR_H +#define QUESTIONGENERATOR_H + +#include "user.h" +#include +#include +#include +#include +#include +#include +#include + +//题目生成器类,根据年级生成不同难度的数学题目 +//支持小学、初中、高中三个级别的题目生成,包含查重功能。 +class QuestionGenerator { +private: + Grade currentGrade; + std::set generatedQuestions; + std::random_device rd; + std::mt19937 gen; + + //生成小学级别的基础算术题目 + std::wstring generatePrimaryQuestion(); + + //生成初中级别的题目,包含平方或开根号 + std::wstring generateJuniorQuestion(); + + //生成高中级别的题目,包含三角函数 + std::wstring generateSeniorQuestion(); + + //生成指定范围内的随机整数 + std::wstring generateNumber(int min, int max); + + //随机生成算术运算符 + std::wstring generateOperator(); + + //检查题目是否已生成过(查重) + bool IsQuestionUnique(const std::wstring& question); + + //括号需求判断 + bool needsParentheses(const std::wstring& expr, const std::wstring& nextOp); + + // 计算表达式结果 + double calculateExpression(const std::wstring& expression); + double calculatePrimaryExpression(const std::wstring& expr); + double calculateJuniorExpression(const std::wstring& expr); + double calculateSeniorExpression(const std::wstring& expr); + +public: + //构造函数,初始化题目生成器 + QuestionGenerator(); + + //设置当前题目生成难度级别 + void setGrade(Grade grade); + + //生成指定数量的唯一数学题目 + std::vector generateQuestions(int count); + + //生成题目和选项(用于GUI) + struct QuestionWithOptions { + std::wstring question; + std::vector options; + int correctIndex; + }; + + QuestionWithOptions generateQuestionWithOptions(); + + //清空已生成题目的历史记录 + void clearHistory(); + + // 计算正确答案 + double calculateCorrectAnswer(const std::wstring& question); + + // 生成有意义的选项 + std::vector generateMeaningfulOptions(double correctAnswer); +}; + +#endif diff --git a/src/back/user.cpp b/src/back/user.cpp new file mode 100644 index 0000000..65b0f8d --- /dev/null +++ b/src/back/user.cpp @@ -0,0 +1,41 @@ +#include "user.h" +#include + +//用户构造函数的实现 +User::User(const std::wstring& name, const std::wstring& pass, Grade grade) + : username(name), password(pass), gradeType(grade) {} + +//用户验证的实现 +bool User::check(const std::wstring& name, const std::wstring& pass) const { + return (username == name && password == pass); +} + +//获取用户名的实现 +std::wstring User::getUsername() const { + return username; +} + +//获取密码的实现 +std::wstring User::getPassword() const { + return password; +} + +//获取年级枚举值的实现 +Grade User::getGrade() const { + return gradeType; +} + +//获取年级字符串的实现 +std::wstring User::getGradeString() const { + switch(gradeType) { + case Grade::PRIMARY: return L"小学"; + case Grade::JUNIOR: return L"初中"; + case Grade::SENIOR: return L"高中"; + default: return L"未知"; + } +} + +//设置密码的实现 +void User::setPassword(const std::wstring& newPassword) { + password = newPassword; +} \ No newline at end of file diff --git a/src/back/user.h b/src/back/user.h new file mode 100644 index 0000000..554141d --- /dev/null +++ b/src/back/user.h @@ -0,0 +1,41 @@ +#ifndef USER_H +#define USER_H + +#include + +//年级枚举,表示用户所属的学段级别 +//用于区分不同学段的题目难度和生成规则 +enum class Grade { PRIMARY, JUNIOR, SENIOR }; + +//用户信息类,封装用户认证数据和年级信息 +//存储用户名、密码和年级类型,提供验证和查询接口。 +class User { +private: + std::wstring username; + std::wstring password; + Grade gradeType; + +public: + //构造函数,初始化用户信息 + User(const std::wstring& name, const std::wstring& pass, Grade grade); + + //验证用户名和密码是否匹配 + bool check(const std::wstring& name, const std::wstring& pass) const; + + //获取用户名 + std::wstring getUsername() const; + + //获取密码 + std::wstring getPassword() const; + + //获取年级枚举值 + Grade getGrade() const; + + //获取年级的中文描述 + std::wstring getGradeString() const; + + //设置密码 + void setPassword(const std::wstring& newPassword); +}; + +#endif \ No newline at end of file diff --git a/src/back/usermanage.cpp b/src/back/usermanage.cpp new file mode 100644 index 0000000..4fcba32 --- /dev/null +++ b/src/back/usermanage.cpp @@ -0,0 +1,431 @@ +#include "usermanage.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // 添加进程支持 +#include // 添加目录支持 + +// 初始化用户数据的实现 +void UserManager::initializeUsers() { + // 如果文件中有用户数据,就不初始化默认用户 + if (loadUsersFromFile()) { + qDebug() << "UserManager: 从文件加载用户数据成功"; + return; + } + + qDebug() << "UserManager: 初始化默认用户数据"; + + // 初始化默认测试用户(使用英文用户名避免编码问题) + users.emplace_back(L"zhangsan1", L"Abc123", Grade::PRIMARY); + users.emplace_back(L"zhangsan2", L"Abc123", Grade::PRIMARY); + users.emplace_back(L"zhangsan3", L"Abc123", Grade::PRIMARY); + + users.emplace_back(L"lisi1", L"Abc123", Grade::JUNIOR); + users.emplace_back(L"lisi2", L"Abc123", Grade::JUNIOR); + users.emplace_back(L"lisi3", L"Abc123", Grade::JUNIOR); + + users.emplace_back(L"wangwu1", L"Abc123", Grade::SENIOR); + users.emplace_back(L"wangwu2", L"Abc123", Grade::SENIOR); + users.emplace_back(L"wangwu3", L"Abc123", Grade::SENIOR); + + // 保存到文件 + if (saveUsersToFile()) { + qDebug() << "UserManager: 默认用户数据保存成功"; + } else { + qDebug() << "UserManager: 默认用户数据保存失败"; + } +} + +// 用户管理器的构造实现 +UserManager::UserManager() { + qDebug() << "UserManager: 构造函数调用"; + initializeUsers(); +} + +// 用户认证的实现 +User* UserManager::authenticateUser(const std::wstring& username, const std::wstring& password) { + qDebug() << "UserManager: 尝试认证用户:" << QString::fromStdWString(username); + + // 重新加载用户数据,确保包含新注册的用户 + loadUsersFromFile(); + + for (auto& user : users) { + if (user.check(username, password)) { + qDebug() << "UserManager: 用户认证成功"; + return &user; + } + } + + qDebug() << "UserManager: 用户认证失败"; + return nullptr; +} + +// 解析用户类型的实现 +Grade UserManager::parseUserType(const std::wstring& type) { + if (type == L"小学" || type == L"Primary") return Grade::PRIMARY; + if (type == L"初中" || type == L"Junior") return Grade::JUNIOR; + if (type == L"高中" || type == L"Senior") return Grade::SENIOR; + return Grade::PRIMARY; +} + +// 注册新用户的实现 +bool UserManager::registerUser(const std::wstring& username, const std::wstring& password, const std::wstring& grade) { + qDebug() << "UserManager: 尝试注册用户:" << QString::fromStdWString(username) << "年级:" << QString::fromStdWString(grade); + + // 重新加载用户数据,确保检查最新的用户名 + loadUsersFromFile(); + + // 检查用户名是否已存在 + if (isUsernameExists(username)) { + qDebug() << "UserManager: 用户名已存在"; + return false; + } + + Grade userGrade = parseUserType(grade); + users.emplace_back(username, password, userGrade); + + qDebug() << "UserManager: 用户添加到内存,准备保存到文件"; + + // 保存到文件 + bool result = saveUsersToFile(); + if (result) { + qDebug() << "UserManager: 用户注册成功"; + // 重新加载确保数据同步 + loadUsersFromFile(); + } else { + qDebug() << "UserManager: 用户注册失败 - 文件保存失败"; + } + + return result; +} + +// 修改用户密码的实现 +bool UserManager::changePassword(const std::wstring& username, const std::wstring& oldPassword, const std::wstring& newPassword) { + for (auto& user : users) { + if (user.check(username, oldPassword)) { + user.setPassword(newPassword); + return saveUsersToFile(); + } + } + return false; +} + +// 检查用户名是否存在的实现 +bool UserManager::isUsernameExists(const std::wstring& username) { + for (const auto& user : users) { + if (user.getUsername() == username) { + return true; + } + } + return false; +} + +// 从文件加载用户数据的实现 +bool UserManager::loadUsersFromFile() { + std::ifstream file(userFile); + if (!file.is_open()) { + qDebug() << "UserManager: 无法打开用户文件:" << userFile.c_str(); + return false; + } + + users.clear(); + std::string line; + int userCount = 0; + + while (std::getline(file, line)) { + std::istringstream iss(line); + std::string username, password, gradeStr; + + if (iss >> username >> password >> gradeStr) { + // 转换为宽字符串 + std::wstring wusername, wpassword, wgradeStr; + for (char c : username) wusername += wchar_t(c); + for (char c : password) wpassword += wchar_t(c); + for (char c : gradeStr) wgradeStr += wchar_t(c); + + Grade grade = parseUserType(wgradeStr); + users.emplace_back(wusername, wpassword, grade); + userCount++; + qDebug() << "加载用户:" << QString::fromStdWString(wusername) << "年级:" << QString::fromStdWString(wgradeStr); + } + } + + file.close(); + qDebug() << "UserManager: 从文件加载了" << userCount << "个用户"; + return !users.empty(); +} + +// 保存用户数据到文件的实现 +bool UserManager::saveUsersToFile() { + std::ofstream file(userFile); + if (!file.is_open()) { + qDebug() << "UserManager: 无法创建用户文件:" << userFile.c_str(); + return false; + } + + int savedCount = 0; + for (const auto& user : users) { + std::wstring username = user.getUsername(); + std::wstring password = user.getPassword(); + std::wstring gradeStr = user.getGradeString(); + + // 转换为普通字符串 + std::string username_str, password_str, grade_str; + for (wchar_t wc : username) username_str += static_cast(wc); + for (wchar_t wc : password) password_str += static_cast(wc); + for (wchar_t wc : gradeStr) grade_str += static_cast(wc); + + file << username_str << " " << password_str << " " << grade_str << "\n"; + savedCount++; + } + + file.close(); + qDebug() << "UserManager: 保存了" << savedCount << "个用户到文件"; + return true; +} + +// 生成验证码的实现 - 修复为每次都不同 +std::wstring UserManager::generateVerificationCode(int length) { + const std::wstring chars = L"0123456789"; + std::wstring code; + + // 使用当前时间作为随机种子,确保每次不同 + std::random_device rd; + std::mt19937 gen(rd() + static_cast(std::time(nullptr))); + std::uniform_int_distribution<> dis(0, chars.length() - 1); + + for (int i = 0; i < length; ++i) { + code += chars[dis(gen)]; + } + + qDebug() << "UserManager: 生成验证码:" << QString::fromStdWString(code); + return code; +} + +// 发送验证码的实现 - 添加真实邮件发送功能 +bool UserManager::sendVerificationCode(const std::wstring& email, std::wstring& generatedCode) { + qDebug() << "UserManager: 发送验证码到:" << QString::fromStdWString(email); + + // 生成验证码 + generatedCode = generateVerificationCode(); + + // 设置过期时间(10分钟) + auto expiryTime = std::chrono::system_clock::now() + std::chrono::minutes(10); + + // 存储验证码信息 + emailVerifications[email] = {generatedCode, expiryTime, false}; + + qDebug() << "UserManager: 验证码已存储,有效期10分钟"; + + // 尝试发送真实邮件 + if (sendRealEmail(email, generatedCode)) { + qDebug() << "UserManager: 验证码邮件发送成功"; + return true; + } else { + // 如果真实邮件发送失败,使用控制台输出 + std::wcout << L"[邮件模拟] 发送到: " << email << L" 的验证码: " << generatedCode << std::endl; + qDebug() << "UserManager: 真实邮件发送失败,使用模拟模式"; + + // 在调试窗口显示验证码信息 + QString qEmail = QString::fromStdWString(email); + QString qCode = QString::fromStdWString(generatedCode); + qDebug() << "=== 邮件发送模拟 ==="; + qDebug() << "收件人:" << qEmail; + qDebug() << "验证码:" << qCode; + qDebug() << "=== 邮件发送模拟 ==="; + + return true; + } +} + +// 验证邮箱验证码的实现 +bool UserManager::verifyEmailCode(const std::wstring& email, const std::wstring& code) { + qDebug() << "UserManager: 验证邮箱验证码,邮箱:" << QString::fromStdWString(email) + << "验证码:" << QString::fromStdWString(code); + + auto it = emailVerifications.find(email); + if (it == emailVerifications.end()) { + qDebug() << "UserManager: 没有找到该邮箱的验证码记录"; + return false; // 没有找到该邮箱的验证码记录 + } + + EmailVerification& verification = it->second; + + // 检查验证码是否已使用 + if (verification.used) { + qDebug() << "UserManager: 验证码已被使用"; + return false; + } + + // 检查验证码是否过期 + if (std::chrono::system_clock::now() > verification.expiryTime) { + qDebug() << "UserManager: 验证码已过期"; + emailVerifications.erase(it); + return false; + } + + // 检查验证码是否匹配 + if (verification.code == code) { + qDebug() << "UserManager: 验证码验证成功"; + verification.used = true; // 标记为已使用 + return true; + } + + qDebug() << "UserManager: 验证码不匹配"; + return false; +} + +// 真实邮件发送功能 - 仅使用 PowerShell +bool UserManager::sendRealEmail(const std::wstring& email, const std::wstring& code) { + QString qEmail = QString::fromStdWString(email); + QString qCode = QString::fromStdWString(code); + + qDebug() << "尝试使用 PowerShell 发送邮件到:" << qEmail; + + return sendEmailViaPowerShell(qEmail, qCode); +} + +// 辅助函数:安全地转义字符串中的单引号 +QString escapeSingleQuotes(const QString& input) { + QString result = input; + return result.replace("'", "''"); +} + +// 使用 PowerShell Send-MailMessage 发送邮件 +bool UserManager::sendEmailViaPowerShell(const QString& toEmail, const QString& code) { + qDebug() << "使用 PowerShell 发送邮件..."; + + // ==================== 配置区域 ==================== + // 请根据你的邮箱服务商修改以下配置: + + QString smtpServer = "smtp.qq.com"; // SMTP服务器 + int smtpPort = 587; // 端口号 + QString fromEmail = "1453386832@qq.com"; // ⬅️ 修改:发件人邮箱 + QString username = "1453386832@qq.com"; // ⬅️ 修改:邮箱账号 + QString password = "nijuqetihpojffag"; // ⬅️ 修改:邮箱授权码 + + /* + // 其他邮箱配置示例: + + // 163邮箱配置: + // QString smtpServer = "smtp.163.com"; + // int smtpPort = 25; // 或 587 + // QString fromEmail = "your_email@163.com"; + // QString username = "your_email@163.com"; + // QString password = "your_163_authorization_code"; + + // Gmail配置: + // QString smtpServer = "smtp.gmail.com"; + // int smtpPort = 587; + // QString fromEmail = "your_email@gmail.com"; + // QString username = "your_email@gmail.com"; + // QString password = "your_gmail_app_password"; + */ + // ==================== 配置结束 ==================== + + QString subject = "Math Learning Software - Verification Code"; + QString body = QString( + "Dear User:\n\n" + "You are registering for Math Learning Software. Your verification code is: %1\n\n" + "The verification code is valid for 10 minutes. Please complete your registration as soon as possible.\n\n" + "If this was not your operation, please ignore this email.\n\n" + "Math Learning Software Team" + ).arg(code); + + // 安全地转义所有字符串 + QString safePassword = escapeSingleQuotes(password); + QString safeUsername = escapeSingleQuotes(username); + QString safeFromEmail = escapeSingleQuotes(fromEmail); + QString safeToEmail = escapeSingleQuotes(toEmail); + QString safeSubject = escapeSingleQuotes(subject); + QString safeBody = escapeSingleQuotes(body); + QString safeSmtpServer = escapeSingleQuotes(smtpServer); + + // 构建 PowerShell 命令 + QString powerShellScript = QString( + "$secpasswd = ConvertTo-SecureString \"%1\" -AsPlainText -Force\n" + "$credential = New-Object System.Management.Automation.PSCredential(\"%2\", $secpasswd)\n" + "Send-MailMessage -From '%3' -To '%4' -Subject '%5' -Body '%6' -SmtpServer '%7' -Port %8 -Credential $credential -UseSsl" + ).arg(safePassword, + safeUsername, + safeFromEmail, + safeToEmail, + safeSubject, + safeBody, + safeSmtpServer, + QString::number(smtpPort)); + + // 创建临时 PowerShell 脚本文件 + QString tempScriptFile = QDir::tempPath() + "/send_email.ps1"; + QFile scriptFile(tempScriptFile); + + if (!scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qDebug() << "无法创建临时 PowerShell 脚本文件"; + return false; + } + + QTextStream out(&scriptFile); + out << powerShellScript; + scriptFile.close(); + + qDebug() << "执行 PowerShell 脚本..."; + + // 执行 PowerShell 脚本 + QProcess process; + process.start("powershell", QStringList() << "-ExecutionPolicy" << "Bypass" << "-File" << tempScriptFile); + + if (!process.waitForStarted(10000)) { + qDebug() << "无法启动 PowerShell 进程"; + QFile::remove(tempScriptFile); + return false; + } + + if (!process.waitForFinished(60000)) { // 等待60秒 + qDebug() << "PowerShell 进程超时"; + process.kill(); + QFile::remove(tempScriptFile); + return false; + } + + int exitCode = process.exitCode(); + QByteArray output = process.readAllStandardOutput(); + QByteArray error = process.readAllStandardError(); + + qDebug() << "PowerShell 退出代码:" << exitCode; + + if (!output.isEmpty()) { + qDebug() << "PowerShell 输出:" << output; + } + + if (!error.isEmpty()) { + qDebug() << "PowerShell 错误:" << error; + } + + // 清理临时文件 + QFile::remove(tempScriptFile); + + if (exitCode == 0) { + qDebug() << "邮件发送成功!"; + return true; + } else { + qDebug() << "邮件发送失败,退出代码:" << exitCode; + + // 提供详细的错误信息 + if (error.contains("Authentication")) { + qDebug() << "错误:认证失败,请检查用户名和密码"; + } else if (error.contains("Connection")) { + qDebug() << "错误:连接失败,请检查SMTP服务器和端口"; + } else if (error.contains("SSL")) { + qDebug() << "错误:SSL连接失败"; + } + + return false; + } +} diff --git a/src/back/usermanage.h b/src/back/usermanage.h new file mode 100644 index 0000000..9427a6e --- /dev/null +++ b/src/back/usermanage.h @@ -0,0 +1,75 @@ +#ifndef USERMANAGE_H +#define USERMANAGE_H + +#include "user.h" +#include +#include +#include +#include +#include +#include +#include +#include // 添加进程支持 + +// 邮箱验证码结构 +struct EmailVerification { + std::wstring code; + std::chrono::system_clock::time_point expiryTime; + bool used; +}; + +// 用户管理器类,负责用户数据的初始化和认证管理 +class UserManager { +private: + std::vector users; + const std::string userFile = "users.dat"; + + // 邮箱验证码存储 + std::map emailVerifications; + + //初始化预定义用户数据 + void initializeUsers(); + + //从文件加载用户数据 + bool loadUsersFromFile(); + + //保存用户数据到文件 + bool saveUsersToFile(); + + // 真实邮件发送 + bool sendRealEmail(const std::wstring& email, const std::wstring& code); + + // PowerShell 邮件发送方法 + bool sendEmailViaPowerShell(const QString& toEmail, const QString& code); + +public: + //构造函数,自动初始化用户数据 + UserManager(); + + //用户认证,验证用户名和密码 + User* authenticateUser(const std::wstring& username, const std::wstring& password); + + //将中文年级字符串解析为年级枚举值 + Grade parseUserType(const std::wstring& type); + + //注册新用户 + bool registerUser(const std::wstring& username, const std::wstring& password, const std::wstring& grade); + + //修改用户密码 + bool changePassword(const std::wstring& username, const std::wstring& oldPassword, const std::wstring& newPassword); + + //检查用户名是否存在 + bool isUsernameExists(const std::wstring& username); + + // 邮箱验证相关方法 + bool sendVerificationCode(const std::wstring& email, std::wstring& generatedCode); + bool verifyEmailCode(const std::wstring& email, const std::wstring& code); + + // 生成随机验证码 + std::wstring generateVerificationCode(int length = 6); + + //获取所有用户(用于测试) + std::vector& getUsers() { return users; } +}; + +#endif -- 2.34.1 From 3450614a4b34f431dc822b3f9581a8ba3039f219 Mon Sep 17 00:00:00 2001 From: lxf450481 <705835715@qq.com> Date: Sun, 12 Oct 2025 14:39:19 +0800 Subject: [PATCH 9/9] =?UTF-8?q?=E5=88=A0=E9=99=A4=20project=20=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=A4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- project/filesaver.cpp | 205 ------------ project/filesaver.h | 63 ---- project/questiongenerator.cpp | 596 ---------------------------------- project/questiongenerator.h | 78 ----- project/user.cpp | 41 --- project/user.h | 41 --- project/usermanage.cpp | 431 ------------------------ project/usermanage.h | 75 ----- 8 files changed, 1530 deletions(-) delete mode 100644 project/filesaver.cpp delete mode 100644 project/filesaver.h delete mode 100644 project/questiongenerator.cpp delete mode 100644 project/questiongenerator.h delete mode 100644 project/user.cpp delete mode 100644 project/user.h delete mode 100644 project/usermanage.cpp delete mode 100644 project/usermanage.h diff --git a/project/filesaver.cpp b/project/filesaver.cpp deleted file mode 100644 index d9d724d..0000000 --- a/project/filesaver.cpp +++ /dev/null @@ -1,205 +0,0 @@ -#include "filesaver.h" -#include -#include -#include -#include -#include - -//创建目录(跨平台实现) -bool FileSaver::createDirectory(const std::wstring& wpath) { -#ifdef _WIN32 - return _wmkdir(wpath.c_str()) == 0; -#else - std::string path; - for (wchar_t wc : wpath) { - if (wc < 128) path += static_cast(wc); - } - return mkdir(path.c_str(), 0755) == 0; -#endif -} - -//将包含中文的用户名转换为安全的英文文件名 -std::string FileSaver::usernameToSafeName(const std::wstring& username) { - std::string result; - for (wchar_t wc : username) { - if ((wc >= L'0' && wc <= L'9') || (wc >= L'A' && wc <= L'Z') || - (wc >= L'a' && wc <= L'z') || wc == L'_') { - result += static_cast(wc); - } else { - switch(wc) { - case L'张': result += "Zhang"; break; - case L'三': result += "San"; break; - case L'李': result += "Li"; break; - case L'四': result += "Si"; break; - case L'王': result += "Wang"; break; - case L'五': result += "Wu"; break; - default: result += "User"; break; - } - } - } - return result; -} - -//将中文年级转换为英文标识 -std::string FileSaver::gradeToEnglish(const std::wstring& grade) { - if (grade == L"小学") return "Primary"; - if (grade == L"初中") return "Junior"; - if (grade == L"高中") return "Senior"; - return "Unknown"; -} - -//宽字符串到普通字符串的转换 -std::string FileSaver::wstringToString(const std::wstring& wstr) { - std::string result; - for (wchar_t wc : wstr) { - if (wc < 128) { - result += static_cast(wc); - } - } - return result; -} - -//将题目列表写入文件的实现 -bool FileSaver::writeQuestionsToFile(const std::string& filename, - const std::string& safeUsername, - const std::string& englishGrade, - const std::vector& questions) { - std::ofstream file(filename); - if (!file.is_open()) { - return false; - } - - file << "Math Exam Paper\n"; - file << "Username: " << safeUsername << "\n"; - file << "Grade: " << englishGrade << "\n"; - file << "Time: " << wstringToString(getCurrentTime()) << "\n\n"; - - for (size_t i = 0; i < questions.size(); i++) { - file << (i + 1) << ". "; - - for (wchar_t wc : questions[i]) { - switch(wc) { - case L'√': file << "√"; break; - case L'²': file << "²"; break; - case L's': file << "s"; break; - case L'i': file << "i"; break; - case L'n': file << "n"; break; - case L'c': file << "c"; break; - case L'o': file << "o"; break; - case L't': file << "t"; break; - case L'a': file << "a"; break; - case L'(': file << "("; break; - case L')': file << ")"; break; - case L'+': file << "+"; break; - case L'-': file << "-"; break; - case L'×': file << "×"; break; - case L'÷': file << "÷"; break; - case L'=': file << "="; break; - case L'?': file << "?"; break; - case L' ': file << " "; break; - case L'0': case L'1': case L'2': case L'3': case L'4': - case L'5': case L'6': case L'7': case L'8': case L'9': - file << static_cast(wc); - break; - default: file << static_cast(wc); break; - } - } - file << "\n\n"; - } - - file.close(); - return true; -} - -//准备文件和目录的实现 -std::string FileSaver::prepareFileAndDirectory(const std::wstring& username, - const std::wstring& grade) { - std::string safeUsername = usernameToSafeName(username); - std::string englishGrade = gradeToEnglish(grade); - std::string folderPath = "exam_papers/" + safeUsername + "_" + englishGrade + "_Papers"; - - // 创建目录 - std::wstring wfolderPath; - for (char c : folderPath) { - wfolderPath += wchar_t(c); - } - createDirectory(wfolderPath); - - return folderPath + "/" + wstringToString(generateFilename()) + ".txt"; -} - -//保存题目的完整流程实现 -bool FileSaver::saveToFile(const std::wstring& username, - const std::wstring& grade, - const std::vector& questions) { - std::string safeUsername = usernameToSafeName(username); - std::string englishGrade = gradeToEnglish(grade); - std::string filename = prepareFileAndDirectory(username, grade); - - bool success = writeQuestionsToFile(filename, safeUsername, englishGrade, questions); - - return success; -} - -//生成基于时间戳的文件名实现 -std::wstring FileSaver::generateFilename() { - auto now = std::chrono::system_clock::now(); - auto time_t = std::chrono::system_clock::to_time_t(now); - - std::tm tm = *std::localtime(&time_t); - std::wostringstream oss; - - oss << tm.tm_year + 1900 << L"_" - << (tm.tm_mon + 1 < 10 ? L"0" : L"") << tm.tm_mon + 1 << L"_" - << (tm.tm_mday < 10 ? L"0" : L"") << tm.tm_mday << L"_" - << (tm.tm_hour < 10 ? L"0" : L"") << tm.tm_hour << L"_" - << (tm.tm_min < 10 ? L"0" : L"") << tm.tm_min << L"_" - << (tm.tm_sec < 10 ? L"0" : L"") << tm.tm_sec; - - return oss.str(); -} - -//获取当前格式化时间的实现 -std::wstring FileSaver::getCurrentTime() { - auto now = std::chrono::system_clock::now(); - auto time_t = std::chrono::system_clock::to_time_t(now); - - std::tm tm = *std::localtime(&time_t); - std::wostringstream oss; - - oss << tm.tm_year + 1900 << L"-" - << (tm.tm_mon + 1 < 10 ? L"0" : L"") << tm.tm_mon + 1 << L"-" - << (tm.tm_mday < 10 ? L"0" : L"") << tm.tm_mday << L" " - << (tm.tm_hour < 10 ? L"0" : L"") << tm.tm_hour << L":" - << (tm.tm_min < 10 ? L"0" : L"") << tm.tm_min << L":" - << (tm.tm_sec < 10 ? L"0" : L"") << tm.tm_sec; - - return oss.str(); -} - -//保存考试记录的实现 -bool FileSaver::saveExamRecord(const std::wstring& username, - const std::wstring& grade, - int score, - int totalQuestions, - const std::wstring& examDate) { - std::string safeUsername = usernameToSafeName(username); - std::string filename = "exam_records/" + safeUsername + "_records.txt"; - - // 创建目录 - createDirectory(L"exam_records"); - - std::ofstream file(filename, std::ios::app); - if (!file.is_open()) { - return false; - } - - file << "Date: " << wstringToString(examDate) << "\n"; - file << "Grade: " << gradeToEnglish(grade) << "\n"; - file << "Score: " << score << "/" << totalQuestions << "\n"; - file << "Percentage: " << (score * 100.0 / totalQuestions) << "%\n"; - file << "------------------------\n"; - - file.close(); - return true; -} diff --git a/project/filesaver.h b/project/filesaver.h deleted file mode 100644 index 4f56cc2..0000000 --- a/project/filesaver.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef FILESAVER_H -#define FILESAVER_H - -#include -#include -#include -#include -#include - -#ifdef _WIN32 - #include - #include -#else - #include - #include -#endif - -// 文件保存器类,负责将生成的数学题目保存到文件系统 -class FileSaver { -public: - //将题目列表写入指定文件 - static bool writeQuestionsToFile(const std::string& filename, - const std::string& safeUsername, - const std::string& englishGrade, - const std::vector& questions); - - //准备用户专属文件夹和文件路径 - static std::string prepareFileAndDirectory(const std::wstring& username, - const std::wstring& grade); - - //保存题目列表到用户专属文件 - static bool saveToFile(const std::wstring& username, - const std::wstring& grade, - const std::vector& questions); - - //生成基于当前时间戳的文件名 - static std::wstring generateFilename(); - - //获取当前格式化的时间字符串 - static std::wstring getCurrentTime(); - - //保存考试记录 - static bool saveExamRecord(const std::wstring& username, - const std::wstring& grade, - int score, - int totalQuestions, - const std::wstring& examDate); - -private: - //将包含中文的用户名转换为安全的英文文件名 - static std::string usernameToSafeName(const std::wstring& username); - - //将中文年级转换为英文标识 - static std::string gradeToEnglish(const std::wstring& grade); - - //宽字符串到普通字符串的转换 - static std::string wstringToString(const std::wstring& wstr); - - //创建目录(跨平台实现) - static bool createDirectory(const std::wstring& wpath); -}; - -#endif \ No newline at end of file diff --git a/project/questiongenerator.cpp b/project/questiongenerator.cpp deleted file mode 100644 index d19e285..0000000 --- a/project/questiongenerator.cpp +++ /dev/null @@ -1,596 +0,0 @@ -#include "questiongenerator.h" -#include -#include -#include -#include -#include -#include -#include -#include - -//题目生成器构造函数的实现 -QuestionGenerator::QuestionGenerator() : currentGrade(Grade::PRIMARY), gen(rd()) {} - -//设置年级的实现 -void QuestionGenerator::setGrade(Grade grade) { - currentGrade = grade; - qDebug() << "QuestionGenerator: 设置年级为:" << static_cast(grade); -} - -//生成随机数的实现 -std::wstring QuestionGenerator::generateNumber(int min, int max) { - std::uniform_int_distribution<> dis(min, max); - return std::to_wstring(dis(gen)); -} - -//生成随机运算符的实现 -std::wstring QuestionGenerator::generateOperator() { - std::vector operators = {L"+", L"-", L"×", L"÷"}; - std::uniform_int_distribution<> dis(0, static_cast(operators.size()) - 1); - return operators[dis(gen)]; -} - -//括号需求判断的辅助函数 -bool QuestionGenerator::needsParentheses(const std::wstring& expr, const std::wstring& nextOp) { - if (expr.find(L"+") != std::wstring::npos || expr.find(L"-") != std::wstring::npos) { - if (nextOp == L"×" || nextOp == L"÷") { - return true; - } - } - return false; -} - -//生成小学题目的实现 - 至少2个操作数 -std::wstring QuestionGenerator::generatePrimaryQuestion() { - std::uniform_int_distribution<> operandDis(2, 5); // 2-5个操作数(至少2个) - int operandCount = operandDis(gen); - std::wstring question; - std::vector numbers; - std::vector operators; - - // 生成操作数和运算符 - for (int i = 0; i < operandCount; i++) { - numbers.push_back(generateNumber(1, 100)); // 1-100 - if (i < operandCount - 1) { - operators.push_back(generateOperator()); - } - } - - // 构建表达式 - question = numbers[0]; - for (size_t i = 0; i < operators.size(); i++) { - // 简单的括号逻辑:在需要时添加括号 - if (i < operators.size() - 1 && (operators[i] == L"+" || operators[i] == L"-") && - (operators[i+1] == L"×" || operators[i+1] == L"÷")) { - question = L"(" + question + L")"; - } - question += L" " + operators[i] + L" " + numbers[i + 1]; - } - - return question + L" = ?"; -} - -//生成初中题目的实现 - 可以为1个操作数 -std::wstring QuestionGenerator::generateJuniorQuestion() { - std::uniform_int_distribution<> operandDis(1, 5); // 1-5个操作数(可以为1) - int operandCount = operandDis(gen); - std::uniform_int_distribution<> typeDis(0, 1); // 0:平方, 1:开根号 - - std::wstring question; - - // 如果只有一个操作数,直接生成平方或开根号题目 - if (operandCount == 1) { - int specialType = typeDis(gen); - if (specialType == 0) { - // 平方 - std::wstring number = generateNumber(1, 100); - question = number + L"² = ?"; - } else { - // 开根号 - 确保是平方数 - int num = std::uniform_int_distribution<>(1, 20)(gen); - int square = num * num; - question = L"√" + std::to_wstring(square) + L" = ?"; - } - return question; // 直接返回,不再添加 = ? - } else { - // 多个操作数的情况 - std::vector numbers; - std::vector operators; - - // 生成基础操作数和运算符 - for (int i = 0; i < operandCount; i++) { - numbers.push_back(generateNumber(1, 100)); // 1-100 - if (i < operandCount - 1) { - operators.push_back(generateOperator()); - } - } - - // 随机选择一个位置插入平方或开根号 - int specialPos = std::uniform_int_distribution<>(0, operandCount - 1)(gen); - int specialType = typeDis(gen); - - // 构建表达式 - question = numbers[0]; - for (size_t i = 0; i < operators.size(); i++) { - // 在指定位置插入特殊运算符 - if (static_cast(i) == specialPos) { - if (specialType == 0) { - // 平方 - question = L"(" + question + L")²"; - } else { - // 开根号 - 确保是平方数 - int num = std::uniform_int_distribution<>(1, 20)(gen); - int square = num * num; - question = L"√" + std::to_wstring(square) + L" " + operators[i] + L" " + question; - } - } else { - // 简单的括号逻辑 - if (i < operators.size() - 1 && (operators[i] == L"+" || operators[i] == L"-") && - (operators[i+1] == L"×" || operators[i+1] == L"÷")) { - question = L"(" + question + L")"; - } - question += L" " + operators[i] + L" " + numbers[i + 1]; - } - } - - // 如果特殊运算符在最后一个位置 - if (specialPos == static_cast(operators.size())) { - if (specialType == 0) { - question = L"(" + question + L")²"; - } else { - int num = std::uniform_int_distribution<>(1, 20)(gen); - int square = num * num; - question = L"√" + std::to_wstring(square) + L" " + operators.back() + L" " + question; - } - } - - return question + L" = ?"; // 只有多个操作数时才在这里添加 = ? - } -} - -//生成高中题目的实现 - 可以为1个操作数 -std::wstring QuestionGenerator::generateSeniorQuestion() { - std::uniform_int_distribution<> operandDis(1, 5); // 1-5个操作数(可以为1) - int operandCount = operandDis(gen); - std::vector trigFunctions = {L"sin", L"cos", L"tan"}; - - std::wstring question; - - // 如果只有一个操作数,直接生成三角函数题目 - if (operandCount == 1) { - int trigType = std::uniform_int_distribution<>(0, 2)(gen); - int angle = std::uniform_int_distribution<>(0, 90)(gen); // 角度范围0-90度 - question = trigFunctions[trigType] + L"(" + std::to_wstring(angle) + L"°) = ?"; - return question; // 直接返回,不再添加 = ? - } else { - // 多个操作数的情况 - std::vector numbers; - std::vector operators; - - // 生成基础操作数和运算符 - for (int i = 0; i < operandCount; i++) { - numbers.push_back(generateNumber(1, 100)); // 1-100 - if (i < operandCount - 1) { - operators.push_back(generateOperator()); - } - } - - // 随机选择一个位置插入三角函数 - int trigPos = std::uniform_int_distribution<>(0, operandCount - 1)(gen); - int trigType = std::uniform_int_distribution<>(0, 2)(gen); - int angle = std::uniform_int_distribution<>(0, 90)(gen); // 角度范围0-90度 - - // 构建表达式 - question = numbers[0]; - for (size_t i = 0; i < operators.size(); i++) { - // 在指定位置插入三角函数 - if (static_cast(i) == trigPos) { - std::wstring trigExpr = trigFunctions[trigType] + L"(" + std::to_wstring(angle) + L"°)"; - question = trigExpr + L" " + operators[i] + L" " + question; - } else { - // 简单的括号逻辑 - if (i < operators.size() - 1 && (operators[i] == L"+" || operators[i] == L"-") && - (operators[i+1] == L"×" || operators[i+1] == L"÷")) { - question = L"(" + question + L")"; - } - question += L" " + operators[i] + L" " + numbers[i + 1]; - } - } - - // 如果三角函数在最后一个位置 - if (trigPos == static_cast(operators.size())) { - std::wstring trigExpr = trigFunctions[trigType] + L"(" + std::to_wstring(angle) + L"°)"; - question = trigExpr + L" " + operators.back() + L" " + question; - } - - return question + L" = ?"; // 只有多个操作数时才在这里添加 = ? - } -} - -//题目查重的实现 -bool QuestionGenerator::IsQuestionUnique(const std::wstring& question) { - return generatedQuestions.find(question) == generatedQuestions.end(); -} - -//生成指定数量题目的实现 -std::vector QuestionGenerator::generateQuestions(int count) { - // 验证题目数量范围 - if (count < 10 || count > 30) { - qDebug() << "QuestionGenerator: 题目数量超出范围(10-30),使用默认值10"; - count = 10; - } - - std::vector questions; - int attempts = 0; - const int maxAttempts = count * 10; // 增加尝试次数 - - for (int i = 0; i < count && attempts < maxAttempts; i++) { - std::wstring question; - - switch(currentGrade) { - case Grade::PRIMARY: - question = generatePrimaryQuestion(); - break; - case Grade::JUNIOR: - question = generateJuniorQuestion(); - break; - case Grade::SENIOR: - question = generateSeniorQuestion(); - break; - } - - if (IsQuestionUnique(question)) { - questions.push_back(question); - generatedQuestions.insert(question); - qDebug() << "生成题目" << i + 1 << ":" << QString::fromStdWString(question); - } else { - i--; - qDebug() << "题目重复,重新生成"; - } - attempts++; - } - - if (static_cast(questions.size()) < count) { - qDebug() << "QuestionGenerator: 警告:只生成了" << questions.size() << "个唯一题目"; - } - - qDebug() << "QuestionGenerator: 成功生成" << questions.size() << "个题目"; - return questions; -} - -// 计算小学表达式结果 - 改进版本 -double QuestionGenerator::calculatePrimaryExpression(const std::wstring& expr) { - try { - // 简单的表达式计算,支持基础运算 - std::wstring expression = expr; - - // 移除空格 - expression.erase(std::remove(expression.begin(), expression.end(), L' '), expression.end()); - - // 简单的递归计算函数 - std::function calculate = [&](const std::wstring& str) -> double { - if (str.empty()) return 0.0; - - // 处理括号 - if (str.front() == L'(' && str.back() == L')') { - return calculate(str.substr(1, str.length() - 2)); - } - - // 查找最低优先级的运算符 - int parenCount = 0; - int lowestPriority = -1; - int lowestPos = -1; - - for (int i = static_cast(str.length()) - 1; i >= 0; i--) { - wchar_t c = str[i]; - if (c == L')') parenCount++; - else if (c == L'(') parenCount--; - else if (parenCount == 0) { - if ((c == L'+' || c == L'-') && lowestPriority < 1) { - lowestPriority = 1; - lowestPos = i; - } else if ((c == L'×' || c == L'÷') && lowestPriority < 0) { - lowestPriority = 0; - lowestPos = i; - } - } - } - - if (lowestPos != -1) { - std::wstring left = str.substr(0, lowestPos); - std::wstring right = str.substr(lowestPos + 1); - wchar_t op = str[lowestPos]; - - double leftVal = calculate(left); - double rightVal = calculate(right); - - switch(op) { - case L'+': return leftVal + rightVal; - case L'-': return leftVal - rightVal; - case L'×': return leftVal * rightVal; - case L'÷': - if (rightVal == 0) { - qDebug() << "除零错误"; - return 0.0; - } - return leftVal / rightVal; - } - } - - // 没有运算符,直接解析数字 - try { - return std::stod(str); - } catch (...) { - qDebug() << "无法解析数字:" << QString::fromStdWString(str); - return 0.0; - } - }; - - return calculate(expression); - } catch (const std::exception& e) { - qDebug() << "计算小学表达式异常:" << e.what(); - return 0.0; - } -} - -// 计算初中表达式结果 - 改进版本 -double QuestionGenerator::calculateJuniorExpression(const std::wstring& expr) { - try { - std::wstring expression = expr; - - // 处理平方运算 - size_t squarePos = expression.find(L"²"); - while (squarePos != std::wstring::npos) { - // 找到平方的基数 - int start = static_cast(squarePos) - 1; - while (start >= 0 && (std::iswdigit(expression[start]) || expression[start] == L')')) { - start--; - } - if (start >= 0 && expression[start] == L'(') start--; - - std::wstring baseStr = expression.substr(start + 1, squarePos - start - 1); - double base = calculatePrimaryExpression(baseStr); - double result = base * base; - - // 替换平方部分 - expression.replace(start + 1, squarePos - start + 1, std::to_wstring(result)); - squarePos = expression.find(L"²"); - } - - // 处理开方运算 - size_t sqrtPos = expression.find(L"√"); - while (sqrtPos != std::wstring::npos) { - // 找到被开方数 - size_t end = sqrtPos + 1; - while (end < expression.length() && (std::iswdigit(expression[end]) || expression[end] == L'(')) { - end++; - } - - std::wstring radicandStr = expression.substr(sqrtPos + 1, end - sqrtPos - 1); - double radicand = calculatePrimaryExpression(radicandStr); - if (radicand < 0) { - qDebug() << "负数开方错误"; - return 0.0; - } - double result = std::sqrt(radicand); - - // 替换开方部分 - expression.replace(sqrtPos, end - sqrtPos, std::to_wstring(result)); - sqrtPos = expression.find(L"√"); - } - - return calculatePrimaryExpression(expression); - } catch (const std::exception& e) { - qDebug() << "计算初中表达式异常:" << e.what(); - return 0.0; - } -} - -// 计算高中表达式结果 - 改进版本 -double QuestionGenerator::calculateSeniorExpression(const std::wstring& expr) { - try { - std::wstring expression = expr; - - // 处理三角函数 - std::vector trigFuncs = {L"sin", L"cos", L"tan"}; - - for (const auto& func : trigFuncs) { - size_t pos = expression.find(func); - while (pos != std::wstring::npos) { - // 找到括号内的角度 - size_t parenStart = pos + func.length(); - if (parenStart >= expression.length() || expression[parenStart] != L'(') { - break; - } - - size_t parenEnd = expression.find(L')', parenStart); - if (parenEnd == std::wstring::npos) { - break; - } - - std::wstring angleStr = expression.substr(parenStart + 1, parenEnd - parenStart - 1); - // 移除度符号 - if (!angleStr.empty() && angleStr.back() == L'°') { - angleStr.pop_back(); - } - - double angle = std::stod(angleStr); - double result = 0.0; - - // 转换为弧度并计算 - double radians = angle * M_PI / 180.0; - if (func == L"sin") { - result = std::sin(radians); - } else if (func == L"cos") { - result = std::cos(radians); - } else if (func == L"tan") { - if (std::abs(std::cos(radians)) < 1e-10) { - qDebug() << "tan函数计算错误,角度:" << angle; - return 0.0; - } - result = std::tan(radians); - } - - // 替换三角函数部分 - expression.replace(pos, parenEnd - pos + 1, std::to_wstring(result)); - pos = expression.find(func); - } - } - - return calculateJuniorExpression(expression); - } catch (const std::exception& e) { - qDebug() << "计算高中表达式异常:" << e.what(); - return 0.0; - } -} - -// 计算表达式结果 -double QuestionGenerator::calculateExpression(const std::wstring& expression) { - try { - switch(currentGrade) { - case Grade::PRIMARY: - return calculatePrimaryExpression(expression); - case Grade::JUNIOR: - return calculateJuniorExpression(expression); - case Grade::SENIOR: - return calculateSeniorExpression(expression); - default: - return 0.0; - } - } catch (const std::exception& e) { - qDebug() << "计算表达式异常:" << e.what(); - return 0.0; - } -} - -// 计算正确答案 -double QuestionGenerator::calculateCorrectAnswer(const std::wstring& question) { - try { - // 移除 "= ?" 部分 - std::wstring expression = question; - size_t pos = expression.find(L" = ?"); - if (pos != std::wstring::npos) { - expression = expression.substr(0, pos); - } - - qDebug() << "计算题目答案:" << QString::fromStdWString(expression); - double result = calculateExpression(expression); - qDebug() << "计算结果:" << result; - return result; - } catch (const std::exception& e) { - qDebug() << "计算正确答案异常:" << e.what(); - return 0.0; - } -} - -// 生成有意义的选项 -std::vector QuestionGenerator::generateMeaningfulOptions(double correctAnswer) { - try { - std::vector options; - std::uniform_real_distribution dis(-2.0, 2.0); - std::set usedValues; - - // 格式化正确答案 - std::stringstream oss; - oss << std::fixed << std::setprecision(6) << correctAnswer; - std::string correctStr = oss.str(); - // 移除末尾多余的0 - correctStr.erase(correctStr.find_last_not_of('0') + 1, std::string::npos); - if (!correctStr.empty() && correctStr.back() == '.') { - correctStr.pop_back(); - } - - std::wstring wcorrectStr(correctStr.begin(), correctStr.end()); - options.push_back(wcorrectStr); - usedValues.insert(correctAnswer); - - // 生成有意义的干扰项 - for (int i = 0; i < 3; ++i) { - double variation = dis(gen); - double wrongAnswer = correctAnswer + variation; - - // 确保干扰项与正确答案不同且唯一 - int attempts = 0; - while ((std::abs(wrongAnswer - correctAnswer) < 0.001 || - usedValues.find(wrongAnswer) != usedValues.end()) && - attempts < 10) { - variation = dis(gen); - wrongAnswer = correctAnswer + variation; - attempts++; - } - - usedValues.insert(wrongAnswer); - - std::stringstream wrongOss; - wrongOss << std::fixed << std::setprecision(6) << wrongAnswer; - std::string wrongStr = wrongOss.str(); - // 移除末尾多余的0 - wrongStr.erase(wrongStr.find_last_not_of('0') + 1, std::string::npos); - if (!wrongStr.empty() && wrongStr.back() == '.') { - wrongStr.pop_back(); - } - - std::wstring wwrongStr(wrongStr.begin(), wrongStr.end()); - options.push_back(wwrongStr); - } - - // 随机打乱选项顺序 - std::shuffle(options.begin(), options.end(), gen); - - return options; - } catch (const std::exception& e) { - qDebug() << "生成选项异常:" << e.what(); - // 返回默认选项 - return {L"1.0", L"2.0", L"3.0", L"4.0"}; - } -} - -//生成题目和选项的实现(用于GUI考试) -QuestionGenerator::QuestionWithOptions QuestionGenerator::generateQuestionWithOptions() { - QuestionWithOptions qwo; - - try { - // 生成题目 - switch(currentGrade) { - case Grade::PRIMARY: - qwo.question = generatePrimaryQuestion(); - break; - case Grade::JUNIOR: - qwo.question = generateJuniorQuestion(); - break; - case Grade::SENIOR: - qwo.question = generateSeniorQuestion(); - break; - } - - // 计算正确答案并生成有意义的选项 - double correctAnswer = calculateCorrectAnswer(qwo.question); - qwo.options = generateMeaningfulOptions(correctAnswer); - - // 找到正确答案的索引 - std::stringstream oss; - oss << std::fixed << std::setprecision(6) << correctAnswer; - std::string correctStr = oss.str(); - std::wstring wcorrectStr(correctStr.begin(), correctStr.end()); - - for (size_t i = 0; i < qwo.options.size(); ++i) { - if (qwo.options[i] == wcorrectStr) { - qwo.correctIndex = static_cast(i); - break; - } - } - } catch (const std::exception& e) { - qDebug() << "生成题目和选项异常:" << e.what(); - // 返回默认题目 - qwo.question = L"1 + 1 = ?"; - qwo.options = {L"1.0", L"2.0", L"3.0", L"4.0"}; - qwo.correctIndex = 1; - } - - return qwo; -} - -//清空历史记录的实现 -void QuestionGenerator::clearHistory() { - generatedQuestions.clear(); -} diff --git a/project/questiongenerator.h b/project/questiongenerator.h deleted file mode 100644 index 487b79c..0000000 --- a/project/questiongenerator.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef QUESTIONGENERATOR_H -#define QUESTIONGENERATOR_H - -#include "user.h" -#include -#include -#include -#include -#include -#include -#include - -//题目生成器类,根据年级生成不同难度的数学题目 -//支持小学、初中、高中三个级别的题目生成,包含查重功能。 -class QuestionGenerator { -private: - Grade currentGrade; - std::set generatedQuestions; - std::random_device rd; - std::mt19937 gen; - - //生成小学级别的基础算术题目 - std::wstring generatePrimaryQuestion(); - - //生成初中级别的题目,包含平方或开根号 - std::wstring generateJuniorQuestion(); - - //生成高中级别的题目,包含三角函数 - std::wstring generateSeniorQuestion(); - - //生成指定范围内的随机整数 - std::wstring generateNumber(int min, int max); - - //随机生成算术运算符 - std::wstring generateOperator(); - - //检查题目是否已生成过(查重) - bool IsQuestionUnique(const std::wstring& question); - - //括号需求判断 - bool needsParentheses(const std::wstring& expr, const std::wstring& nextOp); - - // 计算表达式结果 - double calculateExpression(const std::wstring& expression); - double calculatePrimaryExpression(const std::wstring& expr); - double calculateJuniorExpression(const std::wstring& expr); - double calculateSeniorExpression(const std::wstring& expr); - -public: - //构造函数,初始化题目生成器 - QuestionGenerator(); - - //设置当前题目生成难度级别 - void setGrade(Grade grade); - - //生成指定数量的唯一数学题目 - std::vector generateQuestions(int count); - - //生成题目和选项(用于GUI) - struct QuestionWithOptions { - std::wstring question; - std::vector options; - int correctIndex; - }; - - QuestionWithOptions generateQuestionWithOptions(); - - //清空已生成题目的历史记录 - void clearHistory(); - - // 计算正确答案 - double calculateCorrectAnswer(const std::wstring& question); - - // 生成有意义的选项 - std::vector generateMeaningfulOptions(double correctAnswer); -}; - -#endif diff --git a/project/user.cpp b/project/user.cpp deleted file mode 100644 index 65b0f8d..0000000 --- a/project/user.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "user.h" -#include - -//用户构造函数的实现 -User::User(const std::wstring& name, const std::wstring& pass, Grade grade) - : username(name), password(pass), gradeType(grade) {} - -//用户验证的实现 -bool User::check(const std::wstring& name, const std::wstring& pass) const { - return (username == name && password == pass); -} - -//获取用户名的实现 -std::wstring User::getUsername() const { - return username; -} - -//获取密码的实现 -std::wstring User::getPassword() const { - return password; -} - -//获取年级枚举值的实现 -Grade User::getGrade() const { - return gradeType; -} - -//获取年级字符串的实现 -std::wstring User::getGradeString() const { - switch(gradeType) { - case Grade::PRIMARY: return L"小学"; - case Grade::JUNIOR: return L"初中"; - case Grade::SENIOR: return L"高中"; - default: return L"未知"; - } -} - -//设置密码的实现 -void User::setPassword(const std::wstring& newPassword) { - password = newPassword; -} \ No newline at end of file diff --git a/project/user.h b/project/user.h deleted file mode 100644 index 554141d..0000000 --- a/project/user.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef USER_H -#define USER_H - -#include - -//年级枚举,表示用户所属的学段级别 -//用于区分不同学段的题目难度和生成规则 -enum class Grade { PRIMARY, JUNIOR, SENIOR }; - -//用户信息类,封装用户认证数据和年级信息 -//存储用户名、密码和年级类型,提供验证和查询接口。 -class User { -private: - std::wstring username; - std::wstring password; - Grade gradeType; - -public: - //构造函数,初始化用户信息 - User(const std::wstring& name, const std::wstring& pass, Grade grade); - - //验证用户名和密码是否匹配 - bool check(const std::wstring& name, const std::wstring& pass) const; - - //获取用户名 - std::wstring getUsername() const; - - //获取密码 - std::wstring getPassword() const; - - //获取年级枚举值 - Grade getGrade() const; - - //获取年级的中文描述 - std::wstring getGradeString() const; - - //设置密码 - void setPassword(const std::wstring& newPassword); -}; - -#endif \ No newline at end of file diff --git a/project/usermanage.cpp b/project/usermanage.cpp deleted file mode 100644 index 4fcba32..0000000 --- a/project/usermanage.cpp +++ /dev/null @@ -1,431 +0,0 @@ -#include "usermanage.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // 添加进程支持 -#include // 添加目录支持 - -// 初始化用户数据的实现 -void UserManager::initializeUsers() { - // 如果文件中有用户数据,就不初始化默认用户 - if (loadUsersFromFile()) { - qDebug() << "UserManager: 从文件加载用户数据成功"; - return; - } - - qDebug() << "UserManager: 初始化默认用户数据"; - - // 初始化默认测试用户(使用英文用户名避免编码问题) - users.emplace_back(L"zhangsan1", L"Abc123", Grade::PRIMARY); - users.emplace_back(L"zhangsan2", L"Abc123", Grade::PRIMARY); - users.emplace_back(L"zhangsan3", L"Abc123", Grade::PRIMARY); - - users.emplace_back(L"lisi1", L"Abc123", Grade::JUNIOR); - users.emplace_back(L"lisi2", L"Abc123", Grade::JUNIOR); - users.emplace_back(L"lisi3", L"Abc123", Grade::JUNIOR); - - users.emplace_back(L"wangwu1", L"Abc123", Grade::SENIOR); - users.emplace_back(L"wangwu2", L"Abc123", Grade::SENIOR); - users.emplace_back(L"wangwu3", L"Abc123", Grade::SENIOR); - - // 保存到文件 - if (saveUsersToFile()) { - qDebug() << "UserManager: 默认用户数据保存成功"; - } else { - qDebug() << "UserManager: 默认用户数据保存失败"; - } -} - -// 用户管理器的构造实现 -UserManager::UserManager() { - qDebug() << "UserManager: 构造函数调用"; - initializeUsers(); -} - -// 用户认证的实现 -User* UserManager::authenticateUser(const std::wstring& username, const std::wstring& password) { - qDebug() << "UserManager: 尝试认证用户:" << QString::fromStdWString(username); - - // 重新加载用户数据,确保包含新注册的用户 - loadUsersFromFile(); - - for (auto& user : users) { - if (user.check(username, password)) { - qDebug() << "UserManager: 用户认证成功"; - return &user; - } - } - - qDebug() << "UserManager: 用户认证失败"; - return nullptr; -} - -// 解析用户类型的实现 -Grade UserManager::parseUserType(const std::wstring& type) { - if (type == L"小学" || type == L"Primary") return Grade::PRIMARY; - if (type == L"初中" || type == L"Junior") return Grade::JUNIOR; - if (type == L"高中" || type == L"Senior") return Grade::SENIOR; - return Grade::PRIMARY; -} - -// 注册新用户的实现 -bool UserManager::registerUser(const std::wstring& username, const std::wstring& password, const std::wstring& grade) { - qDebug() << "UserManager: 尝试注册用户:" << QString::fromStdWString(username) << "年级:" << QString::fromStdWString(grade); - - // 重新加载用户数据,确保检查最新的用户名 - loadUsersFromFile(); - - // 检查用户名是否已存在 - if (isUsernameExists(username)) { - qDebug() << "UserManager: 用户名已存在"; - return false; - } - - Grade userGrade = parseUserType(grade); - users.emplace_back(username, password, userGrade); - - qDebug() << "UserManager: 用户添加到内存,准备保存到文件"; - - // 保存到文件 - bool result = saveUsersToFile(); - if (result) { - qDebug() << "UserManager: 用户注册成功"; - // 重新加载确保数据同步 - loadUsersFromFile(); - } else { - qDebug() << "UserManager: 用户注册失败 - 文件保存失败"; - } - - return result; -} - -// 修改用户密码的实现 -bool UserManager::changePassword(const std::wstring& username, const std::wstring& oldPassword, const std::wstring& newPassword) { - for (auto& user : users) { - if (user.check(username, oldPassword)) { - user.setPassword(newPassword); - return saveUsersToFile(); - } - } - return false; -} - -// 检查用户名是否存在的实现 -bool UserManager::isUsernameExists(const std::wstring& username) { - for (const auto& user : users) { - if (user.getUsername() == username) { - return true; - } - } - return false; -} - -// 从文件加载用户数据的实现 -bool UserManager::loadUsersFromFile() { - std::ifstream file(userFile); - if (!file.is_open()) { - qDebug() << "UserManager: 无法打开用户文件:" << userFile.c_str(); - return false; - } - - users.clear(); - std::string line; - int userCount = 0; - - while (std::getline(file, line)) { - std::istringstream iss(line); - std::string username, password, gradeStr; - - if (iss >> username >> password >> gradeStr) { - // 转换为宽字符串 - std::wstring wusername, wpassword, wgradeStr; - for (char c : username) wusername += wchar_t(c); - for (char c : password) wpassword += wchar_t(c); - for (char c : gradeStr) wgradeStr += wchar_t(c); - - Grade grade = parseUserType(wgradeStr); - users.emplace_back(wusername, wpassword, grade); - userCount++; - qDebug() << "加载用户:" << QString::fromStdWString(wusername) << "年级:" << QString::fromStdWString(wgradeStr); - } - } - - file.close(); - qDebug() << "UserManager: 从文件加载了" << userCount << "个用户"; - return !users.empty(); -} - -// 保存用户数据到文件的实现 -bool UserManager::saveUsersToFile() { - std::ofstream file(userFile); - if (!file.is_open()) { - qDebug() << "UserManager: 无法创建用户文件:" << userFile.c_str(); - return false; - } - - int savedCount = 0; - for (const auto& user : users) { - std::wstring username = user.getUsername(); - std::wstring password = user.getPassword(); - std::wstring gradeStr = user.getGradeString(); - - // 转换为普通字符串 - std::string username_str, password_str, grade_str; - for (wchar_t wc : username) username_str += static_cast(wc); - for (wchar_t wc : password) password_str += static_cast(wc); - for (wchar_t wc : gradeStr) grade_str += static_cast(wc); - - file << username_str << " " << password_str << " " << grade_str << "\n"; - savedCount++; - } - - file.close(); - qDebug() << "UserManager: 保存了" << savedCount << "个用户到文件"; - return true; -} - -// 生成验证码的实现 - 修复为每次都不同 -std::wstring UserManager::generateVerificationCode(int length) { - const std::wstring chars = L"0123456789"; - std::wstring code; - - // 使用当前时间作为随机种子,确保每次不同 - std::random_device rd; - std::mt19937 gen(rd() + static_cast(std::time(nullptr))); - std::uniform_int_distribution<> dis(0, chars.length() - 1); - - for (int i = 0; i < length; ++i) { - code += chars[dis(gen)]; - } - - qDebug() << "UserManager: 生成验证码:" << QString::fromStdWString(code); - return code; -} - -// 发送验证码的实现 - 添加真实邮件发送功能 -bool UserManager::sendVerificationCode(const std::wstring& email, std::wstring& generatedCode) { - qDebug() << "UserManager: 发送验证码到:" << QString::fromStdWString(email); - - // 生成验证码 - generatedCode = generateVerificationCode(); - - // 设置过期时间(10分钟) - auto expiryTime = std::chrono::system_clock::now() + std::chrono::minutes(10); - - // 存储验证码信息 - emailVerifications[email] = {generatedCode, expiryTime, false}; - - qDebug() << "UserManager: 验证码已存储,有效期10分钟"; - - // 尝试发送真实邮件 - if (sendRealEmail(email, generatedCode)) { - qDebug() << "UserManager: 验证码邮件发送成功"; - return true; - } else { - // 如果真实邮件发送失败,使用控制台输出 - std::wcout << L"[邮件模拟] 发送到: " << email << L" 的验证码: " << generatedCode << std::endl; - qDebug() << "UserManager: 真实邮件发送失败,使用模拟模式"; - - // 在调试窗口显示验证码信息 - QString qEmail = QString::fromStdWString(email); - QString qCode = QString::fromStdWString(generatedCode); - qDebug() << "=== 邮件发送模拟 ==="; - qDebug() << "收件人:" << qEmail; - qDebug() << "验证码:" << qCode; - qDebug() << "=== 邮件发送模拟 ==="; - - return true; - } -} - -// 验证邮箱验证码的实现 -bool UserManager::verifyEmailCode(const std::wstring& email, const std::wstring& code) { - qDebug() << "UserManager: 验证邮箱验证码,邮箱:" << QString::fromStdWString(email) - << "验证码:" << QString::fromStdWString(code); - - auto it = emailVerifications.find(email); - if (it == emailVerifications.end()) { - qDebug() << "UserManager: 没有找到该邮箱的验证码记录"; - return false; // 没有找到该邮箱的验证码记录 - } - - EmailVerification& verification = it->second; - - // 检查验证码是否已使用 - if (verification.used) { - qDebug() << "UserManager: 验证码已被使用"; - return false; - } - - // 检查验证码是否过期 - if (std::chrono::system_clock::now() > verification.expiryTime) { - qDebug() << "UserManager: 验证码已过期"; - emailVerifications.erase(it); - return false; - } - - // 检查验证码是否匹配 - if (verification.code == code) { - qDebug() << "UserManager: 验证码验证成功"; - verification.used = true; // 标记为已使用 - return true; - } - - qDebug() << "UserManager: 验证码不匹配"; - return false; -} - -// 真实邮件发送功能 - 仅使用 PowerShell -bool UserManager::sendRealEmail(const std::wstring& email, const std::wstring& code) { - QString qEmail = QString::fromStdWString(email); - QString qCode = QString::fromStdWString(code); - - qDebug() << "尝试使用 PowerShell 发送邮件到:" << qEmail; - - return sendEmailViaPowerShell(qEmail, qCode); -} - -// 辅助函数:安全地转义字符串中的单引号 -QString escapeSingleQuotes(const QString& input) { - QString result = input; - return result.replace("'", "''"); -} - -// 使用 PowerShell Send-MailMessage 发送邮件 -bool UserManager::sendEmailViaPowerShell(const QString& toEmail, const QString& code) { - qDebug() << "使用 PowerShell 发送邮件..."; - - // ==================== 配置区域 ==================== - // 请根据你的邮箱服务商修改以下配置: - - QString smtpServer = "smtp.qq.com"; // SMTP服务器 - int smtpPort = 587; // 端口号 - QString fromEmail = "1453386832@qq.com"; // ⬅️ 修改:发件人邮箱 - QString username = "1453386832@qq.com"; // ⬅️ 修改:邮箱账号 - QString password = "nijuqetihpojffag"; // ⬅️ 修改:邮箱授权码 - - /* - // 其他邮箱配置示例: - - // 163邮箱配置: - // QString smtpServer = "smtp.163.com"; - // int smtpPort = 25; // 或 587 - // QString fromEmail = "your_email@163.com"; - // QString username = "your_email@163.com"; - // QString password = "your_163_authorization_code"; - - // Gmail配置: - // QString smtpServer = "smtp.gmail.com"; - // int smtpPort = 587; - // QString fromEmail = "your_email@gmail.com"; - // QString username = "your_email@gmail.com"; - // QString password = "your_gmail_app_password"; - */ - // ==================== 配置结束 ==================== - - QString subject = "Math Learning Software - Verification Code"; - QString body = QString( - "Dear User:\n\n" - "You are registering for Math Learning Software. Your verification code is: %1\n\n" - "The verification code is valid for 10 minutes. Please complete your registration as soon as possible.\n\n" - "If this was not your operation, please ignore this email.\n\n" - "Math Learning Software Team" - ).arg(code); - - // 安全地转义所有字符串 - QString safePassword = escapeSingleQuotes(password); - QString safeUsername = escapeSingleQuotes(username); - QString safeFromEmail = escapeSingleQuotes(fromEmail); - QString safeToEmail = escapeSingleQuotes(toEmail); - QString safeSubject = escapeSingleQuotes(subject); - QString safeBody = escapeSingleQuotes(body); - QString safeSmtpServer = escapeSingleQuotes(smtpServer); - - // 构建 PowerShell 命令 - QString powerShellScript = QString( - "$secpasswd = ConvertTo-SecureString \"%1\" -AsPlainText -Force\n" - "$credential = New-Object System.Management.Automation.PSCredential(\"%2\", $secpasswd)\n" - "Send-MailMessage -From '%3' -To '%4' -Subject '%5' -Body '%6' -SmtpServer '%7' -Port %8 -Credential $credential -UseSsl" - ).arg(safePassword, - safeUsername, - safeFromEmail, - safeToEmail, - safeSubject, - safeBody, - safeSmtpServer, - QString::number(smtpPort)); - - // 创建临时 PowerShell 脚本文件 - QString tempScriptFile = QDir::tempPath() + "/send_email.ps1"; - QFile scriptFile(tempScriptFile); - - if (!scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) { - qDebug() << "无法创建临时 PowerShell 脚本文件"; - return false; - } - - QTextStream out(&scriptFile); - out << powerShellScript; - scriptFile.close(); - - qDebug() << "执行 PowerShell 脚本..."; - - // 执行 PowerShell 脚本 - QProcess process; - process.start("powershell", QStringList() << "-ExecutionPolicy" << "Bypass" << "-File" << tempScriptFile); - - if (!process.waitForStarted(10000)) { - qDebug() << "无法启动 PowerShell 进程"; - QFile::remove(tempScriptFile); - return false; - } - - if (!process.waitForFinished(60000)) { // 等待60秒 - qDebug() << "PowerShell 进程超时"; - process.kill(); - QFile::remove(tempScriptFile); - return false; - } - - int exitCode = process.exitCode(); - QByteArray output = process.readAllStandardOutput(); - QByteArray error = process.readAllStandardError(); - - qDebug() << "PowerShell 退出代码:" << exitCode; - - if (!output.isEmpty()) { - qDebug() << "PowerShell 输出:" << output; - } - - if (!error.isEmpty()) { - qDebug() << "PowerShell 错误:" << error; - } - - // 清理临时文件 - QFile::remove(tempScriptFile); - - if (exitCode == 0) { - qDebug() << "邮件发送成功!"; - return true; - } else { - qDebug() << "邮件发送失败,退出代码:" << exitCode; - - // 提供详细的错误信息 - if (error.contains("Authentication")) { - qDebug() << "错误:认证失败,请检查用户名和密码"; - } else if (error.contains("Connection")) { - qDebug() << "错误:连接失败,请检查SMTP服务器和端口"; - } else if (error.contains("SSL")) { - qDebug() << "错误:SSL连接失败"; - } - - return false; - } -} diff --git a/project/usermanage.h b/project/usermanage.h deleted file mode 100644 index 9427a6e..0000000 --- a/project/usermanage.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef USERMANAGE_H -#define USERMANAGE_H - -#include "user.h" -#include -#include -#include -#include -#include -#include -#include -#include // 添加进程支持 - -// 邮箱验证码结构 -struct EmailVerification { - std::wstring code; - std::chrono::system_clock::time_point expiryTime; - bool used; -}; - -// 用户管理器类,负责用户数据的初始化和认证管理 -class UserManager { -private: - std::vector users; - const std::string userFile = "users.dat"; - - // 邮箱验证码存储 - std::map emailVerifications; - - //初始化预定义用户数据 - void initializeUsers(); - - //从文件加载用户数据 - bool loadUsersFromFile(); - - //保存用户数据到文件 - bool saveUsersToFile(); - - // 真实邮件发送 - bool sendRealEmail(const std::wstring& email, const std::wstring& code); - - // PowerShell 邮件发送方法 - bool sendEmailViaPowerShell(const QString& toEmail, const QString& code); - -public: - //构造函数,自动初始化用户数据 - UserManager(); - - //用户认证,验证用户名和密码 - User* authenticateUser(const std::wstring& username, const std::wstring& password); - - //将中文年级字符串解析为年级枚举值 - Grade parseUserType(const std::wstring& type); - - //注册新用户 - bool registerUser(const std::wstring& username, const std::wstring& password, const std::wstring& grade); - - //修改用户密码 - bool changePassword(const std::wstring& username, const std::wstring& oldPassword, const std::wstring& newPassword); - - //检查用户名是否存在 - bool isUsernameExists(const std::wstring& username); - - // 邮箱验证相关方法 - bool sendVerificationCode(const std::wstring& email, std::wstring& generatedCode); - bool verifyEmailCode(const std::wstring& email, const std::wstring& code); - - // 生成随机验证码 - std::wstring generateVerificationCode(int length = 6); - - //获取所有用户(用于测试) - std::vector& getUsers() { return users; } -}; - -#endif -- 2.34.1