ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

JavaWeb在线教务管理系统:环境部署与代码改造实战指南

2026/10/4 15:42:27 拓冰建站 浏览量
JavaWeb在线教务管理系统:环境部署与代码改造实战指南 简介基于JavaWeb的在线教务管理系统源代码是一套面向计算机相关专业毕业设计及JavaWeb初学者的完整工程实例适合需要以SSM框架搭建管理系统的读者参考。项目采用Spring、SpringMVC、MyBatis集成框架开发覆盖需求分析、模块设计、编码实现等软件工程环节重点实现在线考试相关功能包括试题库管理、组卷策略、自动评分与成绩查询等。资源压缩包为zip格式共424个文件大小14.92MB包含55个Java源文件、152个class编译产物、56个JSP视图页面、93个XML配置及30个JAR依赖库另有数据库SQL脚本、前端样式脚本和字体图标目录结构清晰便于按模块学习。已有2081人学习下载。通过研究源码可掌握SSM框架整合、MyBatis持久层映射及前后端交互也能借鉴在线教务业务模块划分对毕业设计和实际项目开发有直接帮助。1. JavaWeb 在线教务管理系统为什么说它简单又为什么总有人跑不起来在线教务管理系统是 JavaWeb 课设和毕设里出现频率最高的题目之一拆开看就是「管理员管课程和学生、学生选课、教师录成绩」三个闭环。它看起来只有增删改查但真正让人卡住的往往不是业务代码而是 Tomcat 版本、MySQL 驱动、JAR 有没有打进 Artifact 这些环境细节。我见过不少人把源码通读了一遍最后在 IDEA 的部署配置上耗掉一整天。下面按实操顺序来先告诉你怎么读项目结构再给一套能直接导入的 MySQL 建表脚本沿着登录、选课、成绩录入三条链路把代码串起来最后用一份避坑清单把运行期的高频翻车点一次说清。适合已经选了在线教务管理作为毕设、需要快速跑通再二次开发的读者。2. 读结构先于读代码在线教务系统的分层、包结构与 JAR 依赖2.1 为什么毕设级教务系统总选 ServletJSPJDBC技术栈对比与选型理由你拿到的这套在线教务管理系统源码大概率是 Servlet JSP JDBC MySQL Tomcat 的组合少数给 SSM极少数给 Spring Boot。对毕业设计来说Servlet/JSP 这套组合不是落后反而是最安全的选项——答辩老师最常问的就是「一个请求从浏览器到数据库之间经历了什么」你用 doGet、doPost、Service、DAO 能一步一步讲清楚。用 Spring Boot 的 RequestMapping 反而容易把自己绕进去追问到拦截器和事务边界时说不清。技术栈上手难度答辩好讲程度部署复杂度适合场景ServletJSPJDBC低高链路透明低Tomcat 直接跑课设、毕设SSM (SpringSpringMVCMyBatis)中中要讲 IoC 和 AOP中配置文件多有框架要求的毕设Spring Boot低低黑匣子居多低内嵌 Tomcat已有完整 Java 基础的练手项目教务系统的业务边界太标准了学生选课、教师录成绩、管理员维护基础数据权限角色清晰天然适合用 Servlet 做控制层、JSP 做展示层、DAO 做数据层。相比论坛、购物车这类交互复杂的功能它的表结构更容易设计成「一张业务表对应一个 DAO」的模式对第一次完成完整项目的学生来说这种一一对应关系非常好写。这也是很多「javaweb 项目完整案例 mysql」资料都用它做演示项目的原因。2.2 IDEA 里第一眼看包结构从登录页到数据库工具类的分层打开工程先别急着点启动按钮。先在 Project 面板里确认目录结构保证心里有张地图。常见的包结构是这种edu-system/ ├── src/main/java/com/edu/ │ ├── servlet/ # LoginServlet、CourseServlet、ScoreServlet │ ├── service/ # 业务方法选课/录入成绩的事务边界放在这里 │ ├── dao/ # 对应每张表的数据访问对象 │ ├── entity/ # Student、Teacher、Course、Elective │ ├── util/ # DBUtil 数据库连接、StringUtil 判空 │ └── filter/ # 登录拦截、字符集编码 ├── web/ │ ├── jsp/ # student.jsp、teacher.jsp、admin.jsp │ ├── static/ # css、js、images │ └── WEB-INF/ │ ├── web.xml # 注册 Servlet/Filter │ └── lib/ # MySQL 驱动、JSTL JAR 包 └── sql/ └── init.sql # 建库建表脚本看代码的推荐顺序是先找 util/DBUtil——所有 DAO 都会从这里拿 Connection再看 filter 里的登录拦截器搞清楚哪些页面不能裸访问最后按「登录 → 选课 → 成绩」的业务顺序读 servlet。不要从 index.jsp 开始读页面页面上的 JS 和标签容易把你带到细节里出不来。判断一个项目代码质量先看 util 里有没有统一的连接工具类再看 DAO 里有没有到处都是 new Connection()——后者就是典型「能跑但答辩没法聊」的写法。参数说明Servlet 3.0 以后可以用 WebServlet 注解替代 web.xml 里servlet和servlet-mapping两段配置。老项目里两种方式都存在只要 URL 映射能对得上就行读的时候先在 web.xml 或注解里确认你要访问的路径和实际 class 是否匹配。这套「util dao servlet jsp」的分层跟黑马 JavaWeb 笔记里的案例高度一致按笔记练过的人读起来会非常快。2.3 JAR 依赖到底放哪WEB-INF/lib 与 IDEA 的 Artifact 概念毕设翻车最多的地方不是代码逻辑而是「IDEA 里编译通过Tomcat 一启动就 ClassNotFoundException」。原因是开发类路径和部署类路径在传统 Web 项目里不是一回事。传统 JavaWeb 项目必须在web/WEB-INF/lib/下放运行期 JARIDEA 打包成 Artifact 后这个目录会同步进部署包里。如果你用 IDEA 的 Project Structure Artifacts 查看 Output Layout看不到 WEB-INF/lib 下的驱动那启动后必然报找不到类。这套源码里常见需要的 JAR 就三个mysql-connector-java连 MySQL 用、jstlJSP 页面里 c:forEach 用、servlet-apiTomcat 自带lib 里通常不放。检查方法Project Structure → Artifacts → 选中你的 web 产物 → Output Layout → 展开 WEB-INF/lib → 确认 mysql-connector 和 jstl 都在里面没有的话在 Output Layout 面板里右键选择 Add Copy of → 选 Module 的库依赖让 IDEA 把依赖打进去。如果你拿到的源码是 Maven 结构pom.xml 里已经写了依赖但 IDEA 里只有引入后才会同步到 Artifact只看 pom 不等同于部署包里有 JAR。这个区别理解不透后面每一个报错都会让你怀疑人生。顺带补一个 web.xml 里的欢迎页配置很多源码会让你访问根路径时自动跳到登录页web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd version3.1 welcome-file-list welcome-filelogin.jsp/welcome-file /welcome-file-list /web-app这段配置的作用是让http://localhost:8080/edu这个根地址直接打开登录页。如果你发现访问根路径时出现目录列表或者 404优先检查这里。像这样的环境级细节比业务代码更早决定项目能不能跑起来。3. 数据库是主心骨能直接导入 MySQL 的建表脚本与 6 张核心表边界3.1 用户模型设计学生、教师、管理员共用一张表还是拆开先想清楚数据模型再谈代码。教务系统里学生有学号、班级教师有职称、工号管理员只有账号和密码属性差异很明显。但毕业设计不建议拆三张用户表我一般建议合并成一张 sys_user加 role 字段区分三类角色。登录时一次查询就能完成密码校验逻辑只写一份权限判断用一个字符串比较就能解决学生的学号、教师的工号这些额外属性放到 student、teacher 子表里用 user_id 做关联。这样既保证登录链路简单又能在你需要扩充学生信息时不动主表。如果拆成三张独立的用户表登录查询就得按「先查学生表再查教师表再查管理员」的顺序逐张尝试。课设期间代码少感觉不到差别但答辩时讲登录逻辑会明显绕。生产系统为了数据隔离确实会拆可在线教务作为毕设复杂度控制比模型纯净度更重要。3.2 能直接导入的建表脚本院系、用户、学生、教师、课程、选课常见做法是在源码里放一个 init.sql。导入时按顺序执行department → sys_user → student → teacher → course → elective。这个顺序就是外键依赖顺序父表必须建在子表前面。提示先执行 CREATE DATABASE 和 USE 语句再执行建表语句。表名大小写在 Linux MySQL 上区分严格脚本里用 course代码里拼接 SQL 时也要保持一致。-- 建库推荐显式指定字符集避免后续到处是 ?? CREATE DATABASE IF NOT EXISTS edu_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE edu_db; -- 院系表先建父表后面 student/teacher 要引用它 CREATE TABLE department ( id INT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(50) NOT NULL UNIQUE ); -- 用户表三种角色合并成一张表 CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL, role ENUM(admin,teacher,student) NOT NULL DEFAULT student, UNIQUE KEY uk_username (username) ); -- 学生表主键直接引用 sys_user.id不再另造自增主键 CREATE TABLE student ( user_id INT PRIMARY KEY, student_no VARCHAR(20) NOT NULL UNIQUE, real_name VARCHAR(30) NOT NULL, gender CHAR(2), department_id INT, CONSTRAINT fk_student_user FOREIGN KEY (user_id) REFERENCES sys_user (id) ); -- 教师表 CREATE TABLE teacher ( user_id INT PRIMARY KEY, teacher_no VARCHAR(20) NOT NULL UNIQUE, real_name VARCHAR(30) NOT NULL, title VARCHAR(20), department_id INT, CONSTRAINT fk_teacher_user FOREIGN KEY (user_id) REFERENCES sys_user (id) ); -- 课程表 CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(50) NOT NULL, teacher_id INT, credit DECIMAL(2,1) DEFAULT 2.0, capacity INT DEFAULT 60, remain INT DEFAULT 60, CONSTRAINT fk_course_teacher FOREIGN KEY (teacher_id) REFERENCES teacher (user_id) ); -- 选课表同时存分数一个学生同一门课只能选一次 CREATE TABLE elective ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, score DECIMAL(4,1) DEFAULT NULL, UNIQUE KEY uk_student_course (student_id, course_id), CONSTRAINT fk_ele_student FOREIGN KEY (student_id) REFERENCES student (user_id), CONSTRAINT fk_ele_course FOREIGN KEY (course_id) REFERENCES course (id) );逻辑说明student 和 teacher 的主键直接用 user_id而不是自己再生成一个 id这样前端拿到登录用户的 ID 后可以直接用 JOIN 查出个人信息不用再维护「sys_user.id 与 student.id」两套映射。elective 表里 score 字段直接存成绩选课和成绩的关联就收在一起了比单独建 score 表少一次 join。course 表保留 capacity 和 remain 两个字段remain 在选课时做余量校验。参数说明ENUM 类型在 MySQL 里可读性好但扩展性弱如果想加 super_admin 角色得改表结构生产上通常用 TINYINT课设里用 ENUM 更直观。DECIMAL(4,1) 表示成绩最高 999.9实际录入时在代码里限定 0 到 100。注意这两张有外键的表在删除时顺序必须反过来否则第 5 章会让你见识到外键约束的威力。3.3 容易被忽略的 3 个建表细节utf8mb4、时间字段与唯一索引第一是字符集。连接 URL 里的 characterEncodingutf8 只管 TCP 传输表结构的默认字符集决定数据落盘的编码。CREATE DATABASE 里显式写 DEFAULT CHARACTER SET utf8mb4是最省心的一条。你从网上导出的 SQL 里如果没有这一句在 Windows 上中文常常没问题一换到 Linux 就变成问号。第二是时间字段。选课时间、公告发布事件在后续统计里几乎必用否则页面上要展示「近三天新增课程」就只能靠字符串排序。多数毕设源码不会在每张表都放 create_time你可以自己补ALTER TABLE course ADD COLUMN create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;第三是唯一索引。elective 表里的UNIQUE KEY uk_student_course (student_id, course_id)是防止重复选课的最终兜底。业务代码里可以先 SELECT 判断再插入但两个请求同时通过检查时就会插进重复数据唯一索引让数据库在约束层面直接拦截。同样的思路也用在 sys_user 表的 username 唯一约束上注册新账号时靠它兜底。初始化数据时至少得有一条管理员账号才能登录系统。常见做法是直接在脚本里插一条密码用明文可以跑通答辩前再改成加盐哈希INSERT INTO sys_user (username, password, role) VALUES (admin, admin123, admin);4. 顺着一条业务线读代码登录、选课、成绩录入的最小链路4.1 登录链路LoginServlet 的 doPost 与 Session 生命周期从登录页开始读代码顺序最顺。常见的登录 Servlet 核心逻辑长这样protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 先设置编码再取参数顺序不能反 request.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8); String username request.getParameter(username); String password request.getParameter(password); User user userDao.findByUsernameAndPassword(username, password); if (user ! null) { HttpSession session request.getSession(); session.setAttribute(loginUser, user); session.setMaxInactiveInterval(60 * 30); // 30 分钟无操作失效 response.sendRedirect(index.jsp); } else { request.setAttribute(error, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } }逻辑说明userDao 返回 null 表示用户名密码不匹配处理方式是回到 login.jsp 并携带 error 提示而不是直接重定向到首页。登录成功后把 user 对象放进 Session后续所有页面都靠session.getAttribute(loginUser)判断当前是谁。参数说明request.getParameter(username)取到的是表单里 nameusername 的输入框JSP 里的 input 必须同名。session.setMaxInactiveInterval 设的是无操作失效时间不设置的话以 Tomcat 默认 web.xml 为准一般是 30 分钟。如果用 forward 而不是 sendRedirect地址栏不会变刷新页面会重复提交表单这是课设里常见的体验 bug。4.2 权限拦截Filter 里判断 role后台页面不会被裸访问登录只是起点真正决定系统能不能叫「管理」系统的是权限拦截。常见做法是写一个 AuthFilterpublic void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; HttpSession session request.getSession(false); if (session null || session.getAttribute(loginUser) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); }逻辑说明Filter 在 Servlet 之前执行适合做登录校验。这里用request.getSession(false)没有 Session 时返回 null而不是新创建一个 Session避免静态资源也被迫建会话。注意 sendRedirect 里用了request.getContextPath()返回的是/你的应用名这样项目改了部署名也不会跳错。参数说明web.xml 里给 Filter 映射/*时会把 CSS、JS 也拦下来所以要么在 Filter 中放行 /static/ 前缀要么只映射 /jsp/ 和 /servlet/ 路径。很多源码会把角色判断也放在 Filter 里管理员只能访问 admin 开头的 JSP教师不能访问学生功能。读代码时看到loginUser.role判断就是在做角色级权限控制。4.3 选课逻辑事务、FOR UPDATE 与「一人一课一次」的约束选课是教务系统里最值得细读的代码因为它的数据一致性比增删改查高一个层次。一个标准的选课 DAO 里会有这样一段public boolean addElective(Elective e) { Connection conn null; PreparedStatement ps null; boolean success false; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 关闭自动提交 // 1. 查询课程剩余容量FOR UPDATE 锁住这行记录 ps conn.prepareStatement( SELECT remain FROM course WHERE id ? FOR UPDATE); ps.setInt(1, e.getCourseId()); ResultSet rs ps.executeQuery(); int remain 0; if (rs.next()) { remain rs.getInt(remain); } if (remain 0) { return false; } // 2. 扣减余量 ps conn.prepareStatement( UPDATE course SET remain remain - 1 WHERE id ?); ps.setInt(1, e.getCourseId()); ps.executeUpdate(); // 3. 插入选课记录 ps conn.prepareStatement( INSERT INTO elective (student_id, course_id) VALUES (?, ?)); ps.setInt(1, e.getStudentId()); ps.setInt(2, e.getCourseId()); ps.executeUpdate(); conn.commit(); success true; } catch (Exception ex) { if (conn ! null) conn.rollback(); ex.printStackTrace(); } finally { DBUtil.close(conn, ps, null); } return success; }逻辑说明这段代码的关键不是每一行都懂而是看懂三个动作必须在一个事务里。如果不关闭自动提交扣减余量后插入失败会出现「课程人数少了但学生没选上」的数据不一致。手动事务的顺序是 setAutoCommit(false) → 多步 SQL → commit任何一步抛异常都 rollback。参数说明SELECT ... FOR UPDATE是对选中的行加悲观锁避免两个学生同时选同一门课时都读到 remain60。答辩时你只要能说清这一句的语义事务这一关就稳了。注意 FOR UPDATE 必须配合事务使用如果事务一直不提交锁会长时间占用页面直接卡死。看到这个关键词先检查 commit 和 rollback 是否成对出现。4.4 成绩录入教师角色校验与批量参数读取成绩录入模块一般由教师角色触发核心是批量更新。Servlet 里常见这段// 教师只能给自己教的课程录成绩这里先做角色判断 User loginUser (User) request.getSession().getAttribute(loginUser); if (!teacher.equals(loginUser.getRole())) { response.sendError(403); return; } // 页面里多个隐藏域每个学生一行 String[] stuIds request.getParameterValues(stuId); String[] scores request.getParameterValues(score); int courseId Integer.parseInt(request.getParameter(courseId)); for (int i 0; i stuIds.length; i) { int sid Integer.parseInt(stuIds[i]); double sc Double.parseDouble(scores[i]); scoreDao.updateScore(courseId, sid, sc); }逻辑说明request.getParameterValues(score)取到的是页面上所有 namescore 的输入框组成的数组下标与 stuId 数组一一对应。真正严格的系统还应该再查一次 course.teacher_id 和当前登录教师的 user_id 是否匹配防止一名教师修改其他教师的课程成绩课设代码里大多只做了角色判断。参数说明表单里多个输入框用同一 name后端就能批量接收数组长度不一致说明页面渲染有问题读代码时注意这个隐含前提。如果成绩录入要求一个事务完成这里的手动提交要比选课更严格——所有学生的分数要么同时成功要么全部失败你可以在源码的 scoreDao.updateScore 里确认它是否也传了同一个 Connection。5. 排错与避坑IDEA 运行 JavaWeb 项目时的 5 个高频翻车点5.1 启动后访问根路径 404Artifact 没部署到 Tomcat现象Tomcat 正常启动但http://localhost:8080/项目名/返回 404。原因IDEA 里 Tomcat 的 Deployment 配置中没有把这个模块的 Artifact 加进去或者 On update action 选的是不更新。访问根路径时 Tomcat 找的是 webapps 下的应用目录Artifact 没有部署就等于这个目录不存在。解决打开 Run/Debug Configurations → Tomcat Server → Deployment 标签页 → 点 号 → 选 Artifact → 选择xxx:war exploded。这里 war exploded 是展开目录模式改 JSP 能即时看到效果war 模式更新慢。保存后重启 Tomcat如果还 404清理一下 target 目录和 Tomcat 的缓存再启动。5.2 数据库驱动 ClassNotFoundException 与连接超时现象启动后首次访问数据库相关页面报java.lang.ClassNotFoundException: com.mysql.jdbc.Driver或者直接Communications link failure。原因前一个是 MySQL 驱动 JAR 没打到 WEB-INF/lib后一个是 MySQL 版本与驱动类名、连接参数不匹配。MySQL 8.x 的驱动类名必须改成 com.mysql.cj.jdbc.Driver并且 URL 里要加 serverTimezoneMySQL 5.x 才用 com.mysql.jdbc.Driver。解决先确认 lib 下的驱动版本。MySQL 5.x 用jdbc:mysql://localhost:3306/edu_db?useUnicodetruecharacterEncodingutf8MySQL 8.x 用jdbc:mysql://localhost:3306/edu_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai如果是 Maven 项目检查 pom 里 mysql 依赖的 version改动后重新把依赖加入 Artifact。这条是部署问题不是代码问题也是毕设项目里最耗耐心的血泪经验。5.3 中文乱码POST、响应头、JSP 三处要统一 UTF-8现象登录成功后页面上的中文全是问号或者输入数据库的中文取出来变成乱码。原因数据库表用了 latin1 默认字符集或者请求/响应编码不一致。中文从浏览器到 Java 再到 MySQL 要经过三层编码任何一层用了默认 ISO-8859-1 都会出现替换符。解决数据库建表用 utf8mb4每个 Servlet 的 doPost 第一行加request.setCharacterEncoding(UTF-8)响应用response.setContentType(text/html;charsetUTF-8)JSP 页面第一行写% page contentTypetext/html;charsetUTF-8 languagejava %。如果整套源码每个 Servlet 都缺编码设置最省事的方案是写一个 EncodingFilter在 chain.doFilter 之前统一设置请求和响应编码避免逐个文件改。提示MySQL 连接 URL 里也要有 characterEncodingutf8否则 Java 与 MySQL 通信时仍可能被转成 latin1。5.4 删除课程时外键约束报错现象管理员删除课程页面报Cannot delete or update a parent row: a foreign key constraint fails。原因elective 表里还有这条课程的选课记录外键约束不允许先删父表记录。这恰恰说明当初保留外键设计是对的——数据库拦住了不该发生的删除。解决先删选课记录再删课程。写在 DAO 里是两个 DELETE用事务包住也可以在建表时给 elective 的外键加 ON DELETE CASCADE让数据库自动清理子表。毕设推荐用前者因为可以在页面提示「还有 12 名学生选了该课程」功能上更好看。如果你发现源码里的 elective 建表语句没写外键约束删除顺序就只能靠代码硬保证这种隐藏逻辑最容易踩坑。5.5 改了 JSP 不生效IDEA 更新动作与 Tomcat 缓存现象改了 login.jsp 上的标题和样式刷新浏览器还是旧页面。原因IDEA 没有开启自动更新动作。我们在 5.1 选了 war exploded 模式后还需要在 Tomcat 配置里把 On frame deactivation 设为 Update classes and resourcesIDEA 才会把改过的文件拷进部署目录。另一个常见原因是浏览器缓存JSP 里引用的 CSS/JS 被本地缓存。解决打开 Run/Debug Configurations → Tomcat Server → Server → On frame deactivation 下拉选 Update classes and resources。改完 JSP 后点击 Build → Rebuild 再刷新。这种缓存不生效的问题很容易被当成玄学实际就是 IDEA 没触发编译先看 Tomcat 日志里访问的路径对不对再排查浏览器缓存顺序反了会白忙半天。6. 从能运行到能过答辩验收清单与三个值得动手的加分项6.1 功能验收清单9 条路径必须全部走通把系统交出去之前我习惯按角色把主流程完整走一遍。不少项目代码看着完整真到答辩现场才发现某个按钮点下去是 404。建议照着这个清单过一遍编号验收路径预期结果1管理员登录跳转到管理首页2管理员新增课程、教师、公告列表出现新数据3学生登录只能看到学生功能4学生选课余量减一列表出现记录5学生对同一课程重复选课被唯一索引或业务校验拦截6学生退课余量加一记录删除7教师登录只能看到所教课程8教师录入并修改成绩成绩可正常保存9存储过程和页面中文无乱码6.2 三个加分项密码加盐、分页、统计 SQL时间充裕的话优先做这三件事工作量不大但答辩效果明显。第一是密码改成 MD5 加盐改用户校验那一段就能讲出「我没有明文存储密码」第二是课程列表分页用 LIMIT 加当前页参数即可第三是及格率统计一条 SQL 就能展示你懂聚合查询SELECT c.course_name, COUNT(e.id) AS total, SUM(CASE WHEN e.score 60 THEN 1 ELSE 0 END) AS pass_count FROM course c LEFT JOIN elective e ON e.course_id c.id GROUP BY c.id;我自己带过的项目里最怕的不是业务 bug而是学生把环境问题当成代码问题最后时间全耗在启动上。建议你先把第 5 章的排查清单保存在手边跑通之后再做上面的加分项每一步都能说出改动理由这个项目才算真正变成你自己的。希望这次能顺利帮你把在线教务管理系统跑起来跑起来之后再动手改代码你会发现它远不止一个毕设那么简单。本文还有配套的精品资源点击获取