【新增】MYSQL、浏览器、文件操作试题判分第一版,操作日志文件控制台输出

This commit is contained in:
YOHO\20373
2025-04-25 17:56:48 +08:00
parent b8924b91e9
commit 8fc67c2457
16 changed files with 1250 additions and 2 deletions

View File

@@ -0,0 +1,29 @@
package pc.exam.pp.module.judgement.controller.admin.Browser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import pc.exam.pp.framework.common.pojo.CommonResult;
import pc.exam.pp.module.judgement.controller.service.browser.IBrowserServerice;
import java.io.IOException;
@RestController
@RequestMapping("/tool/Browser")
public class BrowserComtroller {
@Autowired
private IBrowserServerice browserServerice;
/**
* 浏览器判分
* @return 得分
*/
@PostMapping("/run_judgement")
public CommonResult run_C_code() throws IOException {
return CommonResult.success(browserServerice.Judgement());
}
}

View File

@@ -0,0 +1,28 @@
package pc.exam.pp.module.judgement.controller.admin.Mysql;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import pc.exam.pp.framework.common.pojo.CommonResult;
import pc.exam.pp.module.judgement.controller.service.mysql.IMysqlServerice;
@RestController
@RequestMapping("/tool/Mysql")
public class MysqlController {
@Autowired
private IMysqlServerice mysqlServerice;
/**
* Mysql判分
* @return 得分
*/
@PostMapping("/run_judgement")
public CommonResult run_C_code() {
return CommonResult.success(mysqlServerice.Judgement());
}
}

View File

@@ -0,0 +1,44 @@
package pc.exam.pp.module.judgement.controller.admin.WindowFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import pc.exam.pp.framework.common.pojo.CommonResult;
import pc.exam.pp.module.judgement.controller.service.file.IFileServerice;
import java.io.IOException;
/**
* 文件操作题判分
* rwb
*/
@RestController
@RequestMapping("/tool/File")
public class WindowsFileController {
@Autowired
private IFileServerice fileServerice;
/**
* 得出文件操作考点
* @return 得分
*/
// @PostMapping("/get_filePoint")
//todo 后期需要素材文件和答案文件的 父目录 如D:/exam/3/shucai,D:/exam/3/win
public CommonResult get_file_point() throws IOException {
return CommonResult.success(fileServerice.get_file_point());
}
/**
* 文件操作题判分
* @return 判分
*/
@PostMapping("/run_file")
public CommonResult run_file_point() throws IOException {
//"权值得分比重"
return CommonResult.success(fileServerice.run_file_point());
}
}

View File

@@ -0,0 +1,108 @@
package pc.exam.pp.module.judgement.controller.service.browser;
import org.springframework.stereotype.Service;
import pc.exam.pp.module.exam.dal.dataobject.ExamQuestionAnswer;
import pc.exam.pp.module.judgement.controller.utils.brower.BookmarkChecker;
import pc.exam.pp.module.judgement.controller.utils.brower.BookmarkDeleter;
import pc.exam.pp.module.judgement.controller.utils.file.GetDifferencesBetweenFolders;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
public class BrowserServericeImpl implements IBrowserServerice {
//考生试题文件夹
static final String BASE_DIR = "D:/exam/4/";
//谷歌浏览器
String chromeBookmarkPath = System.getenv("LOCALAPPDATA") + "\\Google\\Chrome\\User Data\\Default\\Bookmarks";
@Override
public String Judgement() throws IOException {
//根据题目,查找考点
//得到该试题的得分点
//Net得分点 文件类型 文件名 考点权值
// 添加到文件夹 1
// 添加到收藏夹 2
List<ExamQuestionAnswer> answerList=new ArrayList<>();
answerList.add(new ExamQuestionAnswer("","","","","1", "文件名1", "1", "1"));
answerList.add(new ExamQuestionAnswer("","","","","1", "文件名2", "1", "2"));
answerList.add(new ExamQuestionAnswer("","","","","2", "百度一下,你就知道", "1", "3"));
//判断如果类型为1为添加到文件夹的html后缀加 .html
for (ExamQuestionAnswer answer : answerList) {
if ("1".equals(answer.getContent())) {
String fileName = answer.getContentIn();
if (!fileName.endsWith(".html")) {
answer.setContentIn(fileName + ".html");
}
}
}
//分为两点1文件夹-- 去考生文件夹去找 文件名
// 2收藏夹--去浏览器去找 根据值 文件名 查找
// 找到后 —————— +权值 ,根据文件名删除
//根据路径,得到考生答题文件集合
Map<String, String> stuFiles = GetDifferencesBetweenFolders.listFilesAndFoldersWithAttributes(Paths.get(BASE_DIR));
// 对比学生提交内容与试题得分点
//这里指挥判断存在文件夹的得分点
Integer studentScore = compareStuAndTestFiles(answerList, stuFiles);
//判断收藏夹得分点
for (ExamQuestionAnswer examQuestionAnswer : answerList) {
int currentScore = Integer.parseInt(examQuestionAnswer.getScoreRate()); // 当前得分
if ( examQuestionAnswer.getContent().equals("2")){
String bookmarkNameToDelete = examQuestionAnswer.getContentIn();
//检查收藏夹是否有书签
if (BookmarkChecker.bookmarkExists(chromeBookmarkPath, bookmarkNameToDelete)) {
//如果有 +权值
studentScore += currentScore;
System.out.println("📌 发现书签,准备删除...");
//删除此书签
BookmarkDeleter.deleteBookmarkByName(chromeBookmarkPath, bookmarkNameToDelete);
}
}
}
// 计算试题总分
int totalScore = answerList.stream()
.mapToInt(a -> Integer.parseInt(a.getScoreRate()))
.sum();
// 计算最终得分比例(保留两位小数)
double scoreRatio = totalScore == 0 ? 0 : (double) studentScore / totalScore;
String formattedScoreRatio = String.format("%.2f", scoreRatio);
return formattedScoreRatio;
}
// 对比学生提交内容与试题得分点
static Integer compareStuAndTestFiles(List<ExamQuestionAnswer> answerList, Map<String, String> stuFiles) {
int totalScore = 0; // 记录总得分
for (ExamQuestionAnswer answer : answerList) {
String filePath = answer.getContentIn(); // 试题文件路径
int currentScore = Integer.parseInt(answer.getScoreRate()); // 当前得分
boolean isCorrect = false;
// 如果学生提交中存在该文件,则得分
isCorrect = stuFiles.containsKey(filePath);
// 如果正确,则累加总分
if (isCorrect) {
totalScore += currentScore;
System.out.println(answer.getContentIn() + " -> 得分权值:" + answer.getScoreRate()+"-> 是否得分:"+isCorrect);
}else {
System.out.println(answer.getContentIn() + " -> 得分权值:" + answer.getScoreRate()+"-> 是否得分:"+isCorrect);
}
}
//返回累加的得分点
return totalScore;
}
}

