在class类中编写了根据学生id查班级id接口(暂无别用),目前用于作业查询部分调用方法;

优化部分前端字体大小,排版格式等;
初步构建了作业部分的后端大致框架,尝试编写学生端查询作业信息模块,后端逻辑未全部完成。
This commit is contained in:
musteven 2025-03-16 10:11:16 +08:00
parent a3f430f02d
commit ef8707272c
19 changed files with 510 additions and 16 deletions

View File

@ -19,5 +19,11 @@ export default {
updateUserPwd: (data) => request.post('/user/editUserPassword', data), updateUserPwd: (data) => request.post('/user/editUserPassword', data),
signUser: (data) => request.post('/user/signUser', data, { noNeedToken: true }), signUser: (data) => request.post('/user/signUser', data, { noNeedToken: true }),
//班级相关
takeClassByInviteCode: (data) => request.post('/classes/takeClassByInviteCode', data), //¼ÓÈë°à¼¶ takeClassByInviteCode: (data) => request.post('/classes/takeClassByInviteCode', data), //¼ÓÈë°à¼¶
//作业题目部分
getExamList: (data) =>request.post('/problem/getExamList')//学生获取作业列表
} }

View File

@ -1,12 +1,12 @@
<template> <template>
<div flex-col gap-y-10> <div flex-col gap-y-10>
<n-button type="primary" size="small" @click="onStart">参加</n-button> <n-button type="primary" size="small" @click="onStart">进入</n-button>
<n-button type="success" size="small" @click="onGrade">成绩</n-button> <n-button type="success" size="small" @click="onGrade">成绩</n-button>
</div> </div>
</template> </template>
<script setup> <script setup>
// 控制“参加”按钮在短时间内只能被点击一次,该按钮若短时间内多次点击会出现 // 控制“进入”按钮在短时间内只能被点击一次,该按钮若短时间内多次点击会出现
// “Redis未启动”、“提交本题不能过快,请稍后重试”等错误 // “Redis未启动”、“提交本题不能过快,请稍后重试”等错误
const isClicked = ref(false) const isClicked = ref(false)

View File

