成功接入了算法,实现了点击提交按钮后同步查重并返回查重信息的功能,但前端逻辑尚待修正。
删去了部分不需要的依赖包以解决冲突问题。 之后处理异步操作。
This commit is contained in:
parent
12bd542196
commit
4522512654
|
|
@ -38,6 +38,8 @@ export default {
|
|||
|
||||
getSubmitReport:(config) => request.get('/problem/getSubmitReport', config),//获取学生已上传报告信息
|
||||
|
||||
pickExam: (data) => request.post('/problem/pickExam', data),
|
||||
|
||||
//文件处理部分
|
||||
uploadFiles: (data) =>
|
||||
request({
|
||||
|
|
@ -52,6 +54,10 @@ export default {
|
|||
method: 'get',
|
||||
}), // 文件下载
|
||||
|
||||
|
||||
runPlagiarism:(data) => request.post('/plagiarism/runPlagiarism', data),//运行查重算法
|
||||
|
||||
|
||||
//保存文件名到数据库
|
||||
saveInfoToProps: (data) => request.post('/problem/saveInfoToProps', data),
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
<script setup>
|
||||
// 控制“进入”按钮在短时间内只能被点击一次,该按钮若短时间内多次点击会出现
|
||||
// “Redis未启动”、“提交本题不能过快,请稍后重试”等错误
|
||||
const isClicked = ref(false)
|
||||
|
||||
const emit = defineEmits(['clickStart', 'clickGrade'])
|
||||
|
|
|
|||
|
|
@ -40,8 +40,18 @@ const paginationReactive = reactive({
|
|||
|
||||
const onStartExam = async (exam) => {//点击进入控件的逻辑
|
||||
try {
|
||||
|
||||
const examId = exam.id
|
||||
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 } })
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
|
|
@ -156,16 +166,7 @@ const queryByPage = async (currentPage) => {//进入页面加载逻辑
|
|||
paginationReactive.itemCount = total
|
||||
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) {
|
||||
console.log(err)
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ const onSave = async () => { //保存
|
|||
const submitReport = {
|
||||
attachment,
|
||||
problemId,
|
||||
adminuserId,
|
||||
adminuserId
|
||||
};
|
||||
const res = await api.saveInfoToProps(submitReport);
|
||||
// 处理成功逻辑
|
||||
|
|
@ -89,12 +89,15 @@ const onSaveAndSubmit = async () => {//保存并提交
|
|||
adminuserId
|
||||
};
|
||||
const res = await api.submitInfoToProps(submitReport1);
|
||||
// 提交成功后,调用查重函数
|
||||
await checkPlagiarism();
|
||||
emit('clickSaveAndSubmit');
|
||||
emit('refreshAttachment');
|
||||
ElMessage.success('提交成功!');
|
||||
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error("Failed to submit:", error);
|
||||
ElMessage.error('提交时发生错误!');
|
||||
}
|
||||
}).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>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
46
AES/pom.xml
46
AES/pom.xml
|
|
@ -145,13 +145,6 @@
|
|||
<systemPath>${project.basedir}/src/main/WEB-INF/lib/jave-1.0.2.jar</systemPath>
|
||||
</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>
|
||||
<groupId>commons-codec</groupId>
|
||||
|
|
@ -180,6 +173,45 @@
|
|||
<version>2.5.29</version>
|
||||
</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>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import com.example.aes.global.model.RespBean;
|
|||
import com.example.aes.problem.model.*;
|
||||
import com.example.aes.problem.model.DataGrid;
|
||||
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.PMUser;
|
||||
import com.example.aes.user.service.ClassesServiceI;
|
||||
|
|
@ -427,8 +428,8 @@ public class ProblemController {
|
|||
}
|
||||
}
|
||||
else { //新生成一条数据
|
||||
submitReport.setPlagiarismStatus("0");//查重状态默认为0
|
||||
submitReport.setSubmissionStatus("0");//提交状态在保存时设置为0
|
||||
submitReport.setPlagiarismStatus("未查重");//查重状态默认为未查重
|
||||
submitReport.setSubmissionStatus("未提交");//提交状态在保存时设置为未提交
|
||||
submitReport.setSubmissionTime(now);
|
||||
boolean result = problemServiceI.saveSubmitInfoToProps(submitReport);
|
||||
if (result) {
|
||||
|
|
@ -448,6 +449,11 @@ public class ProblemController {
|
|||
submitReport.setStudentId(userId);
|
||||
|
||||
if (problemServiceI.findReportSubmitted(submitReport)) {
|
||||
String submissionStatus=problemServiceI.findReportSubmittedStatus(submitReport);
|
||||
if(submissionStatus.equals("已提交")){
|
||||
return RespBean.error("请勿重复提交!");
|
||||
}
|
||||
else {
|
||||
boolean result = problemServiceI.updateSubmitInfoForSubmission(submitReport);
|
||||
if (result) {
|
||||
return RespBean.ok("提交信息更新成功");
|
||||
|
|
@ -455,6 +461,7 @@ public class ProblemController {
|
|||
return RespBean.error("提交信息更新失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
return RespBean.error("未找到该学生提交信息!");
|
||||
}
|
||||
|
|
@ -468,10 +475,10 @@ public class ProblemController {
|
|||
submitReport.setStudentId(userId);
|
||||
if (problemServiceI.findReportSubmitted(submitReport)) {
|
||||
String submissionstatus=problemServiceI.findReportSubmittedStatus(submitReport);
|
||||
if(submissionstatus.equals("2")){
|
||||
if(submissionstatus.equals("已撤回")){
|
||||
return RespBean.error("实验报告已撤回!");
|
||||
}
|
||||
else if(submissionstatus.equals("0")){
|
||||
else if(submissionstatus.equals("未提交")){
|
||||
return RespBean.error("尚未提交实验报告!");
|
||||
}
|
||||
else{
|
||||
|
|
@ -487,4 +494,30 @@ public class ProblemController {
|
|||
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("领取失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -317,7 +317,7 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
|
|||
Session session = this.getCurrentSession();
|
||||
try {
|
||||
|
||||
String hql = "UPDATE SubmitReport SET submissionStatus = '1' " +
|
||||
String hql = "UPDATE SubmitReport SET submissionStatus = '已提交' " +
|
||||
"WHERE problemId = :problemId " +
|
||||
"AND adminuserId = :adminuserId " +
|
||||
"AND studentId = :studentId";
|
||||
|
|
@ -340,7 +340,7 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
|
|||
Session session = this.getCurrentSession();
|
||||
try {
|
||||
|
||||
String hql = "UPDATE SubmitReport SET submissionStatus = '2' " +
|
||||
String hql = "UPDATE SubmitReport SET submissionStatus = '已撤回' " +
|
||||
"WHERE problemId = :problemId " +
|
||||
"AND adminuserId = :adminuserId " +
|
||||
"AND studentId = :studentId";
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ public class PMProblem extends Problem implements java.io.Serializable{//继承P
|
|||
private String submissionStatus;//提交状态,来自submitproblem
|
||||
private LocalDateTime startTime;//开始时间,来自postproblem
|
||||
private LocalDateTime endTime;//结束时间
|
||||
private String submissonStatus;//提交状态,来自submitproblem
|
||||
private String plagiarismStatus;//查重状态
|
||||
private float score;//最终分数
|
||||
private String status;//最后在页面显示的状态,取决于上面三个参数。
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -149,7 +149,10 @@ public class ProblemServiceImpl implements ProblemServiceI {
|
|||
for (PMProblem pmProblem : pMproblems) {
|
||||
// 从Map中获取对应的ReportSubmitted对象
|
||||
ReportSubmitted reportStatus = reportStatusMap.get(pmProblem.getId());
|
||||
|
||||
//如果学生尚未提交过任何报告
|
||||
if(reportStatus == null){
|
||||
pmProblem.setStatus("未提交");
|
||||
}
|
||||
// 确保reportStatus不为null并且studentId匹配
|
||||
if (reportStatus != null && studentId.equals(reportStatus.getStudentId())) {
|
||||
pmProblem.setSubmissionStatus(reportStatus.getSubmissionStatus());
|
||||
|
|
@ -158,14 +161,12 @@ public class ProblemServiceImpl implements ProblemServiceI {
|
|||
pmProblem.setScore(reportStatus.getScore());
|
||||
}
|
||||
|
||||
// 根据submissionStatus设置status
|
||||
if ("0".equals(reportStatus.getSubmissionStatus())) {
|
||||
String submissionStatus = reportStatus.getSubmissionStatus();
|
||||
if ("未领取".equals(submissionStatus) || "已领取".equals(submissionStatus)||submissionStatus==null) {
|
||||
pmProblem.setStatus("未提交");
|
||||
}
|
||||
else if ("1".equals(reportStatus.getSubmissionStatus())) {
|
||||
} else if ("已提交".equals(submissionStatus)) {
|
||||
pmProblem.setStatus("已提交");
|
||||
}
|
||||
else if("2".equals(reportStatus.getSubmissionStatus())){
|
||||
} else if ("已撤回".equals(submissionStatus)) {
|
||||
pmProblem.setStatus("已撤回");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
@ -10,10 +10,7 @@ import org.springframework.core.io.ResourceLoader;
|
|||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
|
|
|||
Loading…
Reference in New Issue