View File

@@ -0,0 +1,8 @@
package pc.exam.pp.module.judgement.controller.service.browser;
import java.io.IOException;
public interface IBrowserServerice {
String Judgement() throws IOException;
}

View File

@@ -0,0 +1,126 @@
package pc.exam.pp.module.judgement.controller.service.file;
import org.springframework.stereotype.Service;
import pc.exam.pp.module.exam.dal.dataobject.ExamQuestionAnswer;
import pc.exam.pp.module.judgement.controller.utils.file.GetDifferencesBetweenFolders;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@Service
public class FileServericeImpl implements IFileServerice {
static final String BASE_DIR = "D:/exam/3/";
@Override
public List<ExamQuestionAnswer> get_file_point() throws IOException {
List<ExamQuestionAnswer> answerList = new ArrayList<>();
AtomicInteger sortCounter = new AtomicInteger(1); // 计数器
String path1 = BASE_DIR + "shucai";
String path2 = BASE_DIR + "win";
// 获取 shucai 和 win 文件夹的差异
Map<String, String> differences = GetDifferencesBetweenFolders.getDifferencesBetweenFolders(path1, path2);
List<ExamQuestionAnswer> formattedDifferences = differences.entrySet().stream()
.map(entry -> {
String key = entry.getKey();
String value = entry.getValue();
ExamQuestionAnswer answer = new ExamQuestionAnswer();
answer.setContent(key); // 设置文件路径
answer.setScoreRate("1");
//这里设置answer的排序按照循环次数来
answer.setSort(String.valueOf(sortCounter.getAndIncrement())); // 按顺序设置排序值
if (value.startsWith("仅存在于 "+path1)) {
answer.setContentIn("考察删除");
} else if (value.startsWith("仅存在于 "+path2)) {
answer.setContentIn("考察名称");
} else if (value.startsWith("属性不同")) {
answer.setContentIn("考察属性");
// answer.setContent(key + " -> " + value.split(" vs ")[1]); // 设置属性信息
}
return answer;
})
.sorted(Comparator.comparingInt(answer -> Integer.parseInt(answer.getSort()))) // 按 sort 排序
.collect(Collectors.toList());
answerList.addAll(formattedDifferences);
return answerList;
}
@Override
public String run_file_point() throws IOException {
//todo 得到学生的考题答案
List<ExamQuestionAnswer> answerList=new ArrayList<>();
answerList.add(new ExamQuestionAnswer("","","","","HGACYL\\RLQM.MEM", "考察删除", "1", "1"));
answerList.add(new ExamQuestionAnswer("","","","","TING\\XYU\\AUTOE.BAT", "考察删除", "1", "2"));
answerList.add(new ExamQuestionAnswer("","","","","AHEWL\\KMENS", "考察名称", "1", "3"));
answerList.add(new ExamQuestionAnswer("","","","","EDZK\\RONGHE.COM", "考察名称", "1", "4"));
answerList.add(new ExamQuestionAnswer("","","","","HGACYL\\PLAY.MEM", "考察名称", "1", "5"));
answerList.add(new ExamQuestionAnswer("","","","","WUE\\PB6.txt", "考察名称", "1", "6"));
String stuPath = BASE_DIR + "stu";
// 获取 stu 文件夹的内容
Map<String, String> stuFiles = GetDifferencesBetweenFolders.listFilesAndFoldersWithAttributes(Paths.get( stuPath));
// 输出学生提交的内容
System.out.println("\n=== 学生提交内容stu 目录) ===");
stuFiles.forEach((key, value) -> System.out.println(key + " -> " + value));
System.out.println("\n=== 学生提交内容得分点 ===");
// 对比学生提交内容与试题得分点
Integer studentScore = compareStuAndTestFiles(answerList, stuFiles);
//获取answerList里的所有sorcerate,和integer相除得到一个小于等于1的数
// 计算试题总分
int totalScore = answerList.stream()
.mapToInt(a -> Integer.parseInt(a.getScoreRate()))
.sum();
// 计算最终得分比例(保留两位小数)
double scoreRatio = totalScore == 0 ? 0 : (double) studentScore / totalScore;
String formattedScoreRatio = String.format("%.2f", scoreRatio);
return formattedScoreRatio;
}
// 对比学生提交内容与试题得分点
static Integer compareStuAndTestFiles(List<ExamQuestionAnswer> answerList, Map<String, String> stuFiles) {
int totalScore = 0; // 记录总得分
for (ExamQuestionAnswer answer : answerList) {
String filePath = answer.getContent(); // 试题文件路径
String checkType = answer.getContentIn(); // 考察类型(考察删除 / 考察名称 / 考察属性)
int currentScore = Integer.parseInt(answer.getScoreRate()); // 当前得分
boolean isCorrect = false;
if ("考察删除".equals(checkType)) {
// 如果学生提交中不存在该文件,则得分
isCorrect = !stuFiles.containsKey(filePath);
} else if ("考察名称".equals(checkType)) {
// 如果学生提交中存在该文件,则得分
isCorrect = stuFiles.containsKey(filePath);
} else if ("考察属性".equals(checkType)) {
// 如果学生提交中存在该文件,且属性匹配,则得分
if (stuFiles.containsKey(filePath)) {
String studentAttrs = stuFiles.get(filePath); // 学生提交的属性
String expectedAttrs = answer.getScoreRate(); // 试题中期望的属性
isCorrect = studentAttrs.equals(expectedAttrs);
}
}
// 如果正确,则累加总分
if (isCorrect) {
totalScore += currentScore;
System.out.println(answer.getContent() + " -> " + answer.getContentIn() + " -> 得分权值:" + answer.getScoreRate()+"-> 是否得分:"+isCorrect);
}else {
System.out.println(answer.getContent() + " -> " + answer.getContentIn() + " -> 得分权值:" + answer.getScoreRate()+"-> 是否得分:"+isCorrect);
}
}
//返回累加的总分
return totalScore;
}
}

