""" 数学学习软件 - 数据管理模块 负责用户数据存储、题目生成等业务逻辑 """ import json import os import random import re import time import smtplib from datetime import datetime, timedelta from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from typing import Dict, List, Tuple, Optional from email_config import get_smtp_config, DEFAULT_SENDER_CONFIG class DataManager: """数据管理类,负责用户信息和题目数据的管理""" def __init__(self): self.users_file = "users.json" self.email_config_file = "email_config.json" self.current_user = None # 邮件配置 self.sender_config = DEFAULT_SENDER_CONFIG self.ensure_data_files() self.load_email_config() self.verification_code_expiry = 0.2 def ensure_data_files(self): """确保数据文件存在""" if not os.path.exists(self.users_file): with open(self.users_file, 'w', encoding='utf-8') as f: json.dump({}, f, ensure_ascii=False, indent=2) def load_email_config(self): """加载邮箱配置""" try: if os.path.exists(self.email_config_file): with open(self.email_config_file, 'r', encoding='utf-8') as f: config = json.load(f) self.sender_config.update(config) except Exception as e: print(f"加载邮箱配置失败: {e}") def load_users(self) -> Dict: """加载用户数据""" try: with open(self.users_file, 'r', encoding='utf-8') as f: return json.load(f) except: return {} def save_users(self, users_data: Dict): """保存用户数据""" with open(self.users_file, 'w', encoding='utf-8') as f: json.dump(users_data, f, ensure_ascii=False, indent=2) def generate_verification_code(self) -> str: """生成6位数字验证码""" return str(random.randint(100000, 999999)) def send_verification_email(self, email: str, verification_code: str) -> bool: """发送验证码邮件""" try: # 检查发送邮箱配置 if (self.sender_config["email"] == "your_email@qq.com" or self.sender_config["password"] == "your_app_password"): print("请先配置发送邮箱信息") return False # 获取SMTP配置 smtp_config = get_smtp_config(self.sender_config["email"]) # 创建邮件内容 msg = MIMEMultipart() msg['From'] = self.sender_config['email'] # QQ邮箱要求简单格式 msg['To'] = email msg['Subject'] = "数学学习软件 - 注册验证码" # 邮件正文 body = f"""欢迎使用数学学习软件! 您的注册验证码是:{verification_code} 请在软件中输入此验证码完成注册。 验证码有效期为10分钟。 如果您没有申请注册,请忽略此邮件。 祝您学习愉快! {self.sender_config['name']}""" msg.attach(MIMEText(body, 'plain', 'utf-8')) # 连接SMTP服务器并发送邮件 server = smtplib.SMTP(smtp_config["smtp_server"], smtp_config["smtp_port"]) if smtp_config["use_tls"]: server.starttls() # 启用TLS加密 server.login(self.sender_config["email"], self.sender_config["password"]) text = msg.as_string() server.sendmail(self.sender_config["email"], email, text) server.quit() return True except Exception as e: print(f"邮件发送失败: {e}") return False # 返回True以便测试 def register_user(self, email: str, username: str) -> bool: """用户注册,发送验证码到邮箱""" users = self.load_users() if email in users: existing_user = users[email] # 判断是否为未完成注册状态 if not existing_user.get("verified") or existing_user.get("password") is None: del users[email] # 删除未完成的记录 else: raise ValueError("该邮箱已注册") # 已完成注册则提示 # 检查用户名是否已存在 for user_data in users.values(): if user_data.get("username") == username: raise ValueError("该用户名已存在") verification_code = self.generate_verification_code() # 发送验证码邮件 if not self.send_verification_email(email, verification_code): raise ValueError("验证码邮件发送失败,请检查邮箱地址") code_generated_time = datetime.now().timestamp() users[email] = { "username": username, "verification_code": verification_code, "verified": False, "password": None, "code_generated_time": code_generated_time } self.save_users(users) return True def verify_registration(self, email: str, code: str) -> bool: """验证注册码""" users = self.load_users() if email not in users: return False user = users[email] current_time = datetime.now().timestamp() time_diff = current_time - user.get("code_generated_time", 0) if time_diff > self.verification_code_expiry * 60: # 验证码过期,删除无效记录 del users[email] self.save_users(users) return False if user["verification_code"] == code: user["verified"] = True self.save_users(users) return True return False def validate_password(self, password: str) -> bool: """验证密码格式:6-10位,必须含大小写字母和数字""" if len(password) < 6 or len(password) > 10: return False has_upper = bool(re.search(r'[A-Z]', password)) has_lower = bool(re.search(r'[a-z]', password)) has_digit = bool(re.search(r'\d', password)) return has_upper and has_lower and has_digit def set_password(self, email: str, password: str) -> bool: """设置用户密码""" if not self.validate_password(password): users = self.load_users() if email in users and users[email]["verified"]: del users[email] self.save_users(users) return False users = self.load_users() if email not in users or not users[email]["verified"]: return False users[email]["password"] = password self.save_users(users) return True def login(self, email: str, password: str) -> bool: """用户登录""" users = self.load_users() if email not in users: return False user = users[email] # 新增:如果用户已验证但未设置密码,清理记录 if user["verified"] and user["password"] is None: del users[email] self.save_users(users) return False # 检查验证码是否过期(针对未验证用户) current_time = datetime.now().timestamp() time_diff = current_time - user.get("code_generated_time", 0) if time_diff > self.verification_code_expiry * 60: del users[email] self.save_users(users) return False if user["verified"] and user["password"] == password: self.current_user = email return True return False def change_password(self, old_password: str, new_password: str) -> bool: """修改密码""" if not self.current_user: return False users = self.load_users() user = users[self.current_user] if user["password"] != old_password: return False if not self.validate_password(new_password): return False user["password"] = new_password self.save_users(users) return True def logout(self): """用户登出""" self.current_user = None def get_current_username(self) -> str: """获取当前登录用户的用户名""" if not self.current_user: return "" users = self.load_users() if self.current_user in users: return users[self.current_user].get("username", "") return "" class QuestionGenerator: """题目生成器""" def __init__(self): self.difficulty_levels = { "小学": {"range": (1, 100), "operations": ["+", "-", "*"]}, "初中": {"range": (1, 1000), "operations": ["+", "-", "*", "/"]}, "高中": {"range": (1, 10000), "operations": ["+", "-", "*", "/", "**"]} } def generate_question(self, level: str) -> Dict: """生成单个题目""" config = self.difficulty_levels[level] min_val, max_val = config["range"] operations = config["operations"] # 随机选择运算符 operation = random.choice(operations) if operation == "**": # 幂运算特殊处理 base = random.randint(2, 10) exponent = random.randint(2, 4) question = f"{base}^{exponent} = ?" correct_answer = base ** exponent elif operation == "/": # 除法确保整除 divisor = random.randint(2, 20) quotient = random.randint(2, max_val // divisor) dividend = divisor * quotient question = f"{dividend} ÷ {divisor} = ?" correct_answer = quotient else: # 加减乘法 num1 = random.randint(min_val, max_val) num2 = random.randint(min_val, max_val) if operation == "+": question = f"{num1} + {num2} = ?" correct_answer = num1 + num2 elif operation == "-": # 确保结果为正数 if num1 < num2: num1, num2 = num2, num1 question = f"{num1} - {num2} = ?" correct_answer = num1 - num2 else: # multiplication question = f"{num1} × {num2} = ?" correct_answer = num1 * num2 # 生成选项 options = self.generate_options(correct_answer) return { "question": question, "options": options, "correct_answer": correct_answer } def generate_options(self, correct_answer: int) -> List[int]: """生成四个选项,其中一个是正确答案""" options = [correct_answer] # 生成3个错误选项 while len(options) < 4: # 在正确答案附近生成错误选项 offset = random.randint(-abs(correct_answer)//2, abs(correct_answer)//2) if offset == 0: offset = random.choice([-1, 1]) * random.randint(1, 10) wrong_answer = correct_answer + offset if wrong_answer > 0 and wrong_answer not in options: options.append(wrong_answer) # 打乱选项顺序 random.shuffle(options) return options def generate_test_paper(self, level: str, question_count: int) -> List[Dict]: """生成试卷""" questions = [] generated_questions = set() # 用于避免重复题目 attempts = 0 while len(questions) < question_count and attempts < question_count * 10: question_data = self.generate_question(level) question_text = question_data["question"] if question_text not in generated_questions: generated_questions.add(question_text) questions.append(question_data) attempts += 1 return questions class ScoreCalculator: """分数计算器""" @staticmethod def calculate_score(correct_count: int, total_count: int) -> int: """计算分数(百分制)""" if total_count == 0: return 0 return int((correct_count / total_count) * 100) @staticmethod def get_grade_comment(score: int) -> str: """根据分数获取评价""" if score >= 90: return "优秀!继续保持!" elif score >= 80: return "良好!再接再厉!" elif score >= 70: return "中等,还需努力!" elif score >= 60: return "及格,继续加油!" else: return "需要更多练习!"