Compare commits
32 Commits
fanwen_bra
...
develop
@ -0,0 +1,103 @@
|
|||||||
|
package com.example.myapp;
|
||||||
|
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
public class Main extends Application {
|
||||||
|
private static Stage primaryStage;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Stage stage) throws Exception{
|
||||||
|
primaryStage = stage;
|
||||||
|
showLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showLogin() throws Exception{
|
||||||
|
FXMLLoader loader = new FXMLLoader(Main.class.getResource("/fxml/login.fxml"));
|
||||||
|
primaryStage.setScene(new Scene(loader.load()));
|
||||||
|
primaryStage.setTitle("登录");
|
||||||
|
primaryStage.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showRegister() throws Exception{
|
||||||
|
FXMLLoader loader = new FXMLLoader(Main.class.getResource("/fxml/register.fxml"));
|
||||||
|
primaryStage.setScene(new Scene(loader.load()));
|
||||||
|
primaryStage.setTitle("注册 - 输入邮箱");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showSetPassword(String email) throws Exception{
|
||||||
|
FXMLLoader loader = new FXMLLoader(Main.class.getResource("/fxml/set_password.fxml"));
|
||||||
|
// 传递 email 给 controller
|
||||||
|
Parent root = loader.load();
|
||||||
|
var ctrl = loader.getController();
|
||||||
|
((com.example.myapp.controller.SetPasswordController)ctrl).setEmail(email);
|
||||||
|
primaryStage.setScene(new Scene(root));
|
||||||
|
primaryStage.setTitle("设置密码");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showDashboard(String email) throws Exception{
|
||||||
|
FXMLLoader loader = new FXMLLoader(Main.class.getResource("/fxml/dashboard.fxml"));
|
||||||
|
Parent root = loader.load();
|
||||||
|
var ctrl = loader.getController();
|
||||||
|
((com.example.myapp.controller.DashboardController)ctrl).initUser(email);
|
||||||
|
primaryStage.setScene(new Scene(root));
|
||||||
|
primaryStage.setTitle("用户界面");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在 Main.java 中添加以下方法
|
||||||
|
public static void showGradeSelection(String email) throws Exception {
|
||||||
|
FXMLLoader loader = new FXMLLoader(Main.class.getResource("/fxml/grade_selection.fxml"));
|
||||||
|
Parent root = loader.load();
|
||||||
|
var ctrl = loader.getController();
|
||||||
|
((com.example.myapp.controller.GradeSelectionController)ctrl).initUser(email);
|
||||||
|
primaryStage.setScene(new Scene(root));
|
||||||
|
primaryStage.setTitle("选择学段");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showExamPage(String email) throws Exception {
|
||||||
|
FXMLLoader loader = new FXMLLoader(Main.class.getResource("/fxml/exam.fxml"));
|
||||||
|
Parent root = loader.load();
|
||||||
|
|
||||||
|
// 获取控制器并初始化
|
||||||
|
var ctrl = loader.getController();
|
||||||
|
((com.example.myapp.controller.ExamController)ctrl).initExam(email);
|
||||||
|
|
||||||
|
primaryStage.setScene(new Scene(root));
|
||||||
|
primaryStage.setTitle("答题界面");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showScorePage(String email, int score) throws Exception {
|
||||||
|
FXMLLoader loader = new FXMLLoader(Main.class.getResource("/fxml/score.fxml"));
|
||||||
|
Parent root = loader.load();
|
||||||
|
var ctrl = loader.getController();
|
||||||
|
((com.example.myapp.controller.ScoreController)ctrl).initScore(email, score);
|
||||||
|
primaryStage.setScene(new Scene(root));
|
||||||
|
primaryStage.setTitle("考试成绩");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showChangePassword(String email) throws Exception {
|
||||||
|
FXMLLoader loader = new FXMLLoader(Main.class.getResource("/fxml/change_password.fxml"));
|
||||||
|
Parent root = loader.load();
|
||||||
|
var ctrl = loader.getController();
|
||||||
|
((com.example.myapp.controller.ChangePasswordController)ctrl).initUser(email);
|
||||||
|
primaryStage.setScene(new Scene(root));
|
||||||
|
primaryStage.setTitle("修改密码");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 在 Main.java 中添加
|
||||||
|
public static void showForgotPassword(String email) throws Exception {
|
||||||
|
FXMLLoader loader = new FXMLLoader(Main.class.getResource("/fxml/forgot_password.fxml"));
|
||||||
|
Parent root = loader.load();
|
||||||
|
var ctrl = loader.getController();
|
||||||
|
((com.example.myapp.controller.ForgotPasswordController)ctrl).setEmail(email);
|
||||||
|
primaryStage.setScene(new Scene(root));
|
||||||
|
primaryStage.setTitle("忘记密码");
|
||||||
|
}
|
||||||
|
public static void main(String[] args){
|
||||||
|
launch(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
package com.example.myapp.controller;
|
||||||
|
|
||||||
|
import com.example.myapp.Main;
|
||||||
|
import com.example.myapp.service.UserService;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
|
||||||
|
public class DashboardController {
|
||||||
|
@FXML private Label welcomeLabel;
|
||||||
|
@FXML private Button changePasswordBtn;
|
||||||
|
@FXML private Button studyBtn;
|
||||||
|
@FXML private Button logoutBtn;
|
||||||
|
|
||||||
|
private String email;
|
||||||
|
private final UserService userService = UserService.getInstance();
|
||||||
|
|
||||||
|
public void initUser(String email){
|
||||||
|
this.email = email;
|
||||||
|
String displayName = userService.getDisplayName(email);
|
||||||
|
welcomeLabel.setText("欢迎: " + displayName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void onChangePassword() {
|
||||||
|
try {
|
||||||
|
Main.showChangePassword(email);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void onStudy() {
|
||||||
|
try {
|
||||||
|
Main.showGradeSelection(email);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void onLogout() {
|
||||||
|
try {
|
||||||
|
Main.showLogin();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
package com.example.myapp.controller;
|
||||||
|
|
||||||
|
import com.example.myapp.Main;
|
||||||
|
import com.example.myapp.service.ExamService;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
|
||||||
|
public class GradeSelectionController {
|
||||||
|
@FXML private Label welcomeLabel;
|
||||||
|
|
||||||
|
private String email;
|
||||||
|
private final ExamService examService = ExamService.getInstance();
|
||||||
|
|
||||||
|
public void initUser(String email) {
|
||||||
|
this.email = email;
|
||||||
|
welcomeLabel.setText("欢迎 " + email + ",请选择学段");
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void onPrimarySchoolSelected() {
|
||||||
|
showQuestionCountDialog("小学");
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void onMiddleSchoolSelected() {
|
||||||
|
showQuestionCountDialog("初中");
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void onHighSchoolSelected() {
|
||||||
|
showQuestionCountDialog("高中");
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void onBackToLogin() {
|
||||||
|
try {
|
||||||
|
Main.showLogin();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void onBackToDashboard() {
|
||||||
|
try {
|
||||||
|
Main.showDashboard(email);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showQuestionCountDialog(String gradeLevel) {
|
||||||
|
TextInputDialog dialog = new TextInputDialog("10");
|
||||||
|
dialog.setTitle("题目数量");
|
||||||
|
dialog.setHeaderText("选择" + gradeLevel + "题目");
|
||||||
|
dialog.setContentText("请输入题目数量(1-100):");
|
||||||
|
|
||||||
|
dialog.showAndWait().ifPresent(countStr -> {
|
||||||
|
try {
|
||||||
|
int questionCount = Integer.parseInt(countStr);
|
||||||
|
if (questionCount < 1 || questionCount > 100) {
|
||||||
|
showAlert("题目数量必须在1-100之间");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success = examService.generateExam(gradeLevel, questionCount, email);
|
||||||
|
if (success) {
|
||||||
|
// 修复:添加异常处理
|
||||||
|
try {
|
||||||
|
Main.showExamPage(email);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
showAlert("无法进入考试页面");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showAlert("生成试卷失败,请重试");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
showAlert("请输入有效的数字");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showAlert(String message) {
|
||||||
|
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||||
|
alert.setTitle("错误");
|
||||||
|
alert.setHeaderText(null);
|
||||||
|
alert.setContentText(message);
|
||||||
|
alert.showAndWait();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,142 @@
|
|||||||
|
package com.example.myapp.controller;
|
||||||
|
|
||||||
|
import com.example.myapp.Main;
|
||||||
|
import com.example.myapp.service.EmailService;
|
||||||
|
import com.example.myapp.service.UserService;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class RegisterController {
|
||||||
|
@FXML private TextField emailField;
|
||||||
|
@FXML private Button sendCodeBtn;
|
||||||
|
@FXML private TextField codeField;
|
||||||
|
@FXML private Button verifyBtn;
|
||||||
|
@FXML private Label statusLabel;
|
||||||
|
|
||||||
|
private final UserService userService = UserService.getInstance();
|
||||||
|
private EmailService emailService;
|
||||||
|
|
||||||
|
public RegisterController(){
|
||||||
|
try {
|
||||||
|
emailService = new EmailService();
|
||||||
|
} catch(Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void onSendCode(){
|
||||||
|
String email = emailField.getText().trim();
|
||||||
|
|
||||||
|
// 验证邮箱格式
|
||||||
|
if(email.isEmpty()){
|
||||||
|
statusLabel.setText("请输入邮箱地址");
|
||||||
|
statusLabel.setStyle("-fx-text-fill: red;");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isValidEmail(email)){
|
||||||
|
statusLabel.setText("请输入有效的邮箱地址");
|
||||||
|
statusLabel.setStyle("-fx-text-fill: red;");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查邮箱是否已被注册
|
||||||
|
if(userService.emailExists(email)){
|
||||||
|
// 检查用户是否已完成注册(设置了密码)
|
||||||
|
if(userService.isUserRegistered(email)){
|
||||||
|
statusLabel.setText("该邮箱已被注册,请直接登录");
|
||||||
|
statusLabel.setStyle("-fx-text-fill: red;");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// 用户已存在但未完成注册,可以重新发送验证码
|
||||||
|
statusLabel.setText("该邮箱正在注册流程中,重新发送验证码");
|
||||||
|
statusLabel.setStyle("-fx-text-fill: orange;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String code = generateCode();
|
||||||
|
userService.createPendingUser(email, code);
|
||||||
|
try{
|
||||||
|
emailService.sendRegistrationCode(email, code);
|
||||||
|
statusLabel.setText("注册码已发送到您的邮箱,请查收");
|
||||||
|
statusLabel.setStyle("-fx-text-fill: green;");
|
||||||
|
|
||||||
|
// 禁用发送按钮一段时间,防止重复发送
|
||||||
|
disableSendButtonTemporarily();
|
||||||
|
|
||||||
|
}catch(Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
statusLabel.setText("发送失败: " + e.getMessage());
|
||||||
|
statusLabel.setStyle("-fx-text-fill: red;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateCode(){
|
||||||
|
Random r = new Random();
|
||||||
|
int v = 100000 + r.nextInt(900000);
|
||||||
|
return String.valueOf(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void onVerify(){
|
||||||
|
String email = emailField.getText().trim();
|
||||||
|
String code = codeField.getText().trim();
|
||||||
|
|
||||||
|
if(email.isEmpty() || code.isEmpty()){
|
||||||
|
statusLabel.setText("请输入邮箱和验证码");
|
||||||
|
statusLabel.setStyle("-fx-text-fill: red;");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(userService.verifyCode(email, code)){
|
||||||
|
statusLabel.setText("验证通过,请设置密码");
|
||||||
|
statusLabel.setStyle("-fx-text-fill: green;");
|
||||||
|
try {
|
||||||
|
Main.showSetPassword(email);
|
||||||
|
} catch(Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
statusLabel.setText("注册码错误或已过期");
|
||||||
|
statusLabel.setStyle("-fx-text-fill: red;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@FXML
|
||||||
|
public void onClear() {
|
||||||
|
emailField.clear();
|
||||||
|
codeField.clear();
|
||||||
|
statusLabel.setText("");
|
||||||
|
sendCodeBtn.setDisable(false);
|
||||||
|
}
|
||||||
|
@FXML
|
||||||
|
public void onBackToLogin() {
|
||||||
|
try {
|
||||||
|
Main.showLogin();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidEmail(String email) {
|
||||||
|
String emailRegex = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$";
|
||||||
|
return email.matches(emailRegex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disableSendButtonTemporarily() {
|
||||||
|
sendCodeBtn.setDisable(true);
|
||||||
|
new java.util.Timer().schedule(
|
||||||
|
new java.util.TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
javafx.application.Platform.runLater(() -> {
|
||||||
|
sendCodeBtn.setDisable(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
60000 // 60秒后才能重新发送
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
package com.example.myapp.controller;
|
||||||
|
|
||||||
|
import com.example.myapp.Main;
|
||||||
|
import com.example.myapp.service.ExamService;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
|
||||||
|
public class ScoreController {
|
||||||
|
@FXML private Label scoreLabel;
|
||||||
|
@FXML private Label commentLabel;
|
||||||
|
@FXML private Button continueBtn;
|
||||||
|
@FXML private Button exitBtn;
|
||||||
|
|
||||||
|
private String email;
|
||||||
|
private int score;
|
||||||
|
private ExamService examService = ExamService.getInstance();
|
||||||
|
|
||||||
|
public void initScore(String email, int score) {
|
||||||
|
this.email = email;
|
||||||
|
this.score = score;
|
||||||
|
|
||||||
|
scoreLabel.setText("得分: " + score + " 分");
|
||||||
|
|
||||||
|
// 根据分数给出评价
|
||||||
|
if (score >= 90) {
|
||||||
|
commentLabel.setText("优秀!表现非常出色!");
|
||||||
|
commentLabel.setStyle("-fx-text-fill: #27ae60;");
|
||||||
|
} else if (score >= 80) {
|
||||||
|
commentLabel.setText("良好!继续努力!");
|
||||||
|
commentLabel.setStyle("-fx-text-fill: #2980b9;");
|
||||||
|
} else if (score >= 60) {
|
||||||
|
commentLabel.setText("及格!还有提升空间!");
|
||||||
|
commentLabel.setStyle("-fx-text-fill: #f39c12;");
|
||||||
|
} else {
|
||||||
|
commentLabel.setText("不及格!需要加强学习!");
|
||||||
|
commentLabel.setStyle("-fx-text-fill: #e74c3c;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void onContinue() {
|
||||||
|
// 清除当前考试数据
|
||||||
|
examService.clearExam(email);
|
||||||
|
|
||||||
|
// 返回选择界面
|
||||||
|
try {
|
||||||
|
Main.showGradeSelection(email);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void onExit() {
|
||||||
|
// 清除考试数据
|
||||||
|
examService.clearExam(email);
|
||||||
|
|
||||||
|
// 返回登录界面
|
||||||
|
try {
|
||||||
|
Main.showLogin();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
package com.example.myapp.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
public class Exam {
|
||||||
|
private String gradeLevel;
|
||||||
|
private int questionCount;
|
||||||
|
private List<Question> questions;
|
||||||
|
private int score;
|
||||||
|
private LocalDateTime startTime;
|
||||||
|
private LocalDateTime endTime;
|
||||||
|
|
||||||
|
public Exam(String gradeLevel, int questionCount, List<Question> questions) {
|
||||||
|
this.gradeLevel = gradeLevel;
|
||||||
|
this.questionCount = questionCount;
|
||||||
|
this.questions = questions;
|
||||||
|
this.startTime = LocalDateTime.now();
|
||||||
|
this.score = -1; // -1表示未评分
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public String getGradeLevel() { return gradeLevel; }
|
||||||
|
public void setGradeLevel(String gradeLevel) { this.gradeLevel = gradeLevel; }
|
||||||
|
|
||||||
|
public int getQuestionCount() { return questionCount; }
|
||||||
|
public void setQuestionCount(int questionCount) { this.questionCount = questionCount; }
|
||||||
|
|
||||||
|
public List<Question> getQuestions() { return questions; }
|
||||||
|
public void setQuestions(List<Question> questions) { this.questions = questions; }
|
||||||
|
|
||||||
|
public int getScore() { return score; }
|
||||||
|
public void setScore(int score) { this.score = score; }
|
||||||
|
|
||||||
|
public LocalDateTime getStartTime() { return startTime; }
|
||||||
|
public void setStartTime(LocalDateTime startTime) { this.startTime = startTime; }
|
||||||
|
|
||||||
|
public LocalDateTime getEndTime() { return endTime; }
|
||||||
|
public void setEndTime(LocalDateTime endTime) { this.endTime = endTime; }
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
// Expression.java
|
||||||
|
package com.example.myapp.model;
|
||||||
|
|
||||||
|
public class Expression {
|
||||||
|
public String expression;
|
||||||
|
public double value; // 改为 double 类型支持小数答案
|
||||||
|
public String mainOperator;
|
||||||
|
|
||||||
|
public Expression(String expression, double value, String mainOperator) {
|
||||||
|
this.expression = expression;
|
||||||
|
this.value = value;
|
||||||
|
this.mainOperator = mainOperator;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
package com.example.myapp.model;
|
||||||
|
|
||||||
|
public class Question {
|
||||||
|
private String questionText;
|
||||||
|
private String optionA;
|
||||||
|
private String optionB;
|
||||||
|
private String optionC;
|
||||||
|
private String optionD;
|
||||||
|
private String correctAnswer;
|
||||||
|
private String userAnswer;
|
||||||
|
|
||||||
|
// public Question(String questionText, String optionA, String optionB,
|
||||||
|
// String optionC, String optionD, String correctAnswer) {
|
||||||
|
// this.questionText = questionText;
|
||||||
|
// this.optionA = optionA;
|
||||||
|
// this.optionB = optionB;
|
||||||
|
// this.optionC = optionC;
|
||||||
|
// this.optionD = optionD;
|
||||||
|
// this.correctAnswer = correctAnswer;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public Question(String questionText, String optionA, String optionB,
|
||||||
|
String optionC, String optionD, String correctAnswer) {
|
||||||
|
this.questionText = questionText;
|
||||||
|
this.optionA = optionA;
|
||||||
|
this.optionB = optionB;
|
||||||
|
this.optionC = optionC;
|
||||||
|
this.optionD = optionD;
|
||||||
|
this.correctAnswer = correctAnswer;
|
||||||
|
}
|
||||||
|
public String getQuestionText() { return questionText; }
|
||||||
|
public void setQuestionText(String questionText) { this.questionText = questionText; }
|
||||||
|
|
||||||
|
public String getOptionA() { return optionA; }
|
||||||
|
public void setOptionA(String optionA) { this.optionA = optionA; }
|
||||||
|
|
||||||
|
public String getOptionB() { return optionB; }
|
||||||
|
public void setOptionB(String optionB) { this.optionB = optionB; }
|
||||||
|
|
||||||
|
public String getOptionC() { return optionC; }
|
||||||
|
public void setOptionC(String optionC) { this.optionC = optionC; }
|
||||||
|
|
||||||
|
public String getOptionD() { return optionD; }
|
||||||
|
public void setOptionD(String optionD) { this.optionD = optionD; }
|
||||||
|
|
||||||
|
public String getCorrectAnswer() { return correctAnswer; }
|
||||||
|
public void setCorrectAnswer(String correctAnswer) { this.correctAnswer = correctAnswer; }
|
||||||
|
|
||||||
|
public String getUserAnswer() { return userAnswer; }
|
||||||
|
public void setUserAnswer(String userAnswer) { this.userAnswer = userAnswer; }
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
package com.example.myapp.model;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
public class User implements Serializable {
|
||||||
|
private String email;
|
||||||
|
private String username; // 新增用户名字段
|
||||||
|
private String passwordHash;
|
||||||
|
private boolean verified;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private LocalDateTime lastModified;
|
||||||
|
|
||||||
|
public User() {
|
||||||
|
this.createdAt = LocalDateTime.now();
|
||||||
|
this.lastModified = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
public User(String email, String username) {
|
||||||
|
this.email = email;
|
||||||
|
this.username = username;
|
||||||
|
this.verified = false;
|
||||||
|
this.createdAt = LocalDateTime.now();
|
||||||
|
this.lastModified = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public String getEmail() { return email; }
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
this.lastModified = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() { return username; }
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
this.lastModified = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPasswordHash() { return passwordHash; }
|
||||||
|
public void setPasswordHash(String passwordHash) {
|
||||||
|
this.passwordHash = passwordHash;
|
||||||
|
this.lastModified = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVerified() { return verified; }
|
||||||
|
public void setVerified(boolean verified) {
|
||||||
|
this.verified = verified;
|
||||||
|
this.lastModified = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreatedAt() { return createdAt; }
|
||||||
|
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
||||||
|
|
||||||
|
public LocalDateTime getLastModified() { return lastModified; }
|
||||||
|
public void setLastModified(LocalDateTime lastModified) { this.lastModified = lastModified; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "User{email='" + email + "', username='" + username + "', verified=" + verified + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
package com.example.myapp.service;
|
||||||
|
|
||||||
|
import jakarta.mail.*;
|
||||||
|
import jakarta.mail.internet.*;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class EmailService {
|
||||||
|
private final Properties props = new Properties();
|
||||||
|
private final String username;
|
||||||
|
private final String password;
|
||||||
|
private final String from;
|
||||||
|
|
||||||
|
public EmailService() throws Exception{
|
||||||
|
try(InputStream is = getClass().getResourceAsStream("/app.properties")){
|
||||||
|
Properties p = new Properties();
|
||||||
|
p.load(is);
|
||||||
|
String host = p.getProperty("smtp.host");
|
||||||
|
String port = p.getProperty("smtp.port");
|
||||||
|
username = p.getProperty("smtp.username");
|
||||||
|
password = p.getProperty("smtp.password");
|
||||||
|
from = p.getProperty("smtp.from");
|
||||||
|
|
||||||
|
props.put("mail.smtp.auth", "true");
|
||||||
|
props.put("mail.smtp.starttls.enable", "true");
|
||||||
|
props.put("mail.smtp.host", host);
|
||||||
|
props.put("mail.smtp.port", port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendRegistrationCode(String toEmail, String code) throws MessagingException {
|
||||||
|
Session session = Session.getInstance(props, new Authenticator(){
|
||||||
|
protected PasswordAuthentication getPasswordAuthentication(){
|
||||||
|
return new PasswordAuthentication(username, password);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Message message = new MimeMessage(session);
|
||||||
|
message.setFrom(new InternetAddress(from));
|
||||||
|
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail));
|
||||||
|
message.setSubject("你的注册码");
|
||||||
|
message.setText("你的注册码是: " + code + "\n\n请输入此注册码完成注册。");
|
||||||
|
|
||||||
|
Transport.send(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,104 @@
|
|||||||
|
package com.example.myapp.service;
|
||||||
|
|
||||||
|
import com.example.myapp.model.User;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class FileStorageService {
|
||||||
|
private static final String DATA_DIR = "data";
|
||||||
|
private static final String USERS_FILE = DATA_DIR + "/users.json";
|
||||||
|
private static final String REGISTRATION_CODES_FILE = DATA_DIR + "/registration_codes.json";
|
||||||
|
private static final String RESET_PASSWORD_CODES_FILE = DATA_DIR + "/reset_password_codes.json";
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
public FileStorageService() {
|
||||||
|
this.objectMapper = new ObjectMapper();
|
||||||
|
objectMapper.registerModule(new JavaTimeModule());
|
||||||
|
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||||
|
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Files.createDirectories(Paths.get(DATA_DIR));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取用户数据
|
||||||
|
public Map<String, User> readUsersData() {
|
||||||
|
try {
|
||||||
|
File file = new File(USERS_FILE);
|
||||||
|
if (!file.exists()) {
|
||||||
|
return new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
return objectMapper.readValue(file, new TypeReference<ConcurrentHashMap<String, User>>() {});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取注册码数据
|
||||||
|
public Map<String, String> readRegistrationCodesData() {
|
||||||
|
try {
|
||||||
|
File file = new File(REGISTRATION_CODES_FILE);
|
||||||
|
if (!file.exists()) {
|
||||||
|
return new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
return objectMapper.readValue(file, new TypeReference<ConcurrentHashMap<String, String>>() {});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:读取重置密码码数据
|
||||||
|
public Map<String, String> readResetPasswordCodesData() {
|
||||||
|
try {
|
||||||
|
File file = new File(RESET_PASSWORD_CODES_FILE);
|
||||||
|
if (!file.exists()) {
|
||||||
|
return new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
return objectMapper.readValue(file, new TypeReference<ConcurrentHashMap<String, String>>() {});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入用户数据
|
||||||
|
public void writeUsersData(Map<String, User> data) {
|
||||||
|
try {
|
||||||
|
objectMapper.writeValue(new File(USERS_FILE), data);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入注册码数据
|
||||||
|
public void writeRegistrationCodesData(Map<String, String> data) {
|
||||||
|
try {
|
||||||
|
objectMapper.writeValue(new File(REGISTRATION_CODES_FILE), data);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:写入重置密码码数据
|
||||||
|
public void writeResetPasswordCodesData(Map<String, String> data) {
|
||||||
|
try {
|
||||||
|
objectMapper.writeValue(new File(RESET_PASSWORD_CODES_FILE), data);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,113 @@
|
|||||||
|
// MiddleQueSeting.java
|
||||||
|
package com.example.myapp.service;
|
||||||
|
|
||||||
|
import com.example.myapp.model.Expression;
|
||||||
|
|
||||||
|
public class MiddleQueSeting extends AbstractQuestionSeting {
|
||||||
|
|
||||||
|
public Expression applyUnary(Expression child, String op) {
|
||||||
|
switch (op) {
|
||||||
|
case "²":
|
||||||
|
if (child.mainOperator == null) {
|
||||||
|
return new Expression(child.expression + "²", child.value * child.value, "²");
|
||||||
|
}
|
||||||
|
return new Expression("(" + child.expression + ")²", child.value * child.value, "²");
|
||||||
|
case "√":
|
||||||
|
// 使用适合开方的数(主要是完全平方数)
|
||||||
|
String numStr = getNumberForSqrt();
|
||||||
|
child = new Expression(numStr, parseNumber(numStr), null);
|
||||||
|
|
||||||
|
double sqrtValue = Math.sqrt(child.value);
|
||||||
|
if (child.mainOperator == null) {
|
||||||
|
return new Expression("√" + child.expression, sqrtValue, "√");
|
||||||
|
}
|
||||||
|
return new Expression("√(" + child.expression + ")", sqrtValue, "√");
|
||||||
|
default:
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String addParenthesesIfNeeded(Expression child, String parentOp, boolean isRightChild) {
|
||||||
|
if (child.mainOperator == null || child.mainOperator.equals("²") || child.mainOperator.equals("√")) {
|
||||||
|
return child.expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parentPriority = getPriority(parentOp);
|
||||||
|
int childPriority = getPriority(child.mainOperator);
|
||||||
|
|
||||||
|
if (childPriority < parentPriority) {
|
||||||
|
return "(" + child.expression + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRightChild && (parentOp.equals("-") || parentOp.equals("/"))) {
|
||||||
|
if (parentPriority == childPriority) {
|
||||||
|
return "(" + child.expression + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return child.expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expression setQuestion(int count) {
|
||||||
|
Expression result = firstSetQuestion(count);
|
||||||
|
// 确保初中题目包含平方或开方
|
||||||
|
while (!result.expression.contains("²") && !result.expression.contains("√")) {
|
||||||
|
result = firstSetQuestion(count);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expression firstSetQuestion(int count) {
|
||||||
|
if (count == 1) {
|
||||||
|
String numStr = getRandomNumber();
|
||||||
|
Expression expr = new Expression(numStr, parseNumber(numStr), null);
|
||||||
|
return probability(expr);
|
||||||
|
}
|
||||||
|
int leftCount = 1 + rand.nextInt(count - 1);
|
||||||
|
int rightCount = count - leftCount;
|
||||||
|
Expression left = firstSetQuestion(leftCount);
|
||||||
|
Expression right = firstSetQuestion(rightCount);
|
||||||
|
String op = getRandomOperator();
|
||||||
|
double value = 0;
|
||||||
|
switch (op) {
|
||||||
|
case "+":
|
||||||
|
value = left.value + right.value;
|
||||||
|
break;
|
||||||
|
case "-":
|
||||||
|
value = left.value - right.value;
|
||||||
|
if (value < 0) {
|
||||||
|
Expression temp = left;
|
||||||
|
left = right;
|
||||||
|
right = temp;
|
||||||
|
value = left.value - right.value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "*":
|
||||||
|
value = left.value * right.value;
|
||||||
|
break;
|
||||||
|
case "/":
|
||||||
|
if (Math.abs(right.value) < 1e-10) {
|
||||||
|
right = firstSetQuestion(rightCount);
|
||||||
|
}
|
||||||
|
value = left.value / right.value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
String leftExpr = addParenthesesIfNeeded(left, op, false);
|
||||||
|
String rightExpr = addParenthesesIfNeeded(right, op, true);
|
||||||
|
Expression result = new Expression(leftExpr + " " + op + " " + rightExpr, value, op);
|
||||||
|
result = probability(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expression probability(Expression result) {
|
||||||
|
if (rand.nextDouble() < 0.3) {
|
||||||
|
String[] unaryOps = {"²", "√"};
|
||||||
|
String unaryOp = unaryOps[rand.nextInt(unaryOps.length)];
|
||||||
|
result = applyUnary(result, unaryOp);
|
||||||
|
result.mainOperator = unaryOp;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
// PrimaryQueSeting.java
|
||||||
|
package com.example.myapp.service;
|
||||||
|
|
||||||
|
import com.example.myapp.model.Expression;
|
||||||
|
|
||||||
|
public class PrimaryQueSeting extends AbstractQuestionSeting {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expression setQuestion(int count) {
|
||||||
|
if (count == 1) {
|
||||||
|
String expr = getRandomNumber();
|
||||||
|
return new Expression(expr, parseNumber(expr), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
int leftCount = 1 + rand.nextInt(count - 1);
|
||||||
|
int rightCount = count - leftCount;
|
||||||
|
Expression left = setQuestion(leftCount);
|
||||||
|
Expression right = setQuestion(rightCount);
|
||||||
|
|
||||||
|
String op = getRandomOperator();
|
||||||
|
|
||||||
|
// 处理除数为0的情况
|
||||||
|
while (op.equals("/") && Math.abs(right.value) < 1e-10) {
|
||||||
|
right = setQuestion(rightCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保减法结果不为负数
|
||||||
|
if (op.equals("-") && left.value < right.value) {
|
||||||
|
Expression temp = left;
|
||||||
|
left = right;
|
||||||
|
right = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
String leftExpr = addParenthesesIfNeeded(left, op, false);
|
||||||
|
String rightExpr = addParenthesesIfNeeded(right, op, true);
|
||||||
|
|
||||||
|
double value = switch (op) {
|
||||||
|
case "+" -> left.value + right.value;
|
||||||
|
case "-" -> left.value - right.value;
|
||||||
|
case "*" -> left.value * right.value;
|
||||||
|
case "/" -> left.value / right.value;
|
||||||
|
default -> 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Expression(leftExpr + " " + op + " " + rightExpr, value, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String addParenthesesIfNeeded(Expression child, String parentOp, boolean isRightChild) {
|
||||||
|
if (child.mainOperator == null) {
|
||||||
|
return child.expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parentPriority = getPriority(parentOp);
|
||||||
|
int childPriority = getPriority(child.mainOperator);
|
||||||
|
|
||||||
|
if (childPriority < parentPriority) {
|
||||||
|
return "(" + child.expression + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRightChild && (parentOp.equals("-") || parentOp.equals("/"))) {
|
||||||
|
if (parentPriority == childPriority) {
|
||||||
|
return "(" + child.expression + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return child.expression;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
// QueSetingFactory.java
|
||||||
|
package com.example.myapp.service;
|
||||||
|
|
||||||
|
public class QueSetingFactory {
|
||||||
|
public QuestionSeting getQueSeting(String type) {
|
||||||
|
switch (type) {
|
||||||
|
case "小学":
|
||||||
|
return new PrimaryQueSeting();
|
||||||
|
case "初中":
|
||||||
|
return new MiddleQueSeting();
|
||||||
|
case "高中":
|
||||||
|
return new HighQueSeting();
|
||||||
|
default:
|
||||||
|
System.out.println("类型错误");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
// QuestionSeting.java
|
||||||
|
package com.example.myapp.service;
|
||||||
|
|
||||||
|
import com.example.myapp.model.Expression;
|
||||||
|
|
||||||
|
public interface QuestionSeting {
|
||||||
|
Expression setQuestion(int count);
|
||||||
|
}
|
||||||
@ -0,0 +1,181 @@
|
|||||||
|
package com.example.myapp.service;
|
||||||
|
|
||||||
|
import com.example.myapp.model.User;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class UserService {
|
||||||
|
private Map<String, User> users;
|
||||||
|
private Map<String, String> usernameToEmail;
|
||||||
|
private Map<String, String> registrationCodes;
|
||||||
|
private Map<String, String> resetPasswordCodes;
|
||||||
|
|
||||||
|
private final FileStorageService fileStorageService;
|
||||||
|
private static final UserService instance = new UserService();
|
||||||
|
|
||||||
|
public static UserService getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserService() {
|
||||||
|
this.fileStorageService = new FileStorageService();
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadData() {
|
||||||
|
users = fileStorageService.readUsersData();
|
||||||
|
registrationCodes = fileStorageService.readRegistrationCodesData();
|
||||||
|
resetPasswordCodes = fileStorageService.readResetPasswordCodesData();
|
||||||
|
|
||||||
|
usernameToEmail = new ConcurrentHashMap<>();
|
||||||
|
if (users != null) {
|
||||||
|
for (User user : users.values()) {
|
||||||
|
if (user.getUsername() != null && !user.getUsername().trim().isEmpty()) {
|
||||||
|
usernameToEmail.put(user.getUsername().toLowerCase(), user.getEmail());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (users == null) users = new ConcurrentHashMap<>();
|
||||||
|
if (registrationCodes == null) registrationCodes = new ConcurrentHashMap<>();
|
||||||
|
if (resetPasswordCodes == null) resetPasswordCodes = new ConcurrentHashMap<>();
|
||||||
|
if (usernameToEmail == null) usernameToEmail = new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveUsers() {
|
||||||
|
fileStorageService.writeUsersData(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveRegistrationCodes() {
|
||||||
|
fileStorageService.writeRegistrationCodesData(registrationCodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveResetPasswordCodes() {
|
||||||
|
fileStorageService.writeResetPasswordCodesData(resetPasswordCodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean emailExists(String email) {
|
||||||
|
return users.containsKey(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean usernameExists(String username) {
|
||||||
|
return usernameToEmail.containsKey(username.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUserByIdentifier(String identifier) {
|
||||||
|
if (identifier.contains("@")) {
|
||||||
|
return users.get(identifier);
|
||||||
|
} else {
|
||||||
|
String email = usernameToEmail.get(identifier.toLowerCase());
|
||||||
|
return email != null ? users.get(email) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean userExists(String identifier) {
|
||||||
|
if (identifier.contains("@")) {
|
||||||
|
return emailExists(identifier);
|
||||||
|
} else {
|
||||||
|
return usernameExists(identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUserRegistered(String email) {
|
||||||
|
User user = users.get(email);
|
||||||
|
return user != null && user.getPasswordHash() != null && !user.getPasswordHash().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createPendingUser(String email, String code) {
|
||||||
|
if (users.containsKey(email)) {
|
||||||
|
User existingUser = users.get(email);
|
||||||
|
if (existingUser.getPasswordHash() == null || existingUser.getPasswordHash().isEmpty()) {
|
||||||
|
registrationCodes.put(email, code);
|
||||||
|
saveRegistrationCodes();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
users.putIfAbsent(email, new User(email, null));
|
||||||
|
registrationCodes.put(email, code);
|
||||||
|
saveUsers();
|
||||||
|
saveRegistrationCodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setUsername(String email, String username) {
|
||||||
|
if (username == null || username.trim().isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String usernameLower = username.toLowerCase();
|
||||||
|
if (usernameToEmail.containsKey(usernameLower)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
User user = users.get(email);
|
||||||
|
if (user != null) {
|
||||||
|
if (user.getUsername() != null) {
|
||||||
|
usernameToEmail.remove(user.getUsername().toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
user.setUsername(username);
|
||||||
|
usernameToEmail.put(usernameLower, email);
|
||||||
|
saveUsers();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean verifyCode(String email, String code) {
|
||||||
|
String storedCode = registrationCodes.get(email);
|
||||||
|
if (storedCode != null && storedCode.equals(code)) {
|
||||||
|
User u = users.get(email);
|
||||||
|
if (u != null) {
|
||||||
|
u.setVerified(true);
|
||||||
|
registrationCodes.remove(email);
|
||||||
|
saveUsers();
|
||||||
|
saveRegistrationCodes();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 忘记密码相关方法
|
||||||
|
public void createResetPasswordCode(String email, String code) {
|
||||||
|
resetPasswordCodes.put(email, code);
|
||||||
|
saveResetPasswordCodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean verifyResetPasswordCode(String email, String code) {
|
||||||
|
String storedCode = resetPasswordCodes.get(email);
|
||||||
|
if (storedCode != null && storedCode.equals(code)) {
|
||||||
|
resetPasswordCodes.remove(email);
|
||||||
|
saveResetPasswordCodes();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String email, String passwordHash) {
|
||||||
|
User u = users.get(email);
|
||||||
|
if (u != null) {
|
||||||
|
u.setPasswordHash(passwordHash);
|
||||||
|
u.setVerified(true);
|
||||||
|
saveUsers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkPassword(String identifier, String plain) {
|
||||||
|
User user = getUserByIdentifier(identifier);
|
||||||
|
if (user == null || user.getPasswordHash() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return com.example.myapp.util.PasswordUtil.check(plain, user.getPasswordHash());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName(String identifier) {
|
||||||
|
User user = getUserByIdentifier(identifier);
|
||||||
|
if (user == null) return identifier;
|
||||||
|
return user.getUsername() != null ? user.getUsername() : user.getEmail();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.example.myapp.util;
|
||||||
|
|
||||||
|
import org.mindrot.jbcrypt.BCrypt;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class PasswordUtil {
|
||||||
|
private static final Pattern validPattern = Pattern.compile("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{6,10}$");
|
||||||
|
|
||||||
|
public static boolean validatePasswordRules(String pwd){
|
||||||
|
if(pwd==null) return false;
|
||||||
|
return validPattern.matcher(pwd).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String hash(String plain){
|
||||||
|
return BCrypt.hashpw(plain, BCrypt.gensalt(12));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean check(String plain, String hash){
|
||||||
|
return BCrypt.checkpw(plain, hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in new issue