View File

@@ -0,0 +1,17 @@
package pc.exam.pp.module.judgement.controller.service.file;
import pc.exam.pp.module.exam.dal.dataobject.ExamQuestionAnswer;
import java.io.IOException;
import java.util.List;
public interface IFileServerice {
List<ExamQuestionAnswer> get_file_point() throws IOException;
String run_file_point() throws IOException;
}

View File

@@ -0,0 +1,8 @@
package pc.exam.pp.module.judgement.controller.service.mysql;
public interface IMysqlServerice {
int Judgement();
}

View File

@@ -0,0 +1,280 @@
package pc.exam.pp.module.judgement.controller.service.mysql;
import org.springframework.stereotype.Service;
import pc.exam.pp.module.judgement.controller.utils.Mysql.MySQLExporterUtil;
import pc.exam.pp.module.judgement.controller.utils.Mysql.SQLComparatorUtil;
import pc.exam.pp.module.judgement.controller.utils.Mysql.SqlFileProcessor;
import java.awt.*;
import java.io.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class MysqlServericeImpl implements IMysqlServerice {
List<String> backupFiles;
String databaseName;
@Override
public int Judgement() {
backupFiles = new ArrayList<>(); // 用于存储生成的备份文件路径
//todo
//根据考生考试的试卷id来获取答案文件路径、 原始 数据库SQL 文件路径
// 文件路径
String filePath = "C:\\Users\\20373\\Desktop\\202504211120_mysql\\71\\MYS_002_114\\结果素材\\结果素材\\Teacher\\答案.txt"; // 答案文件路径
String answerPath = "C:\\Users\\20373\\Desktop\\202504211120_mysql\\71\\MYS_002_114\\考试素材\\考试素材\\Student\\db\\db_hrm.sql"; // 原始 SQL 文件路径
// 生成随机数据库名,临时使用
databaseName = "db_" + UUID.randomUUID().toString().replace("-", "").substring(0, 6);
// 连接到 MySQL 默认数据库
String url = "jdbc:mysql://localhost:3306/mysql?useSSL=false&serverTimezone=Asia/Shanghai";
String user = "root";
String password = "123";
int similarityRatio = 0;
try {
// **连接到 MySQL 默认数据库,创建新的数据库**
try (Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement()) {
String createDbSql = "CREATE DATABASE " + databaseName;
stmt.executeUpdate(createDbSql);
System.out.println("已创建数据库:" + databaseName);
}
// **建立连接到新创建的数据库**
String newDbUrl = "jdbc:mysql://localhost:3306/" + databaseName + "?useSSL=false&serverTimezone=Asia/Shanghai";
// 解析 SQL 文件
Map<String, List<String>> sqlMap = SqlFileProcessor.processSqlFile(filePath);
//把select语句和其他语句分开
List<String> selectStatements = sqlMap.getOrDefault("SELECT", new ArrayList<>());
List<String> otherStatements = sqlMap.getOrDefault("OTHER", new ArrayList<>());
// **通过命令行执行 SQL 文件**
boolean sqlFileExecuted = executeSqlFileUsingCommandLine(answerPath, databaseName, user, password);
// 执行完 SQL 后,进行数据库导出
MySQLExporterUtil exporter = new MySQLExporterUtil(databaseName, user, password, "backup_0");
exporter.export();
// 如果 SQL 文件执行成功,执行其他 SQL 语句
if (sqlFileExecuted) {
executeOtherSqlStatements(otherStatements, newDbUrl, user, password);
}
//todo 后面改成试题数据库,这里是完整正确答案数据库
//执行完所有的sql语句后把结果数据库导出来
MySQLExporterUtil exporterDataBase = new MySQLExporterUtil(databaseName, user, password, databaseName);
exporterDataBase.export();
// 导出的结果数据库文件路径
String exportedDatabaseFilePath = "./" + databaseName + ".sql";
//String exportedDatabaseFilePath = "C:\\Users\\20373\\Desktop\\exam-master\\exam-api\\"+"db_bfd3b5bc"+".sql";
// 计算与第一个文件的比较结果
similarityRatio = compareBackupFiles(exportedDatabaseFilePath, backupFiles);
// // 输出匹配比例
System.out.println("匹配比例个数: " + similarityRatio);
//删除临时创建的数据库databaseName
try (Connection conn = DriverManager.getConnection(newDbUrl, user, password);
Statement stmt = conn.createStatement()) {
String dropDbSql = "DROP DATABASE " + databaseName;
stmt.executeUpdate(dropDbSql);
System.out.println("已删除数据库:" + databaseName);
}
//比较select语句正确个数
//后期直接根据考生文件路径和试卷答案路径来比对select文件
String filepath1 = "C:\\Users\\20373\\Desktop\\1.txt";
String filepath2 = "C:\\Users\\20373\\Desktop\\2.txt";
boolean isEquivalent = SQLComparatorUtil.compareSQLResults(filepath1, filepath2);
if (isEquivalent) {
++similarityRatio;
}
} catch (IOException | SQLException e) {
e.printStackTrace();
}
return similarityRatio;
}
/**
* 通过命令行执行 SQL 文件
*/
private static boolean executeSqlFileUsingCommandLine(String filePath, String databaseName, String user, String password) throws IOException {
// 构建 MySQL 命令
String command = String.format("mysql -u %s -p%s %s < %s", user, password, databaseName, filePath);
// 使用 ProcessBuilder 执行命令
ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/c", command);
processBuilder.inheritIO(); // 将输入输出重定向到当前控制台
Process process = processBuilder.start();
try {
// 等待命令执行完成
int exitCode = process.waitFor();
if (exitCode == 0) {
System.out.println("SQL 文件执行成功:" + filePath);
return true; // 返回 true 表示执行成功
} else {
System.err.println("SQL 文件执行失败:" + filePath);
return false; // 返回 false 表示执行失败
}
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
/**
* 执行其他 SQL 语句
*/
private void executeOtherSqlStatements(List<String> otherStatements, String dbUrl, String user, String password) throws SQLException {
int fileName = 1; // 初始化 fileName
String outputPath = "./"; // 备份文件存放路径
backupFiles.add(outputPath+ "backup_" + 0 + ".sql");
try (Connection conn = DriverManager.getConnection(dbUrl, user, password);
Statement stmt = conn.createStatement()) {
for (String sql : otherStatements) {
String trimmedSql = sql.trim();
if (!trimmedSql.isEmpty() && !trimmedSql.startsWith("--")) { // 忽略空行和注释
try {
stmt.executeUpdate(trimmedSql); // 执行 SQL 语句
System.out.println("成功执行 SQL: " + trimmedSql);
// 执行完 SQL 后,进行数据库导出
String backupFile = outputPath + "backup_" + fileName + ".sql";
MySQLExporterUtil exporter = new MySQLExporterUtil(databaseName, user, password, "backup_" + fileName);
exporter.export();
// 将备份文件路径添加到备份文件列表中
backupFiles.add(backupFile);
fileName++; // 每执行一次 SQL 语句fileName 递增
} catch (SQLException e) {
System.err.println("执行 SQL 失败: " + trimmedSql);
e.printStackTrace();
}
}
}
// 所有 SQL 执行完后,反向顺序进行备份文件比较
for (int i = backupFiles.size() -1; i > 0; i--) {
String currentBackupFile = backupFiles.get(i);
String previousBackupFile = backupFiles.get(i - 1);
filterBackupFile(previousBackupFile, currentBackupFile); // 过滤并保留差异部分
}
}
System.out.println("所有 SQL 语句执行完成");
}
/**
* 过滤 `backup_X.sql`,只保留与 `backup_0.sql` 不同的 SQL 语句(按 `;` 进行断句)
*/
public static void filterBackupFile(String baseFile, String newFile) {
try {
Set<String> baseSqlSet = readSqlStatements(baseFile);
List<String> newSqlList = readSqlStatements(newFile).stream()
.filter(sql -> !baseSqlSet.contains(sql)) // 只保留 `backup_0.sql` 中没有的 SQL
.collect(Collectors.toList());
// 重新写入 `backup_X.sql`,仅包含不同的 SQL 语句
try (BufferedWriter writer = new BufferedWriter(new FileWriter(newFile))) {
for (String sql : newSqlList) {
writer.write(sql + ";");
writer.newLine();
}
}
System.out.println("已过滤 " + newFile + ",仅保留与 " + baseFile + " 不同的 SQL 语句");
} catch (IOException e) {
System.err.println("过滤文件失败:" + newFile);
e.printStackTrace();
}
}
/**
* 比较备份文件与导出的数据库备份之间的差异,计算匹配比例
*/
public static int compareBackupFiles(String exportedDatabaseFilePath, List<String> backupFiles) {
// 读取导出的数据库备份文件并按 SQL 语句进行分割
Set<String> exportedSqlStatements = readSqlStatements(exportedDatabaseFilePath);
// 用于存储符合条件的文件数量
int matchedCount = 0;
// 遍历其他备份文件进行比较
for (int i = 1; i < backupFiles.size(); i++) {
String currentFilePath = backupFiles.get(i);
// 读取当前备份文件并按 SQL 语句进行分割
Set<String> currentSqlStatements = readSqlStatements(currentFilePath);
// 输出当前备份文件中的所有 SQL 语句
System.out.println("文件 " + currentFilePath + " 中的 SQL 语句:");
for (String sql : currentSqlStatements) {
System.out.println(sql);
}
// 判断当前备份文件中的每条 SQL 语句是否都在导出的数据库备份中
boolean allMatched = true; // 假设所有 SQL 语句都匹配
for (String currentSql : currentSqlStatements) {
if (!exportedSqlStatements.contains(currentSql)) {
// 如果有任何一条 SQL 语句不在导出的数据库备份中,则设置 allMatched 为 false
allMatched = false;
System.out.println("未匹配的 SQL 语句: " + currentSql); // 输出未匹配的 SQL 语句
}
}
// 如果所有 SQL 语句都匹配,则计数器增加
if (allMatched) {
matchedCount++;
}
// 输出当前文件的匹配率
System.out.println("文件 " + currentFilePath + " 与导出数据库的匹配结果: " + allMatched);
}
// 返回匹配的文件数量
return matchedCount;
}
/**
* 读取 SQL 文件并按 `;` 进行分割,返回去重后的 SQL 语句集合
*/
private static Set<String> readSqlStatements(String filePath) {
StringBuilder content = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = reader.readLine()) != null) {
String trimmedLine = line.trim();
if (!trimmedLine.isEmpty() && !trimmedLine.startsWith("--")) { // 忽略空行和注释
content.append(trimmedLine).append(" "); // 以空格拼接,防止换行影响 SQL 语句完整性
}
}
} catch (IOException e) {
System.err.println("读取文件失败:" + filePath);
e.printStackTrace();
}
// 按 `;` 进行分割,并去重
return Arrays.stream(content.toString().split(";"))
.map(String::trim)
.filter(sql -> !sql.isEmpty())
.collect(Collectors.toSet());
}
}

