简介:这份图书管理系统软件工程文档面向软件工程课程设计、毕业设计及需要撰写开发文档的学生,围绕需求分析、系统设计、数据库设计与系统实现提供完整范例,可用于梳理开发流程和补足建模表达。压缩包内为1个doc文件,约535KB,以课程设计报告或开发说明文档形式集中组织,便于按章节检索与对照。目录覆盖课题背景与关键技术、系统目标与需求描述、功能性能要求,以及读者请求服务、借阅归还、系统维护等用例图和时序图,并延伸到类关系、E-R图、数据表创建与数据库连接。系统实现部分还包含工程创建、模块与类模块添加、界面设计,以及以登录窗体为例的详细设计思路。已有441人学习,适合参照完整目录结构、UML建模与数据库设计写法进行查漏补缺。
1. 一份软件工程文档,抵得上半个可运行项目
拿到「图书管理系统 软件工程」这套资料时,我先翻的不是代码目录,而是目录页——需求分析、用例图、时序图、类图、E-R 图、建表清单、窗体设计思路,一路排到第 5 章的系统实现。很多人下载这类资源是奔着一个能双击运行的 exe 去的,打开发现主体是文档就关掉了;真做过交付的都清楚,需求边界和表结构只要定错一次,后面写多少代码都是给返工铺路。这套资料的定位很清楚:把一个中小型 MIS 从选题背景、技术选型、需求定义,一路推到建表和窗体交互,技术底座是 Visual Basic 6.0 + SQL Server 2000 + ADO,配 Rational ROSE 做 UML 建模。它适合两类人:一类要把完整设计文档和可运行代码一起交出去,另一类想看看老派两层 C/S 系统在真实约束下是怎么被拆开的。下面按「先看架构、再落到表、最后进窗体」的顺序拆。
2. 架构与建模:VB6 + SQL Server 2000 的两层 C/S 骨架
这套系统的技术选型放在今天看很复古,但它把「小团队、短周期、单人能交付」这几个约束解决得很干净。理解它的架构,不能只看文档里那张三层架构图,得看代码实际怎么流动。
2.1 三层架构在这份文档里的真实落地形态
文档把系统描述成 Presentation Layer、Application Layer、Data Layer 三层。作为设计意图这没问题,但落到 VB6 工程里,它往往是「两层半」:窗体既画界面又接事件,业务判断直接写在按钮的 Click 里,ADO 连接对象挂在全局模块上。这不是作者偷懒,而是 VB6 的工程模型决定的——标准 EXE 里最自然的组织方式是「窗体 + 标准模块 + 类模块」,没有一个容器帮你托管业务层。
我一般会把它强行掰成这样一份工程结构,让三层至少名义上对得上:
| 工程对象 | 类型 | 职责 | 典型内容 |
|---|---|---|---|
| frmLogin | 窗体 | 身份识别 | 用户名/密码输入、角色分流 |
| frmMain | 窗体 | 主框架与导航 | 菜单、状态栏、按角色隐藏入口 |
| frmBook | 窗体 | 图书维护 | 增删改查、分类筛选 |
| frmReader | 窗体 | 读者维护 | 注册、注销、信息变更 |
| frmBorrow / frmReturn | 窗体 | 借还书 | 借阅登记、归还与超期提示 |
| modConn | 标准模块 | 数据层入口 | 全局 Connection、打开/关闭、错误捕获 |
| modBiz | 标准模块 | 业务层 | 借阅规则校验、超期计算、库存变更 |
| clsBook / clsReader | 类模块 | 领域对象 | 字段封装 + 落库方法 |
这样拆之后,窗体的 Click 事件只做三件事:从控件取值、调modBiz里的函数、按返回值弹提示。业务规则改一次只改一处,比把If 已借数量 >= 上限 Then抄在每个借书按钮里强得多。常见做法是在modBiz里用Function返回一个布尔值加ErrMsg输出参数,窗体负责展示,逻辑不回头依赖界面。
提示:VB6 没有命名空间,模块名就是你的命名空间。
modBiz.BorrowBook这种调用方式虽然丑,但它让「哪一层在干活」一眼可辨,后期接手的人会感谢你。
2.2 用例驱动:三类参与者与九条主用例
文档的需求部分是用例驱动的,先把参与者钉死,再往下画场景。三类参与者分别是读者、图书管理员、系统管理员,这三分法在图书业务里是标准切法:读者只碰自己的数据,管理员碰借还动作,系统管理员碰基础数据。
| 参与者 | 主用例 | 触发场景 |
|---|---|---|
| 读者 | 登录、查个人信息、查图书、借书、还书、续借 | 到馆或远程查询,自助发起借阅 |
| 图书管理员 | 借书处理、还书处理 | 柜台核验后确认动作 |
| 系统管理员 | 增删改图书、增删改读者、系统状态维护 | 新书入库、读者注销、数据订正 |
这套划分的价值在于权限矩阵可以直接从用例推导出来:frmMain启动时按登录返回的角色把菜单项Visible置 False,而不是在每个窗体内部再判断一次。我见过不少同类工程把权限判断散在二十几个按钮里,改一次规则要全局搜索,那就是纯粹的体力活。
需求定义部分还有两条容易被忽略的非功能约束:一是「能按书名、编号、作者多方式查询」,这决定了book表上要建组合索引而不是只靠主键;二是「超期自动提示」,这决定了系统在登录或进入主界面时必须跑一次日期比对,而不是等用户点进借阅列表才算。
2.3 从用例到时序图、类图:需求怎么翻译成对象
文档给了一张「系统管理员添加图书」的时序图,三条消息:add item()添加、find(string)按书名查重、create(integer, title)落库。这三步的顺序很讲究——先查重再创建,避免同一本书被重复登记成两条书目记录。很多新手直接调create,结果书库里同一本书出现七八条,后续借阅统计全乱套。
类图部分列了六个核心类:item(书目)、title(书名)、loan(借出)、reader(读者)、librarian(图书管理员)、administrator(系统管理员)。这里item和title的分离是整张类图里最有含量的设计——它对应图书馆学里的「书目-册」模型:title描述一种书(ISBN、作者、出版社),item描述馆藏的具体一册(条码号、入库日期、状态)。一本书买三本,就是一个title挂三个item。
类之间的关系标注是1对n:一个title对应多本item,一个reader对应多条loan。把这个关系映射到表上,就是book表用bannercode(图书编号)做实体标识、用bookname做逻辑归并,borrow表用user_cardno关联读者。文档里画的是标准 UML,但落到 SQL 时主键选错,1:n 就会退化成 1:1,这是后面避坑章节要重点说的事。
阅读类图时还有个细节:reader类的方法里同时有「读者信息查询」「图书信息查询」「借阅信息查询」,这三个查询的返回结构完全不同,硬塞进一个类会让类迅速膨胀。我的做法是把查询拆到modBiz的独立函数里,reader类只管自己的属性,符合单一职责,也让后面换数据源时改动面更小。
3. 数据库设计:8 张表、E-R 图与 ADO 连接层
数据库这块是整份资料里最值得逐字读的部分,因为表结构一旦定稿,窗体和代码都是围着它转的。文档从 E-R 图讲到字段清单,再到连接代码,链路是完整的,但中间有几处需要动手修正才跑得通。
3.1 表清单与字段设计:文档说 8 张,实际列出 7 张
文档原文写「需建立 8 个表」,但往下逐个列出来的只有 7 张:book(书籍)、borrow(借书)、login(登录)、punishment(罚款)、user(读者信息)、libraryer(图书管理员)、admin(系统管理员)。少的那张大概率是「图书分类表」——因为book表里已经有kindno(分类号)和kindname(分类名)两个字段,功能说明里也提到「书籍类别标准的制定,类别编号、类别名称、关键词、备注」,这就是一张独立的分类表该管的事。
这正是我建议动手补的第一件事:把分类从book表里抽出去。原因是分类名会被大量重复存储,改一次分类名要UPDATE上千行,而且字段长度只有 20,稍微长一点的分类名就截断了。
| 表名 | 主键(文档原样) | 关键字段 | 我建议的修正 |
|---|---|---|---|
| book | bookname | bannercode、kindno、author、price、state | 主键改 bannercode,bookname 降为普通索引 |
| borrow | user_cardno | bannercode、borrowdate、receivedate、renew | 主键改自增 ID 或 (user_cardno, bannercode) 联合主键 |
| user | user_cardnumber | user_name、user_sex、user_status、user_state | 保持,补唯一约束 |
| login | user_name | psw | 补角色字段,或与三张角色表分建 |
| punishment | user_cardnumber | sum_account | 补罚款明细字段与产生时间 |
| libraryer | libr_name | libr_sex、libr_address | 姓名做主键不稳,建议加工号 |
| admin | Adminname | Admin_sex、Adminaddress | 同上 |
表格里最扎眼的是book表拿书名当主键。书名重复在真实书库里是常态——同一本书三个出版社出三个版本,书名一模一样。主键冲突会让第二条记录根本插不进去,而这个错误在录入三五条测试数据时完全暴露不出来,等数据攒到几百条才发现,那时候改主键要动外键、动窗体、动所有查询语句。
3.2 建表 SQL:主键、唯一约束与状态字段怎么定
下面这段是我按文档字段清单重写过的建表语句,保留了原字段名和长度,只调整了约束。用 SQL Server 语法,直接在查询分析器里能跑。
-- 图书分类表:文档漏列的第 8 张表 CREATE TABLE category ( kindno VARCHAR(20) NOT NULL PRIMARY KEY, -- 分类号 kindname VARCHAR(40) NOT NULL, -- 分类名,长度从 20 放宽到 40 keywords VARCHAR(50) NULL, -- 检索关键词 remark VARCHAR(100) NULL ); -- 书籍表:主键从书名改为图书编号 CREATE TABLE book ( bannercode VARCHAR(30) NOT NULL PRIMARY KEY, -- 图书编号,唯一标识一册 bookname VARCHAR(60) NOT NULL, -- 书名,允许重名 kindno VARCHAR(20) NULL, -- 分类号,外键指向 category author VARCHAR(30) NULL, publishing VARCHAR(50) NULL, -- 出版社 publishtime DATETIME NULL, -- 出版日期 putintime DATETIME NULL, -- 入库日期 price DECIMAL(10,2) NULL, state VARCHAR(4) NOT NULL DEFAULT '0', -- 0 在架 1 借出 2 遗失 introduction VARCHAR(200) NULL, CONSTRAINT fk_book_kind FOREIGN KEY (kindno) REFERENCES category(kindno) ); -- 借书表:一次借阅一条记录,联合主键防重复 CREATE TABLE borrow ( borrowid INT IDENTITY(1,1) PRIMARY KEY, -- 自增流水,替代原 user_cardno 单主键 user_cardno VARCHAR(20) NOT NULL, -- 读者证件号 bannercode VARCHAR(30) NOT NULL, -- 图书编号 borrowdate DATETIME NOT NULL, receivedate DATETIME NULL, -- 归还日期,NULL 表示未还 renew VARCHAR(2) NOT NULL DEFAULT '0', -- 是否续借 CONSTRAINT uk_borrow UNIQUE (user_cardno, bannercode, borrowdate) );建表部分有几个参数必须说清楚。state字段默认值给'0',是为了让新入库的书天然处于「在架」状态,省掉插数据时漏填的问题;receivedate允许为 NULL,这是判断「是否已归还」的唯一依据,比再单开一个isreturned字段可靠得多——两个字段描述同一件事,早晚不一致。borrow表加borrowid自增列是因为原设计拿证件号当主键,一个读者借第二本书时就会主键冲突,这个坑后面还会展开。
bookname从VARCHAR(30)放宽到 60,是踩过坑的:书名里带副标题、丛书名的情况太多,30 个字符在中文里只有 15 个字,一本《XXX原理与实现(第三版)》直接超长,ADO 这时候不报错,它会静默截断,你查回来的书名少了半截,还以为是编码问题。
3.3 ADO 连接类的拆解与 VB6 版本重写
文档 3.4 节给的连接代码是一段 C# 的ConnectionClass,用了System.Data.SqlClient、SqlDataReader、StreamReader读F://conn.txt。这段代码本身没大问题,但它和前面定的 VB6 + ADO 技术栈不是一套东西——C# 的SqlClient是 .NET 的提供程序,VB6 里既没有System.Data命名空间,也引用不了这个类库。这就是这份资料最需要动手改的地方:不能直接抄,得按 ADO 对象模型重写。
VB6 的等价实现如下,放在标准模块modConn.bas里:
' modConn.bas —— VB6 的 ADO 连接与数据读取 Option Explicit Public gConn As ADODB.Connection ' 全局唯一连接对象 Public Function OpenDB() As Boolean On Error GoTo ErrHandler If gConn Is Nothing Then Set gConn = New ADODB.Connection ' Provider 指定 OLE DB 提供程序;Data Source 用 . 表示本机实例 gConn.ConnectionString = "Provider=SQLOLEDB;Data Source=.;" & _ "Initial Catalog=LibraryDB;User ID=sa;Password=your_pwd;" gConn.ConnectionTimeout = 10 ' 连接超时,单位秒 gConn.CommandTimeout = 30 ' 命令执行超时 End If If gConn.State = adStateClosed Then gConn.Open OpenDB = True Exit Function ErrHandler: MsgBox "数据库连接失败:" & Err.Description, vbCritical, "提示" OpenDB = False End Function ' 返回只进只读记录集,调用方负责关闭 Public Function GetReader(ByVal sql As String) As ADODB.Recordset Dim rs As New ADODB.Recordset If Not OpenDB() Then Exit Function rs.CursorLocation = adUseClient ' 客户端游标,支持 RecordCount rs.Open sql, gConn, adOpenStatic, adLockReadOnly Set GetReader = rs End Function参数说明:Provider=SQLOLEDB是老版本 SQL Server 的标准 OLE DB 提供程序,不要换成 ODBC 的MSDASQL,那条路需要额外配 DSN,部署时多一个故障点。Data Source=.表示本机默认实例,如果实例名是SQLEXPRESS就得写成.\SQLEXPRESS。CursorLocation设成adUseClient之后RecordCount才有意义,服务端游标默认返回 -1,这个差异足够让新手懵半天。
原文从F://conn.txt读连接串的做法我一般不推荐照搬:绝对路径写死盘符,换台机器就崩,而且连接串里带明文密码,放在文本文件里等于不设防。稍好一点的做法是写注册表SaveSetting/GetSetting,或者干脆构建一个conn.ini放在 exe 同目录用App.Path拼路径。不管选哪种,都比把F://硬编码强。
4. VB6 工程实现:从新建工程到登录窗体跑通
文档第 4、5 章讲系统实现,篇幅不长,但把「建工程、加模块、加类模块、画界面」这条动线走了一遍。这部分对没碰过 VB6 的人来说是最省时间的,因为它替你踩了工程组织的坑。
4.1 工程与模块的创建顺序
正确的顺序是先定引用、再建模块、最后画窗体。顺序反了,你会在一堆窗体里到处补ADODB的类型声明。
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 新建「标准 EXE」工程 | 生成Project1.vbp和默认的Form1 |
| 2 | 工程-引用,勾选 Microsoft ActiveX Data Objects 2.8 Library | 不勾这一步,ADODB.Connection直接编译报错 |
| 3 | 添加标准模块modConn | 放全局连接和通用查询函数 |
| 4 | 添加标准模块modBiz | 放借阅校验、超期计算等业务函数 |
| 5 | 添加类模块clsBook、clsReader | 封装领域对象的属性和落库方法 |
| 6 | 逐个添加窗体并改名 | 命名前缀统一 frm,控件前缀统一 cmd/txt/lst |
第 2 步是最常见的翻车点。VB6 的「引用」对话框里 ADO 版本有好几个,2.5、2.6、2.8 并存,随便勾一个也能编译,但如果开发机和部署机勾的版本不一致,打包后在客户机上会报「找不到指定的 DLL」。稳妥做法是勾一个版本后把.vbp文件里对应的 GUID 记下来,部署环境照抄。
4.2 登录窗体的详细设计思路
文档拿登录窗体当详细设计的范例,这个选择是对的——它麻雀虽小,把「取输入、查库、判分支、进主界面」四件事全占了。
界面只需要两个 TextBox(txtUser、txtPass)+ 两个按钮(cmdLogin、cmdCancel)。txtPass的PasswordChar设成*,防止旁人瞄到密码。真正的内容在按钮事件里,这里我用ADODB.Command加参数,而不是把用户输入拼进 SQL 字符串:
' frmLogin.cmdLogin_Click —— 参数化校验,避免引号注入与拼串错误 Private Sub cmdLogin_Click() Dim cmd As ADODB.Command Dim rs As ADODB.Recordset Dim role As String If Trim(txtUser.Text) = "" Or Trim(txtPass.Text) = "" Then MsgBox "用户名和密码都不能为空", vbExclamation, "提示" Exit Sub End If If Not OpenDB() Then Exit Sub Set cmd = New ADODB.Command Set cmd.ActiveConnection = gConn cmd.CommandType = adCmdText cmd.CommandText = "SELECT user_name, psw FROM login WHERE user_name = ?" ' 长度 20 对应 login.user_name 的 VARCHAR(10) 留出余量 cmd.Parameters.Append cmd.CreateParameter("u", adVarWChar, adParamInput, 20, Trim(txtUser.Text)) Set rs = cmd.Execute If rs.EOF Then MsgBox "用户不存在", vbExclamation, "提示" ElseIf rs.Fields("psw").Value <> txtPass.Text Then MsgBox "密码错误", vbExclamation, "提示" Else role = GetRole(Trim(txtUser.Text)) ' 查角色,决定主界面菜单可见性 Unload Me frmMain.Show frmMain.ApplyRole role End If rs.Close Set rs = Nothing Set cmd = Nothing End Sub这段代码里CreateParameter的四个关键参数是名称、类型、方向、长度。adVarWChar对应 Unicode 变长字符串,配 SQLOLEDB 没问题;如果误写成adVarChar,中文用户名会变成问号。参数长度 20 是照着login.user_name的字段长度给的,给短了会截断,给长了部分提供程序会报错。
登录成功后不直接frmMain.Show,而是先Unload Me再显示主窗体,是为了让主窗体成为唯一的活动窗体,避免用户在任务栏里切回一个已经没什么用的登录框。ApplyRole是主窗体的公开方法,按角色隐藏「系统维护」菜单,把权限收敛在一个入口里。
4.3 借还书的核心事件与事务边界
借书动作要同时改两张表:往borrow插一条借阅记录,把book.state从 0 改成 1。这两步必须在一个事务里,否则第一步成功第二步失败,书在架状态和借阅记录就对不上,后面盘点怎么都对不平。
' modBiz.BorrowBook —— 借书业务,返回是否成功 Public Function BorrowBook(ByVal cardno As String, ByVal barcode As String, ByRef msg As String) As Boolean On Error GoTo Rollback ' 前置校验:书必须在架,读者借阅数不能超上限 If GetBookState(barcode) <> "0" Then msg = "该书当前不可借" Exit Function End If If GetBorrowCount(cardno) >= 5 Then ' 上限 5 本,按馆规调整 msg = "已达到最大借阅数量" Exit Function End If gConn.BeginTrans gConn.Execute "INSERT INTO borrow(user_cardno, bannercode, borrowdate, renew) " & _ "VALUES('" & cardno & "','" & barcode & "',GETDATE(),'0')" gConn.Execute "UPDATE book SET state = '1' WHERE bannercode = '" & barcode & "' AND state = '0'" gConn.CommitTrans BorrowBook = True Exit Function Rollback: gConn.RollbackTrans msg = "借书失败:" & Err.Description BorrowBook = False End FunctionBeginTrans / CommitTrans / RollbackTrans三个调用构成 VB6 里最朴素的事务边界。UPDATE语句末尾的AND state = '0'是个小技巧:并发场景下两个人同时借同一本书,第二个人影响行数是 0,虽然这里没检查影响行数,但至少不会把一条已借出的记录再改一遍。真实一点的做法是拿gConn.Execute的RecordsAffected判断,为 0 就主动RollbackTrans。
还书逻辑对称:插receivedate、把state改回'0',再顺手算一下receivedate - borrowdate是否超过借期(比如 30 天),超了就往punishment表累加罚款。超期提示不要放在还书时才算,登录进主界面时就应该扫一遍当前读者的未还记录,DateDiff("d", borrowdate, Date) > 30的挑出来弹窗提示,这才是文档里「超期自动提示」的正确实现位置。
5. 避坑与常见问题排查:文档和代码对不上的五处
这份资料的质量在同类文档里算上乘,但它终究是拼装产物,有几处明显是从别的项目里带过来的。下面五条是我照着复现时真金白银踩出来的,按严重程度排。
5.1 表数量、主键与借阅记录的三处硬伤
第一处,文档写「建立 8 个表」,实际列了 7 张,缺的正是分类表。现象是照着文档建完库,book表里的kindno无处可关联,按分类筛选只能靠kindname做字符串匹配。原因是作者把分类信息合并进了书籍表以简化设计,但文字部分忘了同步。解决就是补一张category表,book.kindno加外键指向它,把重复的分类名收敛成一行。
第二处,book表把bookname标成了主键,bannercode只给唯一约束。现象是录入同名书的第二个版本时,INSERT 直接报主键冲突,而 SQL Server 的报错信息只说「违反 PRIMARY KEY 约束」,新手根本想不到是书名重了。原因是把业务标识和实体标识混为一谈。解决是把主键换成bannercode,bookname建普通非唯一索引供检索——这一步要改所有引用旧主键的查询,建议一开始就改,别等数据多了再动。
第三处,borrow表拿user_cardno当主键。现象是同一读者借第二本书时插入失败,系统看起来像「每人只能借一本」。原因是一对多关系被压成了一对一。解决是加自增borrowid做主键,把(user_cardno, bannercode, borrowdate)做成唯一约束防止重复登记同一笔借阅。
5.2 数据库连接代码语言不一致,抄了跑不通
文档 3.4 节那段ConnectionClass是 C# 写的,用了System.Data.SqlClient、SqlDataReader、StreamReader。现象是把这段代码原封不动放进 VB6 工程,编译直接报「用户定义类型未定义」,因为SqlConnection这个类型在 VB6 里根本不存在。原因是这套文档的素材来自不同项目,数据库章节混进了一份 .NET 的连接示例。解决是按第 3.3 节给出的 VB6 + ADO 版本重写,Connection对象换成ADODB.Connection,SqlDataReader换成ADODB.Recordset,读配置文件用Open ... For Input而不是StreamReader。
还有个小坑藏在同一段代码里:原文从F://conn.txt读连接串。现象是换台机器运行就报「文件未找到」,即使手动建了F:盘也可能因为没有写权限失败。原因是绝对路径加硬编码盘符。解决是用App.Path & "\conn.ini"拼相对路径,或者用GetSetting存注册表,别让部署环境依赖某个固定盘符。
5.3 需求章节里混进了不相干的功能描述
文档 2.4 节的功能说明,开头第一条写的是「选修课基本情况的添加,修改,删除」,还单独列了「学生选修模块」,讲新读者注册、老读者信息更改、注销。现象是照着这份需求做功能清单,会多出两个跟图书业务毫无关系的模块,评审的时候被人问「这个系统到底是管书的还是管选课的」。原因是段落从另一个项目的需求文档里复制过来没删干净,后面「新读者的注册」那几条其实是对的,只是标题挂错了。解决是把「选修课」和「学生选修模块」两个标题删掉,保留下面的注册/变更/注销三条,归到「读者信息管理」下面。
这类问题的危害不在代码层面,而在交付层面:文档是要给人看的,一处明显的复制痕迹会让人怀疑整份文档的严谨度。我一般会在交付前做一次全文搜索,把「学生」「课程」「成绩」「选修」这类词筛一遍,十有八九能揪出残留。
5.4 运行环境的老版本依赖
VB6 编译出来的 exe 不是绿色程序,它依赖MSVBVM60.DLL和一堆 ActiveX 控件。现象是在开发机上跑得好好的,拷到另一台机器双击没反应,或者弹「部件 'MSADODC.OCX' 或其依存项未正确注册」。原因是目标机没装 VB6 运行时,也没注册 ADO 控件。解决是用 VB6 自带的「打包和展开向导」生成安装包,它会把运行库和依赖控件一起打进去并在安装时注册;实在要绿色部署,就得手工确认MSVBVM60.DLL、MSADODC.OCX、MSDATGRD.OCX都在系统目录且已regsvr32。
SQL Server 2000 那一侧也有版本问题:新系统上装不了,需要兼容模式或换版本。这一条不是代码能解决的,只能提前在部署文档里写清楚环境要求,别让接手的人自己去猜。
6. 二次开发:把这个底座改成能拿得出手的样子
这份资料的原始形态是「能讲清楚、能跑起来」,但离「能维护」还差几步。如果你的目的是拿它练手或者填进作品集,我建议按下面三个方向改,改完这套东西的含金量会明显不一样。
第一步是把字符串拼接的 SQL 全部换成参数化。第 4 章登录那段已经示范了ADODB.Command加CreateParameter的写法,剩下的查询照着改就行。这件事的意义不只是防注入——拼串的时候但凡字段里出现一个单引号,整条语句就语法错误,而报错信息通常只丢给你一个「附近有语法错误」,查半天查不到源头,这是纯粹的玄学。
第二步是把借还书的业务规则从窗体里抽干净。判断逻辑都放进modBiz,窗体只负责收集参数和展示msg。抽完之后加一条规则只改一个函数,测试也简单——modBiz里的函数不依赖窗体,可以单独写一个测试窗体循环调用。我一般会顺手把借阅上限、借期天数、罚款单价做成Const或者一张config表,别散在代码里。
第三步,如果你想让它看起来更现代一点,可以在不换技术栈的前提下加两个视图:
-- 超期未还视图:主界面加载时直接查这张视图 CREATE VIEW v_overdue AS SELECT b.user_cardno, u.user_name, b.bannercode, bk.bookname, b.borrowdate, DATEDIFF(day, b.borrowdate, GETDATE()) AS overdue_days FROM borrow b JOIN user u ON u.user_cardnumber = b.user_cardno JOIN book bk ON bk.bannercode = b.bannercode WHERE b.receivedate IS NULL AND DATEDIFF(day, b.borrowdate, GETDATE()) > 30;视图把三表关联和日期计算封在数据库侧,VB6 那边一句SELECT * FROM v_overdue就完事,窗体代码能少三十行。同理可以再建一张「在架图书视图」,state = '0'的过滤条件就不用每个查询都写一遍了。
改完怎么验证?我的顺序是:先用category、book、user各插十条数据,跑一遍增删改查;再借五本书触发上限拦截;再手工把一条borrowdate改成 40 天前,重新登录看超期提示弹不弹;最后拔掉网线启动程序,看连接失败的错误提示是不是人话。这四步过了,基本就能交付了。
血泪经验是:这份资料最容易被低估的部分恰恰是前面那几章建模图。我最初也是直奔第 5 章照着画窗体,结果画到借书界面发现借阅表主键不对,回头改表、改查询、改控件绑定,等于重做一遍。从那以后我每次拿到这类文档,都先把表结构对着用例逐条过一遍,确认主键、外键、状态字段都立得住,再动手敲第一行窗体代码。希望帮到你。
本文还有配套的精品资源,点击获取