@ -1,7 +1,223 @@
<template> <template>
<h1>作业列表</h1> <CommonPage show-footer>
<n-data-table
remote
:single-line="false"
:columns="columns"
:data="examList"
:loading="loading"
:pagination="paginationReactive"
:row-key="rowKey"
:scroll-x="900"
@update:page="onChangePage"
/>
</CommonPage>
</template> </template>
<script setup> <script setup>
import { ref, reactive, onMounted } from 'vue'
import api from '@/api/user'
import ExamActions from './components/exam-actions.vue'
import { formatDateTime, getToken } from '@/utils'
import { useUserStore } from '@/store'
import { useRouter } from 'vue-router'
import { useCountdown } from '@/composables'
const userStore = useUserStore()
const router = useRouter()
const loading = ref(true)
const examList = ref([])
const countdownControllers = {}
const paginationReactive = reactive({
page: 1,
pageSize: 10,
prefix({ itemCount }) {
return `总共 ${itemCount} 条`
},
})
const onStartExam = async (exam) => {//点击进入控件的逻辑
const { status, id, type } = exam
try {
await api.addExamInfo({ id })
if (type == 'iTraining') {
router.push({ name: 'TrainingIndex', params: { examId: id } })
} else {
router.push({ name: 'ContestIndex', params: { examId: id } })
}
} catch (err) {
console.log(err)
}
}
const onViewScore = (exam) => {//点击成绩控件的逻辑
var trainingViewScore = ''
if (exam.type == 'iTraining') {
trainingViewScore = 'iTraining'
} else {
trainingViewScore = exam.studentViewScore
}
const examId = exam.id
const problemNum = exam.problemNum
switch (trainingViewScore) {
case 'System':
if (parseInt(problemNum) <= 10) {
router.push({ name: 'ContestScoreRankStatus', params: { examId }, query: { fromclient: false } })
} else {
router.push({ name: 'ContestScoreTop10', params: { examId }, query: { fromclient: false } })
}
break
case 'RankAndStatus':
router.push({ name: 'ContestScoreRankStatus', params: { examId }, query: { fromclient: false } })
break
case 'OnlyRank':
router.push({ name: 'ContestScoreOnlyRank', params: { examId }, query: { fromclient: false } })
break
case 'Top10':
router.push({ name: 'ContestScoreTop10', params: { examId }, query: { fromclient: false } })
break
case 'iTraining':
router.push({ name: 'TrainingScoreTrainRank', params: { examId }, query: { fromclient: false } })
break
case 'NoScorePage':
$message.warning("当前作业不能查看成绩");
break
default:
}
}
const columns = [
{
key: 'name',
title: '作业名称',
fixed: 'left',
width: 150,
align: 'center',
},
{
key: 'description',
title: '描述信息',
},
{
key: 'starttime',
title: '开始时间',
width: '108',
align: 'center',
render: (row) => {
return row['starttime'] ? formatDateTime(row['starttime']) : ''
},
},
{
key: 'endtime',
title: '结束时间',
width: '108',
align: 'center',
render: (row) => {
return row['endtime'] ? formatDateTime(row['endtime']) : ''
},
},
{
key: 'status',
title: '状态',
width: '116',
render: (row, idx) => {
if (countdownControllers[row.id]) {
const { leftTime, minute, second } = countdownControllers[row.id]
if (leftTime.value <= 0) {
examList.value[idx].status = '考试进行中'
return '考试进行中'
}
return `还有${minute.value}分${second.value}秒`
}
return row.status
},
},
{
key: 'actions',
title: '操作',
width: '80',
fixed: 'right',
render(row) {
// console.log(toRaw(row))
return h(ExamActions, {
onClickStart: () => onStartExam(row),
onClickGrade: () => onViewScore(toRaw(row)),
})
},
},
]
const queryByPage = async (currentPage) => {
loading.value = true
//先清理所有定时器
for (let key in countdownControllers) {
countdownControllers[key].clearCountdown()
}
try {
const res = await api.getExamList({
pageNum: currentPage,
pageSize: paginationReactive.pageSize,
})
examList.value = res.data // 假设返回的数据在 res.data 中
const { pageNum, pageSize, total, list } = res.data
paginationReactive.page = pageNum
paginationReactive.pageSize = pageSize
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 {
loading.value = false
}
}
const onChangePage = (page) => {
paginationReactive.page = page
if (!loading.value) {
queryByPage(page)
}
}
const onUpdatePageSize = (pageSize) => {
paginationReactive.pageSize = pageSize
paginationReactive.page = 1
queryByPage(1)
}
const rowKey = (rowData) => {
return rowData.id
}
onMounted(() => {
queryByPage(paginationReactive.page)
})
//销毁倒计时
onBeforeUnmount(() => {
for (let key in countdownControllers) {
countdownControllers[key].clearCountdown()
}
})
</script> </script>

View File

@ -95,7 +95,7 @@ img.shadow-img {
/* 文字说明样式 */ /* 文字说明样式 */
.index-right-div { .index-right-div {
margin-left: 35px; margin-left: 35px;
width: 400px; width: 300px;
} }
/* 修改文字说明样式,添加悬停效果 */ /* 修改文字说明样式,添加悬停效果 */

View File

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

View File

@ -63,7 +63,7 @@ const handleValidateButtonClick = ()=>{
} }
.lead .lead
{ {
font-size:1.25rem; font-size:1.4rem;
font-weight:300 font-weight:300
} }
.divCode{ .divCode{

View File

@ -0,0 +1,72 @@
package com.example.aes.problem.controller;
import com.example.aes.global.model.DecodeToken;
import com.example.aes.global.model.RespBean;
import com.example.aes.problem.model.PMProblem;
import com.example.aes.problem.service.ProblemServiceI;
import com.example.aes.user.dao.AccountDao;
import com.example.aes.user.model.Adminusers;
import com.example.aes.user.model.Classstudents;
import com.example.aes.user.model.DataGrid;
import com.example.aes.user.model.Users;
import com.example.aes.user.service.ClassesServiceI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/problem")
public class ProblemController {
@Autowired
private ClassesServiceI classesServiceI;
@Autowired
private ProblemServiceI problemServiceI;
//获取作业列表
@GetMapping("/getExamList")
public RespBean getExamList(Integer pageNum,Integer pageSize, HttpServletRequest request) {
if (pageNum < 1 || pageSize < 1)
return RespBean.error("参数错误!");
// PMProblem pMproblem = new PMProblem();
// pMproblem.setPageNum(pageNum);
// pMproblem.setPageSize(pageSize);
DecodeToken decodeToken = new DecodeToken(request);
String userId = decodeToken.getUserId();
// 根据学生id查找班级id
List<Classstudents> classstudentsList = classesServiceI.findClassIdByStudentId(Integer.parseInt(userId));
List<Integer> classIds = new ArrayList<Integer>();
for (Classstudents classstudent : classstudentsList) {
//提取所有的classstudentslist中的classid并存放到新的classids表里面用作后续处理
classIds.add(classstudent.getClassId());
}
List<PMProblem> pMproblems = problemServiceI.getProblemsByClassIds(classIds);
if (pMproblems != null) {
return RespBean.ok("获取作业列表成功!",pMproblems);
} else {
return RespBean.error("获取作业列表失败!");
}
}
////////////逻辑在未完成
}

View File

@ -0,0 +1,20 @@
package com.example.aes.problem.dao;
import com.example.aes.problem.model.PMProblem;
import com.example.aes.problem.model.Problem;
import com.example.aes.user.dao.BaseDaoI;
import com.example.aes.user.model.Classstudents;
import java.util.List;
public interface ProblemDaoI extends BaseDaoI<Problem> {
List<PMProblem> findProblemsByClassIds(List<Integer> classIds);
}

View File

@ -0,0 +1,42 @@
package com.example.aes.problem.dao.impl;
import com.example.aes.problem.dao.ProblemDaoI;
import com.example.aes.problem.model.PMProblem;
import com.example.aes.problem.model.Problem;
import com.example.aes.user.dao.BaseDaoI;
import com.example.aes.user.dao.impl.BaseDaoImpl;
import com.example.aes.user.model.Classstudents;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository("ProblemDao")
public class ProblemDaoImpl extends BaseDaoImpl<Problem> implements ProblemDaoI{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public List<PMProblem> findProblemsByClassIds(List<Integer> classIds){
// SQL查询,根据classIds获取problemId, startTime, endTime
String sql = "SELECT p.problem_id AS problemId, pp.start_time AS startTime, pp.end_time AS endTime " +
"FROM postproblem pp " +
"JOIN problem p ON pp.problem_id = p.id " +
"WHERE pp.class_id IN (?)";
// 将classIds转换为逗号分隔的字符串
String classIdsStr = classIds.toString().replace("[", "").replace("]", "");
// 执行查询
List<PMProblem> problems = jdbcTemplate.query(
sql,
new Object[]{classIdsStr},
new BeanPropertyRowMapper<>(PMProblem.class)
);
return problems;
}
}

View File

@ -0,0 +1,39 @@
package com.example.aes.problem.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PMProblem extends Problem implements java.io.Serializable{//继承Problems子类
// //作业problem基本属性,继承自Problem类
// private int Id;
// private int courseId;
// private int adminuserId;
// private String title;
// private String description;
// 作业列表分页exam
private int pageNum;
private int pageSize;
// (作业列表)页面上其他需要的属性
private Date startTime;//开始时间,来自postproblem
private Date endTime;//结束时间
private String submissonStatus;//提交状态,来自submitproblem
private String plagiarismStatus;//查重状态
private float score;//最终分数
private String status;//最后在页面显示的状态,取决于上面三个参数。
//(作业列表)页面需要的后台数据
private int studentId;//学生id
private int classId;//班级id
}

View File

@ -0,0 +1,27 @@
package com.example.aes.problem.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Table;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "Problem")
public class Problem implements java.io.Serializable{ //Problem父类
//作业problem基本属性
private int Id;//此处为ProblemId
private int courseId;
private int adminuserId;
private String title;
private String description;
// 作业列表分页exam
private int pageNum;
private int pageSize;
}

View File

@ -0,0 +1,23 @@
package com.example.aes.problem.service.Impl;
import com.example.aes.problem.dao.ProblemDaoI;
import com.example.aes.problem.model.PMProblem;
import com.example.aes.problem.service.ProblemServiceI;
import com.example.aes.user.dao.ClassesDaoI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ProblemServiceImpl implements ProblemServiceI {
private ProblemDaoI problemDao;
@Autowired
public void setProblemDao(ProblemDaoI problemDao) {
this.problemDao = problemDao;
}
public List<PMProblem> getProblemsByClassIds(List<Integer> classIds){
return problemDao.findProblemsByClassIds(classIds);
}
}

View File

@ -0,0 +1,12 @@
package com.example.aes.problem.service;
import com.example.aes.problem.model.PMProblem;
import com.example.aes.user.model.*;
import java.util.List;
public interface ProblemServiceI {
public List<PMProblem> getProblemsByClassIds(List<Integer> classIds);
}

View File

@ -5,10 +5,7 @@ import com.example.aes.global.model.PageBean;
import com.example.aes.global.model.RespBean; import com.example.aes.global.model.RespBean;
import com.example.aes.user.dao.ClassesDaoI; import com.example.aes.user.dao.ClassesDaoI;
import com.example.aes.user.dao.ClassstudentsDaoI; import com.example.aes.user.dao.ClassstudentsDaoI;
import com.example.aes.user.model.DataGrid; import com.example.aes.user.model.*;
import com.example.aes.user.model.Json;
import com.example.aes.user.model.PMClasses;
import com.example.aes.user.model.UClass;
import com.example.aes.user.service.ClassesServiceI; import com.example.aes.user.service.ClassesServiceI;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -176,4 +173,18 @@ public class ClassesController{
} }
@PostMapping("/findClassIdByStudentId")// 根据学生id查找班级id(暂时无用
public RespBean findClassIdByStudentId(HttpServletRequest request)
{
DecodeToken decodeToken = new DecodeToken(request);
String userId = decodeToken.getUserId();//获取学生id
int studentId = Integer.parseInt(userId);
List<Classstudents> classstudentsList = classesServiceI.findClassIdByStudentId(studentId);
if (classstudentsList == null || classstudentsList.isEmpty()) { // 如果列表为空,返回错误信息
return RespBean.error("没有找到相关班级");
}
else return RespBean.ok("查找班级成功", classstudentsList);
}
} }