View File

@@ -0,0 +1,123 @@
package pc.exam.pp.module.judgement.controller.utils.Mysql;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.*;
public class MySQLExporterUtil {
private final String databaseName;
private final String jdbcUrl;
private final String user;
private final String password;
private final String outputFile;
public MySQLExporterUtil(String databaseName, String user, String password, String fileName) {
this.databaseName = databaseName;
this.jdbcUrl = "jdbc:mysql://localhost:3306/" + databaseName + "?useSSL=false&serverTimezone=Asia/Shanghai";
this.user = user;
this.password = password;
this.outputFile = fileName + ".sql";
}
public void export() {
File file = new File("");
File parentDir = file.getParentFile();
if (parentDir != null && !parentDir.exists()) {
parentDir.mkdirs(); // 自动创建父级目录
}
try (Connection conn = DriverManager.getConnection(jdbcUrl, user, password);
FileWriter writer = new FileWriter(outputFile)) {
switchDatabase(conn);
exportTables(conn, writer);
exportViews(conn, writer);
System.out.println("导出成功!文件路径: " + outputFile);
} catch (Exception e) {
e.printStackTrace();
}
}
private void switchDatabase(Connection conn) throws SQLException {
try (Statement stmt = conn.createStatement()) {
stmt.execute("USE " + databaseName);
}
}
private void exportTables(Connection conn, FileWriter writer) throws SQLException, IOException {
DatabaseMetaData meta = conn.getMetaData();
try (ResultSet tables = meta.getTables(databaseName, null, "%", new String[]{"TABLE"})) {
while (tables.next()) {
String tableName = tables.getString("TABLE_NAME");
writer.write(getCreateTableSQL(conn, tableName) + ";\n\n");
exportTableData(conn, tableName, writer);
}
}
}
private String getCreateTableSQL(Connection conn, String tableName) throws SQLException {
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SHOW CREATE TABLE " + tableName)) {
// 获取 CREATE TABLE 语句
String createTableSQL = rs.next() ? rs.getString("Create Table") : "";
// 移除 AUTO_INCREMENT 部分
createTableSQL = createTableSQL.replaceAll("AUTO_INCREMENT=\\d+", "");
// 移除 ENGINE, CHARSET 和 ROW_FORMAT 部分
createTableSQL = createTableSQL.replaceAll("ENGINE=[^ ]+|DEFAULT CHARSET=[^ ]+|ROW_FORMAT=[^ ]+", "");
// 清理多余的空格
createTableSQL = createTableSQL.trim();
return createTableSQL;
}
}
private void exportTableData(Connection conn, String tableName, FileWriter writer) throws SQLException, IOException {
String query = "SELECT * FROM " + tableName;
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query)) {
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
while (rs.next()) {
StringBuilder sb = new StringBuilder("INSERT INTO " + tableName + " (");
for (int i = 1; i <= columnCount; i++) {
sb.append(metaData.getColumnName(i)).append(i < columnCount ? ", " : ") VALUES (");
}
for (int i = 1; i <= columnCount; i++) {
sb.append(rs.getString(i) == null ? "NULL" : "'" + rs.getString(i).replace("'", "''") + "'")
.append(i < columnCount ? ", " : ");\n");
}
writer.write(sb.toString());
}
}
}
private void exportViews(Connection conn, FileWriter writer) throws SQLException, IOException {
DatabaseMetaData meta = conn.getMetaData();
try (ResultSet views = meta.getTables(databaseName, null, "%", new String[]{"VIEW"})) {
while (views.next()) {
String viewName = views.getString("TABLE_NAME");
writer.write(getCreateViewSQL(conn, viewName) + ";\n\n");
}
}
}
private String getCreateViewSQL(Connection conn, String viewName) throws SQLException {
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SHOW CREATE VIEW " + viewName)) {
return rs.next() ? rs.getString("Create View") : "";
}
}
}

