Compare commits
No commits in common. 'main' and 'maochengshang_branch' have entirely different histories.
main
...
maochengsh
Binary file not shown.
Binary file not shown.
@ -1,86 +0,0 @@
|
|||||||
package Base;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
public class Email_settings {
|
|
||||||
private static final String CONFIG_FILE = "config/email.properties";
|
|
||||||
private static Properties properties;
|
|
||||||
private static long lastModified = 0;
|
|
||||||
|
|
||||||
static {
|
|
||||||
loadConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void loadConfig() {
|
|
||||||
properties = new Properties();
|
|
||||||
setDefaultProperties();
|
|
||||||
File configFile = new File(CONFIG_FILE);
|
|
||||||
if (configFile.exists()) {
|
|
||||||
try (FileInputStream input = new FileInputStream(configFile)) {
|
|
||||||
properties.load(input);
|
|
||||||
lastModified = configFile.lastModified();
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.err.println("加载邮箱配置文件失败,使用默认配置: " + e.getMessage());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
createDefaultConfigFile();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void setDefaultProperties() {
|
|
||||||
properties.setProperty("smtp.host", "smtp.qq.com");
|
|
||||||
properties.setProperty("smtp.port", "587");
|
|
||||||
properties.setProperty("from.email", "835981889@qq.com");
|
|
||||||
properties.setProperty("email.password", "fpqfprqznbvdbcdf");
|
|
||||||
properties.setProperty("ssl.enable", "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void createDefaultConfigFile() {
|
|
||||||
File configDir = new File("config");
|
|
||||||
if (!configDir.exists()) {
|
|
||||||
configDir.mkdirs();
|
|
||||||
}
|
|
||||||
|
|
||||||
try (FileOutputStream output = new FileOutputStream(CONFIG_FILE)) {
|
|
||||||
properties.store(output, "Settings");
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.err.println("创建默认配置文件失败: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkForUpdates() {
|
|
||||||
File configFile = new File(CONFIG_FILE);
|
|
||||||
if (configFile.exists() && configFile.lastModified() > lastModified) {
|
|
||||||
loadConfig();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getSmtpHost() {
|
|
||||||
checkForUpdates();
|
|
||||||
return properties.getProperty("smtp.host");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getSmtpPort() {
|
|
||||||
checkForUpdates();
|
|
||||||
return properties.getProperty("smtp.port");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getFromEmail() {
|
|
||||||
checkForUpdates();
|
|
||||||
return properties.getProperty("from.email");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getEmailPassword() {
|
|
||||||
checkForUpdates();
|
|
||||||
return properties.getProperty("email.password");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isSslEnable() {
|
|
||||||
checkForUpdates();
|
|
||||||
return Boolean.parseBoolean(properties.getProperty("ssl.enable"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
package Base;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class Exam_result {
|
|
||||||
private String exam_type;
|
|
||||||
private int total_questions;
|
|
||||||
private int correct_answers;
|
|
||||||
private double score;
|
|
||||||
private long duration; // 考试时长(秒)
|
|
||||||
private List<Integer> wrong_questions; // 错题索引
|
|
||||||
|
|
||||||
public Exam_result( String examType, int total,
|
|
||||||
int correct, double score, long duration,
|
|
||||||
List<Integer> wrong) {
|
|
||||||
this.exam_type = examType;
|
|
||||||
this.total_questions = total;
|
|
||||||
this.correct_answers = correct;
|
|
||||||
this.score = Math.round(score * 100.0) / 100.0;
|
|
||||||
this.duration = duration;
|
|
||||||
this.wrong_questions = wrong;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getExamType() { return exam_type; }
|
|
||||||
public int getTotalQuestions() { return total_questions; }
|
|
||||||
public int getCorrectAnswers() { return correct_answers; }
|
|
||||||
public double getScore() { return score; }
|
|
||||||
public long getDuration() { return duration; }
|
|
||||||
public List<Integer> getWrongQuestions() { return wrong_questions; }
|
|
||||||
|
|
||||||
public String get_time() {
|
|
||||||
long minutes = duration / 60;
|
|
||||||
long seconds = duration % 60;
|
|
||||||
return String.format("%d分%d秒", minutes, seconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCorrectRate() {
|
|
||||||
return String.format("%.1f%%", (double) correct_answers / total_questions * 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,267 +0,0 @@
|
|||||||
package Base;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
public class Question {
|
|
||||||
private int number;
|
|
||||||
private String content;
|
|
||||||
private String type;
|
|
||||||
private String answer;
|
|
||||||
private String[] options;
|
|
||||||
private final Random ra= new Random();
|
|
||||||
public static final HashMap<String, String> trigValues = new HashMap<>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
// 15°
|
|
||||||
trigValues.put("sin(15°)", "√2*(√3-1)/4");
|
|
||||||
trigValues.put("cos(15°)", "√2*(√3+1)/4");
|
|
||||||
trigValues.put("tan(15°)", "(2-√3)");
|
|
||||||
trigValues.put("sin²(15°)", "(2-√3)/4");
|
|
||||||
trigValues.put("cos²(15°)", "(2+√3)/4");
|
|
||||||
trigValues.put("tan²(15°)", "(7-4*√3)");
|
|
||||||
|
|
||||||
// 22.5°
|
|
||||||
trigValues.put("sin(22.5°)", "√0.59/2"); //√(2-√2)/2
|
|
||||||
trigValues.put("cos(22.5°)", "√3.41/2"); //√(2+√2)/2
|
|
||||||
trigValues.put("tan(22.5°)", "(√2-1)");
|
|
||||||
trigValues.put("sin²(22.5°)", "(2-√2)/4");
|
|
||||||
trigValues.put("cos²(22.5°)", "(2+√2)/4");
|
|
||||||
trigValues.put("tan²(22.5°)", "(3-2*√2)");
|
|
||||||
|
|
||||||
// 75°
|
|
||||||
trigValues.put("sin(75°)", "√2*(√3+1)/4");
|
|
||||||
trigValues.put("cos(75°)", "√2*(√3-1)/4");
|
|
||||||
trigValues.put("tan(75°)", "(2+√3)");
|
|
||||||
trigValues.put("sin²(75°)", "(2+√3)/4");
|
|
||||||
trigValues.put("cos²(75°)", "(2-√3)/4");
|
|
||||||
trigValues.put("tan²(75°)", "(7+4*√3)");
|
|
||||||
|
|
||||||
// 105°
|
|
||||||
trigValues.put("sin(105°)", "√2*(√3+1)/4");
|
|
||||||
trigValues.put("cos(105°)", "√2*(1-√3)/4");
|
|
||||||
trigValues.put("tan(105°)", "(0-(2+√3))");
|
|
||||||
trigValues.put("sin²(105°)", "(2+√3)/4");
|
|
||||||
trigValues.put("cos²(105°)", "(2-√3)/4");
|
|
||||||
trigValues.put("tan²(105°)", "(7+4*√3)");
|
|
||||||
|
|
||||||
// 120°
|
|
||||||
trigValues.put("sin(120°)", "√3/2");
|
|
||||||
trigValues.put("cos(120°)", "(0-1/2)");
|
|
||||||
trigValues.put("tan(120°)", "(0-√3)");
|
|
||||||
trigValues.put("sin²(120°)", "3/4");
|
|
||||||
trigValues.put("cos²(120°)", "1/4");
|
|
||||||
trigValues.put("tan²(120°)", "3");
|
|
||||||
|
|
||||||
// 135°
|
|
||||||
trigValues.put("sin(135°)", "√2/2");
|
|
||||||
trigValues.put("cos(135°)", "(0-√2/2)");
|
|
||||||
trigValues.put("tan(135°)", "(0-1)");
|
|
||||||
trigValues.put("sin²(135°)", "1/2");
|
|
||||||
trigValues.put("cos²(135°)", "1/2");
|
|
||||||
trigValues.put("tan²(135°)", "1");
|
|
||||||
|
|
||||||
// 150°
|
|
||||||
trigValues.put("sin(150°)", "1/2");
|
|
||||||
trigValues.put("cos(150°)", "(0-√3/2)");
|
|
||||||
trigValues.put("tan(150°)", "(0-√3/3)");
|
|
||||||
trigValues.put("sin²(150°)", "1/4");
|
|
||||||
trigValues.put("cos²(150°)", "3/4");
|
|
||||||
trigValues.put("tan²(150°)", "1/3");
|
|
||||||
|
|
||||||
// 210°
|
|
||||||
trigValues.put("sin(210°)", "(0-1/2)");
|
|
||||||
trigValues.put("cos(210°)", "(0-√3/2)");
|
|
||||||
trigValues.put("tan(210°)", "√3/3");
|
|
||||||
trigValues.put("sin²(210°)", "1/4");
|
|
||||||
trigValues.put("cos²(210°)", "3/4");
|
|
||||||
trigValues.put("tan²(210°)", "1/3");
|
|
||||||
|
|
||||||
// 300°
|
|
||||||
trigValues.put("sin(300°)", "(0-√3/2)");
|
|
||||||
trigValues.put("cos(300°)", "1/2");
|
|
||||||
trigValues.put("tan(300°)", "(0-√3)");
|
|
||||||
trigValues.put("sin²(300°)", "3/4");
|
|
||||||
trigValues.put("cos²(300°)", "1/4");
|
|
||||||
trigValues.put("tan²(300°)", "3");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Question(int number, String content, String type) {
|
|
||||||
this.number = number;
|
|
||||||
this.content = content;
|
|
||||||
this.type = type;
|
|
||||||
options=new String[4];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNumber() { return number; }
|
|
||||||
public String getContent() { return content; }
|
|
||||||
public String getType() { return type; }
|
|
||||||
public String getAnswer() {return answer;}
|
|
||||||
public String getOptions(int i) {return options[i];}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(){
|
|
||||||
return this.number+". "+this.content+" = ?";
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set_options(){
|
|
||||||
answer=calculate(this.content);
|
|
||||||
int which=ra.nextInt(4);
|
|
||||||
options[which]=answer;
|
|
||||||
for (int i=0;i<4;i++) {
|
|
||||||
if (i != which) {
|
|
||||||
if (this.type.equals("高中")) {
|
|
||||||
options[i] = answer.equals("不可解") ? String.format("%.2f", Math.sqrt(3) * ra.nextInt(10) + ra.nextDouble(1)) :
|
|
||||||
String.format("%.2f", Double.parseDouble(answer) + Math.sqrt(2) * ra.nextInt(5));
|
|
||||||
} else {
|
|
||||||
if (Double.parseDouble(answer) ==Math.floor(Double.parseDouble(answer)) ) {
|
|
||||||
options[i] = answer.equals("不可解") ? String.valueOf((int) ra.nextInt(1000) + ra.nextInt(10)) :
|
|
||||||
String.valueOf((int) Double.parseDouble(answer) + ra.nextInt(10));
|
|
||||||
} else {
|
|
||||||
options[i] = answer.equals("不可解") ? String.format("%.2f", ra.nextInt(1000) + ra.nextDouble(10)) :
|
|
||||||
String.format("%.2f", Double.parseDouble(answer) + ra.nextDouble(10));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int j = 0; j < i; j++) {
|
|
||||||
if (options[j].equals(options[i])) {
|
|
||||||
i--;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (options[i].equals(answer)) {
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String calculate(String question){
|
|
||||||
try {
|
|
||||||
String expr = question.replaceAll(" ", "");
|
|
||||||
double result;
|
|
||||||
if (!type.equals("高中")) {
|
|
||||||
result = deal_calculate(expr);
|
|
||||||
if (Double.isNaN(result) || Double.isInfinite(result)) {
|
|
||||||
return "不可解";
|
|
||||||
}
|
|
||||||
if (result == Math.floor(result)) {
|
|
||||||
return String.valueOf((int) result);
|
|
||||||
} else {
|
|
||||||
return String.format("%.2f", result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return deal_sen_calculate(expr);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
return "不可解";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private double deal_calculate(String expr){
|
|
||||||
Stack<Double> numbers = new Stack<>();
|
|
||||||
Stack<Character> operators = new Stack<>();
|
|
||||||
expr=expr.replace("²", "^2");
|
|
||||||
for (int i = 0; i < expr.length(); i++) {
|
|
||||||
char c = expr.charAt(i);
|
|
||||||
if (Character.isDigit(c)) {
|
|
||||||
StringBuilder temp = new StringBuilder();
|
|
||||||
while (i < expr.length() && (Character.isDigit(expr.charAt(i)) || expr.charAt(i) == '.')) {
|
|
||||||
temp.append(expr.charAt(i++));
|
|
||||||
}
|
|
||||||
i--;
|
|
||||||
numbers.push(Double.parseDouble(temp.toString()));
|
|
||||||
} else if (c == '(') {
|
|
||||||
operators.push(c);
|
|
||||||
} else if (c == ')') {
|
|
||||||
while (operators.peek() != '(') {
|
|
||||||
numbers.push(Deal_Operator(operators.pop(), numbers.pop(), numbers.pop()));
|
|
||||||
}
|
|
||||||
operators.pop();
|
|
||||||
} else if (isOperator(c)) {
|
|
||||||
while (!operators.isEmpty() && hasPrecedence(c, operators.peek())) {
|
|
||||||
numbers.push(Deal_Operator(operators.pop(), numbers.pop(), numbers.pop()));
|
|
||||||
}
|
|
||||||
operators.push(c);
|
|
||||||
}
|
|
||||||
else if (c == '√'){
|
|
||||||
i++;
|
|
||||||
StringBuilder temp = new StringBuilder();
|
|
||||||
while (i < expr.length() && (Character.isDigit(expr.charAt(i)) || expr.charAt(i) == '.')) {
|
|
||||||
temp.append(expr.charAt(i++));
|
|
||||||
}
|
|
||||||
i--;
|
|
||||||
numbers.push(Math.sqrt(Double.parseDouble(temp.toString())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (!operators.isEmpty()) {
|
|
||||||
numbers.push(Deal_Operator(operators.pop(), numbers.pop(), numbers.pop()));
|
|
||||||
}
|
|
||||||
return numbers.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String deal_sen_calculate(String m_expr){
|
|
||||||
try {
|
|
||||||
String expr = m_expr.replaceAll(" ", "");
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
while (i < expr.length()) {
|
|
||||||
char c = expr.charAt(i);
|
|
||||||
if (c == 's' || c == 'c' || c == 't') {
|
|
||||||
StringBuilder trigFunc = new StringBuilder();
|
|
||||||
while (i < expr.length() && expr.charAt(i) != ')') {
|
|
||||||
trigFunc.append(expr.charAt(i++));
|
|
||||||
}
|
|
||||||
trigFunc.append(')'); // 添加右括号
|
|
||||||
String trigKey = trigFunc.toString();
|
|
||||||
String trigValue = trigValues.get(trigKey);
|
|
||||||
if (trigValue != null) {
|
|
||||||
result.append(trigValue);
|
|
||||||
}
|
|
||||||
} else if (isOperator(c) || c == '(' || c == ')') {
|
|
||||||
result.append(c);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return String.format("%.2f",deal_calculate(Deal_Expression(result.toString())));
|
|
||||||
} catch (Exception e) {
|
|
||||||
return "不可解";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private double Deal_Operator(char operator, double b, double a) {
|
|
||||||
switch (operator) {
|
|
||||||
case '+': return a + b;
|
|
||||||
case '-': return a - b;
|
|
||||||
case '*': return a * b;
|
|
||||||
case '/': {
|
|
||||||
if (b == 0) throw new ArithmeticException("除零错误");
|
|
||||||
return a / b;
|
|
||||||
}
|
|
||||||
case '^': return Math.pow(a, b);
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOperator(char c) {
|
|
||||||
return c == '+' || c == '-' || c == '*' || c == '/' || c == '^';
|
|
||||||
}
|
|
||||||
|
|
||||||
//检验运算优先级,来决定先计算再压栈还是直接压栈
|
|
||||||
private boolean hasPrecedence(char op1, char op2) {
|
|
||||||
if ( (op2 == '(' || op2 == ')')
|
|
||||||
|| ((op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-'))
|
|
||||||
|| (op1 == '^' && (op2 == '*' || op2 == '/' || op2 == '+' || op2 == '-'))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String Deal_Expression(String expr) {
|
|
||||||
String simplified = expr.replace("+-","-");
|
|
||||||
simplified = simplified.replace("--","+");
|
|
||||||
return simplified;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
package Base;
|
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
public class User {
|
|
||||||
private String email;
|
|
||||||
private String id;
|
|
||||||
private String password;
|
|
||||||
|
|
||||||
public User(String email) {
|
|
||||||
this.email = email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String get_email() { return email; }
|
|
||||||
public String get_password() { return password; }
|
|
||||||
public String get_id() {return id;}
|
|
||||||
public void set_id(String id) {this.id=id;}
|
|
||||||
public void set_password(String password) { this.password = password; }
|
|
||||||
|
|
||||||
public static boolean check_password(String password) {
|
|
||||||
if (password == null || password.length() < 6 || password.length() > 20) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasUpper = false;
|
|
||||||
boolean hasLower = false;
|
|
||||||
boolean hasDigit = false;
|
|
||||||
|
|
||||||
for (char c : password.toCharArray()) {
|
|
||||||
if (Character.isUpperCase(c)) hasUpper = true;
|
|
||||||
if (Character.isLowerCase(c)) hasLower = true;
|
|
||||||
if (Character.isDigit(c)) hasDigit = true;
|
|
||||||
}
|
|
||||||
return hasUpper && hasLower && hasDigit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String hash_pwd(String password){
|
|
||||||
try {
|
|
||||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
|
||||||
byte[] hashBytes = digest.digest(password.getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (byte b : hashBytes) {
|
|
||||||
sb.append(String.format("%02x", b));
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException("无法创建SHA-256实例", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean check_hash_pwd(String pwd,String hash_pwd){
|
|
||||||
return hash_pwd(pwd).equals(hash_pwd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
package Generator;
|
|
||||||
|
|
||||||
public interface G_ques {
|
|
||||||
String g_question();
|
|
||||||
String g_type();
|
|
||||||
char g_operator();
|
|
||||||
String add_brackets(StringBuilder s,int count);
|
|
||||||
}
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
package Generator;
|
|
||||||
|
|
||||||
import Base.Question;
|
|
||||||
import Service.Deal_file;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class Generate_paper {
|
|
||||||
public static ArrayList<Question> g_paper(int num,String type,String id) {
|
|
||||||
ArrayList<Question> result = new ArrayList<>();
|
|
||||||
G_ques generator;
|
|
||||||
switch (type){
|
|
||||||
case "小学":{
|
|
||||||
generator=new Pri_g_ques();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "初中":{
|
|
||||||
generator=new Jun_g_ques();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "高中":{
|
|
||||||
generator=new Sen_g_ques();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:{
|
|
||||||
generator=new Pri_g_ques();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i=0;i<num;i++){
|
|
||||||
String temp;
|
|
||||||
int try_times = 0;
|
|
||||||
do {
|
|
||||||
temp = generator.g_question();
|
|
||||||
try_times++;
|
|
||||||
} while (check_repetition(result,temp) && try_times <= 50);
|
|
||||||
Question t=new Question(i+1,temp,generator.g_type());
|
|
||||||
t.set_options();
|
|
||||||
result.add(t);
|
|
||||||
}
|
|
||||||
/*Deal_file d=new Deal_file();
|
|
||||||
d.savePaper(result,id);*/
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean check_repetition(ArrayList<Question> all,String ques){
|
|
||||||
for (Question q:all){
|
|
||||||
if (q.getContent().equals(ques)){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,82 +0,0 @@
|
|||||||
package Generator;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
public class Jun_g_ques implements G_ques{
|
|
||||||
private Random ra= new Random();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String g_question() {
|
|
||||||
int count=ra.nextInt(5)+1;
|
|
||||||
StringBuilder question = new StringBuilder();
|
|
||||||
boolean flag=false;
|
|
||||||
for (int i=0;i<count;i++){
|
|
||||||
if (i>0){
|
|
||||||
question.append(" ").append(g_operator()).append(" ");
|
|
||||||
}
|
|
||||||
if (ra.nextDouble()<0.25) {
|
|
||||||
if (ra.nextBoolean()) {
|
|
||||||
question.append(ra.nextInt(30) + 1).append("²");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
question.append("√").append(ra.nextInt(100) + 1);
|
|
||||||
}
|
|
||||||
flag=true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
question.append(ra.nextInt(100) + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!flag){
|
|
||||||
if(count==1){
|
|
||||||
if (ra.nextBoolean()){
|
|
||||||
question.append("²");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
question.insert(0,"√");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
String[] parts = question.toString().split(" ");
|
|
||||||
int pos = ra.nextInt(count);
|
|
||||||
if (ra.nextBoolean()) {
|
|
||||||
parts[pos * 2] = parts[pos * 2] + "²";
|
|
||||||
} else {
|
|
||||||
parts[pos * 2] = "√" + parts[pos * 2];
|
|
||||||
}
|
|
||||||
question = new StringBuilder(String.join(" ", parts));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return add_brackets(question,count);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String g_type(){
|
|
||||||
return "初中";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char g_operator(){
|
|
||||||
char[] op={'+','-','*','/'};
|
|
||||||
return op[ra.nextInt(op.length)];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String add_brackets(StringBuilder s,int count){
|
|
||||||
String res=s.toString();
|
|
||||||
String[] parts=s.toString().split(" ");
|
|
||||||
if (ra.nextBoolean()&&parts.length!=1) {
|
|
||||||
int num=ra.nextInt(3)+1;
|
|
||||||
for (int i=0;i<num;i++) {
|
|
||||||
int pos = ra.nextInt(count);
|
|
||||||
if (pos==count-1){
|
|
||||||
pos=pos-1;
|
|
||||||
}
|
|
||||||
parts[pos * 2] = "(" + parts[pos * 2];
|
|
||||||
parts[parts.length-1]=parts[parts.length-1]+")";
|
|
||||||
}
|
|
||||||
res = String.join(" ", parts);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
package Generator;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
public class Pri_g_ques implements G_ques{
|
|
||||||
private Random ra= new Random();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String g_question() {
|
|
||||||
int count=ra.nextInt(4)+2;
|
|
||||||
StringBuilder question = new StringBuilder();
|
|
||||||
int count_bracket=0;
|
|
||||||
for (int i=0;i<count;i++) {
|
|
||||||
if (i > 0) {
|
|
||||||
question.append(" ").append(g_operator()).append(" ");
|
|
||||||
}
|
|
||||||
if (i!=count-1&&ra.nextDouble()<0.4){
|
|
||||||
question.append("(");
|
|
||||||
count_bracket++;
|
|
||||||
}
|
|
||||||
question.append(ra.nextInt(100) + 1);
|
|
||||||
}
|
|
||||||
if (count_bracket!=0){
|
|
||||||
question.append(")".repeat(Math.max(0, count_bracket)));
|
|
||||||
}
|
|
||||||
return question.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String g_type(){
|
|
||||||
return "小学";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char g_operator(){
|
|
||||||
char[] op={'+','-','*','/'};
|
|
||||||
return op[ra.nextInt(op.length)];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String add_brackets(StringBuilder s,int count){
|
|
||||||
return s.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
package Generator;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
public class Sen_g_ques implements G_ques{
|
|
||||||
private Random ra= new Random();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String g_question() {
|
|
||||||
int count=ra.nextInt(5)+1;
|
|
||||||
StringBuilder question = new StringBuilder();
|
|
||||||
for (int i=0;i<count;i++) {
|
|
||||||
if (i > 0) {
|
|
||||||
question.append(" ").append(g_operator()).append(" ");
|
|
||||||
}
|
|
||||||
if (ra.nextDouble()<0.3){
|
|
||||||
question.append(g_trig()).append("²").append("(").append(g_angle()).append("°)");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
question.append(g_trig()).append("(").append(g_angle()).append("°)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return add_brackets(question,count);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String g_type(){
|
|
||||||
return "高中";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char g_operator(){
|
|
||||||
char[] op={'+','-','*','/'};
|
|
||||||
return op[ra.nextInt(op.length)];
|
|
||||||
}
|
|
||||||
|
|
||||||
public String g_trig(){
|
|
||||||
String[] trig={"sin","cos","tan"};
|
|
||||||
return trig[ra.nextInt(trig.length)];
|
|
||||||
}
|
|
||||||
|
|
||||||
public String g_angle(){
|
|
||||||
String[] angle={"15","22.5","75","105","120","135","150","210","300"};
|
|
||||||
return angle[ra.nextInt(angle.length)];
|
|
||||||
}
|
|
||||||
|
|
||||||
public String add_brackets(StringBuilder s,int count){
|
|
||||||
String res=s.toString();
|
|
||||||
String[] parts=s.toString().split(" ");
|
|
||||||
if (ra.nextBoolean()&&parts.length!=1) {
|
|
||||||
int num=ra.nextInt(3)+1;
|
|
||||||
for (int i=0;i<num;i++) {
|
|
||||||
int pos = ra.nextInt(count);
|
|
||||||
if (pos==count-1){
|
|
||||||
pos=pos-1;
|
|
||||||
}
|
|
||||||
parts[pos * 2] = "(" + parts[pos * 2];
|
|
||||||
parts[parts.length-1]=parts[parts.length-1]+")";
|
|
||||||
}
|
|
||||||
res = String.join(" ", parts);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
import View.MainFrame;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
public class Math_learning_app {
|
|
||||||
public static void main(String[] args) {
|
|
||||||
// 设置系统样式 - 使用正确的方法
|
|
||||||
try {
|
|
||||||
// 使用跨平台外观
|
|
||||||
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
|
|
||||||
|
|
||||||
// 或者使用系统默认外观(推荐)
|
|
||||||
// UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
// 如果设置失败,使用默认外观
|
|
||||||
System.err.println("无法设置外观,使用默认外观");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在事件分发线程中创建和显示GUI
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
MainFrame mainFrame = new MainFrame();
|
|
||||||
mainFrame.setVisible(true);
|
|
||||||
} catch (Exception e) {
|
|
||||||
JOptionPane.showMessageDialog(null,
|
|
||||||
"程序启动失败: " + e.getMessage(),
|
|
||||||
"错误",
|
|
||||||
JOptionPane.ERROR_MESSAGE);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
package Send_Email;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
public class Deal_i_code{
|
|
||||||
private static final Map<String, I_Code> codeMap = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
private static class I_Code {
|
|
||||||
String code;
|
|
||||||
long createTime;
|
|
||||||
private static final long EXPIRATION_TIME = 5 * 60 * 1000;
|
|
||||||
|
|
||||||
I_Code(String code) {
|
|
||||||
this.code = code;
|
|
||||||
this.createTime = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isExpired() {
|
|
||||||
return System.currentTimeMillis() - createTime > EXPIRATION_TIME;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String generate_code(String email) {
|
|
||||||
String code = Generate_i_code.generateCode(6);
|
|
||||||
codeMap.put(email, new I_Code(code));
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean judge_code(String email, String inputCode) {
|
|
||||||
I_Code codeInfo = codeMap.get(email);
|
|
||||||
if (codeInfo == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (codeInfo.isExpired()) {
|
|
||||||
codeMap.remove(email);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
boolean isValid = codeInfo.code.equals(inputCode);
|
|
||||||
if (isValid) {
|
|
||||||
codeMap.remove(email);
|
|
||||||
}
|
|
||||||
return isValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void clean_codes() {
|
|
||||||
codeMap.entrySet().removeIf(entry -> entry.getValue().isExpired());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void clean_all_codes(){
|
|
||||||
if (!codeMap.isEmpty()) {
|
|
||||||
codeMap.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
package Send_Email;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
public class Generate_i_code {
|
|
||||||
public static String generateCode(int length) {
|
|
||||||
Random ra = new Random();
|
|
||||||
StringBuilder code = new StringBuilder();
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
if (ra.nextBoolean()){
|
|
||||||
code.append((char)('A'+ra.nextInt(26)));
|
|
||||||
} else {
|
|
||||||
code.append((char)('0'+ra.nextInt(10)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return code.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
package Service;
|
|
||||||
|
|
||||||
import Base.Question;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class Deal_file {
|
|
||||||
private static final String BASE_DIR="试卷/";
|
|
||||||
|
|
||||||
public Deal_file() {
|
|
||||||
File baseDir = new File(BASE_DIR);
|
|
||||||
if (!baseDir.exists()) {
|
|
||||||
if (!baseDir.mkdirs())
|
|
||||||
System.out.println("目录创建失败!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void savePaper(ArrayList<Question> paper, String username) {
|
|
||||||
String userDirPath = BASE_DIR + username + "/";
|
|
||||||
File userDir = new File(userDirPath);
|
|
||||||
if (!userDir.exists()) {
|
|
||||||
if (!userDir.mkdirs()) {
|
|
||||||
System.out.println("目录创建失败!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String timestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
|
|
||||||
String filePath = userDirPath + timestamp + ".txt";
|
|
||||||
|
|
||||||
try (PrintWriter writer = new PrintWriter(new FileWriter(filePath))) {
|
|
||||||
for (int i = 0; i < paper.size(); i++) {
|
|
||||||
writer.println(paper.get(i).toString());
|
|
||||||
if (i < paper.size() - 1) {
|
|
||||||
writer.println();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.out.println("保存文件出错: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,115 +0,0 @@
|
|||||||
package Service;
|
|
||||||
|
|
||||||
import Base.Exam_result;
|
|
||||||
import Base.Question;
|
|
||||||
import Generator.Generate_paper;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class Exam_service {
|
|
||||||
private String id;
|
|
||||||
private String type;
|
|
||||||
private ArrayList<Question> paper;
|
|
||||||
private int now_index;
|
|
||||||
private Date start_time;
|
|
||||||
private Date end_time;
|
|
||||||
private Map<Integer, Integer> user_answers;
|
|
||||||
|
|
||||||
public Exam_service(int num,String type,String id){
|
|
||||||
this.id=id;
|
|
||||||
this.type=type;
|
|
||||||
paper= Generate_paper.g_paper(num,type,id);
|
|
||||||
now_index=0;
|
|
||||||
this.start_time = new Date();
|
|
||||||
this.user_answers = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayList<Question> get_paper() { return paper; }
|
|
||||||
public int get_now_index() { return now_index; }
|
|
||||||
public Date get_start_time() { return start_time; }
|
|
||||||
public Date get_end_time() { return end_time; }
|
|
||||||
public Map<Integer, Integer> get_user_answers() {return user_answers;}
|
|
||||||
|
|
||||||
public void set_now_index(int i){
|
|
||||||
if (i==1 && now_index<paper.size()-1){
|
|
||||||
now_index+=i;
|
|
||||||
}
|
|
||||||
else if (i==-1 && now_index>0){
|
|
||||||
now_index+=i;
|
|
||||||
}
|
|
||||||
else if (i==0){
|
|
||||||
now_index=0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Question get_now_question() {
|
|
||||||
if (now_index < paper.size()) {
|
|
||||||
return paper.get(now_index);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean next_one(int answer_index) {
|
|
||||||
if (now_index < paper.size()) {
|
|
||||||
user_answers.put(now_index, answer_index);
|
|
||||||
|
|
||||||
if (now_index < paper.size() - 1) {
|
|
||||||
now_index++;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
end_time = new Date();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean pre_one() {
|
|
||||||
if (now_index > 0) {
|
|
||||||
now_index--;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean change_answer(int index,int choice){
|
|
||||||
if (user_answers.containsKey(index)){
|
|
||||||
user_answers.put(index,choice);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer get_user_answer(int question_index) {
|
|
||||||
return user_answers.get(question_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean check_finished(){
|
|
||||||
for (int i=0;i< paper.size();i++){
|
|
||||||
if (user_answers.get(i)==-1){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Exam_result calculate_result(){
|
|
||||||
int correct = 0;
|
|
||||||
List<Integer> wrong = new ArrayList<>();
|
|
||||||
|
|
||||||
for (int i = 0; i < paper.size(); i++) {
|
|
||||||
Integer userAnswer = user_answers.get(i);
|
|
||||||
if (userAnswer != null && paper.get(i).getOptions(userAnswer).equals(paper.get(i).getAnswer())) {
|
|
||||||
correct++;
|
|
||||||
} else {
|
|
||||||
wrong.add(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double score = (double) correct / paper.size() * 100;
|
|
||||||
long duration = (end_time.getTime() - start_time.getTime()) / 1000; // 秒
|
|
||||||
|
|
||||||
return new Exam_result(type, paper.size(), correct,
|
|
||||||
score, duration, wrong);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package ui;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
public class MathLearningApp {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// 确保界面创建在事件分发线程中
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
// 设置系统外观 - 使用正确的方法
|
||||||
|
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
try {
|
||||||
|
// 如果系统外观设置失败,使用默认的跨平台外观
|
||||||
|
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new MainFrame().setVisible(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,264 @@
|
|||||||
|
package ui.panels;
|
||||||
|
|
||||||
|
import Base.Exam_result;
|
||||||
|
import ui.MainFrame;
|
||||||
|
import ui.service.BackendService;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
public class ExamPanel extends JPanel {
|
||||||
|
private MainFrame mainFrame;
|
||||||
|
private JLabel questionLabel;
|
||||||
|
private JRadioButton[] optionButtons;
|
||||||
|
private ButtonGroup buttonGroup;
|
||||||
|
private JLabel progressLabel;
|
||||||
|
private JButton prevBtn;
|
||||||
|
private JButton nextBtn;
|
||||||
|
private JButton submitBtn;
|
||||||
|
private JButton forceReloadBtn;
|
||||||
|
|
||||||
|
// 添加自动同步标志
|
||||||
|
private boolean autoSynced = false;
|
||||||
|
|
||||||
|
public ExamPanel(MainFrame mainFrame) {
|
||||||
|
this.mainFrame = mainFrame;
|
||||||
|
initializeUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeUI() {
|
||||||
|
setLayout(new BorderLayout(10, 10));
|
||||||
|
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||||
|
|
||||||
|
// 进度显示
|
||||||
|
progressLabel = new JLabel("等待试卷加载...", JLabel.CENTER);
|
||||||
|
progressLabel.setFont(new Font("宋体", Font.BOLD, 16));
|
||||||
|
|
||||||
|
// 题目显示
|
||||||
|
questionLabel = new JLabel("", JLabel.CENTER);
|
||||||
|
questionLabel.setFont(new Font("宋体", Font.PLAIN, 16));
|
||||||
|
questionLabel.setBorder(BorderFactory.createEmptyBorder(20, 10, 20, 10));
|
||||||
|
|
||||||
|
// 选项面板
|
||||||
|
JPanel optionsPanel = new JPanel(new GridLayout(4, 1, 10, 10));
|
||||||
|
optionsPanel.setBorder(BorderFactory.createTitledBorder("请选择答案"));
|
||||||
|
optionButtons = new JRadioButton[4];
|
||||||
|
buttonGroup = new ButtonGroup();
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
optionButtons[i] = new JRadioButton();
|
||||||
|
optionButtons[i].setFont(new Font("宋体", Font.PLAIN, 14));
|
||||||
|
buttonGroup.add(optionButtons[i]);
|
||||||
|
optionsPanel.add(optionButtons[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按钮面板
|
||||||
|
JPanel buttonPanel = new JPanel(new GridLayout(1, 4, 10, 10));
|
||||||
|
prevBtn = new JButton("上一题");
|
||||||
|
nextBtn = new JButton("下一题");
|
||||||
|
submitBtn = new JButton("提交试卷");
|
||||||
|
forceReloadBtn = new JButton("强制同步");
|
||||||
|
|
||||||
|
prevBtn.addActionListener(e -> showPreviousQuestion());
|
||||||
|
nextBtn.addActionListener(e -> showNextQuestion());
|
||||||
|
submitBtn.addActionListener(e -> finishExam());
|
||||||
|
forceReloadBtn.addActionListener(e -> forceSyncExamState());
|
||||||
|
|
||||||
|
buttonPanel.add(prevBtn);
|
||||||
|
buttonPanel.add(nextBtn);
|
||||||
|
buttonPanel.add(submitBtn);
|
||||||
|
buttonPanel.add(forceReloadBtn);
|
||||||
|
|
||||||
|
// 添加到主面板
|
||||||
|
add(progressLabel, BorderLayout.NORTH);
|
||||||
|
add(questionLabel, BorderLayout.CENTER);
|
||||||
|
add(optionsPanel, BorderLayout.EAST);
|
||||||
|
add(buttonPanel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
// 初始状态显示等待
|
||||||
|
showWaitingState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加自动同步方法
|
||||||
|
public void autoSyncExamState() {
|
||||||
|
if (!autoSynced) {
|
||||||
|
System.out.println("=== 自动同步试卷状态 ===");
|
||||||
|
forceSyncExamState();
|
||||||
|
autoSynced = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示等待状态
|
||||||
|
private void showWaitingState() {
|
||||||
|
progressLabel.setText("等待同步");
|
||||||
|
questionLabel.setText("<html><div style='text-align: center; padding: 20px;'>" +
|
||||||
|
"等待试卷数据同步,请稍候...</div></html>");
|
||||||
|
|
||||||
|
String[] waitingOptions = {"A. 同步中...", "B. 同步中...", "C. 同步中...", "D. 同步中..."};
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
optionButtons[i].setText(waitingOptions[i]);
|
||||||
|
optionButtons[i].setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonGroup.clearSelection();
|
||||||
|
|
||||||
|
// 禁用所有功能按钮
|
||||||
|
prevBtn.setEnabled(false);
|
||||||
|
nextBtn.setEnabled(false);
|
||||||
|
submitBtn.setEnabled(false);
|
||||||
|
forceReloadBtn.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void forceSyncExamState() {
|
||||||
|
System.out.println("=== 强制同步试卷状态 ===");
|
||||||
|
|
||||||
|
// 重置自动同步标志
|
||||||
|
autoSynced = true;
|
||||||
|
|
||||||
|
// 强制重新加载
|
||||||
|
BackendService.reloadCurrentQuestion();
|
||||||
|
|
||||||
|
// 检查试卷状态
|
||||||
|
String status = BackendService.getExamStatus();
|
||||||
|
System.out.println("试卷状态: " + status);
|
||||||
|
|
||||||
|
int totalQuestions = BackendService.getTotalQuestions();
|
||||||
|
int currentIndex = BackendService.getCurrentQuestionIndex();
|
||||||
|
|
||||||
|
System.out.println("题目总数: " + totalQuestions);
|
||||||
|
System.out.println("当前索引: " + currentIndex);
|
||||||
|
|
||||||
|
if ("READY".equals(status) && totalQuestions > 0) {
|
||||||
|
showCurrentQuestion();
|
||||||
|
} else {
|
||||||
|
showErrorState("试卷同步失败,状态: " + status +
|
||||||
|
",题目数: " + totalQuestions +
|
||||||
|
",当前索引: " + currentIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showErrorState(String errorMessage) {
|
||||||
|
progressLabel.setText("系统提示");
|
||||||
|
questionLabel.setText("<html><div style='text-align: center; color: red; padding: 20px;'>" +
|
||||||
|
errorMessage + "</div></html>");
|
||||||
|
|
||||||
|
// 设置默认选项
|
||||||
|
String[] defaultOptions = {"A. 请点击强制同步", "B. 检查后端日志", "C. 重新生成试卷", "D. 联系技术支持"};
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
optionButtons[i].setText(defaultOptions[i]);
|
||||||
|
optionButtons[i].setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonGroup.clearSelection();
|
||||||
|
|
||||||
|
// 禁用功能按钮,只保留同步按钮
|
||||||
|
prevBtn.setEnabled(false);
|
||||||
|
nextBtn.setEnabled(false);
|
||||||
|
submitBtn.setEnabled(false);
|
||||||
|
forceReloadBtn.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showCurrentQuestion() {
|
||||||
|
String questionText = BackendService.getCurrentQuestionText();
|
||||||
|
String[] options = BackendService.getCurrentQuestionOptions();
|
||||||
|
|
||||||
|
int currentIndex = BackendService.getCurrentQuestionIndex();
|
||||||
|
int totalQuestions = BackendService.getTotalQuestions();
|
||||||
|
|
||||||
|
// 更新显示
|
||||||
|
progressLabel.setText(String.format("第 %d/%d 题", currentIndex + 1, totalQuestions));
|
||||||
|
questionLabel.setText(questionText);
|
||||||
|
|
||||||
|
// 更新选项
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
optionButtons[i].setText(options[i]);
|
||||||
|
optionButtons[i].setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复用户之前的选择
|
||||||
|
Integer userAnswer = BackendService.getUserAnswer(currentIndex);
|
||||||
|
if (userAnswer != null && userAnswer >= 0 && userAnswer < 4) {
|
||||||
|
optionButtons[userAnswer].setSelected(true);
|
||||||
|
} else {
|
||||||
|
buttonGroup.clearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新按钮状态
|
||||||
|
updateButtonStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showNextQuestion() {
|
||||||
|
int selectedAnswer = getSelectedAnswer();
|
||||||
|
if (selectedAnswer != -1) {
|
||||||
|
boolean hasNext = BackendService.submitAnswer(selectedAnswer);
|
||||||
|
if (hasNext) {
|
||||||
|
showCurrentQuestion();
|
||||||
|
} else {
|
||||||
|
finishExam();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
JOptionPane.showMessageDialog(this, "请选择一个答案");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showPreviousQuestion() {
|
||||||
|
if (BackendService.getPreviousQuestion()) {
|
||||||
|
showCurrentQuestion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSelectedAnswer() {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (optionButtons[i].isSelected()) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateButtonStates() {
|
||||||
|
int currentIndex = BackendService.getCurrentQuestionIndex();
|
||||||
|
int totalQuestions = BackendService.getTotalQuestions();
|
||||||
|
|
||||||
|
boolean hasPrevious = currentIndex > 0;
|
||||||
|
boolean hasNext = currentIndex < totalQuestions - 1;
|
||||||
|
boolean isLastQuestion = currentIndex == totalQuestions - 1;
|
||||||
|
|
||||||
|
prevBtn.setEnabled(hasPrevious && totalQuestions > 0);
|
||||||
|
nextBtn.setEnabled(hasNext && totalQuestions > 0);
|
||||||
|
submitBtn.setEnabled(isLastQuestion && totalQuestions > 0);
|
||||||
|
forceReloadBtn.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void finishExam() {
|
||||||
|
System.out.println("=== 完成考试 ===");
|
||||||
|
|
||||||
|
// 提交最后一题的答案
|
||||||
|
int selectedAnswer = getSelectedAnswer();
|
||||||
|
if (selectedAnswer != -1) {
|
||||||
|
System.out.println("提交最后一题答案: " + selectedAnswer);
|
||||||
|
BackendService.submitAnswer(selectedAnswer);
|
||||||
|
} else {
|
||||||
|
System.out.println("最后一题未选择答案,提交默认答案");
|
||||||
|
BackendService.submitAnswer(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算并缓存考试结果
|
||||||
|
Exam_result result = BackendService.finishExam();
|
||||||
|
if (result != null) {
|
||||||
|
System.out.println("考试结果计算成功,准备跳转到结果页面");
|
||||||
|
} else {
|
||||||
|
System.out.println("考试结果计算失败");
|
||||||
|
JOptionPane.showMessageDialog(this,
|
||||||
|
"考试结果计算失败,请重新考试",
|
||||||
|
"错误",
|
||||||
|
JOptionPane.ERROR_MESSAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置自动同步标志
|
||||||
|
autoSynced = false;
|
||||||
|
|
||||||
|
// 显示考试结果
|
||||||
|
mainFrame.showPanel(MainFrame.RESULT_PAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
package ui.panels;
|
||||||
|
|
||||||
|
import ui.MainFrame;
|
||||||
|
import ui.service.BackendService;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
public class GradeSelectPanel extends JPanel {
|
||||||
|
private MainFrame mainFrame;
|
||||||
|
private String selectedGrade;
|
||||||
|
|
||||||
|
public GradeSelectPanel(MainFrame mainFrame) {
|
||||||
|
this.mainFrame = mainFrame;
|
||||||
|
initializeUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeUI() {
|
||||||
|
setLayout(new GridLayout(5, 1, 10, 10));
|
||||||
|
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||||
|
|
||||||
|
JLabel titleLabel = new JLabel("选择年级", JLabel.CENTER);
|
||||||
|
titleLabel.setFont(new Font("宋体", Font.BOLD, 18));
|
||||||
|
|
||||||
|
JButton primaryBtn = new JButton("小学");
|
||||||
|
JButton middleBtn = new JButton("初中");
|
||||||
|
JButton highBtn = new JButton("高中");
|
||||||
|
JButton backBtn = new JButton("返回");
|
||||||
|
|
||||||
|
// 为每个按钮设置年级并跳转
|
||||||
|
primaryBtn.addActionListener(e -> {
|
||||||
|
selectedGrade = "小学";
|
||||||
|
showQuestionCountPanel();
|
||||||
|
});
|
||||||
|
|
||||||
|
middleBtn.addActionListener(e -> {
|
||||||
|
selectedGrade = "初中";
|
||||||
|
showQuestionCountPanel();
|
||||||
|
});
|
||||||
|
|
||||||
|
highBtn.addActionListener(e -> {
|
||||||
|
selectedGrade = "高中";
|
||||||
|
showQuestionCountPanel();
|
||||||
|
});
|
||||||
|
|
||||||
|
backBtn.addActionListener(e -> {
|
||||||
|
mainFrame.showPanel(MainFrame.LOGIN_PAGE);
|
||||||
|
});
|
||||||
|
|
||||||
|
add(titleLabel);
|
||||||
|
add(primaryBtn);
|
||||||
|
add(middleBtn);
|
||||||
|
add(highBtn);
|
||||||
|
add(backBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示题目数量选择面板
|
||||||
|
private void showQuestionCountPanel() {
|
||||||
|
// 使用更简单的方式传递年级信息
|
||||||
|
// 直接跳转到题目数量页面,年级信息会在进入时设置
|
||||||
|
try {
|
||||||
|
// 尝试通过主框架获取题目数量面板
|
||||||
|
QuestionCountPanel questionCountPanel = mainFrame.getQuestionCountPanel();
|
||||||
|
if (questionCountPanel != null) {
|
||||||
|
questionCountPanel.setGrade(selectedGrade);
|
||||||
|
mainFrame.showPanel(MainFrame.QUESTION_COUNT_PAGE);
|
||||||
|
} else {
|
||||||
|
// 如果获取失败,使用备用方案
|
||||||
|
JOptionPane.showMessageDialog(this,
|
||||||
|
"系统初始化中,请稍后重试");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 如果出现异常,使用备用方案
|
||||||
|
System.out.println("获取题目数量面板异常: " + e.getMessage());
|
||||||
|
// 直接跳转,年级信息将在进入页面后设置
|
||||||
|
mainFrame.showPanel(MainFrame.QUESTION_COUNT_PAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
package ui.panels;
|
||||||
|
|
||||||
|
import ui.MainFrame;
|
||||||
|
import ui.service.BackendService;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
public class LoginPanel extends JPanel {
|
||||||
|
private MainFrame mainFrame;
|
||||||
|
|
||||||
|
public LoginPanel(MainFrame mainFrame) {
|
||||||
|
this.mainFrame = mainFrame;
|
||||||
|
initializeUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeUI() {
|
||||||
|
setLayout(new GridLayout(4, 2, 10, 10));
|
||||||
|
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||||
|
|
||||||
|
JLabel userIdLabel = new JLabel("用户名:");
|
||||||
|
JTextField userIdField = new JTextField();
|
||||||
|
JLabel passwordLabel = new JLabel("密码:");
|
||||||
|
JPasswordField passwordField = new JPasswordField();
|
||||||
|
|
||||||
|
JButton loginBtn = new JButton("登录");
|
||||||
|
JButton registerBtn = new JButton("注册");
|
||||||
|
|
||||||
|
loginBtn.addActionListener(e -> {
|
||||||
|
String userId = userIdField.getText();
|
||||||
|
String password = new String(passwordField.getPassword());
|
||||||
|
|
||||||
|
if (userId.isEmpty() || password.isEmpty()) {
|
||||||
|
JOptionPane.showMessageDialog(this, "请输入用户名和密码");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用后端登录服务
|
||||||
|
boolean success = BackendService.login(userId, password);
|
||||||
|
if (success) {
|
||||||
|
JOptionPane.showMessageDialog(this, "登录成功");
|
||||||
|
mainFrame.showPanel(MainFrame.GRADE_SELECT_PAGE);
|
||||||
|
} else {
|
||||||
|
JOptionPane.showMessageDialog(this, "登录失败,请检查用户名和密码");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
registerBtn.addActionListener(e -> {
|
||||||
|
mainFrame.showPanel(MainFrame.REGISTER_PAGE);
|
||||||
|
});
|
||||||
|
|
||||||
|
add(userIdLabel);
|
||||||
|
add(userIdField);
|
||||||
|
add(passwordLabel);
|
||||||
|
add(passwordField);
|
||||||
|
add(loginBtn);
|
||||||
|
add(registerBtn);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,123 @@
|
|||||||
|
package ui.panels;
|
||||||
|
|
||||||
|
import ui.MainFrame;
|
||||||
|
import ui.service.BackendService;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
public class QuestionCountPanel extends JPanel {
|
||||||
|
private MainFrame mainFrame;
|
||||||
|
private String currentGrade;
|
||||||
|
|
||||||
|
public QuestionCountPanel(MainFrame mainFrame) {
|
||||||
|
this.mainFrame = mainFrame;
|
||||||
|
this.currentGrade = "小学";
|
||||||
|
initializeUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGrade(String grade) {
|
||||||
|
this.currentGrade = grade;
|
||||||
|
System.out.println("设置年级: " + grade);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeUI() {
|
||||||
|
setLayout(new GridLayout(7, 2, 10, 10));
|
||||||
|
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||||
|
|
||||||
|
JLabel titleLabel = new JLabel("生成试卷", JLabel.CENTER);
|
||||||
|
titleLabel.setFont(new Font("宋体", Font.BOLD, 16));
|
||||||
|
|
||||||
|
JLabel countLabel = new JLabel("题目数量:");
|
||||||
|
JTextField countField = new JTextField("10");
|
||||||
|
JLabel gradeLabel = new JLabel("当前年级:");
|
||||||
|
JLabel currentGradeLabel = new JLabel(currentGrade);
|
||||||
|
|
||||||
|
JButton generateBtn = new JButton("生成试卷");
|
||||||
|
JButton testBtn = new JButton("测试模式");
|
||||||
|
JButton backBtn = new JButton("返回");
|
||||||
|
|
||||||
|
generateBtn.addActionListener(e -> {
|
||||||
|
try {
|
||||||
|
int count = Integer.parseInt(countField.getText());
|
||||||
|
if (count <= 0 || count > 50) {
|
||||||
|
JOptionPane.showMessageDialog(this, "请输入1-50之间的题目数量");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("=== 生成试卷请求 ===");
|
||||||
|
System.out.println("年级: " + currentGrade + ", 题目数量: " + count);
|
||||||
|
|
||||||
|
// 确保用户ID存在
|
||||||
|
if (BackendService.getCurrentUserId() == null) {
|
||||||
|
BackendService.setTempUserId("temp_user_" + System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success = BackendService.generatePaper(currentGrade, count);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
// 立即检查状态
|
||||||
|
String status = BackendService.getExamStatus();
|
||||||
|
int totalQuestions = BackendService.getTotalQuestions();
|
||||||
|
int currentIndex = BackendService.getCurrentQuestionIndex();
|
||||||
|
|
||||||
|
System.out.println("生成后状态: " + status +
|
||||||
|
", 题目数: " + totalQuestions +
|
||||||
|
", 当前索引: " + currentIndex);
|
||||||
|
|
||||||
|
if ("READY".equals(status) && totalQuestions > 0) {
|
||||||
|
JOptionPane.showMessageDialog(this,
|
||||||
|
String.format("成功生成%d道%s数学题目", totalQuestions, currentGrade));
|
||||||
|
// 重置自动同步状态
|
||||||
|
BackendService.resetAutoSync();
|
||||||
|
// 跳转到考试界面
|
||||||
|
mainFrame.showPanel(MainFrame.EXAM_PAGE);
|
||||||
|
} else {
|
||||||
|
JOptionPane.showMessageDialog(this,
|
||||||
|
"试卷生成但状态异常,状态: " + status +
|
||||||
|
",题目数: " + totalQuestions +
|
||||||
|
",当前索引: " + currentIndex);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
JOptionPane.showMessageDialog(this, "试卷生成失败,请检查控制台日志");
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
JOptionPane.showMessageDialog(this, "请输入有效的数字");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
testBtn.addActionListener(e -> {
|
||||||
|
// 使用测试数据
|
||||||
|
System.out.println("=== 测试模式 ===");
|
||||||
|
|
||||||
|
// 确保用户ID存在
|
||||||
|
if (BackendService.getCurrentUserId() == null) {
|
||||||
|
BackendService.setTempUserId("test_user_" + System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success = BackendService.generatePaper("小学", 5);
|
||||||
|
if (success) {
|
||||||
|
JOptionPane.showMessageDialog(this, "测试试卷生成成功");
|
||||||
|
// 重置自动同步状态
|
||||||
|
BackendService.resetAutoSync();
|
||||||
|
mainFrame.showPanel(MainFrame.EXAM_PAGE);
|
||||||
|
} else {
|
||||||
|
JOptionPane.showMessageDialog(this, "测试试卷生成失败");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
backBtn.addActionListener(e -> {
|
||||||
|
mainFrame.showPanel(MainFrame.GRADE_SELECT_PAGE);
|
||||||
|
});
|
||||||
|
|
||||||
|
add(titleLabel);
|
||||||
|
add(new JLabel());
|
||||||
|
add(countLabel);
|
||||||
|
add(countField);
|
||||||
|
add(gradeLabel);
|
||||||
|
add(currentGradeLabel);
|
||||||
|
add(generateBtn);
|
||||||
|
add(testBtn);
|
||||||
|
add(backBtn);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in new issue