Compare commits

...

11 Commits

@ -0,0 +1,58 @@
# 带UI的数学学习软件
## 项目结构
Math_learning
|——lib  依赖jar包
  |——javax.mail-1.6.2.jar  发送邮件相关
  |——activation-1.1.1.jar  javax.mail所需依赖
|——src  源代码目录
  |——Base  基础类包
    |——Email_settings.java  邮件发送服务配置
    |——Exam_result.java  考试结果类
    |——Question.java  题目类
    |——User.java  用户类
  |——Generator  题目生成器包
    |——G_ques.java  生成器接口
    |——Pri_g_ques.java  小学题目生成器
    |——Jun_g_ques.java  初中题目生成器
    |——Sen_g_ques.java  高中题目生成器
    |——Generate_paper.java  生成试卷
  |——Send_Email  邮件发送包
    |——Deal_i_code.java  管理验证码以及验证码校验
    |——Generate_i_code.java  产生验证码
    |——Send_email.java  发送邮件类
  |——Service  服务类包,供前端调用
    |——User_service.java  用户服务类,包括注册、登录、修改密码等
    |——Exam_service.java  考试服务类
    |——Deal_file.java  可选功能:保存试卷为文件
  |——View  前端类包
|——doc  说明文档
  |——README.md
## 邮件发送说明
发件人信息通过配置文件存储首次运行时会创建config/email.properties配置文件并使用默认配置乔毅凡的qq邮箱
修改配置文件可更改发件人配置,如果要使用自己的邮箱,需要去设置中开启特定配置。
以QQ邮箱为例子
1. 需要去自己的邮箱设置中找到POP3/IMAP/SMTP/Exchange/CardDAV 服务,选择开启,生成授权码。
2. 查看QQ邮箱配置方法中的说明看到“发送邮件服务器 smtp.qq.com使用SSL端口号465或587”这就是发件服务器和使用的端口以及使用SSL。
3. 将上述信息修改到配置文件中。发件服务器、端口、SSL在默认配置中已经为QQ邮箱的配置修改邮箱和授权码即可
4. 其他邮箱配置方法具体见官方说明。
## 用户信息
用户信息保存在本地,用户/用户信息.txt(运行时会产生)
## 运行环境
可执行文件Math_Learning.jar依赖已经打包
Windows  UTF-8编码  
---
软件2302
刘星宇 202326010226
毛承上 202326010227
乔毅凡 202326010228

@ -1,13 +1,86 @@
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 {
public static final String SMTP_HOST = "smtp.qq.com";
// 邮箱服务器端口
public static final String SMTP_PORT = "587";
// 发件人邮箱
public static final String FROM_EMAIL = "835981889@qq.com";
// 发件人邮箱授权码
public static final String EMAIL_PASSWORD = "fpqfprqznbvdbcdf";
// 是否启用SSL
public static final boolean SSL_ENABLE = true;
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"));
}
}

@ -0,0 +1,41 @@
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);
}
}

@ -2,15 +2,17 @@ package Base;
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) {

@ -1,15 +1,12 @@
package Service;
package Generator;
import Base.Question;
import Generator.G_ques;
import Generator.Jun_g_ques;
import Generator.Pri_g_ques;
import Generator.Sen_g_ques;
import Service.Deal_file;
import java.util.ArrayList;
public class Generate_paper {
public static ArrayList<Question> g_paper(int num,String type) {
public static ArrayList<Question> g_paper(int num,String type,String id) {
ArrayList<Question> result = new ArrayList<>();
G_ques generator;
switch (type){
@ -30,10 +27,27 @@ public class Generate_paper {
}
}
for (int i=0;i<num;i++){
Question temp=new Question(i+1,generator.g_question(),generator.g_type());
temp.set_options();
result.add(temp);
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;
}
}

@ -48,6 +48,8 @@ public class Deal_i_code{
}
public static void clean_all_codes(){
codeMap.clear();
if (!codeMap.isEmpty()) {
codeMap.clear();
}
}
}

@ -9,83 +9,73 @@ import java.util.Properties;
import java.util.regex.Pattern;
public class Send_email {
public static boolean judge_email_address(String email) {
public static String judge_email_address(String email) {
if (email == null || email.trim().isEmpty()) {
System.out.println("邮箱不能为空");
return false;
return "邮箱不能为空";
}
// 去除前后空格
email = email.trim();
// 检查长度
if (email.length() > 254) { // RFC标准规定邮箱最大长度
System.out.println("邮箱地址过长");
return false;
return "邮箱地址过长";
}
// 检查是否包含@
if (!email.contains("@")) {
System.out.println("邮箱格式错误:缺少@符号");
return false;
return "邮箱格式错误:缺少@符号";
}
// 分割本地部分和域名部分
String[] parts = email.split("@");
if (parts.length != 2) {
System.out.println("邮箱格式错误:只能有一个@符号");
return false;
return "邮箱格式错误:只能有一个@符号";
}
String localPart = parts[0];
String domainPart = parts[1];
// 验证本地部分
if (localPart.isEmpty()) {
System.out.println("邮箱格式错误:@前必须有内容");
return false;
return "邮箱格式错误:@前必须有内容";
}
if (localPart.length() > 64) {
System.out.println("邮箱格式错误:@前内容过长");
return false;
return "邮箱格式错误:@前内容过长";
}
// 验证域名部分
if (domainPart.isEmpty()) {
System.out.println("邮箱格式错误:@后必须有内容");
return false;
return "邮箱格式错误:@后必须有内容";
}
if (!domainPart.contains(".")) {
System.out.println("邮箱格式错误:域名不完整");
return false;
return "邮箱格式错误:域名不完整";
}
// 验证顶级域名
String[] domainParts = domainPart.split("\\.");
String topLevelDomain = domainParts[domainParts.length - 1];
if (topLevelDomain.length() < 2) {
System.out.println("邮箱格式错误:顶级域名太短");
return false;
return "邮箱格式错误:顶级域名太短";
}
String emailRegex = "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@"
+ "[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$";
boolean matches = Pattern.matches(emailRegex, email);
System.out.println(matches ? "" : "邮箱格式错误");
return matches;
return matches ? "邮箱格式正确" : "邮箱格式错误";
}
public static boolean send_email(String toEmail, String Code) {
try {
Properties props = new Properties();
props.put("mail.smtp.host", Email_settings.SMTP_HOST);
props.put("mail.smtp.port", Email_settings.SMTP_PORT);
props.put("mail.smtp.host", Email_settings.getSmtpHost());
props.put("mail.smtp.port", Email_settings.getSmtpPort());
props.put("mail.smtp.auth", "true");
if (Email_settings.SSL_ENABLE) {
if (Email_settings.isSslEnable()) {
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.ssl.trust", Email_settings.SMTP_HOST);
props.put("mail.smtp.ssl.trust", Email_settings.getSmtpHost());
}
// 创建会话
Session session = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(Email_settings.FROM_EMAIL, Email_settings.EMAIL_PASSWORD);
return new PasswordAuthentication(Email_settings.getFromEmail(), Email_settings.getEmailPassword());
}
});
// 创建邮件消息
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(Email_settings.FROM_EMAIL));
message.setFrom(new InternetAddress(Email_settings.getFromEmail()));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail));
message.setSubject("注册验证码");
// 邮件内容

@ -0,0 +1,43 @@
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());
}
}
}