View File

@@ -0,0 +1,149 @@
package pc.exam.pp.module.judgement.controller.utils.Mysql;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* SQL 查询结果比较工具类
*/
public class SQLComparatorUtil {
/**
* 比较两个 SQL 文件中的查询结果
*
* @param filepath1 SQL 文件 1 的路径
* @param filepath2 SQL 文件 2 的路径
* @return 是否等价
*/
public static boolean compareSQLResults(String filepath1, String filepath2) throws IOException, SQLException {
// 读取 SQL 语句
String sql1 = readSQLFromFile(filepath1);
String sql2 = readSQLFromFile(filepath2);
System.out.println("SQL 1: " + sql1);
System.out.println("SQL 2: " + sql2);
// 执行 SQL 查询
List<List<String>> result1 = executeQuery(sql1);
List<List<String>> result2 = executeQuery(sql2);
// 打印查询结果
System.out.println("执行 SQL 1 的查询结果:");
printResult(result1);
System.out.println("\n执行 SQL 2 的查询结果:");
printResult(result2);
// 比较查询结果
return compareResults(result1, result2);
}
/**
* 读取 SQL 文件中的内容
*/
private static String readSQLFromFile(String filePath) throws IOException {
StringBuilder sql = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = reader.readLine()) != null) {
sql.append(line).append(" ");
}
}
return sql.toString().trim();
}
/**
* 执行 SQL 查询并返回结果
*/
private static List<List<String>> executeQuery(String sql) throws SQLException {
List<List<String>> result = new ArrayList<>();
try (Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/test2?useSSL=false&serverTimezone=Asia/Shanghai",
"root", "123");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
// 获取列数和列名
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
// 获取列名
List<String> columnNames = new ArrayList<>();
for (int i = 1; i <= columnCount; i++) {
columnNames.add(metaData.getColumnLabel(i));
}
result.add(columnNames); // 将列名添加为结果的第一行
// 遍历结果集
while (rs.next()) {
List<String> row = new ArrayList<>();
for (int i = 1; i <= columnCount; i++) {
row.add(rs.getString(i));
}
result.add(row);
}
}
return result;
}
/**
* 打印查询结果
*/
private static void printResult(List<List<String>> result) {
if (result.isEmpty()) {
System.out.println("查询结果为空");
} else {
for (int i = 0; i < result.size(); i++) {
List<String> row = result.get(i);
if (i == 0) { // 第一次打印是列名
System.out.println("列名:");
}
for (String value : row) {
System.out.print(value + "\t");
}
System.out.println();
}
}
}
/**
* 比较两个 SQL 查询的结果
*/
private static boolean compareResults(List<List<String>> result1, List<List<String>> result2) {
if (result1.isEmpty() || result2.isEmpty()) {
return result1.isEmpty() && result2.isEmpty();
}
// 获取列名并比较(顺序无关)
Set<String> columnSet1 = new HashSet<>(result1.get(0));
Set<String> columnSet2 = new HashSet<>(result2.get(0));
if (!columnSet1.equals(columnSet2)) {
return false;
}
// 获取数据行(去除列名)
List<List<String>> rows1 = result1.subList(1, result1.size());
List<List<String>> rows2 = result2.subList(1, result2.size());
// 使用 Set 存储每行数据并比较
Set<Set<String>> rowSet1 = new HashSet<>();
for (List<String> row : rows1) {
rowSet1.add(new HashSet<>(row));
}
Set<Set<String>> rowSet2 = new HashSet<>();
for (List<String> row : rows2) {
rowSet2.add(new HashSet<>(row));
}
// 比较行数据(无顺序)
return rowSet1.equals(rowSet2);
}
}

