From 287b6c78559d82177ccb6dd2eb32f3e87407898e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E6=99=A8=E6=97=AD?= <15550449+ruantang231@user.noreply.gitee.com> Date: Sat, 11 Oct 2025 17:07:05 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=AB=98=E4=B8=AD?= =?UTF-8?q?=E5=87=BA=E9=A2=98=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChoiceQuestionGenerator.java | 300 ++++++++++++++---- user_data.txt | 2 + 2 files changed, 233 insertions(+), 69 deletions(-) create mode 100644 user_data.txt diff --git a/src/main/java/com/example/mathsystemtogether/ChoiceQuestionGenerator.java b/src/main/java/com/example/mathsystemtogether/ChoiceQuestionGenerator.java index 6fce20b..9638bad 100644 --- a/src/main/java/com/example/mathsystemtogether/ChoiceQuestionGenerator.java +++ b/src/main/java/com/example/mathsystemtogether/ChoiceQuestionGenerator.java @@ -9,31 +9,31 @@ public class ChoiceQuestionGenerator { private final Random random = new Random(); private final int MAX_OPERAND_VALUE = 50; // 降低数值范围,便于计算 private final int MIN_OPERAND_VALUE = 1; - + /** * 生成指定数量的选择题 */ public List generateQuestions(Level level, int count) { List questions = new ArrayList<>(); Set usedQuestions = new HashSet<>(); - + int attempts = 0; int maxAttempts = count * 100; // 最大尝试次数 - + while (questions.size() < count && attempts < maxAttempts) { Question question = generateSingleQuestion(level, questions.size() + 1); String questionKey = question.getQuestionText(); - + if (!usedQuestions.contains(questionKey)) { usedQuestions.add(questionKey); questions.add(question); } attempts++; } - + return questions; } - + /** * 生成单个选择题 */ @@ -49,38 +49,38 @@ public class ChoiceQuestionGenerator { return generatePrimaryQuestion(questionNumber); } } - + /** * 生成小学题目 */ private Question generatePrimaryQuestion(int questionNumber) { int a = MIN_OPERAND_VALUE + random.nextInt(MAX_OPERAND_VALUE); int b = MIN_OPERAND_VALUE + random.nextInt(MAX_OPERAND_VALUE); - + String operation = getRandomOperation("+*-"); String questionText = String.format("%d %s %d = ?", a, operation, b); int correctAnswer = calculate(a, b, operation); - + // 生成错误选项 List options = generateWrongOptions(correctAnswer); options.add(correctAnswer); Collections.shuffle(options); - - return new Question(questionText, - String.valueOf(options.get(0)), - String.valueOf(options.get(1)), - String.valueOf(options.get(2)), - String.valueOf(options.get(3)), - String.valueOf(correctAnswer), + + return new Question(questionText, + String.valueOf(options.get(0)), + String.valueOf(options.get(1)), + String.valueOf(options.get(2)), + String.valueOf(options.get(3)), + String.valueOf(correctAnswer), questionNumber); } - + /** * 生成初中题目 */ private Question generateJuniorQuestion(int questionNumber) { int a = MIN_OPERAND_VALUE + random.nextInt(MAX_OPERAND_VALUE); - + // 50%概率生成平方或开方题目 if (random.nextBoolean()) { // 平方题目 @@ -89,13 +89,13 @@ public class ChoiceQuestionGenerator { List options = generateWrongOptions(correctAnswer); options.add(correctAnswer); Collections.shuffle(options); - - return new Question(questionText, - String.valueOf(options.get(0)), - String.valueOf(options.get(1)), - String.valueOf(options.get(2)), - String.valueOf(options.get(3)), - String.valueOf(correctAnswer), + + return new Question(questionText, + String.valueOf(options.get(0)), + String.valueOf(options.get(1)), + String.valueOf(options.get(2)), + String.valueOf(options.get(3)), + String.valueOf(correctAnswer), questionNumber); } else { // 开方题目 @@ -105,42 +105,216 @@ public class ChoiceQuestionGenerator { List options = generateWrongOptions(correctAnswer); options.add(correctAnswer); Collections.shuffle(options); - - return new Question(questionText, - String.valueOf(options.get(0)), - String.valueOf(options.get(1)), - String.valueOf(options.get(2)), - String.valueOf(options.get(3)), - String.valueOf(correctAnswer), + + return new Question(questionText, + String.valueOf(options.get(0)), + String.valueOf(options.get(1)), + String.valueOf(options.get(2)), + String.valueOf(options.get(3)), + String.valueOf(correctAnswer), questionNumber); } } - + /** - * 生成高中题目 - */ + * 生成高中题目 + */ private Question generateSeniorQuestion(int questionNumber) { - int angle = random.nextInt(360); + // 使用常见角度: 0°, 30°, 45°, 60°, 90°, 120°, 135°, 150°, 180°, 210°, 225°, 270°, 300°, 315°, 330° + int[] commonAngles = {0, 30, 45, 60, 90, 120, 135, 150, 180, 210, 225, 270, 300, 315, 330}; + int angle = commonAngles[random.nextInt(commonAngles.length)]; String[] functions = {"sin", "cos", "tan"}; String function = functions[random.nextInt(functions.length)]; - + String questionText = String.format("%s(%d°) = ?", function, angle); double result = calculateTrigFunction(function, angle); - int correctAnswer = (int) Math.round(result * 100) / 100; // 保留两位小数 - - List options = generateWrongOptions(correctAnswer); - options.add(correctAnswer); + + // 生成选项 + List options = generateTrigOptions(result); + // 使用格式化后的值作为正确答案,确保与选项一致 + String correctAnswer = formatTrigValue(result); + + // 随机打乱选项 Collections.shuffle(options); - - return new Question(questionText, - String.valueOf(options.get(0)), - String.valueOf(options.get(1)), - String.valueOf(options.get(2)), - String.valueOf(options.get(3)), - String.valueOf(correctAnswer), - questionNumber); + + return new Question(questionText, + options.get(0), + options.get(1), + options.get(2), + options.get(3), + correctAnswer, + questionNumber); +} + +/** + * 生成三角函数题目的选项 + */ +private List generateTrigOptions(double correctAnswer) { + List options = new ArrayList<>(); + Set usedOptions = new HashSet<>(); + + // 格式化正确答案,保留适当的小数位数 + String formattedCorrectAnswer = formatTrigValue(correctAnswer); + options.add(formattedCorrectAnswer); + usedOptions.add(formattedCorrectAnswer); + + // 添加常见三角函数值作为错误选项 + String[] commonValues = {"0", "0.5", "0.71", "0.87", "1", "1.73", "-0.5", "-0.71", "-0.87", "-1", "-1.73", "undefined"}; + List commonValuesList = new ArrayList<>(Arrays.asList(commonValues)); + + // 打乱常见值列表 + Collections.shuffle(commonValuesList); + + // 添加错误选项 + int wrongOptionsNeeded = 3; + for (String value : commonValuesList) { + if (wrongOptionsNeeded <= 0) break; + // 确保不添加与正确答案相同的选项 + if (!usedOptions.contains(value) && !value.equals(formattedCorrectAnswer)) { + options.add(value); + usedOptions.add(value); + wrongOptionsNeeded--; + } + } + + // 如果还需要更多选项,则生成接近正确答案的选项 + while (options.size() < 4) { + String wrongOption = generateNearbyValue(correctAnswer, usedOptions); + // 确保新生成的选项不重复且不与正确答案相同 + if (!usedOptions.contains(wrongOption) && !wrongOption.equals(formattedCorrectAnswer)) { + options.add(wrongOption); + usedOptions.add(wrongOption); + } + } + + return options; +} + +/** + * 格式化三角函数值 + */ +private String formatTrigValue(double value) { + // 对于特殊值进行精确表示 + if (Math.abs(value) < 1e-10) return "0"; // 接近0的值 + if (Math.abs(value - 1.0) < 1e-10) return "1"; + if (Math.abs(value + 1.0) < 1e-10) return "-1"; + if (Math.abs(value - 0.5) < 1e-10) return "0.5"; + if (Math.abs(value + 0.5) < 1e-10) return "-0.5"; + if (Math.abs(value - Math.sqrt(2)/2) < 1e-10) return "0.71"; // √2/2 ≈ 0.707 + if (Math.abs(value + Math.sqrt(2)/2) < 1e-10) return "-0.71"; + if (Math.abs(value - Math.sqrt(3)/2) < 1e-10) return "0.87"; // √3/2 ≈ 0.866 + if (Math.abs(value + Math.sqrt(3)/2) < 1e-10) return "-0.87"; + if (Math.abs(value - Math.sqrt(3)) < 1e-10) return "1.73"; // √3 ≈ 1.732 + if (Math.abs(value + Math.sqrt(3)) < 1e-10) return "-1.73"; + + // 对于tan(90°)等未定义值 + if (Double.isInfinite(value) || Double.isNaN(value)) { + return "undefined"; + } + + // 其他值保留两位小数 + return String.format("%.2f", value); +} + +/** + * 生成接近正确答案的选项 + */ +private String generateNearbyValue(double correctAnswer, Set usedOptions) { + // 对于特殊值直接返回简单值 + if (Double.isInfinite(correctAnswer) || Double.isNaN(correctAnswer)) { + return "0"; + } + + // 生成在正确答案附近的值 + double variation = (random.nextDouble() - 0.5) * 2; // -1 到 1 之间的随机数 + double wrongAnswer = correctAnswer + variation; + + // 格式化生成的值 + String formatted = formatTrigValue(wrongAnswer); + + // 确保不会生成与已使用选项相同的值,最多尝试10次 + int attempts = 0; + while (usedOptions.contains(formatted) && attempts < 10) { + variation = (random.nextDouble() - 0.5) * 2; + wrongAnswer = correctAnswer + variation; + formatted = formatTrigValue(wrongAnswer); + attempts++; + } + + return formatted; +} + +/** + * 计算三角函数 + */ +private double calculateTrigFunction(String function, int angle) { + double radians = Math.toRadians(angle); + switch (function) { + case "sin": + // 对特定角度返回精确值 + switch (angle) { + case 0: return 0; + case 30: return 0.5; + case 45: return Math.sqrt(2)/2; + case 60: return Math.sqrt(3)/2; + case 90: return 1; + case 120: return Math.sqrt(3)/2; + case 135: return Math.sqrt(2)/2; + case 150: return 0.5; + case 180: return 0; + case 210: return -0.5; + case 225: return -Math.sqrt(2)/2; + case 270: return -1; + case 300: return -Math.sqrt(3)/2; + case 315: return -Math.sqrt(2)/2; + case 330: return -0.5; + default: return Math.sin(radians); + } + case "cos": + // 对特定角度返回精确值 + switch (angle) { + case 0: return 1; + case 30: return Math.sqrt(3)/2; + case 45: return Math.sqrt(2)/2; + case 60: return 0.5; + case 90: return 0; + case 120: return -0.5; + case 135: return -Math.sqrt(2)/2; + case 150: return -Math.sqrt(3)/2; + case 180: return -1; + case 210: return -Math.sqrt(3)/2; + case 225: return -Math.sqrt(2)/2; + case 270: return 0; + case 300: return 0.5; + case 315: return Math.sqrt(2)/2; + case 330: return Math.sqrt(3)/2; + default: return Math.cos(radians); + } + case "tan": + // 对特定角度返回精确值或未定义 + switch (angle) { + case 0: return 0; + case 30: return Math.sqrt(3)/3; + case 45: return 1; + case 60: return Math.sqrt(3); + case 90: return Double.POSITIVE_INFINITY; // 未定义 + case 120: return -Math.sqrt(3); + case 135: return -1; + case 150: return -Math.sqrt(3)/3; + case 180: return 0; + case 210: return Math.sqrt(3)/3; + case 225: return 1; + case 270: return Double.POSITIVE_INFINITY; // 未定义 + case 300: return -Math.sqrt(3); + case 315: return -1; + case 330: return -Math.sqrt(3)/3; + default: return Math.tan(radians); + } + default: return 0; } - +} + + /** * 生成错误选项 */ @@ -148,7 +322,7 @@ public class ChoiceQuestionGenerator { List options = new ArrayList<>(); Set usedOptions = new HashSet<>(); usedOptions.add(correctAnswer); - + while (options.size() < 3) { int wrongAnswer; if (correctAnswer == 0) { @@ -158,16 +332,16 @@ public class ChoiceQuestionGenerator { int variation = random.nextInt(Math.max(1, Math.abs(correctAnswer) / 2)) + 1; wrongAnswer = correctAnswer + (random.nextBoolean() ? variation : -variation); } - + if (!usedOptions.contains(wrongAnswer)) { options.add(wrongAnswer); usedOptions.add(wrongAnswer); } } - + return options; } - + /** * 计算基本运算 */ @@ -180,20 +354,8 @@ public class ChoiceQuestionGenerator { default: return 0; } } - - /** - * 计算三角函数 - */ - private double calculateTrigFunction(String function, int angle) { - double radians = Math.toRadians(angle); - switch (function) { - case "sin": return Math.sin(radians); - case "cos": return Math.cos(radians); - case "tan": return Math.tan(radians); - default: return 0; - } - } - + + /** * 获取随机运算符 */ @@ -201,7 +363,7 @@ public class ChoiceQuestionGenerator { int index = random.nextInt(operations.length()); return String.valueOf(operations.charAt(index)); } - + /** * 难度级别枚举 */ diff --git a/user_data.txt b/user_data.txt new file mode 100644 index 0000000..cc12efd --- /dev/null +++ b/user_data.txt @@ -0,0 +1,2 @@ +wgll|123456|ymhlovesLQX@163.com|小学|1760162416490 +666|123456789|252436951@qq.com|小学|1760166999971 From b0a1eceeaa4ecafda5e43d7828db0a18c3e7a9f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E6=99=A8=E6=97=AD?= <15550449+ruantang231@user.noreply.gitee.com> Date: Sat, 11 Oct 2025 21:19:45 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChangePasswordController.java | 149 ++++++++++++++++++ .../mathsystemtogether/ExamController.java | 53 ++++++- .../change-password-view.fxml | 70 ++++++++ .../example/mathsystemtogether/exam-view.fxml | 10 +- user_data.txt | 4 +- 5 files changed, 275 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/example/mathsystemtogether/ChangePasswordController.java create mode 100644 src/main/resources/com/example/mathsystemtogether/change-password-view.fxml diff --git a/src/main/java/com/example/mathsystemtogether/ChangePasswordController.java b/src/main/java/com/example/mathsystemtogether/ChangePasswordController.java new file mode 100644 index 0000000..f8f9269 --- /dev/null +++ b/src/main/java/com/example/mathsystemtogether/ChangePasswordController.java @@ -0,0 +1,149 @@ +package com.example.mathsystemtogether; + +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.stage.Stage; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.List; + +public class ChangePasswordController { + + @FXML + private TextField usernameField; + + @FXML + private PasswordField oldPasswordField; + + @FXML + private PasswordField newPasswordField; + + @FXML + private PasswordField confirmNewPasswordField; + + @FXML + private Button changePasswordButton; + + @FXML + private Button cancelButton; + + @FXML + private Label statusLabel; + + private String currentUser; + private ExamController examController; + private static final String USER_DATA_FILE = "user_data.txt"; + + public void setCurrentUser(String username) { + this.currentUser = username; + usernameField.setText(username); + } + + public void setExamController(ExamController examController) { + this.examController = examController; + } + + @FXML + private void handleChangePassword() { + String oldPassword = oldPasswordField.getText(); + String newPassword = newPasswordField.getText(); + String confirmNewPassword = confirmNewPasswordField.getText(); + + if (oldPassword.isEmpty() || newPassword.isEmpty() || confirmNewPassword.isEmpty()) { + showStatus("请填写所有字段", true); + return; + } + + if (!examController.checkUserPassword(currentUser, oldPassword)) { + showStatus("原密码错误", true); + return; + } + + if (newPassword.length() < 6) { + showStatus("新密码至少需要6个字符", true); + return; + } + + if (!newPassword.equals(confirmNewPassword)) { + showStatus("两次输入的新密码不一致", true); + return; + } + + if (oldPassword.equals(newPassword)) { + showStatus("新密码不能与原密码相同", true); + return; + } + + if (updatePassword(currentUser, newPassword)) { + showStatus("密码修改成功!", false); + + // 延迟关闭窗口 + new java.util.Timer().schedule( + new java.util.TimerTask() { + @Override + public void run() { + javafx.application.Platform.runLater(() -> { + handleClose(); + }); + } + }, + 1500 + ); + } else { + showStatus("密码修改失败,请重试", true); + } + } + + @FXML + private void handleCancel() { + handleClose(); + } + + private void handleClose() { + Stage stage = (Stage) cancelButton.getScene().getWindow(); + stage.close(); + } + + private boolean updatePassword(String username, String newPassword) { + try { + // 读取所有用户数据 + List lines = Files.readAllLines(Paths.get(USER_DATA_FILE)); + boolean userFound = false; + + for (int i = 0; i < lines.size(); i++) { + String[] parts = lines.get(i).split("\\|"); + if (parts.length >= 2 && parts[0].equals(username)) { + // 更新密码 + parts[1] = newPassword; + lines.set(i, String.join("|", parts)); + userFound = true; + break; + } + } + + if (userFound) { + // 写回文件 + Files.write(Paths.get(USER_DATA_FILE), lines); + // 更新内存中的用户数据 + examController.updateUserPassword(username, newPassword); + return true; + } + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + + private void showStatus(String message, boolean isError) { + statusLabel.setText(message); + if (isError) { + statusLabel.setStyle("-fx-text-fill: #DC143C; -fx-font-weight: bold; -fx-font-size: 14;"); + } else { + statusLabel.setStyle("-fx-text-fill: #228B22; -fx-font-weight: bold; -fx-font-size: 14;"); + } + } +} diff --git a/src/main/java/com/example/mathsystemtogether/ExamController.java b/src/main/java/com/example/mathsystemtogether/ExamController.java index cb60676..1f390ba 100644 --- a/src/main/java/com/example/mathsystemtogether/ExamController.java +++ b/src/main/java/com/example/mathsystemtogether/ExamController.java @@ -33,6 +33,7 @@ public class ExamController { @FXML private Button startExamButton; @FXML private Button logoutButton; @FXML private Label statusLabel; + @FXML private Button changePasswordButton; // 数据成员 private Account currentAccount; @@ -80,7 +81,7 @@ public class ExamController { if (!Files.exists(Paths.get(USER_DATA_FILE))) { return; } - + List lines = Files.readAllLines(Paths.get(USER_DATA_FILE)); for (String line : lines) { String[] parts = line.split("\\|"); @@ -88,14 +89,14 @@ public class ExamController { String username = parts[0]; String password = parts[1]; String levelStr = parts[3]; - + Level level; try { level = Level.valueOf(levelStr); } catch (IllegalArgumentException e) { level = Level.小学; // 默认级别 } - + userMap.put(username, new Account(username, password, level)); } } @@ -140,7 +141,7 @@ public class ExamController { registerStage.setScene(scene); registerStage.setResizable(false); registerStage.show(); - + } catch (Exception e) { loginStatusLabel.setText("打开注册界面失败:" + e.getMessage()); loginStatusLabel.setStyle("-fx-text-fill: red;"); @@ -197,6 +198,48 @@ public class ExamController { } } +@FXML +private void handleChangePassword() { + if (currentAccount == null) { + statusLabel.setText("请先登录"); + statusLabel.setStyle("-fx-text-fill: red;"); + return; + } + + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("change-password-view.fxml")); + Scene scene = new Scene(loader.load(), 600, 500); + Stage changePasswordStage = new Stage(); + changePasswordStage.setTitle("修改密码"); + changePasswordStage.setScene(scene); + changePasswordStage.setResizable(false); + + // 传递当前用户信息给密码修改控制器 + ChangePasswordController controller = loader.getController(); + controller.setCurrentUser(currentAccount.username); + controller.setExamController(this); + + changePasswordStage.show(); + } catch (Exception e) { + statusLabel.setText("打开修改密码界面失败:" + e.getMessage()); + statusLabel.setStyle("-fx-text-fill: red;"); + } +} + +// 供 ChangePasswordController 调用的方法 +public boolean checkUserPassword(String username, String password) { + Account account = userMap.get(username); + return account != null && account.password.equals(password); +} + +// 更新用户密码 +public void updateUserPassword(String username, String newPassword) { + Account account = userMap.get(username); + if (account != null) { + account.password = newPassword; + } +} + private void startExam() { try { // 打开专门的考试界面 @@ -240,4 +283,4 @@ public class ExamController { enum Level { 小学, 初中, 高中 } -} \ No newline at end of file +} diff --git a/src/main/resources/com/example/mathsystemtogether/change-password-view.fxml b/src/main/resources/com/example/mathsystemtogether/change-password-view.fxml new file mode 100644 index 0000000..7b7970b --- /dev/null +++ b/src/main/resources/com/example/mathsystemtogether/change-password-view.fxml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +