ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

课程作业与实验管理系统:前后端分离课设源码全解析

课程作业与实验管理系统:前后端分离课设源码全解析 每学期帮学弟学妹看课程设计代码我遇到最多的题目就是“课程作业和实验管理系统”。这题目听起来不复杂可真正要实现作业发布、实验报告上传、教师批改、成绩统计这条完整链路时很多人第一版交上去是跑不通的。这篇文章基于一套完整的“可白嫖源码”课设项目来做案例分析把“课程作业和实验管理系统”从需求、数据库表、接口设计、前端页面到跑通步骤完整拆开讲适合正在选课设题、想直接改造一套现成源码的读者也适合想学一个完整前后端分离项目怎么落地的人。先说一个基本判断这类系统技术难度不算高难的是把学生、教师、管理员三种角色的权限和状态流转想清楚。只要这一层想明白代码写起来很快。下面我按自己的理解从需求讲到数据库再讲到核心接口实现和上线跑通的完整过程尽量说人话不绕弯子。1. 课程作业与实验管理的真实痛点先搞清系统为谁而做1.1 传统管理方式的三宗罪为什么每届学生都在做这个题目因为现实中太多课程还在用“微信群Excel”管理作业。我见过最离谱的命名是“作业最终版2(新建).docx”老师的电脑桌面堆了几十个这样的文件期末统计成绩时一个个对学号命名的同学甚至用拼音简写比对学生名单能花一下午。实验课的问题更明显。实验报告要么打印纸质版要么一个个邮件发过来老师要下载、归类、重命名、评分最后再把成绩敲进Excel。遇到截止日期时学生不知道到底提交成功没有老师也不知道谁没交。课程作业和实验管理系统本质上就是来解决这三个问题的文件归档、流程透明、成绩自动汇总。1.2 三方角色的诉求差异这类系统的功能边界完全取决于三种角色的痛点。学生最关心的三件事什么时候截止、交成功没有、老师给多少分。所以学生端必须有清晰的待办任务列表提交后要能看历史提交记录成绩出来要能第一时间看到。教师的诉求是“少干活”能一键发布作业、能批量查看提交、能在线上给分写评语、能自动统计提交率和平均分。这里最容易被忽略的是“待批改数量提醒”。教师登录后第一眼应该看到还有多少份作业没批而不是自己记着截止日期再去后台翻。管理员的需求相对简单管理用户账号、维护课程与专业信息、查看整个系统的使用情况。很多课设把管理员做成最高权限角色能看所有数据这也是可以的但权限一定要和教师、学生彻底分开不能出现学生接口能查到所有用户的情况。1.3 功能优先级怎么排我见过很多学生把这个系统越做越大加了一堆聊天、点赞、动态功能最后作业提交的核心链路反而没做完。如果你做课设优先级应该这样排第一优先级登录注册、作业/实验发布、文件提交、列表查看、教师批改打分第二优先级选课关系统计、提交率/平均分报表、实验与作业分类管理第三优先级有余力再做公告通知、个人中心头像、导成绩Excel、图表可视化把第一优先级做完系统已经能用了。第二优先级是答辩时的加分点。第三优先级属于锦上添花别让它拖垮主线。2. 技术选型与源码结构为什么推荐 Spring Boot Vue 前后端分离2.1 这套源码用到的技术栈我整理过的课设源码里用 Java 后端的占了很大比例尤其是 Spring Boot 这套组合。以下是这套源码默认的技术栈层次选型说明后端框架Spring Boot 2.7稳定、配置少、资料多ORMMyBatis-Plus单表CRUD几乎不用写SQL数据库MySQL 8.0utf8mb4支持中文友好认证方式JWT Token无状态登录适合前后端分离前端框架Vue 2 Element UI组件成熟课设资料最全前端请求Axios Vue Router统一拦截token和错误码构建工具Maven / npm后端打包、前端启动2.2 为什么不推荐传统 JSP、HTML 直接塞后端很多学校的课程还在教 JSP但你要是在课设里用 JSP 写页面会发现自己调试起来非常痛苦改一个按钮样式要重启服务接口和数据层全耦合在一起答辩时也说不清楚模块边界。前后端分离最大的优势是把“后端接口”和“前端页面”彻底分开。你可以先用 Postman 测接口接口全通了再去写页面最后联调时问题容易定位。而且以后想换皮肤、换前端框架后端代码一句都不用动。对课程设计来说接口文档还是答辩时可展示的加分内容你在 PPT 里贴上 API 列表比贴一堆页面截图更有说服力。如果你的导师更偏向 Python这套表结构和接口设计也可以很轻松换成 Flask 或 Django 重写因为核心设计完全一样差的无非是框架语法。这一点待会讲到数据库设计时你会更清楚。2.3 源码目录结构速览拿到源码后第一件事不是看业务代码而是先看目录结构。这套源码大概长这样course-assignment-system/ ├── backend/ │ ├── pom.xml │ ├── src/main/java/com/example/course/ │ │ ├── controller/ # 接口层路由入口 │ │ ├── service/ # 业务逻辑层核心判断 │ │ ├── mapper/ # MyBatis-Plus 数据访问 │ │ ├── entity/ # 数据库实体 │ │ ├── common/ # 统一返回结果、异常处理 │ │ ├── config/ # 跨域配置、JWT拦截器 │ │ └── CourseApplication.java │ └── src/main/resources/ │ ├── application.yml # 数据库和文件上传配置 │ └── mapper/ # 复杂SQL的XML文件 └── frontend/ ├── package.json ├── vue.config.js # 开发环境代理配置 └── src/ ├── api/ # 所有请求方法统一封装 ├── router/ # 路由表含角色判断 ├── store/ # 登录用户信息 ├── views/ # 学生端、教师端、管理端页面 └── permission.js # 全局路由守卫为什么要保持这种分层因为你在答辩时可以说“这是我按三层架构设计的”而不是“代码都在一个文件里”。三层架构的好处是controller 只管接收参数和返回结果service 负责业务规则mapper 只做数据库操作。后面要改任何逻辑都知道去哪改。3. 数据库设计拆解六张核心表撑起整套系统3.1 用户与课程基础表数据库设计决定了这个系统最多能做什么。这套源码里最核心的是用户表、课程表、选课关系表。CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_no VARCHAR(32) NOT NULL COMMENT 学号或工号, real_name VARCHAR(32) NOT NULL COMMENT 姓名, password VARCHAR(100) NOT NULL COMMENT 加密存储, role VARCHAR(16) NOT NULL COMMENT admin/teacher/student, department_name VARCHAR(64) COMMENT 院系或专业, create_time DATETIME DEFAULT CURRENT_TIMESTAMP );用户表的关键在于 role 字段而不是单独建一张角色表。很多课设为了炫技搞了五张表的 RBAC 权限模型最后自己都理不清。对于课程作业和实验管理系统来说角色就三种直接在用户表里加一个 role 字段最简单清晰查询也不需要 JOIN。课程表存课程基本信息包括课程名称、课程编号、授课教师ID、学期、学分。选课关系表存课程和学生之间的关系也就是谁选了哪门课。CREATE TABLE course_student ( id BIGINT PRIMARY KEY AUTO_INCREMENT, course_id BIGINT NOT NULL, student_id BIGINT NOT NULL, status TINYINT DEFAULT 1 COMMENT 1在修 0退课 );这张表特别重要因为后面算提交率时分母不是系统里注册了多少学生而是“选修这门课的学生数”。很多人算提交率直接用提交总数除以所有学生总数明显是错的。3.2 作业与实验两种业务分开建模作业和实验看着相似但实际操作不同作业通常是一次性任务可能有截止时间实验一般会安排多个实验项目可能要求提交报告和代码文件。所以这套源码把它们设计成两张表分别叫 assignment 和 experiment。CREATE TABLE assignment ( id BIGINT PRIMARY KEY AUTO_INCREMENT, course_id BIGINT NOT NULL, title VARCHAR(128) NOT NULL, content TEXT COMMENT 作业要求, deadline DATETIME NOT NULL COMMENT 截止时间, status TINYINT DEFAULT 1 COMMENT 1已发布 0已关闭 ); CREATE TABLE experiment ( id BIGINT PRIMARY KEY AUTO_INCREMENT, course_id BIGINT NOT NULL, exp_name VARCHAR(128) NOT NULL, content TEXT COMMENT 实验指导书内容, start_time DATETIME, end_time DATETIME, status TINYINT DEFAULT 1 );有人会问“作业和实验结构差不多为什么不合并成一张 task 表加个 type 字段”这是可以的但分开建表在实际开发中扩展性更好。比如实验需要填“实验地点”作业不需要作业可以设置“是否允许补交”实验可能不需要单独设置。两种表分开后面加字段互不影响查询时语义也更清楚。3.3 提交记录表状态字段设计的重点整套系统里最关键的就是提交记录表。无论是作业提交还是实验报告提交都往这张表里插数据。CREATE TABLE submission ( id BIGINT PRIMARY KEY AUTO_INCREMENT, biz_type VARCHAR(16) NOT NULL COMMENT assignment/experiment, biz_id BIGINT NOT NULL COMMENT 对应作业或实验ID, student_id BIGINT NOT NULL, content TEXT COMMENT 文字说明, file_url VARCHAR(255) COMMENT 附件路径, submit_time DATETIME DEFAULT CURRENT_TIMESTAMP, status TINYINT DEFAULT 1 COMMENT 1已提交 2已批改, score DECIMAL(5,1), comment_text VARCHAR(255) COMMENT 教师评语, review_time DATETIME COMMENT 批改时间 );我之所以说这张表容易出事是因为很多人会忽略一个核心问题允许学生重复提交吗如果允许那同一学生同一作业就可能有两条记录查询时要按 submit_time 倒序取最新如果不允许就需要加唯一约束第二次提交要么被拒绝要么走“先删旧再插新”。这套源码采用的做法是允许重复提交新提交记录会生成新的记录查询列表时按“学生任务ID”分组取最新一条。这样历史提交记录都保留在库里教师能看到学生交了几次答辩时被问到“重复提交怎么处理”也有话说。注意这里的 status 字段不是用来区分“几次提交”的而是用来标识教师是否批改过。批改前后端展示逻辑完全不同。3.4 没有外键的设计是否合理这套源码里所有表都没有物理外键只保留了逻辑关联比如 submission 表里的 student_id 逻辑上对应 sys_user 表。这是我建议课设采用的做法物理外键在插入、删除时会被数据库约束拦住报错信息不直观而且删除一个老师时如果课程表还有数据整个删除直接失败。用逻辑外键业务层自己控制关联关系改起来灵活查询性能也更好。4. 核心业务逻辑实现从提交作业到统计成绩一条线讲清4.1 登录鉴权与三层权限控制权限控制是一道送分题也是最容易被问出漏洞的地方。这套源码的权限分了三层第一层是前端路由守卫。登录时后端返回的角色字段被存在 Vuex 里路由表配置了 meta.roles守卫里判断当前用户角色是否在允许列表里不在就跳转回首页。这层的作用是让普通学生看不到管理端入口提升体验。第二层是后端 JWT 拦截器。用户登录成功后后端生成一个包含用户ID和角色的 token返回给前端。前端每次请求都把 token 放在请求头的 Authorization 字段里。后端用一个拦截器解析 token解析失败直接返回 401请求根本进不到 controller。这层保证了接口不是“前端隐藏了就安全”哪怕有人绕过前端直接调用接口没有 token 也一样拿不到数据。第三层是接口内部的用户归属判断。比如教师查询“我的课程”时不是传课程ID从表里查而是从 token 里取出 teacher_id再按这个ID查询课程。学生提交作业时后端从 token 里取 student_id不信任前端传来的任何“当前用户ID”。这是很多人会踩的坑前端传什么就信什么结果换个人提交就能写进别人的记录答辩时被老师一问直接懵。4.2 作业提交的后端双重校验前端页面上作业过了截止日期提交按钮会灰掉但这只是“用户体验”。真正的校验必须放在后端因为任何人都可以绕过前端直接调用接口如果后端不校验截止时间凌晨三点照样能提交系统就失去了意义。后端处理提交的核心逻辑大概是这样的public Result submit(SubmitRequest req) { // 从token获取当前学生ID Long studentId JwtUtil.getUserId(); // 校验任务是否存在 Assignment assignment assignmentMapper.selectById(req.getAssignmentId()); if (assignment null) { return Result.error(作业不存在); } // 校验时间只能用后端时间为准 if (LocalDateTime.now().isAfter(assignment.getDeadline())) { return Result.error(已过截止时间无法提交); } // 校验学生是否选了这门课 CourseStudent cs courseStudentMapper.selectOne( new QueryWrapperCourseStudent() .eq(course_id, assignment.getCourseId()) .eq(student_id, studentId)); if (cs null) { return Result.error(未选修该课程); } // 插入提交记录 submissionMapper.insert(buildEntity(studentId, req)); return Result.success(); }这一段逻辑里有三层校验任务存在性、时间合法性、选课关系。很多课设源码只做第一层后面两层全没有。但你想一个没选课的学生也能交作业那成绩表不就乱套了吗这一块是答辩时最值得讲的点。关于文件上传源码用的是本地磁盘存储方案在 application.yml 里配一个 upload.path 目录上传的文件重命名为 UUID 加后缀存储到磁盘数据库只存相对路径。这样设计是为了避免中文文件名乱码和同名覆盖。千万别用原文件名直接存两个学生都交 main.py 会互相覆盖血泪教训。我建议你在文件上传接口里再做一个类型白名单校验只允许 doc、docx、pdf、zip、jpg、png 这些常见格式。不是限制用户而是防止有些人传个 html 文件存进服务器留下隐患。扩展名要从文件原始名称里取不能信任前端传的 content-type因为那个可以随便伪造。4.3 教师批改与成绩统计教师端批改列表的查询很典型首先从 token 拿 teacher_id再查出这个教师的所有课程 ID 集合然后用课程 ID 集合去查对应的作业集合最后查这些作业下的所有提交记录。这个链路涉及多次查询如果只在 mapper 里写一个超级复杂的联表 SQL反而容易出错。我的建议是拆开查查询次数多一些没关系课程设计的数据量根本不用担心性能。代码可读性比微秒级的查询更快更重要。统计逻辑上最容易被追问的是提交率和平均分怎么算。答案要记牢提交率 这门课程下“某作业/实验的已提交人数” ÷ “选课学生总数”平均分 所有已批改提交记录的成绩平均值未批改的不参与计算-- 某作业的提交人数 SELECT COUNT(DISTINCT student_id) FROM submission WHERE biz_typeassignment AND biz_id #{assignmentId}; -- 某课程的选课人数 SELECT COUNT(*) FROM course_student WHERE course_id #{courseId} AND status 1; -- 某作业的平均分 SELECT AVG(score) FROM submission WHERE biz_typeassignment AND biz_id #{assignmentId} AND status 2 AND score IS NOT NULL;这三条 SQL 写对统计功能就稳了。注意最后一条 SQL 加了 score IS NOT NULL而不是只靠 status2因为有可能出现教师把状态改成已批改但没填分数的情况默认分数为空AVG 会忽略空值但保险起见最好过滤。4.4 三个角色的页面组织方式前端页面按角色分目录组织这个方式建议沿用views/ ├── student/ │ ├── AssignmentList.vue # 待交作业列表 │ ├── ExperimentList.vue # 实验列表 │ └── MyScores.vue # 我的成绩 ├── teacher/ │ ├── CourseMine.vue # 我的课程 │ ├── AssignmentPublish.vue # 发布作业 │ ├── SubmissionReview.vue # 批改提交 │ └── StatisticsDashboard.vue # 课程统计 └── admin/ ├── UserManage.vue # 用户管理 ├── CourseManage.vue # 课程管理 └── GlobalStats.vue # 全局数据面板学生端和教师端最核心的交互就是“待办列表”。学生登录后看到的待办列表本质上是查自己选修的所有课程 → 再查这些课程下所有已发布的作业/实验 → 再过滤掉自己已经提交过的。这三步是嵌套查询初学者容易在联表时搞混前端直接调一个专门的接口比较省事比如/api/student/todo-list后端一次性封装好返回结构。5. 快速跑通这套源码环境配置、启动与高频报错处理5.1 本地环境准备要把这套源码在本地跑起来你需要准备这些环境组件版本建议说明JDK8 或 11Spring Boot 2.7 都支持Maven3.6后端依赖管理Node.js14 或 16Vue 2 项目不要用 Node 20 以上容易编译报错MySQL5.7 或 8.08.0 需要注意连接驱动IDEIDEA / VSCode后端用 IDEA 更顺手数据库先建好注意字符集CREATE DATABASE course_assignment DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;然后把源码里的 SQL 文件导入或者直接用 MyBatis-Plus 的自动建表功能看 README 怎么写。如果源码里有 init.sql优先用 init.sql因为除了表结构它还包含初始化数据和默认账号。5.2 后端前端分别启动后端启动比较简单。用 IDEA 打开 backend 目录等 Maven 依赖下载完修改 application.yml 里的数据库账号密码和上传目录spring: datasource: url: jdbc:mysql://localhost:3306/course_assignment?serverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8 username: root password: 你自己的密码 upload: path: D:/course-upload直接运行 CourseApplication 类。看到 Tomcat started on port(s): 8080 就是成功。前端启动要留意 npm 源的问题国内网络环境用官方源下载依赖容易卡住建议提前换镜像源npm config set registry https://registry.npmmirror.com npm install npm run serve启动成功后访问 http://localhost:8081如果 vue.config.js 里配了代理前端请求会转发到 8080 端口。跨域问题就直接绕过了不用再单独配。5.3 高频报错与解决办法我整理了跑这套源码时最容易遇到的一批报错都是课设群里高频出现的报错现象原因对应方案Access denied for user数据库账号密码错误检查 application.yml别用 Navicat 里能连就当配置对Server returns invalid timezoneMySQL 8.0 时区问题URL 上加 serverTimezoneAsia/ShanghaiPort 8080 was already in use端口被占用改 application.yml 的 server.portCannot find module node-sassNode 版本和依赖版本不匹配Node 换回 14或删除 node_modules 重新 npm install前端请求全部 404接口路径错误或代理没生效检查 vue.config.js 的 proxy 和 axios baseURL文件上传报 500 或失败上传目录不存在或没有写权限手动创建上传目录Windows 别用 C 盘根目录JWT token 过期本地调试时间过长重新登录或调大 token 过期时间配置5.4 三分钟验证系统闭环跑起来之后别急着看页面好不好看先验证核心链路通不通。用学生账号登录 → 找一门课 → 上传一份测试文件 → 用教师账号登录 → 找到对应提交 → 打分写评语 → 再用学生账号刷新 → 看到成绩和评语。如果这条链路没问题系统的主干功能已经可用了。这条验证路径的价值在于它走遍了用户、课程、选课、作业、提交、批改六张表。答辩时老师问“你怎么测的”你就可以说我用了一个真实的业务闭环验证而不是只看页面能不能打开。这个回答比“我打开看没问题”高级得多。6. 拿到源码后怎么改成自己的课设改造点与答辩思路6.1 先把“别人的痕迹”清干净白嫖的源码一定要先改表面信息否则一开 PPT 就露馅。重点改这几处前端登录页的标题、导航栏的 logo、浏览器标签页的 favicon、页脚版权信息、后端接口文档里的作者注释。前端登录页标题一般在登录组件里写死比如 “课程作业管理系统”改成自己学校名称和自己的项目名字。favicon 很多人会忽略浏览器标签页上还挂着别人系统的图标答辩评委眼皮底下很尴尬。数据库里的初始管理员账号、联系我们电话这些也别漏了。清理这一步花不了十分钟但能让你后面说“这是我自己完善过的项目”时底气更足。6.2 低成本加需求优先加这三个方向想从“抄代码”变成“做了项目”关键在于加一个能说清楚“我自己写”的功能。我建议优先加以下三类之一第一导出成绩 Excel。用 EasyExcel 或 POI 写一个导出接口教师点击按钮就能把当前课程所有学生的成绩导出成 Excel。这个功能实用、工作量适中、答辩时可以直接现场演示导出文件的表头、格式都是你控制讲起来有内容。第二用 ECharts 做成绩分布图。前端引入 ECharts统计某个作业各分数段人数画一个饼图或柱状图。这能体现你对前端组件的掌握而且视觉效果好放在答辩 PPT 里很占优势。第三公告通知模块。管理员或教师发布公告学生登录后首页展示。实现只需要一张 notice 表加两个接口但系统完整度会明显提升。我自己更推荐导出 Excel因为教师用它最多演示效果直观而且后端代码量不大非常适合作为“我的核心贡献”来讲。6.3 答辩时老师最爱问的三个问题和应对思路根据往年经验答辩老师围着这类系统最常问这三个问题回答思路提前准备好第一个“权限是怎么控制的”不要说“我在页面里判断角色”。标准回答是前端用路由守卫控制页面可见性后端用 JWT 拦截器统一校验接口内部再按 token 里的用户身份做数据归属过滤三层配合。这样答既体现了分层思想又证明你不是只在写页面。第二个“学生重复提交怎么处理”如果源码允许重复提交答系统允许学生在截止前重复提交并且每次提交都会生成新记录历史记录保留教师可查看提交次数。这就把一个设计决策讲成了一个特点。第三个“统计提交率和平均分的逻辑是什么”答提交率是以选课名单为分母已提交人数为分子平均分只统计已批改的提交记录。一句话就能讲清楚但前提是你真把 SQL 写对了。结尾分享一个我自己的体会整理这套源码时最大的收获不是某段代码写得多巧妙而是发现了这类系统的通用骨架——“发布 → 提交 → 批改 → 统计”这条链路换成考勤系统、问卷系统、报修系统思路都是一模一样的。白嫖的源码一定要动手改哪怕只是加一个导出 Excel 的小功能答辩时被问到“你做了什么”时至少能说出一件自己亲手写出来的东西。最后别忘了把数据库密码、上传路径、前端标题这些细节处理干净祝课设顺利。
返回列表