View File

@@ -0,0 +1,73 @@
package pc.exam.pp.module.judgement.controller.utils.Mysql;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 工具类:把答案文件按照 select语句和其他语句取出
*/
public class SqlFileProcessor {
public static Map<String, List<String>> processSqlFile(String filePath) throws IOException {
List<String> sqlStatements = readSQLFromFile(filePath);
List<String> selectStatements = new ArrayList<>();
List<String> otherStatements = new ArrayList<>();
for (String sql : sqlStatements) {
if (sql.trim().toUpperCase().startsWith("SELECT")) {
selectStatements.add(sql);
} else {
otherStatements.add(sql);
}
}
Map<String, List<String>> result = new HashMap<>();
result.put("SELECT", selectStatements);
result.put("OTHER", otherStatements);
return result;
}
private static List<String> readSQLFromFile(String filePath) throws IOException {
List<String> sqlStatements = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "UTF-8"))) {
StringBuilder sqlStatement = new StringBuilder();
String line;
boolean insideBlockComment = false;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.contains("/*")) {
insideBlockComment = true;
line = line.split("/\\*")[0].trim();
}
if (line.contains("*/")) {
insideBlockComment = false;
line = line.split("\\*/")[1].trim();
}
if (line.startsWith("--") || line.isEmpty() || insideBlockComment) {
continue;
}
sqlStatement.append(line).append(" ");
if (line.endsWith(";")) {
sqlStatements.add(sqlStatement.toString().trim());
sqlStatement.setLength(0);
}
}
if (sqlStatement.length() > 0) {
sqlStatements.add(sqlStatement.toString().trim());
}
}
return sqlStatements;
}
}

