pull/1/head
Violet 7 months ago
parent 199e016c63
commit db4e07b840

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: src.Main
Class-Path: .

@ -0,0 +1,19 @@
@echo off
REM Math Question Generator System - Windows Batch File
REM Author: 赵俊杰
REM Created: 2025
@echo off
REM Set console encoding to GBK to match scanner input
REM GBK code page is 936, which supports Chinese characters
chcp 936 >nul
echo Starting Math Question Generator System...
echo.
REM Execute Java program
java -jar MathQuestionGenerator.jar
REM Pause after program execution to prevent window from closing immediately
pause

Binary file not shown.

@ -17,6 +17,8 @@ Personal_Project/
│ └── SeniorTea.java # 高中教师类
├── doc/ # 文档目录
│ └── 项目说明.md # 项目说明文档
├── MANIFEST.MF # JAVA JAR清单文件
├── 运行程序.bat # Windows批处理文件
├── 张三1/ # 教师个人文件夹(自动生成)
├── 张三2/
├── 张三3/
@ -72,18 +74,20 @@ Personal_Project/
### 文件操作
- **NIO API**使用Java NIO进行高效的文件读写操作
- **异常处理**:完善的异常处理机制确保程序稳定性
- **路径管理**:自动创建和管理教师个人文件夹
- **路径管理**:自动创建和管理教师个人文件夹以及题目文档
## 使用流程
1. **启动程序**运行Main.java
2. **用户登录**:根据提示输入预置的用户名和密码(格式:用户名 密码)
3. **选择模式**:系统显示当前出题类型,可选择切换
4. **切换类型**:输入" 切换** "切换至对应出题类型
5. **生成题目**输入题目数量10-30
6. **查看结果**:题目生成后显示保存路径
7. **重复使用**:题目生成后可选择继续生成或切换难度
8. **退出登录**:输入题目数量为-1则退出当前用户可重新登录
1. **保存文件**:将项目保存至同一目录
2. **启动程序**:在当前目录下双击运行 “运行程序.bat”
3. **用户登录**:根据提示输入预置的用户名和密码(格式:用户名 密码)
4. **选择模式**:系统显示当前出题类型,可选择切换
5. **切换类型**:输入" 切换** "切换至对应出题类型
6. **生成题目**输入题目数量10-30
7. **查看结果**:题目生成后显示保存路径
8. **重复使用**:题目生成后可选择继续生成或切换难度
9. **退出登录**:输入题目数量为-1则退出当前用户可重新登录
10. **退出系统**:可选返回登陆或者退出系统
## 预置用户信息
@ -99,9 +103,183 @@ Personal_Project/
| 王五2 | 123 | 高中教师 | 高中 |
| 王五3 | 123 | 高中教师 | 高中 |
## API文档
### Teacher 抽象基类
#### 构造函数
```java
public Teacher(String name, String password, String path)
```
- **参数**
- `name`: 教师用户名
- `password`: 教师密码
- `path`: 教师文件夹路径可为null自动创建
#### 公共方法
```java
public boolean checkDuplicate(String question)
```
- **功能**: 检查题目是否与历史题目重复
- **参数**: `question` - 待检查的题目字符串
- **返回**: `true` - 存在重复,`false` - 无重复
```java
public String generatePath()
```
- **功能**: 生成题目文件的相对存储路径
- **返回**: 包含时间戳的文件路径
```java
public String generateEX(int num)
```
- **功能**: 生成指定数量的题目并保存到文件
- **参数**: `num` - 题目数量10-30
- **返回**: 保存题目的文件路径
#### 抽象方法
```java
protected abstract String generateSingleQuestion()
```
- **功能**: 生成单个题目(由子类实现)
- **返回**: 题目字符串
```java
public abstract String getType()
```
- **功能**: 返回教师类型
- **返回**: "小学"、"初中"或"高中"
### SysFunc 系统功能类
#### 静态字段
```java
static final int LEAST_NUM = 10 // 最小题目数量
static final int MAX_NUM = 30 // 最大题目数量
static ArrayList<Teacher> teachers // 用户列表
static Teacher teacher // 当前用户
static HashMap<String, Integer> typeMap // 类型映射
```
#### 静态方法
```java
public static void init()
```
- **功能**: 初始化系统,预加载用户数据
```java
public static Teacher login(Scanner scanner)
```
- **功能**: 用户登录界面
- **参数**: `scanner` - 输入扫描器
- **返回**: 登录成功的教师对象
```java
public static void ShiftType(Scanner scanner)
```
- **功能**: 切换出题类型界面
- **参数**: `scanner` - 输入扫描器
```java
public static void Operate(Scanner scanner)
```
- **功能**: 主操作界面,处理题目生成流程
- **参数**: `scanner` - 输入扫描器
### PrimaryTea 小学教师类
#### 构造函数
```java
public PrimaryTea(String name, String password, String path)
```
#### 特有方法
```java
public void getRandom()
```
- **功能**: 生成随机操作数和括号位置
#### 重写方法
```java
@Override
protected String generateSingleQuestion()
```
- **功能**: 生成小学难度数学题目(四则运算)
```java
@Override
public String getType()
```
- **返回**: "小学"
### JuniorTea 初中教师类
#### 构造函数
```java
public JuniorTea(String name, String password, String path)
```
#### 特有方法
```java
public void getRandom()
```
- **功能**: 生成随机操作数、特殊运算符和括号位置
#### 重写方法
```java
@Override
protected String generateSingleQuestion()
```
- **功能**: 生成初中难度数学题目(四则运算 + 平方/开方)
```java
@Override
public String getType()
```
- **返回**: "初中"
### SeniorTea 高中教师类
#### 构造函数
```java
public SeniorTea(String name, String password, String path)
```
#### 特有方法
```java
public void getRandom()
```
- **功能**: 生成随机操作数、三角函数和括号位置
#### 重写方法
```java
@Override
protected String generateSingleQuestion()
```
- **功能**: 生成高中难度数学题目(四则运算 + 三角函数)
```java
@Override
public String getType()
```
- **返回**: "高中"
### Main 程序入口类
#### 主方法
```java
public static void main(String[] args)
```
- **功能**: 程序入口点,启动数学题目生成系统
- **流程**:
1. 初始化系统
2. 创建GBK编码的Scanner
3. 进入主循环(登录 → 操作 → 退出选择)
## 扩展性
系统具有良好的扩展性,可以通过以下方式增强功能:
- 添加用户注册功能
- 添加新的学段类型
- 增加新的数学运算类型
- 支持自定义题目难度
@ -109,8 +287,8 @@ Personal_Project/
- 支持批量导入导出
## 开发环境
- **语言**Java
- **编码**GBK支持中文输入
- **语言**Java21.0.4
- **编码**标准输入scannerGBK支持中文输入,文件保存为UTF-8
- **依赖**纯Java标准库无需额外依赖
## 项目价值

