Compare commits
No commits in common. 'develop' and 'fanwen_branch' have entirely different histories.
develop
...
fanwen_bra
@ -1,103 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,92 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,142 +0,0 @@
|
|||||||
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秒后才能重新发送
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
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; }
|
|
||||||
}
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
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; }
|
|
||||||
}
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
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 + "}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,113 +0,0 @@
|
|||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
// QuestionSeting.java
|
|
||||||
package com.example.myapp.service;
|
|
||||||
|
|
||||||
import com.example.myapp.model.Expression;
|
|
||||||
|
|
||||||
public interface QuestionSeting {
|
|
||||||
Expression setQuestion(int count);
|
|
||||||
}
|
|
||||||
@ -1,181 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
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