Compare commits
No commits in common. "3091fbb7dd1ce80e12359a644afb4af722cc8999" and "a9837d2128170f3681691c2ae6345fd7accb741e" have entirely different histories.
3091fbb7dd
...
a9837d2128
|
|
@ -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
|
||||
|
|
@ -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),
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@
|
|||
>
|
||||
<LoginForm
|
||||
:login="api.login"
|
||||
:title="isAdminEnd() ? '报告自动批阅系统 - 管理端' : '报告自动批阅系统 - 登录'"
|
||||
:title="isAdminEnd() ? '报告自动评测系统 - 管理端' : '报告自动评测系统 - 登录'"
|
||||
@close="userStore.closeLoginModal"
|
||||
/>
|
||||
</n-modal>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
<script setup>
|
||||
// 控制“进入”按钮在短时间内只能被点击一次,该按钮若短时间内多次点击会出现
|
||||
// “Redis未启动”、“提交本题不能过快,请稍后重试”等错误
|
||||
const isClicked = ref(false)
|
||||
|
||||
const emit = defineEmits(['clickStart', 'clickGrade'])
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<!-- 整个页面外层container -->
|
||||
<div class="container">
|
||||
<!-- 测试标题 -->
|
||||
<p id="indexTitle" class="lead">在线报告批阅系统(首页)</p>
|
||||
<p id="indexTitle" class="lead">在线报告评测系统(首页)</p>
|
||||
<!-- 图片和文字说明的外层容器 -->
|
||||
<div class="index-content">
|
||||
<!-- 图片 -->
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "index"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -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 = [];
|
||||
|
||||
} 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 {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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为空则显示 "-"
|
||||
};
|
||||
|
||||
|
|
|
|||
50
AES/pom.xml
50
AES/pom.xml
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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("查重失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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("批改过程中发生错误!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -37,11 +35,6 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
|
|||
".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 " +
|
||||
|
|
@ -123,7 +116,6 @@ 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" +
|
||||
|
|
@ -140,7 +132,6 @@ 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();
|
||||
|
|
@ -191,7 +182,6 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
|
|||
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object[]> findScoreList(PMStudentSubmit pmStudentSubmit){
|
||||
int classId= pmStudentSubmit.getClassId();
|
||||
|
|
@ -256,7 +246,6 @@ public class SubmitReportDaoImpl extends BaseDaoImpl<SubmitReport> implements Su
|
|||
query.setParameter("problemId", problemId);
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Integer> findNextId(int problemId){
|
||||
String hql="SELECT " +
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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;//最后在页面显示的状态,取决于上面三个参数。
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -40,7 +40,8 @@ public class SubmitReport {
|
|||
@Column(name = "addTime")
|
||||
private LocalDateTime addTime;
|
||||
@Column(name="simularity")
|
||||
private Float simularity;
|
||||
private float simularity;
|
||||
@Column(name = "similardocs")
|
||||
private String similardocs;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);//保存自动批阅信息
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
324
docs/aes.sql
324
docs/aes.sql
File diff suppressed because one or more lines are too long
1041
docs/自动评分系统需求说明书.md
1041
docs/自动评分系统需求说明书.md
File diff suppressed because it is too large
Load Diff
54
original.py
54
original.py
|
|
@ -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)
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
智能手机在当代社会的作用不可小觑,它不仅使得人们的通讯变得极为便捷,还提供了多样化的娱乐和工作功能。在我国,智能手机的普及率不断上升,几乎成了每个人生活中的必需品。它的多功能性和便携性,使其成为了现代科技的一个标志性产物。无论是通讯、娱乐还是学习,都离不开这款电子设备。
|
||||
在城市边缘的那所著名大学的操场上,一场几千人参加的批斗会已经进行了近两个小时。
|
||||
“爱因斯坦是反动的学术权威,他有奶便是娘,跑去为美帝国主义造原子弹!要建立起革命的科学,就要打倒以相对论为代表的资产阶级理论黑旗!”
|
||||
然后在法文词下列出同义的上海方言词语,再尽可能详尽地列出近义的上海方言词语、短语或句子。这种编写方式,对中法两国学习者或词典使用者尽可能多地掌握法汉词汇,显然是很有意义的。
|
||||
Loading…
Reference in New Issue