摘要随着教育信息化与在线教学的普及传统纸质作业布置、收取与批改方式效率低、反馈慢难以满足师生对作业过程管理与数据分析的需求。本文设计并实现了一套基于 B/S 架构的学生作业管理系统采用前后端分离模式面向教务管理员、任课教师与学生三类角色覆盖班级与账号管理、作业发布、在线提交、批改反馈、错题记录、完成率统计及作业数据可视化等完整业务链路。系统后端基于 Spring Boot 构建 RESTful 服务持久层采用 MyBatis-Plus 访问 MySQL 数据库通过 JWT 实现无状态身份认证与基于角色的访问控制前端采用 Vue 3 单页应用配合 Element Plus 与 ECharts界面采用 AdminLTE 风格深色侧边栏与浅色内容区主色调为 #165DFF。数据库共设计七张业务表三类角色分表存储。学生可上传 pdf、doc、图片等格式附件教师批改时可标注问题并写入错题记录供学生后续查阅。经功能测试系统各模块运行稳定权限边界清晰满足高校及实训场景下的作业管理需求对同类 Web 教学管理系统的开发具有一定的参考价值。技术栈 Spring Boot 3 MyBatis-Plus MySQL Vue 3 Element Plus ECharts数据库表7张文末获取联系文末获取联系作者介绍专注计算机课设、毕设辅导个人开发坚持原创非工作室源码全网唯一。✅技术主流SpringBoot Vue 前后端分离MySQLEcharts数据统计可本地运行✅配套资料源码 数据库 实验报告/论文 答辩 PPT部署演示远程调试问题解答技术范围SpringBoot、Vue、数据可视化、小程序、HLMT、Nodejs、uni-app、MySQL数据库、ElementUi等设计与开发。适用范围软件工程、软件技术、数据库课程设计、计算机科学与技术、数据库系统原理、JavaWeb开发、JavaEE、Java、Web应用开发、动态网页设计的课程设计、课设、大作业、课程实验、期末作业实验报告参考内容实验报告可供大家参考使用功能展示学生教师管理员数据库及架构系统数据库设计为Controller及Service层核心代码写法package com.springboot.controller; import com.springboot.auth.RequireRole; import com.springboot.dto.*; import com.springboot.entity.Submission; import com.springboot.entity.UserRole; import com.springboot.service.SubmissionService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; RestController RequestMapping(/api/submissions) RequiredArgsConstructor public class SubmissionController { private final SubmissionService submissionService; GetMapping(/page) RequireRole({UserRole.TEACHER, UserRole.STUDENT}) public ApiResponsePageResultSubmission page( RequestParam(required false) String keyword, RequestParam(required false) String status, RequestParam(required false) Long assignment_id, RequestParam(defaultValue 1) int page, RequestParam(defaultValue 10) int size) { return ApiResponse.ok(submissionService.page(keyword, status, assignment_id, page, size)); } PostMapping RequireRole({UserRole.STUDENT}) public ApiResponseSubmission submit(RequestBody SubmissionFormDTO dto) { return ApiResponse.ok(提交成功, submissionService.submit(dto)); } PutMapping(/{id}/grade) RequireRole({UserRole.TEACHER}) public ApiResponseSubmission grade(PathVariable Long id, RequestBody GradingDTO dto) { return ApiResponse.ok(批改成功, submissionService.grade(id, dto)); } } package com.springboot.service; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.springboot.auth.AuthContext; import com.springboot.dto.GradingDTO; import com.springboot.dto.PageResult; import com.springboot.dto.SubmissionFormDTO; import com.springboot.dto.WrongQuestionItemDTO; import com.springboot.entity.Assignment; import com.springboot.entity.SchoolClass; import com.springboot.entity.Student; import com.springboot.entity.Submission; import com.springboot.entity.WrongQuestion; import com.springboot.mapper.AssignmentMapper; import com.springboot.mapper.SchoolClassMapper; import com.springboot.mapper.StudentMapper; import com.springboot.mapper.SubmissionMapper; import com.springboot.mapper.WrongQuestionMapper; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.time.LocalDateTime; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; Service RequiredArgsConstructor public class SubmissionService { private final SubmissionMapper submissionMapper; private final AssignmentMapper assignmentMapper; private final StudentMapper studentMapper; private final SchoolClassMapper schoolClassMapper; private final WrongQuestionMapper wrongQuestionMapper; public PageResultSubmission page(String keyword, String status, Long assignmentId, int page, int size) { var wrapper Wrappers.SubmissionlambdaQuery() .eq(StringUtils.hasText(status), Submission::getStatus, status) .eq(assignmentId ! null, Submission::getAssignment_id, assignmentId) .eq(AuthContext.isStudent(), Submission::getStudent_id, AuthContext.getUserId()) .and(AuthContext.isTeacher(), w - { ListLong assignmentIds assignmentMapper.selectList( Wrappers.AssignmentlambdaQuery().eq(Assignment::getTeacher_id, AuthContext.getUserId())) .stream().map(Assignment::getId).toList(); if (assignmentIds.isEmpty()) w.eq(Submission::getId, -1L); else w.in(Submission::getAssignment_id, assignmentIds); }) .and(StringUtils.hasText(keyword), w - w.like(Submission::getContent, keyword)) .orderByDesc(Submission::getId); PageSubmission result submissionMapper.selectPage(new Page(page, size), wrapper); enrich(result.getRecords()); return PageResult.of(result); } Transactional public Submission submit(SubmissionFormDTO dto) { if (dto.getAssignment_id() null) throw new RuntimeException(请选择作业); Assignment assignment assignmentMapper.selectById(dto.getAssignment_id()); if (assignment null) throw new RuntimeException(作业不存在); Student student studentMapper.selectById(AuthContext.getUserId()); if (student null || !Objects.equals(assignment.getClass_id(), student.getClass_id())) { throw new RuntimeException(无权提交该作业); } if (assignment.getDeadline() ! null LocalDateTime.now().isAfter(assignment.getDeadline())) { throw new RuntimeException(作业已过截止时间); } Submission existing submissionMapper.selectOne(Wrappers.SubmissionlambdaQuery() .eq(Submission::getAssignment_id, dto.getAssignment_id()) .eq(Submission::getStudent_id, AuthContext.getUserId())); if (existing ! null) { if (GRADED.equals(existing.getStatus())) throw new RuntimeException(作业已批改无法修改); existing.setContent(dto.getContent()); existing.setAttachment_url(dto.getAttachment_url()); existing.setSubmitted_at(LocalDateTime.now()); submissionMapper.updateById(existing); enrich(List.of(existing)); return existing; } Submission sub new Submission(); sub.setAssignment_id(dto.getAssignment_id()); sub.setStudent_id(AuthContext.getUserId()); sub.setContent(dto.getContent()); sub.setAttachment_url(dto.getAttachment_url()); sub.setStatus(SUBMITTED); sub.setSubmitted_at(LocalDateTime.now()); submissionMapper.insert(sub); enrich(List.of(sub)); return sub; } Transactional public Submission grade(Long id, GradingDTO dto) { Submission sub getForTeacher(id); if (!SUBMITTED.equals(sub.getStatus()) !GRADED.equals(sub.getStatus())) { throw new RuntimeException(无法批改该提交); } if (dto.getScore() null || dto.getScore() 0 || dto.getScore() 100) { throw new RuntimeException(请填写0-100之间的分数); } sub.setScore(dto.getScore()); sub.setFeedback(dto.getFeedback()); sub.setProblem_note(dto.getProblem_note()); sub.setStatus(GRADED); sub.setGraded_at(LocalDateTime.now()); submissionMapper.updateById(sub); wrongQuestionMapper.delete(Wrappers.WrongQuestionlambdaQuery().eq(WrongQuestion::getSubmission_id, id)); if (dto.getWrong_questions() ! null) { for (WrongQuestionItemDTO item : dto.getWrong_questions()) { if (!StringUtils.hasText(item.getQuestion_title())) continue; WrongQuestion wq new WrongQuestion(); wq.setStudent_id(sub.getStudent_id()); wq.setAssignment_id(sub.getAssignment_id()); wq.setSubmission_id(sub.getId()); wq.setQuestion_title(item.getQuestion_title()); wq.setWrong_content(item.getWrong_content()); wq.setCorrect_content(item.getCorrect_content()); wq.setTeacher_note(item.getTeacher_note()); wq.setCreated_at(LocalDateTime.now()); wrongQuestionMapper.insert(wq); } } enrich(List.of(sub)); return sub; } private Submission getForTeacher(Long id) { Submission sub submissionMapper.selectById(id); if (sub null) throw new RuntimeException(提交记录不存在); Assignment assignment assignmentMapper.selectById(sub.getAssignment_id()); if (assignment null) throw new RuntimeException(作业不存在); if (AuthContext.isTeacher() !Objects.equals(assignment.getTeacher_id(), AuthContext.getUserId())) { throw new RuntimeException(无权批改该作业); } return sub; } private void enrich(ListSubmission list) { if (list null || list.isEmpty()) return; ListLong assignmentIds list.stream().map(Submission::getAssignment_id).distinct().toList(); ListLong studentIds list.stream().map(Submission::getStudent_id).distinct().toList(); MapLong, Assignment assignmentMap assignmentMapper.selectBatchIds(assignmentIds).stream() .collect(Collectors.toMap(Assignment::getId, a - a)); MapLong, Student studentMap studentMapper.selectBatchIds(studentIds).stream() .collect(Collectors.toMap(Student::getId, s - s)); ListLong classIds studentMap.values().stream().map(Student::getClass_id).filter(Objects::nonNull).distinct().toList(); MapLong, SchoolClass classMap classIds.isEmpty() ? Map.of() : schoolClassMapper.selectBatchIds(classIds).stream().collect(Collectors.toMap(SchoolClass::getId, c - c)); for (Submission sub : list) { Assignment a assignmentMap.get(sub.getAssignment_id()); if (a ! null) sub.setAssignment_title(a.getTitle()); Student s studentMap.get(sub.getStudent_id()); if (s ! null) { sub.setStudent_name(s.getReal_name() ! null ? s.getReal_name() : s.getUsername()); SchoolClass sc classMap.get(s.getClass_id()); if (sc ! null) sub.setClass_name(sc.getName()); } } } }擅长功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文降重、长期答辩答疑辅导、腾讯会议一对一专业讲解辅导答辩、模拟答辩演练、和理解代码逻辑思路等。获取联系项目功能完整可在本地运行并可远程调试确保运行顺利获取联系方式课程设计获取https://blog.csdn.net/qq_59059632/article/details/163685632?spm1001.2014.3001.5501