From 8955dbc7e6b5f9472be716aac86c33c7a59d92af Mon Sep 17 00:00:00 2001 From: zjx1 <1803419208@qq.com> Date: Mon, 29 Sep 2025 15:46:30 +0800 Subject: [PATCH] first --- README.md | 2 - doc/README.md | 148 ++++++ doc/bin/start.bat | 15 + doc/bin/start.ps1 | 28 ++ doc/paper/abc/2025-09-28-23-33-37.txt | 5 + doc/paper/admin/2025-09-28-23-33-35.txt | 5 + doc/paper/admin/2025-09-28-23-33-37.txt | 5 + doc/paper/admin123/2025-09-28-23-33-36.txt | 5 + doc/paper/admin123/2025-09-28-23-33-37.txt | 5 + doc/paper/null/2025-09-28-23-33-36.txt | 5 + doc/paper/primary1/2025-09-28-21-58-30.txt | 23 + doc/paper/test123/2025-09-28-22-48-11.txt | 9 + doc/paper/test123/2025-09-28-22-50-30.txt | 39 ++ doc/paper/test123/2025-09-28-23-32-54.txt | 5 + doc/paper/user/2025-09-28-23-33-36.txt | 5 + doc/paper/张三1/2025-09-28-22-02-48.txt | 31 ++ doc/paper/张三1/2025-09-28-22-03-02.txt | 23 + doc/paper/张三1/2025-09-28-22-59-11.txt | 25 + doc/paper/张三1/2025-09-28-22-59-26.txt | 25 + doc/paper/张三1/2025-09-28-22-59-37.txt | 29 ++ doc/paper/张三1/2025-09-29-00-09-16.txt | 27 + doc/paper/王五2/2025-09-28-22-26-06.txt | 25 + src/com/mathpaper/Main.class | Bin 0 -> 11471 bytes src/com/mathpaper/Main.java | 469 ++++++++++++++++++ .../mathpaper/generator/JuniorGenerator.class | Bin 0 -> 3042 bytes .../mathpaper/generator/JuniorGenerator.java | 151 ++++++ .../generator/PrimaryGenerator.class | Bin 0 -> 2671 bytes .../mathpaper/generator/PrimaryGenerator.java | 130 +++++ .../generator/QuestionGenerator.class | Bin 0 -> 3779 bytes .../generator/QuestionGenerator.java | 194 ++++++++ .../mathpaper/generator/SeniorGenerator.class | Bin 0 -> 4500 bytes .../mathpaper/generator/SeniorGenerator.java | 216 ++++++++ src/com/mathpaper/model/User.class | Bin 0 -> 2560 bytes src/com/mathpaper/model/User.java | 118 +++++ src/com/mathpaper/util/DuplicateChecker.class | Bin 0 -> 8835 bytes src/com/mathpaper/util/DuplicateChecker.java | 281 +++++++++++ src/com/mathpaper/util/FileUtil.class | Bin 0 -> 7014 bytes src/com/mathpaper/util/FileUtil.java | 290 +++++++++++ start.bat | 6 + start.ps1 | 5 + 40 files changed, 2347 insertions(+), 2 deletions(-) delete mode 100644 README.md create mode 100644 doc/README.md create mode 100644 doc/bin/start.bat create mode 100644 doc/bin/start.ps1 create mode 100644 doc/paper/abc/2025-09-28-23-33-37.txt create mode 100644 doc/paper/admin/2025-09-28-23-33-35.txt create mode 100644 doc/paper/admin/2025-09-28-23-33-37.txt create mode 100644 doc/paper/admin123/2025-09-28-23-33-36.txt create mode 100644 doc/paper/admin123/2025-09-28-23-33-37.txt create mode 100644 doc/paper/null/2025-09-28-23-33-36.txt create mode 100644 doc/paper/primary1/2025-09-28-21-58-30.txt create mode 100644 doc/paper/test123/2025-09-28-22-48-11.txt create mode 100644 doc/paper/test123/2025-09-28-22-50-30.txt create mode 100644 doc/paper/test123/2025-09-28-23-32-54.txt create mode 100644 doc/paper/user/2025-09-28-23-33-36.txt create mode 100644 doc/paper/张三1/2025-09-28-22-02-48.txt create mode 100644 doc/paper/张三1/2025-09-28-22-03-02.txt create mode 100644 doc/paper/张三1/2025-09-28-22-59-11.txt create mode 100644 doc/paper/张三1/2025-09-28-22-59-26.txt create mode 100644 doc/paper/张三1/2025-09-28-22-59-37.txt create mode 100644 doc/paper/张三1/2025-09-29-00-09-16.txt create mode 100644 doc/paper/王五2/2025-09-28-22-26-06.txt create mode 100644 src/com/mathpaper/Main.class create mode 100644 src/com/mathpaper/Main.java create mode 100644 src/com/mathpaper/generator/JuniorGenerator.class create mode 100644 src/com/mathpaper/generator/JuniorGenerator.java create mode 100644 src/com/mathpaper/generator/PrimaryGenerator.class create mode 100644 src/com/mathpaper/generator/PrimaryGenerator.java create mode 100644 src/com/mathpaper/generator/QuestionGenerator.class create mode 100644 src/com/mathpaper/generator/QuestionGenerator.java create mode 100644 src/com/mathpaper/generator/SeniorGenerator.class create mode 100644 src/com/mathpaper/generator/SeniorGenerator.java create mode 100644 src/com/mathpaper/model/User.class create mode 100644 src/com/mathpaper/model/User.java create mode 100644 src/com/mathpaper/util/DuplicateChecker.class create mode 100644 src/com/mathpaper/util/DuplicateChecker.java create mode 100644 src/com/mathpaper/util/FileUtil.class create mode 100644 src/com/mathpaper/util/FileUtil.java create mode 100644 start.bat create mode 100644 start.ps1 diff --git a/README.md b/README.md deleted file mode 100644 index c8e97a6..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# mathpaper - diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..34124b9 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,148 @@ +# 数学试卷生成系统 + +## 项目简介 + +这是一个智能数学试卷生成系统,能够根据不同年级(小学、初中、高中)自动生成相应难度的数学题目,并保存为试卷文件。 + +## 系统特性 + +### 🎯 核心功能 +- **多年级支持**:支持小学、初中、高中三个年级的题目生成 +- **智能题目生成**:根据年级自动调整题目难度和类型 +- **重复检测**:防止生成重复题目,确保试卷质量 +- **文件保存**:自动保存试卷到指定目录,支持时间戳命名 + +### 🚀 性能优化 +- **高效缓存机制**:智能缓存用户题目,支持LRU和时间过期策略 +- **优化文件I/O**:使用NIO和缓冲区提升文件操作性能 +- **内存管理**:自动清理过期缓存,防止内存泄漏 +- **异常处理**:完善的错误处理和降级机制 + +### 🎨 用户体验 +- **美观界面**:现代化的控制台界面设计 +- **进度显示**:题目生成过程中显示实时进度条 +- **即时预览**:生成完成后立即预览题目内容 +- **友好提示**:清晰的操作指引和状态反馈 + +## 题目类型 + +### 小学数学 +- 基础四则运算(加减乘除) +- 简单括号表达式 +- 数值范围:1-100 + +### 初中数学 +- 复杂四则运算 +- 多层括号嵌套 +- 平方运算 +- 数值范围:1-1000 + +### 高中数学 +- 三角函数(sin, cos, tan) +- 根式运算(√) +- 幂运算(x²) +- 复合函数表达式 +- 数值范围:1-10000 + +## 系统架构 + +``` +src/com/mathpaper/ +├── Main.java # 主程序入口 +├── model/ +│ └── User.java # 用户模型 +├── generator/ +│ ├── QuestionGenerator.java # 题目生成器接口 +│ ├── PrimaryGenerator.java # 小学题目生成器 +│ ├── JuniorGenerator.java # 初中题目生成器 +│ └── SeniorGenerator.java # 高中题目生成器 +└── util/ + ├── FileUtil.java # 文件操作工具 + └── DuplicateChecker.java # 重复检测工具 +``` + +## 使用方法 + +### 启动程序 +```bash +# Windows PowerShell +.\start.ps1 + +# 或直接运行 +java com.mathpaper.Main +``` + +### 操作流程 +1. **登录**:输入用户名(3-20位字母、数字或下划线) +2. **选择年级**:选择小学、初中或高中 +3. **生成试卷**:输入题目数量(1-50道) +4. **查看结果**:系统自动保存并预览生成的试卷 + +### 文件结构 +``` +doc/paper/用户名/时间戳.txt +``` +例如:`doc/paper/test123/2025-09-28-22-50-30.txt` + +## 技术特点 + +### 代码质量 +- **模块化设计**:清晰的分层架构,职责分离 +- **设计模式**:使用工厂模式、策略模式等 +- **异常处理**:完善的错误处理和用户友好的提示 +- **代码规范**:统一的编码风格和命名规范 + +### 性能优化 +- **缓存策略**: + - LRU缓存淘汰算法 + - 时间过期自动清理 + - 最大缓存用户数限制 +- **文件I/O优化**: + - NIO文件操作 + - 8KB缓冲区 + - UTF-8编码支持 +- **内存管理**: + - 自动垃圾回收 + - 资源及时释放 + - 内存泄漏防护 + +### 用户体验 +- **响应式界面**:实时进度反馈 +- **智能提示**:操作指引和错误提示 +- **数据验证**:输入参数严格验证 +- **容错处理**:优雅的错误恢复机制 + +## 开发环境 + +- **Java版本**:Java 8+ +- **编码格式**:UTF-8 +- **操作系统**:Windows/Linux/macOS +- **依赖**:无外部依赖,纯Java实现 + +## 版本历史 + +### v2.1 (当前版本) +- ✅ 完成性能优化和代码重构 +- ✅ 优化用户界面和交互体验 +- ✅ 增强缓存机制和内存管理 +- ✅ 改进文件I/O操作效率 +- ✅ 完善异常处理和错误恢复 + +### v2.0 +- ✅ 重构代码架构,提升可维护性 +- ✅ 优化题目生成算法 +- ✅ 增加重复检测功能 +- ✅ 改进用户界面设计 + +### v1.0 +- ✅ 基础功能实现 +- ✅ 多年级题目生成 +- ✅ 文件保存功能 + +## 贡献指南 + +欢迎提交Issue和Pull Request来改进这个项目! + +## 许可证 + +本项目采用MIT许可证。 \ No newline at end of file diff --git a/doc/bin/start.bat b/doc/bin/start.bat new file mode 100644 index 0000000..e86b85c --- /dev/null +++ b/doc/bin/start.bat @@ -0,0 +1,15 @@ +@echo off +chcp 65001 >nul +echo 启动数学试卷生成系统... +echo. +echo === 可用用户账号 === +echo 小学用户: 张三1, 张三2, 张三3, zhangsan1, zhangsan2, zhangsan3 +echo 初中用户: 李四1, 李四2, 李四3, lisi1, lisi2, lisi3 +echo 高中用户: 王五1, 王五2, 王五3, wangwu1, wangwu2, wangwu3 +echo 测试账号: test123, 测试 +echo =============================== +echo. +echo 正在启动程序... +cd /d "%~dp0..\.." +java -cp src com.mathpaper.Main +pause \ No newline at end of file diff --git a/doc/bin/start.ps1 b/doc/bin/start.ps1 new file mode 100644 index 0000000..bab752a --- /dev/null +++ b/doc/bin/start.ps1 @@ -0,0 +1,28 @@ +# Math Paper Generation System Startup Script +# Automatically set UTF-8 encoding and start the program + +Write-Host "启动数学试卷生成系统..." -ForegroundColor Green + +# Set console encoding to UTF-8 +Write-Host "设置字符编码为UTF-8..." -ForegroundColor Yellow +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 +[Console]::InputEncoding = [System.Text.Encoding]::UTF8 + +Write-Host "编码设置完成!" -ForegroundColor Green +Write-Host "" + +# Display available user accounts +Write-Host "=== 可用用户账号 ===" -ForegroundColor Cyan +Write-Host "小学用户: 张三1, 张三2, 张三3, zhangsan1, zhangsan2, zhangsan3" -ForegroundColor White +Write-Host "初中用户: 李四1, 李四2, 李四3, lisi1, lisi2, lisi3" -ForegroundColor White +Write-Host "高中用户: 王五1, 王五2, 王五3, wangwu1, wangwu2, wangwu3" -ForegroundColor White +Write-Host "测试账号: test123, 测试" -ForegroundColor White +Write-Host "===============================" -ForegroundColor Cyan +Write-Host "" + +# Change to project root directory +Set-Location (Join-Path $PSScriptRoot "..\..") + +# Start Java program +Write-Host "正在启动程序..." -ForegroundColor Green +java -cp src com.mathpaper.Main \ No newline at end of file diff --git a/doc/paper/abc/2025-09-28-23-33-37.txt b/doc/paper/abc/2025-09-28-23-33-37.txt new file mode 100644 index 0000000..b875614 --- /dev/null +++ b/doc/paper/abc/2025-09-28-23-33-37.txt @@ -0,0 +1,5 @@ +1. 82 + 16 + 23 * (35 + 94) + +2. 56 + +3. 23 + 90 - 15 * (76 + 26) \ No newline at end of file diff --git a/doc/paper/admin/2025-09-28-23-33-35.txt b/doc/paper/admin/2025-09-28-23-33-35.txt new file mode 100644 index 0000000..b427967 --- /dev/null +++ b/doc/paper/admin/2025-09-28-23-33-35.txt @@ -0,0 +1,5 @@ +1. 42 - 60 + +2. (19 * 60 + 9) + 64 + 89 + +3. 79 \ No newline at end of file diff --git a/doc/paper/admin/2025-09-28-23-33-37.txt b/doc/paper/admin/2025-09-28-23-33-37.txt new file mode 100644 index 0000000..f4ae68d --- /dev/null +++ b/doc/paper/admin/2025-09-28-23-33-37.txt @@ -0,0 +1,5 @@ +1. 69 + 54 * 22 + +2. 6 + +3. 32 / 24 / 47 \ No newline at end of file diff --git a/doc/paper/admin123/2025-09-28-23-33-36.txt b/doc/paper/admin123/2025-09-28-23-33-36.txt new file mode 100644 index 0000000..d7682ed --- /dev/null +++ b/doc/paper/admin123/2025-09-28-23-33-36.txt @@ -0,0 +1,5 @@ +1. 31 / (37 / 66) + +2. 61 / 34 + (91 * 94) + 78 + +3. 93 + 77 * 2 + 54 \ No newline at end of file diff --git a/doc/paper/admin123/2025-09-28-23-33-37.txt b/doc/paper/admin123/2025-09-28-23-33-37.txt new file mode 100644 index 0000000..eb0bed2 --- /dev/null +++ b/doc/paper/admin123/2025-09-28-23-33-37.txt @@ -0,0 +1,5 @@ +1. 47 * 35 + +2. (25 + 39 / 16) * 2 - 93 + +3. 28 + 60 \ No newline at end of file diff --git a/doc/paper/null/2025-09-28-23-33-36.txt b/doc/paper/null/2025-09-28-23-33-36.txt new file mode 100644 index 0000000..843d11f --- /dev/null +++ b/doc/paper/null/2025-09-28-23-33-36.txt @@ -0,0 +1,5 @@ +1. 20 * 32 + +2. 74 + 35 + +3. 95 * 14 \ No newline at end of file diff --git a/doc/paper/primary1/2025-09-28-21-58-30.txt b/doc/paper/primary1/2025-09-28-21-58-30.txt new file mode 100644 index 0000000..379d618 --- /dev/null +++ b/doc/paper/primary1/2025-09-28-21-58-30.txt @@ -0,0 +1,23 @@ +1. 100 - 55 + 32 + +2. 80 + +3. 92 + +4. 76 * 100 / 2 / 73 * 6 + +5. 37 * 52 + 90 + +6. 36 - 73 + +7. 26 * (95 * 90) + 60 + +8. 100 + +9. 82 - 2 + +10. 67 + +11. 27 * 22 / 30 * 52 + +12. 98 - 41 / (95 / 83) \ No newline at end of file diff --git a/doc/paper/test123/2025-09-28-22-48-11.txt b/doc/paper/test123/2025-09-28-22-48-11.txt new file mode 100644 index 0000000..e3a3038 --- /dev/null +++ b/doc/paper/test123/2025-09-28-22-48-11.txt @@ -0,0 +1,9 @@ +1. 77 + +2. 10 - (98 * 46) + +3. (63 + 61 * 45) + +4. 10 + 98 + +5. (51 - 48 + 2) \ No newline at end of file diff --git a/doc/paper/test123/2025-09-28-22-50-30.txt b/doc/paper/test123/2025-09-28-22-50-30.txt new file mode 100644 index 0000000..c860a04 --- /dev/null +++ b/doc/paper/test123/2025-09-28-22-50-30.txt @@ -0,0 +1,39 @@ +1. sin(3) * tan(3) + +2. tan(√(5+2)) + tan(5) - 8 / 10 + 4 + +3. sin(6) / sin(2) + 6 * 4^2 + √7 + +4. tan(7) - 7^2 - (3 * 1 * tan(3)) + +5. cos(5) - √2 / 4^2 + +6. tan(√(8+4)) - 1 + +7. sin(√(8+2)) + √7 * 8 + +8. tan(√(4+4)) - tan(9) + √3 + √3 + +9. cos(3) - 5 + +10. sin(2) / tan(2) / 7 + +11. cos(3) * 4 * 5 + +12. sin(6) / sin(4) - 6^2 + 7 + +13. tan(√(8+3)) - tan(9) / 10 / 7 + +14. cos(6) - √9 - 9^2 + 2^2 * 2^2 + +15. tan(10) - 7 (* tan(6) + 9) + +16. tan(√(10+2)) + cos(3) + +17. cos(√(4+1)) * 7 + +18. tan(10) - √2 / tan(√(10+3)) / 4 / 7^2 + +19. sin(6) - sin(2) - 1 / √8 - 8 + +20. tan(3) / cos(5) \ No newline at end of file diff --git a/doc/paper/test123/2025-09-28-23-32-54.txt b/doc/paper/test123/2025-09-28-23-32-54.txt new file mode 100644 index 0000000..5da9266 --- /dev/null +++ b/doc/paper/test123/2025-09-28-23-32-54.txt @@ -0,0 +1,5 @@ +1. 66 / 95 + 87 + +2. (27 / 36 / 19) * 68 + +3. 1 / 2 \ No newline at end of file diff --git a/doc/paper/user/2025-09-28-23-33-36.txt b/doc/paper/user/2025-09-28-23-33-36.txt new file mode 100644 index 0000000..cd20eeb --- /dev/null +++ b/doc/paper/user/2025-09-28-23-33-36.txt @@ -0,0 +1,5 @@ +1. 33 + +2. 27 + 99 - (24 + 51 / 26) + +3. (25 + 26) + 71 \ No newline at end of file diff --git a/doc/paper/张三1/2025-09-28-22-02-48.txt b/doc/paper/张三1/2025-09-28-22-02-48.txt new file mode 100644 index 0000000..6ffe6ba --- /dev/null +++ b/doc/paper/张三1/2025-09-28-22-02-48.txt @@ -0,0 +1,31 @@ +1. 39 + +2. 76 - 14 / 8 + +3. 49 + +4. 11 - 76 * (58 / 38 - 73) + +5. 16 - 61 * 69 + +6. (47 / 82 / 65) + +7. (62 / 70) / 10 + +8. 94 * 27 + 50 - 10 + +9. 44 - (2 + 76 + 58) - 2 + +10. 27 + +11. 57 / 60 + 23 + +12. 90 * 63 + +13. 72 + 81 + 54 * 1 + +14. 46 / 92 + +15. 58 + 12 + +16. (49 * 19 * 69 * 9) \ No newline at end of file diff --git a/doc/paper/张三1/2025-09-28-22-03-02.txt b/doc/paper/张三1/2025-09-28-22-03-02.txt new file mode 100644 index 0000000..07c7565 --- /dev/null +++ b/doc/paper/张三1/2025-09-28-22-03-02.txt @@ -0,0 +1,23 @@ +1. 54 + +2. 100 / 73 + 69 * 11 / 60 + +3. 8 + 4 * 40 * 60 + +4. 91 + +5. 88 / 58 - 77 + +6. 2 + +7. 89 * 87 - 99 + +8. 89 - 36 + 12 + +9. 90 / 21 + +10. 61 - 16 / 93 * 57 * 68 + +11. 33 * (96 / 45) + +12. 14 + (63 + 26) \ No newline at end of file diff --git a/doc/paper/张三1/2025-09-28-22-59-11.txt b/doc/paper/张三1/2025-09-28-22-59-11.txt new file mode 100644 index 0000000..93f214d --- /dev/null +++ b/doc/paper/张三1/2025-09-28-22-59-11.txt @@ -0,0 +1,25 @@ +1. tan(2) / 2 + 6 - √7 - tan(√(2+1)) + +2. sin(6) * 6^2 / tan(4) + +3. sin(1) * cos(10) / √5 + 1 + +4. tan(√(10+5)) + cos(6) / 10^2 * √6 + 7 + +5. tan(8) / (4 / 9 + 1) + +6. (tan(8) / 9^2 - 6^2 /) 2 + +7. tan(1) * √6 - sin(7) * 5 * √5 + +8. cos(8) / cos(8) / √5 - √2 / 10 + +9. cos(√(1+2)) * 10 / tan(2) / 5 + 3 + +10. cos(√(1+1)) * 5 + 6 + 6 + +11. sin(5) * 9^2 - tan(7) / 4 * 10 + +12. sin(10) / √8 + 8^2 * 6^2 - 5 + +13. tan(4) * 10 \ No newline at end of file diff --git a/doc/paper/张三1/2025-09-28-22-59-26.txt b/doc/paper/张三1/2025-09-28-22-59-26.txt new file mode 100644 index 0000000..041d96b --- /dev/null +++ b/doc/paper/张三1/2025-09-28-22-59-26.txt @@ -0,0 +1,25 @@ +1. √23 * 31 + +2. 18^2 - 1 + √23 / 18 + +3. 23^2 - (33 / 25) + +4. 27^2 - 9^2 + +5. √10 - 22^2 * 7 + +6. 40^2 * √25 * 12 - 16 + +7. √25 / √1 * 18 * 8 + 32 + +8. 7^2 / √24 / (50 * 35) + +9. 5^2 (* 21 + 35) / 9 * 33 + +10. √25 - 48^2 + 9 + +11. 20^2 * 48 + 39 + +12. √25 / 38 - 21 * 28 * 32 + +13. 4^2 / √2 * 15 \ No newline at end of file diff --git a/doc/paper/张三1/2025-09-28-22-59-37.txt b/doc/paper/张三1/2025-09-28-22-59-37.txt new file mode 100644 index 0000000..e88cec6 --- /dev/null +++ b/doc/paper/张三1/2025-09-28-22-59-37.txt @@ -0,0 +1,29 @@ +1. 65 / 18 + +2. 28 * 78 + +3. 22 + 93 * 63 * 98 + +4. 25 - 33 - (35 + 18 / 82) + +5. 59 + +6. 98 + 52 * 94 + +7. 100 * (87 + 51) + +8. (15 / 44 / 55) + +9. 37 - 48 * 84 + +10. 10 * 38 + +11. 76 + +12. 67 / (23 * 85) + +13. 38 - 100 * 77 + +14. 94 * 33 + +15. 38 \ No newline at end of file diff --git a/doc/paper/张三1/2025-09-29-00-09-16.txt b/doc/paper/张三1/2025-09-29-00-09-16.txt new file mode 100644 index 0000000..245915c --- /dev/null +++ b/doc/paper/张三1/2025-09-29-00-09-16.txt @@ -0,0 +1,27 @@ +1. 10^2 + 42 - 20 + +2. √5 * 20 + +3. (√20 - √25 + 11) / 26 + 29 + +4. √23 * 6 / 32 + +5. 31^2 * (35 - √25) + +6. √25 + 41 * 11 * 8 + +7. 48^2 / 7 + +8. √25 / 17 + +9. 14^2 / 3 + +10. √25 / 1 + √3 * 21 + +11. √25 * (17 / 32^2) + +12. 26^2 / 49 - 15^2 + +13. 43^2 + (49 - 18^2) + +14. 49^2 / 27 - 28^2 / 13 + 5 \ No newline at end of file diff --git a/doc/paper/王五2/2025-09-28-22-26-06.txt b/doc/paper/王五2/2025-09-28-22-26-06.txt new file mode 100644 index 0000000..494c07b --- /dev/null +++ b/doc/paper/王五2/2025-09-28-22-26-06.txt @@ -0,0 +1,25 @@ +1. cos(1) * 4 * tan(√(6+5)) * 2 / √6 + +2. cos(10) * sin(√(10+2)) / √1 + +3. sin(√(3+3)) + sin(√(5+2)) + 6 / 10 + 9 + +4. cos(√(9+2)) / √1 * 9^2 + +5. sin(9) + tan(√(2+5)) + +6. cos(8) - 1 - sin(√(10+2)) + 8 + +7. tan(2) - 5 + tan(9) / 1 + +8. tan(3) / 10 * tan(4) + 7 + 7^2 + +9. tan(√(9+4)) - tan(√(7+4)) + +10. tan(1) / sin(√(3+2)) / 4 + +11. tan(10) / 3 * 3 - cos(√(2+5)) * sin(10) + +12. sin(3) + √3 / 5 + +13. tan(5) / cos(7) / sin(√(6+3)) - tan(3) + 2 \ No newline at end of file diff --git a/src/com/mathpaper/Main.class b/src/com/mathpaper/Main.class new file mode 100644 index 0000000000000000000000000000000000000000..d203ccaf46d47050ce6f09bb5f999d44c156d19f GIT binary patch literal 11471 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 b=O^;{nS6dBpVtj0tjI#R`NnydLW%zcN)^TF literal 0 HcmV?d00001 diff --git a/src/com/mathpaper/Main.java b/src/com/mathpaper/Main.java new file mode 100644 index 0000000..1d74e91 --- /dev/null +++ b/src/com/mathpaper/Main.java @@ -0,0 +1,469 @@ +package com.mathpaper; + +import com.mathpaper.model.User; +import com.mathpaper.generator.QuestionGenerator; +import com.mathpaper.generator.PrimaryGenerator; +import com.mathpaper.generator.JuniorGenerator; +import com.mathpaper.generator.SeniorGenerator; +import com.mathpaper.util.FileUtil; +import com.mathpaper.util.DuplicateChecker; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Scanner; + +/** + * 数学试卷生成程序主类 + * 负责用户登录、交互和试卷生成的主要流程 + * + * @author 系统 + * @version 2.0 + */ +public class Main { + + // 常量定义 + private static final int MIN_QUESTIONS = 1; + private static final int MAX_QUESTIONS = 100; + private static final int PREVIEW_COUNT = 5; + private static final int MAX_ATTEMPTS_MULTIPLIER = 10; + private static final String GRADE_SWITCH_PREFIX = "切换为"; + private static final String EXIT_COMMAND = "-1"; + + // 当前年级(全局变量) + private static String currentGrade = "小学"; + + // 界面美化常量 + private static final String SEPARATOR = "=" + "=".repeat(50); + private static final String MENU_SEPARATOR = "=" + "=".repeat(50); + private static final int PROGRESS_BAR_LENGTH = 30; + + // 预设用户账号 + private static final Map USERS = new HashMap<>(); + + static { + initializeUsers(); + } + + /** + * 初始化用户账号 + */ + private static void initializeUsers() { + // 小学用户(中文和英文用户名) + addUser("张三1", "123", "小学"); + addUser("张三2", "123", "小学"); + addUser("张三3", "123", "小学"); + addUser("zhangsan1", "123", "小学"); + addUser("zhangsan2", "123", "小学"); + addUser("zhangsan3", "123", "小学"); + addUser("primary1", "123", "小学"); + addUser("primary2", "123", "小学"); + + // 初中用户(中文和英文用户名) + addUser("李四1", "123", "初中"); + addUser("李四2", "123", "初中"); + addUser("李四3", "123", "初中"); + addUser("lisi1", "123", "初中"); + addUser("lisi2", "123", "初中"); + addUser("lisi3", "123", "初中"); + addUser("junior1", "123", "初中"); + addUser("junior2", "123", "初中"); + + // 高中用户(中文和英文用户名) + addUser("王五1", "123", "高中"); + addUser("王五2", "123", "高中"); + addUser("王五3", "123", "高中"); + addUser("wangwu1", "123", "高中"); + addUser("wangwu2", "123", "高中"); + addUser("wangwu3", "123", "高中"); + addUser("senior1", "123", "高中"); + addUser("senior2", "123", "高中"); + + // 测试账号 + addUser("test123", "123", "小学"); + addUser("测试", "123", "小学"); + } + + /** + * 添加用户到系统 + */ + private static void addUser(String username, String password, String grade) { + // 由于User类已简化,这里只存储用户名 + try { + USERS.put(username, new User(username)); + } catch (IllegalArgumentException e) { + System.err.println("添加用户失败: " + username + " - " + e.getMessage()); + } + } + + /** + * 程序主入口 + */ + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + + try { + // 定期清理过期缓存 + DuplicateChecker.cleanupExpiredCache(); + + // 用户登录 + User user = performLogin(scanner); + if (user == null) { + System.out.println("登录失败,程序退出"); + return; + } + + // 主交互循环 + runMainLoop(scanner, user); + + } catch (Exception e) { + System.err.println("程序运行出现异常: " + e.getMessage()); + } finally { + scanner.close(); + } + } + + /** + * 用户登录 + */ + private static User performLogin(Scanner scanner) { + System.out.println("=== 用户登录 ==="); + System.out.println("请输入用户名和密码进行登录"); + System.out.println(); + + while (true) { + System.out.print("用户名: "); + String username = scanner.nextLine().trim(); + + // 验证用户名格式 + String validationError = User.getValidationError(username); + if (validationError != null) { + System.out.println("❌ " + validationError); + System.out.println("请重新输入:"); + continue; + } + + // 检查用户是否存在 + if (!USERS.containsKey(username)) { + System.out.println("❌ 用户名不存在,请检查输入或使用以下用户名:"); + System.out.println("中文用户:张三1, 张三2, 张三3, 李四1, 李四2, 李四3, 王五1, 王五2, 王五3, 测试"); + System.out.println("英文用户:zhangsan1, zhangsan2, zhangsan3, lisi1, lisi2, lisi3, wangwu1, wangwu2, wangwu3, test123"); + System.out.println("请重新输入:"); + continue; + } + + System.out.print("密码: "); + String password = scanner.nextLine().trim(); + + // 验证密码(所有用户密码都是123) + if (!"123".equals(password)) { + System.out.println("❌ 密码错误!所有用户的密码都是 123"); + System.out.println("请重新输入:"); + continue; + } + + try { + User user = new User(username); + System.out.println("✅ 登录成功!欢迎," + username + "!"); + System.out.println(); + return user; + } catch (IllegalArgumentException e) { + System.out.println("❌ " + e.getMessage()); + System.out.println("请重新输入:"); + } + } + } + + /** + * 验证登录信息(简化版本) + */ + private static User validateLogin(String input) { + String[] parts = input.split("\\s+"); + + if (parts.length != 2) { + return null; + } + + String username = parts[0].trim(); + String password = parts[1].trim(); + + // 简单验证:检查用户是否存在 + User user = USERS.get(username); + if (user != null) { + return user; + } + + return null; + } + + /** + * 运行主交互循环 + */ + private static void runMainLoop(Scanner scanner, User user) { + QuestionGenerator generator = getGenerator(currentGrade); + + while (true) { + displayMainMenu(user); + String choice = scanner.nextLine().trim(); + + switch (choice) { + case "1": + handleGradeSwitch(scanner, user); + generator = getGenerator(currentGrade); + break; + case "2": + handleQuestionGeneration(scanner, user, generator); + break; + case "3": + displayExitMessage(); + return; + default: + System.out.println("❌ 无效选择,请重新输入!"); + } + } + } + + /** + * 显示欢迎界面 + */ + private static void displayWelcomeScreen(User user) { + System.out.println(SEPARATOR); + System.out.println("🎓 数学题目生成系统"); + System.out.println("👤 用户:" + user.getUsername()); + System.out.println("📚 当前年级:" + currentGrade); + System.out.println(SEPARATOR); + } + + /** + * 显示主菜单 + */ + private static void displayMainMenu(User user) { + System.out.println(MENU_SEPARATOR); + System.out.println("📚 当前年级:" + currentGrade); + System.out.println("👤 当前用户:" + user.getUsername()); + System.out.println(); + System.out.println("1. 切换年级"); + System.out.println("2. 生成试卷"); + System.out.println("3. 退出系统"); + System.out.println(MENU_SEPARATOR); + System.out.print("\n🔹 请选择操作(当前:" + currentGrade + "):"); + } + + /** + * 显示分隔线 + */ + private static void displaySeparator() { + System.out.println("\n" + "-".repeat(30) + "\n"); + } + + /** + * 显示退出消息 + */ + private static void displayExitMessage() { + System.out.println("\n" + SEPARATOR); + System.out.println("👋 感谢使用数学题目生成系统!"); + System.out.println("🎉 祝您学习愉快,再见!"); + System.out.println(SEPARATOR); + } + + /** + * 根据年级获取对应的题目生成器 + */ + private static QuestionGenerator getGenerator(String grade) { + switch (grade) { + case "小学": + return new PrimaryGenerator(); + case "初中": + return new JuniorGenerator(); + case "高中": + return new SeniorGenerator(); + default: + System.err.println("未知年级: " + grade + ",使用小学模式"); + return new PrimaryGenerator(); + } + } + + /** + * 验证年级是否有效 + */ + private static boolean isValidGrade(String grade) { + return "小学".equals(grade) || "初中".equals(grade) || "高中".equals(grade); + } + + /** + * 生成并保存试卷 + */ + private static void generateAndSavePaper(QuestionGenerator generator, int count, User user) { + try { + List questions = generateQuestionsWithProgress(generator, count, user.getUsername()); + + if (questions.isEmpty()) { + System.out.println("❌ 生成失败:无法生成题目,请稍后重试"); + return; + } + + if (questions.size() < count) { + System.out.println("⚠️ 注意:由于查重限制,实际生成了 " + questions.size() + " 道题目"); + } + + System.out.print("💾 正在保存试卷..."); + String path = FileUtil.getSavePath(user.getUsername()); + FileUtil.savePaper(path, questions); + System.out.println(" 完成!"); + System.out.println("✅ 试卷已保存至:" + path); + + displayQuestionPreview(questions); + + } catch (Exception e) { + System.err.println("❌ 生成试卷时出现错误: " + e.getMessage()); + } + } + + /** + * 带进度提示的题目生成 + */ + private static List generateQuestionsWithProgress(QuestionGenerator generator, int count, String username) { + List questions = new ArrayList<>(); + generator.clearCurrentQuestions(); + + int attempts = 0; + int maxAttempts = count * MAX_ATTEMPTS_MULTIPLIER; + + System.out.println("📊 生成进度:"); + + while (questions.size() < count && attempts < maxAttempts) { + try { + String question = generator.generateUniqueQuestion(); + + if (question != null && !question.trim().isEmpty()) { + // 检查是否与历史题目重复 + if (!DuplicateChecker.isDuplicate(question, username)) { + questions.add(question); + + // 显示进度 + displayProgress(questions.size(), count); + } + } + + } catch (Exception e) { + System.err.println("⚠️ 生成题目时出现错误: " + e.getMessage()); + } + + attempts++; + } + + if (questions.size() < count && attempts >= maxAttempts) { + System.out.println("\n⚠️ 提示:已达到最大尝试次数,可能存在题目重复过多的情况"); + } else if (questions.size() == count) { + System.out.println("\n🎉 题目生成完成!"); + } + + return questions; + } + + /** + * 显示生成进度 + */ + private static void displayProgress(int current, int total) { + int percentage = (int) ((double) current / total * 100); + int progressLength = (int) ((double) current / total * PROGRESS_BAR_LENGTH); + + StringBuilder progressBar = new StringBuilder(); + progressBar.append("["); + + for (int i = 0; i < PROGRESS_BAR_LENGTH; i++) { + if (i < progressLength) { + progressBar.append("█"); + } else { + progressBar.append("░"); + } + } + + progressBar.append("]"); + + System.out.print("\r" + progressBar.toString() + " " + current + "/" + total + " (" + percentage + "%)"); + + if (current == total) { + System.out.println(); // 换行 + } + } + + /** + * 显示题目预览 + */ + private static void displayQuestionPreview(List questions) { + System.out.println("\n📝 题目预览"); + System.out.println("─".repeat(40)); + + int displayCount = Math.min(PREVIEW_COUNT, questions.size()); + for (int i = 0; i < displayCount; i++) { + System.out.println((i + 1) + ". " + questions.get(i)); + } + + if (questions.size() > PREVIEW_COUNT) { + System.out.println(" ... 还有 " + (questions.size() - PREVIEW_COUNT) + " 道题目"); + } + + System.out.println("─".repeat(40)); + System.out.println("📊 总计:" + questions.size() + " 道题目"); + } + + /** + * 处理年级切换 + */ + private static void handleGradeSwitch(Scanner scanner, User user) { + System.out.println("\n📚 请选择年级:"); + System.out.println("1. 小学"); + System.out.println("2. 初中"); + System.out.println("3. 高中"); + System.out.print("请输入选择(1-3):"); + + String choice = scanner.nextLine().trim(); + String newGrade = null; + + switch (choice) { + case "1": + newGrade = "小学"; + break; + case "2": + newGrade = "初中"; + break; + case "3": + newGrade = "高中"; + break; + default: + System.out.println("❌ 无效选择!"); + return; + } + + if (!newGrade.equals(currentGrade)) { + currentGrade = newGrade; + System.out.println("✅ 已切换到" + currentGrade + "模式"); + } else { + System.out.println("ℹ️ 当前已是" + currentGrade + "模式"); + } + } + + /** + * 处理题目生成 + */ + private static void handleQuestionGeneration(Scanner scanner, User user, QuestionGenerator generator) { + System.out.print("\n📝 请输入要生成的题目数量(1-50):"); + String input = scanner.nextLine().trim(); + + try { + int count = Integer.parseInt(input); + if (count < 1 || count > 50) { + System.out.println("❌ 题目数量必须在1-50之间!"); + return; + } + + System.out.println("\n🔄 开始生成" + count + "道" + currentGrade + "数学题目..."); + generateAndSavePaper(generator, count, user); + + } catch (NumberFormatException e) { + System.out.println("❌ 请输入有效的数字!"); + } + } +} \ No newline at end of file diff --git a/src/com/mathpaper/generator/JuniorGenerator.class b/src/com/mathpaper/generator/JuniorGenerator.class new file mode 100644 index 0000000000000000000000000000000000000000..3296504d68daec7a54805a830914e43a9d47fe10 GIT binary patch literal 3042 zcma)7TXR#_8C~1DoFn-J6ybw0#svZaS(Z_YL2Z$1kS|FDmP{;TBa;9J>BN>JORjuG zASHLVp{4icf(e;P+z0y7ne;LOnt{no-#UHkQ(rsn^fxp^OV>U}mWAVVs?lg`@BLlZ zx4v)x=|6wC4`2v4RkR3r|8e!hzu)<_A8vRQcr_?c1-f(P`RKf1%`O-VW+ggfmduJ_ zl`GL_tLCCrD3_jIJ)wpELaAU42`uztfQH8r6xdvk$xK)0FPN1@fi0nAw-Z3s zDioumg+(haHQF@jQsePejWMPqlUbCv?HabBgTWj5d?J6*DCNw&Bfu#G{RhJyl{Q=FmMav^83zM*FMRqIVA>PHF=2?W-XbXcMw zCeXhAbg)_|=FN)C?i(7ui6ew;vG6jzxk8c)htVbPzNKLRM|n77GFSaki0W=7$_WjF z(uP_nS(6e`nfkDX5j;f>JLw1no2ol*^N$>Q21y0SHJrfcYV>wl9RUG<$-HC@m#Y_w zPH@BCqkrkMKT`LkhG#J@&?=<{%jKeJlPGOP}7z+z#iFMlXXa){Kbu8j4 zP7Ac0Jt#G&rREud-i_L>3HnDZ`0+f>DmbU%ylgUuEH<4|YrUnr@#z(0SxdP4;R z61$|g7c|U(_o`KP3<|V`HlV&F$()84Q6!s5yfo*9nIEhexfe}~{j~4@iZ<>&`qyRo zx#3427BqYt>;%u^Lb1Ri@88HqGXb3lS-Cz~8mhAF-lAEWv1V1g#Ee2JE_3Nt@f|Kb zDqi8ztKu~-VHIz1iIU@~Wcr-l6X{{Lgh&cJHpHP|8FdD^I7c}`wN&DCLr$J~NC~N% zK9w3A8J8zQo=|G-ggw-r7@ka|hen2}eD37<*kEEXIhvfv($z3KaBw^^bYf)U(Hl~q zWN{Cb^Q7A5(L$*{|0WEZe1X*&w>qa8a>gR|DPy57;>#3fN`_Uf&_JlcwZ?esZ(`2s zfEkEO%)c;Qn?>u0OUdWgPt0byHIq$^CdX8>P{Y^;{8HqkQu}ko`lI8|l&h7T`BXvn zUfa6oA_}J~GT|SchmF{!Am+5zZ zitX9X+t`tE4Rm#`V0$F&4jiQYJJ@*_Pqtuc$t7+*{AWa(Zm|R03J?GH62AioAW9?; zU_T##U+`2z%fHd8V4wILop{IAd6#;8Ya5Q8G&XSjTlCZr{S1Bo)A12Hj?-~J9mnZ- z;7c99N0)rY9mnD<^Yt^JKYxnD*`B-jI;XZadmHhzE9Q2^Jg%5m_d4e*__#;+-bH^4 zKEtjWj>Qz+y^MC<<5__o{ym0jc)AU#>o=jeZ-LsXt}f$vcZ8>OZ^Vvj1zo+t9K|Ay82Y%gkEWKqtj2Vh z#WrgwtQ94VEzIp1R`>+kFp74h@g&ALqsK9f3?GG4n8qaXIE_V|!4*75TA#%=oMTZ9 zT<6U%Vd5V3{)jmn(+KA)yCSz-^8;a57kL+R>u%5am`8U9yyqkL-Fb`Vfue??4>eaBQoM-8dsIHh#_eJinU{Lqz?vK#gp$0EX)H~FO?jw=2Q@l`|i%ZT{ zT3xXvCCXDvK0JTI{yFJmWOVJE$c2wuZMypCgd1IIZI zvK$;a4vsmzjS2_E6%K>9*#6hpZSN4_o4jAayEd*ND1isqq~Q8Pve<9K+s7TT6+*#b zBCKx8HIwxQU(fswJ!@0M%L;%@5V*m$ H1t0tu46V4b literal 0 HcmV?d00001 diff --git a/src/com/mathpaper/generator/JuniorGenerator.java b/src/com/mathpaper/generator/JuniorGenerator.java new file mode 100644 index 0000000..e1f6d72 --- /dev/null +++ b/src/com/mathpaper/generator/JuniorGenerator.java @@ -0,0 +1,151 @@ +package com.mathpaper.generator; + +import java.util.ArrayList; +import java.util.List; + +/** + * 初中题目生成器 + * 生成2-5个操作数的题目,至少含1个平方(^2)或开根号(√),支持嵌套括号 + * + * @author 系统 + * @version 2.0 + */ +public class JuniorGenerator extends QuestionGenerator { + + // 常量定义 + private static final int MIN_OPERANDS = 2; + private static final int MAX_OPERANDS = 5; + private static final int MIN_NUMBER = 1; + private static final int MAX_NUMBER = 50; // 避免数字过大 + private static final String[] OPERATORS = {"+", "-", "*", "/"}; + private static final double ADVANCED_OP_PROBABILITY = 0.3; // 30%概率添加高级运算 + private static final double BRACKET_PROBABILITY = 0.4; // 40%概率添加括号 + + /** + * 构造函数 + */ + public JuniorGenerator() { + super("初中"); + } + + @Override + public String generateQuestion() { + int numCount = randomInt(MIN_OPERANDS, MAX_OPERANDS); + List nums = generateNumbers(numCount); + List ops = generateOperators(numCount - 1); + return addAdvancedOperations(nums, ops); + } + + /** + * 生成指定数量的随机数(1-50,避免过大) + * @param count 数字数量 + * @return 数字列表 + */ + private List generateNumbers(int count) { + List nums = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + nums.add(randomInt(MIN_NUMBER, MAX_NUMBER)); + } + return nums; + } + + /** + * 生成指定数量的运算符 + * @param count 运算符数量 + * @return 运算符列表 + */ + private List generateOperators(int count) { + List ops = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + ops.add(randomChoice(OPERATORS)); + } + return ops; + } + + /** + * 添加高级运算(平方、开根号)和嵌套括号 + * @param nums 数字列表 + * @param ops 运算符列表 + * @return 包含高级运算的表达式字符串 + */ + private String addAdvancedOperations(List nums, List ops) { + StringBuilder sb = new StringBuilder(nums.size() * 12); // 预估容量 + + // 确保至少有一个平方或开根号 + boolean hasAdvanced = false; + int advancedCount = 0; + + for (int i = 0; i < nums.size(); i++) { + int num = nums.get(i); + + // 决定是否添加高级运算 + boolean shouldAddAdvanced = !hasAdvanced || + (advancedCount < 2 && random.nextDouble() < ADVANCED_OP_PROBABILITY); + + if (shouldAddAdvanced) { + if (random.nextBoolean()) { + // 添加平方 + sb.append(num).append("^2"); + } else { + // 添加开根号(选择较小的数避免复杂计算) + int sqrtNum = Math.min(num, 25); // 限制开根号的数不超过25 + sb.append("√").append(sqrtNum); + } + hasAdvanced = true; + advancedCount++; + } else { + sb.append(num); + } + + if (i < ops.size()) { + sb.append(" ").append(ops.get(i)).append(" "); + } + } + + // 添加嵌套括号 + return addNestedBrackets(sb.toString()); + } + + /** + * 添加嵌套括号 + * @param expression 原始表达式 + * @return 带括号的表达式 + */ + private String addNestedBrackets(String expression) { + if (random.nextDouble() >= BRACKET_PROBABILITY) { + return expression; + } + + String[] parts = expression.split(" "); + if (parts.length < 3) { + return expression; + } + + // 选择括号范围(确保至少包含一个完整的运算) + int maxStart = parts.length - 3; // 至少需要 数字 运算符 数字 + int start = randomInt(0, maxStart); + int minEnd = start + 2; // 至少包含3个部分 + int maxEnd = Math.min(start + 6, parts.length - 1); // 最多包含7个部分 + int end = randomInt(minEnd, maxEnd); + + StringBuilder result = new StringBuilder(expression.length() + 4); + + for (int i = 0; i < parts.length; i++) { + if (i == start) { + result.append("("); + } + + result.append(parts[i]); + + if (i == end) { + result.append(")"); + } + + if (i < parts.length - 1) { + result.append(" "); + } + } + + return result.toString(); + } +} \ No newline at end of file diff --git a/src/com/mathpaper/generator/PrimaryGenerator.class b/src/com/mathpaper/generator/PrimaryGenerator.class new file mode 100644 index 0000000000000000000000000000000000000000..b5aad3260d316eb53bf945fd2d16c1a501e84884 GIT binary patch literal 2671 zcmaJ?-E$LF6#w03lij9EDWL)Ehoyj!qzOU6DorUgZGqS}DM@JvUuByv?LxlXY_K3I zI=nmM6AwBn%tnE}d$e zkcCmLq#0uj{=m#N<%SY1D5aHX#?ZCWN-Ve_!pjhNmAyG#D=PZUra3R#AtQD73y~?mXkO*1`YKB&b&S+I5#&2}X@beqn z8aGnJiLVSvZgLo2 z^q`-I_H_na0gsc@eVymvGI)ivaDb&=|RfZi+UZyi!Bi4caI3OY* zBZv?~XUi{l$>1VF8*-BMuimee(4vRqp#Sf(dE$N#3%ryu8ZQpPEs z_-z!Yj8?1^)JFQbG$G?XF6~~bX@%vuQq}TPYZYCsR;l_$%rNZS$S2M+dFw!4^wECbukE#-vY*OM9u8^~Uge6*n5(q6Dl0a3;A`(_<2}!s{OO%Rb zHj!L3iz_)vwZ#$)TPDh-s-cvO1zy}P$^_$jqKft~>Ts+S5RxyFD;aYucHBMDHSQ(DMV5?j_rdRx#vb58?WWUK!Tt=_LWsOF;VQ zy2ko@@1rLi61vD@$8Q*Tgg4rdz3XB>y!a=~O}Ci}Su2!iVOa+jNhEG0028Pl5!Y`|vsF`1_kX*>Cvobc#ee2nRw<)}+qeTdnlYgq6L*2#Uu zB13-R5t41FBc1gNVH47Q^hbOPb({;^LJ9@#%%kjGfzt4Vl6;{B?R82ous~@&N6>PV zlk@Z)xqvgY&tlPpx&^N1=oT@}MxPNhDO??YA>m<+;8T!fr+G~Qg@Vb|Wg^h##X9Ge*D>sI4U2xU!7S?+{qAqzjrbM`dX{*KEuNy^V~P|m@{2FAwp7=# z7K!zD>sgPWlfY`kx nums = generateNumbers(numCount); + List ops = generateOperators(numCount - 1); + return addBrackets(nums, ops); + } + + /** + * 生成指定数量的随机数(1-100) + * @param count 数字数量 + * @return 数字列表 + */ + private List generateNumbers(int count) { + List nums = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + nums.add(randomInt(MIN_NUMBER, MAX_NUMBER)); + } + return nums; + } + + /** + * 生成指定数量的运算符 + * @param count 运算符数量 + * @return 运算符列表 + */ + private List generateOperators(int count) { + List ops = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + ops.add(randomChoice(OPERATORS)); + } + return ops; + } + + /** + * 添加括号(最多1层,无嵌套) + * @param nums 数字列表 + * @param ops 运算符列表 + * @return 带括号的表达式字符串 + */ + private String addBrackets(List nums, List ops) { + // 随机决定是否添加括号(需要至少3个操作数) + if (nums.size() >= 3 && random.nextDouble() < BRACKET_PROBABILITY) { + return addSimpleBrackets(nums, ops); + } + + return buildBasicExpression(nums, ops); + } + + /** + * 构建基本表达式(无括号) + * @param nums 数字列表 + * @param ops 运算符列表 + * @return 表达式字符串 + */ + private String buildBasicExpression(List nums, List ops) { + StringBuilder sb = new StringBuilder(nums.size() * 8); // 预估容量 + + for (int i = 0; i < nums.size(); i++) { + sb.append(nums.get(i)); + if (i < ops.size()) { + sb.append(" ").append(ops.get(i)).append(" "); + } + } + + return sb.toString(); + } + + /** + * 添加简单括号(不嵌套) + * @param nums 数字列表 + * @param ops 运算符列表 + * @return 带括号的表达式字符串 + */ + private String addSimpleBrackets(List nums, List ops) { + StringBuilder sb = new StringBuilder(nums.size() * 10); // 预估容量(包含括号) + + // 随机选择括号位置(确保至少包含2个操作数) + int maxStart = nums.size() - 2; + int bracketStart = randomInt(0, maxStart); + int bracketEnd = randomInt(bracketStart + 1, nums.size() - 1); + + for (int i = 0; i < nums.size(); i++) { + if (i == bracketStart) { + sb.append("("); + } + + sb.append(nums.get(i)); + + if (i == bracketEnd) { + sb.append(")"); + } + + if (i < ops.size()) { + sb.append(" ").append(ops.get(i)).append(" "); + } + } + + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/com/mathpaper/generator/QuestionGenerator.class b/src/com/mathpaper/generator/QuestionGenerator.class new file mode 100644 index 0000000000000000000000000000000000000000..1a42a0ee0abbfbe5b53823e85a1aa019eb1360ae GIT binary patch literal 3779 zcmai0`&$&}6@F*A%(4!ME0QWCf{HGnlA2hJs1aAxHC)u)XwW1$ECY<}?re5uji!AP z)7WZFqOsL_Ni1q@NE#v}QAE77Kef-(ze6thseeG9CVjt|Sy+NicphfwyPfl%_q^xK z_pk3p9{~tr)CUi8WaKKyL%xLK!|D;WE~X~B>smVwYf(!={zg5aTbm{11giHHz>5MI zJ_U22NT`k)@w&Kb^(57#ma6O45?V^Nj8xs8v}Rhmk=T)$kx*zEmKtlYEG?e2ObJp* z7%NgRS6EtLrlV2KG`rF<_Y6;)KFot(#(V`OSTF-!*h=Y%ZV9>kjY}vAR5wjScNXi# z>8BL@3=1X5y4gs-$Luf;$S9MrWcolT7Sp;3F4dimYYD6IXjDrI_+C6M!9RBI_QYua zUfA!><&rC25*uREo7YBV%^qm1Pe=Gj2fmL99y zshT}u&GMlN0b#$I5e7nzxmpD)v5G82)2S3uW;iC7a~;dJ2_9$Ub$C|B&lRk}a}t(M zJBS(y_jp~TC#9*KO-59W?Nt+<25FStH3>BtLT#HrcB)>aajk-1U>(JhaxJ;B+NT}s z1m(pG3O3l8bfuzYf)I-v6>I{9HZNhM;%ZEPYigAtJc;Gme9plZG|1ShP}|$6x#_@3 ziv%wNu~a=l%4Vd^(RzLL0dao2f*s;;j@sENBP3z|v{0s$QGl1wB%@hD3tDF?^O#AA z^SG8u(T#&qH|lm%uoji6sc}-V6MGcw6;$}THLID*SGzSoYcitXWkJGIakWB*?91!Yru+22jJSK&+Si%np5ieeoKziGW=~c7V3zfxJA&Mia zpc53PFQp}8YE)~8#U!kM0@JgJ{y*i`t)NE)HODfVj5oDZP&GBd>0t%EqQB%tW15;0 zGjRn87?iikJlj=w&ibWW4`$3!#g-G<6YtQ*E6)R%}95| z7~O)vwrpR{G+oWZ2?f8wZ&^59Li3d93s0c`U>44dRI{escIU+@3D1mwa(ry)?AY-K zPQMzv+5dR-jF`OH&q*In<9!()DEJU(GElRMlmsQTqZY|<8V_^w5gXU|g`tW2C$c*r z$!TtQrF}#NT{`?y&)25Y~CFSv+6Qp-6y!tAwz4}p+ha9NT{KyJ=_>+4>s&> z2!cPDal`|9)^qa`sJ?@8qLLW2c5(<=M46 zK5(5v3aov$dTu@jeHg-5GQJl5?i&fqrkkvuIAZi_bqvxeKW#=MH4?D6IVl|A$dLjMB>} zypKg~{_?wc=5IA(@(wC%?xU)$c*|X^xPzLS5v;z0=fAR@T;_8*3izy~=|y}iLlKq` z_EOF-$7-xVJp$N@YFy#2VtBsiqDO{TW}`$wt|aabT#@iGe;t_LA&1t0ccrGBP36UF zZXjASjP*@5ZMPxk9wKZaUgsjdf>i=%eM`-4)U3LP7d`Z}$Q?9#uwR@i6sHPnhx#$U zPCINzIgn(U{Dj*alGpPfuMj8^RNaHl>*16jL~ z;L6phR6Wg+u#zWu6KI@c!U_20l<+6}fDqpjj>N3bamZWAzz_Dn&GkN9<5TQ!B|z~l zshlCtyqp0$-Oa+vN;}=cj6;Hh_9T&>qSW3;DL$}+r|OG@b6x;ktVe2`6Qho?eTMn&MhTC$5u@R)g zZGxOa`z@b3K3alb^^V}}@Ce?UX3T-!M}n`hz_0VJ-$#fy@MjnBej*WURF#$AbZhja zsL`_WYsf1bpoizn%Q|zZi{F(Unr5Vw)en5Z=Kv)!$UZSdR=&xw!k(RroA?X)6O@PO zLWpfKDfiYsy5c%ID;(7d7qyRQf6cV&9DW{ currentQuestions; // 本次生成的题目(防重复) + protected final Random random; + + // 性能统计 + private int totalAttempts = 0; + private int successfulGenerations = 0; + + /** + * 构造函数 + * @param grade 年级 + */ + public QuestionGenerator(String grade) { + if (grade == null || grade.trim().isEmpty()) { + throw new IllegalArgumentException("年级不能为空"); + } + + this.grade = grade.trim(); + this.currentQuestions = new HashSet<>(INITIAL_SET_CAPACITY); + this.random = ThreadLocalRandom.current(); // 使用线程安全的随机数生成器 + } + + /** + * 抽象方法:生成一道题目 + * 子类必须实现此方法 + * @return 生成的题目字符串 + */ + public abstract String generateQuestion(); + + /** + * 生成不重复的题目(优化版本) + * @return 生成的题目字符串,如果无法生成返回null + */ + public String generateUniqueQuestion() { + String question = null; + int attempts = 0; + + while (attempts < MAX_GENERATION_ATTEMPTS) { + try { + question = generateQuestion(); + totalAttempts++; + + if (question != null && !question.trim().isEmpty()) { + String normalizedQuestion = normalizeQuestion(question); + + if (!currentQuestions.contains(normalizedQuestion)) { + currentQuestions.add(normalizedQuestion); + successfulGenerations++; + return question; + } + } + + } catch (Exception e) { + System.err.println("生成题目时发生错误: " + e.getMessage()); + } + + attempts++; + } + + // 如果达到最大尝试次数仍未成功,记录警告 + if (attempts >= MAX_GENERATION_ATTEMPTS) { + System.err.println("警告:" + grade + "题目生成器达到最大尝试次数(" + MAX_GENERATION_ATTEMPTS + ")"); + } + + return question; // 返回最后一次生成的题目,即使可能重复 + } + + /** + * 标准化题目格式,用于重复检查 + * @param question 原始题目 + * @return 标准化后的题目 + */ + protected String normalizeQuestion(String question) { + if (question == null) { + return ""; + } + + return question.trim() + .replaceAll("\\s+", " ") // 统一空格 + .toLowerCase(); // 转小写(如果需要) + } + + /** + * 清空当前题目集合 + */ + public void clearCurrentQuestions() { + currentQuestions.clear(); + // 重置统计信息 + totalAttempts = 0; + successfulGenerations = 0; + } + + /** + * 获取年级 + * @return 年级字符串 + */ + public String getGrade() { + return grade; + } + + /** + * 获取当前已生成的题目数量 + * @return 题目数量 + */ + public int getCurrentQuestionCount() { + return currentQuestions.size(); + } + + /** + * 检查题目是否已存在 + * @param question 要检查的题目 + * @return 如果题目已存在返回true + */ + public boolean isQuestionExists(String question) { + if (question == null || question.trim().isEmpty()) { + return false; + } + + String normalized = normalizeQuestion(question); + return currentQuestions.contains(normalized); + } + + /** + * 获取生成效率统计 + * @return 成功率(0.0-1.0) + */ + public double getGenerationEfficiency() { + if (totalAttempts == 0) { + return 0.0; + } + return (double) successfulGenerations / totalAttempts; + } + + /** + * 获取统计信息字符串 + * @return 统计信息 + */ + public String getStatistics() { + return String.format("生成器统计[%s]: 总尝试=%d, 成功=%d, 效率=%.2f%%", + grade, totalAttempts, successfulGenerations, + getGenerationEfficiency() * 100); + } + + /** + * 生成指定范围内的随机整数(包含边界) + * @param min 最小值 + * @param max 最大值 + * @return 随机整数 + */ + protected int randomInt(int min, int max) { + if (min > max) { + throw new IllegalArgumentException("最小值不能大于最大值"); + } + + if (min == max) { + return min; + } + + return random.nextInt(max - min + 1) + min; + } + + /** + * 从数组中随机选择一个元素 + * @param array 数组 + * @return 随机选择的元素 + */ + protected T randomChoice(T[] array) { + if (array == null || array.length == 0) { + throw new IllegalArgumentException("数组不能为空"); + } + + return array[random.nextInt(array.length)]; + } +} \ No newline at end of file diff --git a/src/com/mathpaper/generator/SeniorGenerator.class b/src/com/mathpaper/generator/SeniorGenerator.class new file mode 100644 index 0000000000000000000000000000000000000000..66e8b4e6cdf0d18b8bd0fa72cb46512800ad23e9 GIT binary patch literal 4500 zcma)9YjjlA75+}5Aw*FEWF`qw0#lptN+v-@^1w_WCa5q>F3G@TCQfcp zEPdH%ebm-!TM1glXiZzu;%fp}snyzt^-*iW zoU_k4`|<5>?{j$l?Z#J$dBsC!g}b36}!51_i2sKb9J*8;Y8P!_i?Q zT{mDPjdavZrR%O4HAc*MDtXnkgc{bzlW}vSfL_(MKYB1)mxv|@>LO-3o*ZcO?^NIs zSoLZ8B1STvO3!HXARn5BLKF$)r=!XK)KEBS3b?AmVSiYzmS|W6uRw9uC!=F@Xs?kT z5xA%-?6(~lHRFl8w)lwIC{L7X(B+9m(RT}&V2o%lQl_muo3~yeB zI`{53VrHYiFP|{fXsE?%g5J5kwX3PSvnxX9cYQLS60*xRT!A$Lnw_A~U@9InnB%J4 z{nOfWRAfa8)(Vu&25B=zK~SJ-ygAwJvN%W{ZADnY)f%>; zZQ6S)t@Z{1Ptv&AY)OsoP1w%0xHtWIT>d#KZEV+YjfA(TyDNOv?kzhyLfzrcj);5H zyYRlG&NDoD^J@tIUqC(Vtr$S(!xQ z9@z`NWueMekdJ#cdl2pqIV= z0S!kaeq}?^14by7j780!xH-tt2qQ7s2kUWE!-IH;nKn~0c&l46(klP1kcx+ywOSR& z_!Ceu!Jj%6kJIH5ncOEhAT}y~!~qquGqi_0c3YjKqlJw`garzxtyy*FpjW(nwDzWJ zXZA^%i-jc5cI;?xZtap1Atl^3EAg=YtTNi()!E$C9BvDDllh{B{OD+nbhozbp0iNa z)UvawBSdlM_PHw;HFq_IwzYQ8l{!PIewJ`?TRfS)Te_oGQI|~LAJcargNm4>VS98q zyCpvoA4o>c(KHoQ%@p3uwXr^@c&8ip#zx7OPuyI&TeInvJFn03>VK%rX<)MbFQ{yR zT2ruE!7D85T_Kjn`dA`+v3nw^(R9q%5|_QLbl#P{TJ~(Fgx$d$G1JlEcEcP@^^*?2 zp(asP?8fUJ{0?s@cvJH5ErH6}HXlzOOdT+?uLYZ$TcR;u&W8k!r=D?c8Dn~NsVE3V6V}b3tmASyhl~oanCK8djX)q63ILQ}LC=neQF-8=; zBe3EVF)ie(f_DWj{_L$RT{|vZ%5LCf4agV6kMR=-JSpo2$2os0XZ|$jKh2q+;QX1K z`Lmq!cIVk8@*1*!pTqMr`r0XfA!pvjd1l^xYR+8cSvDbK!hAa7+TSdKm1@bG+$G9$G^1p~Bz2#F_+V0p`Q9gksH34Udhx$+Al2cfbhn_Kq zn7r`M8mT(Za&R$R9A8eHuRsab5b-Ohd@Ww2R72i*6ew6J&Y~PYv(CK4b9_wB7`DS$ z!0@xE&YQneU>#ull z3XL6(pwkg_If8E8ZObP_VYTi)h4p!O8Ot)*98`4YILdUFYXW-Ud4w{!sub-<9)jYW z1W&8FI*x(Bv*@%VXvm-0)x}+_15TN&peW{h$B%179kob;)kC|$N>dc%lV#T5bwaR^&Y@m7HjDGvCKS}`XQ8evbAN=& zvt+By!=IA~d=lB^lvhYP6q63itG$Oa`0`Qc)!tjJfmZDu<6u0S+YL*{kzb12C*dwd z*VS44{SQ0{SM`Z%iPRkpE}!d+Iw1udDE&cqz@fWk!WCU{?;$%Bbq`mZ{Z>i>x+?QQ zI!G!`;hPNwx)RiMP4_&8<-P)+OV`%OJW9y~3Va3Gd}yr1_k9J-M}rpdX&HQbT-vk< zox%Oe9l#co#E&H|lT?8kyq_e1)~*KD6O}a^nFq;Ry3Tj(!|P zns24Uco?^l1NY(>XOFVoK7tc?3{!Z5l{Sf2D0zpi`hB+Qzu{^8n=Sc2c#dp%Ubrwt zHk=e?I4&xfQptv`C@E*s&+)&@&5m;@;=d9FFJC|dG>akR1m`4(AS>39CdOehiNV&N);Pj|&LM&dsu_x1{`)p#8!wt}-_C z{14&!5Gub=@#@S?)?r;Fz#8Qx)b~2P*GGDt-XBDIUEW6`y>9QLk>^nDwwE50OFxuL olazMOzw}CZXXw1fIQl&!`6GP)1vxlJ`Tzg` literal 0 HcmV?d00001 diff --git a/src/com/mathpaper/generator/SeniorGenerator.java b/src/com/mathpaper/generator/SeniorGenerator.java new file mode 100644 index 0000000..83e199c --- /dev/null +++ b/src/com/mathpaper/generator/SeniorGenerator.java @@ -0,0 +1,216 @@ +package com.mathpaper.generator; + +import java.util.ArrayList; +import java.util.List; + +/** + * 高中题目生成器 + * 生成2-5个操作数的题目,至少含1个sin/cos/tan(弧度制),支持函数嵌套 + * + * @author 系统 + * @version 2.0 + */ +public class SeniorGenerator extends QuestionGenerator { + + // 常量定义 + private static final int MIN_OPERANDS = 2; + private static final int MAX_OPERANDS = 5; + private static final int MIN_NUMBER = 1; + private static final int MAX_NUMBER = 10; // 避免过大的弧度值 + private static final String[] OPERATORS = {"+", "-", "*", "/"}; + private static final String[] TRIG_FUNCTIONS = {"sin", "cos", "tan"}; + private static final double TRIG_PROBABILITY = 0.4; // 40%概率添加三角函数 + private static final double NESTED_PROBABILITY = 0.3; // 30%概率函数嵌套 + private static final double ADVANCED_OP_PROBABILITY = 0.3; // 30%概率添加高级运算 + private static final double BRACKET_PROBABILITY = 0.3; // 30%概率添加括号 + + /** + * 构造函数 + */ + public SeniorGenerator() { + super("高中"); + } + + @Override + public String generateQuestion() { + int numCount = randomInt(MIN_OPERANDS, MAX_OPERANDS); + List nums = generateNumbers(numCount); + List ops = generateOperators(numCount - 1); + return addTrigonometricFunctions(nums, ops); + } + + /** + * 生成指定数量的随机数(1-10,避免过大的弧度值) + * @param count 数字数量 + * @return 数字列表 + */ + private List generateNumbers(int count) { + List nums = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + nums.add(randomInt(MIN_NUMBER, MAX_NUMBER)); + } + return nums; + } + + /** + * 生成指定数量的运算符 + * @param count 运算符数量 + * @return 运算符列表 + */ + private List generateOperators(int count) { + List ops = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + ops.add(randomChoice(OPERATORS)); + } + return ops; + } + + /** + * 添加三角函数和函数嵌套 + * @param nums 数字列表 + * @param ops 运算符列表 + * @return 包含三角函数的表达式字符串 + */ + private String addTrigonometricFunctions(List nums, List ops) { + StringBuilder sb = new StringBuilder(nums.size() * 15); // 预估容量 + + // 确保至少有一个三角函数 + boolean hasTrig = false; + int trigCount = 0; + + for (int i = 0; i < nums.size(); i++) { + int num = nums.get(i); + + // 决定是否添加三角函数 + boolean shouldAddTrig = !hasTrig || + (trigCount < 2 && random.nextDouble() < TRIG_PROBABILITY); + + if (shouldAddTrig) { + String func = randomChoice(TRIG_FUNCTIONS); + + // 决定是否嵌套函数 + if (random.nextDouble() < NESTED_PROBABILITY) { + // 函数嵌套,如 sin(√(5+3)) + int innerNum = randomInt(1, 5); + sb.append(func).append("(√(").append(num) + .append("+").append(innerNum).append("))"); + } else { + // 简单函数,如 sin(3) + sb.append(func).append("(").append(num).append(")"); + } + hasTrig = true; + trigCount++; + } else { + // 添加其他高级运算或普通数字 + if (random.nextDouble() < ADVANCED_OP_PROBABILITY) { + if (random.nextBoolean()) { + sb.append(num).append("^2"); + } else { + // 限制开根号的数值避免过于复杂 + int sqrtNum = Math.min(num, 9); + sb.append("√").append(sqrtNum); + } + } else { + sb.append(num); + } + } + + if (i < ops.size()) { + sb.append(" ").append(ops.get(i)).append(" "); + } + } + + // 添加复杂括号 + return addComplexBrackets(sb.toString()); + } + + /** + * 添加复杂括号结构 + * @param expression 原始表达式 + * @return 带括号的表达式 + */ + private String addComplexBrackets(String expression) { + // 如果表达式中已有函数调用的括号,减少额外括号的概率 + boolean hasFunctionBrackets = expression.contains("sin(") || + expression.contains("cos(") || + expression.contains("tan("); + + double actualBracketProb = hasFunctionBrackets ? + BRACKET_PROBABILITY * 0.5 : BRACKET_PROBABILITY; + + if (random.nextDouble() >= actualBracketProb) { + return expression; + } + + String[] parts = expression.split(" "); + if (parts.length < 5) { + return expression; + } + + // 选择括号范围,避免破坏函数调用结构 + int maxStart = parts.length - 4; + int start = randomInt(0, maxStart); + int minEnd = start + 3; + int maxEnd = Math.min(start + 5, parts.length - 1); + int end = randomInt(minEnd, maxEnd); + + // 检查是否会破坏函数调用结构 + StringBuilder testBuilder = new StringBuilder(); + for (int i = start; i <= end; i++) { + testBuilder.append(parts[i]); + if (i < end) testBuilder.append(" "); + } + String testSection = testBuilder.toString(); + + // 如果选中的部分包含不完整的函数调用,跳过添加括号 + if (containsIncompleteFunction(testSection)) { + return expression; + } + + StringBuilder result = new StringBuilder(expression.length() + 4); + + for (int i = 0; i < parts.length; i++) { + if (i == start) { + result.append("("); + } + + result.append(parts[i]); + + if (i == end) { + result.append(")"); + } + + if (i < parts.length - 1) { + result.append(" "); + } + } + + return result.toString(); + } + + /** + * 检查字符串是否包含不完整的函数调用 + * @param text 要检查的文本 + * @return 如果包含不完整的函数调用返回true + */ + private boolean containsIncompleteFunction(String text) { + for (String func : TRIG_FUNCTIONS) { + if (text.contains(func + "(") && !text.contains(")")) { + return true; + } + if (text.contains(")") && !text.contains(func + "(")) { + // 检查是否有孤立的右括号 + int leftCount = 0; + int rightCount = 0; + for (char c : text.toCharArray()) { + if (c == '(') leftCount++; + if (c == ')') rightCount++; + } + if (rightCount > leftCount) { + return true; + } + } + } + return false; + } +} \ No newline at end of file diff --git a/src/com/mathpaper/model/User.class b/src/com/mathpaper/model/User.class new file mode 100644 index 0000000000000000000000000000000000000000..af09c72a38400e3f2538c6b00db8c84f15ace466 GIT binary patch literal 2560 zcmai0>r)d~6#v~2vLP%WQB)|RSX2ncP<*tCiioJRfYcx=KIjszNo2Ddla19n&a~5U zWTw(ur#|YZ+G({99O(-gtfimqO#h9Npr85=IGvum85yc2al_-z-BKja#eTU*a#!wjNhL~wegfo=Yc4RgCjaXO+R&!rDt~@rEWcF^foq3KORZmznOY4^4I$xq^^(A zoF>x~A0>yc%}h?EhHp$y+)v%OO#3sVcU>q$g@~x^LM2v6sFJY~tLB8)3Ic*Ogc;<5ko!Na1)}Bx;;DdFT^**UDIjS|Uj0TF?lY2kj!H9vSsmPiTkyZ3nwr zI@|iYTY7qE%1~~BB#f}?i}4^o=j&DsgIH>EVFP>;8f3hNMuuf(+~&)XA;fneBq-jP zOKNLKiTU}N1lIz*DeX(n^-k*&!EC~230q{mF1(rtla|ZC#GTkm>62k+dSc}1hmWTx zCTG5#bfSr2W#;JF^{M3KT}vQ2_UZKG$Bo3A6FV4IEhzMK{L<5@%O`U} zM<~fFmy%b%e+li$Ygckfmax9_@@m5_6D8}Ki5u#b#P&6aXur@bpv!$>Q z_cpZ2*o%Es8$oWgs!E)0FP@Vp<|f6o674eH#9NdMM1i7`;GBxHYPqS-%$rUbT_SIs zAtg?R4RR;C8LCenQ@roBc>5Z>Tl$(v)VoJ1=8zFs=vh);#TS#0;r2E*8b!^?m z5(h#qgmGHJkjM;`p(fjr!rEDVi2E#LD_Qm`0Yi^bmEX#ZYg*6(+plPYDv#H6==xA1 z+B83Pa(3oi>^UFh3y2@fk)KVJoUOEqRz+3)VS`i2$?clPV`lF0xCEVH%{-qLGF8GE zhN_o~QU+Uop_)3GjhMEhLbNlQRp1bM!Ejil29D7BXwJHi_UI*{vjUQ#-b>$OwDQot z(Btwv1oPC7L;8VM49DqP43SHfgt3BdRnhJVtV-WoK_jEne9zA)8p~X9n44?pOD`aO z1;jp5BOog3Cs2ADMUPN+)V=H>%BKY0E5D#fK-x@5H-UjFNrDfh)N9Mph?BIF2Ubcp z&d%p%H!9{Gq<2mJ9mrx_wB#$X|0C*X^aILR5C*Er@sW2OCxFY<~isN_Zwqb_%GpbZyTGn zz~SCLmNnrvo$VZBB5s!79=K?JfGp`Izx`NBWL6P<)kLBPeehD~w~r%?6;rD9l5zj5rphY~US1Csq7?0MOsS`-cug({XqqukL=lZ)YyL^R7K zI95>R iWfR%DSwemekf03#4ASVN3qksEL}=xrnT9B0DE}8#Ri%;u literal 0 HcmV?d00001 diff --git a/src/com/mathpaper/model/User.java b/src/com/mathpaper/model/User.java new file mode 100644 index 0000000..52de919 --- /dev/null +++ b/src/com/mathpaper/model/User.java @@ -0,0 +1,118 @@ +package com.mathpaper.model; + +import java.util.regex.Pattern; + +/** + * 用户实体类 + * 表示系统中的用户信息 + * + * @author 系统 + * @version 2.0 + */ +public class User { + + // 用户名验证正则表达式:允许字母、数字、下划线和中文字符,长度2-20 + private static final Pattern USERNAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_\\u4e00-\\u9fa5]{2,20}$"); + + // 用户名长度限制 + private static final int MIN_USERNAME_LENGTH = 2; + private static final int MAX_USERNAME_LENGTH = 20; + + private String username; + + /** + * 构造函数 + * @param username 用户名 + * @throws IllegalArgumentException 如果用户名无效 + */ + public User(String username) { + setUsername(username); + } + + /** + * 获取用户名 + * @return 用户名 + */ + public String getUsername() { + return username; + } + + /** + * 设置用户名(带验证) + * @param username 用户名 + * @throws IllegalArgumentException 如果用户名无效 + */ + public void setUsername(String username) { + if (!isValidUsername(username)) { + throw new IllegalArgumentException("用户名无效:必须是2-20位字母、数字、下划线或中文字符"); + } + this.username = username.trim(); + } + + /** + * 验证用户名是否有效 + * @param username 要验证的用户名 + * @return 如果用户名有效返回true,否则返回false + */ + public static boolean isValidUsername(String username) { + if (username == null || username.trim().isEmpty()) { + return false; + } + + String trimmed = username.trim(); + + // 检查长度 + if (trimmed.length() < MIN_USERNAME_LENGTH || trimmed.length() > MAX_USERNAME_LENGTH) { + return false; + } + + // 检查格式 + return USERNAME_PATTERN.matcher(trimmed).matches(); + } + + /** + * 获取用户名验证错误信息 + * @param username 要验证的用户名 + * @return 错误信息,如果用户名有效则返回null + */ + public static String getValidationError(String username) { + if (username == null || username.trim().isEmpty()) { + return "用户名不能为空"; + } + + String trimmed = username.trim(); + + if (trimmed.length() < MIN_USERNAME_LENGTH) { + return "用户名长度不能少于" + MIN_USERNAME_LENGTH + "位"; + } + + if (trimmed.length() > MAX_USERNAME_LENGTH) { + return "用户名长度不能超过" + MAX_USERNAME_LENGTH + "位"; + } + + if (!USERNAME_PATTERN.matcher(trimmed).matches()) { + return "用户名只能包含字母、数字、下划线和中文字符"; + } + + return null; // 验证通过 + } + + @Override + public String toString() { + return "User{username='" + username + "'}"; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + + User user = (User) obj; + return username != null ? username.equals(user.username) : user.username == null; + } + + @Override + public int hashCode() { + return username != null ? username.hashCode() : 0; + } +} \ No newline at end of file diff --git a/src/com/mathpaper/util/DuplicateChecker.class b/src/com/mathpaper/util/DuplicateChecker.class new file mode 100644 index 0000000000000000000000000000000000000000..e04e51cc29db8714266dc8080e0e97f7e7c6d4ac GIT binary patch literal 8835 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 E8KF`fLjV8( literal 0 HcmV?d00001 diff --git a/src/com/mathpaper/util/DuplicateChecker.java b/src/com/mathpaper/util/DuplicateChecker.java new file mode 100644 index 0000000..8531601 --- /dev/null +++ b/src/com/mathpaper/util/DuplicateChecker.java @@ -0,0 +1,281 @@ +package com.mathpaper.util; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 查重工具类 + * 负责检查题目是否在历史文件中重复 + * + * @author 系统 + * @version 2.0 + */ +public class DuplicateChecker { + + // 缓存用户的历史题目,提高查重性能 + private static final ConcurrentHashMap> userQuestionCache = new ConcurrentHashMap<>(); + + // 缓存的最大用户数,防止内存溢出 + private static final int MAX_CACHED_USERS = 10; + + // 缓存访问时间记录,用于LRU清理 + private static final ConcurrentHashMap cacheAccessTime = new ConcurrentHashMap<>(); + + // 缓存清理阈值(毫秒) + private static final long CACHE_EXPIRY_TIME = 30 * 60 * 1000; // 30分钟 + + /** + * 检查题目是否在历史文件中存在 + * @param question 要检查的题目 + * @param username 用户名 + * @return 如果题目重复返回true,否则返回false + */ + public static boolean isDuplicate(String question, String username) { + if (question == null || question.trim().isEmpty()) { + return false; + } + + if (username == null || username.trim().isEmpty()) { + return false; + } + + try { + String cleanQuestion = cleanQuestionText(question); + Set userQuestions = getUserQuestions(username); + return userQuestions.contains(cleanQuestion); + + } catch (Exception e) { + System.err.println("查重检查时发生错误: " + e.getMessage()); + return false; // 出错时默认不重复,避免阻塞题目生成 + } + } + + /** + * 获取用户的所有历史题目(带缓存) + */ + private static Set getUserQuestions(String username) { + // 检查缓存 + Set cachedQuestions = userQuestionCache.get(username); + if (cachedQuestions != null) { + // 更新访问时间 + cacheAccessTime.put(username, System.currentTimeMillis()); + return cachedQuestions; + } + + // 缓存未命中,从文件加载 + Set questions = loadUserQuestionsFromFiles(username); + + // 缓存管理:如果缓存已满,清理最久未使用的条目 + if (userQuestionCache.size() >= MAX_CACHED_USERS) { + cleanupOldestCache(); + } + + // 添加到缓存 + userQuestionCache.put(username, questions); + cacheAccessTime.put(username, System.currentTimeMillis()); + + return questions; + } + + /** + * 从文件加载用户的历史题目 + */ + private static Set loadUserQuestionsFromFiles(String username) { + Set questions = new HashSet<>(); + + try { + String userDir = FileUtil.getUserPaperDir(username); + Path userPath = Paths.get(userDir); + + if (!Files.exists(userPath)) { + return questions; + } + + // 使用NIO遍历用户目录下的所有文件 + Files.walk(userPath) + .filter(Files::isRegularFile) + .filter(path -> path.toString().endsWith(".txt")) + .forEach(path -> { + try { + Files.lines(path, StandardCharsets.UTF_8) + .map(String::trim) + .filter(line -> !line.isEmpty()) + .map(DuplicateChecker::cleanQuestionText) + .forEach(questions::add); + } catch (IOException e) { + System.err.println("读取文件失败: " + path + " - " + e.getMessage()); + } + }); + + } catch (Exception e) { + System.err.println("加载用户题目失败: " + e.getMessage()); + } + + return questions; + } + + /** + * 清理最久未使用的缓存条目 + */ + private static void cleanupOldestCache() { + if (cacheAccessTime.isEmpty()) { + return; + } + + // 找到最久未访问的用户 + String oldestUser = cacheAccessTime.entrySet().stream() + .min((e1, e2) -> Long.compare(e1.getValue(), e2.getValue())) + .map(entry -> entry.getKey()) + .orElse(null); + + if (oldestUser != null) { + userQuestionCache.remove(oldestUser); + cacheAccessTime.remove(oldestUser); + } + } + + /** + * 定期清理过期缓存 + */ + public static void cleanupExpiredCache() { + long currentTime = System.currentTimeMillis(); + + cacheAccessTime.entrySet().removeIf(entry -> { + if (currentTime - entry.getValue() > CACHE_EXPIRY_TIME) { + userQuestionCache.remove(entry.getKey()); + return true; + } + return false; + }); + } + + /** + * 从单个文件加载题目 + */ + private static void loadQuestionsFromFile(Path filePath, Set questions) { + try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)) { + String line; + while ((line = reader.readLine()) != null) { + String cleanLine = cleanQuestionText(line); + if (!cleanLine.isEmpty()) { + questions.add(cleanLine); + } + } + } catch (IOException e) { + System.err.println("读取文件时发生错误: " + filePath + " - " + e.getMessage()); + } + } + + /** + * 清理题目文本,统一格式用于比较 + * @param text 原始文本 + * @return 清理后的题目内容 + */ + private static String cleanQuestionText(String text) { + if (text == null || text.trim().isEmpty()) { + return ""; + } + + String cleaned = text.trim(); + + // 去除题号(如"1. "、"2) "等格式) + cleaned = cleaned.replaceFirst("^\\d+[.)、]\\s*", ""); + + // 去除多余的空白字符 + cleaned = cleaned.replaceAll("\\s+", " ").trim(); + + return cleaned; + } + + /** + * 批量检查题目列表是否有重复 + * @param questions 题目列表 + * @param username 用户名 + * @return 重复的题目数量 + */ + public static int countDuplicates(String[] questions, String username) { + if (questions == null || questions.length == 0) { + return 0; + } + + if (username == null || username.trim().isEmpty()) { + return 0; + } + + int duplicateCount = 0; + try { + for (String question : questions) { + if (isDuplicate(question, username)) { + duplicateCount++; + } + } + } catch (Exception e) { + System.err.println("批量查重时发生错误: " + e.getMessage()); + } + + return duplicateCount; + } + + /** + * 获取用户历史试卷文件数量 + * @param username 用户名 + * @return 历史试卷文件数量 + */ + public static int getHistoryFileCount(String username) { + if (username == null || username.trim().isEmpty()) { + return 0; + } + + try { + String userDir = FileUtil.getUserPaperDir(username); + Path dirPath = Paths.get(userDir); + + if (!Files.exists(dirPath) || !Files.isDirectory(dirPath)) { + return 0; + } + + return (int) Files.list(dirPath) + .filter(path -> Files.isRegularFile(path)) + .filter(path -> path.toString().endsWith(".txt")) + .count(); + + } catch (IOException e) { + System.err.println("获取历史文件数量时发生错误: " + e.getMessage()); + return 0; + } + } + + /** + * 清理指定用户的缓存 + * @param username 用户名 + */ + public static void clearUserCache(String username) { + if (username != null) { + userQuestionCache.remove(username); + } + } + + /** + * 清理所有缓存 + */ + public static void clearAllCache() { + userQuestionCache.clear(); + } + + /** + * 获取当前缓存的用户数量(用于监控) + * @return 缓存的用户数量 + */ + public static int getCachedUserCount() { + return userQuestionCache.size(); + } +} \ No newline at end of file diff --git a/src/com/mathpaper/util/FileUtil.class b/src/com/mathpaper/util/FileUtil.class new file mode 100644 index 0000000000000000000000000000000000000000..0d88b4daa9ddd9d2574150f57320b264d02dc614 GIT binary patch literal 7014 zcma)A3w%`7o&HW{CU-Ji1{e?sFA)Jl9z>`LBq$~!fx#riOb`Qf<0W$wMkX_}GZTci z+lSS~wZ%sZYLFJC*q}v;K$DOZW7}Q3YrB1IyYFt@T>_!q_oMx3)$}{(-kHphp}+DA z=gvKk|M@?@|Mx%VzWw3Q8vts=W)Je<(BRbJLcYNC2aSD3dDw__mT%qtpxK@j$X_3d zgpzj&IEq%>=Ybmq8az6tKo^+b9_=dcGLm~@M$C+tcPB&P^14vi+{Paiw$BKMf=1HZ zmN4THqstVyp{Rahhd?qOigZ>=sa{OeFkQzC__CC+rU*DWwM$@D(Td6IrB1VS+<@6! znlPwr=wqhT!rF3qt}Q1^abqq`3`X1Cm?z*;26?am3pFg#aU&KB%v7pJLecULN-uAs z-3hMgH0h3pbLqcz1WwRQ{|=y?L+{%QP(4aSMtjX(WxN zMl;&uCcVBf6lVaV@sR9mw)M;2+14=Kd7WJ=bE;iHzZdH$K`H&-;%}%8v{W@TwbgBH zZm4PzSYTP7H1{XV1EH>1*xX2^>Y{Ptg(j~Q@G6rPXylLwWpH?~3ad4IM8|EoU0|M? zl2Zne*`b69EVYeGi%Kc1fNaCeN5HSqu~x!lPM5LQtcgb2jpX)Fau0tcl13z%n1TvW zxAi*i1QGAcj)@GofEG6s(eOTkxhO}>b9zdOy<10>1h+dGwG`5rem10tDF4>l{q1Hf z8Hz>

