合并后端内容 #1

Merged
pijtbanfl merged 13 commits from wengweibin_branch into main 6 months ago

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

@ -0,0 +1,16 @@
[
{
"email": "3454934335@qq.com",
"username": "wwb",
"registeredAt": "2025-10-11T05:26:22.038Z",
"password": "cc43f52af644042612d98dea06cf5b64d8e2f8af1de8065fe83b208f4a6d8875",
"updatedAt": "2025-10-11T05:26:22.051Z"
},
{
"email": "3063485007@qq.com",
"username": "wqs",
"registeredAt": "2025-10-11T19:08:09.303Z",
"password": "be0c664d19564b568c4dd76d00a26a08ee89214877e52225eee5ffefab36c15f",
"updatedAt": "2025-10-11T19:08:26.339Z"
}
]

@ -0,0 +1,31 @@
// =============================================
// 数学学习软件 - 邮箱配置文件
// =============================================
// ==================== 选择1: QQ邮箱服务 ====================
module.exports = {
service: 'qq',
auth: {
user: '3454934335@qq.com', // 替换为你的QQ邮箱
pass: 'vxhsswmmqiyvchhh' // 替换为QQ邮箱授权码16位
}
};
// ==================== 选择2: 163邮箱服务 ====================
module.exports = {
service: '163',
auth: {
user: '18950579895@163.com',// 替换为你的163邮箱
pass: 'UCgJrE7yzzd4Uz3g' // 替换为163邮箱授权码
}
};
// ==================== 选择3: 测试模式 ====================
// 不发送实际邮件,验证码在控制台显示
// 适用于开发和测试环境
/*
module.exports = {};
*/

File diff suppressed because it is too large Load Diff

@ -0,0 +1,35 @@
{
"name": "math-learning-backend",
"version": "1.0.0",
"description": "数学学习软件后端API - 支持QQ邮箱和163邮箱验证",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"test": "echo \"后端服务测试模式\" && node server.js"
},
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5",
"body-parser": "^1.20.2",
"nodemailer": "^6.9.7"
},
"devDependencies": {
"nodemon": "^3.0.1"
},
"keywords": [
"education",
"math",
"learning",
"email",
"qq",
"163"
],
"author": "数学学习软件团队",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/your-repo/math-learning-software"
}
}

