11 #1

Merged
hnu202326010103 merged 0 commits from develop into main 3 months ago

@ -1,2 +0,0 @@
# MathsQuestionSystem1

@ -0,0 +1,158 @@
# 数学学习软件使用说明
## 项目概述
数学学习软件是一款专为不同学习阶段学生设计的数学练习工具,支持小学、初中和高中三个难度级别的数学题目练习。软件采用用户注册登录系统,通过邮箱验证确保用户身份安全。
## 系统要求
- **操作系统**: Windows 7及以上版本
- **开发环境**: C++ (兼容C++98标准)
- **依赖组件**:
- Windows PowerShell (用于邮件发送)
- curl (可选,备用邮件发送方案)
## 功能特点
### 1. 用户管理
- **用户注册**: 通过邮箱验证码注册账号
- **密码安全**: 密码要求6-10位包含大小写字母和数字
- **登录验证**: 安全的用户身份验证
### 2. 题目生成
- **小学题目**: 基础四则运算支持2-3个操作数
- **初中题目**: 平方、开方运算
- **高中题目**: 三角函数计算
### 3. 答题功能
- **题目数量**: 可自定义10-30道题目
- **实时反馈**: 即时显示答题结果
- **进度跟踪**: 显示答题进度和得分
## 安装与配置
### 1. 编译说明
项目使用标准C++编写可在支持Windows API的开发环境中编译
```bash
g++ -o MathLearning.exe *.cpp -luser32 -lgdi32
```
### 2. 邮件配置
如需使用邮件功能需要配置QQ邮箱SMTP服务
1. 在 `EmailSender.cpp` 中修改发件人信息:
```cpp
const std::string sender_email = "your_email@qq.com";
const std::string sender_password = "your_authorization_code";
```
2. 获取QQ邮箱授权码
- 登录QQ邮箱 → 设置 → 账户
- 开启POP3/SMTP服务
- 生成授权码
## 使用指南
### 1. 启动程序
运行编译后的可执行文件,进入登录界面。
### 2. 用户注册
1. 点击"注册"按钮
2. 输入有效的邮箱地址
3. 点击"获取验证码",查收邮件中的验证码
4. 设置符合要求的密码6-10位包含大小写字母和数字
5. 确认密码并完成注册
### 3. 用户登录
1. 输入已注册的邮箱和密码
2. 点击"登录"进入题目选择界面
### 4. 选择题目类型
- **小学题目**: 适合小学生的基础数学运算
- **初中题目**: 包含平方和开方运算
- **高中题目**: 三角函数计算
### 5. 设置题目数量
在10-30道题目范围内选择练习数量。
### 6. 答题界面
- 阅读题目并选择正确答案
- 点击"下一题"继续答题
- 完成所有题目后查看成绩报告
## 文件结构
```
项目根目录/
├── main.cpp # 程序入口
├── LoginWindow.h/cpp # 登录窗口
├── RegisterWindow.h/cpp # 注册窗口
├── SelectionWindow.h/cpp # 题目选择窗口
├── QuestionWindow.h/cpp # 答题窗口
├── User.h/cpp # 用户类
├── UserManager.h/cpp # 用户管理
├── Question.h/cpp # 题目类
├── QuestionGenerator.h/cpp # 题目生成器
├── EmailSender.h/cpp # 邮件发送器
└── data/
└── users.txt # 用户数据存储
```
## 技术特性
### 1. 邮件发送
- 支持PowerShell和curl两种发送方式
- 自动故障转移机制
- 详细的发送日志记录
### 2. 题目生成算法
- 智能避免重复题目
- 考虑运算优先级
- 确保题目难度适中
### 3. 用户界面
- 基于Windows API的本地化界面
- 支持中文显示
- 响应式窗口设计
## 故障排除
### 常见问题
1. **邮件发送失败**
- 检查邮箱配置是否正确
- 确认网络连接正常
- 查看email_send_log.txt日志文件
2. **程序无法启动**
- 确认系统满足要求
- 检查依赖组件是否安装
3. **题目显示异常**
- 确保编译环境支持中文字符集
- 检查控制台编码设置
### 日志文件
程序生成以下日志文件用于故障诊断:
- `email_send_log.txt` - 邮件发送记录
- 控制台输出 - 题目生成和答题详情
## 开发说明
### 扩展功能
- 可添加更多题目类型
- 支持用户成绩统计
- 增加题目难度分级
### 自定义配置
开发者可以通过修改相应头文件中的常量来调整:
- 题目数量范围
- 密码复杂度要求
- 邮件发送配置
## 版权信息
本软件为教育用途开发,遵循相关开源协议。
---
*最后更新: 2024年*