View File

@@ -0,0 +1,77 @@
package pc.exam.pp.module.judgement.controller.utils.brower;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.FileReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* 检查书签是否存在工具类
*/
public class BookmarkChecker {
/**
* 检查书签是否存在
*
* @param bookmarkFile 书签文件路径
* @param bookmarkName 要检查的书签名称
* @return 是否存在该书签
*/
public static boolean bookmarkExists(String bookmarkFile, String bookmarkName) {
Path path = Paths.get(bookmarkFile);
if (!Files.exists(path)) {
System.out.println("❌ 没有找到书签文件!");
return false;
}
JsonObject root;
try (FileReader reader = new FileReader(bookmarkFile)) {
root = JsonParser.parseReader(reader).getAsJsonObject();
} catch (Exception e) {
System.err.println("❌ 读取书签文件失败:" + e.getMessage());
return false;
}
JsonObject roots = root.getAsJsonObject("roots");
if (roots.has("bookmark_bar")) {
if (searchBookmark(roots.getAsJsonObject("bookmark_bar"), bookmarkName)) {
return true;
}
}
if (roots.has("other")) {
if (searchBookmark(roots.getAsJsonObject("other"), bookmarkName)) {
return true;
}
}
return false;
}
// 递归查找书签
private static boolean searchBookmark(JsonObject node, String nameToFind) {
if (!node.has("children")) return false;
JsonArray children = node.getAsJsonArray("children");
for (JsonElement element : children) {
JsonObject child = element.getAsJsonObject();
String name = child.get("name").getAsString();
String type = child.get("type").getAsString();
if ("url".equals(type) && name.equals(nameToFind)) {
return true;
} else if ("folder".equals(type) && child.has("children")) {
if (searchBookmark(child, nameToFind)) {
return true;
}
}
}
return false;
}
}

