You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ExamSystem/src/shared/backend_interface.cc

548 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "backend_interface.h"
#include <random>
#include <algorithm>
#include <sstream>
#include <iomanip>
#include <curl/curl.h>
namespace exam_system {
// 常量配置
namespace {
constexpr int kVerificationCodeLength = 6;
constexpr int kVerificationCodeExpirySeconds = 300; // 5分钟
constexpr int kOptionsCount = 4;
// libcurl 写回调函数
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* response) {
size_t total_size = size * nmemb;
response->append((char*)contents, total_size);
return total_size;
}
} // namespace
BackendImpl::BackendImpl() {
// 初始化 ExamSystem
exam_system_ = std::make_unique<ExamSystem>();
// 配置邮箱设置
email_config_.smtp_server = "smtp.163.com";
email_config_.smtp_port = 587; // 使用STARTTLS
email_config_.username = "your_email@163.com";
email_config_.password = "your_authorization_code"; // 注意:这是授权码,不是登录密码
email_config_.use_curl = true;
// 初始化libcurl
curl_global_init(CURL_GLOBAL_DEFAULT);
Logger::Log(Logger::Level::INFO, "BackendImpl初始化完成");
}
BackendImpl::~BackendImpl() {
curl_global_cleanup();
}
bool BackendImpl::SendVerificationCode(const std::string& email) {
if (email.empty() || email.find('@') == std::string::npos) {
Logger::Log(Logger::Level::ERROR, "邮箱地址格式无效: " + email);
return false;
}
// 清理过期的验证码
CleanExpiredVerificationCodes();
// 生成验证码
std::string code = GenerateVerificationCode();
// 保存验证码
VerificationCode vc;
vc.code = code;
vc.generate_time = std::time(nullptr);
vc.email = email;
verification_codes_[email] = vc;
// 发送邮件
std::string subject = "数学考试系统 - 验证码";
std::string body = "您的验证码是: " + code + "\n\n";
body += "该验证码有效期为5分钟请尽快使用。\n";
body += "如果不是您本人操作,请忽略此邮件。\n\n";
body += "数学考试系统团队";
bool send_success = SendEmail(email, subject, body);
if (send_success) {
Logger::Log(Logger::Level::INFO,
"验证码发送成功至: " + email + ", 验证码: " + code);
return true;
} else {
Logger::Log(Logger::Level::ERROR, "验证码发送失败: " + email);
verification_codes_.erase(email);
return false;
}
}
bool BackendImpl::UserRegister(const std::string& username,
const std::string& password,
const std::string& email,
const std::string& code) {
// 输入验证
if (username.empty() || password.empty() || email.empty() || code.empty()) {
Logger::Log(Logger::Level::ERROR, "注册信息不完整");
return false;
}
// 验证邮箱验证码
if (!ValidateVerificationCode(email, code)) {
Logger::Log(Logger::Level::ERROR, "验证码错误或已过期");
return false;
}
// 创建用户信息并添加到ExamSystem
UserInfo new_user;
new_user.username = username;
new_user.password = password;
new_user.email = email;
new_user.user_type = "小学"; // 默认类型
bool success = exam_system_->AddUser(new_user);
if (success) {
// 注册成功后清除验证码
verification_codes_.erase(email);
Logger::Log(Logger::Level::INFO,
"用户注册成功: " + username + ", 邮箱: " + email);
} else {
Logger::Log(Logger::Level::ERROR, "用户注册失败: " + username);
}
return success;
}
bool BackendImpl::UserLogin(const std::string& username,
const std::string& password) {
if (username.empty() || password.empty()) {
Logger::Log(Logger::Level::ERROR, "用户名或密码为空");
return false;
}
// 使用ExamSystem进行登录
LoginRequest request;
request.username = username;
request.password = password;
LoginResponse response = exam_system_->Login(request);
if (response.success) {
current_user_ = username;
Logger::Log(Logger::Level::INFO, "用户登录成功: " + username);
return true;
} else {
Logger::Log(Logger::Level::ERROR,
"用户登录失败: " + username + ", 错误: " + response.error_message);
return false;
}
}
void BackendImpl::UserLogout() {
if (!current_user_.empty()) {
exam_system_->Logout(current_user_);
current_user_.clear();
user_questions_.erase(current_user_);
current_correct_answers_.clear();
Logger::Log(Logger::Level::INFO, "用户登出: " + current_user_);
}
}
bool BackendImpl::ChangePassword(const std::string& old_password,
const std::string& new_password) {
if (current_user_.empty()) {
Logger::Log(Logger::Level::ERROR, "用户未登录,无法修改密码");
return false;
}
if (new_password.empty()) {
Logger::Log(Logger::Level::ERROR, "新密码不能为空");
return false;
}
// 使用ExamSystem修改密码
ChangePasswordRequest request;
request.username = current_user_;
request.old_password = old_password;
request.new_password = new_password;
ChangePasswordResponse response = exam_system_->ChangePassword(request);
if (response.success) {
Logger::Log(Logger::Level::INFO, "密码修改成功: " + current_user_);
return true;
} else {
Logger::Log(Logger::Level::ERROR,
"密码修改失败: " + current_user_ + ", 错误: " + response.error_message);
return false;
}
}
void BackendImpl::UserExit() {
UserLogout();
Logger::Log(Logger::Level::INFO, "用户退出系统");
}
std::vector<QuestionInfo> BackendImpl::GenerateQuestions(
const std::string& difficulty, int count) {
std::vector<QuestionInfo> questions;
if (current_user_.empty()) {
Logger::Log(Logger::Level::ERROR, "用户未登录,无法生成题目");
return questions;
}
// 使用ExamSystem生成题目
GenerateRequest request;
request.username = current_user_;
request.difficulty = difficulty;
request.count = count;
GenerateResponse response = exam_system_->GenerateProblems(request);
if (!response.success) {
Logger::Log(Logger::Level::ERROR,
"生成题目失败: " + response.error_message);
return questions;
}
// 转换为前端需要的格式
current_correct_answers_.clear();
for (size_t i = 0; i < response.problems.size(); ++i) {
QuestionInfo question;
question.id = i + 1;
// 提取题目内容(去除题号)
std::string problem_content = response.problems[i];
size_t dot_pos = problem_content.find('.');
if (dot_pos != std::string::npos) {
problem_content = problem_content.substr(dot_pos + 2); // 跳过". "
}
question.content = problem_content;
// 计算正确答案
std::string correct_answer = CalculateSimpleAnswer(problem_content);
current_correct_answers_.push_back(correct_answer);
// 生成选项
question.options = GenerateOptions(correct_answer);
// 设置正确答案索引
for (size_t j = 0; j < question.options.size(); ++j) {
if (question.options[j] == correct_answer) {
question.correct_answer = j;
break;
}
}
questions.push_back(question);
}
// 缓存用户题目
user_questions_[current_user_] = questions;
// 保存题目到文件
exam_system_->SaveProblems(current_user_, response.problems);
Logger::Log(Logger::Level::INFO,
"为用户 " + current_user_ + " 生成 " +
std::to_string(count) + "" + difficulty + " 难度题目");
return questions;
}
TestResult BackendImpl::SubmitAnswers(const std::vector<int>& user_answers) {
TestResult result;
result.total_questions = 0;
result.correct_answers = 0;
result.score = 0.0;
result.difficulty = "unknown";
if (current_user_.empty()) {
Logger::Log(Logger::Level::ERROR, "用户未登录,无法提交答案");
return result;
}
// 获取用户当前题目
auto it = user_questions_.find(current_user_);
if (it == user_questions_.end()) {
Logger::Log(Logger::Level::ERROR, "用户没有生成的题目");
return result;
}
const std::vector<QuestionInfo>& questions = it->second;
result.total_questions = questions.size();
if (user_answers.size() != questions.size()) {
Logger::Log(Logger::Level::ERROR,
"答案数量与题目数量不匹配: " +
std::to_string(user_answers.size()) + " vs " +
std::to_string(questions.size()));
return result;
}
// 统计正确答案
result.correct_answers = 0;
for (size_t i = 0; i < questions.size(); ++i) {
if (user_answers[i] >= 0 && user_answers[i] < static_cast<int>(questions[i].options.size()) &&
user_answers[i] == questions[i].correct_answer) {
++result.correct_answers;
}
}
// 计算得分
result.score = CalculateScore(result.correct_answers, result.total_questions);
// 设置难度(从最后生成的题目中推断)
if (!questions.empty()) {
// 这里可以根据题目内容推断难度简化实现使用primary
result.difficulty = "primary";
}
Logger::Log(Logger::Level::INFO,
"用户 " + current_user_ + " 提交答案,正确: " +
std::to_string(result.correct_answers) + "/" +
std::to_string(result.total_questions) +
", 得分: " + std::to_string(result.score));
return result;
}
std::string BackendImpl::GetCurrentUser() const {
return current_user_;
}
// ==================== 私有方法实现 ====================
bool BackendImpl::SendEmail(const std::string& recipient,
const std::string& subject,
const std::string& body) {
if (email_config_.use_curl) {
return SendEmailViaCurl(recipient, subject, body);
} else {
// 备用邮件发送方法
Logger::Log(Logger::Level::ERROR, "仅支持libcurl邮件发送");
return false;
}
}
bool BackendImpl::SendEmailViaCurl(const std::string& recipient,
const std::string& subject,
const std::string& body) {
CURL* curl;
CURLcode res = CURLE_OK;
struct curl_slist* recipients = nullptr;
std::string response_string;
curl = curl_easy_init();
if (!curl) {
Logger::Log(Logger::Level::ERROR, "libcurl初始化失败");
return false;
}
// 设置SMTP服务器
curl_easy_setopt(curl, CURLOPT_URL, ("smtp://" + email_config_.smtp_server + ":" + std::to_string(email_config_.smtp_port)).c_str());
// 设置用户名和密码
curl_easy_setopt(curl, CURLOPT_USERNAME, email_config_.username.c_str());
curl_easy_setopt(curl, CURLOPT_PASSWORD, email_config_.password.c_str());
// 设置发件人和收件人
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, email_config_.username.c_str());
recipients = curl_slist_append(recipients, recipient.c_str());
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
// 设置邮件内容
std::string email_data =
"To: " + recipient + "\r\n"
"From: " + email_config_.username + "\r\n"
"Subject: " + subject + "\r\n"
"\r\n" + body + "\r\n";
curl_easy_setopt(curl, CURLOPT_READFUNCTION, [](char* buffer, size_t size, size_t nitems, void* instream) -> size_t {
std::string* email_data = static_cast<std::string*>(instream);
size_t buffer_size = size * nitems;
if (email_data->empty()) {
return 0;
}
size_t copy_size = std::min(buffer_size, email_data->size());
memcpy(buffer, email_data->c_str(), copy_size);
email_data->erase(0, copy_size);
return copy_size;
});
curl_easy_setopt(curl, CURLOPT_READDATA, &email_data);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
// 启用TLS
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
// 设置服务器证书验证生产环境应该设为1
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
// 设置响应回调
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_string);
// 设置超时
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
// 发送邮件
res = curl_easy_perform(curl);
// 清理
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
if (res != CURLE_OK) {
Logger::Log(Logger::Level::ERROR,
"邮件发送失败: " + std::string(curl_easy_strerror(res)));
return false;
}
Logger::Log(Logger::Level::INFO, "邮件发送成功: " + recipient);
return true;
}
std::string BackendImpl::GenerateVerificationCode() {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int> dis(0, 9);
std::string code;
for (int i = 0; i < kVerificationCodeLength; ++i) {
code += std::to_string(dis(gen));
}
return code;
}
bool BackendImpl::ValidateVerificationCode(const std::string& email,
const std::string& code) {
auto it = verification_codes_.find(email);
if (it == verification_codes_.end()) {
return false;
}
const VerificationCode& vc = it->second;
std::time_t current_time = std::time(nullptr);
// 检查验证码是否过期
if (current_time - vc.generate_time > kVerificationCodeExpirySeconds) {
verification_codes_.erase(it);
return false;
}
// 检查验证码是否匹配
if (vc.code != code) {
return false;
}
return true;
}
void BackendImpl::CleanExpiredVerificationCodes() {
std::time_t current_time = std::time(nullptr);
std::vector<std::string> expired_emails;
for (const auto& pair : verification_codes_) {
if (current_time - pair.second.generate_time > kVerificationCodeExpirySeconds) {
expired_emails.push_back(pair.first);
}
}
for (const auto& email : expired_emails) {
verification_codes_.erase(email);
Logger::Log(Logger::Level::INFO, "清理过期验证码: " + email);
}
}
std::string BackendImpl::CalculateSimpleAnswer(const std::string& problem) {
// 简化实现,根据题目类型返回示例答案
// 实际项目中应该使用完整的表达式计算库
if (problem.find('+') != std::string::npos) {
return "25";
} else if (problem.find('-') != std::string::npos) {
return "12";
} else if (problem.find('*') != std::string::npos) {
return "36";
} else if (problem.find('/') != std::string::npos) {
return "4";
} else if (problem.find('²') != std::string::npos) {
return "64";
} else if (problem.find('') != std::string::npos) {
return "8";
} else if (problem.find("sin") != std::string::npos) {
return "0.50";
} else if (problem.find("cos") != std::string::npos) {
return "0.87";
} else if (problem.find("tan") != std::string::npos) {
return "1.00";
}
return "42"; // 默认答案
}
std::vector<std::string> BackendImpl::GenerateOptions(const std::string& correct_answer) {
std::vector<std::string> options(kOptionsCount);
// 设置正确答案到一个随机位置
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int> pos_dist(0, kOptionsCount - 1);
int correct_pos = pos_dist(gen);
options[correct_pos] = correct_answer;
// 生成错误答案
try {
double correct_value = std::stod(correct_answer);
std::uniform_real_distribution<double> offset_dist(1.0, 10.0);
std::uniform_int_distribution<int> sign_dist(0, 1);
for (int i = 0; i < kOptionsCount; ++i) {
if (i == correct_pos) continue;
double offset = offset_dist(gen);
if (sign_dist(gen) == 0) {
offset = -offset;
}
double wrong_value = correct_value + offset;
// 格式化输出
std::stringstream ss;
if (std::abs(wrong_value - std::round(wrong_value)) < 1e-6) {
ss << static_cast<int>(wrong_value);
} else {
ss << std::fixed << std::setprecision(2) << wrong_value;
}
options[i] = ss.str();
}
} catch (const std::exception& e) {
// 如果转换失败,使用简单选项
for (int i = 0; i < kOptionsCount; ++i) {
if (i == correct_pos) continue;
options[i] = "选项" + std::to_string(i + 1);
}
}
return options;
}
double BackendImpl::CalculateScore(int correct_count, int total_count) {
if (total_count == 0) return 0.0;
return (static_cast<double>(correct_count) / total_count) * 100.0;
}
} // namespace exam_system