简介:这份《软件工程课程设计指导书》面向计算机科学与技术专业的初学者与课程设计指导教师,用于解决从需求分析到报告撰写缺少规范范例的问题,帮助读者按软件工程思想完成一个小型系统的完整开发流程。压缩包内共1个doc文档,约539KB,内容以文字教程为主,集中呈现选课系统的设计任务、字段要求、实验环境与考核标准。全书以选课系统为案例,明确学生、教师、课程、成绩等字段要求,以及登录、查询、选课、退课、成绩管理等功能目标,并给出用例图、序列图、对象模型与状态图的文档撰写要求,还附有ODBC数据源配置和C++ Builder 6.0入门操作指南,便于边看边练。目前已有99人学习,适合作为课程设计前期的任务书、文档模板与实施路线参考。
1. 从一张“选课系统”题目开始:这份指导书到底解决什么问题
如果你手里只有一个“选课系统”题目,却不知道该先画用例图还是先建 Access 表,这份软件工程课程设计指导书就是冲着这个卡点来的。它把 10 学时上机拆成需求分析、软件设计、测试数据、源代码和书面报告几条线,开发环境锁定 C++ Builder 6.0 与 Access 2000,示例题目是学生最熟悉的选课系统。
它适合要交课程设计的新手,也适合想复现传统 C/S 数据库开发链路的开发者。指导书不是现成源码包,而是一份把选题、建库、连接、报告和测试串起来的实践手册,附录里还给了 ODBC 数据源和 C++ Builder 窗体连接的完整操作路径。
拿到后先看报告结构,再照着附录建 ODBC 数据源,基本能把从建库到登录选课的骨架跑起来。
2. 指导书骨架拆解:从需求分析到源代码的交付清单
课程设计最容易翻车的地方不是代码写不出,而是做到一半发现报告缺图、测试数据不够、源代码和文档对不上。指导书把交付物拆成需求分析、软件设计、软件测试、源代码和报告几个块,每个块都有明确的验收物。读这份指导书时,不要把它当成“实验步骤说明”翻过去,而要把它当成一张交付清单:每一项最终都要落到文件、图、表或可运行界面上。
课程设计的基本要求写得很直白:了解面向对象开发过程与方法,完成所要求的系统,报告文档符合具体要求。这三句话对应三个验收面:过程要有需求分析和设计图,系统要能登录、查询、选课、退课、查成绩,报告要能按章节交上去。成绩考核按书面报告、软件系统和考勤三项加权,所以只把代码跑起来并不够,报告里的图和数据同样是得分点。
2.1 交付目录与报告章节怎么对应
我一般会建议把课程设计目录按“文档、数据库、源码、测试数据、截图”分开,而不是把所有文件堆在桌面。指导书里的报告章节可以作为目录骨架:题目、报告人信息、课程设计目的、课程设计要求、课程设计内容、需求分析、软件设计、软件测试、源代码、系统评价、自我心得。报告人信息在模板里留占位符,提交前替换成自己的信息,不要把真实姓名学号直接写进示例文件。
下面这张表适合拿来对照交付物,避免报告写完了却发现少图少表。
| 交付物 | 必须包含 | 验收时看什么 | 常见扣分点 |
|---|---|---|---|
| 需求分析 | 用例图、序列图、功能列表 | 角色和用例是否覆盖登录、查询开课、查询选课、查询成绩、选课、退课 | 只画登录,选课和退课没有序列 |
| 软件设计 | 对象模型、用例图、序列图、至少一个重要对象状态图、主要界面 | 学生、教师、课程、选课记录是否成为对象 | 对象模型和数据库表完全对不上 |
| 软件测试 | 不同方法设计的测试数据,至少 20 个不同对象测试序列 | 正常、边界、异常是否都有 | 20 条全是正确登录 |
| 源代码 | 手动编写的代码,可忽略自动生成部分 | 登录、选课、退课、成绩查询是否有实现 | 只交界面文件,没有业务逻辑 |
| 书面报告 | 目的、要求、内容、评价、心得 | 章节完整,图表编号清楚 | 心得写成空话,系统评价没有具体指标 |
这张表不是让你照抄,而是让你在动手前知道每一章最后要产出什么。需求分析阶段产出的用例图,后面要能在软件设计里找到对应对象;软件设计里的状态图,后面要能在测试数据里找到状态迁移用例。指导书强调“整个系统的开发过程及方法应符合软件工程的要求”,落到操作上就是:图不是装饰,图要能追到代码和测试。
2.2 用例图、序列图、对象模型和状态图落到哪些文件
用例图先定角色。选课系统里至少有三类角色:学生、教师、系统管理员。学生侧用例包括登录、查询开课情况、查询选课情况、查询成绩、选课、退课;教师侧用例包括登录、查询开课情况、录入或查询成绩;管理员侧用例可以包括维护学生信息、维护教师信息、维护课程信息。指导书的功能要求写的是“至少应有登录、查询开课情况、查询选课情况、查询成绩、选课、退课等”,所以用例图不要只画学生一侧,教师和课程维护也要有落点。
序列图不要画成“用户点击按钮,系统返回结果”这种一句话图。登录序列图至少要有登录界面、登录控制对象、学生表访问对象、数据库;选课序列图要有选课界面、选课控制对象、课程表访问对象、选课表访问对象、数据库,并且把“检查人数限制、检查先行课、检查重复选课、写入选课记录、更新选课人数”几个消息按顺序画出来。退课序列图要把“删除选课记录、减少选课人数”画成事务里的两个动作。成绩查询序列图要体现按学号和课号过滤。
对象模型用 Student、Teacher、Course、Enrollment 四个核心对象就够撑起选课系统。Student 有学号、姓名、密码、性别、出生年月、籍贯、地址、电话、家庭地址;Teacher 有教师号、姓名、教研室、职称、性别、电话;Course 有课号、课名、学分、先行课号、课时、开课教室、人数限制、选课人数;Enrollment 有学号、课号、考试成绩、平时成绩、总评成绩。对象模型里的关联关系要和数据库表的主外键对应,比如 Enrollment 关联 Student 和 Course,Course 自关联到先行课号。
状态图选“选课记录”最合适。状态可以定为:未选、已选、已退、已录成绩。未选到已选需要人数未满、先行课满足、没有重复选课;已选到已退需要退课操作;已选到已录成绩需要教师录入成绩。状态图不用画得多复杂,但每条迁移要有触发条件,后面测试数据就能按这些迁移设计。
主要界面至少包括登录界面、开课查询界面、选课界面、退课界面、成绩查询界面。界面截图放进报告时,最好把控件命名也标一下,比如登录按钮叫 btnLogin,学号输入框叫 edtStudentId,后面写代码和排查时能对上。
2.3 测试数据与源代码提交边界
指导书要求测试数据至少包括 20 个不同的对象测试序列,并且要用不同方法设计测试数据。常见做法是等价类划分加边界值:登录分正确学号正确密码、正确学号错误密码、不存在学号、空学号;选课分正常选课、重复选课、人数已满、先行课未修、课号不存在;退课分已选课程退课、未选课程退课、退课后再次退课;成绩查询分有成绩、无成绩、成绩为 0、总评成绩边界 60。20 条不是凑数,而是覆盖不同对象和不同状态迁移。
源代码提交边界要写清楚:只交手写代码,自动生成的窗体文件可以省略。报告里可以贴关键代码段,但不要把整个工程文件贴进去。指导书原文写“程序部分只须包含手动编写的代码,可损略自动生成的部分”,这里的“可损略”应是“可省略”的笔误。提交时建议保留工程文件、主窗体手写代码、数据库脚本和测试数据说明,自动生成的备份文件不要打包进去。
3. 数据模型与数据库搭建:把一张大表拆成四张表
指导书列出的字段很多:学生姓名、学号、登录密码、性别、出生年月、籍贯、地址、学生电话、家庭地址、教师号、教师姓名、教研室、职称、性别、教师电话、课名、课号、学分、先行课号、课时、开课教室、人数限制、选课人数、考试成绩、平时成绩、总评成绩。新手最容易把这些字段全塞进一张表,结果选课人数重复存储、教师信息重复录入、成绩和课程混在一起。合格做法是按对象拆成学生表、教师表、课程表、选课表四张核心表,再按需要加管理员表。
拆表的原则是:一个对象一张表,一个多对多关系一张中间表。学生和课程是多对多,选课表就是中间表,同时承载考试成绩、平时成绩、总评成绩。课程表里的“选课人数”是冗余字段,用来快速判断人数限制,但它必须和选课表的实际记录数对得上,后面排查章节会给对账 SQL。
3.1 字段规范化:四张核心表怎么落
下面这段 Access 2000 近似 DDL 可以在 Access 查询视图里执行,字段长度按实际调整。表名用英文,字段名用英文缩写,界面显示中文,这样后面 C++ Builder 里写 SQL 不容易碰到空格和保留字。
-- 学生表:学号做主键,密码字段不要用 password 这类保留字 CREATE TABLE students ( student_id TEXT(20) NOT NULL PRIMARY KEY, student_name TEXT(30) NOT NULL, pwd TEXT(30) NOT NULL, gender TEXT(2), birth_date DATETIME, native_place TEXT(50), address TEXT(100), phone TEXT(20), home_address TEXT(100) ); -- 教师表:教师号做主键 CREATE TABLE teachers ( teacher_id TEXT(20) NOT NULL PRIMARY KEY, teacher_name TEXT(30) NOT NULL, department TEXT(50), title TEXT(30), gender TEXT(2), phone TEXT(20) ); -- 课程表:enrolled 是冗余人数,capacity 是人数限制 CREATE TABLE courses ( course_id TEXT(20) NOT NULL PRIMARY KEY, course_name TEXT(50) NOT NULL, credits DOUBLE, pre_course_id TEXT(20), hours INTEGER, classroom TEXT(30), capacity INTEGER, enrolled INTEGER DEFAULT 0 ); -- 选课表:一名学生选一门课只有一条记录 CREATE TABLE enrollments ( enroll_id COUNTER PRIMARY KEY, student_id TEXT(20) NOT NULL, course_id TEXT(20) NOT NULL, usual_score DOUBLE, exam_score DOUBLE, total_score DOUBLE );代码后的逻辑说明:students 存学生基本信息和登录密码;teachers 存教师信息;courses 存课名、课号、学分、先行课号、课时、开课教室、人数限制和选课人数;enrollments 存选课关系和三组成绩。参数说明:TEXT(20) 表示短文本字段,DOUBLE 表示双精度数值,DATETIME 表示日期时间,COUNTER 是 Access 的自增主键。Access 里外键关系可以在“关系”窗口里拖拽建立,也可以在执行 DDL 后手动补关系;如果 DDL 里的外键约束报错,先建表再拖关系更稳。
课程表里的 pre_course_id 是先行课号,它指向 courses.course_id。选课检查时,如果先行课号不为空,就去 enrollments 里查该学生是否已经修过先行课并且总评成绩达到及格线。课程设计场景可以把及格线写成 60,也可以写成常量,后面二次开发再改成配置项。
3.2 Access 建库与 ODBC 数据源配置
先用 Access 建一个空的 .mdb 文件,比如放在短英文路径C:\course_design\course.mdb,再执行上面的建表语句。数据可以手工录入几条学生、教师、课程记录,方便调试。数据库路径不要放桌面,不要带中文和空格,旧工具在路径解析上容易出玄学问题。
ODBC 数据源按下面顺序配置,前后步骤要连着做,不要中途换管理器:
- 打开 32 位 ODBC 数据源管理器。C++ Builder 6.0 是 32 位工具,必须用 32 位管理器建数据源。
- 选择“用户 DSN”或“系统 DSN”,点击“添加”。
- 驱动选择 Access 驱动(*.mdb),不要选成其他数据库驱动。
- 数据源名填“选课数据源”,描述可以留空。
- 在数据库选择里定位到
C:\course_design\course.mdb,点击确定。 - 回到数据源列表,确认“选课数据源”已经出现,点击确定保存。
提示:如果 64 位系统里只看到 64 位 ODBC 管理器,建出来的数据源在 C++ Builder 6.0 里可能找不到,必须找 32 位管理器。
指导书附录还提到 BDE Administrator 的建别名流程:打开 BDE Administrator,新建 Database Driver Name,选择 Access 驱动,设置 ODBC 数据源,Apply 后 Open 测试,字体变黑表示连接成功。实际做课程设计时,BDE 和 ADO 不必双套都上。C++ Builder 6.0 里用 ADOConnection 直接连 ODBC 数据源更直观,BDE 可以作为备用路径。常见做法是:ODBC 建数据源,ADOConnection 的 ConnectionString 指向该数据源,ADOQuery 挂在 ADOConnection 上。
3.3 连接串与 BDE/ADO 的落点
ADOConnection 的 ConnectionString 可以写成下面这样,DSN 写 ODBC 数据源名,DBQ 写数据库绝对路径,UID 和 PWD 留空表示 Access 无密码。
DSN=选课数据源; DBQ=C:\course_design\course.mdb; UID=; PWD=;参数说明:DSN 是 ODBC 数据源名,必须和 ODBC 管理器里建的名字完全一致;DBQ 是数据库文件路径,路径里有空格时要用引号包住;UID 和 PWD 是数据库用户名密码,Access 单机库通常为空。连接串写完后点“测试连接”,测试成功再挂 ADOQuery。ADOQuery 的 Connection 属性指向 ADOConnection,SQL 语句里用:参数名占位,不要用字符串拼接。
如果走 BDE,别名里的 PATH 指向数据库所在目录,DATABASE NAME 指向 .mdb 文件。BDE 的坑在于别名和 ODBC 数据源是两套配置,改了一边没改另一边就会连不上。我一般会让学生统一走 ADO,BDE 只作为指导书附录里的了解内容,避免两套配置互相打架。
4. 核心功能链路:登录、查课、选退课、成绩查询怎么串起来
选课系统的功能要求看起来不多:登录、查询开课情况、查询选课情况、查询成绩、选课、退课。真正写起来,难点在业务顺序和事务边界。登录要先查学生表,选课要先查课程容量和先行课,退课要同时删选课记录和减人数,成绩查询要按学号和课号过滤。界面控件可以拖,SQL 和事务不能乱。
我一般会把主窗体分成几个页签或几个子窗体:登录、开课查询、选课、退课、成绩查询。控件命名统一用前缀:edt 表示输入框,btn 表示按钮,qry 表示 ADOQuery,ds 表示 DataSource,dbg 表示 DBGrid。这样后面看代码时,edtStudentId 和 qryCourse 一眼能认出来。
4.1 登录与会话状态
登录不要用字符串拼接 SQL,学号里带单引号就会翻车。用参数化查询,把学号和密码作为参数传进去。下面这段 C++ Builder 6.0 风格的代码可以放在登录按钮事件里。
// 登录:参数化查询,返回学生姓名;失败返回空串 AnsiString TryLogin(AnsiString sid, AnsiString pwd) { qryLogin->Close(); qryLogin->SQL->Clear(); qryLogin->SQL->Add("SELECT student_id, student_name FROM students " "WHERE student_id=:sid AND pwd=:pwd"); qryLogin->Parameters->ParamByName("sid")->Value = Trim(sid); qryLogin->Parameters->ParamByName("pwd")->Value = Trim(pwd); qryLogin->Open(); if (qryLogin->Eof) { return ""; // 登录失败 } return qryLogin->FieldByName("student_name")->AsString; }逻辑说明:先关闭上一次查询,清空 SQL,再重新加语句;ParamByName("sid")和ParamByName("pwd")分别绑定学号和密码;Open()执行查询;Eof为真表示没有匹配记录。参数说明:sid 是学号输入框的值,pwd 是密码输入框的值,Trim 用来去掉首尾空格。登录成功后把学生学号存到全局变量或主窗体成员变量里,后面选课、退课、查成绩都用这个学号,不要每次让用户重新输入。
登录失败要给出明确提示,比如“学号或密码不正确”,不要只弹一个空框。连续失败三次可以锁定一分钟,课程设计里不强制,但加上去报告里好写。教师登录可以另做一个入口,查教师表,成功后记住教师号,成绩录入时用。
4.2 选课与退课事务
选课的业务顺序是:检查课程是否存在、检查人数是否已满、检查先行课是否满足、检查是否重复选课、写入选课记录、更新课程选课人数。写入选课记录和更新人数必须在一个事务里,否则会出现选课表有记录但人数没加,或者人数加了但选课记录没写进去。
// 选课:返回 true 表示成功,msg 带回失败原因 bool EnrollCourse(AnsiString sid, AnsiString cid, AnsiString &msg) { // 1. 查课程容量和先行课 qryCourse->Close(); qryCourse->SQL->Clear(); qryCourse->SQL->Add("SELECT capacity, enrolled, pre_course_id " "FROM courses WHERE course_id=:cid"); qryCourse->Parameters->ParamByName("cid")->Value = cid; qryCourse->Open(); if (qryCourse->Eof) { msg = "课号不存在"; return false; } int capacity = qryCourse->FieldByName("capacity")->AsInteger; int enrolled = qryCourse->FieldByName("enrolled")->AsInteger; AnsiString preId = qryCourse->FieldByName("pre_course_id")->AsString; if (enrolled >= capacity) { msg = "人数已满"; return false; } // 2. 检查先行课:示例只判断是否修过且总评及格 if (!preId.IsEmpty()) { qryPre->Close(); qryPre->SQL->Clear(); qryPre->SQL->Add("SELECT total_score FROM enrollments " "WHERE student_id=:sid AND course_id=:preid"); qryPre->Parameters->ParamByName("sid")->Value = sid; qryPre->Parameters->ParamByName("preid")->Value = preId; qryPre->Open(); if (qryPre->Eof || qryPre->FieldByName("total_score")->AsFloat < 60) { msg = "先行课未通过"; return false; } } // 3. 检查重复选课 qryDup->Close(); qryDup->SQL->Clear(); qryDup->SQL->Add("SELECT COUNT(*) AS cnt FROM enrollments " "WHERE student_id=:sid AND course_id=:cid"); qryDup->Parameters->ParamByName("sid")->Value = sid; qryDup->Parameters->ParamByName("cid")->Value = cid; qryDup->Open(); if (qryDup->FieldByName("cnt")->AsInteger > 0) { msg = "不能重复选课"; return false; } // 4. 事务写入:先插选课记录,再按容量条件更新人数 try { adoConn->BeginTrans(); qryIns->Close(); qryIns->SQL->Clear(); qryIns->SQL->Add("INSERT INTO enrollments(student_id, course_id) " "VALUES(:sid, :cid)"); qryIns->Parameters->ParamByName("sid")->Value = sid; qryIns->Parameters->ParamByName("cid")->Value = cid; qryIns->ExecSQL(); qryUpd->Close(); qryUpd->SQL->Clear(); qryUpd->SQL->Add("UPDATE courses SET enrolled=enrolled+1 " "WHERE course_id=:cid AND enrolled<capacity"); qryUpd->Parameters->ParamByName("cid")->Value = cid; int affected = qryUpd->ExecSQL(); if (affected != 1) { throw Exception("人数已满,更新失败"); } adoConn->CommitTrans(); msg = "选课成功"; return true; } catch (Exception &e) { adoConn->RollbackTrans(); msg = e.Message; return false; } }逻辑说明:前半段是检查,后半段是事务写入。BeginTrans开启事务,CommitTrans提交,RollbackTrans回滚。ExecSQL返回受影响行数,更新人数时用WHERE enrolled<capacity再判一次容量,防止并发下超选。参数说明:sid 是当前登录学生学号,cid 是课程号,msg 是返回给界面的提示。异常捕获里要回滚,不然事务挂住会影响后续操作。
退课事务反过来:先查选课记录是否存在,再删记录,再减人数。减人数时用UPDATE courses SET enrolled=enrolled-1 WHERE course_id=:cid AND enrolled>0,防止人数变成负数。退课和选课都要刷新 DBGrid,让用户马上看到结果。
4.3 成绩查询与界面绑定
成绩查询按学号过滤,可选按课号过滤。SQL 用 JOIN 把课程名带出来,界面上显示课号、课名、学分、平时成绩、考试成绩、总评成绩。
// 成绩查询:按当前学生学号查,课号为空表示全部 void LoadScores(AnsiString sid, AnsiString cid) { qryScore->Close(); qryScore->SQL->Clear(); AnsiString sql = "SELECT c.course_id, c.course_name, c.credits, " "e.usual_score, e.exam_score, e.total_score " "FROM enrollments e INNER JOIN courses c " "ON e.course_id=c.course_id " "WHERE e.student_id=:sid"; if (!cid.IsEmpty()) { sql += " AND e.course_id=:cid"; } qryScore->SQL->Add(sql); qryScore->Parameters->ParamByName("sid")->Value = sid; if (!cid.IsEmpty()) { qryScore->Parameters->ParamByName("cid")->Value = cid; } qryScore->Open(); dsScore->DataSet = qryScore; dbgScore->DataSource = dsScore; }逻辑说明:SQL 用 INNER JOIN 把选课表和课程表连起来,课号不为空时追加条件。参数说明:sid 是当前学生学号,cid 是可选课号。界面绑定用 DataSource 和 DBGrid,dsScore->DataSet = qryScore把数据集挂到数据源,dbgScore->DataSource = dsScore把数据源挂到表格。查询后不要忘记Open(),否则 DBGrid 是空的。
开课查询和选课查询可以共用课程查询数据集,开课查询看课程表全量,选课查询看当前学生已选课程。序列图上对应的消息就是:界面触发查询、控制对象拼 SQL、数据集打开、表格刷新。测试时先录一条学生、一门课、一条选课记录,再逐项验证。
5. 常见问题排查:ODBC、ADO、Access 和旧工具的五个坑
旧工具链的坑很集中:ODBC 位数不对、路径带空格、参数绑定错位、事务没包住、数据库引擎缺失。下面五条是课程设计里最容易翻车的记录,每条按“现象、原因、解决”写,照着排查能省不少时间。
5.1 环境与连接排查
现象一:ODBC 管理器里明明建了“选课数据源”,ADOConnection 测试连接却报“找不到数据源名称”。原因通常是位数不匹配,C++ Builder 6.0 是 32 位程序,读的是 32 位 ODBC 数据源,而你在 64 位管理器里建的数据源它看不到。解决方法是打开 32 位 ODBC 数据源管理器重新建一遍,建完在 ADOConnection 的 ConnectionString 里把 DSN 写成完全一致的名字,注意不要多空格。血泪经验是:数据源名里不要带中文括号,界面显示正常,连接串解析时容易出问题。
现象二:数据库文件放在桌面或中文目录,测试连接时报“找不到文件”或“无效路径”。原因是旧版 Access 驱动对长路径、空格、中文路径的解析不稳定,路径里有空格时 DBQ 没加引号也会断。解决方法是把 .mdb 放到短英文路径,比如C:\course_design\course.mdb,DBQ 用绝对路径,路径有空格时用双引号包住。建库后先复制一份备份,改表结构前备份,后悔药比排查快。
现象三:编译能过,运行到qryLogin->Open()就报“未指定的错误”。原因可能是 ADOConnection 没连上、ADOQuery 的 Connection 属性没指向 ADOConnection,或者数据源被别的程序占用。解决方法是先单独放一个按钮测试adoConn->Connected,再看 ADOQuery 的 Connection 属性;Access 数据库被打开时可能锁住,调试时关掉 Access 界面。旧工具没有黑匣子,错误信息很短,只能按连接、数据集、SQL 三段拆。
5.2 数据一致性与业务排查
现象四:选课成功提示出来了,但课程表里的选课人数没变,或者人数加了但选课表没有记录。原因是插入和更新没放在同一个事务里,或者更新语句没有判断受影响行数。解决方法是把BeginTrans、插入、更新、CommitTrans包在 try 块里,异常时RollbackTrans;更新人数时用WHERE enrolled<capacity,并检查ExecSQL()返回值是否为 1。下面这段对账 SQL 可以定期跑,找出人数和实际记录不一致的课程。
-- 对账:课程表 enrolled 与选课表实际记录数不一致的课程 SELECT c.course_id, c.course_name, c.enrolled AS stored_count, COUNT(e.student_id) AS real_count FROM courses c LEFT JOIN enrollments e ON c.course_id = e.course_id GROUP BY c.course_id, c.course_name, c.enrolled HAVING c.enrolled <> COUNT(e.student_id);逻辑说明:LEFT JOIN 保证没有选课记录的课程也能查出来,COUNT(e.student_id) 统计实际选课人数,HAVING 过滤出不一致的行。参数说明:stored_count 是课程表冗余人数,real_count 是选课表实际人数。查出来之后不要手工乱改,先看是插入失败还是更新失败,再决定补记录还是重算人数。
现象五:换一台机器演示时,程序打不开数据库,提示驱动不存在。原因是目标机器没装对应版本的 Access 数据库引擎,或者装的是 64 位引擎而程序是 32 位。解决方法是提前在演示机器上装 32 位 Access 驱动,或者把运行环境做成虚拟机镜像;提交前在干净环境里跑一遍登录、选课、退课、查成绩四条链路。课程设计答辩现场最怕“在我电脑上是好的”,所以打包时把数据库、驱动说明、数据源配置步骤写进 README,别只交一个 exe。
6. 二次开发:把课程设计从能跑改成能讲清楚
课程设计做完基础功能后,二次开发不用贪多,挑三个点就能让答辩时有的讲:角色权限、选课时间窗口、成绩录入校验。角色权限在登录后区分学生和教师,学生只能选退课和查自己的成绩,教师只能录成绩和查开课;改动位置在登录成功后的主窗体初始化,验证方法是分别用学生账号和教师账号登录,看菜单项是否灰掉。选课时间窗口在课程表加 start_time 和 end_time 两个字段,选课前判断当前时间是否在窗口内;验证方法是把窗口设成过去时间,看选课按钮是否提示“不在选课时间”。成绩录入校验在保存总评成绩时判断平时成绩和考试成绩是否在 0 到 100 之间,总评按比例算完后回写;验证方法是输入 -1 和 101,看是否拦截。
| 改造点 | 改动位置 | 验证方法 |
|---|---|---|
| 角色权限 | 登录事件、主窗体菜单初始化 | 学生账号看不到成绩录入,教师账号看不到选课 |
| 选课时间窗口 | courses 表加时间字段,EnrollCourse 开头判断 | 窗口外选课提示失败,窗口内成功 |
| 成绩录入校验 | 成绩保存按钮事件 | 输入 -1、101、正常值三组数据看提示 |
| 人数对账 | 管理员菜单加对账按钮,执行对账 SQL | 手工改错 enrolled 后能查出不一致 |
对账 SQL 可以直接用上一章那段,查到不一致后提供“按选课表重算人数”的按钮,执行UPDATE courses SET enrolled=(SELECT COUNT(*) FROM enrollments WHERE enrollments.course_id=courses.course_id)。这样答辩时老师问“选课人数错了怎么办”,你有排查路径也有修复动作。从那以后我每次带课程设计,都先让学生跑一遍人数对账 SQL,再谈界面美化;这份指导书里的操作指南和报告模板,适合拿来当底盘,改完记得在干净环境里重跑登录、选课、退课、查成绩四条链路。希望帮到你。
本文还有配套的精品资源,点击获取