@ -0,0 +1,8 @@
|
||||
import ui.MainApplication;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
// R1: 启动图形化界面应用 (JavaFX)
|
||||
MainApplication.launch(MainApplication.class, args);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package auth;
|
||||
|
||||
public class User {
|
||||
private String username;
|
||||
private String password;
|
||||
private String type;
|
||||
private String email;
|
||||
|
||||
public User(String username, String password, String type, String email) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.type = type;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getUsername() { return username; }
|
||||
public String getPassword() { return password; }
|
||||
public String getType() { return type; }
|
||||
public String getEmail() { return email; }
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package auth;
|
||||
|
||||
public class UserManager {
|
||||
private final AuthService authService;
|
||||
|
||||
public UserManager() {
|
||||
this.authService = new AuthService();
|
||||
}
|
||||
|
||||
public User login(String username, String password) {
|
||||
return authService.login(username, password);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package generator;
|
||||
|
||||
import util.ExpressionUtils;
|
||||
import java.util.*;
|
||||
|
||||
public class HighGenerator implements ProblemGenerator {
|
||||
@Override
|
||||
public List<Problem> generateProblems(int count) {
|
||||
List<Problem> problems = new ArrayList<>();
|
||||
int attempts = 0;
|
||||
final int maxAttempts = count * 3; // 最多尝试3倍数量
|
||||
|
||||
while (problems.size() < count && attempts < maxAttempts) {
|
||||
Problem problem = ExpressionUtils.generateHighExpr();
|
||||
if (problem != null) {
|
||||
problems.add(problem);
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
|
||||
// 如果无法生成足够题目,用简单题目填充
|
||||
while (problems.size() < count) {
|
||||
problems.add(ExpressionUtils.createProblem("sin(30) + " + (problems.size() + 1)));
|
||||
}
|
||||
|
||||
return problems;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package generator;
|
||||
|
||||
import util.ExpressionUtils;
|
||||
import java.util.*;
|
||||
|
||||
public class MiddleGenerator implements ProblemGenerator {
|
||||
@Override
|
||||
public List<Problem> generateProblems(int count) {
|
||||
List<Problem> problems = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
problems.add(ExpressionUtils.generateMiddleExpr());
|
||||
}
|
||||
return problems;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package generator;
|
||||
|
||||
import util.ExpressionUtils;
|
||||
import java.util.*;
|
||||
|
||||
public class PrimaryGenerator implements ProblemGenerator {
|
||||
@Override
|
||||
public List<Problem> generateProblems(int count) {
|
||||
List<Problem> problems = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
problems.add( ExpressionUtils.generatePrimaryExpr());
|
||||
}
|
||||
return problems;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package generator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collections;
|
||||
|
||||
public class Problem {
|
||||
private final String expression;
|
||||
private final double result;
|
||||
private final List<String> options;
|
||||
private final String correctAnswerOption;
|
||||
|
||||
public Problem(String expression, double result, List<String> options,
|
||||
String correctAnswerOption) {
|
||||
this.expression = expression;
|
||||
this.result = result;
|
||||
this.options = options;
|
||||
this.correctAnswerOption = correctAnswerOption;
|
||||
}
|
||||
|
||||
public String getQuestionText() {
|
||||
return "计算: " + expression + " = ?";
|
||||
}
|
||||
|
||||
public String getExpression() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
public List<String> getOptions() {
|
||||
return Collections.unmodifiableList(options);
|
||||
}
|
||||
|
||||
public String getCorrectAnswerOption() {
|
||||
return formatResult(result);
|
||||
}
|
||||
|
||||
private String formatResult(double result) {
|
||||
if (Double.isNaN(result)) {
|
||||
return "Error";
|
||||
}
|
||||
if (Math.abs(result - Math.round(result)) < 0.0001) {
|
||||
return String.valueOf((int) Math.round(result));
|
||||
}
|
||||
return String.format("%.2f", result);
|
||||
}
|
||||
|
||||
public double getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getQuestionText()).append("\n");
|
||||
char optionChar = 'A';
|
||||
for (String option : options) {
|
||||
sb.append(optionChar++).append(". ").append(option).append(" ");
|
||||
}
|
||||
sb.append("\n正确答案: ").append(correctAnswerOption);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package generator;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ProblemGenerator {
|
||||
List<Problem> generateProblems(int count);
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
package service;
|
||||
|
||||
import auth.User;
|
||||
import generator.Problem;
|
||||
import generator.ProblemGenerator;
|
||||
import generator.PrimaryGenerator;
|
||||
import generator.MiddleGenerator;
|
||||
import generator.HighGenerator;
|
||||
import util.FileUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class ExamService {
|
||||
|
||||
public ProblemGenerator getGenerator(String type) {
|
||||
switch (type) {
|
||||
case "小学": return new PrimaryGenerator();
|
||||
case "初中": return new MiddleGenerator();
|
||||
case "高中": return new HighGenerator();
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Problem generateUniqueProblem(ProblemGenerator generator,
|
||||
Set<String> historyExpressions) {
|
||||
int attempts = 0;
|
||||
final int maxAttemptsPerProblem = 1000;
|
||||
|
||||
while (attempts < maxAttemptsPerProblem) {
|
||||
Problem problem = generator.generateProblems(1).get(0);
|
||||
// 查重基于题干 (表达式字符串)
|
||||
if (!historyExpressions.contains(problem.getExpression())) {
|
||||
historyExpressions.add(problem.getExpression());
|
||||
return problem;
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// R5: 生成一张唯一试卷,并将其保存到用户的历史记录中。
|
||||
public List<Problem> generateExam(User currentUser, int count) {
|
||||
if (count < 10 || count > 30) {
|
||||
throw new IllegalArgumentException("题目数量必须在 10 到 30 之间");
|
||||
}
|
||||
|
||||
ProblemGenerator generator = getGenerator(currentUser.getType());
|
||||
if (generator == null) {
|
||||
System.err.println("错误: 无法获取题目生成器,用户类型: " + currentUser.getType());
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Set<String> historyExpressions = FileUtils.loadHistory(currentUser.getUsername());
|
||||
List<Problem> problems = new ArrayList<>();
|
||||
|
||||
for(int i = 0; i < count; i++) {
|
||||
Problem problem = generateUniqueProblem(generator, historyExpressions);
|
||||
if (problem != null) {
|
||||
problems.add(problem);
|
||||
} else {
|
||||
System.err.println("警告: 无法生成足够数量的唯一题目。已生成: " + problems.size() + " 题");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!problems.isEmpty()) {
|
||||
FileUtils.saveProblems(currentUser.getUsername(), problems);
|
||||
} else {
|
||||
System.err.println("错误: 未能生成任何题目");
|
||||
}
|
||||
|
||||
return problems;
|
||||
}
|
||||
|
||||
// 将这个私有方法改为公共方法,以便在其他类中调用
|
||||
public boolean isAnswerCorrect(Problem problem, String userAnswer) {
|
||||
if (userAnswer == null || problem.getCorrectAnswerOption() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isCorrect = userAnswer.trim().equalsIgnoreCase(problem.getCorrectAnswerOption().trim());
|
||||
return isCorrect;
|
||||
}
|
||||
|
||||
// R6: 根据答对的百分比计算分数。
|
||||
public int calculateScore(List<Problem> problems, List<String> answers) {
|
||||
if (problems == null || answers == null ||
|
||||
problems.size() != answers.size() ||
|
||||
problems.isEmpty()) {
|
||||
System.err.println("评分错误: 题目和答案数量不匹配");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int correctCount = 0;
|
||||
for (int i = 0; i < problems.size(); i++) {
|
||||
Problem problem = problems.get(i);
|
||||
String userAnswer = answers.get(i);
|
||||
|
||||
if (isAnswerCorrect(problem, userAnswer)) {
|
||||
correctCount++;
|
||||
}
|
||||
}
|
||||
|
||||
double score = ((double) correctCount / problems.size()) * 100;
|
||||
int finalScore = (int) Math.round(score);
|
||||
return finalScore;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
package util;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import javax.mail.*;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
public class EmailService {
|
||||
// 邮件配置:请修改此处或设置环境变量
|
||||
private static final String SENDER_EMAIL = "2810672597@qq.com";
|
||||
private static final String SENDER_PASSWORD =("acyhhpwgcqkqdgff");
|
||||
private static final String SMTP_HOST = "smtp.qq.com"; // 例如: smtp.gmail.com
|
||||
private static final String SMTP_PORT = "587"; // 或 465 (SSL)
|
||||
private static final String SUBJECT = "数学学习软件注册验证码";
|
||||
|
||||
public String generateVerificationCode() {
|
||||
Random rand = new Random();
|
||||
return String.format("%06d", rand.nextInt(1000000));
|
||||
}
|
||||
|
||||
private Properties getMailProperties() {
|
||||
Properties props = new Properties();
|
||||
props.put("mail.smtp.auth", "true");
|
||||
props.put("mail.smtp.starttls.enable", "true");
|
||||
props.put("mail.smtp.host", SMTP_HOST);
|
||||
props.put("mail.smtp.port", SMTP_PORT);
|
||||
return props;
|
||||
}
|
||||
|
||||
private Message createMessage(Session session, String recipientEmail,
|
||||
String code) throws MessagingException {
|
||||
Message message = new MimeMessage(session);
|
||||
message.setFrom(new InternetAddress(SENDER_EMAIL));
|
||||
message.setRecipients(
|
||||
Message.RecipientType.TO,
|
||||
InternetAddress.parse(recipientEmail)
|
||||
);
|
||||
message.setSubject(SUBJECT);
|
||||
message.setText("您的注册验证码是: " + code +
|
||||
"\n\n请在应用中输入此代码完成注册。");
|
||||
return message;
|
||||
}
|
||||
|
||||
// R2: 实际发送验证邮件
|
||||
public boolean sendVerificationEmail(String recipientEmail, String code) {
|
||||
if (SENDER_EMAIL == null || SENDER_PASSWORD == null) {
|
||||
System.err.println("错误: 邮件发送凭证未设置。");
|
||||
return false;
|
||||
}
|
||||
|
||||
Session session = Session.getInstance(getMailProperties(), new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(SENDER_EMAIL, SENDER_PASSWORD);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
Message message = createMessage(session, recipientEmail, code);
|
||||
Transport.send(message);
|
||||
return true;
|
||||
|
||||
} catch (MessagingException e) {
|
||||
System.err.println("邮件发送失败: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
package util;
|
||||
|
||||
import generator.Problem;
|
||||
import java.io.*;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
public class FileUtils {
|
||||
private static final String FOLDER_PATH = "exams/";
|
||||
|
||||
public static String getUserFolder(String username) {
|
||||
String folder = FOLDER_PATH + username;
|
||||
File dir = new File(folder);
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
return folder;
|
||||
}
|
||||
|
||||
private static void processHistoryFile(File file, Set<String> history) {
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (!line.trim().isEmpty() && line.matches("^\\d+\\..*")) {
|
||||
// 仅提取题干 (表达式字符串) 用于查重
|
||||
history.add(line.substring(line.indexOf('.') + 1).trim());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<String> loadHistory(String username) {
|
||||
Set<String> history = new HashSet<>();
|
||||
File dir = new File(getUserFolder(username));
|
||||
File[] files = dir.listFiles((d, name) -> name.endsWith(".txt"));
|
||||
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
processHistoryFile(file, history);
|
||||
}
|
||||
}
|
||||
return history;
|
||||
}
|
||||
|
||||
private static void writeProblem(BufferedWriter bw, int index, Problem problem) throws IOException {
|
||||
bw.write(index + "." + problem.getExpression());
|
||||
bw.newLine();
|
||||
|
||||
char optionChar = 'A';
|
||||
StringBuilder optionsLine = new StringBuilder();
|
||||
for (String option : problem.getOptions()) {
|
||||
optionsLine.append(optionChar++).append(". ").append(option).append(" ");
|
||||
}
|
||||
|
||||
bw.write(optionsLine.toString().trim());
|
||||
bw.newLine();
|
||||
bw.write("答案: " + problem.getCorrectAnswerOption());
|
||||
bw.newLine();
|
||||
bw.newLine();
|
||||
}
|
||||
|
||||
public static void saveProblems(String username, List<Problem> problems) {
|
||||
String folder = getUserFolder(username);
|
||||
String timestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
|
||||
String filename = folder + "/" + timestamp + ".txt";
|
||||
|
||||
try (BufferedWriter bw = new BufferedWriter(new FileWriter(filename))) {
|
||||
int index = 1;
|
||||
for (Problem problem : problems) {
|
||||
writeProblem(bw, index++, problem);
|
||||
}
|
||||
System.out.println("卷子已保存到: " + filename);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
package util;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility class to modify specific fields in user.txt.
|
||||
* 替换了 ModifyClass.java 和 ModifyPassword.java。
|
||||
*/
|
||||
public class ModifyUtils {
|
||||
|
||||
private static List<String> readAllLines(String filePath, List<String> lines) throws IOException {
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
lines.add(line);
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
private static void writeAllLines(String filePath, List<String> lines) throws IOException {
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
|
||||
for (String l : lines) {
|
||||
writer.write(l + System.lineSeparator());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int findTargetLine(List<String> lines, String targetName) {
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
String line = lines.get(i).trim();
|
||||
if (line.isEmpty()) continue;
|
||||
|
||||
String[] parts = line.split("\\s+");
|
||||
if (parts.length > 0 && parts[0].equals(targetName)) {
|
||||
return i; // 0-based index
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 核心逻辑:修改文件中特定用户的某个字段 (0-based)
|
||||
public static int modifyFileField(String filePath, String targetName,
|
||||
int fieldIndex, String newValue) {
|
||||
List<String> lines = new ArrayList<>();
|
||||
try {
|
||||
readAllLines(filePath, lines);
|
||||
} catch (IOException e) {
|
||||
System.err.println("读取文件错误: " + e.getMessage());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int idx = findTargetLine(lines, targetName);
|
||||
if (idx == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
String[] parts = lines.get(idx).trim().split("\\s+");
|
||||
if (parts.length > fieldIndex) {
|
||||
parts[fieldIndex] = newValue;
|
||||
lines.set(idx, String.join(" ", parts));
|
||||
try {
|
||||
writeAllLines(filePath, lines);
|
||||
return idx + 1; // 返回 1-based 行号
|
||||
} catch (IOException e) {
|
||||
System.err.println("写入文件错误: " + e.getMessage());
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
System.out.println("目标行元素不足。");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue