From 8fc67c245768d73df4ac9289a6453a1f2ba3fee6 Mon Sep 17 00:00:00 2001 From: "YOHO\\20373" <2037305722@qq.com> Date: Fri, 25 Apr 2025 17:56:48 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91MYSQL?= =?UTF-8?q?=E3=80=81=E6=B5=8F=E8=A7=88=E5=99=A8=E3=80=81=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E8=AF=95=E9=A2=98=E5=88=A4=E5=88=86=E7=AC=AC?= =?UTF-8?q?=E4=B8=80=E7=89=88=EF=BC=8C=E6=93=8D=E4=BD=9C=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=8E=A7=E5=88=B6=E5=8F=B0=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mapper/exam/EducationPaperTaskMapper.xml | 3 +- .../admin/Browser/BrowserComtroller.java | 29 ++ .../admin/Mysql/MysqlController.java | 28 ++ .../WindowFile/WindowsFileController.java | 44 +++ .../service/browser/BrowserServericeImpl.java | 108 +++++++ .../service/browser/IBrowserServerice.java | 8 + .../service/file/FileServericeImpl.java | 126 ++++++++ .../service/file/IFileServerice.java | 17 ++ .../service/mysql/IMysqlServerice.java | 8 + .../service/mysql/MysqlServericeImpl.java | 280 ++++++++++++++++++ .../utils/Mysql/MySQLExporterUtil.java | 123 ++++++++ .../utils/Mysql/SQLComparatorUtil.java | 149 ++++++++++ .../utils/Mysql/SqlFileProcessor.java | 73 +++++ .../utils/brower/BookmarkChecker.java | 77 +++++ .../utils/brower/BookmarkDeleter.java | 88 ++++++ .../file/GetDifferencesBetweenFolders.java | 91 ++++++ 16 files changed, 1250 insertions(+), 2 deletions(-) create mode 100644 exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/Browser/BrowserComtroller.java create mode 100644 exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/Mysql/MysqlController.java create mode 100644 exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/WindowFile/WindowsFileController.java create mode 100644 exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/browser/BrowserServericeImpl.java create mode 100644 exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/browser/IBrowserServerice.java create mode 100644 exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/file/FileServericeImpl.java create mode 100644 exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/file/IFileServerice.java create mode 100644 exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/mysql/IMysqlServerice.java create mode 100644 exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/mysql/MysqlServericeImpl.java create mode 100644 exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/Mysql/MySQLExporterUtil.java create mode 100644 exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/Mysql/SQLComparatorUtil.java create mode 100644 exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/Mysql/SqlFileProcessor.java create mode 100644 exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/brower/BookmarkChecker.java create mode 100644 exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/brower/BookmarkDeleter.java create mode 100644 exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/file/GetDifferencesBetweenFolders.java diff --git a/exam-module-exam/exam-module-exam-biz/src/main/resources/mapper/exam/EducationPaperTaskMapper.xml b/exam-module-exam/exam-module-exam-biz/src/main/resources/mapper/exam/EducationPaperTaskMapper.xml index 921fe7a7..e4a1472f 100644 --- a/exam-module-exam/exam-module-exam-biz/src/main/resources/mapper/exam/EducationPaperTaskMapper.xml +++ b/exam-module-exam/exam-module-exam-biz/src/main/resources/mapper/exam/EducationPaperTaskMapper.xml @@ -25,8 +25,6 @@ - - @@ -64,6 +62,7 @@ SELECT sp_id, parent_id, ancestors, sp_name, order_num FROM knowledge_points WHERE FIND_IN_SET(#{id}, ancestors) + and del_flag='0' ORDER BY order_num; diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/Browser/BrowserComtroller.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/Browser/BrowserComtroller.java new file mode 100644 index 00000000..1ba0adca --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/Browser/BrowserComtroller.java @@ -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()); + } + +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/Mysql/MysqlController.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/Mysql/MysqlController.java new file mode 100644 index 00000000..39add505 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/Mysql/MysqlController.java @@ -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()); + } + +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/WindowFile/WindowsFileController.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/WindowFile/WindowsFileController.java new file mode 100644 index 00000000..f700a551 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/WindowFile/WindowsFileController.java @@ -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()); + } + +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/browser/BrowserServericeImpl.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/browser/BrowserServericeImpl.java new file mode 100644 index 00000000..e5a8291d --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/browser/BrowserServericeImpl.java @@ -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 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 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 answerList, Map 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; + } +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/browser/IBrowserServerice.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/browser/IBrowserServerice.java new file mode 100644 index 00000000..eb6b260a --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/browser/IBrowserServerice.java @@ -0,0 +1,8 @@ +package pc.exam.pp.module.judgement.controller.service.browser; + +import java.io.IOException; + +public interface IBrowserServerice { + String Judgement() throws IOException; + +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/file/FileServericeImpl.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/file/FileServericeImpl.java new file mode 100644 index 00000000..a0c3fa85 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/file/FileServericeImpl.java @@ -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 get_file_point() throws IOException { + List answerList = new ArrayList<>(); + AtomicInteger sortCounter = new AtomicInteger(1); // 计数器 + String path1 = BASE_DIR + "shucai"; + String path2 = BASE_DIR + "win"; + + // 获取 shucai 和 win 文件夹的差异 + Map differences = GetDifferencesBetweenFolders.getDifferencesBetweenFolders(path1, path2); + + List 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 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 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 answerList, Map 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; + } +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/file/IFileServerice.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/file/IFileServerice.java new file mode 100644 index 00000000..89068a13 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/file/IFileServerice.java @@ -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 get_file_point() throws IOException; + + String run_file_point() throws IOException; + + +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/mysql/IMysqlServerice.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/mysql/IMysqlServerice.java new file mode 100644 index 00000000..868d380e --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/mysql/IMysqlServerice.java @@ -0,0 +1,8 @@ +package pc.exam.pp.module.judgement.controller.service.mysql; + + +public interface IMysqlServerice { + int Judgement(); + + +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/mysql/MysqlServericeImpl.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/mysql/MysqlServericeImpl.java new file mode 100644 index 00000000..17808542 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/service/mysql/MysqlServericeImpl.java @@ -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 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> sqlMap = SqlFileProcessor.processSqlFile(filePath); + //把select语句和其他语句分开 + List selectStatements = sqlMap.getOrDefault("SELECT", new ArrayList<>()); + List 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 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 baseSqlSet = readSqlStatements(baseFile); + List 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 backupFiles) { + // 读取导出的数据库备份文件并按 SQL 语句进行分割 + Set exportedSqlStatements = readSqlStatements(exportedDatabaseFilePath); + + // 用于存储符合条件的文件数量 + int matchedCount = 0; + + // 遍历其他备份文件进行比较 + for (int i = 1; i < backupFiles.size(); i++) { + String currentFilePath = backupFiles.get(i); + + // 读取当前备份文件并按 SQL 语句进行分割 + Set 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 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()); + } + + } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/Mysql/MySQLExporterUtil.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/Mysql/MySQLExporterUtil.java new file mode 100644 index 00000000..18d32196 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/Mysql/MySQLExporterUtil.java @@ -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") : ""; + } + } + + +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/Mysql/SQLComparatorUtil.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/Mysql/SQLComparatorUtil.java new file mode 100644 index 00000000..eb142a11 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/Mysql/SQLComparatorUtil.java @@ -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> result1 = executeQuery(sql1); + List> 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> executeQuery(String sql) throws SQLException { + List> 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 columnNames = new ArrayList<>(); + for (int i = 1; i <= columnCount; i++) { + columnNames.add(metaData.getColumnLabel(i)); + } + result.add(columnNames); // 将列名添加为结果的第一行 + + // 遍历结果集 + while (rs.next()) { + List 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> result) { + if (result.isEmpty()) { + System.out.println("查询结果为空"); + } else { + for (int i = 0; i < result.size(); i++) { + List 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> result1, List> result2) { + if (result1.isEmpty() || result2.isEmpty()) { + return result1.isEmpty() && result2.isEmpty(); + } + + // 获取列名并比较(顺序无关) + Set columnSet1 = new HashSet<>(result1.get(0)); + Set columnSet2 = new HashSet<>(result2.get(0)); + if (!columnSet1.equals(columnSet2)) { + return false; + } + + // 获取数据行(去除列名) + List> rows1 = result1.subList(1, result1.size()); + List> rows2 = result2.subList(1, result2.size()); + + // 使用 Set 存储每行数据并比较 + Set> rowSet1 = new HashSet<>(); + for (List row : rows1) { + rowSet1.add(new HashSet<>(row)); + } + + Set> rowSet2 = new HashSet<>(); + for (List row : rows2) { + rowSet2.add(new HashSet<>(row)); + } + + // 比较行数据(无顺序) + return rowSet1.equals(rowSet2); + } + +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/Mysql/SqlFileProcessor.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/Mysql/SqlFileProcessor.java new file mode 100644 index 00000000..2ea14655 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/Mysql/SqlFileProcessor.java @@ -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> processSqlFile(String filePath) throws IOException { + List sqlStatements = readSQLFromFile(filePath); + List selectStatements = new ArrayList<>(); + List otherStatements = new ArrayList<>(); + + for (String sql : sqlStatements) { + if (sql.trim().toUpperCase().startsWith("SELECT")) { + selectStatements.add(sql); + } else { + otherStatements.add(sql); + } + } + + Map> result = new HashMap<>(); + result.put("SELECT", selectStatements); + result.put("OTHER", otherStatements); + + return result; + } + + private static List readSQLFromFile(String filePath) throws IOException { + List 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; + } +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/brower/BookmarkChecker.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/brower/BookmarkChecker.java new file mode 100644 index 00000000..d899b5b3 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/brower/BookmarkChecker.java @@ -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; + } +} + diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/brower/BookmarkDeleter.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/brower/BookmarkDeleter.java new file mode 100644 index 00000000..534d37b1 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/brower/BookmarkDeleter.java @@ -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; + } +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/file/GetDifferencesBetweenFolders.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/file/GetDifferencesBetweenFolders.java new file mode 100644 index 00000000..fd354ae8 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/utils/file/GetDifferencesBetweenFolders.java @@ -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 getDifferencesBetweenFolders( String folder1, String folder2) throws IOException { + + Path path1 = Paths.get(folder1); + Path path2 = Paths.get( folder2); + + Map files1 = listFilesAndFoldersWithAttributes(path1); + Map files2 = listFilesAndFoldersWithAttributes(path2); + + Map 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 listFilesAndFoldersWithAttributes(Path folder) throws IOException { + if (!Files.exists(folder)) return Collections.emptyMap(); + try (Stream 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 "无法获取属性"; + } + } + + + +}