View File

@ -1,12 +1,7 @@
package com.example.aes.user.dao; package com.example.aes.user.dao;
import com.example.aes.user.model.Classes; import com.example.aes.user.model.*;
import com.example.aes.user.model.PMClasses;
import com.example.aes.user.model.PMUser;
import com.example.aes.user.model.UClass;
import java.util.Date; import java.util.Date;
@ -49,4 +44,6 @@ public interface ClassesDaoI extends BaseDaoI<Classes> {
public long findClassStudentsByIdNum(int classId); public long findClassStudentsByIdNum(int classId);
public List<PMUser> findClassStudentsByIdPage(int classId, int pageNum, int pageSize); public List<PMUser> findClassStudentsByIdPage(int classId, int pageNum, int pageSize);
public boolean deleteClassStudentsByUserId(int classId, int userId); // 通过userId删除班级学生 public boolean deleteClassStudentsByUserId(int classId, int userId); // 通过userId删除班级学生
public List<Classstudents>findClassIdByStudentId(int studentId);//根据班级id查找学生id
} }

View File

@ -538,4 +538,12 @@ public class ClassesDaoImpl extends BaseDaoImpl<Classes> implements ClassesDaoI
return this.count(totalhql).longValue(); return this.count(totalhql).longValue();
} }
@Override
public List<Classstudents> findClassIdByStudentId(int studentId){
String hql = "from Classstudents c where c.userId=" + studentId;
Query q = this.getCurrentSession().createQuery(hql);
return q.list();
}
} }