@ -0,0 +1,310 @@
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
// 导入工具类
const UserManager = require('./utils/user-manager');
const MultiEmailService = require('./utils/multi-email-service');
const MathQuestionGenerator = require('./utils/question-generator');
// 创建Express应用实例
const app = express();
const PORT = 8080;
// 配置中间件
app.use(cors());
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, '../project4')));
// 实例化管理器
const userManager = new UserManager();
const emailService = new MultiEmailService();
// 存储验证码(内存存储,重启后失效)
const verificationCodes = new Map();
// 生成6位数字验证码
function generateVerificationCode() {
return Math.floor(100000 + Math.random() * 900000).toString();
}
// 验证邮箱格式是否正确
function validateEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
// API路由
// 检查服务器健康状态
app.get('/api/health', (req, res) => {
res.json({
ok: true,
message: '服务正常运行',
emailService: emailService.transporter ? '已配置' : '测试模式'
});
});
// 发送邮箱验证码
app.post('/api/send-code', async (req, res) => {
try {
const {email, username} = req.body;
console.log('收到发送验证码请求:', {email, username});
if (!email || !username) {
return res.json({ok: false, message: '邮箱和用户名不能为空'});
}
// 验证邮箱格式
if (!validateEmail(email)) {
return res.json({ok: false, message: '邮箱格式不正确'});
}
// 限制邮箱域名只允许QQ邮箱和163邮箱
if (!email.includes('@qq.com') && !email.includes('@163.com')) {
return res.json({ok: false, message: '只支持QQ邮箱和163邮箱'});
}
// 检查用户名是否已存在
if (userManager.findUserByUsername(username)) {
return res.json({ok: false, message: '用户名已存在'});
}
// 检查邮箱是否已注册
if (userManager.findUserByEmail(email)) {
return res.json({ok: false, message: '邮箱已被注册'});
}
const code = generateVerificationCode();
verificationCodes.set(email, {
code,
username,
timestamp: Date.now(),
});
console.log(`为邮箱 ${email} 生成验证码: ${code}`);
// 发送邮件
const emailResult = await emailService.sendVerificationCode(email, code, username);
if (emailResult.success) {
res.json({ok: true, message: emailResult.message});
} else {
// 如果邮件发送失败,但在开发模式下提供了验证码,仍然算成功
if (emailResult.debug) {
res.json({ok: true, message: emailResult.message + ' ' + emailResult.debug});
} else {
res.json({ok: false, message: emailResult.message});
}
}
} catch (error) {
console.error('发送验证码错误:', error);
res.json({ok: false, message: '服务器内部错误'});
}
});
// 验证码验证
app.post('/api/verify-code', (req, res) => {
try {
const {email, username, code} = req.body;
console.log('收到验证码验证请求:', {email, username, code});
if (!email || !username || !code) {
return res.json({ok: false, message: '请填写完整信息'});
}
const storedData = verificationCodes.get(email);
if (!storedData) {
return res.json({ok: false, message: '请先获取验证码'});
}
// 验证码10分钟过期
if (Date.now() - storedData.timestamp > 10 * 60 * 1000) {
verificationCodes.delete(email);
return res.json({ok: false, message: '验证码已过期,请重新获取'});
}
if (storedData.code !== code) {
return res.json({ok: false, message: '验证码不正确'});
}
if (storedData.username !== username) {
return res.json({ok: false, message: '用户名与验证时不一致'});
}
// 清除验证码
verificationCodes.delete(email);
res.json({ok: true, message: '验证成功,请设置密码'});
} catch (error) {
console.error('验证码验证错误:', error);
res.json({ok: false, message: error.message});
}
});
// 注册(创建用户 + 设置密码)
app.post('/api/register', (req, res) => {
try {
const {email, username, password} = req.body;
console.log('收到注册请求:', {email, username});
if (!email || !username || !password) {
return res.json({ok: false, message: '请填写完整信息'});
}
// 创建用户并设置密码
userManager.createUser(email, username);
userManager.setPassword(email, password);
res.json({ok: true, message: '注册成功'});
} catch (error) {
console.error('注册错误:', error);
res.json({ok: false, message: error.message});
}
});
// 登录
app.post('/api/login', (req, res) => {
try {
const {account, password} = req.body;
if (!account || !password) {
return res.json({ok: false, message: '请填写账号和密码'});
}
// 通过邮箱或用户名查找用户
let user = userManager.findUserByEmail(account);
if (!user) {
user = userManager.findUserByUsername(account);
}
if (!user || !user.password) {
return res.json({ok: false, message: '用户不存在'});
}
if (!userManager.verifyPassword(user.email, password)) {
return res.json({ok: false, message: '密码不正确'});
}
res.json({
ok: true,
message: '登录成功',
data: {
email: user.email,
username: user.username,
},
});
} catch (error) {
console.error('登录错误:', error);
res.json({ok: false, message: error.message});
}
});
// 修改密码
app.post('/api/change-password', (req, res) => {
try {
const {email, oldPassword, newPassword} = req.body;
if (!email || !oldPassword || !newPassword) {
return res.json({ok: false, message: '请填写完整信息'});
}
userManager.changePassword(email, oldPassword, newPassword);
res.json({ok: true, message: '密码修改成功'});
} catch (error) {
console.error('修改密码错误:', error);
res.json({ok: false, message: error.message});
}
});
// 修改用户名
app.post('/api/change-username', (req, res) => {
try {
const {email, username} = req.body;
if (!email || !username) {
return res.json({ok: false, message: '请填写完整信息'});
}
userManager.changeUsername(email, username);
res.json({ok: true, message: '用户名修改成功'});
} catch (error) {
console.error('修改用户名错误:', error);
res.json({ok: false, message: error.message});
}
});
// 删除账号
app.post('/api/delete-account', (req, res) => {
try {
const {email, password} = req.body;
if (!email || !password) {
return res.json({ok: false, message: '请填写完整信息'});
}
// 验证密码
if (!userManager.verifyPassword(email, password)) {
return res.json({ok: false, message: '密码不正确'});
}
userManager.deleteUser(email);
res.json({ok: true, message: '账号删除成功'});
} catch (error) {
console.error('删除账号错误:', error);
res.json({ok: false, message: error.message});
}
});
// 获取题目
app.get('/api/questions', (req, res) => {
try {
const {grade, count} = req.query;
if (!grade || !count) {
return res.json({ok: false, message: '请选择年级和题目数量'});
}
const countNum = parseInt(count);
if (isNaN(countNum) || countNum < 10 || countNum > 30) {
return res.json({ok: false, message: '题目数量需在10-30之间'});
}
const generator = new MathQuestionGenerator();
const questions = generator.generateQuestions(grade, countNum);
res.json({
ok: true,
data: questions,
message: '题目生成成功',
});
} catch (error) {
console.error('生成题目失败:', error);
res.json({ok: false, message: '生成题目失败'});
}
});
// 提供前端页面
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '../project4/index.html'));
});
// 启动服务器
app.listen(PORT, () => {
console.log(`========================================`);
console.log(`🎓 数学学习软件后端服务已启动`);
console.log(`📍 服务地址: http://localhost:${PORT}`);
console.log(`🌐 前端页面: http://localhost:${PORT}/`);
console.log(`🔧 API文档: http://localhost:${PORT}/api/health`);
console.log(`📧 邮箱服务: ${emailService.transporter ? '已配置' : '测试模式'}`);
console.log(`========================================`);
});
module.exports = app;

