简介这是一份面向高校学籍管理场景的数据库综合实验文档适用于数据库课程设计、信息系统开发学习以及相关教务管理需求分析。文档依据实际学籍管理流程覆盖学院、班级、教师、学生、课程、选课与成绩等核心业务数据完整记录了从需求分析、概念结构设计含E-R图、数据字典与数据流图到逻辑结构设计、数据表主键与字段定义、系统功能模块划分再到数据库创建与初始化的实施过程文末还附带实验演示截图与心得体会可帮助读者直观理解每一步设计思路与落地细节。资源共1个doc文件压缩包约653KB结构紧凑无冗余适合直接查阅或作为课程设计参考。目前已有196人学习下载适合正在完成学籍管理类课题或希望系统掌握关系数据库设计流程的读者。1. 高校学籍管理系统这套资源一份能照做的数据库课程设计完整样本高校学籍管理系统这套资源是一份2009年的数据库综合实验报告技术栈锁定在Visual Basic 6.0、SQL Server 2000和ADO内容却十分完整——需求分析、E-R图、数据字典、数据流图、七张数据表结构、功能模块图、登录到选课成绩的每一步截图和操作说明一字不缺。对正在赶数据库课程设计的学生或想快速搭学籍管理Demo看模块怎么组织的开发者这份文档的价值在于关键决策都摊开了主键选什么、外键怎么连、权限怎么分、校验怎么做。我按它复现了一遍流程走得通也踩了几个坑。它适合三类人准备交综合实验报告的学生、需要设计教务模块的新手开发者、想对比早期VBADO写法的老系统维护者。2. 需求分析与角色设计三类登录用户、八项管理和查询权限的边界画法2.1 信息需求七类数据实体各自负责什么学籍管理表面上看是“管学生”拆开之后要落库的东西比预想多得多。文档的需求分析部分把信息需求分成七类学院信息、班级信息、任课教师信息、学生信息、课程信息、选课记录和成绩数据。这里容易被忽略的是选课记录和成绩被拆成独立的一块而不是挂在学生表或课程表下面。原因不复杂一个学生选多门课一门课被多个学生选这是典型的多对多关系必须有独立的中间表来承接否则课程和学生的关联在关系模型里根本表达不完整。把七类数据放到一起看结构其实很清晰。学院和班级是组织架构维度学生和教师是人员维度课程、选课和成绩是业务运行维度。每一类数据在E-R图里都对应实体在物理设计里都对应一张数据表维度之间靠外键串起来。如果偷懒只按“人”建模不做选课中间表做到成绩录入环节就会卡死成绩到底挂在哪挂在学生表里一门课只能存一个成绩挂在课程表里又对不上具体学生。所以动手建表之前先把数据实体分类理清楚是这份文档做得最扎实的部分也是最值得照着学的一步。数据字典在这份文档里承担的是冻结字段的作用。系统用户数据、学院信息数据、班级信息数据、教师信息数据、学生信息数据、课程信息数据、选课记录数据七类每一项都写清了包含哪些数据项。比如教师信息是编号、姓名、所属学院编号、联系电话、电子邮件学生信息则多了出生日期、家庭住址、个人简历。字段定得越早后面建表越省事。很多课程设计翻车都是死在“表已经建了发现缺字段再去ALTER TABLE加列”这份文档用数据字典把这个风险提前压掉了。2.2 功能拆解管理员、教师、学生各自能碰哪些按钮功能要求的关键词是八个字三类用户、八项功能。系统管理员负责日常学籍管理文档列出来的包括学院信息管理、班级信息管理、教师信息管理、学生信息管理、课程信息管理、选课数据管理、系统用户管理外加数据查询一共八项覆盖全部录入、修改、删除操作。教师端则收敛到教学班信息查询和学生成绩管理两块。学生端最小只有选课和成绩查询。这个权限划分在职场上叫最小权限原则在课程设计里叫按角色分菜单。管理员的权限是全表可写教师被限制在自己讲授课程的教学班范围内学生更窄只能读与自己学号绑定的成绩记录。文档在实验演示部分明确写了一句“学生只能进行成绩的查询不能进行任何的修改”这句话到了实现阶段直接翻译成两件事一是界面上学生登录后看不到编辑按钮二是后台SQL按学号过滤数据。权限边界画得清晰程序实现就简单边界模糊后面权限校验就得打一堆补丁。再往细看教师端的功能也不全放开。教学班成绩管理里教师想改某个学生成绩系统会先弹一次身份验证确认任课教师身份后才允许修改。这个设计思路比单纯靠登录判断进没进系统更严谨一层登录验证的是“你是不是教师”修改成绩时验证的是“你够不够资格改这门课”。课程编号和教师编号的匹配关系在这里就成了天然的越权拦截器不用额外写复杂的权限表就能挡住大多数误操作。2.3 技术栈选型为什么偏偏是VB 6.0、SQL Server 2000和ADO文档写的系统环境是Windows XP SP3开发工具VB 6.0数据库SQL Server 2000数据访问用ADO。这套技术栈放在2009年非常典型放到今天看确实老旧但当时选择的理由很实际VB 6.0窗体靠拖拽就能出来出界面速度比写MFC快一个量级SQL Server 2000的企业管理器足够完成建库建表ADO连接字符串短不用配置ODBC DSN省了环境搭建的功夫。对于需要在有限时间交实验报告的场景这套组合是当时成功率最高的方案。ADO在文档里被明确描述为让应用程序直接访问并修改数据源的Client/Server模型访问数据库的过程固定成三步创建Connection对象建立连接创建Recordset对象取得数据检索记录集并显示或修改。这个三步法贯穿了文档后面所有功能模块的实现。复现时一个很容易踩的细节是Recordset的游标类型选择adOpenKeyset和adLockOptimistic组合适合一边遍历一边修改如果误用只读游标做成绩修改时会发现更新提交不上去。提示VB 6.0的字符串处理有个习惯性问题——SQL Server的CHAR定长字段取出后自带尾部空格比较时记得Trim。2.4 模块化拆分与集成顺序从单窗体到主窗体菜单文档在数据库设计实施部分写了一段值得注意的话先模块后系统集成。各个功能模块先独立设计和调试创建系统主窗体时才通过菜单系统把模块集成到一起最后做整体联调。这个顺序对应到实际操作就是先做登录模块验证数据库连通再逐个实现用户、学院、班级、教师、学生、课程、选课、成绩管理窗体每个窗体单独能跑通增删改查最后用主窗体的菜单把这些窗体串起来。这样做的好处是排错范围被压缩得很小某个模块报错只查那一个窗体的代码不至于一处错误牵扯全局。我在自己拆过的几个管理系统里也沿用这套顺序只是把“主窗体菜单”换成了权限路由配置。对新手来说这个集成顺序比任何编码技巧都值钱因为它决定了你调试时面对的是几十行代码还是一个千行大文件。3. 从E-R图到七张数据表主键、外键和定长字段的取舍规则3.1 E-R图和数据字典先画关系再定字段文档的概念结构设计给出了完整的E-R图和数据字典。实体一共七个管理员、系部、班级、学生、教师、课程、选课信息。实体之间的关系是系部拥有班级班级拥有学生教师讲授课程学生选修课程并产生成绩。这个关系集合画成图就一张纸但它直接决定了后面物理表之间的外键走向也决定了哪些表是一对多、哪些是多对多。把E-R图翻译成表的规则很固定一对多关系在“多”的那端放外键多对多关系必须拆出一张中间表。套用在这个系统里班级表放系编号外键学生表放系编号和班编号两个外键教师表放系编号外键课程表放教师编号外键选课表作为学生和课程的中间表承担成绩字段。数据字典则把每个字段的类型、长度、是否允许空、是否主键敲定。例如学生编号是10位定长、系编号是4位定长、课程编号是8位定长、成绩在课程结束前是空。这些约束看起来琐碎但都是后面界面校验程序的直接依据。3.2 七张表的完整结构主键、外键、允许空与定长约束按数据字典整理出来的物理表结构如下列提取自文档文字描述类型按SQL Server 2000常见写法还原。表名主要字段主键外键关键约束系统用户表用户名、口令用户名无用户名0~6位口令必须6位系部信息表系编号、系名称系编号无系编号4位定长班级信息表班级编号、班级名称班级编号无班级编号8位定长学生信息表学生编号、系编号、班编号、姓名、性别、生日、住址、电话、Email、简历学生编号系编号、班编号学生编号10位定长住址电话Email允许空教师信息表教师编号、姓名、系编号、电话、Email教师编号系编号教师编号6位定长电话Email允许空课程信息表课程编号、课程名称、教师编号、学分课程编号教师编号课程编号8位定长学分1位选课记录表记录编号、学生编号、课程编号、成绩记录编号学生编号、课程编号记录编号int自增成绩允许空这里有几个设计细节值得单独说明。学生信息表的“编号”被设计成10位定长字符串而不是自增整数这在老系统里很常见学号本身携带入学年份、学院代码等信息用定长字符串既保证格式统一也方便按学号前缀做粗粒度筛选。课程学分用1位定长表示意味着只支持1到9这样的个位数设计意图是课程设计学分通常不超过9分用CHAR(1)还能顺带做输入约束。选课记录表的主键用int自增标识是这几张表里唯一的代理键理由是从业务上讲“第几条选课记录”没有自然主键自增列最省事。3.3 把表结构落到SQL Server 2000一份可用的建表脚本原文档没有给出建表SQL脚本只有文字描述的表结构。复现时我一般会按下面这种方式补全供参考。先建独立表再建依赖外键的表。-- 系部信息表系编号4位定长主键 CREATE TABLE Department ( dept_id CHAR(4) PRIMARY KEY, -- 系编号4位定长 dept_name NVARCHAR(50) NOT NULL -- 系名称 ); -- 教师信息表教师编号6位定长主键系编号外键 CREATE TABLE Teacher ( teacher_id CHAR(6) PRIMARY KEY, -- 教师编号6位定长 teacher_name NVARCHAR(20) NOT NULL, -- 教师姓名 dept_id CHAR(4) NOT NULL REFERENCES Department(dept_id), phone VARCHAR(20) NULL, -- 联系电话允许空 email VARCHAR(50) NULL -- 邮箱允许空 );逻辑说明先建Department和Teacher是合理的因为它们不依赖其他业务表Teacher外键指向Department所以Department要先于Teacher创建。外键全部用内联REFERENCES写法省掉后续ALTER TABLE ADD CONSTRAINT2000年风格的做法也基本是这样。文档作者在实验心得体会里自己承认了一个设计缺陷系部信息和班级信息之间没有实现逻辑上的联系。也就是说班级表里没有系编号外键E-R图里画了“系部拥有班级”物理表里却没落下来。按E-R图补全的话班级表应该是这样的。-- 班级信息表班级编号8位定长主键补齐系编号外键 CREATE TABLE Class ( class_id CHAR(8) PRIMARY KEY, -- 班级编号8位定长 class_name NVARCHAR(50) NOT NULL, -- 班级名称 dept_id CHAR(4) NOT NULL REFERENCES Department(dept_id) ); -- 学生信息表学号10位定长主键系编号、班编号双外键 CREATE TABLE Student ( stu_id CHAR(10) PRIMARY KEY, -- 学生编号10位定长 dept_id CHAR(4) NOT NULL REFERENCES Department(dept_id), class_id CHAR(8) NOT NULL REFERENCES Class(class_id), stu_name NVARCHAR(20) NOT NULL, -- 姓名 sex CHAR(2) NULL, -- 性别 birthday SMALLDATETIME NULL, -- 生日 address NVARCHAR(100) NULL, -- 住址允许空 phone VARCHAR(20) NULL, -- 电话允许空 email VARCHAR(50) NULL, -- 邮箱允许空 resume NTEXT NULL -- 简历允许空 );逻辑说明Student表同时携带dept_id和class_id这就是文档里描述的“系编号、班编号为此表的外键分别与系部信息表和班级信息表中的系编号、班编号对应”。冗余存一份系编号不是设计失误而是查询效率的考量——按系统计学生时不必连班级表。代价是数据一致性风险所以程序里修改学生班级时必须同步更新系编号。birthday选SMALLDATETIME而不是DATETIME是因为SMALLDATETIME精度到分钟存生日足够体积小一半。resume用NTEXT是2000年的处理方式现在迁移到新版本要改成NVARCHAR(MAX)。课程和选课两张表继续补全。-- 课程信息表课程编号8位定长主键教师编号外键 CREATE TABLE Course ( course_id CHAR(8) PRIMARY KEY, -- 课程编号8位定长 course_name NVARCHAR(50) NOT NULL, -- 课程名称 teacher_id CHAR(6) NOT NULL REFERENCES Teacher(teacher_id), credit CHAR(1) NOT NULL -- 学分1位定长 ); -- 选课记录表自增编号主键学生编号和课程编号外键 CREATE TABLE Enroll ( id INT IDENTITY(1,1) PRIMARY KEY, -- 记录编号自增 stu_id CHAR(10) NOT NULL REFERENCES Student(stu_id), course_id CHAR(8) NOT NULL REFERENCES Course(course_id), score DECIMAL(4,1) NULL -- 成绩允许空 );逻辑说明Enroll表是学生和课程的多对多中间表成绩字段放在这里而不是学生表或课程表是因为成绩属于“某学生选某门课”这一事件而不是属于学生或课程本身。score精度用DECIMAL(4,1)支持到999.9分对百分制够用也兼容部分学校采用的五级制换算数字。credit字段如果后续想支持0.5学分CHAR(1)就不够用了我复现时会把它改成DECIMAL(2,1)代价是失去免费的定长校验。系统用户表的建表脚本单独拎出来因为它涉及登录模块。-- 系统用户表用户名主键口令固定6位 CREATE TABLE SysUser ( user_name VARCHAR(6) PRIMARY KEY, -- 用户名0~6位 password CHAR(6) NOT NULL -- 口令必须6位 ); -- 插入两个演示账号对应文档中的管理员admin和yangyi INSERT INTO SysUser (user_name, password) VALUES (admin, 111111); INSERT INTO SysUser (user_name, password) VALUES (yangyi, 302425);口令字段用CHAR(6)数据库层面强制了长度输入7位或5位都会报错。用户名用VARCHAR(6)而不是CHAR(6)是因为文档写的是0~6位字符串空用户名的场景虽然没意义但VARCHAR比CHAR少了一次尾部补空格的麻烦。这两个演示账号在文档的登录演示部分被用来验证系统启动密码是明文存储的——这在课程设计场景里是常态但拿到生产环境必须换掉。4. 用VB 6.0 ADO把界面跑起来登录判定、增删改查、选课与成绩录入4.1 ADO三件套Connection、Recordset如何配合文档里把ADO的访问过程拆成三步创建Connection对象建立数据库连接创建Recordset对象获取数据检索记录集显示或修改。落到VB 6.0代码就是标准的ADODB对象。连接字符串用SQLOLEDB Provider直连不走ODBC。Dim conn As New ADODB.Connection Dim rs As New ADODB.Recordset Provider用SQLOLEDB直连本机SQL Server 2000 conn.Open ProviderSQLOLEDB;Data Source.;Initial CatalogStudentDB;User IDsa;Password123456 打开学生表keyset游标允许前后滚动乐观锁允许就地更新 rs.Open SELECT * FROM Student, conn, adOpenKeyset, adLockOptimistic Do While Not rs.EOF Debug.Print rs.Fields(stu_name).Value rs.MoveNext Loop逻辑说明Connection负责数据库会话Open里的连接字符串包含数据源、库名、账号口令。Recordset承载查询结果第三个参数adOpenKeyset让游标可以双向滚动第四个参数adLockOptimistic只在Update方法调用时锁定记录适合单用户桌面程序的课程设计场景。循环遍历记录集用EOF判断边界MoveNext推进游标这是VB 6.0访问数据库最基础的写法。注意SQLServer登录账号不要用sa跑业务查询复现时单独建一个student_user账号授权能少很多麻烦。实际开发时我不会把连接字符串写死在每个窗体里。文档虽是课程设计但模块多了之后复用连接是个现实问题。常见做法是在一个公共模块里定义全局连接变量所有窗体引用它这样改数据库地址只改一处。 公共模块 modDB.bas Public gConn As New ADODB.Connection Public Sub InitDB() gConn.Open ProviderSQLOLEDB;Data Source.;Initial CatalogStudentDB;User IDstudent_user;Password123456 End Sub逻辑说明Public gConn做成全局对象主窗体加载时调用InitDB建立一次连接后续所有窗体共用。课程设计规模下这样够用但要清楚这种写法在多线程环境下不安全VB 6.0传统窗体本来也基本单线程所以问题不大。4.2 登录判定最多三次失败自动退出登录模块是文档演示的第一个功能逻辑做了三重限制用户名判空、口令必须6位、整个登录过程最多失败三次。三条规则加起来既是格式校验也是简易防爆破。Dim loginCount As Integer 模块级变量记录失败次数 Private Sub cmdLogin_Click() Dim sql As String Dim rs As New ADODB.Recordset 口令必须6位长度不对直接拦截 If Len(txtPassword.Text) 6 Then MsgBox 口令必须是6位字符串 Exit Sub End If 用户名和口令拼进查询取匹配记录 sql SELECT * FROM SysUser WHERE user_name txtUser.Text AND password txtPassword.Text rs.Open sql, gConn, adOpenKeyset, adLockReadOnly If Not rs.EOF Then 登录成功根据用户名判断角色 MsgBox 欢迎登录 rs.Fields(user_name).Value 这里按用户名去匹配教师表/学生表决定主界面菜单可用性 Else loginCount loginCount 1 If loginCount 3 Then MsgBox 登录失败三次系统将退出 Unload Me Else MsgBox 用户名或口令错误剩余次数 (3 - loginCount) End If End If rs.Close End Sub逻辑说明口令长度校验放在最前面等于把格式错误的请求挡在SQL查询之前。查询语句把用户名和口令直接拼进SQL这在2009年的课程设计里是普遍写法但对现在的复现来说是个隐患换成参数化查询更稳妥。登录成功分支没有真正区分角色文档里的角色判定逻辑是管理员看用户表教师用户名等于教师姓名、口令等于教师编号学生用户名等于学生姓名、口令等于学生编号——也就是说教师和学生的登录凭证散落在业务表里登录查询要在教师表和学生表里再各查一次。复现时我会把角色字段直接加进SysUser表省掉这三表联动。4.3 增删改查与记录导航定长编号的校验逻辑文档以学生信息管理为例演示了增删改查操作路径是记录导航条选记录点添加或修改按钮编辑字段保存。这里最有技术含量的是编号字段的定长校验——学生编号10位、系编号4位、教师编号6位、课程编号8位输错位数系统直接弹提示。Private Sub cmdSave_Click() 学生编号必须10位用Len判断不够直接拒绝 If Len(txtStuId.Text) 10 Then MsgBox 学生编号应为10位定长字符串 Exit Sub End If 系编号4位、班编号8位同理校验 If Len(txtDeptId.Text) 4 Or Len(txtClassId.Text) 8 Then MsgBox 系编号应为4位班编号应为8位 Exit Sub End If 通过全部校验后进入新增或更新分支 If isNewRecord Then gConn.Execute INSERT INTO Student (stu_id, dept_id, class_id, stu_name, sex) VALUES ( _ txtStuId.Text , txtDeptId.Text , txtClassId.Text , _ txtStuName.Text , txtSex.Text ) Else gConn.Execute UPDATE Student SET stu_name txtStuName.Text WHERE stu_id txtStuId.Text End If MsgBox 保存成功 End Sub逻辑说明保存前的定长校验用Len函数完成这是模拟文档截图里“编号输入不正确时弹出提示”的行为。isNewRecord是记录导航条状态决定的标志位导航条停在空记录表示新增停在已有记录表示修改。INSERT写得很短因为文档没提必填字段校验但我复现时会把姓名、性别也做成非空校验否则数据库NOT NULL约束会给出一个不友好的底层报错。记录导航条是VB 6.0数据绑定控件的基础能力相当于Access里的记录浏览器。文档演示删除功能时操作顺序是先选记录、点删除、弹确认框、点“是”。这个确认框属于防误操作设计删除是不可逆动作课程设计里通常直接用MsgBox两选项实现。4.4 选课与成绩管理预选、保存、重复提示和教师身份验证选课功能在文档里被描述为学分制的核心操作流程串起来是先选学生编号通过记录导航条查看课程在左下角课程列表选择课程加入预选课程列表点保存后课程编号进入已选课程列表重复选择同一门课自动提示删除选课则是选中编号点删除并确认。这里的关键是“预选列表”和“已选列表”两个临时态的设计——预选是本次操作还没落库的临时集合保存才批量写入Enroll表。Private Sub cmdAddCourse_Click() Dim i As Integer 遍历预选列表逐个检查是否已在已选列表中出现 For i 0 To lstPreSelect.ListCount - 1 If lstPreSelect.Selected(i) Then If InStr(lstSelected.Text, lstPreSelect.List(i)) 0 Then MsgBox 该课程已在选课列表中不能重复选择 Exit Sub End If End If Next i 通过检查后把选中的课程加入预选列表 For i 0 To lstPreSelect.ListCount - 1 If lstPreSelect.Selected(i) Then lstSelected.AddItem lstPreSelect.List(i) End If Next i End Sub逻辑说明重复选课判断用InStr在已选课程文本里检索课程编号这是VB 6.0里最朴素的做法。注意这种判断只防了界面层的重复数据库层如果有并发或异常绕过界面靠InStr是兜不住的正经做法是在Enroll表上加UNIQUE约束。文档演示截图里重复选择弹了提示说明程序层判断是生效的但它没提数据库约束这是可以补强的地方。教学班成绩管理的操作顺序则是另一个逻辑选课程编号、点确定、弹教师身份验证框、确认后显示教学班学生名单教师在成绩栏直接修改改完保存。这里的身份验证框就是第2章提到的那道越权拦截器验证的是“当前教师是否为该课程主讲的教师”。实现上就是在保存前查Teacher和Course两张表。Private Sub cmdVerifyTeacher_Click() Dim rs As New ADODB.Recordset Dim sql As String 校验当前登录教师是否主讲该课程 sql SELECT COUNT(*) AS cnt FROM Course WHERE course_id _ txtCourseId.Text AND teacher_id gTeacherId rs.Open sql, gConn, adOpenKeyset, adLockReadOnly If rs.Fields(cnt).Value 0 Then MsgBox 您不是该课程的主讲教师无权修改成绩 Exit Sub End If 通过验证加载教学班学生名单 End Sub逻辑说明这条SQL通过课程编号和当前教师编号的联合匹配判断权限返回0表示该教师与课程无关联直接拒绝。gTeacherId是教师登录时存入全局变量的教师编号。这个设计思路比单纯依赖登录角色高明的地方在于即使教师登录成功也只能操作自己名下的课程属于行级权限控制。文档还提到教学班成绩管理和教学班信息查询窗体实现了打印和打印预览功能用VB 6.0的DataReport或Printer对象渲染报表这块在课程设计里通常是加分项。5. 复现排错与避坑记录五条能省半天的血泪经验5.1 定长字段校验翻车编号输错总提示格式不对现象添加学生记录时学生编号输入不足10位或超过10位系统弹“编号格式错误”记录死活存不进去。原因文档把字符编号设计成CHAR定长数据库层对长度敏感。学生表在外键引用时要求关联字段长度完全一致比如Student的class_id是CHAR(8)Class的class_id也是CHAR(8)一边偏差一个字节外键匹配直接失效。解决先别急着改数据库把输入窗体的MaxLength属性设成对应长度。学生编号MaxLength10系编号4课程编号8。这样用户在输入阶段就框死了长度Len校验只是第二道防线。如果已经出现外键匹配不上检查两端字段类型是否同为CHAR且长度一致VARCHAR和CHAR混用也会导致匹配异常。5.2 系部与班级没做外键按学院统计时对不上账现象按系统计学生人数结果等于全校人数或者某个系调走班级行政关系变了数据却完全没反应。原因这是文档作者自己在实验心得体会里承认的缺陷——E-R图里画了系部拥有班级物理表里Class却没有dept_id外键。Student表虽然冗余存了dept_id但班级的归属变动没人管数据一致性全靠手工维护。解决按第3章补全Class表的dept_id外键字段同时给Student表的dept_id和class_id加上联合索引。迁移老数据时先保证班级表的dept_id填对再根据Class反刷Student的dept_id。这个坑的教训是E-R图画了关系物理表就必须有对应外键不能只活在概念设计里。5.3 口令明文存储管理员口令直接写在表里现象打开SysUser表能看到admin的口令是111111yangyi的备用口令也看得一清二楚。任何能连上数据库的人不用绕过程序直接查表就能拿到全部登录凭证。原因课程设计为了演示方便口令直接明文入库且口令本身就是6位数字暴力穷举只需要一百万种组合用现代GPU几分钟跑完。解决如果是复现练习至少把口令改成常见哈希存储。VB 6.0里可以用MD5算法或者调用CAPICOM的HashedData类。登录时把用户输入的口令先哈希再比对。信息系统对密码有合规要求的还要加盐Salt按用户维度独立存储。这个改动不复杂但能直接把系统从“玩具”拉到“能演示安全意识”。5.4 重复选课只靠程序判断数据库层没有唯一约束兜底现象正常操作下重复选课会被程序拦截但用数据库工具直接往Enroll表插入同学生同课程的记录没有报错。原因文档只实现了界面层的InStr判断Enroll表没有加UNIQUE约束。程序判断和数据库约束是两道不同的门界面层拦得住普通用户拦不住绕过界面的写入或并发场景。解决给Enroll表的stu_id和course_id加复合唯一索引。SQL Server 2000里直接建UNIQUE索引即可。注意加约束前先清理历史重复数据否则索引建不上去。这个坑的真实场景是课程设计答辩时被老师问“你凭什么保证不重复”一句“数据库有唯一约束”比“程序里判断了”更有说服力。5.5 SQL Server 2000和VB 6.0在今天的机器上装不上现象Win10或Win11上装SQL Server 2000报兼容性错误VB 6.0装完打不开或组件注册失败OLEDB连接串在64位系统上找不到Provider。原因SQL Server 2000是XP时代的产物微软早就不支持安装程序里的某些组件在新系统上被拦截VB 6.0的IDE对高DPI和64位环境适配很差SQLOLEDB Provider是32位组件64位程序默认访问不到。解决最省事的路子是装虚拟机VMware或VirtualBox里跑一个Windows XP SP3SQL Server 2000和VB 6.0全装进去按文档原样复现一次跑通。如果不想动虚拟机数据库换SQL Server 2019 Express连接字符串的Provider改成SQLNCLI11或MSOLEDBSQL代码层面ADO对象不变表结构按第3章建。VB 6.0项目文件在32位Win7上编译成exe再拷到新系统运行也能绕开IDE兼容问题。注意SQL Server 2000的备份文件后缀是.bak2000版本的备份不能直接恢复到2019需要先用2000或2005还原再备份成高版本兼容的格式。6. 把2009年的系统跑在今天环境迁移、测试用例与三处加固6.1 迁移要点SQL Server 2000到2019 Express的差异数据表迁移时先处理类型兼容NTEXT和IMAGE在2019里虽然还能用但官方建议换成NVARCHAR(MAX)和VARBINARY(MAX)。SMALLDATETIME建议保持原样精度到分钟对生日足够。外键关系可以原样带入但要注意排序规则——2000默认排序规则和2019可能不同跨库比较会出乱码隐性问题。连接字符串从SQLOLEDB换成MSOLEDBSQL连接参数不变ADO代码一行都不用改。6.2 登录与权限测试用例一张表验证三类角色用例编号角色输入预期结果T01管理员admin / 111111进入系统八项功能菜单全部可见T02管理员admin / 111112提示口令错误剩余次数2T03教师张三 / 教师编号仅见教学班查询和成绩管理菜单T04学生李四 / 学生编号仅见选课和成绩查询菜单T05任意口令长度不为6直接拦截不发起查询T06任意连续失败3次自动退出系统这张用例表我复现时是手动走的T01到T04分别验证三类角色的菜单可见性T05验证定长校验是否在输入层生效T06验证三次失败退出逻辑。登录模块改过任何一行代码这六条就得重新跑一遍。6.3 三处低成本加固外键、唯一约束、口令哈希给Class表补dept_id外键给Enroll表加(stu_id, course_id)唯一约束把SysUser的口令改成MD5哈希存储这三处改动半小时内能完成却补上了文档自己承认的三块短板。从那以后我每次拿到别人的课程设计文档都强制走一遍同一套检查E-R图里的关系有没有全部落到外键、业务上的唯一性有没有数据库约束兜底、登录凭证是不是明文入库。这三条不过关文档写得再漂亮都不敢直接复用希望帮到你。本文还有配套的精品资源点击获取