Binary file not shown.

@ -1,58 +1,68 @@
package src;
import java.util.Random;
public class JuniorTea extends Teacher{
final static String[] binaryOps = {"+", "-", "*", "/"}; // 操作符
final static String[] unaryOps = {"^2", "√"};
int operandCount;
String[] questionParts; // 操作数
boolean[] specialOpFlags; // 特殊操作符索引序列
int parenStart;
int parenEnd;
public JuniorTea(String name, String password, String path) {
super(name, password, path);
}
@Override
protected String generateSingleQuestion(){
Random random = new Random();
String[] binaryOps = {"+", "-", "*", "/"};
String[] unaryOps = {"^2", "√"};
int operandCount = random.nextInt(4) + 2;
String[] questionParts = new String[operandCount];
// 数据预处理
public void getRandom(){
this.operandCount = random.nextInt(4) + 2;
this.questionParts = new String[this.operandCount];
// 随机决定特殊操作符的数量和位置
int specialNum = Math.min(operandCount, random.nextInt(operandCount) + 1);
boolean[] specialOpFlags = new boolean[operandCount];
int specialNum = Math.min(this.operandCount, random.nextInt(this.operandCount) + 1);
this.specialOpFlags = new boolean[this.operandCount];
for (int i = 0; i < specialNum; i++) {
int pos;
do {
pos = random.nextInt(operandCount);
} while (specialOpFlags[pos]);
specialOpFlags[pos] = true;
pos = random.nextInt(this.operandCount);
} while (this.specialOpFlags[pos]);
this.specialOpFlags[pos] = true;
}
for (int i = 0; i < operandCount; i++) {
for (int i = 0; i < this.operandCount; i++) {
int operand = random.nextInt(100) + 1;
if (specialOpFlags[i]) {
if (this.specialOpFlags[i]) {
String op = unaryOps[random.nextInt(unaryOps.length)];
if (op.equals("√")) {
questionParts[i] = op + operand;
this.questionParts[i] = op + operand;
} else {
questionParts[i] = operand + op;
this.questionParts[i] = operand + op;
}
} else {
questionParts[i] = String.valueOf(operand);
this.questionParts[i] = String.valueOf(operand);
}
}
// 生成有效括号
boolean useParen = this.operandCount > 2 && random.nextBoolean();
this.parenStart = 0;
this.parenEnd = this.operandCount - 1;
while (this.parenStart == 0 && this.parenEnd == this.operandCount - 1) {
this.parenStart = useParen ? random.nextInt(this.operandCount - 1) : -2;
this.parenEnd = useParen ? random.nextInt(this.operandCount - 1 - this.parenStart) + this.parenStart + 1 : -2;
}
}
@Override
protected String generateSingleQuestion(){
getRandom();
StringBuilder question = new StringBuilder();
boolean useParen = operandCount > 2 && random.nextBoolean();
int parenStart = useParen ? random.nextInt(operandCount - 1) : -2;
for (int i = 0; i < operandCount; i++) {
if (i == parenStart) {
question.append("(");
}
question.append(questionParts[i]);
if (i == parenStart + 1) {
if (i == parenEnd) {
question.append(")");
}
if (i < operandCount - 1) {
question.append(" ").append(binaryOps[random.nextInt(binaryOps.length)]).append(" ");
}

Binary file not shown.

@ -1,6 +1,7 @@
package src;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) {
@ -12,6 +13,22 @@ public class Main {
SysFunc.login(scanner);
// 出题界面
SysFunc.Operate(scanner);
// 退出选项
System.out.println("是否返回登陆(y/n)?");
String choice = scanner.nextLine();
if (choice.charAt(0) == 'y') {
continue;
} else if (choice.charAt(0) == 'n') {
System.out.println("感谢您的使用");
break;
} else {
System.out.println("无效输入, 正在返回登陆界面");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
}

Binary file not shown.

@ -1,40 +1,52 @@
package src;
import java.util.Random;
public class PrimaryTea extends Teacher{
final static char[] operators = {'+', '-', '*', '/'}; // 操作符
int operandCount; // 操作数个数
int[] operands; // 操作数列表
int parenStart; // 左括号索引
int parenEnd; // 右括号
public PrimaryTea(String name, String password, String path) {
super(name, password, path);
}
@Override
protected String generateSingleQuestion() {
Random random = new Random();
char[] operators = {'+', '-', '*', '/'};
// 数据预处理
public void getRandom(){
// 随机决定操作数个数
int operandCount = random.nextInt(4) + 2;
StringBuilder question = new StringBuilder();
int[] operands = new int[operandCount];
this.operandCount = random.nextInt(4) + 2;
this.operands = new int[operandCount];
for (int j = 0; j < operandCount; j++) {
operands[j] = random.nextInt(100) + 1;
}
// 产生有效括号
boolean useParen = this.operandCount > 2 && random.nextBoolean();
this.parenStart = 0;
this.parenEnd = this.operandCount - 1;
while (this.parenStart == 0 && this.parenEnd == this.operandCount - 1) {
this.parenStart = useParen ? random.nextInt(this.operandCount - 1) : -2;
this.parenEnd = useParen ? random.nextInt(this.operandCount - 1 - this.parenStart) + this.parenStart + 1 : -2;
}
}
boolean useParen = operandCount > 2 && random.nextBoolean();
int parenStart = useParen ? random.nextInt(operandCount - 1) : -2;
@Override
protected String generateSingleQuestion() {
getRandom();
StringBuilder question = new StringBuilder();
for (int j = 0; j < operandCount; j++) {
if (j == parenStart) {
for (int j = 0; j < this.operandCount; j++) {
if (j == this.parenStart) {
question.append("(");
}
question.append(operands[j]);
if (j == parenStart + 1) {
if (j == this.parenEnd) {
question.append(")");
}
if (j < operandCount - 1) {
if (j < this.operandCount - 1) {
char op = operators[random.nextInt(operators.length)];
if (op == '/') {
operands[j+1] = operands[j+1] == 0 ? 1 : operands[j+1];
this.operands[j+1] = this.operands[j+1] == 0 ? 1 : this.operands[j+1];
}
question.append(" ").append(op).append(" ");
}

Binary file not shown.

@ -1,31 +1,32 @@
package src;
import java.util.Random;
public class SeniorTea extends Teacher{
final static String[] binaryOps = {"+", "-", "*", "/"}; // 操作符
final static String[] trigOps = {"sin", "cos", "tan"};
int operandCount;
String[] questionParts; // 操作数
boolean[] specialOpFlags; // 特殊操作符索引序列
int parenStart;
int parenEnd;
public SeniorTea(String name, String password, String path) {
super(name, password, path);
}
@Override
protected String generateSingleQuestion(){
Random random = new Random();
String[] binaryOps = {"+", "-", "*", "/"};
String[] trigOps = {"sin", "cos", "tan"};
int operandCount = random.nextInt(4) + 2;
String[] questionParts = new String[operandCount];
// 数据预处理
public void getRandom(){
this.operandCount = random.nextInt(4) + 2;
this.questionParts = new String[this.operandCount];
// 随机决定特殊操作符的数量和位置
int specialNum = Math.min(operandCount, random.nextInt(operandCount) + 1);
boolean[] specialOpFlags = new boolean[operandCount];
int specialNum = Math.min(this.operandCount, random.nextInt(this.operandCount) + 1);
this.specialOpFlags = new boolean[this.operandCount];
for (int i = 0; i < specialNum; i++) {
int pos;
do {
pos = random.nextInt(operandCount);
} while (specialOpFlags[pos]);
specialOpFlags[pos] = true;
pos = random.nextInt(this.operandCount);
} while (this.specialOpFlags[pos]);
this.specialOpFlags[pos] = true;
}
for (int i = 0; i < operandCount; i++) {
int operand = random.nextInt(100) + 1;
if (specialOpFlags[i]) {
@ -35,17 +36,27 @@ public class SeniorTea extends Teacher{
questionParts[i] = String.valueOf(operand);
}
}
// 生成有效括号
boolean useParen = this.operandCount > 2 && random.nextBoolean();
this.parenStart = 0;
this.parenEnd = this.operandCount - 1;
while (this.parenStart == 0 && this.parenEnd == this.operandCount - 1) {
this.parenStart = useParen ? random.nextInt(this.operandCount - 1) : -2;
this.parenEnd = useParen ? random.nextInt(this.operandCount - 1 - this.parenStart) + this.parenStart + 1 : -2;
}
}
@Override
protected String generateSingleQuestion(){
getRandom();
StringBuilder question = new StringBuilder();
boolean useParen = operandCount > 2 && random.nextBoolean();
int parenStart = useParen ? random.nextInt(operandCount - 1) : -2;
for (int i = 0; i < operandCount; i++) {
if (i == parenStart) {
question.append("(");
}
question.append(questionParts[i]);
if (i == parenStart + 1) {
if (i == parenEnd) {
question.append(")");
}

Binary file not shown.

@ -1,17 +1,29 @@
package src;
import java.util.Scanner;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;
public class SysFunc {
// 题目数量
static final int LEAST_NUM = 10;
static final int MAX_NUM = 30;
// 用户列表
static ArrayList<Teacher> teachers;
// 当前用户
static Teacher teacher;
static Teacher teacher;
static HashMap<String, Integer> typeMap;
// ANSI颜色代码
static final String RESET = "\u001B[0m";
static final String RED = "\u001B[31m";
static final String GREEN = "\u001B[32m";
static final String YELLOW = "\u001B[33m";
static final String BLUE = "\u001B[34m";
static final String PURPLE = "\u001B[35m";
static final String CYAN = "\u001B[36m";
static final String BOLD = "\u001B[1m";
public static void init(){
// 用户预加载
@ -31,15 +43,18 @@ public class SysFunc {
typeMap.put("高中", 3);
}
// 登陆界面
public static Teacher login(Scanner scanner) {
boolean flag = false;
// 登录界面
while(!flag){
System.out.println("-------请登录您的账户-------");
System.out.println(" 请输入: 用户名 密码 ");
System.out.println(BOLD + CYAN + "=== 数学题目生成系统 ===" + RESET);
System.out.println(BOLD + CYAN + "请登录您的账户" + RESET);
while (!flag) {
System.out.println(BOLD + YELLOW + "请输入: 用户名 密码" + RESET);
System.out.print(BOLD + BLUE + "> " + RESET);
String cur = scanner.nextLine();
if (cur.indexOf(" ") == -1) {
System.out.println("请输入用户名和密码,中间用空格隔开");
System.out.println(RED + "请输入用户名和密码,中间用空格隔开");
continue;
}
String cur_name = cur.substring(0, cur.indexOf(" "));
@ -50,46 +65,50 @@ public class SysFunc {
if (t.name.equals(cur_name) && t.password.equals(cur_password)) {
teacher = t;
flag = true;
System.out.println("当前选择为" + teacher.getType() + "出题");
System.out.println(GREEN + "当前选择为" + teacher.getType() + "出题");
break;
}
}
if (!flag) {
System.out.println("请输入正确的用户名、密码");
System.out.println(RED + "请输入正确的用户名、密码");
}
}
return teacher;
return teacher;
}
// 切换类型界面
public static void ShiftType(Scanner scanner) {
int type = SysFunc.typeMap.get(SysFunc.teacher.getType());
System.out.println("-------是否切换出题类型(y/n)-------");
String choice = scanner.nextLine();
if (choice.equals("y")) {
System.out.println("请输入类型(切换**)支持小学,初中,高中难度:");
while (true) {
String choice_type = scanner.nextLine();
if (choice_type.substring(0, 2).equals("切换") && typeMap.containsKey(choice_type.substring(2))) {
type = typeMap.get(choice_type.substring(2));
if (type == 1) {
teacher = new PrimaryTea(teacher.name, teacher.password, teacher.path);
} else if (type == 2) {
teacher = new JuniorTea(teacher.name, teacher.password, teacher.path);
} else {
teacher = new SeniorTea(teacher.name, teacher.password, teacher.path);
}
System.out.println("准备生成" + teacher.getType() + "数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):");
break;
System.out.println(BOLD + PURPLE + "=== 是否切换出题类型(y/n) ===" + RESET);
System.out.print(BOLD + BLUE + "> " + RESET);
String choice = scanner.nextLine();
if (choice.equals("y")) {
System.out.println(BOLD + YELLOW + "请输入指令: 切换** (支持小学,初中,高中难度): " + RESET);
while (true) {
System.out.print(BOLD + BLUE + "> " + RESET);
String choice_type = scanner.nextLine();
if (choice_type.substring(0, 2).equals("切换") && typeMap.containsKey(choice_type.substring(2))) {
type = typeMap.get(choice_type.substring(2));
if (type == 1) {
teacher = new PrimaryTea(teacher.name, teacher.password, teacher.path);
} else if (type == 2) {
teacher = new JuniorTea(teacher.name, teacher.password, teacher.path);
} else {
System.out.println("请输入小学、初中和高中三个选项中的一个");
teacher = new SeniorTea(teacher.name, teacher.password, teacher.path);
}
System.out.println(GREEN + "准备生成" + teacher.getType() + "数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):");
break;
} else {
System.out.println(RED + RESET + "请输入小学、初中和高中三个选项中的一个");
}
} else if (choice.equals("n")) {
System.out.println("请继续输入题目数量:");
} else {
System.out.println("无效输入,已为您设置默认类型");
}
} else if (choice.equals("n")) {
System.out.println(GREEN + "请继续输入题目数量:");
} else {
System.out.println(RED + RESET + "无效输入,已为您设置默认类型:" + teacher.getType());
System.out.println(GREEN + "请输入题目数量:");
}
}
@ -101,30 +120,33 @@ public class SysFunc {
int num = 0;
while (true) {
try {
System.out.print(BOLD + BLUE + "> " + RESET);
String input = scanner.nextLine();
num = Integer.parseInt(input);
break;
} catch (NumberFormatException e) {
System.out.println("请输入有效的数字(10-30之间的整数,或-1退出登录):");
System.out.println(RED + RESET + "请输入有效的数字(10-30之间的整数,或-1退出登录):");
}
}
while (true) {
if (num == -1) {
// 返回登录界面
System.out.println("已退出登录");
System.out.println(BOLD + YELLOW + "已退出登录" + RESET);
logout = true;
break;
}
if (num >= LEAST_NUM && num <= MAX_NUM) {
String filepath = teacher.GenerateEX(num);
System.out.println("题目生成成功,已保存至" + filepath);
System.out.println(BOLD + GREEN + "正在生成题目..." + RESET);
String filepath = teacher.generateEX(num);
System.out.println(GREEN + RESET + "题目生成成功,已保存至" + filepath);
System.out.println(BOLD + CYAN + "=== 操作完成 ===" + RESET);
break;
} else {
System.out.println("题目数量应在10-30之间,请重新输入");
System.out.println(RED + RESET + "题目数量应在10-30之间,请重新输入");
try {
System.out.print(BOLD + BLUE + "> " + RESET);
num = Integer.parseInt(scanner.nextLine());
} catch (NumberFormatException e) {
System.out.println("请输入有效的数字:");
System.out.println(RED + RESET + "请输入有效的数字:");
}
}
}

Binary file not shown.

@ -10,11 +10,13 @@ import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
public abstract class Teacher {
String name;
String password;
String path = null; // 教师文件夹路径
protected String name;
protected String password;
protected String path = null; // 教师文件夹路径
public Random random = new Random(); // 共用Random
public Teacher(String name, String password, String path) {
this.name = name;
@ -85,7 +87,7 @@ public abstract class Teacher {
}
// 生成并存储题目
public String GenerateEX(int num) {
public String generateEX(int num) {
Path filePath = Paths.get(generatePath());
for (int i = 0; i < num; i++) {
String question = generateSingleQuestion();

Loading…
Cancel
Save