View File

@@ -0,0 +1,88 @@
package pc.exam.pp.module.judgement.controller.utils.brower;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.FileReader;
import java.io.FileWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* 删除指定书签工具类
*/
public class BookmarkDeleter {
/**
* 删除指定书签
*
* @param bookmarkFile 书签文件路径
* @param bookmarkNameToDelete 要删除的书签名称
* @return 是否删除成功
*/
public static boolean deleteBookmarkByName(String bookmarkFile, String bookmarkNameToDelete) {
Path path = Paths.get(bookmarkFile);
if (!Files.exists(path)) {
System.out.println("❌ 没有找到书签文件!");
return false;
}
JsonObject root;
try (FileReader reader = new FileReader(bookmarkFile)) {
root = JsonParser.parseReader(reader).getAsJsonObject();
} catch (Exception e) {
System.err.println("❌ 读取书签文件失败:" + e.getMessage());
return false;
}
JsonObject roots = root.getAsJsonObject("roots");
boolean deleted = false;
if (roots.has("bookmark_bar")) {
deleted |= deleteUrlBookmark(roots.getAsJsonObject("bookmark_bar"), bookmarkNameToDelete);
}
if (roots.has("other")) {
deleted |= deleteUrlBookmark(roots.getAsJsonObject("other"), bookmarkNameToDelete);
}
if (deleted) {
try (FileWriter writer = new FileWriter(bookmarkFile)) {
new GsonBuilder().setPrettyPrinting().create().toJson(root, writer);
} catch (Exception e) {
System.err.println("❌ 写入书签文件失败:" + e.getMessage());
return false;
}
System.out.println("✅ 成功删除书签: " + bookmarkNameToDelete);
} else {
System.out.println("⚠️ 未找到该书签: " + bookmarkNameToDelete);
}
return deleted;
}
// 私有方法只删除普通网页书签type = url支持递归
private static boolean deleteUrlBookmark(JsonObject node, String nameToDelete) {
if (!node.has("children")) return false;
JsonArray children = node.getAsJsonArray("children");
boolean deleted = false;
for (int i = children.size() - 1; i >= 0; i--) {
JsonObject child = children.get(i).getAsJsonObject();
String name = child.get("name").getAsString();
String type = child.get("type").getAsString();
if ("url".equals(type) && name.equals(nameToDelete)) {
children.remove(i);
deleted = true;
} else if ("folder".equals(type) && child.has("children")) {
deleted |= deleteUrlBookmark(child, nameToDelete); // 递归
}
}
return deleted;
}
}

View File

@@ -0,0 +1,91 @@
package pc.exam.pp.module.judgement.controller.utils.file;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 获取文件夹差异工具类
*/
public class GetDifferencesBetweenFolders {
// 获取两个文件夹的差异
public static Map<String, String> getDifferencesBetweenFolders( String folder1, String folder2) throws IOException {
Path path1 = Paths.get(folder1);
Path path2 = Paths.get( folder2);
Map<String, String> files1 = listFilesAndFoldersWithAttributes(path1);
Map<String, String> files2 = listFilesAndFoldersWithAttributes(path2);
Map<String, String> differences = new LinkedHashMap<>();
// 找出仅存在于 folder1 的文件
for (String file : files1.keySet()) {
if (!files2.containsKey(file)) {
differences.put(file, "仅存在于 " + folder1);
} else {
String file1Attributes = files1.get(file);
String file2Attributes = files2.get(file);
if (!file1Attributes.equals(file2Attributes)) {
differences.put(file, "属性不同: " + file1Attributes + " vs " + file2Attributes);
}
}
}
// 找出仅存在于 folder2 的文件
for (String file : files2.keySet()) {
if (!files1.containsKey(file)) {
differences.put(file, "仅存在于 " + folder2);
}
}
return differences;
}
// 列出文件夹下的所有文件及其属性
public static Map<String, String> listFilesAndFoldersWithAttributes(Path folder) throws IOException {
if (!Files.exists(folder)) return Collections.emptyMap();
try (Stream<Path> stream = Files.walk(folder)) {
return stream.collect(Collectors.toMap(
path -> folder.relativize(path).toString(),
GetDifferencesBetweenFolders::getFileAttributes,
(a, b) -> a,
LinkedHashMap::new
));
}
}
// 在 getFileAttributes 方法中,只返回文件的属性,如果是文件夹则返回特殊标识
static String getFileAttributes(Path path) {
try {
if (Files.isDirectory(path)) {
return "文件夹"; // 特别标记为文件夹
} else {
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
boolean isHidden = Files.isHidden(path);
boolean isReadable = Files.isReadable(path);
boolean isWritable = Files.isWritable(path);
return String.format("大小: %dB, 隐藏: %b, 可读: %b, 可写: %b",
attrs.size(), isHidden, isReadable, isWritable);
}
} catch (IOException e) {
return "无法获取属性";
}
}
}