【新增】MYSQL、浏览器、文件操作试题判分第一版,操作日志文件控制台输出
This commit is contained in:
@@ -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());
|
||||
}
|
||||
|
||||
}
|
@@ -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());
|
||||
}
|
||||
|
||||
}
|
@@ -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());
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package pc.exam.pp.module.judgement.controller.service.browser;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface IBrowserServerice {
|
||||
String Judgement() throws IOException;
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package pc.exam.pp.module.judgement.controller.service.mysql;
|
||||
|
||||
|
||||
public interface IMysqlServerice {
|
||||
int Judgement();
|
||||
|
||||
|
||||
}
|
@@ -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());
|
||||
}
|
||||
|
||||
}
|
@@ -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") : "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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 "无法获取属性";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
Reference in New Issue
Block a user