@ -0,0 +1,115 @@
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);
}
}

@ -1,6 +1,9 @@
package Service;
import Base.User;
import Send_Email.Deal_i_code;
import Send_Email.Send_email;
import java.io.*;
import java.util.ArrayList;
@ -14,6 +17,58 @@ public class User_service {
}
}
public String register(String email){
Deal_i_code.clean_all_codes();
if (find_user(email)!=null){
return "邮箱已被注册";
}
String judge_result=Send_email.judge_email_address(email);
if (judge_result.equals("邮箱格式正确")){
String code=Deal_i_code.generate_code(email);
if (!Send_email.send_email(email,code)){
return "邮件发送失败";
}
return "验证码已发送";
}
else{
return judge_result;
}
}
public String check_register(String email,String input_code,String pwd,String id){
if (Deal_i_code.judge_code(email,input_code)){
if (!User.check_password(pwd)){
return "密码长6-20位至少包含大小写字母和数字";
}
User new_one=new User(email);
new_one.set_password(pwd);
new_one.set_id(id);
if (find_user_i(id)!=null){
return "用户名重复";
}
users.add(new_one);
Deal_i_code.clean_all_codes();
if (!save_users()){
return "注册失败,请重试";
}
return "注册成功";
}
else {
return "验证码不存在或不正确";
}
}
public String login(String id,String pwd){
User u=find_user_i(id);
if (u==null){
return "请先注册";
}
if (u.get_password().equals(pwd)){
return "登陆成功";
}
return "密码有误";
}
public String change_pwd(String email, String oldPassword, String newPassword){
User user=find_user(email);
if (user == null) {
@ -35,6 +90,18 @@ public class User_service {
return "修改失败";
}
public String Unregister(String email){
User user=find_user(email);
if (user!=null) {
if (users.remove(user)) {
if (save_users()) {
return "删除成功";
}
}
}
return "删除失败";
}
public User find_user(String email){
for (User user : users) {
if (user.get_email().equals(email)) {
@ -44,7 +111,16 @@ public class User_service {
return null;
}
public boolean save_users(){
public User find_user_i(String id){
for (User user : users) {
if (user.get_id().equals(id)) {
return user;
}
}
return null;
}
private boolean save_users(){
File baseDir = new File(base_dir);
if (!baseDir.exists()) {
if (!baseDir.mkdirs()){
@ -53,8 +129,11 @@ public class User_service {
}
String file_path=base_dir+"用户信息.txt";
try (PrintWriter writer = new PrintWriter(new FileWriter(file_path))) {
if (users.isEmpty()){
return true;
}
for (int i = 0; i < users.size(); i++) {
writer.println(users.get(i).get_email()+" "+users.get(i).get_password());
writer.println(users.get(i).get_email()+" "+users.get(i).get_password()+" "+users.get(i).get_id());
if (i < users.size() - 1) {
writer.println();
}
@ -65,7 +144,7 @@ public class User_service {
}
}
public boolean load_users(){
private boolean load_users(){
users=new ArrayList<>();
String file_path=base_dir+"用户信息.txt";
File file=new File(file_path);
@ -76,9 +155,10 @@ public class User_service {
if (!line.trim().isEmpty()) {
String l=line.trim();
String[] parts=l.split(" ");
if (parts.length==2){
if (parts.length==3){
User temp=new User(parts[0]);
temp.set_password(parts[1]);
temp.set_id(parts[2]);
users.add(temp);
}
}

Loading…
Cancel
Save