View File

@ -26,4 +26,5 @@ public interface ClassesServiceI {
public DataGrid findClassNotInCourse(UClass uClass, int pageNum, int pageSize); // 获取未参加课程的班级 public DataGrid findClassNotInCourse(UClass uClass, int pageNum, int pageSize); // 获取未参加课程的班级
public DataGrid findClassNotInCourse(int courseId, int teacherId, int pageNum, int pageSize, boolean history); // 获取未参加课程的班级 public DataGrid findClassNotInCourse(int courseId, int teacherId, int pageNum, int pageSize, boolean history); // 获取未参加课程的班级
public List<Classstudents> findClassIdByStudentId(int studentId); // 根据学生id查找班级id
} }

View File

@ -232,4 +232,11 @@ public void setclassstudentsDao(ClassstudentsDaoI classstudentsDao) {
public boolean deleteClassStudentsByUserId(int classId, int userId) { public boolean deleteClassStudentsByUserId(int classId, int userId) {
return classesDao.deleteClassStudentsByUserId(classId, userId); return classesDao.deleteClassStudentsByUserId(classId, userId);
} }
@Override
public List<Classstudents> findClassIdByStudentId(int studentId){
return classesDao.findClassIdByStudentId(studentId);
};
} }

View File

@ -5,7 +5,7 @@ server.port=8082
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource 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_url=jdbc:mysql://localhost:3306/aes?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=True
jdbc_username=root 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.url=jdbc:mysql://localhost:3306/aes?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=True
#spring.datasource.username=root #spring.datasource.username=root
#spring.datasource.password=123456 #spring.datasource.password=123456