Compare commits

..

No commits in common. "3091fbb7dd1ce80e12359a644afb4af722cc8999" and "a9837d2128170f3681691c2ae6345fd7accb741e" have entirely different histories.

33 changed files with 139 additions and 4795 deletions

53
.gitignore vendored
View File

@ -1,53 +0,0 @@
# OS
.DS_Store
**/.DS_Store
Thumbs.db
# Editor / local tooling
.cursor/
.idea/inspectionProfiles/
*.swp
AES-front/.npmrc
AES-front/pnpm-workspace.yaml
# Dependencies and build output
node_modules/
**/node_modules/
AES/target/
**/target/
dist/
*.class
# Unrelated local documents
211310132.pdf
211310509.pdf
docs/需求说明.mp4
docs/通用语音识别_需求说明.mp4 (1).txt
docs/自动评分系统/
# Binary / media files (keep already-tracked assets)
*.pdf
*.mp4
*.mp3
*.wav
*.zip
*.rar
*.7z
*.doc
*.docx
*.xls
*.xlsx
*.ppt
*.pptx
*.jar
*.war
# Runtime uploads and empty local dumps
upload/
dependency-tree.txt
AES/停用词.txt
# Logs and Python cache
*.log
__pycache__/
*.pyc

View File

@ -38,8 +38,6 @@ export default {
getSubmitReport:(config) => request.get('/problem/getSubmitReport', config),//获取学生已上传报告信息
pickExam: (data) => request.post('/problem/pickExam', data),
//文件处理部分
uploadFiles: (data) =>
request({
@ -53,17 +51,4 @@ export default {
url: `/downloadFile/downloadFile/${fileName}`,
method: 'get',
}), // 文件下载
runPlagiarism:(data) => request.post('/plagiarism/runPlagiarism', data),//运行查重算法
autoScore:(data) => request.post('/problem/autoscore', data),//运行自动批阅
//保存文件名到数据库
saveInfoToProps: (data) => request.post('/problem/saveInfoToProps', data),
submitInfoToProps: (data) => request.post('/problem/submitInfoToProps', data),
withdrawInfoToProps: (data) => request.post('/problem/withdrawInfoToProps', data),
}

View File

@ -1,6 +1,6 @@
<template>
<footer f-c-c flex-col text-14 color="#6a6a6a">
<p>报告自动批阅系统</p>
<p>Online Judge 2.0</p>
<p>Copyright © 2023-2024 . All Rights Reserved.</p>
<p>技术支持:Turbo Email:11249242@qq.com</p>
</footer>

View File

@ -75,7 +75,7 @@
>
<LoginForm
:login="api.login"
:title="isAdminEnd() ? '报告自动批阅系统 - 管理端' : '报告自动批阅系统 - 登录'"
:title="isAdminEnd() ? '报告自动评测系统 - 管理端' : '报告自动评测系统 - 登录'"
@close="userStore.closeLoginModal"
/>
</n-modal>

View File

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

View File

@ -40,18 +40,8 @@ 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)
@ -120,12 +110,12 @@ const columns = [
{
key: 'status',
title: '状态(分数标红为疑似抄袭)',
title: '状态',
width: '116',
align: 'center',
render: (row) => {
const statusText = row.status; // 假设分数存储在row.status
const isPlagiarized = row.plagiarismStatus === "疑似抄袭";
const isPlagiarized = row.plagiarismStatus === "2";//疑似抄袭
// 使用内联样式标红
const style = isPlagiarized ? { color: 'red' } : {};
// 如果使用内联样式
@ -166,7 +156,16 @@ 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 {

View File

@ -3,7 +3,7 @@
<!-- 整个页面外层container -->
<div class="container">
<!-- 测试标题 -->
<p id="indexTitle" class="lead">在线报告批阅系统(首页)</p>
<p id="indexTitle" class="lead">在线报告评测系统(首页)</p>
<!-- 图片和文字说明的外层容器 -->
<div class="index-content">
<!-- 图片 -->

View File

@ -0,0 +1,13 @@
<template>
</template>
<script>
export default {
name: "index"
}
</script>
<style scoped>
</style>

View File

@ -12,18 +12,10 @@
<div slot="header" class="clearfix card-header">
<span class="subtitle">实验提交</span>
</div>
<div v-if="attachment" class="file-list">
<span class="file-list-title">已保存的文件:</span>
<ul>
<li>
{{ attachment }}
</li>
</ul>
</div>
<br>
<el-button size="small" type="success" @click="showUploadDialog">上传附件</el-button>
<div v-if="fileUrls.length > 0" class="file-list">
<span class="file-list-title">已上传文件(保存过的文件将被覆盖保存):</span>
<span class="file-list-title">已上传文件:</span>
<ul>
<li v-for="(file, index) in fileUrls" :key="index">
{{ file.name }}
@ -43,8 +35,6 @@
v-model:file-list="fileList"
drag
accept=".txt,.doc,.docx,.pdf"
:limit="1"
:on-exceed="handleExceed"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
@ -54,15 +44,7 @@
<el-button type="primary" @click="uploadFile">确定</el-button>
</span>
</el-dialog>
<ReportActions
:fileUrls="fileUrls"
:ProblemId="ProblemId"
:adminuserId="adminuserId"
:attachment="attachment"
:disabled="isReportActionsDisabled"
@refreshAttachment="refreshAttachmentInfo"
class="bottom"
/>
<ReportActions class="bottom" />
</div>
</template>
@ -77,24 +59,18 @@ import ReportActions from "@/views/user/report-submit/components/report-actions.
const route = useRoute();
const ProblemId = route.params.examId;
const examId = route.params.examId;
const description = ref(""); // 实验描述
const adminuserId = ref(""); //布置实验的教师id
const reportText = ref(""); // 实验报告文本
const uploadDialogVisible = ref(false); // 控制上传弹窗的显示
const fileList = ref([]); // 上传的文件列表
const fileUrls = ref([]); // 文件的URL列表
const attachment = ref(null);//已保存的文件信息
const isReportActionsDisabled = ref(false); // 控制ReportActions组件的禁用状态
const uploadComponent = ref(null); // 用于引用el-upload组件
const plagiarismStatus = ref(0); // 初始化抄袭状态
const getProblemInfo = async () => {
// 查询作业信息
try {
const res = await api.findProblemById1({ params: { ProblemId } });
description.value = res.data.description; // 更新作业名称
adminuserId.value = res.data.adminuserId.toString(); // 更新教师id
} catch (error) {
console.error("Failed to fetch problem info:", error);
}
@ -102,10 +78,6 @@ const getProblemInfo = async () => {
// 显示上传弹窗
const showUploadDialog = () => {
if (fileUrls.value.length > 0) {
ElMessage.warning('您已经上传了文件,不能再次上传。');
return;
}
uploadDialogVisible.value = true;
};
@ -117,11 +89,11 @@ const handleFileChange = (file, fileList) => {
// 处理文件上传成功
const handleUploadSuccess = (response, file, fileList) => {
if (response.success) {
ElMessage.success('文件上传成功');
// 更新文件URL列表
fileUrls.value.push({ url: response.urls, name: response.data });
uploadDialogVisible.value = false;
fileList.value = [];
ElMessage.success('文件上传成功');
} else {
ElMessage.error('文件上传失败');
}
@ -133,11 +105,6 @@ const handleUploadError = (err, file, fileList) => {
ElMessage.error('文件上传失败');
};
// 处理文件数量超出限制
const handleExceed = (files, fileList) => {
ElMessage.error('只能上传一个附件');
};
// 上传文件
const uploadFile = async () => {
if (fileList.value.length === 0) {
@ -152,59 +119,38 @@ const uploadFile = async () => {
try {
const response = await api.uploadFiles(formData); // 调用上传文件的方法
console.log('Response from server:', response); // 打印响应内容
if (response.code === 0) {
ElMessage.success('文件上传成功');
// 更新文件URL列表
fileUrls.value.push({ url: response.urls, name: response.data });
uploadDialogVisible.value = false;
fileList.value = [];
}catch (error) {
} else {
ElMessage.error('文件上传失败1');
}
} catch (error) {
console.error("Failed to upload files:", error);
ElMessage.error('文件上传失败2');
}
};
const getSubmitReportInfo = async () => {
// 查询已保存的文件信息
try {
const res = await api.getSubmitReport({ params: { examId } });
if (res.data.attachment) {
attachment.value = res.data.attachment; // 更新附件信息
} else {
attachment.value = null;
}
plagiarismStatus.value = res.data.plagiarismStatus; // 更新抄袭状态
if(plagiarismStatus.value ==="2") {//疑似抄袭,允许再次提交
isReportActionsDisabled.value = false; // 启用ReportActions组件
}
else{
if (res.data.score !== null && res.data.score !== undefined) {
isReportActionsDisabled.value = true; // 禁用ReportActions组件
} else {
isReportActionsDisabled.value = false; // 启用ReportActions组件
}
}
} catch (error) {
}
};
const refreshAttachmentInfo = async () => {
//更新已上传的文件信息
await getSubmitReportInfo();
// 下载文件
const downloadFile = (url) => {
const link = document.createElement('a');
link.href = url;
link.target = '_blank';
link.download = url.split('/').pop(); // 提取文件名
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
// 生命周期钩子
onMounted(() => {
getProblemInfo(); // 挂载时查询作业信息
getSubmitReportInfo();//查询已保存的文件信息
});
</script>
<style scoped>
.card-header {
background-color: #f5f5f5; /* 加深灰色背景 */
@ -241,7 +187,7 @@ onMounted(() => {
}
.file-list {
margin-top: 10px;
margin-top: 20px;
}
.file-list-title {

View File

@ -1,193 +1,32 @@
<template>
<div class="button-container">
<n-button type="primary" size="small" @click="onSave" :disabled="props.disabled">保存</n-button>
<n-button type="success" size="small" @click="onSaveAndSubmit" :disabled="props.disabled">保存并提交</n-button>
<n-button type="warning" size="small" @click="onWithdraw" :disabled="props.disabled">撤回提交</n-button>
<n-button type="primary" size="small" @click="onSave">保存</n-button>
<n-button type="success" size="small" @click="onSaveAndSubmit">保存并提交</n-button>
<n-button type="warning" size="small" @click="onWithdraw">申请撤回</n-button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { useRoute } from 'vue-router';
import api from '@/api/user';
import {ElMessage, ElMessageBox} from "element-plus";
import {longRequest} from "@/utils";
const route = useRoute();
const isClicked = ref(false);
const ProblemId = route.params.examId;
const isCheckingPlagiarism = ref(false); // 查重状态
const emit = defineEmits(['clickSave', 'clickSaveAndSubmit', 'clickWithdraw']);
const props = defineProps({
fileUrls: Array,
adminuserId: [String],
attachment: [String],
disabled:Boolean
});
const emit = defineEmits(['clickSave', 'clickSaveAndSubmit', 'clickWithdraw', 'refreshAttachment']);
const onSave = async () => { //保存
// 检查是否有文件信息且数组不为空
if (props.fileUrls && props.fileUrls.length > 0) {
// 调用 API 保存提交信息到数据库,包括文件名、题目id,学生id,老师id,
try {
const attachment = props.fileUrls[0].name;
const adminuserId =props.adminuserId;
const problemId = ProblemId;
const submitReport = {
attachment,
problemId,
adminuserId
};
const res = await api.saveInfoToProps(submitReport);
// 处理成功逻辑
const onSave = () => {
// TODO: 实现保存逻辑
emit('clickSave');
emit('refreshAttachment');
ElMessage.success('保存成功!');
} catch (error) {
// 处理错误逻辑
console.error("Failed to save Files:", error);
ElMessage.error('保存文件时发生错误!');
}
} else {
// 如果没有文件信息,在这里处理
ElMessage.error('尚未上传新的文件!');
}
};
const onSaveAndSubmit = async () => {//保存并提交
let shouldSubmit = false;
// 检查是否有新上传的文件
const hasNewFiles = props.fileUrls && props.fileUrls.length > 0;
if (props.attachment && !hasNewFiles) {
// 如果attachment不为空且没有新文件,准备提交
shouldSubmit = true;
} else if (hasNewFiles) {
// 如果有新上传的文件,无论attachment是否为空,都先保存新文件
await onSave(); // 先调用保存逻辑
shouldSubmit = true;
} else {
ElMessage.error('尚未上传文件!');
return;
}
if (shouldSubmit) {
// 弹出确认提交的对话框
ElMessageBox.confirm('确定要提交吗?提交时将以当下已保存的文件为准。', '确认提交', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {//提交逻辑
const adminuserId =props.adminuserId;
const problemId = ProblemId;
const submitReport1 = {
problemId,
adminuserId
};
const res = await api.submitInfoToProps(submitReport1);
// 提交成功后,调用查重函数与自动批阅并后台挂起
checkPlagiarismInBackground();
autoScoreBackground();
const onSaveAndSubmit = () => {
// TODO: 实现保存并提交逻辑
emit('clickSaveAndSubmit');
emit('refreshAttachment');
ElMessage.success('提交成功!正在后台进行查重...');
} catch (error) {
}
}).catch(() => {
// 用户取消提交
});
}
};
const onWithdraw = () => { //撤回提交
ElMessageBox.confirm('确定撤回已提交的报告吗?', '确认撤回', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {//撤回逻辑
const adminuserId =props.adminuserId;
const problemId = ProblemId;
const submitReport2 = {
problemId,
adminuserId
};
const res = await api.withdrawInfoToProps(submitReport2);
const onWithdraw = () => {
// TODO: 实现申请撤回逻辑
emit('clickWithdraw');
ElMessage.success('撤回成功!');
} catch (error) {
}
}).catch(() => {
// 用户取消撤回
});
};
// 查重函数
const checkPlagiarismInBackground = async () => {
isCheckingPlagiarism.value = true; // 开始查重,更新状态
try{
// 获取文件名
const attachment = props.attachment;
const adminuserId =props.adminuserId;
const problemId = ProblemId;
const submitReport = {
adminuserId,
problemId,
attachment
};
// 调用查重接口,使用永不超时longRequest
const res = await longRequest.post('/plagiarism/runPlagiarism', submitReport);
console.log('查重结果:', res);
// 查重完成,更新状态
isCheckingPlagiarism.value = false;
} catch (error) {
ElMessage.error('查重过程中发生错误!');
isCheckingPlagiarism.value = false; // 查重失败,也要更新状态
}
};
// 监听查重状态变化,用于通知用户
watch(isCheckingPlagiarism, (newValue) => {
if (!newValue) {
// 查重完成,可以在这里通知用户
ElMessage.success('查重操作已完成!');
}
});
// 自动批阅函数
const autoScoreBackground = async () => {
try{
// 获取文件名
const attachment = props.attachment;
const adminuserId =props.adminuserId;
const problemId = ProblemId;
const submitReport = {
adminuserId,
problemId,
attachment
};
// 调用自动批阅接口,同样使用永不超时longRequest
const res1 = await longRequest.post('/problem/autoscore', submitReport);
console.log('查重结果:', res1);
} catch (error) {
ElMessage.error('自动批阅过程中发生错误!');
}
};
</script>
<style scoped>

View File

@ -5,6 +5,7 @@
<span class="subtitle">教师评语</span>
</div>
<div class="description-content">
<span v-if="plagiarismStatus === '2'" class="plagiarism-warning">疑似抄袭!!<br></span>
<span v-if="comment" class="comment-text">{{ comment }}</span>
<span v-else class="no-comment-text">暂无评价</span>
</div>
@ -17,7 +18,6 @@
import { ref, onMounted } from 'vue';
import api from '@/api/user';
import { useRoute } from 'vue-router';
import {ElMessage} from "element-plus";
const route = useRoute();
const examId = route.params.examId;
@ -28,14 +28,8 @@ const getJudgeInfo = async () => {
// 查询批阅相关信息
try {
const res = await api.getSubmitReport({ params: { examId } });
if(res.data==null){
ElMessage.warning("尚未提交作业!")
}
else {
// 更新教师或AI评语,如果教师评语为空,则使用AI评语
comment.value = res.data.comment || res.data.autocomment;
comment.value = res.data.comment; // 更新教师评语
plagiarismStatus.value = res.data.plagiarismStatus; // 更新抄袭状态
}
} catch (error) {
console.error("Failed to fetch problem info:", error);
}

View File

@ -54,7 +54,7 @@ const queryExamScores = async () => {
params: { examId }
});
title.value = res.data.title; // 更新作业名称
score.value = res.data.score || res.data.autoscore; // 更新个人得分
score.value = res.data.score; // 更新个人得分
rank.value = res.data.rank || "-"; // 更新个人排名,如果rank为空则显示 "-"
};

View File

@ -144,12 +144,15 @@
<scope>system</scope>
<systemPath>${project.basedir}/src/main/WEB-INF/lib/jave-1.0.2.jar</systemPath>
</dependency>
<!-- 执行外部命令-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.3</version>
<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>
<artifactId>commons-codec</artifactId>
@ -177,45 +180,6 @@
<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>

View File

@ -1,87 +0,0 @@
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.DecodeToken;
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.example.aes.problem.service.ProblemServiceI;
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;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping("/plagiarism")
@Transactional
public class PlagiarismController {
@Autowired
private PlagiarismServiceI plagiarismServiceI;
@Autowired
private ProblemServiceI problemServiceI;
@PostMapping("/runPlagiarism")
public RespBean runPlagiarism(@RequestBody SubmitReport submitReport, HttpServletRequest request) {
DecodeToken decodeToken = new DecodeToken(request);
String UserId = decodeToken.getUserId();
int userId = Integer.parseInt(UserId);
submitReport.setStudentId(userId);
// 构建文件路径
String uploadDirectory = System.getProperty("user.dir") + File.separator + "upload";
Path filePath = Paths.get(uploadDirectory,submitReport.getAttachment());
// 检查文件是否存在
if (!Files.exists(filePath)) {
return RespBean.error("文件不存在");
}
// 调用Service层进行查重
PlagiarismResult result = plagiarismServiceI.checkPlagiarism(filePath.toString(), uploadDirectory);
submitReport.setSimilardocs(result.getArticleName());
submitReport.setSimularity(result.getSimilarity());
if(result.getSimilarity()>0.3){//如果文章相似度大于0.3,判定为疑似抄袭并保存到数据库中
submitReport.setPlagiarismStatus("疑似抄袭");
}
boolean saveresult = problemServiceI.savePlagiarismResultToProps(submitReport);
// 返回查重结果
if (result != null) {
return RespBean.ok("查重成功",saveresult);
} else {
return RespBean.error("查重失败");
}
}
}

View File

@ -6,22 +6,12 @@ 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;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import javax.persistence.criteria.CriteriaBuilder;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@ -307,7 +297,7 @@ public class ProblemController {
//根据班级id查询所有作业
List<PMProblem> pMproblems = problemServiceI.getProblemsByClassIds(classIds);
// 根据学生ID和作业列表查询提交报告的状态,分为提交状态和查重状态,并更新到pMproblems中
// 根据学生ID和作业列表查询提交报告的状态,分为提交状态和查重状态,并更新到pmproblem中
problemServiceI.findStatusByStudentIdAndProblems(Integer.parseInt(userId), pMproblems);
//分页处理
@ -392,7 +382,7 @@ public class ProblemController {
UReportSubmitted ureportsubmitted =
problemServiceI.findScoreAndRankByIds(examId, userId);
if (ureportsubmitted != null) {
return RespBean.ok("查询作业信息成功!", ureportsubmitted);
return RespBean.ok("查询成绩排名成功!", ureportsubmitted);
} else {
return RespBean.ok("尚未提交作业!");
}
@ -421,159 +411,4 @@ public class ProblemController {
return RespBean.error("获取教师布置实验具体信息失败!");
}
}
//学生保存报告
@PostMapping("/saveInfoToProps")
public RespBean saveInfoToProps(@RequestBody SubmitReport submitReport,HttpServletRequest request) {
DecodeToken decodeToken = new DecodeToken(request);
String UserId = decodeToken.getUserId();
int userId = Integer.parseInt(UserId);
LocalDateTime now = LocalDateTime.now();
submitReport.setStudentId(userId);
if(problemServiceI.findReportSubmitted(submitReport)){//如果之前保存过数据,更新数据
submitReport.setSubmissionTime(now);
boolean result = problemServiceI.updateSubmitInfoToProps(submitReport);
if (result) {
return RespBean.ok("信息更新成功");
} else {
return RespBean.error("信息更新失败");
}
}
else { //新生成一条数据
submitReport.setPlagiarismStatus("未查重");//查重状态默认为未查重
submitReport.setSubmissionStatus("未提交");//提交状态在保存时设置为未提交
submitReport.setSubmissionTime(now);
boolean result = problemServiceI.saveSubmitInfoToProps(submitReport);
if (result) {
return RespBean.ok("信息添加成功");
} else {
return RespBean.error("信息添加失败");
}
}
}
//学生提交报告
@PostMapping("/submitInfoToProps")
public RespBean submitInfoToProps(@RequestBody SubmitReport submitReport,HttpServletRequest request) {
DecodeToken decodeToken = new DecodeToken(request);
String UserId = decodeToken.getUserId();
int userId = Integer.parseInt(UserId);
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("提交信息更新成功");
} else {
return RespBean.error("提交信息更新失败");
}
}
}
else{
return RespBean.error("未找到该学生提交信息!");
}
}
//学生撤回报告
@PostMapping("/withdrawInfoToProps")
public RespBean withdrawInfoToProps(@RequestBody SubmitReport submitReport,HttpServletRequest request) {
DecodeToken decodeToken = new DecodeToken(request);
String UserId = decodeToken.getUserId();
int userId = Integer.parseInt(UserId);
submitReport.setStudentId(userId);
if (problemServiceI.findReportSubmitted(submitReport)) {
String submissionstatus=problemServiceI.findReportSubmittedStatus(submitReport);
if(submissionstatus.equals("已撤回")){
return RespBean.error("实验报告已撤回!");
}
else if(submissionstatus.equals("未提交")){
return RespBean.error("尚未提交实验报告!");
}
else{
boolean result = problemServiceI.withdrawSubmitInfo(submitReport);
if (result) {
return RespBean.ok("撤回信息更新成功");
} else {
return RespBean.error("撤回信息更新失败");
}
}
}
else{
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("领取失败");
}
}
//自动批改作业返回分数
@PostMapping("/autoscore")
public RespBean autoscore(@RequestBody SubmitReport submitReport, HttpServletRequest request) {
DecodeToken decodeToken = new DecodeToken(request);
String userIdStr = decodeToken.getUserId();
int userId = Integer.parseInt(userIdStr);
submitReport.setStudentId(userId);
// 构建文件路径
String uploadDirectory = System.getProperty("user.dir") + File.separator + "upload";
Path filePath = Paths.get(uploadDirectory, submitReport.getAttachment());
// 检查文件是否存在
if (!Files.exists(filePath)) {
return RespBean.error("文件不存在");
}
// 调用Service层进行批改
AutoScoreResult autoScoreResult =new AutoScoreResult();
autoScoreResult.setTotalScore(-1.0);
// 调用Service层进行批改
autoScoreResult = problemServiceI.getAutoScore(filePath, submitReport.getProblemId());
//自动批阅结果存数据库
Float autoScore =(float) autoScoreResult.getTotalScore();
submitReport.setAutoscore(autoScore);
// 将所有的评语合并为一句,中间用分号隔开
List<String> remarks = autoScoreResult.getRemarks();
String combinedRemarks = String.join("; ", remarks);
submitReport.setAutocomment(combinedRemarks);
boolean saveresult = problemServiceI.saveAutoScoreResultToProps(submitReport);
if(saveresult) {
return RespBean.ok("批改完成,已保存数据!");
}
else{
return RespBean.error("批改过程中发生错误!");
}
}
}

View File

@ -19,14 +19,4 @@ public interface SubmitReportDaoI extends BaseDaoI<SubmitReport> {
public List<SubmitReport> findSimilardocs(PMStudentSubmit pmStudentSubmit);
public List<Integer> findNextId(int problemId);
public boolean saveSubmitInfo(SubmitReport submitReport);
public boolean getReportSubmitted(SubmitReport submitReport);
public boolean updateReportSubmitted(SubmitReport submitReport);
public boolean updateReportSubmittedForSubmission(SubmitReport submitReport);
public boolean withdrawReportSubmitted(SubmitReport submitReport);
public String getReportSubmittedStatus(SubmitReport submitReport);
public boolean savePlagiarismResult(SubmitReport submitReport);
public boolean saveAutoScoreResult(SubmitReport submitReport);
}

View File

@ -8,8 +8,6 @@ import com.example.aes.problem.model.SubmitReport;
import com.example.aes.user.dao.impl.BaseDaoImpl;
import com.example.aes.user.model.Course;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.springframework.stereotype.Repository;
import javax.persistence.criteria.CriteriaBuilder;
@ -19,29 +17,24 @@ import java.util.List;
@Repository("SubmitReportDao")
public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements SubmitReportDaoI {
@Override
public List<Object[]> findWeijiaoList(PMStudentSubmit pmStudentSubmit) {
int classId = pmStudentSubmit.getClassId();
int problemId = pmStudentSubmit.getProblemId();
String studentNo =
'%' + pmStudentSubmit.getStudentNo() + '%';
String chineseName =
'%' + pmStudentSubmit.getChineseName() + '%';
String hql = "SELECT u.chineseName," +
public List<Object[]> findWeijiaoList(PMStudentSubmit pmStudentSubmit){
int classId= pmStudentSubmit.getClassId();
int problemId= pmStudentSubmit.getProblemId();
String studentNo=
'%'+pmStudentSubmit.getStudentNo()+'%';
String chineseName=
'%'+pmStudentSubmit.getChineseName()+'%';
String hql="SELECT u.chineseName," +
"u.studentNo, sr" +
".readTime," +
"sr.submissionStatus,u.id " +
"FROM Classstudents cs left " +
"join Users u on cs" +
".userId=u.id " +
".userId=u.id "+
"left join PostProblem pp on cs" +
".classId=pp.classId "+
"left JOIN"+
" SubmitReport sr on (sr.problemId = pp.problemId and sr.studentId=u.id)"+
".classId=pp.classId " +
"left JOIN" +
" SubmitReport sr ON " +
"sr" +
".studentId=u.id" +
" WHERE cs.classId=:classId " +
"and " +
"pp.problemId " +
@ -54,14 +47,14 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
"and u" +
".chineseName like :chineseName";
Query<Object[]> query =
this.getCurrentSession().createQuery(hql, Object[].class);
this.getCurrentSession().createQuery(hql,Object[].class);
query.setParameter("classId", classId);
query.setParameter("problemId", problemId);
query.setParameter("studentNo", studentNo);
query.setParameter("studentNo",studentNo);
query.setParameter("chineseName",
chineseName);
int page = pmStudentSubmit.getPageNum();
int rows = pmStudentSubmit.getPageSize();
int page= pmStudentSubmit.getPageNum();
int rows= pmStudentSubmit.getPageSize();
// 设置分页
query.setFirstResult((page - 1) * rows); // 计算起始行
query.setMaxResults(rows); // 设置每页行数
@ -102,11 +95,11 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
return num;
}
@Override
public int editScoreAndComment(PMReviewMessage pmReviewMessage) {
Float score = pmReviewMessage.getScore();
String comment =
public int editScoreAndComment(PMReviewMessage pmReviewMessage){
Float score= pmReviewMessage.getScore();
String comment=
pmReviewMessage.getComment();
String hql = "UPDATE SubmitReport sr SET" +
String hql= "UPDATE SubmitReport sr SET" +
" sr.score= :score,sr" +
".comment=:comment,sr" +
".submissionStatus='已批阅' "
@ -114,8 +107,8 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
":problemId" +
" AND sr.studentId= :studentId";
Query q = this.getCurrentSession().createQuery(hql);
q.setParameter("score", score);
q.setParameter("comment", comment);
q.setParameter("score",score);
q.setParameter("comment",comment);
q.setParameter("problemId",
pmReviewMessage.getProblemId());
q.setParameter("studentId",
@ -123,10 +116,9 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
int num = q.executeUpdate();
return num;
}
@Override
public int redoReport(PMReviewMessage pmReviewMessage) {
String hql = "UPDATE SubmitReport sr set" +
public int redoReport(PMReviewMessage pmReviewMessage){
String hql= "UPDATE SubmitReport sr set" +
" sr.submissionStatus='已领取'," +
"sr.submissionTime=null " +
"WHERE sr.problemId = " +
@ -140,16 +132,15 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
int num = q.executeUpdate();
return num;
}
@Override
public List<Object[]> findYijiaoList(PMStudentSubmit pmStudentSubmit) {
int classId = pmStudentSubmit.getClassId();
int problemId = pmStudentSubmit.getProblemId();
String studentNo =
'%' + pmStudentSubmit.getStudentNo() + '%';
String chineseName =
'%' + pmStudentSubmit.getChineseName() + '%';
String hql = "SELECT u.chineseName," +
public List<Object[]> findYijiaoList(PMStudentSubmit pmStudentSubmit){
int classId= pmStudentSubmit.getClassId();
int problemId= pmStudentSubmit.getProblemId();
String studentNo=
'%'+pmStudentSubmit.getStudentNo()+'%';
String chineseName=
'%'+pmStudentSubmit.getChineseName()+'%';
String hql="SELECT u.chineseName," +
"u.studentNo, sr" +
".submissionTime," +
"sr.submissionStatus," +
@ -159,7 +150,7 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
".simularity,sr.similardocs " +
"FROM Classstudents cs left " +
"join Users u on cs" +
".userId=u.id " +
".userId=u.id "+
"left join PostProblem pp on cs" +
".classId=pp.classId "+
"left JOIN"+
@ -177,21 +168,20 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
"and u" +
".chineseName like :chineseName";
Query<Object[]> query =
this.getCurrentSession().createQuery(hql, Object[].class);
this.getCurrentSession().createQuery(hql,Object[].class);
query.setParameter("classId", classId);
query.setParameter("problemId", problemId);
query.setParameter("studentNo", studentNo);
query.setParameter("studentNo",studentNo);
query.setParameter("chineseName",
chineseName);
int page = pmStudentSubmit.getPageNum();
int rows = pmStudentSubmit.getPageSize();
int page= pmStudentSubmit.getPageNum();
int rows= pmStudentSubmit.getPageSize();
// 设置分页
query.setFirstResult((page - 1) * rows); // 计算起始行
query.setMaxResults(rows); // 设置每页行数
return query.getResultList();
}
@Override
public List<Object[]> findScoreList(PMStudentSubmit pmStudentSubmit){
int classId= pmStudentSubmit.getClassId();
@ -205,10 +195,10 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
"AS finalScore ,u.id " +
"FROM Classstudents cs left " +
"join Users u on cs" +
".userId=u.id " +
".userId=u.id "+
"left join PostProblem pp on cs" +
".classId=pp.classId " +
"left JOIN" +
".classId=pp.classId "+
"left JOIN"+
" SubmitReport sr on (sr" +
".problemId = pp.problemId and sr.studentId=u.id)"+
" WHERE cs.classId=:classId " +
@ -219,7 +209,7 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
" and sr" +
".submissionTime is not null";
Query<Object[]> query =
this.getCurrentSession().createQuery(hql, Object[].class);
this.getCurrentSession().createQuery(hql,Object[].class);
query.setParameter("classId", classId);
query.setParameter("problemId", problemId);
return query.getResultList();
@ -251,15 +241,14 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
" " +
"p.id=:problemId";
Query<Object[]> query =
this.getCurrentSession().createQuery(hql, Object[].class);
this.getCurrentSession().createQuery(hql,Object[].class);
query.setParameter("studentId", studentId);
query.setParameter("problemId", problemId);
return query.getResultList();
}
@Override
public List<Integer> findNextId(int problemId) {
String hql = "SELECT " +
public List<Integer> findNextId(int problemId){
String hql="SELECT " +
"sr.studentId " +
"FROM Users u " +
"INNER JOIN " +
@ -276,180 +265,4 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
q.setParameter("problemId", problemId);
return q.list();
}
@Override
public boolean saveSubmitInfo(SubmitReport submitReport) {
Session session = this.getCurrentSession();
try {
session.save(submitReport);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public boolean getReportSubmitted(SubmitReport submitReport) {
Session session = this.getCurrentSession();
boolean isSubmitted = false;
try {
// 构建查询语句 可能报错但不影响
String hql = "FROM SubmitReport WHERE problemId = :problemId AND studentId = :studentId AND adminuserId = :adminuserId";
Query<SubmitReport> query = session.createQuery(hql, SubmitReport.class);
query.setParameter("problemId", submitReport.getProblemId());
query.setParameter("studentId", submitReport.getStudentId());
query.setParameter("adminuserId", submitReport.getAdminuserId());
SubmitReport result = query.uniqueResult();
isSubmitted = (result != null);
} catch (Exception e) {
e.printStackTrace();
}
return isSubmitted;
}
@Override
public boolean updateReportSubmitted(SubmitReport submitReport) {
Session session = this.getCurrentSession();
try {
//只更新submissionTime和attachment
String hql = "UPDATE SubmitReport SET submissionTime = :submissionTime, attachment = :attachment " +
"WHERE problemId = :problemId AND adminuserId = :adminuserId AND studentId = :studentId";
Query query = session.createQuery(hql);
query.setParameter("submissionTime", submitReport.getSubmissionTime());
query.setParameter("attachment", submitReport.getAttachment());
query.setParameter("problemId", submitReport.getProblemId());
query.setParameter("adminuserId", submitReport.getAdminuserId());
query.setParameter("studentId", submitReport.getStudentId());
int result = query.executeUpdate();
return result > 0;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public boolean updateReportSubmittedForSubmission(SubmitReport submitReport) {
Session session = this.getCurrentSession();
try {
String hql = "UPDATE SubmitReport SET submissionStatus = '已提交' " +
"WHERE problemId = :problemId " +
"AND adminuserId = :adminuserId " +
"AND studentId = :studentId";
Query query = session.createQuery(hql);
query.setParameter("problemId", submitReport.getProblemId());
query.setParameter("adminuserId", submitReport.getAdminuserId());
query.setParameter("studentId", submitReport.getStudentId());
int result = query.executeUpdate();
return result > 0;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public boolean withdrawReportSubmitted(SubmitReport submitReport) {
Session session = this.getCurrentSession();
try {
String hql = "UPDATE SubmitReport SET submissionStatus = '已撤回' " +
"WHERE problemId = :problemId " +
"AND adminuserId = :adminuserId " +
"AND studentId = :studentId";
Query query = session.createQuery(hql);
query.setParameter("problemId", submitReport.getProblemId());
query.setParameter("adminuserId", submitReport.getAdminuserId());
query.setParameter("studentId", submitReport.getStudentId());
int result = query.executeUpdate();
return result > 0;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public String getReportSubmittedStatus(SubmitReport submitReport) {
Session session = this.getCurrentSession();
String submissionStatus = null; // 默认为 null,表示没有找到状态或发生错误
try {
// 构建查询语句
String hql = "SELECT submissionStatus FROM SubmitReport WHERE problemId = :problemId AND studentId = :studentId AND adminuserId = :adminuserId";
Query<String> query = session.createQuery(hql, String.class);
query.setParameter("problemId", submitReport.getProblemId());
query.setParameter("studentId", submitReport.getStudentId());
query.setParameter("adminuserId", submitReport.getAdminuserId());
submissionStatus = query.uniqueResult();
} catch (Exception e) {
e.printStackTrace();
}
return submissionStatus;
}
@Override
public boolean savePlagiarismResult(SubmitReport submitReport) {
Session session = this.getCurrentSession();
try {
String hql = "UPDATE SubmitReport SET plagiarismStatus = :plagiarismStatus, " +
"simularity = :simularity, " +
"similardocs = :similardocs " +
"WHERE problemId = :problemId " +
"AND adminuserId = :adminuserId " +
"AND studentId = :studentId";
Query query = session.createQuery(hql);
query.setParameter("plagiarismStatus", submitReport.getPlagiarismStatus());
query.setParameter("simularity", submitReport.getSimularity());
query.setParameter("similardocs", submitReport.getSimilardocs());
query.setParameter("problemId", submitReport.getProblemId());
query.setParameter("adminuserId", submitReport.getAdminuserId());
query.setParameter("studentId", submitReport.getStudentId());
int result = query.executeUpdate();
return result > 0;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public boolean saveAutoScoreResult(SubmitReport submitReport) {
Session session = this.getCurrentSession();
try {
String hql = "UPDATE SubmitReport SET autoscore = :autoscore, " +
"autocomment = :autocomment " +
"WHERE problemId = :problemId " +
"AND adminuserId = :adminuserId " +
"AND studentId = :studentId";
Query query = session.createQuery(hql);
query.setParameter("autoscore", submitReport.getAutoscore());
query.setParameter("autocomment", submitReport.getAutocomment());
query.setParameter("problemId", submitReport.getProblemId());
query.setParameter("adminuserId", submitReport.getAdminuserId());
query.setParameter("studentId", submitReport.getStudentId());
int result = query.executeUpdate();
return result > 0;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}

View File

@ -1,20 +0,0 @@
package com.example.aes.problem.model;
import lombok.Data;
import java.util.List;
@Data
public class AutoScoreResult {
private double totalScore;
private List<String> remarks;
public AutoScoreResult(double totalScore, List<String> remarks) {
this.totalScore = totalScore;
this.remarks = remarks;
}
public AutoScoreResult() {
}
}

View File

@ -29,6 +29,7 @@ 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;//最后在页面显示的状态,取决于上面三个参数。

View File

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

View File

@ -39,8 +39,9 @@ public class SubmitReport {
private String comment;
@Column(name = "addTime")
private LocalDateTime addTime;
@Column(name = "simularity")
private Float simularity;
@Column(name="simularity")
private float simularity;
@Column(name = "similardocs")
private String similardocs;
}

View File

@ -1,382 +0,0 @@
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) {
// 构建文件路径
Path filePath = Paths.get(fileName);
// 读取上传的文件内容
String article1 = readFile(filePath.toString());
// 读取upload目录下的所有文件
List<Path> filePaths = listFilesInDirectory(uploadDirectory);
Float highestSimilarity = (float)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中的字符数
}
// 计算整体相似度
Float similarity = (float)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;
}
public String readFileWithoutClean(String filePath) {
String fileExtension = getFileExtension(filePath).toLowerCase();
String content = "";
if (fileExtension.equals("txt")) {
content = readTextFile(filePath);
} else if (fileExtension.equals("doc")||fileExtension.equals("docx")) {
content = readDocFile(filePath);
} else if (fileExtension.equals("pdf")) {
content = 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 "";
}
/**********************************///数据清洗部分
// 添加一个静态的停用词集合
private static final Set<String> STOP_WORDS = new HashSet<>();//过滤停用词
static {
try {
// 获取项目根目录路径
String projectRootDirectory = System.getProperty("user.dir");
// 构建停用词文件的路径
String stopwordsFilePath = projectRootDirectory + File.separator + "停用词.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

@ -2,7 +2,6 @@ package com.example.aes.problem.service.Impl;
import com.example.aes.problem.dao.*;
import com.example.aes.problem.model.*;
import com.example.aes.problem.service.PlagiarismServiceI;
import com.example.aes.problem.service.ProblemServiceI;
import com.example.aes.user.dao.AdminusersDaoI;
import com.example.aes.user.dao.ClassesDaoI;
@ -10,10 +9,6 @@ import com.example.aes.user.model.DataGrid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
@ -31,10 +26,6 @@ public class ProblemServiceImpl implements ProblemServiceI {
private AdminusersDaoI adminusersDao;
private PostProblemDaoI postProblemDao;
private ClassesDaoI classesDao;
@Autowired
private PlagiarismServiceI plagiarismServiceI;
@Autowired
public void setClassesDao(ClassesDaoI classesDao) {
this.classesDao = classesDao;
@ -158,32 +149,23 @@ 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());
pmProblem.setPlagiarismStatus(reportStatus.getPlagiarismStatus());
if (reportStatus.getScore() != null) {
pmProblem.setScore(reportStatus.getScore());
}
String submissionStatus = reportStatus.getSubmissionStatus();
if ("未领取".equals(submissionStatus) || "已领取".equals(submissionStatus) || submissionStatus == null) {
// 根据submissionStatus设置status
if ("0".equals(reportStatus.getSubmissionStatus())) {
pmProblem.setStatus("未提交");
} else if ("已提交".equals(submissionStatus)) {
} else if ("1".equals(reportStatus.getSubmissionStatus())) {
pmProblem.setStatus("已提交");
} else if ("已撤回".equals(submissionStatus)) {
pmProblem.setStatus("已撤回");
}
// 如果score或者autoscore不为空,则将status设置为score的具体值
// 如果score不为空,则将status设置为score的具体值
if (reportStatus.getScore() != null) {
pmProblem.setStatus(String.format("%.2f", reportStatus.getScore()));
} else if (reportStatus.getAutoscore() != null) {
pmProblem.setStatus(String.format("%.2f", reportStatus.getAutoscore()));
}
}
}
@ -500,105 +482,6 @@ public int redoReport(PMReviewMessage pmReviewMessage){
return studentSubmitDao.findNextId(problemId).get(0);
else return 0;
}
@Override
public boolean saveSubmitInfoToProps(SubmitReport submitReport) {
return studentSubmitDao.saveSubmitInfo(submitReport);
}
@Override
public boolean findReportSubmitted(SubmitReport submitReport) {
return studentSubmitDao.getReportSubmitted(submitReport);
}
@Override
public boolean updateSubmitInfoToProps(SubmitReport submitReport) {
return studentSubmitDao.updateReportSubmitted(submitReport);
}
@Override
public boolean updateSubmitInfoForSubmission(SubmitReport submitReport) {
return studentSubmitDao.updateReportSubmittedForSubmission(submitReport);
}
@Override
public boolean withdrawSubmitInfo(SubmitReport submitReport) {
return studentSubmitDao.withdrawReportSubmitted(submitReport);
}
@Override
public String findReportSubmittedStatus(SubmitReport submitReport) {
return studentSubmitDao.getReportSubmittedStatus(submitReport);
}
@Override
public boolean savePlagiarismResultToProps(SubmitReport submitReport) {
return studentSubmitDao.savePlagiarismResult(submitReport);
}
@Override
public AutoScoreResult getAutoScore(Path filePath, int problemId) {
// 读取报告文件内容
String reportContent = (plagiarismServiceI.readFileWithoutClean(filePath.toString()));
// 从数据库获取评分标准
List<ScoringStandard> standards = scoringStandardDao.getStandardIdByProblemId(problemId);
double totalScore = 0.0;
List<String> remarks = new ArrayList<>(); // 用于存储评语
for (ScoringStandard standard : standards) {
String dimension = standard.getDimension();
// 根据standardId获取评分细则
List<ScoringRubric> rubrics = scoringRubricDao.getScoringRubricByStandardId(standard.getId());
// 如果找到了评分细则,格式化dimension字符串
if (rubrics != null && !rubrics.isEmpty()) {
StringBuilder dimensionBuilder = new StringBuilder(dimension + ":");
for (int i = 0; i < rubrics.size(); i++) {
ScoringRubric rubric = rubrics.get(i);
if (i > 0) {
dimensionBuilder.append(";"); // 分隔不同的细则
}
dimensionBuilder.append(rubric.getLowerLimit())
.append("分~")
.append(rubric.getUpperLimit())
.append("分,")
.append(rubric.getDescription());
}
dimension = dimensionBuilder.toString();
}
double weight = standard.getWeight();
String scoreStr = null;
try { // 调用Python脚本进行评分
scoreStr = executePythonScript(reportContent, dimension);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 使用逗号分割评分和评语
String[] scoreAndReason = scoreStr.split("\n");
if (scoreAndReason.length < 2) {
throw new RuntimeException("Python脚本输出格式不正确");
}
double score = Double.parseDouble(scoreAndReason[0].replace("score: ", "").trim());
String reason = scoreAndReason[1].replace("reason: ", "").trim();
// 存储评语
remarks.add(reason);
// 计算加权分数
totalScore += score * weight;
}
// 将总分转换为100分制
totalScore *= 10;
// 返回评分和评语
return new AutoScoreResult(totalScore, remarks);
}
@Override
public boolean saveAutoScoreResultToProps(SubmitReport submitReport) {
return studentSubmitDao.saveAutoScoreResult(submitReport);
}
@Override
public int editPostProblem(int classId,
int problemId,LocalDateTime startTime,LocalDateTime endTime,boolean allow){
@ -728,33 +611,4 @@ public int redoReport(PMReviewMessage pmReviewMessage){
}
}
}
private String executePythonScript(String content, String dim) throws IOException, InterruptedException {
// 构建Python命令
String pythonCommand = String.format("python original.py \"%s\" \"%s\"", content, dim);
// 执行Python脚本
Process process = Runtime.getRuntime().exec(pythonCommand);
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new RuntimeException("Python脚本执行失败");
}
// 读取输出
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder output = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
reader.close();
String scoreAndReason = output.toString().trim();
if (scoreAndReason == null || scoreAndReason.isEmpty()) {
throw new RuntimeException("Python脚本输出为空");
}
return scoreAndReason;
}
}

View File

@ -1,14 +0,0 @@
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);
public String readFileWithoutClean(String filePath);
}

View File

@ -7,7 +7,6 @@ import com.example.aes.problem.model.DataGrid;
import com.example.aes.user.model.*;
import javax.persistence.criteria.CriteriaBuilder;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.List;
@ -57,14 +56,4 @@ public interface ProblemServiceI {
public List<Float> findScoreList(PMStudentSubmit pmStudentSubmit);
public List<SubmitReport> findSimilardocs(PMStudentSubmit pmStudentSubmit);
public int findNextId(int problemId);
public boolean saveSubmitInfoToProps(SubmitReport submitReport);//学生保存报告(保存,之前未保存过)
public boolean findReportSubmitted(SubmitReport submitReport);//查询该学生之前是否已经提交过该问题的报告
public boolean updateSubmitInfoToProps(SubmitReport submitReport);//学生更新报告信息(保存,之前保存过)
public boolean updateSubmitInfoForSubmission(SubmitReport submitReport);//学生提交报告
public boolean withdrawSubmitInfo(SubmitReport submitReport);//学生撤回报告
public String findReportSubmittedStatus(SubmitReport submitReport);//查询该学生之前已经提交过该问题的报告状态
public boolean savePlagiarismResultToProps(SubmitReport submitReport);//保存查重信息
public AutoScoreResult getAutoScore(Path filePath, int problemId);//自动批阅
public boolean saveAutoScoreResultToProps(SubmitReport submitReport);//保存自动批阅信息
}

View File

@ -10,7 +10,10 @@ 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.*;
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 javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

View File

@ -5,7 +5,7 @@ server.port=8082
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
jdbc_url=jdbc:mysql://localhost:3306/aes?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=True
jdbc_username=root
jdbc_password=123456
jdbc_password=13661744518xh
#spring.datasource.url=jdbc:mysql://localhost:3306/aes?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=True
#spring.datasource.username=root
#spring.datasource.password=123456
@ -43,8 +43,4 @@ spring.servlet.multipart.max-request-size=50MB
spring.main.allow-bean-definition-overriding=true
spring.jackson.serialization-inclusion=non_null
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=hqb@1234
#??null?

View File

@ -16,7 +16,7 @@
https://www.springframework.org/schema/context/spring-context.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">
<context:property-placeholder location="classpath:application.properties,classpath:redis.properties"/>
<context:property-placeholder location="classpath:redis.properties"/>
<!-- 配置数据源 -->
@ -125,7 +125,7 @@
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}"/>
<property name="port" value="${redis.port}"/>
<property name="password" value="${redis.pass}"/>
<!-- <property name="password" value="${redis.pass}"/>-->
<property name="poolConfig" ref="poolConfig"/>
</bean>
</beans>

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,54 +0,0 @@
import openai
import time
import re
import sys
model = "Qwen/Qwen3-8B"
max_tokens = 500
output_format = """
score:
reason:
"""
def getDim(content, dim):
client = openai.OpenAI(base_url='https://api.siliconflow.cn/v1',
api_key="xxx")
time.sleep(3)
messages = []
messages.append({
"role": "system",
"content": "你是一位评分老师,请根据提供给你的评分标准对文本进行评分,结果仅仅输出分数,满分为10分,分数允许小数点后保留两位,并就当前维度给出评语,评语文本不要过长。"
"输出格式:"
f"{output_format}"
})
messages.append({
"role": "user",
"content": f"待评分内容:{content}"
f"评分标准:{dim}"
})
response = client.chat.completions.create(
model=model,
messages=messages,
max_tokens=max_tokens,
temperature=0.1
)
# print(response.choices[0].message.content)
# match = re.search(r'\d+', response.choices[0].message.content)
# if match:
# score = match.group()
# else:
# score = response.choices[0].message.content
# print(score)
# 去除多余的换行符
content = response.choices[0].message.content.strip()
return content
if __name__ == "__main__":
content = sys.argv[1]
dim = sys.argv[2]
result = getDim(content, dim)
print(result)

View File

@ -0,0 +1,4 @@
智能手机在当代社会的作用不可小觑,它不仅使得人们的通讯变得极为便捷,还提供了多样化的娱乐和工作功能。在我国,智能手机的普及率不断上升,几乎成了每个人生活中的必需品。它的多功能性和便携性,使其成为了现代科技的一个标志性产物。无论是通讯、娱乐还是学习,都离不开这款电子设备。
在城市边缘的那所著名大学的操场上,一场几千人参加的批斗会已经进行了近两个小时。
“爱因斯坦是反动的学术权威,他有奶便是娘,跑去为美帝国主义造原子弹!要建立起革命的科学,就要打倒以相对论为代表的资产阶级理论黑旗!”
然后在法文词下列出同义的上海方言词语,再尽可能详尽地列出近义的上海方言词语、短语或句子。这种编写方式,对中法两国学习者或词典使用者尽可能多地掌握法汉词汇,显然是很有意义的。

File diff suppressed because it is too large Load Diff