成功接入了算法,实现了点击提交按钮后同步查重并返回查重信息的功能,但前端逻辑尚待修正。

删去了部分不需要的依赖包以解决冲突问题。
之后处理异步操作。
This commit is contained in:
musteven 2025-04-19 21:35:26 +08:00
parent 12bd542196
commit 4522512654
14 changed files with 601 additions and 46 deletions

View File

@ -38,6 +38,8 @@ export default {
getSubmitReport:(config) => request.get('/problem/getSubmitReport', config),//获取学生已上传报告信息 getSubmitReport:(config) => request.get('/problem/getSubmitReport', config),//获取学生已上传报告信息
pickExam: (data) => request.post('/problem/pickExam', data),
//文件处理部分 //文件处理部分
uploadFiles: (data) => uploadFiles: (data) =>
request({ request({
@ -52,6 +54,10 @@ export default {
method: 'get', method: 'get',
}), // 文件下载 }), // 文件下载
runPlagiarism:(data) => request.post('/plagiarism/runPlagiarism', data),//运行查重算法
//保存文件名到数据库 //保存文件名到数据库
saveInfoToProps: (data) => request.post('/problem/saveInfoToProps', data), saveInfoToProps: (data) => request.post('/problem/saveInfoToProps', data),

View File

@ -7,7 +7,6 @@
<script setup> <script setup>
// 控制“进入”按钮在短时间内只能被点击一次,该按钮若短时间内多次点击会出现 // 控制“进入”按钮在短时间内只能被点击一次,该按钮若短时间内多次点击会出现
// “Redis未启动”、“提交本题不能过快,请稍后重试”等错误
const isClicked = ref(false) const isClicked = ref(false)
const emit = defineEmits(['clickStart', 'clickGrade']) const emit = defineEmits(['clickStart', 'clickGrade'])

View File

@ -40,8 +40,18 @@ const paginationReactive = reactive({
const onStartExam = async (exam) => {//点击进入控件的逻辑 const onStartExam = async (exam) => {//点击进入控件的逻辑
try { try {
const examId = exam.id const examId = exam.id
const classId = exam.classId const classId = exam.classId
const adminuserId = classId //这里先暂时用classid替换,在方法内修改为老师id
const problemId = examId
const submitReport = {
problemId,
adminuserId
};
if(exam.submissionStatus==null) {
const res = await api.pickExam(submitReport)//领取作业
}
router.push({ name: 'ReportSubmit', params: { examId,classId }, query: { fromclient: false } }) router.push({ name: 'ReportSubmit', params: { examId,classId }, query: { fromclient: false } })
} catch (err) { } catch (err) {
console.log(err) console.log(err)
@ -156,16 +166,7 @@ const queryByPage = async (currentPage) => {//进入页面加载逻辑
paginationReactive.itemCount = total paginationReactive.itemCount = total
examList.value = list examList.value = list
// 添加倒计时控制
examList.value.forEach((exam) => {
// 距离考试不到1h,开始倒计时
if (exam.status.startsWith('还有')) {
// 格式:"还有" + minute + ":" + second
const [minute, second] = exam.status.replace('还有', '').split(':')
countdownControllers[exam.id] = useCountdown()
countdownControllers[exam.id].startCountdown(Number(minute) * 60 + Number(second))
}
})
} catch (err) { } catch (err) {
console.log(err) console.log(err)
} finally { } finally {

View File

@ -38,7 +38,7 @@ const onSave = async () => { //保存
const submitReport = { const submitReport = {
attachment, attachment,
problemId, problemId,
adminuserId, adminuserId
}; };
const res = await api.saveInfoToProps(submitReport); const res = await api.saveInfoToProps(submitReport);
// 处理成功逻辑 // 处理成功逻辑
@ -89,12 +89,15 @@ const onSaveAndSubmit = async () => {//保存并提交
adminuserId adminuserId
}; };
const res = await api.submitInfoToProps(submitReport1); const res = await api.submitInfoToProps(submitReport1);
// 提交成功后,调用查重函数
await checkPlagiarism();
emit('clickSaveAndSubmit'); emit('clickSaveAndSubmit');
emit('refreshAttachment'); emit('refreshAttachment');
ElMessage.success('提交成功!'); ElMessage.success('提交成功!');
} catch (error) { } catch (error) {
console.error("Failed to submit:", error);
ElMessage.error('提交时发生错误!');
} }
}).catch(() => { }).catch(() => {
// 用户取消提交 // 用户取消提交
@ -125,6 +128,25 @@ const onWithdraw = () => { //撤回提交
}); });
}; };
// 查重函数
const checkPlagiarism = async () => {
try{
// 获取文件名
const attachment = props.attachment;
const submitReport = {//只需要附件信息
attachment
};
// 调用查重接口
const response = await api.runPlagiarism(submitReport);
const result = response.data;
console.log('查重结果:', result);
// 处理查重结果,例如显示最高相似度的文章名和相似度
} catch (error) {
ElMessage.error('查重过程中发生错误!');
}
};
</script> </script>
<style scoped> <style scoped>

View File

@ -145,13 +145,6 @@
<systemPath>${project.basedir}/src/main/WEB-INF/lib/jave-1.0.2.jar</systemPath> <systemPath>${project.basedir}/src/main/WEB-INF/lib/jave-1.0.2.jar</systemPath>
</dependency> </dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>apache-poi</artifactId>
<version>2.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/WEB-INF/lib/poi-3.6-20091214.jar</systemPath>
</dependency>
<dependency> <dependency>
<groupId>commons-codec</groupId> <groupId>commons-codec</groupId>
@ -180,6 +173,45 @@
<version>2.5.29</version> <version>2.5.29</version>
</dependency> </dependency>
<dependency>
<groupId>com.huaban</groupId>
<artifactId>jieba-analysis</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>com.hankcs</groupId>
<artifactId>hanlp</artifactId>
<version>portable-1.7.8</version>
</dependency>
<!-- Apache POI 用于读取 .doc 文件 前后版本保持一致,为4.1.0-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>4.1.0</version>
</dependency>
<!-- Apache PDFBox 用于读取 .pdf 文件 -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.4</version>
</dependency>
</dependencies> </dependencies>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>

View File

@ -0,0 +1,64 @@
package com.example.aes.problem.controller;
import java.io.*;
import java.nio.file.*;
import java.text.BreakIterator;
import java.util.*;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.regex.Pattern;
import com.example.aes.global.model.RespBean;
import com.example.aes.problem.model.PMScoringRubric;
import com.example.aes.problem.model.PlagiarismResult;
import com.example.aes.problem.model.SubmitReport;
import com.example.aes.problem.service.PlagiarismServiceI;
import com.hankcs.hanlp.HanLP;
import com.hankcs.hanlp.seg.common.Term;
import com.hankcs.hanlp.tokenizer.IndexTokenizer;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@RestController
@RequestMapping("/plagiarism")
@Transactional
public class PlagiarismController {
@Autowired
private PlagiarismServiceI plagiarismServiceI;
@PostMapping("/runPlagiarism")
public ResponseEntity runPlagiarism(@RequestBody SubmitReport submitReport) {
// 构建文件路径
String uploadDirectory = System.getProperty("user.dir") + File.separator + "upload";
Path filePath = Paths.get(uploadDirectory,submitReport.getAttachment());
// 检查文件是否存在
if (!Files.exists(filePath)) {
return ResponseEntity.badRequest().body("文件不存在");
}
// 调用Service层进行查重
PlagiarismResult result = plagiarismServiceI.checkPlagiarism(filePath.toString(), uploadDirectory);
// 返回查重结果
return ResponseEntity.ok(result);
}
}

View File

@ -6,6 +6,7 @@ import com.example.aes.global.model.RespBean;
import com.example.aes.problem.model.*; import com.example.aes.problem.model.*;
import com.example.aes.problem.model.DataGrid; import com.example.aes.problem.model.DataGrid;
import com.example.aes.problem.service.ProblemServiceI; import com.example.aes.problem.service.ProblemServiceI;
import com.example.aes.user.model.Classes;
import com.example.aes.user.model.Classstudents; import com.example.aes.user.model.Classstudents;
import com.example.aes.user.model.PMUser; import com.example.aes.user.model.PMUser;
import com.example.aes.user.service.ClassesServiceI; import com.example.aes.user.service.ClassesServiceI;
@ -427,8 +428,8 @@ public class ProblemController {
} }
} }
else { //新生成一条数据 else { //新生成一条数据
submitReport.setPlagiarismStatus("0");//查重状态默认为0 submitReport.setPlagiarismStatus("未查重");//查重状态默认为未查重
submitReport.setSubmissionStatus("0");//提交状态在保存时设置为0 submitReport.setSubmissionStatus("未提交");//提交状态在保存时设置为未提交
submitReport.setSubmissionTime(now); submitReport.setSubmissionTime(now);
boolean result = problemServiceI.saveSubmitInfoToProps(submitReport); boolean result = problemServiceI.saveSubmitInfoToProps(submitReport);
if (result) { if (result) {
@ -448,6 +449,11 @@ public class ProblemController {
submitReport.setStudentId(userId); submitReport.setStudentId(userId);
if (problemServiceI.findReportSubmitted(submitReport)) { if (problemServiceI.findReportSubmitted(submitReport)) {
String submissionStatus=problemServiceI.findReportSubmittedStatus(submitReport);
if(submissionStatus.equals("已提交")){
return RespBean.error("请勿重复提交!");
}
else {
boolean result = problemServiceI.updateSubmitInfoForSubmission(submitReport); boolean result = problemServiceI.updateSubmitInfoForSubmission(submitReport);
if (result) { if (result) {
return RespBean.ok("提交信息更新成功"); return RespBean.ok("提交信息更新成功");
@ -455,6 +461,7 @@ public class ProblemController {
return RespBean.error("提交信息更新失败"); return RespBean.error("提交信息更新失败");
} }
} }
}
else{ else{
return RespBean.error("未找到该学生提交信息!"); return RespBean.error("未找到该学生提交信息!");
} }
@ -468,10 +475,10 @@ public class ProblemController {
submitReport.setStudentId(userId); submitReport.setStudentId(userId);
if (problemServiceI.findReportSubmitted(submitReport)) { if (problemServiceI.findReportSubmitted(submitReport)) {
String submissionstatus=problemServiceI.findReportSubmittedStatus(submitReport); String submissionstatus=problemServiceI.findReportSubmittedStatus(submitReport);
if(submissionstatus.equals("2")){ if(submissionstatus.equals("已撤回")){
return RespBean.error("实验报告已撤回!"); return RespBean.error("实验报告已撤回!");
} }
else if(submissionstatus.equals("0")){ else if(submissionstatus.equals("未提交")){
return RespBean.error("尚未提交实验报告!"); return RespBean.error("尚未提交实验报告!");
} }
else{ else{
@ -487,4 +494,30 @@ public class ProblemController {
return RespBean.error("未找到该学生提交信息!"); return RespBean.error("未找到该学生提交信息!");
} }
} }
//学生领取作业
@PostMapping("/pickExam")
public RespBean pickExam(@RequestBody SubmitReport submitReport,HttpServletRequest request) {
// if (problemServiceI.findReportSubmitted(submitReport)) {//如果数据库中已有数据,说明已经领取
DecodeToken decodeToken = new DecodeToken(request);
String UserId = decodeToken.getUserId();
int userId = Integer.parseInt(UserId);
Classes classes=classesServiceI.findClassById(submitReport.getAdminuserId());//这里存放的还是classid
int AdminuserId=classes.getTeacherId();
LocalDateTime now = LocalDateTime.now();
submitReport.setStudentId(userId);
submitReport.setAdminuserId(AdminuserId);
submitReport.setSubmissionStatus("已领取");
submitReport.setReadTime(now);
boolean result = problemServiceI.saveSubmitInfoToProps(submitReport);//直接调用保存逻辑
if (result) {
return RespBean.ok("领取成功");
} else {
return RespBean.error("领取失败");
}
}
} }

View File

@ -317,7 +317,7 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
Session session = this.getCurrentSession(); Session session = this.getCurrentSession();
try { try {
String hql = "UPDATE SubmitReport SET submissionStatus = '1' " + String hql = "UPDATE SubmitReport SET submissionStatus = '已提交' " +
"WHERE problemId = :problemId " + "WHERE problemId = :problemId " +
"AND adminuserId = :adminuserId " + "AND adminuserId = :adminuserId " +
"AND studentId = :studentId"; "AND studentId = :studentId";
@ -340,7 +340,7 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
Session session = this.getCurrentSession(); Session session = this.getCurrentSession();
try { try {
String hql = "UPDATE SubmitReport SET submissionStatus = '2' " + String hql = "UPDATE SubmitReport SET submissionStatus = '已撤回' " +
"WHERE problemId = :problemId " + "WHERE problemId = :problemId " +
"AND adminuserId = :adminuserId " + "AND adminuserId = :adminuserId " +
"AND studentId = :studentId"; "AND studentId = :studentId";

View File

@ -29,7 +29,6 @@ public class PMProblem extends Problem implements java.io.Serializable{//继承P
private String submissionStatus;//提交状态,来自submitproblem private String submissionStatus;//提交状态,来自submitproblem
private LocalDateTime startTime;//开始时间,来自postproblem private LocalDateTime startTime;//开始时间,来自postproblem
private LocalDateTime endTime;//结束时间 private LocalDateTime endTime;//结束时间
private String submissonStatus;//提交状态,来自submitproblem
private String plagiarismStatus;//查重状态 private String plagiarismStatus;//查重状态
private float score;//最终分数 private float score;//最终分数
private String status;//最后在页面显示的状态,取决于上面三个参数。 private String status;//最后在页面显示的状态,取决于上面三个参数。

View File

@ -0,0 +1,15 @@
package com.example.aes.problem.model;
import lombok.Data;
@Data
public class PlagiarismResult {//用于存放查重算法的结果(最大值)
private String articleName;
private double similarity;
public PlagiarismResult(String articleName, double similarity) {
this.articleName = articleName;
this.similarity = similarity;
}
}

View File

@ -0,0 +1,372 @@
package com.example.aes.problem.service.Impl;
import com.example.aes.problem.dao.ProblemDaoI;
import com.example.aes.problem.model.PlagiarismResult;
import com.example.aes.problem.service.PlagiarismServiceI;
import com.hankcs.hanlp.HanLP;
import com.hankcs.hanlp.seg.common.Term;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.springframework.stereotype.Service;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Service
public class PlagiarismServiceImpl implements PlagiarismServiceI {
@Override
public PlagiarismResult checkPlagiarism(String fileName, String uploadDirectory) {
// 清理文件名
String cleanedFileName = cleanFileName(fileName);
// 构建文件路径
Path filePath = Paths.get(fileName);
// 读取上传的文件内容
String article1 = readFile(filePath.toString());
// 读取upload目录下的所有文件
List<Path> filePaths = listFilesInDirectory(uploadDirectory);
double highestSimilarity = 0.0;
String similarFileName = "";
// 遍历每个文件
for (Path path : filePaths) {
if (!path.equals(filePath)) {//除了原本的文件
String article2 = readFile(path.toString());
// 分别对两篇文章进行断句
List<String> sentences1 = splitIntoSentences(article1);
List<String> sentences2 = splitIntoSentences(article2);
double totalSimilarityChars = 0.0; // 疑似抄袭的句子字符总数
int totalChars = 0; // 被测文本中的总字符数
// // 使用余弦相似度方法对每对句子进行查重比对
// for (String sentence1 : sentences1) {
// double maxSimilarity = 0.0;
// String mostSimilarSentence = "";
// for (String sentence2 : sentences2) {
// double similarity = calculateSentenceSimilarity1(sentence1, sentence2); // 使用余弦相似度计算方法
// if (similarity > maxSimilarity) {
// maxSimilarity = similarity;
// mostSimilarSentence = sentence2;
// }
// }
// // 重复率高的句子
// if (maxSimilarity > 0.5) { // 假设阈值为0.5
// totalSimilarityChars += sentence1.length(); // 累加疑似抄袭句子的字符数
// }
// totalChars += sentence1.length(); // 累加文本1中的字符数
// }
//
// // 输出文本的相似度
// // 确保totalChars不为0,避免除以0的情况
// if (totalChars > 0) {
// System.out.printf("文章编码:%d,疑似抄袭句总相似度(疑似抄袭句在被检测文本占比):%.2f%%%n", i + 1, (totalSimilarityChars / totalChars) * 100);
// } else {
// System.out.println("文章编码:" + (i + 1) + " 被检测文本为空。");
// }
//
// 使用 Jaccard 相似度计算方法对每对句子进行查重比对
for (String sentence1 : sentences1) {
double maxSimilarity = 0.0;
String mostSimilarSentence = "";
for (String sentence2 : sentences2) {
double similarity = calculateSentenceSimilarity2(sentence1, sentence2);//使用Jaccards相似度计算方法
if (similarity > maxSimilarity) {
maxSimilarity = similarity;
mostSimilarSentence = sentence2;
}
}
// 输出重复率高的句子
if (maxSimilarity > 0.5) { // 假设阈值为0.5
// System.out.println("被检测文段中疑似抄袭句:");
// System.out.println(sentence1);
// System.out.println("原文相似句:");
// System.out.println(mostSimilarSentence);
// System.out.println("该句词语jaccards相似度:" + maxSimilarity);
totalSimilarityChars += sentence1.length(); // 累加疑似抄袭句子的字符数
}
totalChars += sentence1.length(); // 累加文本1中的字符数
}
// 计算整体相似度
double similarity = totalSimilarityChars / totalChars;
if (similarity > highestSimilarity) {
highestSimilarity = similarity;
similarFileName = path.getFileName().toString();
}
}
}
return new PlagiarismResult(similarFileName,highestSimilarity );
}
/**********************************///读取文件部分
// 列出指定文件夹中的所有文件
public List<Path> listFilesInDirectory(String directoryPath) {
try (Stream<Path> paths = Files.walk(Paths.get(directoryPath))) {
return paths.filter(Files::isRegularFile).collect(Collectors.toList());
} catch (IOException e) {
e.printStackTrace();
return Collections.emptyList();
}
}
// 读取文件内容,支持 .txt, .doc, .pdf 文件,并作空白字符清理
public String readFile(String filePath) {
String fileExtension = getFileExtension(filePath).toLowerCase();
String content = "";
if (fileExtension.equals("txt")) {
content = cleanText(readTextFile(filePath));
} else if (fileExtension.equals("doc")||fileExtension.equals("docx")) {
content = cleanText(readDocFile(filePath));
} else if (fileExtension.equals("pdf")) {
content = cleanText(readPdfFile(filePath));
}
return content;
}
//读取.txt文件
public String readTextFile(String filePath) {
StringBuilder contentBuilder = new StringBuilder();
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String currentLine;
while ((currentLine = br.readLine()) != null) {
contentBuilder.append(currentLine).append(System.lineSeparator());
}
} catch (IOException e) {
e.printStackTrace();
return "";
}
return contentBuilder.toString();
}
// 读取.doc文件并返回文本内容
public String readDocFile(String docFilePath) {
try (FileInputStream fis = new FileInputStream(docFilePath)) {
String fileExtension = Files.probeContentType(Paths.get(docFilePath));
if (fileExtension.equals("application/vnd.openxmlformats-officedocument.wordprocessingml.document")) {
// 处理 .docx 文件
XWPFDocument docx = new XWPFDocument(fis);
List<XWPFParagraph> paragraphs = docx.getParagraphs();
StringBuilder textBuilder = new StringBuilder();
for (XWPFParagraph paragraph : paragraphs) {
textBuilder.append(paragraph.getText());
textBuilder.append("\n");
}
return textBuilder.toString();
} else if (fileExtension.equals("application/msword")) {
// 处理 .doc 文件
HWPFDocument doc = new HWPFDocument(fis);
Range range = doc.getRange();
return range.text();
} else {
throw new IllegalArgumentException("Unsupported file type: " + fileExtension);
}
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
// 读取.pdf文件并返回文本内容
public String readPdfFile(String pdfFilePath) {
try (PDDocument document = PDDocument.load(new File(pdfFilePath));
StringWriter writer = new StringWriter()) {
PDFTextStripper stripper = new PDFTextStripper();
stripper.writeText(document, writer);
return writer.toString();
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
// 获取文件扩展名
public String getFileExtension(String fileName) {
int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex > 0) {
return fileName.substring(lastDotIndex + 1);
}
return "";
}
//文件格式清理
public static String cleanFileName(String fileName) {
return fileName.replaceAll("[<>:\"/\\\\|?*]", "_");
}
/**********************************///数据清洗部分
// 添加一个静态的停用词集合
private static final Set<String> STOP_WORDS = new HashSet<>();//过滤停用词
static {
try {
String stopwordsFilePath = "C:/Users/86186/Desktop/停用词.txt";
// 读取文件中的每一行
Files.readAllLines(Paths.get(stopwordsFilePath), StandardCharsets.UTF_8)
.forEach(STOP_WORDS::add);
} catch (IOException e) {
e.printStackTrace();
}
}
// 使用正则表达式移除所有空白字符
private static String cleanText(String text) {//用于分句时去除空白
return text.replaceAll("\\s+", "");
}
// 移除句子中的标点
private static String cleanSentences(String text) {//用于句中文本标点清洗
return text.replaceAll("[,。!?;:“”‘’()《》【】〔〕…—~、\\\\s+]+", "");
}
/**********************************///文本处理
public List<String> splitIntoSentences(String text) {//断句
// 使用 HanLP 进行分词,获取词元列表
List<Term> termList = HanLP.segment(text);
// 存储断好的句子
List<String> sentences = new ArrayList<>();
// 使用 StringBuilder 来构建句子
StringBuilder sentenceBuilder = new StringBuilder();
// 遍历词元列表,根据标点符号进行断句
for (Term term : termList) {
String word = term.word;
// 如果当前词是标点符号,并且不是句子内部的标点(如顿号、逗号等),则断句
if (Pattern.matches("[。!?;:]$", word)) {
sentenceBuilder.append(word);
sentences.add(sentenceBuilder.toString());
sentenceBuilder.setLength(0); // 清空 StringBuilder 以便构建下一个句子
} else {
sentenceBuilder.append(word);
}
}
// 检查 StringBuilder 中是否还有剩余的文本,如果有,则添加到最后一个句子
if (sentenceBuilder.length() > 0) {
sentences.add(sentenceBuilder.toString());
}
return sentences;
}
/**********************************///计算相似度部分
// 计算两个句子之间的余弦相似度
public double calculateSentenceSimilarity1(String sentence1, String sentence2) {
// 分词并创建词频向量
Map<String, Integer> vector1 = buildWordFrequencyVector(sentence1);
Map<String, Integer> vector2 = buildWordFrequencyVector(sentence2);
// // 输出两个句子的词频向量
// System.out.println("Sentence 1 Vector: " + vector1);
// System.out.println("Sentence 2 Vector: " + vector2);
// 计算余弦相似度
double similarity = calculateCosineSimilarity(vector1, vector2);
// //输出
// System.out.println("Cosine Similarity: " + similarity);
return similarity;
}
// 计算两个句子之间的 Jaccard 相似度
public double calculateSentenceSimilarity2(String sentence1, String sentence2) {
// 分词并创建词集合
Set<String> setA = buildWordSet(sentence1);
Set<String> setB = buildWordSet(sentence2);
// 计算 Jaccard 相似度
return calculateJaccardSimilarity(setA, setB);
}
// 构建词频向量 用于余弦相似度计算
public Map<String, Integer> buildWordFrequencyVector(String sentence) {
Map<String, Integer> wordFrequency = new HashMap<>();
String cleanedSentence = cleanSentences(sentence);
// 分词
List<Term> termList = HanLP.segment(cleanedSentence);
for (Term term : termList) {
String word = term.word;
// 过滤停用词
if (!STOP_WORDS.contains(word)) {
wordFrequency.put(word, wordFrequency.getOrDefault(word, 0) + 1);
}
}
// for (Term term : termList) {
// String word = term.word;
// wordFrequency.put(word, wordFrequency.getOrDefault(word, 0) + 1);
// }
return wordFrequency;
}
// 构建词集合 用于Jaccard相似度计算
public Set<String> buildWordSet(String sentence) {
Set<String> wordSet = new HashSet<>();
String cleanedSentence = cleanSentences(sentence);
// 分词
List<Term> termList = HanLP.segment(cleanedSentence);
for (Term term : termList) {
String word = term.word;
// 过滤停用词
if (!STOP_WORDS.contains(word)) {
wordSet.add(word);
}
}
return wordSet;
}
// 计算余弦相似度
public double calculateCosineSimilarity(Map<String, Integer> vectorA, Map<String, Integer> vectorB) {
Set<String> uniqueWords = new HashSet<>(vectorA.keySet());
uniqueWords.addAll(vectorB.keySet());
double dotProduct = 0.0;
double normA = 0.0;
double normB = 0.0;
for (String word : uniqueWords) {
int countA = vectorA.getOrDefault(word, 0);
int countB = vectorB.getOrDefault(word, 0);
dotProduct += countA * countB;
normA += Math.pow(countA, 2);
normB += Math.pow(countB, 2);
}
if (normA == 0.0 || normB == 0.0) {
return 0.0; // 如果任一向量为零向量,则相似度为0
}
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
// 计算两个集合之间的 Jaccard 相似度
public double calculateJaccardSimilarity(Set<String> setA, Set<String> setB) {
Set<String> intersection = new HashSet<>(setA);
intersection.retainAll(setB);
Set<String> union = new HashSet<>(setA);
union.addAll(setB);
// 避免除以零的情况
if (union.size() == 0) {
return 0.0;
}
return (double) intersection.size() / union.size();
}
}

View File

@ -149,7 +149,10 @@ public class ProblemServiceImpl implements ProblemServiceI {
for (PMProblem pmProblem : pMproblems) { for (PMProblem pmProblem : pMproblems) {
// 从Map中获取对应的ReportSubmitted对象 // 从Map中获取对应的ReportSubmitted对象
ReportSubmitted reportStatus = reportStatusMap.get(pmProblem.getId()); ReportSubmitted reportStatus = reportStatusMap.get(pmProblem.getId());
//如果学生尚未提交过任何报告
if(reportStatus == null){
pmProblem.setStatus("未提交");
}
// 确保reportStatus不为null并且studentId匹配 // 确保reportStatus不为null并且studentId匹配
if (reportStatus != null && studentId.equals(reportStatus.getStudentId())) { if (reportStatus != null && studentId.equals(reportStatus.getStudentId())) {
pmProblem.setSubmissionStatus(reportStatus.getSubmissionStatus()); pmProblem.setSubmissionStatus(reportStatus.getSubmissionStatus());
@ -158,14 +161,12 @@ public class ProblemServiceImpl implements ProblemServiceI {
pmProblem.setScore(reportStatus.getScore()); pmProblem.setScore(reportStatus.getScore());
} }
// 根据submissionStatus设置status String submissionStatus = reportStatus.getSubmissionStatus();
if ("0".equals(reportStatus.getSubmissionStatus())) { if ("未领取".equals(submissionStatus) || "已领取".equals(submissionStatus)||submissionStatus==null) {
pmProblem.setStatus("未提交"); pmProblem.setStatus("未提交");
} } else if ("已提交".equals(submissionStatus)) {
else if ("1".equals(reportStatus.getSubmissionStatus())) {
pmProblem.setStatus("已提交"); pmProblem.setStatus("已提交");
} } else if ("已撤回".equals(submissionStatus)) {
else if("2".equals(reportStatus.getSubmissionStatus())){
pmProblem.setStatus("已撤回"); pmProblem.setStatus("已撤回");
} }

View File

@ -0,0 +1,14 @@
package com.example.aes.problem.service;
import com.example.aes.problem.model.*;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.List;
public interface PlagiarismServiceI {
public PlagiarismResult checkPlagiarism(String toString, String uploadDirectory);
}

View File

@ -10,10 +10,7 @@ import org.springframework.core.io.ResourceLoader;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;