软件项目管理期末作业:学生考勤系统从数据库到答辩完整指南
发布时间:2026/10/1 5:36:44来源:尧图网络
简介面向软件项目管理课程设计的完整交付包适用于计算机相关专业期末作业、课程设计场景提供基于Java实现的学生考勤管理系统源码及配套数据库、文档。系统划分学生与教师两种角色内置测试账号便于验收演示降低演示门槛。压缩包内含319个文件以Java源码、HTML页面、CSS样式、JavaScript脚本为主辅以SQL数据库脚本、Maven配置及项目说明文档构成可直接导入IDEA运行的完整工程包体大小36.23MB文档中详细记录了项目运行步骤、测试账号与功能操作说明读者可快速上手。目前已有969人学习适合需要快速完成课设或参考完整项目结构的读者。通过该项目可掌握Spring Boot等项目架构、考勤业务流程设计、前后端交互以及文档撰写规范同时获得可直接运行和二次开发的系统基础。1. 先把这个 zip 当项目盘一遍学生考勤管理系统到底要交什么如果你手头正好躺着一个《软件项目管理期末作业课程设计—学生考勤管理系统源码数据库文档.zip》别急着解压。这类作业包在高校里非常典型课程名叫“软件项目管理”要求却是把需求分析、进度计划、数据库设计、编码实现和课程设计报告一次性交齐。学生考勤管理系统正好卡在“业务简单但流程完整”的位置要管学生、班级、课程、考勤记录还要有老师端登记、学生端查询、统计报表。对准备照着做的人来说这篇笔记是从验收标准倒推的做法——不吹嘘功能只围绕“能跑通、能演示、能答辩”三件事把项目管理文档、数据库脚本和源码串成一条完整链路。2. 用软件项目管理的方法拆需求考勤系统的范围、里程碑和交付物很多人拿到“软件项目管理期末作业课程设计”这样的命题习惯先找源码把跑通当成唯一目标。但课程名里的“软件项目管理”五个字才是评分重心。老师会从需求覆盖度、WBS 分解、进度安排、风险记录去给项目管理部分打分代码只是佐证。我一般会建议用两周做完系统再用两周做文档而不是反过来。因为程序上的小问题可以熬夜修文档对不上需求、没有进度规划答辩时一句话就会被问住。2.1 先定范围一个能过检的学生考勤系统该有哪些功能范围管理的核心是“砍需求”。很多同学在期末作业里加刷脸签到、微信小程序、GPS 定位最后文档没写完代码到处报错。课程设计要的是闭环不是复杂度。一个能过检的学生考勤管理系统把下面这张表里优先级为“高”的功能做好就够了。模块典型功能使用角色优先级验收标准登录认证用户名密码登录、退出、角色跳转全体高错误密码不能进入系统三类角色进入不同主页班级管理班级增删改查管理员高新增班级后可在学生管理和考勤登记中看到学生管理学生信息维护、按班级筛选管理员/教师高新增学生后能在考勤登记下拉框里选中课程管理课程维护、指定授课教师管理员中课程与教师关联正确考勤登记按课程、日期、节次登记每个学生状态教师/管理员高同一学生同一课程同一节次不能重复登记考勤查询按学生、课程、日期范围查询全体高查询结果与登记数据一致统计报表出勤率、缺勤排名教师/管理员中数据准确能截图放进文档可选扩展请假申请、导出 Excel、二维码签到全体低不做不扣分做错反而扣分优先级为“低”的功能我的建议是只写进“扩展计划”一节不要真正实现。老师问起来你能说出思路还能解释为什么没做——因为项目时间和成本不允许。这是软件项目管理里最标准的“范围控制”话术。2.2 把期末作业拆成 WBS四周计划与三个里程碑WBS 是为了把模糊的“做一个考勤系统”拆成可估算、可检查的任务。常见做法是四周完成第一周做需求和数据库设计第二周写代码第三周补测试并准备演示数据第四周专门写文档和答辩材料。下面这个 WBS 可以直接用作任务分解底稿。学生考勤管理系统 WBS 1. 项目启动 1.1 需求调研课堂场景、考勤规则 1.2 范围确认功能列表、角色划分 2. 设计阶段 2.1 数据库设计ER 图、建表 SQL 2.2 界面原型登录页、考勤登记页、统计页 3. 编码阶段 3.1 登录与权限控制 3.2 学生/班级管理模块 3.3 考勤登记模块 3.4 考勤统计模块 4. 测试与数据 4.1 功能测试用例 4.2 准备演示数据 5. 文档与答辩 5.1 软件项目管理文档WBS/甘特图/风险记录 5.2 课程设计报告 5.3 答辩 PPT拆完任务后要在项目管理文档里画甘特图。不用 Project用 Excel 或 draw.io 就能画横轴是四周时间纵轴是上面这些任务给每个任务拉一条水平条。重点是标出三个里程碑第 1 周末attendance_db.sql能在 MySQL 里一次执行成功所有表和测试数据就位第 3 周初登录、考勤登记、统计报表三个核心功能可演示第 4 周末源码、数据库、文档按老师要求的命名格式打包成 zip。这三个里程碑是自检点也是答辩时“进度管理”的证据。2.3 需求追踪矩阵让文档和源码对得上的关键课程设计最常见的扣分项是文档里写的功能和代码做的不一致。解决这个问题不需要什么高级工具一张需求追踪矩阵就够了。每一条需求都编上号写明对应页面、数据库表和代码文件完成后更新状态。这样老师问“REQ-003 在哪里实现的”你能直接指到文件而不是在项目里翻半天。| 需求编号 | 功能描述 | 优先级 | 涉及页面 | 数据库表 | 代码文件 | 状态 | | REQ-001 | 管理员登录 | 高 | login.jsp | t_user | LoginServlet.java | 完成 | | REQ-002 | 班级管理 | 高 | class_list.jsp | t_class | ClassServlet.java | 完成 | | REQ-003 | 学生管理 | 高 | student_list.jsp | t_student | StudentServlet.java | 完成 | | REQ-004 | 考勤登记 | 高 | attendance_add.jsp | t_attendance | AttendanceServlet.java | 完成 | | REQ-005 | 考勤统计 | 中 | stats.jsp | v_student_attendance | StatsServlet.java | 完成 |这张矩阵要在写代码之前就建好每完成一个模块就更新一次状态不要等最后补。如果中途改了设计比如把“迟到 10 分钟算缺勤”改成“迟到算另一种状态”就在变更记录里追加一行变更原因、变更内容、影响到的表。答辩时老师最怕听到“我忘了改”有了变更记录反而显得你做了完整的需求管理。3. 数据库设计考勤系统的表结构和初始化 SQL 脚本学生考勤管理系统这类选题数据库设计决定了后面代码的体感。很多同学一上来就写表写到一半才发现学生登录没地方存、考勤无法区分节次。正确顺序是从用例反推谁登录、谁登记、统计要按什么粒度出数然后建表。这与数据库课程设计的思路一致也能作为软件项目管理文档中“需求分析”的输入。3.1 从用例反推数据模型五张表之间的关系从用例推导出的核心模型很稳定t_user存登录账号t_student存学生档案t_class存班级t_course存课程t_attendance存考勤记录。逻辑关系是一个班级有多个学生一个教师用户负责多门课程一个学生通过考勤记录与课程建立多对多关系。-- 建库字符集统一用 utf8mb4避免中文乱码 CREATE DATABASE IF NOT EXISTS attendance_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE attendance_db; CREATE TABLE t_class ( class_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 班级ID, class_name VARCHAR(50) NOT NULL UNIQUE COMMENT 班级名称, grade_year YEAR NOT NULL COMMENT 入学年份 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT班级表; CREATE TABLE t_user ( user_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 用户ID, username VARCHAR(30) NOT NULL UNIQUE COMMENT 登录用户名, password VARCHAR(64) NOT NULL COMMENT 密码建议存 MD5/SHA256 摘要, role ENUM(admin,teacher,student) NOT NULL COMMENT 角色, student_id CHAR(10) DEFAULT NULL COMMENT 学生角色关联学号, UNIQUE KEY uk_user_student (student_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT登录用户表; CREATE TABLE t_student ( student_id CHAR(10) PRIMARY KEY COMMENT 学号如 2021001001, name VARCHAR(50) NOT NULL COMMENT 姓名, class_id INT NOT NULL COMMENT 所属班级, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, FOREIGN KEY (class_id) REFERENCES t_class(class_id) ON DELETE RESTRICT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT学生表; CREATE TABLE t_course ( course_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 课程ID, course_name VARCHAR(100) NOT NULL COMMENT 课程名称, teacher_id INT NOT NULL COMMENT 授课教师用户ID, schedule VARCHAR(100) DEFAULT NULL COMMENT 上课时间描述如周一3-4节, FOREIGN KEY (teacher_id) REFERENCES t_user(user_id) ON DELETE RESTRICT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT课程表; CREATE TABLE t_attendance ( attendance_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 考勤记录ID, student_id CHAR(10) NOT NULL COMMENT 学号, course_id INT NOT NULL COMMENT 课程ID, class_date DATE NOT NULL COMMENT 上课日期, period TINYINT NOT NULL COMMENT 节次1-4 表示第1-2节、第3-4节等, status ENUM(present,late,absent,leave) NOT NULL COMMENT 考勤状态, remark VARCHAR(200) DEFAULT NULL COMMENT 备注, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 登记时间, UNIQUE KEY uk_stu_course_date_period (student_id, course_id, class_date, period), FOREIGN KEY (student_id) REFERENCES t_student(student_id) ON DELETE CASCADE, FOREIGN KEY (course_id) REFERENCES t_course(course_id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT考勤记录表;几个关键设计决策要能讲清楚。学号用CHAR(10)而不是INT是因为学号可能以 0 开头用数值类型会把前导 0 丢掉。t_user里的student_id允许为空因为管理员和教师不需要关联学生档案这就是“可空外键”的典型用法。考勤表里的唯一索引uk_stu_course_date_period是防重复登记的数据库层兜底即使代码漏了判重SQL 层也能挡下第二次插入。外键策略也值得写进文档。班级删除时用ON DELETE RESTRICT因为一旦删除班级系统就不知道学生属于哪个班宁可报错也不要静默删掉。考勤记录删除时用ON DELETE CASCADE因为考勤只是学生和课程的历史流水学生档案或课程没了流水保留没有意义。3.2 初始化数据测试账号、班级、课程和考勤样例没有演示数据的系统在答辩时非常吃亏。老师点开统计页面看到一片空白提问角度立刻变成“为什么没数据”而不是“功能怎么实现”。初始化数据不要贪多3 个角色账号、2 个班级、2 个学生、2 门课、几条考勤流水足够支撑 5 分钟演示。USE attendance_db; -- 密码字段演示用 MD5实际系统至少要做加盐哈希 INSERT INTO t_class (class_name, grade_year) VALUES (计科2101, 2021), (软工2102, 2021); INSERT INTO t_user (username, password, role, student_id) VALUES (admin, MD5(123456), admin, NULL), (zhangteacher, MD5(123456), teacher, NULL), (2021001001, MD5(123456), student, 2021001001), (2021001002, MD5(123456), student, 2021001002); INSERT INTO t_student (student_id, name, class_id, phone) VALUES (2021001001, 张伟, 1, 13800000001), (2021001002, 李娜, 1, 13800000002); INSERT INTO t_course (course_name, teacher_id, schedule) VALUES (软件工程, 2, 周一3-4节), (数据库原理, 2, 周三1-2节); INSERT INTO t_attendance (student_id, course_id, class_date, period, status, remark) VALUES (2021001001, 1, 2024-11-04, 1, present, NULL), (2021001002, 1, 2024-11-04, 1, late, 迟到10分钟);这条脚本执行时要特别注意插入顺序班级、用户、学生、课程、考勤。因为外键依赖必须先有t_class才能插t_student先有t_user里的教师账号才能插t_course。如果从别处复制脚本后报外键错误多半是顺序乱了或者旧库里有残留数据。验证方式也简单执行完跑一句SELECT COUNT(*) FROM t_attendance;返回 2 条就算成功。3.3 数据库连接池别让连接配置成为连接不上的元凶课程设计里最常见的 MySQL 连接方式是用DriverManager.getConnection()每次现连。这在小 Demo 里没问题但答辩时老师问“如果 100 个学生同时签到数据库会不会崩”你就需要引出连接池。常见做法是用 Tomcat 的 JNDI DataSource配置放在META-INF/context.xml里。!-- META-INF/context.xml放在 Web 应用的 META-INF 目录下 -- Context Resource namejdbc/attendanceDB authContainer typejavax.sql.DataSource driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/attendance_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai usernameroot password你的数据库密码 maxTotal20 maxIdle5 maxWaitMillis10000/ /ContextmaxTotal是连接池最多维护的数据库连接数maxIdle是空闲时保留的连接数maxWaitMillis是请求连接的最大等待时间超过就报超时。这几个参数要能在文档里解释但不需要背数字。重点是和DriverManager对比连接池复用 TCP 连接避免每次请求都做三次握手和数据库身份认证这是软件项目管理里“性能设计”的加分项。4. 核心功能实现登录、签到、统计的最小可运行路径源码部分的常见实现是 JSP Servlet MySQL这也是 java 课程设计案例源码里出现频率最高的组合。它不像 Spring Boot 那样把配置藏起来每一行都在明面上答辩时按包讲老师更容易相信是你自己写的。下面的代码骨架可以直接套进一个标准 JavaWeb 工程编译环境用 JDK 8 Tomcat 9。4.1 登录与权限控制一个 Filter 解决 80% 的页面访问问题登录模块如果在每个 Servlet 里都写一遍“判断 Session 是否为空”不仅重复还容易漏。更稳的做法是写一个AuthFilter拦截所有请求没登录就统一踢回登录页。WebFilter(/*) public class AuthFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; // 注意这里不主动创建 Session避免静态资源也生成无用 Session HttpSession session request.getSession(false); Object loginUser session ! null ? session.getAttribute(loginUser) : null; String uri request.getRequestURI(); // 登录页、登录接口、静态资源放行 if (loginUser ! null || uri.endsWith(login.jsp) || uri.endsWith(LoginServlet) || uri.contains(/css/) || uri.contains(/js/)) { chain.doFilter(request, response); return; } response.sendRedirect(request.getContextPath() /login.jsp); } }这段 Filter 的逻辑很简单先取 Session如果用户已经登录直接放行如果访问的是登录页或登录接口也放行其余请求全重定向到login.jsp。注意request.getSession(false)里的false很关键它不会为一个没登录的请求自动创建 Session能减少服务器内存浪费。登录成功后把用户对象放进 Session 的loginUser属性后续页面就能通过% session.getAttribute(loginUser) %判断当前角色。4.2 考勤登记接口先查重再插入用事务兜底考勤登记是系统的核心写操作最容易踩的坑是重复提交。前端按钮禁用是一道防线但不够后端必须做判重。下面的方法用事务处理先查一遍查到就回滚查不到才插入。public String addAttendance(HttpServletRequest request) { int studentId Integer.parseInt(request.getParameter(studentId)); int courseId Integer.parseInt(request.getParameter(courseId)); String date request.getParameter(date); int period Integer.parseInt(request.getParameter(period)); String status request.getParameter(status); try (Connection conn DBUtil.getConnection()) { conn.setAutoCommit(false); // 开启事务 // 先查重同一学生、同一课程、同一日期、同一节次只能有一条 PreparedStatement check conn.prepareStatement( SELECT attendance_id FROM t_attendance WHERE student_id? AND course_id? AND class_date? AND period? FOR UPDATE); check.setInt(1, studentId); check.setInt(2, courseId); check.setString(3, date); check.setInt(4, period); ResultSet rs check.executeQuery(); if (rs.next()) { conn.rollback(); return 该学生这一节已经登记过不能重复提交; } PreparedStatement insert conn.prepareStatement( INSERT INTO t_attendance(student_id, course_id, class_date, period, status) VALUES(?,?,?,?,?)); insert.setInt(1, studentId); insert.setInt(2, courseId); insert.setString(3, date); insert.setInt(4, period); insert.setString(5, status); insert.executeUpdate(); conn.commit(); return 登记成功; } catch (SQLException e) { // 唯一索引兜底如果两个请求同时通过检查这里会触发 Duplicate 错误 return 登记失败可能是重复登记或数据错误; } }这里用FOR UPDATE对命中的记录加行锁两个请求同时到达时第二个会等第一个提交后再查从而查到这个节次已有记录。如果没有这行锁先查再插的缝隙里可能插入重复数据最终要靠uk_stu_course_date_period唯一索引拦住。对课程设计来说能讲出这两层防重已经很能体现工程意识。参数date统一用yyyy-MM-dd格式period用 1 到 4 表示节次status传present、late、absent、leave四个值和数据库枚举保持一致。4.3 统计报表用视图把复杂 JOIN 封装成单表查询统计报表如果直接在 Servlet 里拼十几行 GROUP BY 和 CASE WHEN代码会很难读也不好测试。更清晰的做法是建视图把统计口径固化在数据库层Java 代码只负责查询和展示。CREATE VIEW v_student_attendance AS SELECT s.student_id, s.name AS student_name, c.course_name, COUNT(a.attendance_id) AS total_times, SUM(CASE WHEN a.status present THEN 1 ELSE 0 END) AS present_times, ROUND( SUM(CASE WHEN a.status present THEN 1 ELSE 0 END) / COUNT(a.attendance_id) * 100, 2 ) AS attendance_rate FROM t_student s JOIN t_attendance a ON s.student_id a.student_id JOIN t_course c ON a.course_id c.course_id GROUP BY s.student_id, c.course_id;视图把“总出勤次数、实到次数、出勤率”一次算好页面查询变成SELECT * FROM v_student_attendance WHERE course_id 1 ORDER BY attendance_rate ASC。要注意视图里没有WHERE条件按课程、日期过滤要在视图外面做。如果业务要求迟到也算出勤改CASE WHEN a.status IN (present,late)即可这就是统计口径的统一入口。数据库增删改查的“查”做到这一步已经覆盖了列表查询、单条详情和聚合统计三种形态。5. 避坑日志从解压到答辩最常见的 6 个翻车点下面这些坑不是某一个 zip 包独有而是这类期末作业交付物的共性问题。我按从解压到答辩的顺序分成两组每一条都可以当排查手册用。很多问题看起来是玄学追到底都是环境版本、约束顺序或事务边界的问题。5.1 启动阶段Tomcat 闪退、数据库连不上、中文乱码现象双击startup.bat后窗口一闪而过浏览器访问http://localhost:8080直接 404。原因最常见是 JDK 和 Tomcat 版本不匹配或者JAVA_HOME、CATALINA_HOME环境变量没配。其次是 8080 端口被其他进程占用Tomcat 启动失败但界面只显示闪退。解决不要双击改成命令行进入 Tomcat 的bin目录执行catalina run这样报错信息不会一闪而过。然后检查环境变量JAVA_HOME要指向 JDK 安装目录不是jre。端口占用用netstat -ano | findstr 8080查出 PID结束占用进程。版本组合直接用最常见的 JDK 8 Tomcat 9 或 Tomcat 8.5避免高版本 JDK 的模块化问题。现象控制台报Access denied for user rootlocalhost或者Communications link failure。原因前者是密码错误或连接串没写对后者是 MySQL 服务没启动、端口不是 3306、驱动版本不匹配。用 MySQL 8.x 时还容易忽略serverTimezone参数。解决先用命令行mysql -uroot -p确认 MySQL 能登录再检查连接串。MySQL 8 的驱动类名是com.mysql.cj.jdbc.Driver连接串至少包含useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai。驱动 jar 必须放在WEB-INF/lib下放错位置在编译时不报错运行时才报ClassNotFoundException。现象SQL 脚本里中文注释在表结构里变成问号页面也出现???。原因数据库连接字符集和文件编码不一致。SQL 文件虽然是 UTF-8 保存的但客户端默认连接字符集可能是latin1或者建库时没指定utf8mb4。解决建库语句固定写DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ciJDBC 连接串加characterEncodingutf8JSP 文件头保持pageEncodingUTF-8。这三处都做对中文乱码基本绝迹。5.2 业务数据阶段重复签到、统计翻倍、外键删除失败现象刷新一下页面同一个学生的同一条考勤记录出现了两条。原因考勤登记 Servlet 只做了页面层按钮禁用后端没有判重数据库也没有唯一索引。点击提交后请求被重复发送或者浏览器刷新重放了上一次 POST。解决后端用 4.2 的“先查再插 事务”数据库再加UNIQUE KEY uk_stu_course_date_period (student_id, course_id, class_date, period)。已经插进去的重复数据要清理先查出来再删不能直接删整个表否则演示数据也没了。现象统计视图里的出勤率看起来翻倍比如一共 4 条记录统计出 8 条。原因多表 JOIN 时表间存在一对多关系。最常见的是t_studentJOINt_attendance时同一个学生在班级表和课程表方向各 JOIN 一次产生笛卡尔积。比如一个学生有两门课、每门课两条考勤JOIN 后可能被重复计算。解决核心是明确统计粒度。统计每个学生的出勤率必须GROUP BY s.student_id, c.course_id或者先在子查询里聚合考勤表再 JOIN 学生表。不要直接对 JOIN 结果做COUNT先SELECT * FROM v_student_attendance看看原始行数是否合理再谈公式。现象删除一个学生或课程时数据库报Cannot delete or update a parent row: a foreign key constraint fails。原因t_attendance里还有指向该学生或课程的考勤记录外键默认的RESTRICT阻止了删除。解决如果确实要物理删除先删考勤记录再删主表或者在建表时对考勤表用ON DELETE CASCADE。课程设计里我更推荐用逻辑删除给t_student加一个is_deleted字段置为 1 表示已停用历史考勤数据还能保存下来。答辩时讲“为什么没删物理记录”可以顺势带出数据完整性设计。6. 答辩前一晚用演示脚本和验收清单把源码、数据库、文档串起来我习惯在答辩前晚花两小时做一次正式“走带”不写新代码只按演示流程完整跑一遍。走带的脚本要固定下来按顺序操作每步都写下预期结果。例如启动 MySQL执行attendance_db.sql确认t_attendance有 2 条数据启动 Tomcat用admin / 123456登录看到管理员菜单进入考勤登记页选课程“软件工程”、日期、节次给张伟登记“出勤”提示成功后再提交一次必须提示重复打开统计报表看到张伟的出勤率与刚才登记的数据一致换成学生账号登录只能看到本人统计看不到其他学生。这五步走完没问题再整理 zip 包。打包前还要做三件小事删掉工程里的target、out、.idea目录避免老师解压后看到一个几 MB 的垃圾文件数据库脚本单独放在sql文件夹并命名带日期比如attendance_db_20241220.sql把需求追踪矩阵的状态全部核对一遍确保文档、代码、数据库三者对得上。答辩时老师常问的三个问题不需要背答案但要有思路为什么考勤状态用枚举不用数字、为什么删学生不动历史考勤、项目延期了你怎么办。前两个都能从数据库设计章节找到答案第三个就讲 WBS 里的缓冲时间。最后提醒一句千万不要在答辩现场现查数据库连接密码那是我见过最尴尬的翻车方式。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网