@ -0,0 +1,217 @@
#include "EmailSender.h"
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <windows.h>
bool EmailSender::sendVerificationCode(const std::string& email, const std::string& code) {
// 首先尝试PowerShell方案如果失败则尝试其他方案
std::cout << "=== 开始发送验证码邮件 ===" << std::endl;
std::cout << "收件人: " << email << std::endl;
std::cout << "验证码: " << code << std::endl;
// 尝试PowerShell方案
if (sendByPowerShell(email, code)) {
return true;
}
std::cout << "PowerShell方案失败尝试CURL方案..." << std::endl;
// 如果PowerShell失败尝试CURL方案
if (sendByCurl(email, code)) {
return true;
}
std::cout << "所有邮件发送方案均失败" << std::endl;
return false;
}
bool EmailSender::sendByPowerShell(const std::string& email, const std::string& code) {
// 配置发送方邮箱信息 - 修改为你的真实信息
const std::string sender_email = "3675658020@qq.com"; // 修改为你的QQ邮箱
const std::string sender_password = "lsagdbxwfolhcheb"; // 修改为QQ邮箱授权码
std::cout << "尝试使用PowerShell发送邮件..." << std::endl;
std::cout << "发件人: " << sender_email << std::endl;
// 创建PowerShell脚本
std::string psScript =
"# 数学学习软件邮件发送脚本\n"
"# 发件人配置\n"
"$SMTPServer = \"smtp.qq.com\"\n"
"$SMTPPort = \"587\"\n"
"$Username = \"" + sender_email + "\"\n"
"$Password = \"" + sender_password + "\"\n"
"$EmailFrom = \"" + sender_email + "\"\n"
"$EmailTo = \"" + email + "\"\n"
"$Subject = \"数学学习软件 - 注册验证码\"\n"
"$Body = @\"\n"
"欢迎注册数学学习软件!\n"
"\n"
"您的注册验证码是:" + code + "\n"
"\n"
"验证码有效期为10分钟请尽快使用。\n"
"\n"
"如果您没有请求此验证码,请忽略此邮件。\n"
"\n"
"谢谢!\n"
"数学学习软件团队\n"
"\"@\n"
"\n"
"Write-Host \"正在发送邮件到: $EmailTo\"\n"
"Write-Host \"验证码: " + code + "\"\n"
"\n"
"try {\n"
" # 创建邮件消息\n"
" $SMTPMessage = New-Object System.Net.Mail.MailMessage($EmailFrom, $EmailTo, $Subject, $Body)\n"
" \n"
" # 创建SMTP客户端\n"
" $SMTPClient = New-Object Net.Mail.SmtpClient($SMTPServer, $SMTPPort)\n"
" $SMTPClient.EnableSsl = $true\n"
" $SMTPClient.Credentials = New-Object System.Net.NetworkCredential($Username, $Password)\n"
" \n"
" # 发送邮件\n"
" $SMTPClient.Send($SMTPMessage)\n"
" \n"
" Write-Host \"邮件发送成功!\" -ForegroundColor Green\n"
" Write-Output \"SUCCESS\"\n"
" \n"
"} catch {\n"
" Write-Host \"邮件发送失败: $($_.Exception.Message)\" -ForegroundColor Red\n"
" Write-Output \"ERROR: $($_.Exception.Message)\"\n"
"}";
// 保存PowerShell脚本
std::ofstream scriptFile("send_email.ps1");
if (!scriptFile) {
std::cout << "创建PowerShell脚本文件失败" << std::endl;
return false;
}
scriptFile << psScript;
scriptFile.close();
std::cout << "正在执行PowerShell脚本..." << std::endl;
// 执行PowerShell脚本
std::string command = "powershell -ExecutionPolicy Bypass -File send_email.ps1";
FILE* pipe = _popen(command.c_str(), "r");
if (!pipe) {
std::cout << "执行PowerShell命令失败" << std::endl;
return false;
}
char buffer[256];
std::string result = "";
while (!feof(pipe)) {
if (fgets(buffer, sizeof(buffer), pipe) != NULL)
result += buffer;
}
_pclose(pipe);
// 记录发送日志
std::ofstream logFile("email_send_log.txt", std::ios::app);
if (logFile) {
logFile << "=== 邮件发送记录 ===" << std::endl;
logFile << "时间: " << __DATE__ << " " << __TIME__ << std::endl;
logFile << "方案: PowerShell" << std::endl;
logFile << "发件人: " << sender_email << std::endl;
logFile << "收件人: " << email << std::endl;
logFile << "验证码: " << code << std::endl;
logFile << "执行结果: " << result << std::endl;
logFile << "=========================" << std::endl << std::endl;
logFile.close();
}
// 检查执行结果
if (result.find("SUCCESS") != std::string::npos) {
std::cout << "? 邮件发送成功!" << std::endl;
std::cout << "验证码已发送到: " << email << std::endl;
return true;
} else {
std::cout << "? PowerShell发送失败: " << result << std::endl;
return false;
}
}
bool EmailSender::sendByCurl(const std::string& email, const std::string& code) {
// 配置发送方邮箱信息 - 修改为你的真实信息
const std::string sender_email = "3675658020@qq.com"; // 修改为你的QQ邮箱
const std::string sender_password = "lsagdbxwfolhcheb"; // 修改为QQ邮箱授权码
std::cout << "尝试使用CURL发送邮件..." << std::endl;
// 检查系统是否安装curl
int curlCheck = system("curl --version > nul 2>&1");
if (curlCheck != 0) {
std::cout << "系统未安装curl跳过此方案" << std::endl;
return false;
}
std::cout << "检测到curl开始发送..." << std::endl;
// 创建邮件内容文件
std::ofstream mailFile("email_content.txt");
if (!mailFile) {
std::cout << "创建邮件内容文件失败" << std::endl;
return false;
}
mailFile << "From: 数学学习软件 <" << sender_email << ">\n";
mailFile << "To: " << email << "\n";
mailFile << "Subject: 数学学习软件 - 注册验证码\n";
mailFile << "Content-Type: text/plain; charset=\"utf-8\"\n";
mailFile << "\n";
mailFile << "欢迎注册数学学习软件!\n";
mailFile << "\n";
mailFile << "您的注册验证码是:" << code << "\n";
mailFile << "\n";
mailFile << "验证码有效期为10分钟请尽快使用。\n";
mailFile << "\n";
mailFile << "如果您没有请求此验证码,请忽略此邮件。\n";
mailFile << "\n";
mailFile << "谢谢!\n";
mailFile << "数学学习软件团队\n";
mailFile.close();
// 使用curl发送邮件
std::string curlCommand =
"curl -s --url \"smtp://smtp.qq.com:587\" "
"--ssl-reqd "
"--mail-from \"" + sender_email + "\" "
"--mail-rcpt \"" + email + "\" "
"--user \"" + sender_email + ":" + sender_password + "\" "
"--upload-file email_content.txt "
"> curl_output.txt 2>&1";
std::cout << "执行CURL命令..." << std::endl;
int result = system(curlCommand.c_str());
// 记录发送日志
std::ofstream logFile("email_send_log.txt", std::ios::app);
if (logFile) {
logFile << "=== CURL邮件发送记录 ===" << std::endl;
logFile << "时间: " << __DATE__ << " " << __TIME__ << std::endl;
logFile << "方案: CURL" << std::endl;
logFile << "发件人: " << sender_email << std::endl;
logFile << "收件人: " << email << std::endl;
logFile << "验证码: " << code << std::endl;
logFile << "命令返回码: " << result << std::endl;
logFile << "=========================" << std::endl << std::endl;
logFile.close();
}
if (result == 0) {
std::cout << "? CURL邮件发送成功" << std::endl;
return true;
} else {
std::cout << "? CURL邮件发送失败返回码: " << result << std::endl;
return false;
}
}
bool EmailSender::sendBySMTP(const std::string& email, const std::string& code) {
// 这个函数保留作为备用方案,当前不使用
std::cout << "SMTP方案暂不可用" << std::endl;
return false;
}

@ -0,0 +1,16 @@
#ifndef EMAILSENDER_H
#define EMAILSENDER_H
#include <string>
class EmailSender {
public:
static bool sendVerificationCode(const std::string& email, const std::string& code);
private:
static bool sendByPowerShell(const std::string& email, const std::string& code);
static bool sendByCurl(const std::string& email, const std::string& code);
static bool sendBySMTP(const std::string& email, const std::string& code);
};
#endif

Binary file not shown.