VVZn{;f3KRX)^B$6i6z+uMY%q7dgT-?MwN>Zw6bX9s#jeB&|%e_2=W`mhX z7@ejM0&6_jiY5*B>S&hPYvDP2J?)E0Y2k=Ws}>#Gq`ziTP@NGD?>5@^28?~C^n^^> z?K~}C&VVzgJ7o+=MSeFJf@$g1V zC-!Iv>39%(1$3nZEtHudP>?WVM%-XdFt(GE(?>4sk_d?iY{?PZ$tx!J^@$C|A9dr?0)kt6@vx51;t?VuZpOk!yIB<`Kd=40 zY|LSi8-L6g>`IhK(U0l)6Fe@R+r2xX7+qj?k>8(VorKiqbsWMIoRTUI7M|iP4@oml zrdoDgtyFr~({(HvGNqo<@ie}`or%sSBTi_^opWYj0S@DchG%p6ox1MZR=c_9o;eH%{w#6|b?BKO{H4x7$oe&P%jJtrRV>)tJ24QY8}u9`xaL4QF+n zlV_LNsbtUImF{T|M-!%oucs(aW4ng$W))tUPHy}qmt8&a+?D5F8-48Au@_HWyX*%#ekcjpnc&e%%K5Q868)7t z&t>Jgb!M#eLBmfak^Z&7Ek*teZEgK?azh{fR>$AT!sJrekm3CY9sej}s}ZsFp$KEu zhkw%XbJ^$W2zMt)_WztJ)Hcssi1y$e{Ht`~C4utmx-eH(%kyX-F3Yu7M;|?9g;2x4aWp&UX~ABL8)P>Ahb#yGDX`k!Av^P~&98~I zl`D40<^QeYf8_G1p+vLU*&R0GGJ|AM`9B^1uZl`2A-jAiz#7-M{ZB4^Ploe1JO<_5 zdBqkVJQCbX@u7}!mJoP@W|;Ks@2FLk>$)p<_;@c^;}#AA{{a7$H8hk3gJqjHmvwcO zC0OxRp^ZnN>Z(9(+eUvgl}{bL?lZ#OvO+giHPtp-W8OM{z2vW!+Qxu?Yol9u2*8!e z{bczm)!XXoI4|JeLADfr2e6oG&TOc6^R9tf2uXfPx>?;8eYSU=9yq7h}n;WkRmM0`$0?hI2+|;&E zdkYG3A|tuJJ#60&J%MOMJRBIY1~a)Q8cevwGA6K) z(R7Pjc!D2&?D^5*A)5&mbzOe8fAqEMFnKKoOg+>xNTj$Xg&o{#+(# z?TJ=AHuLbqtK-u8XmoFP%!ZzlH@VF0=`cXdTPFy%6D(Z`!vjx*I>$;Mi%0S6waK^w}pHYe7cGUAYSJWA!e}U zFx%NE5I(ln)N@?sVy{s=2(7puQwK4#-Z#f}5#CmZuQ1T+EC~!^enY9_B5oSQ&D+mn z#W2cR2TfhVkLBxv#_s55wG8O;C5`p8qQqD^%dBK^jzdX4Kn@+**SKs&D{ z(yUvH-@;UBKtJy5!PH^wX!Y$Hz#oiQeGkmQ_({7vD7&RwEIno`2W;e(q84+p3Cno@ zEyotMrwa~m!EA2G>!`h94>yz^+m`ayS#}xF@!hT=?2S&1dSm7+?@|Kwo{zKl zlLXOc7=(vW$b0V+9HfNDDdTgLa*%5ebM+x4=#hu<6nhSH^l6TM0Y~vgyo4jlf7{{y z082C(*@|%Z(AK2F+$QfKhCZ4uNh#s8gq{`^m*1GP|cL#`TLD|;s&ajCnTJC z6}E!Ha9TLYl|SE+iBiJ2lw81H2&B^Ld%5!GdFpeF`SWGw&vE9@i^`OIIK2gWLB`I! zgu$U$>I8RoQll2foeQ|Z_XSR%u4KUl{3s7^!Ycu>is>U0`Y3KGUf_H{z763gGE#qI zFL4G>Usa_TA42Xh{WuVf3{QMBEWfG$OWkh8PedNip3G`jH(dS@}%_oM=<7R$zE#?y>$2DpC zT+ZV)FvM9GId6nBhdBN%&KThh=uM*To1~3zE2C~h{x~N%Qg}&0h#%n7T`BNr??On( zOu(f;6pDE^t$dw9lSkV?7TvlWr&H-%U&0*zPiuYBz+%3kb z8Iyy&NW6c5`D`_G-pw<#n4dP`YqkmZ{!SBAv2&iDC}qF0Lo{IkiQQw_VVkE6xS4#I zDsfhfeolqnp#hg@z$n9TnKP~sh+{PN7yRP;OJ&F`hG%tR|Jbf37@t)e)i9BxsJ+x3PvV(j~(g)|(W^JU+ z!mnS=5oQlz{%$Yw< zN)^K8Wg^Za=~X*3!&*&EZ%oH>pRH*&du73K5FboLQShk11Bv6b3e`eZ)oi6^Q>i(a zF6N_9EWjeM5I52*E4ia}0&#dn1A_y%Se!m>EmsKvc!#m7b*9K05YBU0aqY2-*(YwI zPxHhQaWkJ=_`zX4{k>GR%T-&X+G5p~s&=JnZ&mGT)!wGsJ5;+?wH2b8@;sDPBWlGa G6#f>DwoyX> literal 0 HcmV?d00001 diff --git a/src/com/mathpaper/util/FileUtil.java b/src/com/mathpaper/util/FileUtil.java new file mode 100644 index 0000000..838daa7 --- /dev/null +++ b/src/com/mathpaper/util/FileUtil.java @@ -0,0 +1,290 @@ +package com.mathpaper.util; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +/** + * 文件操作工具类 + * 负责试卷文件的保存和路径处理 + * + * @author 系统 + * @version 2.1 - 优化资源管理和性能 + */ +public class FileUtil { + + // 基础目录常量 + private static final String BASE_DIR = "doc"; + private static final String PAPER_DIR = "paper"; + private static final String FILE_EXTENSION = ".txt"; + + // 性能优化常量 + private static final int BUFFER_SIZE = 8192; // 8KB缓冲区 + private static final int MAX_FILENAME_LENGTH = 255; + + // 时间戳格式 + private static final SimpleDateFormat TIMESTAMP_FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); + + /** + * 生成保存路径:doc/paper/用户名/时间戳.txt + * @param username 用户名 + * @return 文件保存路径 + * @throws IllegalArgumentException 如果用户名无效 + */ + public static String getSavePath(String username) { + validateUsername(username); + + try { + String sanitizedUsername = sanitizeFilename(username.trim()); + Path userDir = Paths.get(BASE_DIR, PAPER_DIR, sanitizedUsername); + + // 确保目录存在 + Files.createDirectories(userDir); + + String timestamp = TIMESTAMP_FORMAT.format(new Date()); + Path filePath = userDir.resolve(timestamp + FILE_EXTENSION); + + return filePath.toString(); + + } catch (IOException e) { + System.err.println("创建保存路径时发生错误: " + e.getMessage()); + // 降级到传统方式 + return getFallbackSavePath(username); + } + } + + /** + * 验证用户名 + */ + private static void validateUsername(String username) { + if (username == null || username.trim().isEmpty()) { + throw new IllegalArgumentException("用户名不能为空"); + } + if (username.trim().length() > MAX_FILENAME_LENGTH) { + throw new IllegalArgumentException("用户名过长"); + } + } + + /** + * 降级保存路径生成方法 + */ + private static String getFallbackSavePath(String username) { + String sanitizedUsername = sanitizeFilename(username.trim()); + String baseDir = BASE_DIR + File.separator + PAPER_DIR + File.separator + sanitizedUsername; + File dir = new File(baseDir); + if (!dir.exists() && !dir.mkdirs()) { + throw new RuntimeException("无法创建目录: " + baseDir); + } + + String timestamp = TIMESTAMP_FORMAT.format(new Date()); + return baseDir + File.separator + timestamp + FILE_EXTENSION; + } + + /** + * 清理文件名中的非法字符 + */ + private static String sanitizeFilename(String filename) { + if (filename == null) { + return "default"; + } + + // 替换Windows文件名中的非法字符 + String sanitized = filename.replaceAll("[<>:\"/\\\\|?*]", "_") + .replaceAll("\\s+", "_") + .trim(); + + // 确保文件名不为空且不超过最大长度 + if (sanitized.isEmpty()) { + sanitized = "default"; + } + if (sanitized.length() > MAX_FILENAME_LENGTH) { + sanitized = sanitized.substring(0, MAX_FILENAME_LENGTH); + } + + return sanitized; + } + + /** + * 保存试卷到文件(带题号和空行) + * @param path 文件路径 + * @param questions 题目列表 + * @throws IllegalArgumentException 如果参数无效 + * @throws RuntimeException 如果保存失败 + */ + public static void savePaper(String path, List questions) { + validateSaveParameters(path, questions); + + try { + Path filePath = Paths.get(path); + + // 确保父目录存在 + Path parentDir = filePath.getParent(); + if (parentDir != null) { + Files.createDirectories(parentDir); + } + + // 使用NIO写入文件,指定UTF-8编码和缓冲区大小 + try (BufferedWriter writer = Files.newBufferedWriter(filePath, StandardCharsets.UTF_8, + StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) { + + writeQuestionsToFile(writer, questions); + } + + } catch (IOException e) { + System.err.println("保存文件时发生错误: " + e.getMessage()); + // 尝试降级保存 + savePaperFallback(path, questions); + } catch (Exception e) { + System.err.println("保存试卷时发生未知错误: " + e.getMessage()); + throw new RuntimeException("保存试卷失败", e); + } + } + + /** + * 验证保存参数 + */ + private static void validateSaveParameters(String path, List questions) { + if (path == null || path.trim().isEmpty()) { + throw new IllegalArgumentException("文件路径不能为空"); + } + if (questions == null || questions.isEmpty()) { + throw new IllegalArgumentException("题目列表不能为空"); + } + } + + /** + * 将题目写入文件 + */ + private static void writeQuestionsToFile(BufferedWriter writer, List questions) throws IOException { + for (int i = 0; i < questions.size(); i++) { + String question = questions.get(i); + if (question != null && !question.trim().isEmpty()) { + writer.write((i + 1) + ". " + question.trim()); + + // 题间空一行,但最后一题不加空行 + if (i < questions.size() - 1) { + writer.newLine(); + writer.newLine(); + } + } + } + writer.flush(); + } + + /** + * 降级保存方法(使用传统IO) + */ + private static void savePaperFallback(String path, List questions) { + try (BufferedWriter bw = new BufferedWriter( + new java.io.FileWriter(path, StandardCharsets.UTF_8), BUFFER_SIZE)) { + + writeQuestionsToFile(bw, questions); + + } catch (IOException e) { + System.err.println("降级保存也失败: " + e.getMessage()); + throw new RuntimeException("文件保存完全失败", e); + } + } + + /** + * 检查文件是否存在 + * @param path 文件路径 + * @return 文件是否存在 + */ + public static boolean fileExists(String path) { + if (path == null || path.trim().isEmpty()) { + return false; + } + + try { + Path filePath = Paths.get(path); + return Files.exists(filePath) && Files.isRegularFile(filePath); + } catch (Exception e) { + // 降级到传统方式 + File file = new File(path); + return file.exists() && file.isFile(); + } + } + + /** + * 创建目录 + * @param dirPath 目录路径 + * @return 是否创建成功 + */ + public static boolean createDirectory(String dirPath) { + if (dirPath == null || dirPath.trim().isEmpty()) { + return false; + } + + try { + Path path = Paths.get(dirPath); + Files.createDirectories(path); + return true; + } catch (IOException e) { + System.err.println("创建目录失败: " + e.getMessage()); + // 降级到传统方式 + File dir = new File(dirPath); + return dir.mkdirs() || dir.exists(); + } + } + + /** + * 获取用户试卷目录 + * @param username 用户名 + * @return 用户试卷目录路径 + */ + public static String getUserPaperDir(String username) { + if (username == null || username.trim().isEmpty()) { + throw new IllegalArgumentException("用户名不能为空"); + } + + String sanitizedUsername = sanitizeFilename(username.trim()); + return BASE_DIR + File.separator + PAPER_DIR + File.separator + sanitizedUsername; + } + + /** + * 获取文件大小(字节) + * @param path 文件路径 + * @return 文件大小,如果文件不存在返回-1 + */ + public static long getFileSize(String path) { + if (path == null || path.trim().isEmpty()) { + return -1; + } + + try { + Path filePath = Paths.get(path); + return Files.exists(filePath) ? Files.size(filePath) : -1; + } catch (IOException e) { + System.err.println("获取文件大小失败: " + e.getMessage()); + return -1; + } + } + + /** + * 删除文件 + * @param path 文件路径 + * @return 是否删除成功 + */ + public static boolean deleteFile(String path) { + if (path == null || path.trim().isEmpty()) { + return false; + } + + try { + Path filePath = Paths.get(path); + return Files.deleteIfExists(filePath); + } catch (IOException e) { + System.err.println("删除文件失败: " + e.getMessage()); + return false; + } + } +} \ No newline at end of file diff --git a/start.bat b/start.bat new file mode 100644 index 0000000..4d8ac97 --- /dev/null +++ b/start.bat @@ -0,0 +1,6 @@ +@echo off +REM Convenience startup script - redirects to the main startup script +REM 便捷启动脚本 - 重定向到主启动脚本 + +echo Redirecting to main startup script... +call "doc\bin\start.bat" \ No newline at end of file diff --git a/start.ps1 b/start.ps1 new file mode 100644 index 0000000..1d51490 --- /dev/null +++ b/start.ps1 @@ -0,0 +1,5 @@ +# Convenience startup script - redirects to the main startup script +# 便捷启动脚本 - 重定向到主启动脚本 + +Write-Host "Redirecting to main startup script..." -ForegroundColor Yellow +& ".\doc\bin\start.ps1" \ No newline at end of file -- 2.34.1