当前位置: 首页 > news >正文

【原创唯一】基于SpringBoot+Vue的学生作业管理系统 课程设计/大作业/期末作业(源码+MySQL数据库+实验报告+PPT+远程部署)

摘要

随着教育信息化与在线教学的普及,传统纸质作业布置、收取与批改方式效率低、反馈慢,难以满足师生对作业过程管理与数据分析的需求。本文设计并实现了一套基于 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 前后端分离,MySQL,Echarts数据统计,可本地运行

配套资料:源码 + 数据库 + 实验报告/论文 + 答辩 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 ApiResponse<PageResult<Submission>> 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 ApiResponse<Submission> submit(@RequestBody SubmissionFormDTO dto) { return ApiResponse.ok("提交成功", submissionService.submit(dto)); } @PutMapping("/{id}/grade") @RequireRole({UserRole.TEACHER}) public ApiResponse<Submission> 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 PageResult<Submission> page(String keyword, String status, Long assignmentId, int page, int size) { var wrapper = Wrappers.<Submission>lambdaQuery() .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 -> { List<Long> assignmentIds = assignmentMapper.selectList( Wrappers.<Assignment>lambdaQuery().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); Page<Submission> 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.<Submission>lambdaQuery() .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.<WrongQuestion>lambdaQuery().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(List<Submission> list) { if (list == null || list.isEmpty()) return; List<Long> assignmentIds = list.stream().map(Submission::getAssignment_id).distinct().toList(); List<Long> studentIds = list.stream().map(Submission::getStudent_id).distinct().toList(); Map<Long, Assignment> assignmentMap = assignmentMapper.selectBatchIds(assignmentIds).stream() .collect(Collectors.toMap(Assignment::getId, a -> a)); Map<Long, Student> studentMap = studentMapper.selectBatchIds(studentIds).stream() .collect(Collectors.toMap(Student::getId, s -> s)); List<Long> classIds = studentMap.values().stream().map(Student::getClass_id).filter(Objects::nonNull).distinct().toList(); Map<Long, 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?spm=1001.2014.3001.5501

http://www.cnnetsun.cn/news/4036322.html

相关文章:

  • RePKG 实战指南:拆开 Wallpaper Engine 的 PKG,把 TEX 纹理转成可编辑的 PNG
  • 告别LaTeX表格手工编码:超强工具实现图形化设计与代码自动生成
  • 2027亚洲AI算力液冷技术展官方市场信任度充足
  • 若依框架项目名称自定义全攻略:从配置到模板的完整实践
  • 外贸企业必看:谷歌SEO优化服务,助力海外订单增长
  • 基于ReAct与Function Calling构建本地AI编程助手:从原理到实战
  • 从零构建具身智能仿真环境:基于Isaac Gym与强化学习的机械臂抓取实战
  • 无血清培养基为什么越来越常用?从批间差异、动物源风险到下游纯化
  • 【学习地图】AI学习 · 文章索引
  • AI长任务处理:SSE、检查点与幂等性构建可靠异步系统
  • OneNote多人共享协作全攻略:从方案选型到问题解决
  • AI 全栈专业名词科普:从入门到理解
  • 构建AI编程助手的代码大脑:知识图谱与语义检索的工程实践
  • K均值聚类评估:SSE与轮廓系数原理、应用与实战
  • Vim正则表达式实战:从基础语法到高效文本处理
  • DeepSeek 深夜开源一个“神经系统“:大模型时代的胜负手,不在模型本身了?
  • 裁判文书大数据分析:从数据拆分到司法趋势洞察的实战指南
  • 如何在 macOS 上免费实现歌词同步:LyricsX 终极使用指南
  • 输入11位手机号,地图自动跳过去:手机号码定位查询系统的开源玩法
  • 构建高质量数据集描述文档:从Google ClusterData看结构化数据管理实践
  • NVIDIA Profile Inspector实战指南:5个场景解锁显卡隐藏设置
  • BEVDet深度解析:从LSS视角转换到3D检测的自动驾驶感知实践
  • 数学建模竞赛Python仿真优化:SimPy离散事件仿真与代码实战
  • Navicat无限试用手把手实战:三招搞定Mac版14天限制,安全不丢数据
  • Maven彻底卸载与重装指南:解决依赖冲突与构建问题
  • Git本地凭据管理:安全查看与迁移HTTPS/SSH认证信息
  • 天赐范式第135天:原型点火——Φ自动切换机制的第一次真实走通与故障记录
  • 贪心算法解决区间覆盖问题:从视频拼接看算法实战
  • Kerberos黄金票据与白银票据攻击:原理、实战与防御指南
  • C++零基础入门指南:从命令行编译到STL实战项目