@ -0,0 +1,135 @@
#include "LoginWindow.h"
#include "UserManager.h"
#include "RegisterWindow.h"
#include "SelectionWindow.h"
#include <string>
#include <sstream>
HWND LoginWindow::hwnd = NULL;
HWND LoginWindow::hEmail = NULL;
HWND LoginWindow::hPassword = NULL;
HWND LoginWindow::hMessage = NULL;
void LoginWindow::Show() {
// 如果窗口已存在,先销毁
if (hwnd != NULL) {
DestroyWindow(hwnd);
hwnd = NULL;
}
WNDCLASSEX wc;
memset(&wc, 0, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = "LoginWindow";
RegisterClassEx(&wc);
hwnd = CreateWindowEx(0, "LoginWindow", "数学学习软件 - 登录",
WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, NULL, GetModuleHandle(NULL), NULL);
if (!hwnd) {
MessageBox(NULL, "创建登录窗口失败", "错误", MB_OK | MB_ICONERROR);
return;
}
CreateControls();
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
}
void LoginWindow::CreateControls() {
// 标题
CreateWindow("STATIC", "数学学习软件", WS_VISIBLE | WS_CHILD | SS_CENTER,
10, 20, 360, 30, hwnd, NULL, GetModuleHandle(NULL), NULL);
// 邮箱标签和输入框
CreateWindow("STATIC", "邮箱:", WS_VISIBLE | WS_CHILD,
50, 70, 50, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
hEmail = CreateWindow("EDIT", "", WS_VISIBLE | WS_CHILD | WS_BORDER,
100, 70, 200, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
// 密码标签和输入框
CreateWindow("STATIC", "密码:", WS_VISIBLE | WS_CHILD,
50, 110, 50, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
hPassword = CreateWindow("EDIT", "", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_PASSWORD,
100, 110, 200, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
// 登录按钮
CreateWindow("BUTTON", "登录", WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
100, 150, 80, 30, hwnd, (HMENU)1, GetModuleHandle(NULL), NULL);
// 注册按钮
CreateWindow("BUTTON", "注册", WS_VISIBLE | WS_CHILD,
200, 150, 80, 30, hwnd, (HMENU)2, GetModuleHandle(NULL), NULL);
// 消息标签 - 修复这里:将 WM_CHILD 改为 WS_CHILD
hMessage = CreateWindow("STATIC", "", WS_VISIBLE | WS_CHILD | SS_CENTER,
50, 190, 300, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
}
LRESULT CALLBACK LoginWindow::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_COMMAND: {
int cmd = LOWORD(wParam);
if (cmd == 1) { // 登录按钮
OnLogin();
} else if (cmd == 2) { // 注册按钮
OnRegister();
}
break;
}
case WM_CLOSE:
DestroyWindow(hwnd);
PostQuitMessage(0); // 只有关闭登录窗口时才退出程序
break;
case WM_DESTROY:
// 不在WM_DESTROY中调用PostQuitMessage避免程序退出
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
void LoginWindow::OnLogin() {
char email[100], password[100];
GetWindowText(hEmail, email, sizeof(email));
GetWindowText(hPassword, password, sizeof(password));
std::string strEmail(email);
std::string strPassword(password);
if (strEmail.empty() || strPassword.empty()) {
SetWindowText(hMessage, "请输入邮箱和密码");
return;
}
UserManager& userManager = UserManager::getInstance();
if (userManager.login(strEmail, strPassword)) {
SetWindowText(hMessage, "登录成功!");
// 延迟后切换到选择界面
Sleep(800);
DestroyWindow(hwnd);
hwnd = NULL; // 重置窗口句柄
// 显示选择界面
SelectionWindow::Show();
} else {
SetWindowText(hMessage, "登录失败,请检查邮箱和密码");
}
}
void LoginWindow::OnRegister() {
DestroyWindow(hwnd);
hwnd = NULL;
RegisterWindow::Show();
}

@ -0,0 +1,23 @@
#ifndef LOGINWINDOW_H
#define LOGINWINDOW_H
#include <windows.h>
class LoginWindow {
public:
static void Show();
static HWND GetHandle() { return hwnd; }
private:
static HWND hwnd;
static HWND hEmail;
static HWND hPassword;
static HWND hMessage;
static void CreateControls();
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
static void OnLogin();
static void OnRegister();
};
#endif

Binary file not shown.

@ -0,0 +1,55 @@
# Project: ÏîÄ¿1
# Makefile created by Dev-C++ 5.11
CPP = g++.exe
CC = gcc.exe
WINDRES = windres.exe
OBJ = UserManager.o User.o QuestionGenerator.o Question.o LoginWindow.o RegisterWindow.o QuestionWindow.o main.o EmailSender.o SelectionWindow.o
LINKOBJ = UserManager.o User.o QuestionGenerator.o Question.o LoginWindow.o RegisterWindow.o QuestionWindow.o main.o EmailSender.o SelectionWindow.o
LIBS = -L"D:/³ÌÐòÉè¼Æ/Dev-Cpp/MinGW64/lib" -L"D:/³ÌÐòÉè¼Æ/Dev-Cpp/MinGW64/x86_64-w64-mingw32/lib" -static-libgcc -mwindows -lws2_32 -lwininet
INCS = -I"D:/³ÌÐòÉè¼Æ/Dev-Cpp/MinGW64/include" -I"D:/³ÌÐòÉè¼Æ/Dev-Cpp/MinGW64/x86_64-w64-mingw32/include" -I"D:/³ÌÐòÉè¼Æ/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include"
CXXINCS = -I"D:/³ÌÐòÉè¼Æ/Dev-Cpp/MinGW64/include" -I"D:/³ÌÐòÉè¼Æ/Dev-Cpp/MinGW64/x86_64-w64-mingw32/include" -I"D:/³ÌÐòÉè¼Æ/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include" -I"D:/³ÌÐòÉè¼Æ/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include/c++"
BIN = test3.exe
CXXFLAGS = $(CXXINCS)
CFLAGS = $(INCS)
RM = rm.exe -f
.PHONY: all all-before all-after clean clean-custom
all: all-before $(BIN) all-after
clean: clean-custom
${RM} $(OBJ) $(BIN)
$(BIN): $(OBJ)
$(CPP) $(LINKOBJ) -o $(BIN) $(LIBS)
UserManager.o: UserManager.cpp
$(CPP) -c UserManager.cpp -o UserManager.o $(CXXFLAGS)
User.o: User.cpp
$(CPP) -c User.cpp -o User.o $(CXXFLAGS)
QuestionGenerator.o: QuestionGenerator.cpp
$(CPP) -c QuestionGenerator.cpp -o QuestionGenerator.o $(CXXFLAGS)
Question.o: Question.cpp
$(CPP) -c Question.cpp -o Question.o $(CXXFLAGS)
LoginWindow.o: LoginWindow.cpp
$(CPP) -c LoginWindow.cpp -o LoginWindow.o $(CXXFLAGS)
RegisterWindow.o: RegisterWindow.cpp
$(CPP) -c RegisterWindow.cpp -o RegisterWindow.o $(CXXFLAGS)
QuestionWindow.o: QuestionWindow.cpp
$(CPP) -c QuestionWindow.cpp -o QuestionWindow.o $(CXXFLAGS)
main.o: main.cpp
$(CPP) -c main.cpp -o main.o $(CXXFLAGS)
EmailSender.o: EmailSender.cpp
$(CPP) -c EmailSender.cpp -o EmailSender.o $(CXXFLAGS)
SelectionWindow.o: SelectionWindow.cpp
$(CPP) -c SelectionWindow.cpp -o SelectionWindow.o $(CXXFLAGS)

@ -0,0 +1,27 @@
#include "Question.h"
Question::Question(const std::string& text, const std::vector<std::string>& options, int correctAnswer)
: text(text), options(options), correctAnswer(correctAnswer) {}
std::string Question::getText() const {
return text;
}
std::vector<std::string> Question::getOptions() const {
return options;
}
int Question::getCorrectAnswer() const {
return correctAnswer;
}
bool Question::checkAnswer(int userAnswer) const {
return userAnswer == correctAnswer;
}
std::string Question::getCorrectOptionText() const {
if (correctAnswer >= 0 && correctAnswer < (int)options.size()) {
return options[correctAnswer];
}
return "ÎÞÕýÈ·´ð°¸";
}

@ -0,0 +1,25 @@
#ifndef QUESTION_H
#define QUESTION_H
#include <string>
#include <vector>
class Question {
public:
Question(const std::string& text, const std::vector<std::string>& options, int correctAnswer);
std::string getText() const;
std::vector<std::string> getOptions() const;
int getCorrectAnswer() const;
bool checkAnswer(int userAnswer) const;
// Ìí¼Óµ÷ÊÔ·½·¨
std::string getCorrectOptionText() const;
private:
std::string text;
std::vector<std::string> options;
int correctAnswer;
};
#endif

Binary file not shown.

@ -0,0 +1,442 @@
#include "QuestionGenerator.h"
#include "Question.h"
#include <cstdlib>
#include <ctime>
#include <sstream>
#include <cmath>
#include <windows.h>
#include <algorithm>
#include <vector>
#include <iostream>
#include <stack>
// C++98兼容的数字转字符串函数
std::string intToString(int value) {
std::stringstream ss;
ss << value;
return ss.str();
}
// 浮点数转字符串保留2位小数
std::string doubleToString(double value) {
std::stringstream ss;
ss.precision(2);
ss << std::fixed << value;
return ss.str();
}
QuestionGenerator::QuestionGenerator(const std::string& userType) : userType(userType) {
srand(static_cast<unsigned int>(time(NULL)));
}
std::vector<Question> QuestionGenerator::generateQuestions(int count) {
std::vector<Question> questions;
for (int i = 0; i < count; ++i) {
Question question("", std::vector<std::string>(), 0);
if (userType == "elementary") {
question = generateElementaryQuestion();
} else if (userType == "middle") {
question = generateMiddleSchoolQuestion();
} else if (userType == "high") {
question = generateHighSchoolQuestion();
}
if (question.getText() != "" && question.getOptions().size() == 4) {
questions.push_back(question);
} else {
// 如果生成失败,重新生成
i--;
}
}
return questions;
}
// 获取操作符的优先级
int getOperatorPriority(char op) {
if (op == '*' || op == '/') return 2;
if (op == '+' || op == '-') return 1;
return 0;
}
// 计算两个数的运算结果
int calculate(int a, int b, char op) {
switch (op) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/':
if (b != 0 && a % b == 0) return a / b;
return a; // 如果除不尽,返回原值(应该不会发生)
default: return a;
}
}
// 使用栈计算表达式结果(正确考虑运算优先级)
int calculateWithPriority(const std::vector<int>& numbers, const std::vector<char>& operators) {
if (numbers.size() != operators.size() + 1) {
return numbers[0]; // 错误情况,返回第一个数
}
std::stack<int> numStack;
std::stack<char> opStack;
numStack.push(numbers[0]);
for (size_t i = 0; i < operators.size(); ++i) {
char currentOp = operators[i];
int currentNum = numbers[i + 1];
// 处理操作符优先级
while (!opStack.empty() && getOperatorPriority(opStack.top()) >= getOperatorPriority(currentOp)) {
int num2 = numStack.top(); numStack.pop();
int num1 = numStack.top(); numStack.pop();
char op = opStack.top(); opStack.pop();
numStack.push(calculate(num1, num2, op));
}
opStack.push(currentOp);
numStack.push(currentNum);
}
// 处理剩余的操作
while (!opStack.empty()) {
int num2 = numStack.top(); numStack.pop();
int num1 = numStack.top(); numStack.pop();
char op = opStack.top(); opStack.pop();
numStack.push(calculate(num1, num2, op));
}
return numStack.top();
}
// 生成表达式字符串(带括号显示优先级)
std::string generateExpressionString(const std::vector<int>& numbers, const std::vector<char>& operators) {
std::string expression = intToString(numbers[0]);
for (size_t i = 0; i < operators.size(); ++i) {
expression += " ";
expression += operators[i];
expression += " ";
expression += intToString(numbers[i + 1]);
}
return expression;
}
Question QuestionGenerator::generateElementaryQuestion() {
int operandCount = 2 + rand() % 2; // 2-3个操作数
std::vector<int> numbers;
std::vector<char> operators;
// 生成第一个数字
numbers.push_back(1 + rand() % 20);
// 生成操作符和数字
for (int i = 0; i < operandCount; ++i) {
char op;
int num = 1 + rand() % 15;
// 根据情况选择操作符
if (i == 0) {
// 第一个操作符随机选择
op = "+-*/"[rand() % 4];
} else {
// 后续操作符避免复杂组合
if (operators[i-1] == '*' || operators[i-1] == '/') {
op = "+-"[rand() % 2]; // 乘除后接加减
} else {
op = "+-*/"[rand() % 4];
}
}
// 特殊处理
if (op == '/') {
// 确保能整除
if (num == 0) num = 1;
// 调整被除数确保整除
numbers[i] = numbers[i] * num;
} else if (op == '-') {
// 确保减法结果不为负
// 先计算当前值
int currentValue = calculateWithPriority(numbers, operators);
if (currentValue - num < 0) {
num = rand() % currentValue + 1;
}
} else if (op == '*') {
// 避免结果过大
if (numbers[i] * num > 100) {
num = 2;
}
}
operators.push_back(op);
numbers.push_back(num);
}
// 计算正确结果
int result = calculateWithPriority(numbers, operators);
// 确保结果合理
if (result <= 0 || result > 200) {
return generateElementaryQuestion(); // 重新生成
}
// 生成表达式
std::string expression = generateExpressionString(numbers, operators);
std::string questionText = expression + " = ?";
if (isDuplicate(questionText)) {
return generateElementaryQuestion();
}
addToHistory(questionText);
// 生成选项
std::vector<std::string> options;
int correctIndex = rand() % 4;
// 生成3个错误答案
std::vector<int> wrongAnswers;
for (int i = 0; i < 3; ++i) {
int wrongAnswer;
int attempts = 0;
do {
attempts++;
if (attempts > 10) {
wrongAnswer = result + (i + 1) * 3;
break;
}
int offset = (rand() % 10) + 2;
if (rand() % 2 == 0) {
wrongAnswer = result + offset;
} else {
wrongAnswer = result - offset;
if (wrongAnswer < 1) wrongAnswer = result + offset;
}
} while (wrongAnswer == result ||
std::find(wrongAnswers.begin(), wrongAnswers.end(), wrongAnswer) != wrongAnswers.end());
wrongAnswers.push_back(wrongAnswer);
}
// 构建选项
for (int i = 0; i < 4; ++i) {
if (i == correctIndex) {
options.push_back(intToString(result));
} else {
options.push_back(intToString(wrongAnswers[i > correctIndex ? i-1 : i]));
}
}
// 调试输出
std::cout << "小学题目: " << questionText << std::endl;
std::cout << "计算顺序: ";
for (size_t i = 0; i < numbers.size(); ++i) {
std::cout << numbers[i];
if (i < operators.size()) {
std::cout << " " << operators[i] << " ";
}
}
std::cout << " = " << result << std::endl;
std::cout << "正确答案: " << result << std::endl;
std::cout << "------------------------" << std::endl;
return Question(questionText, options, correctIndex);
}
// 辅助函数:找到一个数的约数
int QuestionGenerator::findDivisor(int number) {
if (number <= 1) return 1;
for (int i = 2; i <= 10; ++i) {
if (number % i == 0) {
return i;
}
}
return 1;
}
Question QuestionGenerator::generateMiddleSchoolQuestion() {
int type = rand() % 2; // 0: 平方题, 1: 开方题
if (type == 0) {
// 平方题
int base = 2 + rand() % 15; // 2-16
int coefficient = 1 + rand() % 5;
int result = coefficient * base * base;
std::string questionText;
if (coefficient == 1) {
// 方案1: 使用Unicode上标
// questionText = intToString(base) + "2 = ?";
// 方案2: 使用文本描述
questionText = intToString(base) + "的平方 = ?";
} else {
// questionText = intToString(coefficient) + " × " + intToString(base) + "2 = ?";
questionText = intToString(coefficient) + " × " + intToString(base) + "的平方 = ?";
}
if (isDuplicate(questionText)) {
return generateMiddleSchoolQuestion();
}
addToHistory(questionText);
// 生成选项
std::vector<std::string> options;
int correctIndex = rand() % 4;
std::vector<int> wrongAnswers;
for (int i = 0; i < 3; ++i) {
int wrongAnswer;
do {
int offset = (rand() % 20) + 5;
if (rand() % 2 == 0) {
wrongAnswer = result + offset;
} else {
wrongAnswer = result - offset;
if (wrongAnswer <= 0) wrongAnswer = result + offset;
}
} while (wrongAnswer == result ||
std::find(wrongAnswers.begin(), wrongAnswers.end(), wrongAnswer) != wrongAnswers.end());
wrongAnswers.push_back(wrongAnswer);
}
for (int i = 0; i < 4; ++i) {
if (i == correctIndex) {
options.push_back(intToString(result));
} else {
options.push_back(intToString(wrongAnswers[i > correctIndex ? i-1 : i]));
}
}
return Question(questionText, options, correctIndex);
} else {
// 开方题
int base = 2 + rand() % 12; // 2-13
int squared = base * base;
// 方案1: 使用Unicode平方根符号
// std::string questionText = "√(" + intToString(squared) + ") = ?";
// 方案2: 使用文本描述
std::string questionText = "根号" + intToString(squared) + " = ?";
if (isDuplicate(questionText)) {
return generateMiddleSchoolQuestion();
}
addToHistory(questionText);
// 生成选项
std::vector<std::string> options;
int correctIndex = rand() % 4;
std::vector<int> wrongAnswers;
for (int i = 0; i < 3; ++i) {
int wrongAnswer;
do {
wrongAnswer = base + (rand() % 5) - 2; // -2到+2的偏移
if (wrongAnswer <= 0) wrongAnswer = 1;
} while (wrongAnswer == base ||
std::find(wrongAnswers.begin(), wrongAnswers.end(), wrongAnswer) != wrongAnswers.end());
wrongAnswers.push_back(wrongAnswer);
}
for (int i = 0; i < 4; ++i) {
if (i == correctIndex) {
options.push_back(intToString(base));
} else {
options.push_back(intToString(wrongAnswers[i > correctIndex ? i-1 : i]));
}
}
return Question(questionText, options, correctIndex);
}
}
Question QuestionGenerator::generateHighSchoolQuestion() {
const char* trigFunctions[] = {"sin", "cos", "tan"};
const char* currentFunc = trigFunctions[rand() % 3];
int angle = 0;
double result = 0.0;
int commonAngles[] = {0, 30, 45, 60, 90};
angle = commonAngles[rand() % 5];
double radian = angle * 3.14159 / 180.0;
if (std::string(currentFunc) == "sin") {
result = sin(radian);
} else if (std::string(currentFunc) == "cos") {
result = cos(radian);
} else {
if (angle == 90) angle = 45;
radian = angle * 3.14159 / 180.0;
result = tan(radian);
}
result = round(result * 100) / 100.0;
std::string questionText = std::string(currentFunc) + "(" + intToString(angle) + "°) = ?";
if (isDuplicate(questionText)) {
return generateHighSchoolQuestion();
}
addToHistory(questionText);
std::vector<std::string> options;
int correctIndex = rand() % 4;
std::vector<double> wrongAnswers;
for (int i = 0; i < 3; ++i) {
double wrongAnswer;
do {
double offset = (rand() % 20 + 5) / 100.0;
if (rand() % 2 == 0) {
wrongAnswer = result + offset;
} else {
wrongAnswer = result - offset;
if (wrongAnswer < -1.0) wrongAnswer = result + offset;
}
wrongAnswer = round(wrongAnswer * 100) / 100.0;
} while (wrongAnswer == result ||
std::find(wrongAnswers.begin(), wrongAnswers.end(), wrongAnswer) != wrongAnswers.end());
wrongAnswers.push_back(wrongAnswer);
}
for (int i = 0; i < 4; ++i) {
if (i == correctIndex) {
options.push_back(doubleToString(result));
} else {
options.push_back(doubleToString(wrongAnswers[i > correctIndex ? i-1 : i]));
}
}
return Question(questionText, options, correctIndex);
}
bool QuestionGenerator::isDuplicate(const std::string& questionText) {
for (std::set<std::string>::iterator it = questionHistory.begin(); it != questionHistory.end(); ++it) {
if (*it == questionText) {
return true;
}
}
return false;
}
void QuestionGenerator::addToHistory(const std::string& questionText) {
questionHistory.insert(questionText);
}
void QuestionGenerator::clearHistory() {
questionHistory.clear();
}

@ -0,0 +1,29 @@
#ifndef QUESTIONGENERATOR_H
#define QUESTIONGENERATOR_H
#include "Question.h"
#include <vector>
#include <string>
#include <set>
class QuestionGenerator {
public:
QuestionGenerator(const std::string& userType);
std::vector<Question> generateQuestions(int count);
void clearHistory();
private:
std::string userType;
std::set<std::string> questionHistory;
Question generateElementaryQuestion();
Question generateMiddleSchoolQuestion();
Question generateHighSchoolQuestion();
int findDivisor(int number);
bool isDuplicate(const std::string& questionText);
void addToHistory(const std::string& questionText);
};
#endif

Binary file not shown.

@ -0,0 +1,195 @@
#include "QuestionWindow.h"
#include "QuestionGenerator.h"
#include "SelectionWindow.h"
#include <string>
#include <sstream>
#include <windows.h>
#include <iostream> // 添加iostream用于调试输出
HWND QuestionWindow::hwnd = NULL;
HWND QuestionWindow::hQuestionText = NULL;
HWND QuestionWindow::hOptions[4] = {NULL, NULL, NULL, NULL};
HWND QuestionWindow::hNextButton = NULL;
HWND QuestionWindow::hProgress = NULL;
std::vector<Question> QuestionWindow::questions;
int QuestionWindow::currentQuestion = 0;
int QuestionWindow::score = 0;
void QuestionWindow::Show(const std::string& userType, int questionCount) {
// 如果窗口已存在,先销毁
if (hwnd != NULL) {
DestroyWindow(hwnd);
hwnd = NULL;
}
// 生成题目 - 使用传入的题目数量
QuestionGenerator generator(userType);
questions = generator.generateQuestions(questionCount);
currentQuestion = 0;
score = 0;
WNDCLASSEX wc;
memset(&wc, 0, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = "QuestionWindow";
RegisterClassEx(&wc);
hwnd = CreateWindowEx(0, "QuestionWindow", "数学学习软件 - 答题",
WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 400,
NULL, NULL, GetModuleHandle(NULL), NULL);
if (!hwnd) {
MessageBox(NULL, "创建答题窗口失败", "错误", MB_OK | MB_ICONERROR);
return;
}
CreateControls();
LoadQuestion();
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
}
void QuestionWindow::CreateControls() {
// 进度显示
hProgress = CreateWindow("STATIC", "", WS_VISIBLE | WS_CHILD | SS_LEFT,
10, 10, 200, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
// 题目文本
hQuestionText = CreateWindow("STATIC", "", WS_VISIBLE | WS_CHILD | SS_LEFT | WS_BORDER,
10, 50, 460, 60, hwnd, NULL, GetModuleHandle(NULL), NULL);
// 选项按钮
for (int i = 0; i < 4; i++) {
hOptions[i] = CreateWindow("BUTTON", "", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON,
10, 120 + i * 40, 460, 30, hwnd, (HMENU)(10 + i),
GetModuleHandle(NULL), NULL);
}
// 下一题按钮
hNextButton = CreateWindow("BUTTON", "下一题", WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
200, 300, 100, 30, hwnd, (HMENU)20, GetModuleHandle(NULL), NULL);
}
LRESULT CALLBACK QuestionWindow::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_COMMAND: {
int cmd = LOWORD(wParam);
if (cmd == 20) { // 下一题按钮
OnNextQuestion();
}
break;
}
case WM_CLOSE:
DestroyWindow(hwnd);
SelectionWindow::Show(); // 关闭时返回选择界面
break;
case WM_DESTROY:
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
void QuestionWindow::LoadQuestion() {
if (currentQuestion >= (int)questions.size()) {
ShowResult();
return;
}
Question& question = questions[currentQuestion];
// 更新进度
std::stringstream progress;
progress << "进度: " << (currentQuestion + 1) << "/" << questions.size();
SetWindowText(hProgress, progress.str().c_str());
// 设置题目文本
SetWindowText(hQuestionText, question.getText().c_str());
// 设置选项
std::vector<std::string> options = question.getOptions();
// 调试输出:显示正确答案(在控制台)
std::cout << "题目 " << (currentQuestion + 1) << ": " << question.getText() << std::endl;
std::cout << "正确答案: 选项" << (question.getCorrectAnswer() + 1)
<< " - " << options[question.getCorrectAnswer()] << std::endl;
std::cout << "所有选项: ";
for (int i = 0; i < 4; i++) {
std::cout << "[" << (i+1) << "]" << options[i] << " ";
}
std::cout << std::endl << std::endl;
for (int i = 0; i < 4; i++) {
if (i < (int)options.size()) {
SetWindowText(hOptions[i], options[i].c_str());
}
SendMessage(hOptions[i], BM_SETCHECK, BST_UNCHECKED, 0);
}
if (currentQuestion == (int)questions.size() - 1) {
SetWindowText(hNextButton, "提交");
}
}
void QuestionWindow::OnNextQuestion() {
// 检查当前选择的答案
int selectedAnswer = -1;
for (int i = 0; i < 4; i++) {
if (SendMessage(hOptions[i], BM_GETCHECK, 0, 0) == BST_CHECKED) {
selectedAnswer = i;
break;
}
}
if (selectedAnswer == -1) {
MessageBox(hwnd, "请选择一个答案", "提示", MB_OK | MB_ICONINFORMATION);
return;
}
// 检查答案
if (questions[currentQuestion].checkAnswer(selectedAnswer)) {
score++;
std::cout << "" << (currentQuestion + 1) << "题回答正确!" << std::endl;
} else {
std::cout << "" << (currentQuestion + 1) << "题回答错误!" << std::endl;
}
currentQuestion++;
LoadQuestion();
}
void QuestionWindow::ShowResult() {
std::stringstream result;
int total = questions.size();
int percentage = (total > 0) ? (score * 100 / total) : 0;
result << "答题完成!\n得分: " << score << "/" << total
<< "\n正确率: " << percentage << "%\n\n"
<< "请选择:\n"
<< "是 - 返回选择界面\n"
<< "否 - 结束程序";
// 显示带有选项的消息框
int choice = MessageBox(hwnd, result.str().c_str(), "答题结果",
MB_YESNO | MB_ICONINFORMATION | MB_DEFBUTTON1);
DestroyWindow(hwnd);
hwnd = NULL;
if (choice == IDYES) {
// 用户选择"是" - 继续做题,返回选择界面
SelectionWindow::Show();
} else {
// 用户选择"否" - 退出程序
PostQuitMessage(0);
}
}

@ -0,0 +1,32 @@
#ifndef QUESTIONWINDOW_H
#define QUESTIONWINDOW_H
#include <windows.h>
#include <vector>
#include <string>
#include "Question.h"
class QuestionWindow {
public:
static void Show(const std::string& userType, int questionCount);
static HWND GetHandle() { return hwnd; }
private:
static HWND hwnd;
static HWND hQuestionText;
static HWND hOptions[4];
static HWND hNextButton;
static HWND hProgress;
static std::vector<Question> questions;
static int currentQuestion;
static int score;
static void CreateControls();
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
static void LoadQuestion();
static void OnNextQuestion();
static void ShowResult();
};
#endif

Binary file not shown.

@ -0,0 +1,225 @@
#include "RegisterWindow.h"
#include "UserManager.h"
#include "LoginWindow.h"
#include <string>
#include <sstream>
#include <windows.h>
HWND RegisterWindow::hwnd = NULL;
HWND RegisterWindow::hEmail = NULL;
HWND RegisterWindow::hCode = NULL;
HWND RegisterWindow::hPassword = NULL;
HWND RegisterWindow::hConfirmPassword = NULL;
HWND RegisterWindow::hMessage = NULL;
std::string RegisterWindow::generatedCode = "";
void RegisterWindow::Show() {
// 如果窗口已存在,先销毁
if (hwnd != NULL) {
DestroyWindow(hwnd);
hwnd = NULL;
}
WNDCLASSEX wc;
memset(&wc, 0, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = "RegisterWindow";
RegisterClassEx(&wc);
hwnd = CreateWindowEx(0, "RegisterWindow", "数学学习软件 - 注册",
WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 450,
NULL, NULL, GetModuleHandle(NULL), NULL);
if (!hwnd) {
MessageBox(NULL, "创建注册窗口失败", "错误", MB_OK | MB_ICONERROR);
return;
}
CreateControls();
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
}
void RegisterWindow::CreateControls() {
// 标题
CreateWindow("STATIC", "用户注册", WS_VISIBLE | WS_CHILD | SS_CENTER,
10, 20, 360, 30, hwnd, NULL, GetModuleHandle(NULL), NULL);
// 邮箱标签和输入框
CreateWindow("STATIC", "邮箱:", WS_VISIBLE | WS_CHILD,
50, 70, 50, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
hEmail = CreateWindow("EDIT", "", WS_VISIBLE | WS_CHILD | WS_BORDER,
100, 70, 200, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
// 获取验证码按钮
CreateWindow("BUTTON", "获取验证码", WS_VISIBLE | WS_CHILD,
100, 110, 200, 30, hwnd, (HMENU)1, GetModuleHandle(NULL), NULL);
// 验证码标签和输入框
CreateWindow("STATIC", "验证码:", WS_VISIBLE | WS_CHILD,
50, 160, 50, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
hCode = CreateWindow("EDIT", "", WS_VISIBLE | WS_CHILD | WS_BORDER,
100, 160, 200, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
// 密码标签和输入框
CreateWindow("STATIC", "密码:", WS_VISIBLE | WS_CHILD,
50, 200, 50, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
hPassword = CreateWindow("EDIT", "", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_PASSWORD,
100, 200, 200, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
// 确认密码标签和输入框
CreateWindow("STATIC", "确认密码:", WS_VISIBLE | WS_CHILD,
30, 230, 70, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
hConfirmPassword = CreateWindow("EDIT", "", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_PASSWORD,
100, 230, 200, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
// 密码要求提示
CreateWindow("STATIC", "密码要求: 6-10位包含大小写字母和数字",
WS_VISIBLE | WS_CHILD | SS_CENTER,
50, 260, 300, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
// 注册按钮
CreateWindow("BUTTON", "注册", WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
100, 300, 80, 30, hwnd, (HMENU)2, GetModuleHandle(NULL), NULL);
// 返回按钮
CreateWindow("BUTTON", "返回", WS_VISIBLE | WS_CHILD,
200, 300, 80, 30, hwnd, (HMENU)3, GetModuleHandle(NULL), NULL);
// 消息标签
hMessage = CreateWindow("STATIC", "", WS_VISIBLE | WS_CHILD | SS_CENTER,
50, 340, 300, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
}
LRESULT CALLBACK RegisterWindow::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_COMMAND: {
int cmd = LOWORD(wParam);
if (cmd == 1) { // 获取验证码
OnGetCode();
} else if (cmd == 2) { // 注册按钮
OnRegister();
} else if (cmd == 3) { // 返回按钮
OnBack();
}
break;
}
case WM_CLOSE:
DestroyWindow(hwnd);
LoginWindow::Show();
break;
case WM_DESTROY:
// 不在WM_DESTROY中调用PostQuitMessage
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
void RegisterWindow::OnGetCode() {
char email[100];
GetWindowText(hEmail, email, sizeof(email));
std::string strEmail(email);
if (strEmail.empty()) {
SetWindowText(hMessage, "请输入邮箱地址");
return;
}
UserManager& userManager = UserManager::getInstance();
if (!userManager.validateEmailFormat(strEmail)) {
SetWindowText(hMessage, "邮箱格式不正确");
return;
}
if (userManager.isEmailRegistered(strEmail)) {
SetWindowText(hMessage, "该邮箱已注册");
return;
}
// 显示发送中提示
SetWindowText(hMessage, "正在发送验证码,请稍候...");
// 生成并发送验证码(不显示在界面上)
generatedCode = userManager.generateRegistrationCode(strEmail);
if (!generatedCode.empty()) {
SetWindowText(hMessage, "验证码已发送到您的邮箱,请查收!");
// 清空验证码输入框,让用户手动输入
SetWindowText(hCode, "");
// 设置焦点到验证码输入框
SetFocus(hCode);
} else {
SetWindowText(hMessage, "邮件发送失败,请检查邮箱地址或重试");
generatedCode = ""; // 清空验证码
}
}
void RegisterWindow::OnRegister() {
char email[100], code[100], password[100], confirmPassword[100];
GetWindowText(hEmail, email, sizeof(email));
GetWindowText(hCode, code, sizeof(code));
GetWindowText(hPassword, password, sizeof(password));
GetWindowText(hConfirmPassword, confirmPassword, sizeof(confirmPassword));
std::string strEmail(email);
std::string strCode(code);
std::string strPassword(password);
std::string strConfirmPassword(confirmPassword);
// 验证输入
if (strEmail.empty() || strCode.empty() || strPassword.empty() || strConfirmPassword.empty()) {
SetWindowText(hMessage, "请填写所有字段");
return;
}
// 验证验证码
if (strCode != generatedCode) {
SetWindowText(hMessage, "验证码不正确");
return;
}
// 验证密码一致性
if (strPassword != strConfirmPassword) {
SetWindowText(hMessage, "两次输入的密码不一致");
return;
}
UserManager& userManager = UserManager::getInstance();
// 验证密码格式
if (!userManager.validatePasswordFormat(strPassword)) {
SetWindowText(hMessage, "密码格式不符合要求");
return;
}
// 注册用户
if (registerUserWithPassword(strEmail, strPassword)) {
SetWindowText(hMessage, "注册成功!正在跳转到登录页面...");
Sleep(2000); // 等待2秒显示成功消息
OnBack();
} else {
SetWindowText(hMessage, "注册失败,请重试");
}
}
bool RegisterWindow::registerUserWithPassword(const std::string& email, const std::string& password) {
UserManager& userManager = UserManager::getInstance();
return userManager.registerUserWithPassword(email, password);
}
void RegisterWindow::OnBack() {
DestroyWindow(hwnd);
hwnd = NULL; // 重置窗口句柄
LoginWindow::Show();
}

@ -0,0 +1,30 @@
#ifndef REGISTERWINDOW_H
#define REGISTERWINDOW_H
#include <windows.h>
#include <string>
class RegisterWindow {
public:
static void Show();
static HWND GetHandle() { return hwnd; }
private:
static HWND hwnd;
static HWND hEmail;
static HWND hCode;
static HWND hPassword;
static HWND hConfirmPassword;
static HWND hMessage;
static std::string generatedCode;
static void CreateControls();
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
static void OnGetCode();
static void OnRegister();
static void OnBack();
static bool registerUserWithPassword(const std::string& email, const std::string& password);
};
#endif

Binary file not shown.

@ -0,0 +1,161 @@
#include "SelectionWindow.h"
#include "QuestionWindow.h"
#include "LoginWindow.h"
#include "UserManager.h"
#include <string>
HWND SelectionWindow::hwnd = NULL;
HWND SelectionWindow::hQuestionCount = NULL;
void SelectionWindow::Show() {
// 如果窗口已存在,先销毁
if (hwnd != NULL) {
DestroyWindow(hwnd);
hwnd = NULL;
}
WNDCLASSEX wc;
memset(&wc, 0, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = "SelectionWindow";
RegisterClassEx(&wc);
hwnd = CreateWindowEx(0, "SelectionWindow", "数学学习软件 - 选择难度",
WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 350,
NULL, NULL, GetModuleHandle(NULL), NULL);
if (!hwnd) {
MessageBox(NULL, "创建选择窗口失败", "错误", MB_OK | MB_ICONERROR);
return;
}
CreateControls();
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
}
void SelectionWindow::CreateControls() {
// 标题
CreateWindow("STATIC", "请选择题目难度", WS_VISIBLE | WS_CHILD | SS_CENTER,
10, 20, 360, 30, hwnd, NULL, GetModuleHandle(NULL), NULL);
// 题目数量输入
CreateWindow("STATIC", "题目数量 (10-30):", WS_VISIBLE | WS_CHILD,
50, 60, 120, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
hQuestionCount = CreateWindow("EDIT", "10", WS_VISIBLE | WS_CHILD | WS_BORDER,
180, 60, 60, 25, hwnd, NULL, GetModuleHandle(NULL), NULL);
// 小学按钮
CreateWindow("BUTTON", "小学题目", WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
100, 100, 200, 40, hwnd, (HMENU)1, GetModuleHandle(NULL), NULL);
// 初中按钮
CreateWindow("BUTTON", "初中题目", WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
100, 150, 200, 40, hwnd, (HMENU)2, GetModuleHandle(NULL), NULL);
// 高中按钮
CreateWindow("BUTTON", "高中题目", WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
100, 200, 200, 40, hwnd, (HMENU)3, GetModuleHandle(NULL), NULL);
// 退出登录按钮
CreateWindow("BUTTON", "退出登录", WS_VISIBLE | WS_CHILD,
150, 260, 100, 30, hwnd, (HMENU)4, GetModuleHandle(NULL), NULL);
}
LRESULT CALLBACK SelectionWindow::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_COMMAND: {
int cmd = LOWORD(wParam);
if (cmd == 1) { // 小学题目
OnElementarySelected();
} else if (cmd == 2) { // 初中题目
OnMiddleSchoolSelected();
} else if (cmd == 3) { // 高中题目
OnHighSchoolSelected();
} else if (cmd == 4) { // 退出登录
OnLogout();
}
break;
}
case WM_CLOSE:
DestroyWindow(hwnd);
LoginWindow::Show(); // 关闭选择窗口时返回登录界面
break;
case WM_DESTROY:
// 不在WM_DESTROY中调用PostQuitMessage
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
bool SelectionWindow::validateQuestionCount() {
int count = getQuestionCount();
if (count < 10 || count > 30) {
MessageBox(hwnd, "题目数量必须在10-30之间", "输入错误", MB_OK | MB_ICONWARNING);
SetFocus(hQuestionCount);
return false;
}
return true;
}
int SelectionWindow::getQuestionCount() {
char countText[10];
GetWindowText(hQuestionCount, countText, sizeof(countText));
// 转换为整数
int count = atoi(countText);
if (count <= 0) {
count = 10; // 默认值
}
return count;
}
void SelectionWindow::OnElementarySelected() {
if (!validateQuestionCount()) {
return;
}
int questionCount = getQuestionCount();
DestroyWindow(hwnd);
hwnd = NULL;
QuestionWindow::Show("elementary", questionCount);
}
void SelectionWindow::OnMiddleSchoolSelected() {
if (!validateQuestionCount()) {
return;
}
int questionCount = getQuestionCount();
DestroyWindow(hwnd);
hwnd = NULL;
QuestionWindow::Show("middle", questionCount);
}
void SelectionWindow::OnHighSchoolSelected() {
if (!validateQuestionCount()) {
return;
}
int questionCount = getQuestionCount();
DestroyWindow(hwnd);
hwnd = NULL;
QuestionWindow::Show("high", questionCount);
}
void SelectionWindow::OnLogout() {
UserManager::getInstance().logout();
DestroyWindow(hwnd);
hwnd = NULL;
LoginWindow::Show();
}

@ -0,0 +1,25 @@
#ifndef SELECTIONWINDOW_H
#define SELECTIONWINDOW_H
#include <windows.h>
class SelectionWindow {
public:
static void Show();
static HWND GetHandle() { return hwnd; }
private:
static HWND hwnd;
static HWND hQuestionCount;
static void CreateControls();
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
static void OnElementarySelected();
static void OnMiddleSchoolSelected();
static void OnHighSchoolSelected();
static void OnLogout();
static bool validateQuestionCount();
static int getQuestionCount();
};
#endif

Binary file not shown.

@ -0,0 +1,24 @@
#include "User.h"
User::User(const std::string& email, const std::string& password, const std::string& userType)
: email(email), password(password), userType(userType) {}
std::string User::getEmail() const {
return email;
}
std::string User::getPassword() const {
return password;
}
std::string User::getUserType() const {
return userType;
}
void User::setPassword(const std::string& newPassword) {
password = newPassword;
}
bool User::validatePassword(const std::string& password) const {
return this->password == password;
}

@ -0,0 +1,23 @@
#ifndef USER_H
#define USER_H
#include <string>
class User {
public:
User(const std::string& email, const std::string& password, const std::string& userType);
std::string getEmail() const;
std::string getPassword() const;
std::string getUserType() const;
void setPassword(const std::string& newPassword);
bool validatePassword(const std::string& password) const;
private:
std::string email;
std::string password;
std::string userType;
};
#endif

Binary file not shown.

@ -0,0 +1,164 @@
#include "UserManager.h"
#include "User.h"
#include "EmailSender.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cctype>
#include <cstdlib>
#include <ctime>
#include <windows.h>
#include <sstream>
UserManager::UserManager() {
currentUser = NULL;
srand(static_cast<unsigned int>(time(NULL)));
loadUsers();
}
UserManager& UserManager::getInstance() {
static UserManager instance;
return instance;
}
bool UserManager::validateEmailFormat(const std::string& email) {
size_t atPos = email.find('@');
size_t dotPos = email.find('.');
return atPos != std::string::npos && dotPos != std::string::npos && atPos < dotPos;
}
bool UserManager::validatePasswordFormat(const std::string& password) {
if (password.length() < 6 || password.length() > 10) return false;
bool hasUpper = false, hasLower = false, hasDigit = false;
for (size_t i = 0; i < password.length(); ++i) {
char c = password[i];
if (isupper(c)) hasUpper = true;
if (islower(c)) hasLower = true;
if (isdigit(c)) hasDigit = true;
}
return hasUpper && hasLower && hasDigit;
}
std::string UserManager::generateRandomCode(int length) {
const std::string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
std::string code;
for (int i = 0; i < length; ++i) {
code += chars[rand() % chars.size()];
}
return code;
}
bool UserManager::sendEmail(const std::string& email, const std::string& code) {
// 使用EmailSender类发送邮件
return EmailSender::sendVerificationCode(email, code);
}
std::string UserManager::generateRegistrationCode(const std::string& email) {
std::string code = generateRandomCode(6); // 6位验证码
// 发送邮件
if (sendEmail(email, code)) {
return code;
} else {
return ""; // 发送失败返回空字符串
}
}
bool UserManager::registerUser(const std::string& email) {
if (isEmailRegistered(email)) return false;
std::string defaultPassword = "Temp123";
users.push_back(User(email, defaultPassword, "elementary"));
saveUsers();
return true;
}
bool UserManager::registerUserWithPassword(const std::string& email, const std::string& password) {
if (isEmailRegistered(email)) return false;
// 验证密码格式
if (!validatePasswordFormat(password)) {
return false;
}
users.push_back(User(email, password, "elementary"));
saveUsers();
return true;
}
bool UserManager::login(const std::string& email, const std::string& password) {
for (size_t i = 0; i < users.size(); ++i) {
User& user = users[i];
if (user.getEmail() == email && user.validatePassword(password)) {
currentUser = &user;
return true;
}
}
return false;
}
bool UserManager::changePassword(const std::string& email, const std::string& oldPassword, const std::string& newPassword) {
for (size_t i = 0; i < users.size(); ++i) {
User& user = users[i];
if (user.getEmail() == email && user.validatePassword(oldPassword)) {
if (validatePasswordFormat(newPassword)) {
user.setPassword(newPassword);
saveUsers();
return true;
}
}
}
return false;
}
bool UserManager::isEmailRegistered(const std::string& email) {
for (size_t i = 0; i < users.size(); ++i) {
const User& user = users[i];
if (user.getEmail() == email) {
return true;
}
}
return false;
}
void UserManager::loadUsers() {
std::ifstream file("data/users.txt");
if (!file) return;
std::string line;
while (std::getline(file, line)) {
size_t pos1 = line.find(',');
size_t pos2 = line.find(',', pos1 + 1);
if (pos1 != std::string::npos && pos2 != std::string::npos) {
std::string email = line.substr(0, pos1);
std::string password = line.substr(pos1 + 1, pos2 - pos1 - 1);
std::string userType = line.substr(pos2 + 1);
users.push_back(User(email, password, userType));
}
}
}
void UserManager::saveUsers() {
// 创建数据目录
CreateDirectory("data", NULL);
std::ofstream file("data/users.txt");
for (size_t i = 0; i < users.size(); ++i) {
const User& user = users[i];
file << user.getEmail() << "," << user.getPassword() << "," << user.getUserType() << "\n";
}
}
User* UserManager::getCurrentUser() {
return currentUser;
}
void UserManager::logout() {
currentUser = NULL;
}

@ -0,0 +1,36 @@
#ifndef USERMANAGER_H
#define USERMANAGER_H
#include "User.h"
#include <vector>
#include <string>
class UserManager {
public:
static UserManager& getInstance();
bool registerUser(const std::string& email);
bool registerUserWithPassword(const std::string& email, const std::string& password);
bool login(const std::string& email, const std::string& password);
bool changePassword(const std::string& email, const std::string& oldPassword, const std::string& newPassword);
User* getCurrentUser();
void logout();
// 修改方法签名添加email参数
std::string generateRegistrationCode(const std::string& email);
std::string generateRandomCode(int length);
bool isEmailRegistered(const std::string& email);
bool validatePasswordFormat(const std::string& password);
bool validateEmailFormat(const std::string& email);
void loadUsers();
void saveUsers();
private:
UserManager();
std::vector<User> users;
User* currentUser;
bool sendEmail(const std::string& email, const std::string& code);
};
#endif

Binary file not shown.

@ -0,0 +1,2 @@
1853439381@qq.com,Temp123,elementary
2470975401@qq.com,123456QWe,elementary

@ -0,0 +1,3 @@
? 成功发送验证码到: 3675658020@qq.com | 验证码: OP9R0N | 时间: 22:45:10
? 成功发送验证码到: 3675658020@qq.com | 验证码: B3XMEI | 时间: 22:45:10
? 成功发送验证码到: 3675658020@qq.com | 验证码: BG5ZSN | 时间: 22:45:10

@ -0,0 +1,13 @@
=== 邮件发送记录 ===
时间: Oct 7 2025 23:35:05
方案: PowerShell
发件人: 3675658020@qq.com
收件人: 2470975401@qq.com
验证码: ZH4RXI
执行结果: 正在发送邮件到: 2470975401@qq.com
验证码: ZH4RXI
邮件发送成功!
SUCCESS
=========================

@ -0,0 +1,27 @@
#include <windows.h>
#include <cstdlib>
#include <ctime>
#include "LoginWindow.h"
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 初始化随机数种子
srand(static_cast<unsigned int>(time(NULL)));
// 显示登录窗口
LoginWindow::Show();
// 主消息循环
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (bRet == -1) {
// 错误处理
break;
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}

Binary file not shown.

@ -0,0 +1,44 @@
# 数学学习软件邮件发送脚本
# 发件人配置
$SMTPServer = "smtp.qq.com"
$SMTPPort = "587"
$Username = "3675658020@qq.com"
$Password = "lsagdbxwfolhcheb"
$EmailFrom = "3675658020@qq.com"
$EmailTo = "2470975401@qq.com"
$Subject = "数学学习软件 - 注册验证码"
$Body = @"
ZH4RXI
10使
"@
Write-Host "正在发送邮件到: $EmailTo"
Write-Host "验证码: ZH4RXI"
try {
# 创建邮件消息
$SMTPMessage = New-Object System.Net.Mail.MailMessage($EmailFrom, $EmailTo, $Subject, $Body)
# 创建SMTP客户端
$SMTPClient = New-Object Net.Mail.SmtpClient($SMTPServer, $SMTPPort)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential($Username, $Password)
# 发送邮件
$SMTPClient.Send($SMTPMessage)
Write-Host "邮件发送成功!" -ForegroundColor Green
Write-Output "SUCCESS"
} catch {
Write-Host "邮件发送失败: $($_.Exception.Message)" -ForegroundColor Red
Write-Output "ERROR: $($_.Exception.Message)"
}

@ -0,0 +1,242 @@
[Project]
FileName=test3.dev
Name=ÏîÄ¿1
Type=0
Ver=2
ObjFiles=
Includes=
Libs=
PrivateResource=
ResourceIncludes=
MakeIncludes=
Compiler=
CppCompiler=
Linker=-lws2_32 -lwininet_@@_
IsCpp=1
Icon=
ExeOutput=
ObjectOutput=
LogOutput=
LogOutputEnabled=0
OverrideOutput=0
OverrideOutputName=test3.exe
HostApplication=
UseCustomMakefile=0
CustomMakefile=
CommandLine=
Folders=
IncludeVersionInfo=0
SupportXPThemes=0
CompilerSet=0
CompilerSettings=0000000000000000000000000
UnitCount=19
[VersionInfo]
Major=1
Minor=0
Release=0
Build=0
LanguageID=1033
CharsetID=1252
CompanyName=
FileVersion=1.0.0.0
FileDescription=Developed using the Dev-C++ IDE
InternalName=
LegalCopyright=
LegalTrademarks=
OriginalFilename=
ProductName=
ProductVersion=1.0.0.0
AutoIncBuildNr=0
SyncProduct=1
[Unit9]
FileName=User.h
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit1]
FileName=Question.h
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit2]
FileName=UserManager.h
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit3]
FileName=UserManager.cpp
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit4]
FileName=User.cpp
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit5]
FileName=QuestionGenerator.h
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit6]
FileName=QuestionGenerator.cpp
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit7]
FileName=Question.cpp
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit8]
FileName=LoginWindow.h
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit10]
FileName=LoginWindow.cpp
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit11]
FileName=RegisterWindow.h
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit12]
FileName=RegisterWindow.cpp
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit13]
FileName=QuestionWindow.h
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit14]
FileName=QuestionWindow.cpp
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit15]
FileName=main.cpp
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit17]
FileName=EmailSender.h
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit16]
FileName=EmailSender.cpp
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit19]
FileName=SelectionWindow.h
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit18]
FileName=SelectionWindow.cpp
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=

Binary file not shown.

@ -0,0 +1,98 @@
[Editor_0]
CursorCol=7
CursorRow=25
TopLine=1
LeftChar=1
[Editors]
Order=8,0,1,2,3,4,5,6,7,9,10,11,12,13,14,15,16,17,18
Focused=13
[Editor_1]
CursorCol=7
CursorRow=36
TopLine=1
LeftChar=1
[Editor_2]
CursorCol=2
CursorRow=164
TopLine=122
LeftChar=1
[Editor_3]
CursorCol=2
CursorRow=24
TopLine=1
LeftChar=1
[Editor_4]
CursorCol=7
CursorRow=29
TopLine=1
LeftChar=1
[Editor_5]
CursorCol=2
CursorRow=364
TopLine=326
LeftChar=1
[Editor_6]
CursorCol=2
CursorRow=27
TopLine=1
LeftChar=1
[Editor_7]
CursorCol=7
CursorRow=23
TopLine=1
LeftChar=1
[Editor_8]
CursorCol=7
CursorRow=23
TopLine=1
LeftChar=1
[Editor_9]
CursorCol=2
CursorRow=135
TopLine=1
LeftChar=1
[Editor_10]
CursorCol=7
CursorRow=30
TopLine=1
LeftChar=1
[Editor_11]
CursorCol=2
CursorRow=225
TopLine=131
LeftChar=1
[Editor_12]
CursorCol=7
CursorRow=32
TopLine=1
LeftChar=1
[Editor_13]
CursorCol=18
CursorRow=179
TopLine=150
LeftChar=1
[Editor_14]
CursorCol=2
CursorRow=27
TopLine=1
LeftChar=1
[Editor_15]
CursorCol=56
CursorRow=32
TopLine=1
LeftChar=1
[Editor_16]
CursorCol=7
CursorRow=16
TopLine=1
LeftChar=1
[Editor_17]
CursorCol=2
CursorRow=161
TopLine=1
LeftChar=1
[Editor_18]
CursorCol=7
CursorRow=25
TopLine=1
LeftChar=1
Loading…
Cancel
Save