|
|
|
|
@ -0,0 +1,219 @@
|
|
|
|
|
# 数学题库生成系统详细设计文档
|
|
|
|
|
|
|
|
|
|
## 项目概述
|
|
|
|
|
|
|
|
|
|
### 项目目标
|
|
|
|
|
为小初高学生提供一个本地的桌面端图像化应用,支持用户注册和登录,根据学生年级按规则随机生成数学选择题,完成答题后自动计算分数
|
|
|
|
|
|
|
|
|
|
### 技术栈
|
|
|
|
|
|
|
|
|
|
| 类型 | 技术 | 说明 |
|
|
|
|
|
| -------- | -------------------------- | ---------------------------------------- |
|
|
|
|
|
| 开发语言 | Java 21+ | 保证跨平台兼容性,支持 JavaFX |
|
|
|
|
|
| GUI框架 | JavaFX 17+ | 实现桌面图形化界面,提供组件化开发能力 |
|
|
|
|
|
| 依赖管理 | Maven | 管理 JavaFX、JSON 解析等依赖 |
|
|
|
|
|
| JSON解析 | Gson 2.10+ | 序列化 / 反序列化用户数据、题目数据 |
|
|
|
|
|
| 邮件发送 | JavaMail API + 第三方 SMTP | 发送注册码(如 QQ 邮箱 / 网易邮箱 SMTP) |
|
|
|
|
|
| 数据加密 | BCrypt 0.9.0+ | 加密存储用户密码,避免明文泄露 |
|
|
|
|
|
| 构建打包 | Maven Shade Plugin | 打包成可执行 JAR,支持直接运行 |
|
|
|
|
|
|
|
|
|
|
### 总体设计
|
|
|
|
|
|
|
|
|
|
#### 项目目录结构
|
|
|
|
|
|
|
|
|
|
MathQuizApp/
|
|
|
|
|
├── src/
|
|
|
|
|
│ └── main/
|
|
|
|
|
│ └── java/
|
|
|
|
|
│ └── com/
|
|
|
|
|
│ └── mathquiz/
|
|
|
|
|
│ ├── Main.java # 程序入口
|
|
|
|
|
│ │
|
|
|
|
|
│ ├── model/ # 数据模型
|
|
|
|
|
│ │ ├── User.java
|
|
|
|
|
│ │ ├── Grade.java
|
|
|
|
|
│ │ └── ChoiceQuestion.java
|
|
|
|
|
│ │
|
|
|
|
|
│ ├── service/ # 后端逻辑(无GUI)
|
|
|
|
|
│ │ ├── UserService.java
|
|
|
|
|
│ │ ├── QuestionGenerator.java
|
|
|
|
|
│ │ ├── FileIOService.java
|
|
|
|
|
│ │ └── QuizService.java
|
|
|
|
|
│ │
|
|
|
|
|
│ ├── ui/ # 前端 GUI
|
|
|
|
|
│ │ ├── MainWindow.java
|
|
|
|
|
│ │ ├── RegisterPanel.java
|
|
|
|
|
│ │ ├── LoginPanel.java
|
|
|
|
|
| | ├── PasswordModifyPanel.java
|
|
|
|
|
│ │ ├── GradeSelectPanel.java
|
|
|
|
|
│ │ ├── QuizPanel.java
|
|
|
|
|
│ │ └── ResultPanel.java
|
|
|
|
|
│ │
|
|
|
|
|
│ └── util/ # 工具类
|
|
|
|
|
│ ├── PasswordValidator.java
|
|
|
|
|
| ├── EmailUtil.java
|
|
|
|
|
│ ├── RandomUtils.java
|
|
|
|
|
│ └── FileUtils.java
|
|
|
|
|
│
|
|
|
|
|
├── data/ # 运行时生成(不提交)
|
|
|
|
|
│ ├── users/ # 用户信息 JSON
|
|
|
|
|
│ └── temp_codes/ # 临时注册码文件
|
|
|
|
|
│
|
|
|
|
|
├── pom.xml # Maven 依赖(含 JavaFX)
|
|
|
|
|
└── README.md
|
|
|
|
|
|
|
|
|
|
## 详细模块设计
|
|
|
|
|
|
|
|
|
|
### 模型层设计
|
|
|
|
|
|
|
|
|
|
#### User类
|
|
|
|
|
- String name // 用户ID
|
|
|
|
|
- String email // 用户邮箱
|
|
|
|
|
- String encryptedPwd // 加密后的用户密码
|
|
|
|
|
- Grade grade //学段
|
|
|
|
|
|
|
|
|
|
#### ChoiceQuestion
|
|
|
|
|
String questionId; // 题目唯一ID(UUID生成)
|
|
|
|
|
Grade grade; // 所属学段
|
|
|
|
|
String questionContent; // 题干
|
|
|
|
|
List<String> options; // 选项列表(固定4个,顺序随机)
|
|
|
|
|
String correctAnswer; // 正确答案(如"A"/"B"/"C"/"D")
|
|
|
|
|
|
|
|
|
|
#### Grade 枚举
|
|
|
|
|
PRIMARY("小学", 1)
|
|
|
|
|
JUNIOR("初中", 2)
|
|
|
|
|
SENIOR("高中", 3)
|
|
|
|
|
|
|
|
|
|
### 工具层设计
|
|
|
|
|
|
|
|
|
|
#### PasswordValidator
|
|
|
|
|
|
|
|
|
|
| 方法名 | 参数列表 | 返回值 | 逻辑描述 |
|
|
|
|
|
| -------------------------- | ------------------------------------------------ | --------- | ------------------------------------------------------------ |
|
|
|
|
|
| isValid | String password | boolean | 校验密码:**6–10位**,**必须同时包含大写字母、小写字母和数字** |
|
|
|
|
|
| encrypt | String password | String | 使用 **SHA-256** 对密码哈希,返回 64 位十六进制字符串 |
|
|
|
|
|
| matches | String plainPassword, String encryptedPassword | boolean | 对明文密码加密后与存储的密文比对,判断是否匹配 |
|
|
|
|
|
| generateRegistrationCode | — | String | 生成 **6–10位** 随机字符串,**保证至少含1字母+1数字**,并打乱顺序 |
|
|
|
|
|
| shuffleString | String str | String | (私有)对字符串字符顺序进行 Fisher-Yates 打乱 |
|
|
|
|
|
|
|
|
|
|
#### EmailUtil
|
|
|
|
|
|
|
|
|
|
| 方法名 | 参数列表 | 返回值 | 逻辑描述 |
|
|
|
|
|
| ---------------------- | ----------------------------------------- | --------- | ------------------------------------------------------------ |
|
|
|
|
|
| sendRegistrationCode | String toEmail, String registrationCode | boolean | **模拟发送注册码邮件**(实际不发邮件),打印收件人和注册码到控制台,始终返回 true |
|
|
|
|
|
| sendPasswordReset | String toEmail, String newPassword | boolean | **预留接口**:模拟发送密码重置邮件,打印信息到控制台,始终返回 true |
|
|
|
|
|
| isValidEmail | String email | boolean | 使用正则表达式 ^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ 验证邮箱格式是否合法 |
|
|
|
|
|
|
|
|
|
|
#### FileUtils
|
|
|
|
|
|
|
|
|
|
| 方法名 | 参数列表 | 返回值 | 逻辑描述 |
|
|
|
|
|
| ---------------------------- | -------------------------------------- | --------- | ------------------------------------------------------------ |
|
|
|
|
|
| readFileToString | String filePath | String | 以 UTF-8 编码读取文件内容为字符串,失败抛出 IOException |
|
|
|
|
|
| writeStringToFile | String filePath, String content | void | 以 UTF-8 编码将字符串写入文件(覆盖),失败抛出 IOException |
|
|
|
|
|
| createDirectoryIfNotExists | String dirPath | void | 若目录不存在,则递归创建目录,失败抛出 IOException |
|
|
|
|
|
| exists | String filePath | boolean | 判断文件或目录是否存在 |
|
|
|
|
|
| deleteFile | String filePath | boolean | 删除文件,若文件不存在也返回 true,异常时返回 false 并打印堆栈 |
|
|
|
|
|
| listFiles | String dirPath | File[] | 返回目录下所有文件(不含子目录),若路径无效返回空数组 |
|
|
|
|
|
| appendToFile | String filePath, String content | void | 以 UTF-8 编码追加内容到文件末尾,自动创建文件(若不存在) |
|
|
|
|
|
| copyFile | String sourcePath, String targetPath | void | 复制文件,目标文件存在则覆盖,失败抛出 IOException |
|
|
|
|
|
| getFileSize | String filePath | long | 返回文件大小(字节),失败抛出 IOException |
|
|
|
|
|
|
|
|
|
|
#### RandomUtils
|
|
|
|
|
|
|
|
|
|
| 方法名 | 参数列表 | 返回值 | 逻辑描述 |
|
|
|
|
|
| -------------- | ------------------------ | --------- | ---------------------------------------------------------- |
|
|
|
|
|
| nextInt | int min, int max | int | 生成 [min, max] 范围内的随机整数(含边界) |
|
|
|
|
|
| randomChoice | T[] array | T | 从非空数组中随机返回一个元素 |
|
|
|
|
|
| randomChoice | List<T> list | T | 从非空列表中随机返回一个元素 |
|
|
|
|
|
| shuffle | List<T> list | void | 原地打乱列表顺序(使用 Collections.shuffle) |
|
|
|
|
|
| nextDouble | double min, double max | double | 生成 [min, max) 范围内的随机双精度浮点数 |
|
|
|
|
|
| nextBoolean | — | boolean | 返回 true 或 false(各 50% 概率) |
|
|
|
|
|
| probability | double probability | boolean | 按指定概率返回 true(如 0.7 表示 70% 概率返回 true) |
|
|
|
|
|
|
|
|
|
|
### 服务层设计
|
|
|
|
|
|
|
|
|
|
#### UserService
|
|
|
|
|
|
|
|
|
|
| 方法名 | 参数列表 | 返回值 | 逻辑描述 |
|
|
|
|
|
| -------------------- | ----------------------------------------------------------- | ------- | ------------------------------------------------------------ |
|
|
|
|
|
| sendRegistrationCode | String email | boolean | 生成 6 位数字注册码,调用 EmailUtil.sendTextEmail()发送(开发阶段可模拟),并将注册码写入 data/temp_codes/{email}.txt |
|
|
|
|
|
| verifyCode | String email, String code | boolean | 读取 data/temp_codes/{email}.txt,校验注册码是否匹配 |
|
|
|
|
|
| setPassword | String email, String pwd1, String pwd2 | boolean | 校验两次密码一致且符合规则(6–10位,含大小写+数字),使用 BCrypt加密后保存用户到 data/users/{hash}.json,成功后删除临时注册码文件 |
|
|
|
|
|
| login | String email, String password | User | 读取用户文件,用 BCrypt.matches() 验证密码,返回 User 对象或 null |
|
|
|
|
|
| changePassword | String email, String oldPwd, String newPwd1, String newPwd2 | boolean | 先验证原密码正确,再校验新密码格式,更新加密密码并保存 |
|
|
|
|
|
|
|
|
|
|
#### QuestionGenerator (抽象类)
|
|
|
|
|
|
|
|
|
|
| 方法名 | 参数列表 | 返回值 | 逻辑描述 |
|
|
|
|
|
| ------------- | ------------- | ------------------- | ------------------------------------------------------------ |
|
|
|
|
|
| create | Grade grade | QuestionGenerator | 静态工厂方法,根据年级返回对应子类实例(PrimaryGenerator / MiddleGenerator / SeniorGenerator) |
|
|
|
|
|
| generateOne | — | ChoiceQuestion | 抽象方法,由子类实现,生成一道符合年级要求的选择题(含题干、4选项、正确答案) |
|
|
|
|
|
|
|
|
|
|
#### FileIOService
|
|
|
|
|
|
|
|
|
|
| 方法名 | 参数列表 | 返回值 | 逻辑描述 |
|
|
|
|
|
| ----------------------- | --------------------------- | ------------- | ------------------------------------------------------------ |
|
|
|
|
|
| saveUser | User user | void | 将用户序列化为 JSON,保存到 data/users/{BCrypt.hash(email).substring(0,16)}.json |
|
|
|
|
|
| loadUserByEmail | String email | User | 遍历 data/users/ 下所有 JSON 文件,反序列化后匹配邮箱,返回 User 或 null |
|
|
|
|
|
| saveCode | String email, String code | void | 写入 data/temp_codes/{email}.txt |
|
|
|
|
|
| loadCode | String email | String | 读取 data/temp_codes/{email}.txt,返回注册码或 null |
|
|
|
|
|
| loadUsedQuestionStems | String email | Set<String> | 遍历 data/users/{email}/papers/(若存在)下所有 .txt 文件,提取题干(每行以“1.”、“2.”开头的内容),用于查重 |
|
|
|
|
|
|
|
|
|
|
#### QuizService
|
|
|
|
|
|
|
|
|
|
| 方法名 | 参数列表 | 返回值 | 逻辑描述 |
|
|
|
|
|
| ------------------- | ---------------------------------------------------------- | ---------------------- | ------------------------------------------------------------ |
|
|
|
|
|
| generateQuestions | String email, int count | List<ChoiceQuestion> | 调用 QuestionGenerator.generateOne() 循环生成,确保题干不重复(使用 loadUsedQuestionStems + 当前卷子 Set) |
|
|
|
|
|
| calculateScore | List<ChoiceQuestion> questions, List<String> userAnswers | int | 比对每题 correctAnswer 与用户答案,计算百分比得分(四舍五入) |
|
|
|
|
|
| savePaper | String email, List<ChoiceQuestion> questions | void | 生成时间戳文件名(yyyy-MM-dd-HH-mm-ss.txt),保存题干到 data/users/{email}/papers/(目录自动创建) |
|
|
|
|
|
|
|
|
|
|
### UI层接口设计
|
|
|
|
|
|
|
|
|
|
#### MainWindow
|
|
|
|
|
|
|
|
|
|
| 方法名 | 参数列表 | 返回值 | 逻辑描述 |
|
|
|
|
|
| ---------------------- | --------------------------------------------------------- | ------ | ------------------------------------------------- |
|
|
|
|
|
| showPanel | Pane panel | void | 设置 BorderPane.center 为指定面板,实现页面切换 |
|
|
|
|
|
| showLoginPanel | — | void | 创建并显示 LoginPanel |
|
|
|
|
|
| showRegisterPanel | — | void | 创建并显示 RegisterPanel |
|
|
|
|
|
| showGradeSelectPanel | — | void | 创建并显示 GradeSelectPanel |
|
|
|
|
|
| showQuizPanel | List<ChoiceQuestion> questions, QuizService quizService | void | 创建并显示 QuizPanel |
|
|
|
|
|
| showResultPanel | int score, Runnable onContinue | void | 创建并显示 ResultPanel |
|
|
|
|
|
|
|
|
|
|
#### RegisterPanel
|
|
|
|
|
|
|
|
|
|
| 方法名 | 参数列表 | 返回值 | 逻辑描述 |
|
|
|
|
|
| ---------------- | ----------------------- | ------ | ------------------------------------------------------------ |
|
|
|
|
|
| 构造函数 | MainWindow mainWindow | — | 初始化邮箱、注册码、密码输入框和按钮,绑定事件 |
|
|
|
|
|
| sendCodeAction | — | void | 调用 mainWindow.getUserService().sendRegistrationCode(),提示“注册码已发送(模拟)” |
|
|
|
|
|
| registerAction | — | void | 调用 setPassword(),成功则跳转到年级选择页 |
|
|
|
|
|
|
|
|
|
|
#### LoginPanel
|
|
|
|
|
|
|
|
|
|
| 方法名 | 参数列表 | 返回值 | 逻辑描述 |
|
|
|
|
|
| ------------- | ----------------------- | ------ | ----------------------------------------------------------- |
|
|
|
|
|
| 构造函数 | MainWindow mainWindow | — | 初始化登录表单,绑定“登录”按钮 |
|
|
|
|
|
| loginAction | — | void | 调用 login(),成功则设置 currentUser 并跳转到年级选择页 |
|
|
|
|
|
|
|
|
|
|
#### GradeSelectPanel
|
|
|
|
|
|
|
|
|
|
| 方法名 | 参数列表 | 返回值 | 逻辑描述 |
|
|
|
|
|
| ----------- | ----------------------- | ------ | ------------------------------------------------------------ |
|
|
|
|
|
| 构造函数 | MainWindow mainWindow | — | 创建三个年级按钮 |
|
|
|
|
|
| startQuiz | Grade grade | void | 弹出数量输入对话框(10–30),调用 QuizService.generateQuestions(),跳转到答题页 |
|
|
|
|
|
|
|
|
|
|
#### QuizPanel
|
|
|
|
|
|
|
|
|
|
| 方法名 | 参数列表 | 返回值 | 逻辑描述 |
|
|
|
|
|
| -------------- | ------------------------------------------------------------ | ------ | ------------------------------------------------------------ |
|
|
|
|
|
| 构造函数 | MainWindow mainWindow, List<ChoiceQuestion> questions, QuizService quizService | — | 显示第1题 |
|
|
|
|
|
| showQuestion | int index | void | 渲染当前题干和4个选项(RadioButton),绑定“提交”按钮 |
|
|
|
|
|
| submitAction | — | void | 记录答案,若非最后一题则显示下一题,否则计算分数并跳转结果页 |
|
|
|
|
|
|
|
|
|
|
#### ResultPanel
|
|
|
|
|
|
|
|
|
|
| 方法名 | 参数列表 | 返回值 | 逻辑描述 |
|
|
|
|
|
| ---------------- | ------------------------------------------------------- | ------ | ----------------------------------------------------- |
|
|
|
|
|
| 构造函数 | MainWindow mainWindow, int score, Runnable onContinue | — | 显示得分,绑定“退出”和“继续做题”按钮 |
|
|
|
|
|
| exitAction | — | void | 跳转到登录页 |
|
|
|
|
|
| continueAction | — | void | 执行 onContinue.run()(保存试卷),跳转到年级选择页 |
|