@ -0,0 +1,104 @@
const nodemailer = require('nodemailer');
// 多邮箱服务类,支持多种邮箱服务商
class MultiEmailService {
constructor() {
this.transporters = new Map();
this.initTransporters();
}
// 初始化各种邮箱服务商的传输器
initTransporters() {
// QQ邮箱配置
try {
// 修改
const qqTransporter = nodemailer.createTransport({
auth: {
pass: 'vxhsswmmqiyvchhh',
user: '3454934335@qq.com',
},
service: 'qq',
});
this.transporters.set('qq', qqTransporter);
console.log('✅ QQ邮箱服务已初始化');
} catch (error) {
console.error('❌ QQ邮箱初始化失败:', error);
}
// 163邮箱配置
try {
// 修改
const mail163Transporter = nodemailer.createTransport({
auth: {
pass: 'UCgJrE7yzzd4Uz3g',
user: '18950579895@163.com',
},
service: '163',
});
this.transporters.set('163', mail163Transporter);
console.log('✅ 163邮箱服务已初始化');
} catch (error) {
console.error('❌ 163邮箱初始化失败:', error);
}
}
// 发送验证码邮件
async sendVerificationCode(email, code, username) {
// 根据邮箱域名选择发件箱
let transporter = email.includes('@qq.com') ? this.transporters.get('qq') :
email.includes('@163.com') ? this.transporters.get('163') :
this.transporters.get('qq');
if (!transporter) {
console.log(`🔧 测试模式 - 邮箱: ${email}, 验证码: ${code}`);
return {success: true, message: '验证码已生成(测试模式)', debug: `验证码: ${code}`};
}
const mailOptions = {
from: transporter.options.auth.user,
to: email,
subject: '数学学习软件 - 注册验证码',
html: this.generateEmailTemplate(code, username)
};
try {
await transporter.sendMail(mailOptions);
console.log(`✅ 验证码邮件已发送到: ${email}`);
return {success: true, message: '验证码已发送到您的邮箱'};
} catch (error) {
console.error('❌ 邮件发送失败:', error);
return {success: false, message: '邮件发送失败,请稍后重试', debug: `验证码: ${code} (请使用此验证码完成注册)`};
}
}
// 生成邮件HTML模板
generateEmailTemplate(code, username) {
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
.container { background: white; border-radius: 10px; padding: 30px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.header { color: #3b82f6; text-align: center; margin-bottom: 30px; border-bottom: 2px solid #3b82f6; padding-bottom: 15px; }
.code { text-align: center; margin: 30px 0; font-size: 32px; font-weight: bold; color: #3b82f6; letter-spacing: 5px; padding: 20px; background: #f0f7ff; border-radius: 8px; border: 2px dashed #3b82f6; }
</style>
</head>
<body>
<div class="container">
<div class="header"><h2>🎓 数学学习软件</h2></div>
<p>亲爱的 <strong>${username}</strong></p>
<p>您正在注册数学学习软件验证码为</p>
<div class="code">${code}</div>
<p>验证码有效期为10分钟请尽快完成注册</p>
<p>如果这不是您本人的操作请忽略此邮件</p>
</div>
</body>
</html>
`;
}
}
module.exports = MultiEmailService;

@ -0,0 +1,441 @@
const fs = require('fs');
const path = require('path');
class MathQuestionGenerator {
constructor() {
this.currentSessionQuestions = new Set();
}
// 生成指定年级和数量的题目
generateQuestions(grade, count) {
console.log(`正在生成${grade} ${count}道题目...`);
const questions = [];
this.currentSessionQuestions.clear();
const maxAttempts = count * 20;
let attempts = 0;
while (questions.length < count && attempts < maxAttempts) {
attempts++;
const question = this.generateQuestion(grade);
if (!question) {
continue;
}
const questionKey = `${grade}-${question.stem}`;
if (!this.currentSessionQuestions.has(questionKey)) {
questions.push(question);
this.currentSessionQuestions.add(questionKey);
console.log(`✅ 生成第${questions.length}题: ${question.stem}`);
}
}
if (questions.length < count) {
console.warn(`⚠️ 只生成了${questions.length}道题目,未能达到要求的${count}`);
}
return questions;
}
// 根据年级生成单个题目
generateQuestion(grade) {
try {
switch (grade) {
case '小学':
return this.generatePrimaryQuestion();
case '初中':
return this.generateMiddleSchoolQuestion();
case '高中':
return this.generateHighSchoolQuestion();
default:
return this.generatePrimaryQuestion();
}
} catch (error) {
console.error(`生成${grade}题目时出错:`, error);
return null;
}
}
// 生成小学题目2-5个操作数基础四则运算
generatePrimaryQuestion() {
const numOperands = Math.floor(Math.random() * 4) + 2;
const operations = ['+', '-', '×', '÷'];
let expression = '';
let correctAnswer = 0;
for (let i = 0; i < numOperands; i++) {
const num = Math.floor(Math.random() * 100) + 1;
if (i === 0) {
expression = num.toString();
correctAnswer = num;
} else {
const op = operations[Math.floor(Math.random() * operations.length)];
expression += ` ${op} ${num}`;
correctAnswer = this.applyOperation(correctAnswer, num, op);
}
}
correctAnswer = this.calculateWithPriority(expression);
if (numOperands >= 3 && Math.random() < 0.3) {
const result = this.addParentheses(expression, correctAnswer);
if (result) {
expression = result.expression;
correctAnswer = result.answer;
}
}
const options = this.generateOptions(correctAnswer, 4);
return {
id: `primary-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
stem: `计算:${expression} = ?`,
options: options,
answer: options.find(opt => opt.isCorrect).key
};
}
// 生成初中题目1-5个操作数包含平方和开方
generateMiddleSchoolQuestion() {
const numOperands = Math.floor(Math.random() * 5) + 1;
const operations = ['+', '-', '×', '÷'];
const specialPosition = Math.floor(Math.random() * numOperands);
const isSquare = Math.random() < 0.5;
let expression = '';
let correctAnswer = 0;
for (let i = 0; i < numOperands; i++) {
const {term, value} = i === specialPosition ?
this.generateSpecialTerm(isSquare) : this.generateRandomTerm();
if (i === 0) {
expression = term;
correctAnswer = value;
} else {
const op = operations[Math.floor(Math.random() * operations.length)];
expression += ` ${op} ${term}`;
correctAnswer = this.applyOperation(correctAnswer, value, op);
}
}
correctAnswer = this.calculateWithPriority(expression);
if (numOperands >= 3 && Math.random() < 0.4) {
const result = this.addParentheses(expression, correctAnswer);
if (result) {expression = result.expression; correctAnswer = result.answer;}
}
const options = this.generateOptions(correctAnswer, 4);
return {
id: `middle-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
stem: `计算:${expression} = ?`,
options: options,
answer: options.find(opt => opt.isCorrect).key
};
}
// 生成特殊项(平方或开方)
generateSpecialTerm(isSquare) {
if (isSquare) {
const base = Math.floor(Math.random() * 15) + 1;
return { term: `${base}²`, value: base * base };
} else {
const perfectSquares = [4, 9, 16, 25, 36, 49, 64, 81, 100];
const num = perfectSquares[Math.floor(Math.random() * perfectSquares.length)];
return { term: `${num}`, value: Math.sqrt(num) };
}
}
// 生成随机项(数字、平方或开方)
generateRandomTerm() {
const termType = Math.random();
if (termType < 0.3) {
const base = Math.floor(Math.random() * 15) + 1;
return { term: `${base}²`, value: base * base };
} else if (termType < 0.6) {
const perfectSquares = [4, 9, 16, 25, 36, 49, 64, 81, 100];
const num = perfectSquares[Math.floor(Math.random() * perfectSquares.length)];
return { term: `${num}`, value: Math.sqrt(num) };
} else {
const num = Math.floor(Math.random() * 100) + 1;
return { term: num.toString(), value: num };
}
}
// 生成高中题目1-5个操作数包含三角函数
generateHighSchoolQuestion() {
const numOperands = Math.floor(Math.random() * 5) + 1;
const operations = ['+', '-', '×', '÷'];
const specialPosition = Math.floor(Math.random() * numOperands);
let expression = '', correctAnswer = 0;
for (let i = 0; i < numOperands; i++) {
const {term, value} = i === specialPosition ?
this.generateTrigTerm() : (Math.random() < 0.4 ? this.generateTrigTerm() : this.generateNumberTerm());
if (i === 0) { expression = term; correctAnswer = value; }
else {
const op = operations[Math.floor(Math.random() * operations.length)];
expression += ` ${op} ${term}`;
correctAnswer = this.applyOperation(correctAnswer, value, op);
}
}
correctAnswer = this.calculateWithPriority(expression);
if (numOperands >= 3 && Math.random() < 0.4) {
const result = this.addParentheses(expression, correctAnswer);
if (result) { expression = result.expression; correctAnswer = result.answer; }
}
const options = this.generateOptions(correctAnswer, 4);
return {
id: `high-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
stem: `计算:${expression} = ?`,
options: options,
answer: options.find(opt => opt.isCorrect).key
};
}
// 生成三角函数项
generateTrigTerm() {
const functions = ['sin', 'cos', 'tan'];
const func = functions[Math.floor(Math.random() * functions.length)];
const angle = Math.floor(Math.random() * 100) + 1;
const value = Math.round(Math[func](angle * Math.PI / 180) * 100) / 100;
return { term: `${func}${angle}`, value };
}
// 生成数字项
generateNumberTerm() {
const num = Math.floor(Math.random() * 100) + 1;
return { term: num.toString(), value: num };
}
// 生成选择题选项
generateOptions(correctAnswer, count) {
const options = [];
const keys = ['A', 'B', 'C', 'D'];
const correctIndex = Math.floor(Math.random() * count);
const isInteger = typeof correctAnswer === 'number' && Number.isInteger(correctAnswer);
for (let i = 0; i < count; i++) {
const value = i === correctIndex ? correctAnswer : this.generateWrongOption(correctAnswer, isInteger, i);
options.push({key: keys[i], text: value.toString(), isCorrect: i === correctIndex});
}
return options;
}
// 生成错误选项
generateWrongOption(correctAnswer, isInteger, index) {
if (typeof correctAnswer !== 'number') {
return Math.random().toString(36).substring(2, 6);
}
let attempts = 0;
let value;
do {
const deviation = (Math.random() - 0.5) * 4;
value = isInteger ?
correctAnswer + Math.floor(Math.random() * 10) - 5 :
Math.round((correctAnswer + deviation) * 100) / 100;
if (++attempts > 10) {
value = correctAnswer + (index + 1);
if (isInteger) {
value = Math.round(value);
}
break;
}
} while (value === correctAnswer);
return value;
}
// 获取数字的所有因数
getDivisors(n) {
const divisors = [];
for (let i = 2; i <= Math.min(n, 100); i++) { // 除数也限制在1-100范围内
if (n % i === 0) divisors.push(i);
}
return divisors;
}
// 小学题目专用的括号添加函数,确保括号内不会产生负数
addParenthesesForPrimary(expression, originalAnswer) {
const parts = expression.split(' ');
// 如果表达式太短,不需要加括号
if (parts.length < 5) return null;
// 找到所有可以加括号的位置(运算符位置)
const operatorPositions = [];
for (let i = 1; i < parts.length - 1; i += 2) {
// 只考虑加法和乘法,避免减法导致负数
if (parts[i] === '+' || parts[i] === '×') {
operatorPositions.push(i);
}
}
if (operatorPositions.length === 0) return null;
// 随机选择一个运算符位置
const operatorIndex = operatorPositions[Math.floor(Math.random() * operatorPositions.length)];
// 确定括号的范围(从运算符前一个操作数到运算符后一个操作数)
const startPos = operatorIndex - 1;
const endPos = operatorIndex + 1;
// 构建带括号的表达式
let result = '';
for (let i = 0; i < parts.length; i++) {
if (i === startPos) {
result += '(';
}
result += parts[i];
if (i === endPos) {
result += ')';
}
if (i < parts.length - 1) {
result += ' ';
}
}
// 计算带括号的答案
let newAnswer = this.calculateWithPriority(result);
// 确保答案是非负整数
if (newAnswer < 0 || !Number.isInteger(newAnswer)) {
return null;
}
return {
answer: newAnswer,
expression: result,
};
}
// 通用的括号添加函数
// 为表达式添加括号
addParentheses(expression, originalAnswer) {
const parts = expression.split(' ');
// 如果表达式太短,不需要加括号
if (parts.length < 5) return null;
// 找到所有可以加括号的位置(运算符位置)
const operatorPositions = [];
for (let i = 1; i < parts.length - 1; i += 2) {
operatorPositions.push(i);
}
if (operatorPositions.length === 0) return null;
// 随机选择一个运算符位置
const operatorIndex = operatorPositions[Math.floor(Math.random() * operatorPositions.length)];
// 确定括号的范围(从运算符前一个操作数到运算符后一个操作数)
const startPos = operatorIndex - 1;
const endPos = operatorIndex + 1;
// 构建带括号的表达式
let result = '';
for (let i = 0; i < parts.length; i++) {
if (i === startPos) {
result += '(';
}
result += parts[i];
if (i === endPos) {
result += ')';
}
if (i < parts.length - 1) {
result += ' ';
}
}
// 计算带括号的答案
let newAnswer = this.calculateWithPriority(result);
return {
answer: newAnswer,
expression: result,
};
}
// 执行四则运算操作
applyOperation(current, value, op) {
switch (op) {
case '+': return current + value;
case '-': return current - value;
case '×': return current * value;
case '÷': return value !== 0 ? current / value : current;
default: return current;
}
}
// 使用正确优先级计算表达式的答案
calculateWithPriority(expression) {
// 替换运算符为JavaScript可识别的
let jsExpression = expression
.replace(/×/g, '*')
.replace(/÷/g, '/')
.replace(/²/g, '**2')
.replace(/√(\d+)/g, 'Math.sqrt($1)')
.replace(/sin(\d+)/g, 'Math.sin($1 * Math.PI / 180)')
.replace(/cos(\d+)/g, 'Math.cos($1 * Math.PI / 180)')
.replace(/tan(\d+)/g, 'Math.tan($1 * Math.PI / 180)');
try {
// 使用eval计算表达式
let result = eval(jsExpression);
// 处理特殊情况
if (typeof result === 'number') {
// 如果是整数,返回整数
if (Number.isInteger(result)) {
return result;
}
// 否则保留两位小数
return Math.round(result * 100) / 100;
}
return result;
} catch (error) {
console.error('计算表达式时出错:', expression, error);
// 如果计算失败,返回原始表达式的估算值
return this.estimateExpression(expression);
}
}
// 估算表达式的值当eval失败时使用
estimateExpression(expression) {
// 简单的估算逻辑,按顺序计算
const parts = expression.split(' ');
let result = parseFloat(parts[0]);
for (let i = 1; i < parts.length; i += 2) {
const operator = parts[i];
const num = parseFloat(parts[i + 1]);
switch (operator) {
case '+':
result += num;
break;
case '-':
result -= num;
break;
case '×':
result *= num;
break;
case '÷':
if (num !== 0) result /= num;
break;
}
}
return Math.round(result * 100) / 100;
}
}
module.exports = MathQuestionGenerator;

@ -0,0 +1,183 @@
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const USERS_FILE = path.join(__dirname, '../data/users.json');
// 用户管理类,处理用户注册、登录等操作
class UserManager {
constructor() {
this.ensureDataFile();
}
// 确保数据文件存在
ensureDataFile() {
const dataDir = path.dirname(USERS_FILE);
if (!fs.existsSync(dataDir)) {
fs.mkdirSync(dataDir, {recursive: true});
}
if (!fs.existsSync(USERS_FILE)) {
fs.writeFileSync(USERS_FILE, JSON.stringify([]));
}
}
// 读取所有用户数据
readUsers() {
try {
return JSON.parse(fs.readFileSync(USERS_FILE, 'utf8'));
} catch (error) {
console.error('读取用户数据失败:', error);
return [];
}
}
// 保存用户数据到文件
writeUsers(users) {
try {
fs.writeFileSync(USERS_FILE, JSON.stringify(users, null, 2));
return true;
} catch (error) {
console.error('写入用户数据失败:', error);
return false;
}
}
// 对密码进行SHA256哈希加密
hashPassword(password) {
return crypto.createHash('sha256').update(password).digest('hex');
}
// 验证密码格式6-10位
validatePassword(password) {
if (!password || password.length < 6 || password.length > 10) {
return false;
}
const hasUpper = /[A-Z]/.test(password);
const hasLower = /[a-z]/.test(password);
const hasDigit = /\d/.test(password);
return hasUpper && hasLower && hasDigit;
}
findUserByEmail(email) {
const users = this.readUsers();
return users.find((user) => user.email === email);
}
findUserByUsername(username) {
const users = this.readUsers();
return users.find((user) => user.username === username);
}
createUser(email, username) {
const users = this.readUsers();
// 严格检查邮箱是否已存在
if (users.some((user) => user.email === email)) {
throw new Error('邮箱已被注册,每个邮箱只能注册一个账户');
}
// 检查用户名是否已存在
if (users.some((user) => user.username === username)) {
throw new Error('用户名已存在');
}
const newUser = {
email,
registeredAt: new Date().toISOString(),
username,
};
users.push(newUser);
if (this.writeUsers(users)) {
console.log(`✅ 新用户注册: ${username} (${email})`);
return newUser;
} else {
throw new Error('用户创建失败');
}
}
setPassword(email, password) {
const users = this.readUsers();
const userIndex = users.findIndex((user) => user.email === email);
if (userIndex === -1) {
throw new Error('用户不存在');
}
if (!this.validatePassword(password)) {
throw new Error('密码需6-10位且包含大小写字母和数字');
}
users[userIndex].password = this.hashPassword(password);
users[userIndex].updatedAt = new Date().toISOString();
if (this.writeUsers(users)) {
console.log(`✅ 用户设置密码: ${email}`);
return true;
} else {
throw new Error('密码设置失败');
}
}
verifyPassword(email, password) {
const user = this.findUserByEmail(email);
if (!user || !user.password) {
return false;
}
return user.password === this.hashPassword(password);
}
changePassword(email, oldPassword, newPassword) {
if (!this.verifyPassword(email, oldPassword)) {
throw new Error('原密码不正确');
}
return this.setPassword(email, newPassword);
}
changeUsername(email, newUsername) {
const users = this.readUsers();
const userIndex = users.findIndex((user) => user.email === email);
if (userIndex === -1) {
throw new Error('用户不存在');
}
// 检查新用户名是否已存在(排除当前用户)
if (users.some((user) => user.username === newUsername && user.email !== email)) {
throw new Error('用户名已存在');
}
const oldUsername = users[userIndex].username;
users[userIndex].username = newUsername;
users[userIndex].updatedAt = new Date().toISOString();
if (this.writeUsers(users)) {
console.log(`✅ 用户修改用户名: ${oldUsername} -> ${newUsername}`);
return true;
} else {
throw new Error('用户名修改失败');
}
}
deleteUser(email) {
const users = this.readUsers();
const userIndex = users.findIndex((user) => user.email === email);
if (userIndex === -1) {
throw new Error('用户不存在');
}
const deletedUser = users[userIndex];
users.splice(userIndex, 1);
if (this.writeUsers(users)) {
console.log(`✅ 用户账号已删除: ${deletedUser.username} (${email})`);
return true;
} else {
throw new Error('账号删除失败');
}
}
}
module.exports = UserManager;
Loading…
Cancel
Save