简介面向数据库系统设计课程的学校图书借阅管理系统课程设计报告适合计算机及相关专业学生完成图书管理类项目时参考。报告覆盖从需求分析到详细设计的完整流程包含数据字典、数据流图、结构图与E-R图并给出欢迎界面、读者/管理员登录、图书录入与修改、读者注册与信息维护、图书查询、借还书、数据备份恢复、系统管理等功能模块的设计说明与核心代码实现可直接用于课程作业或毕业设计的方案借鉴。资源共1个doc文档压缩包大小4.16MB内容组织清晰翻阅方便。目前已有11180人学习下载兼具实用性与参考价值。1. 学校图书借阅管理系统课程设计数据库系统设计如果只看界面截图很多人会以为这门数据库系统设计的难点在登录表单和按钮排版真正决定成品能用多久的是那几张数据表的结构。这份基于 VFP 的《学校图书借阅管理系统》课程设计报告把图书管理、读者管理、图书服务、数据安全、系统管理五个板块串成完整闭环读者登录三次失败自动退出管理员接管全部权限图书查询支持书名、作者、出版社三种方式。无论你是拿它交课程设计报告还是想把框架改造成医院、超市、校园一卡通之类的小系统都可以从数据字典和 main.prg 入手。适合正在赶大学课程设计的人也适合刚摸 VFP 想搞清楚事件循环怎么起步的新手。2. 数据字典拆解从五张表的字段宽度看懂这套系统的库表设计2.1 五张表字段逐一对照这份报告的数据字典一共给了五张表Booksinfo图书信息、Dairy登录日志、Lendinfo借阅记录、passwordinfo管理员账号、Readeruinfo读者信息。在 VFP 里字符型用 C 表示数值型用 N日期型固定占 8 位逻辑型固定占 1 位备注型不需要指定宽度。很多大学课程设计的数据字典都是直接从报告模板里复制粘贴的这份整体保留得还算完整但字段清单里存在几处明显的复制残留建库之前务必先理清。图书信息表 Booksinfo 的字段设计如下字段名类型宽度小数位设计说明编号C10-图书内部编号适合做主键书号C28-对应真实图书的 ISBN 或馆藏书号书名C40-文本输入框 MaxLength 必须同步设 40作者C20--出版社C15--定价N60原数据字典写的是“60”按 N(6,0) 理解册数N30库存总量借出后扣减图书种类C18-文学、科技、历史等分类入库日期D8--备注M--不需要指定宽度这里要提醒一句字段宽度不是随便抄的。书名给 40 位意味着录入表单的文本框必须设置 MaxLength40否则用户在界面上输入 50 个字的书名保存时要么被截断要么直接报字段溢出。VFP 的老式 DBF 表结构对长度非常敏感这就是为什么课程设计第一步永远是先对着数据字典建表而不是先画表单。其余四张表的字段相对少但同样有值得注意的地方表名关键字段类型 / 宽度注意点Dairy用户名 C(10)、登录时间 D(8)、离开时间 D(8)-只记录一次登录会话的起止时间Lendinfo读者编号 C(6)、书号 C(28)、借阅日期 D、归还日期 D借阅天数 C(8)、是否延期 L读者编号宽度与读者表不一致见避坑章passwordinfo用户名 C(8)、密码 C(7)、册数 N(30)-“册数”是从图书表复制错的残留字段Readeruinfo读者编号 C(10)、密码 C(10)、姓名 C(6)、性别 C(6)、年龄 C(2)是否允许借 L是否允许借是借书总开关Lendinfo 是整张库的核心。它的读者编号、书号、借阅日期、归还日期、借阅情况这五个字段支撑了“谁借了什么书、什么时候借的、还了没有”所有查询。借阅情况字段建议固定值域在借、已还、逾期不要允许自由填写否则统计报表会变成一团乱麻。passwordinfo 表里的“册数 N(30)”明显是从 Booksinfo 复制过来的残留字段如果直接照抄建表后面 Grid 绑定表时就会多出一列毫无意义的“册数”。2.2 建表语句与主键设计我在复现时一般先把 library.dbc 建出来再按下面的 SQL 建表这样做的好处是主键、索引可以在建表时一次完成不用后期用 ALTER TABLE 补丁式修改。下面是可直接运行的 VFP 建表语句OPEN DATABASE library.dbc CREATE TABLE bookinfo (; 编号 C(10) PRIMARY KEY, ; 书号 C(28), ; 书名 C(40), ; 作者 C(20), ; 出版社 C(15), ; 定价 N(6,0), ; 册数 N(3,0), ; 图书种类 C(18), ; 入库日期 D, ; 备注 M) CREATE TABLE readeruinfo (; 读者编号 C(10) PRIMARY KEY, ; 密码 C(10), ; 姓名 C(6), ; 性别 C(6), ; 年龄 C(2), ; 详细住址 C(28), ; 联系电话 C(11), ; 注册日期 D, ; 是否允许借 L, ; 备注 M) CREATE TABLE passwordinfo (; 用户名 C(8) PRIMARY KEY, ; 密码 C(7), ; 备注 M)注意正文的表结构中Lendinfo 的读者编号只给了 C(6)而 Readeruinfo 的读者编号是 C(10)这两个字段在逻辑上必须能关联长度不一致是隐患。为了忠实还原原始设计下面的建表语句先按原文写 C(6)实际开发时请统一改成 C(10)。借阅天数原文是字符型 C(8)建议换成数值型 N(3,0)这样统计逾期天数时可以直接做算术运算不用先 VAL() 转换。是否延期是逻辑型 L(1)建表语句里直接写 L 即可。CREATE TABLE lendinfo (; 借阅日期 D, ; 读者编号 C(6), ; 书号 C(28), ; 预约日期 D, ; 借阅天数 C(8), ; 归还日期 D, ; 借阅情况 C(8), ; 是否延期 L, ; 备注 M) CREATE TABLE dairy (; 用户名 C(10), ; 登录时间 D, ; 离开时间 D, ; 备注 M)主键方面Booksinfo 的编号和 Readeruinfo 的读者编号都建议设为主键。Lendinfo 没有单一主键它靠“借阅日期 读者编号 书号”共同决定一条记录的唯一性若同一读者同一天重复借同一本书这种设计会允许两条重复数据更稳妥的做法是给 Lendinfo 增加一个自增流水号字段或者在借阅时先查重。Dairy 表不用主键它是日志表只要保证追加顺序即可。2.3 关键关系借阅记录表就是读者和图书之间的连接表从 E-R 图角度理解这套系统核心关系并不复杂Readeruinfo 和 Booksinfo 之间是多对多关系一个读者可以借多本图书一本图书可以被多个读者借阅多对多关系拆出来就是 Lendinfo 这张借阅记录表。Lendinfo 只存读者编号和书号不冗余存书名、作者、出版社需要展示完整信息时再去关联两张大表。Dairy 表的作用是登录审计它只记录用户名、登录时间、离开时间。它的缺点是没区分读者还是管理员查询“谁在什么时候登录过”时用户名可能同时出现在两张账号表里。如果想让审计更完整建议给 Dairy 增加一个“用户类型”字段值为读者或管理员。另外注意“是否允许借”这个逻辑字段放在 Readeruinfo 表里是控制读者能不能借书的总开关。读者有逾期未还的图书时管理员可以将该字段设为 .F.借书按钮再根据这个字段决定是否放行。这个字段在登录代码里没被检查但借书表单必须检查否则就是个摆设。3. 主程序与登录权限main.prg、封面、三次错误锁定3.1 main.prg 启动顺序主程序 main.prg 是这套系统的入口也是课程设计报告里最值得逐行读的代码。它的执行顺序决定了整个程序从启动到进入主菜单的流程。原文代码如下set talk off set safe off set sysmenu off close all * 系统环境设计 release windows 常用,项目管理器,表单控件,布局,命令 modify window screen title 迷你图书管理系统 zoom window screen max * 屏幕和标题设置 set defa to left(sys(16),rat(\,sys(16))) * 设置路径 set path to data;form;pic;class * 系统环境设计 open database library _screen.visible.f. _screen.icon.\icon_love_11.ico public cyonghu,cmima,jiage,checked checked1 cyonghu cmima do form start * 执行表单开始进入系统 read events _screen.visible.t. _screen.picturelib.gif do form 入口 read events do menu_main.mpr read events quit第一段是环境清理set talk off 关闭命令回显set safe off 关闭覆盖确认弹窗set sysmenu off 隐藏 VFP 自带的系统菜单close all 关闭所有打开的表和数据库。这些是 VFP 程序的标准开头主要目的是避免调试界面弹出各种 VFP 原生对话框。最考验新手的是set defa to left(sys(16),rat(\,sys(16)))这行。sys(16) 返回当前正在执行文件的完整路径rat 从字符串右侧找第一个反斜杠left 截取到该位置结果就是把默认目录切到程序所在的文件夹。这样无论从哪个目录启动程序都不会出现“文件不存在”的报错。紧接着的set path to data;form;pic;class告诉 VFP 到这四个子目录去查找表单、图标和类库。接下来open database library打开 library.dbc_screen.visible.f.隐藏 VFP 主窗口让程序看起来像一个独立软件。public cyonghu,cmima,jiage,checked 声明四个全局变量cyonghu 保存当前登录用户的编号cmima 保存密码jiage 用于记录当前选中的图书价格checked 是权限标志位。后面所有表单判断“当前是读者还是管理员”时都会引用 checked 变量。3.2 封面与入口表单主程序执行do form start进入封面表单 start.scx。封面上的版权滚动文字一般由计时器控件驱动原文的计时器代码是IF thisform.label5.left thisform.width thisform.label5.left thisform.label5.left 1 ELSE thisform.label5.left -110 ENDIF这段代码的作用是让 label5 每次计时器触发向右移动 1 像素超出表单宽度后重置到 -110形成文字从左向右滚动再消失的循环。封面点击后进入入口表单入口表单只有两个按钮读者入口执行do form dzdl管理员入口执行do form logo点击按钮后各自 release 当前入口表单。入口本身不做权限校验只是路由。这里要注意事件循环的配合。main.prg 里出现三段 read events每段 read events 都必须由对应的 clear events 退出。登录成功后的按钮代码通常是thisform.release加clear events这两行缺一不可release 关闭当前表单clear events 让 VFP 退出当前事件循环才能继续执行主程序下一行命令。如果只 release 不 clear events程序就会停在 read events 里表现出“点完按钮没反应”的假死状态。3.3 读者登录逻辑与三次锁定读者登录表单的登录按钮 click 代码是这套系统里逻辑最完整的部分原文如下sele readerinfo if n3 if allt(thisform.text1.value) thisform.text1.setfocus wait window at 17.5,65 用户名为空请输入 else if allt(thisform.text2.value) thisform.text2.setfocus wait window at 20,65 密码为空请输入 else locate for allt(读者编号)allt(thisform.text1.value) if found() if allt(密码)allt(thisform.text2.value) thisform.release clear events else thisform.text2.value thisform.text2.setfocus wait window at 20,65 密码错误请重新输入 nn1 endif else thisform.text1.value thisform.text2.value thisform.text1.setfocus wait window at 17.5,65 没有此用户,请重新输入 nn1 endif endif endif else n1 thisform.text1.enabled.f. thisform.text2.enabled.f. thisform.command1.enabled.f. thisform.label1.caption登录已错误三次系统即将自动退出 endif这段代码的核心是 n 计数器。n 在表单的 init 事件里被初始化为 1每输错一次密码或查无此人就 nn1。当 n 达到 3 时if n3 不成立进入 else 分支禁用用户名框、密码框和登录按钮并把提示标签改成“登录已错误三次系统即将自动退出”。这是一种针对暴力尝试的简单防护。几个细节值得注意locate for 是全表顺序查找不是索引查找当 readerinfo 表数据量超过几千条时会有明显卡顿更高效的写法是SELECT * FROM readeruinfo WHERE 读者编号ALLTRIM(thisform.text1.value) INTO CURSOR temp。allt() 函数的作用是去掉字符串两端空格因为 VFP 的字符型字段保存后会用空格补齐宽度不 trim 就比对会失败。密码比对用的是精确比较如果把等号写成单等号会变成赋值而不是比较结果就是无论密码对不对都判断成功。管理员登录的表单代码结构和读者登录基本一致只是把查询表换成 passwordinfo字段换成用户名和密码。管理员进入系统后 checked 变量被置为管理员状态读者登录后 checked 保持普通用户状态后续菜单和按钮就靠这个变量控制显隐和可用性。4. 图书管理、读者管理、图书服务、数据安全五大功能模块的落地做法4.1 功能结构、菜单与权限控制这套系统的功能模块延续了课程设计报告的典型结构图书管理、读者管理、图书服务、数据安全、系统管理五大板块。主程序在第二次 read events 之后执行do menu_main.mpr也就是把菜单程序挂载到主窗口。菜单的组织方式很简单一级菜单对应五大板块二级菜单对应录入、修改、查询、借书、还书、备份、恢复等具体操作。权限控制在菜单层就可以做不必每个表单单独判断。常见做法是给菜单项设置 SKIP FOR 条件例如“图书录入”菜单项在 checked 不等于管理员时灰显写法是SKIP FOR checked ! 1。这样读者登录后能看到的菜单只有图书查询和借书还书管理员登录后才显示录入、修改、备份、恢复等菜单项。正文中 checked 变量在 main.prg 里被初始化为 1读者登录成功时需要把它改成 0管理员登录成功时改成 1否则所有菜单项都会按默认值放行。4.2 图书与读者信息的录入、修改图书录入表单的核心操作是向 bookinfo 表追加一条空记录再把表单里的值写入字段。保存按钮的代码结构一般是SELECT bookinfo APPEND BLANK REPLACE 编号 WITH ALLTRIM(THISFORM.txt编号.VALUE) REPLACE 书号 WITH ALLTRIM(THISFORM.txt书号.VALUE) REPLACE 书名 WITH ALLTRIM(THISFORM.txt书名.VALUE) REPLACE 作者 WITH ALLTRIM(THISFORM.txt作者.VALUE) REPLACE 出版社 WITH ALLTRIM(THISFORM.txt出版社.VALUE) REPLACE 定价 WITH VAL(THISFORM.txt定价.VALUE) REPLACE 册数 WITH VAL(THISFORM.txt册数.VALUE) REPLACE 图书种类 WITH ALLTRIM(THISFORM.txt图书种类.VALUE) REPLACE 入库日期 WITH THISFORM.txt入库日期.VALUEsave 之前必须做必填项校验书号、书名、定价、册数这四个字段建议必填。定价和册数是数值型从文本框拿到的是字符串必须用 VAL() 转换。录入成功后刷新表格 Grid 的 RecordSource让新记录立即显示出来。修改界面的不同之处在于多出上一条、下一条、查询三个按钮。上一条下一条就是移动记录指针SELECT bookinfo SKIP -1 IF BOF() GO TOP ENDIF THISFORM.REFRESH()Bof 和 Eof 是两个经常被忽略的边界指针已经到第一行时再 SKIP -1 会进入文件头此时必须 GO TOP 拉回来否则记录指针就会停留在无效位置表单上显示的字段值全是空的。查询按钮一般用 locate 按编号找记录找到后刷新表单即可。读者录入和修改的逻辑同构只是表换成 readeruinfo保存时要额外确认“是否允许借”默认置为 .T.新读者密码默认可以用“123456”或学号后六位课程设计里这种简化完全可以接受。4.3 图书查询与借书还书图书查询支持书名、作者、出版社三种查询方式落地时最省事的做法是三条 SQL 分支把查询结果输出到临时表再用 Grid 展示SELECT * FROM bookinfo ; WHERE 书名 LIKE % ALLTRIM(THISFORM.txt条件.VALUE) % ; INTO CURSOR temp_search THISFORM.grid1.RecordSourceType 1 THISFORM.grid1.RecordSource temp_search THISFORM.grid1.REFRESH()LIKE 配合百分号是模糊查询的常用姿势书名包含关键字即可命中。查询条件为空时会把整张表都查出来如果数据量大页面会卡可以在 WHERE 条件里加一个AND 册数 0之类的过滤。INTO CURSOR 的输出是临时表程序退出后自动消失不会污染正式数据。借书的流程比查询多几步。先检查 readeruinfo 的“是否允许借”字段是否为 .T.再检查 bookinfo 里对应书号的册数是否大于 0两者都通过就往 lendinfo 插入一条记录SELECT bookinfo LOCATE FOR ALLTRIM(书号) ALLTRIM(THISFORM.txt书号.VALUE) IF !FOUND() WAIT WINDOW 没有这本书 RETURN ENDIF IF 册数 0 WAIT WINDOW 这本书已全部借出 RETURN ENDIF SELECT lendinfo APPEND BLANK REPLACE 读者编号 WITH cyonghu REPLACE 书号 WITH ALLTRIM(bookinfo.书号) REPLACE 借阅日期 WITH DATE() REPLACE 借阅天数 WITH 30 REPLACE 借阅情况 WITH 在借 REPLACE 是否延期 WITH .F. SELECT bookinfo REPLACE 册数 WITH 册数 - 1还书逻辑正好相反按读者编号和书号找到“在借”状态的那条记录更新归还日期为当前日期借阅情况改为“已还”再把 bookinfo 的册数加回去。如果当前日期超过借阅日期加上借阅天数则把是否延期置 .T.方便管理员做逾期统计。4.4 数据备份与恢复数据安全模块在课程设计里往往是凑页数的真正能落地的做法就是文件级备份。备份按钮的核心代码如下CLOSE DATABASES ALL COPY FILE library.dbc TO backup_library.dbc COPY FILE bookinfo.dbf TO backup_bookinfo.dbf COPY FILE readeruinfo.dbf TO backup_readeruinfo.dbf COPY FILE lendinfo.dbf TO backup_lendinfo.dbf COPY FILE passwordinfo.dbf TO backup_passwordinfo.dbf COPY FILE dairy.dbf TO backup_dairy.dbf这里最容易踩的坑就是 CLOSE DATABASES ALL只要 library.dbc 处于打开状态COPY FILE 就会提示文件被占用。恢复操作反过来执行把备份文件复制回原文件名。备份文件建议按日期命名比如 backup_library_20241208.dbc这样需要回滚时可以明确知道是哪个时间点的数据。更完善一点的做法是先把整个 data 目录压缩成 ZIP 文件再复制到本机另一个磁盘目录。VFP 本身没有内置 ZIP 命令可以用 VFP 的SET LIBRARY TO调用外部压缩库或者直接用 Windows 命令行的 RAR 在备份按钮里 Shell 出去。课程设计答辩阶段能讲清楚“备份前先关库、恢复前先覆盖”这个原理就可以了。5. 避坑VFP 图书借阅系统的常见翻车现场与排查5.1 启动报“文件不存在”或直接秒退现象双击运行 main.prgVFP 闪一下就没影了或者提示“文件 start.scx 不存在”。原因VFP 的默认工作目录和程序所在目录不一致。直接 do 一个脚本时VFP 不会自动切换当前目录set defa 没生效自然找不到 data 目录下的库和 form 目录下的表单。解决把 set defa 和 set path 放在主程序最前面确保路径设置发生在任何 do form 之前。检查 sys(16) 返回值是否包含完整路径如果 rat 出来的路径错了多半是直接 do 而不是运行 .exe。这也解释了为什么 main.prg 里那两行路径代码那么值钱删掉它整个系统就是黑匣子。5.2 登录永远提示“没有此用户”现象明明数据库里有读者编号登录却提示“没有此用户”。原因readeruinfo 的读者编号是 C(10)文本框中输入 10 位以内的编号时allt(读者编号) 会去掉右端空格然后比较理论上是能匹配的。真正的问题往往出在 Lendinfo 和 Readeruinfo 两张表的读者编号字段宽度不一致或者读者编号是数值型字段文本框给它传字符比较类型对不上。解决打开表设计器逐张检查“读者编号”的类型和宽度全部统一为 C(10)。如果有的表是数值型在 get 出编号时用 STR(读者编号, 10) 转成字符串。查不到记录时先用SELECT * FROM readeruinfo WHERE 读者编号 输入值在命令窗口验证数据是否存在缩小问题范围。5.3 密码输对了也提示密码错误现象用户确认密码没输错但系统反复提示“密码错误请重新输入”。原因passwordinfo 表的密码字段宽度是 C(7)如果初始录入时管理员密码超过 7 位保存时会被截断到 7 位。密码文本框中输入的内容比字段长时VFP 比较的是被截断后的字段值和完整输入值永远匹配不上。解决把 passwordinfo 密码字段加宽到 C(16) 或 C(20)同时建议读者表的密码字段统一用 C(16)。文本密码框在单机课程设计里够用但要把密码存为在明文这一点在答辩时主动说清楚作为可改进项提出比被老师追问要体面得多。5.4 备份时提示“文件正在使用”现象备份按钮一执行就报“文件正在使用”或共享冲突。原因VFP 打开数据库后library.dbc 和正在使用的表文件都被占用了COPY FILE 不能覆盖被独占打开的 .dbc 或 .dbf 文件。很多人直接写 COPY FILE 却不先关库正好触发这个限制。解决备份前强制加两行CLOSE DATABASES ALL 关闭所有数据库CLOSE TABLES ALL 关闭所有表。备份结束后再重新 OPEN DATABASE library。恢复操作更要先确认程序没有打开任何表否则文件覆盖失败后数据库文件还处于半损坏状态最稳妥的流程是退出系统到 VFP 命令窗口手动复制覆盖后再运行系统。5.5 登录三次锁定失效现象连续输错三次密码表单并没有锁定或者重新进入登录表单后又能继续尝试。原因n 计数器若定义为表单的局部变量每次释放并重新执行登录表单时init 事件都会把 n 重新初始化为 1锁定标志全部清零。系统虽然有三次锁定代码实际却起不到拦截作用。解决把 n 声明为全局变量在 main.prg 里统一初始化或者在表单 init 里读一个自定义表单属性 thisform.nCount。公共变量方案最简单退出系统时顺手重置即可。如果想让锁定更严格还可以配合 VFP 的SET SECURITY或者写一个登录失败日志表记录失败次数和最后失败时间。6. 进阶把课程设计改造成能打包发布的完整系统课程设计能跑起来只是及格线真正加分的是把它从“演示系统”变成“可交付系统”。第一个值得做的事情是建一个借阅历史视图把三张表关联起来查询时不用手动写多表 JOINCREATE SQL VIEW v_lend_history AS ; SELECT Readeruinfo.姓名, Readeruinfo.读者编号, ; Booksinfo.书名, Booksinfo.作者, ; Lendinfo.借阅日期, Lendinfo.归还日期, ; Lendinfo.借阅情况 ; FROM Readeruinfo INNER JOIN Lendinfo ; ON Readeruinfo.读者编号 Lendinfo.读者编号 ; INNER JOIN Booksinfo ON Lendinfo.书号 Booksinfo.书号这个视图把所有借阅明细变成一张平铺表查询表单的 Grid 直接绑定 v_lend_history省去在代码里反复写关联条件也方便后续做统计报表。打包发布是第二个重点。VFP 程序的发行步骤并不复杂将 main.prg 设为主程序新建菜单 menu_main.mnx把项目编译成 .app再用构建管理器生成 .exe。发布目录必须带上 VFP 运行库常见的是 vfp6r.dll、vfp9r.dll 之类否则别人机器上没装 VFP 环境双击 exe 会提示“无法定位程序输入点”。这个细节是很多人血泪经验的来源我见过太多同学在自己机器上跑挺顺copy 到老师电脑上就报错基本都是运行库没带全。最后建议在答辩前跑一遍完整的冒烟验证流程步骤操作预期结果1读者注册新账号记录写入 readeruinfo默认允许借书2新账号登录登录成功菜单不含图书录入3查询一本书Grid 显示结果借阅情况可见4借书Lendinfo 新增在借记录册数减一5还书记录改为已还册数加一6管理员登录后备份备份文件生成成功7恢复备份数据回到备份时状态8连续输错三次密码登录控件禁用提示自动退出这套流程 10 分钟就能跑完但能把表单间跳转、字段更新、权限控制全部覆盖到。从那以后我每次拿到一份数据库课程设计都会先执行一遍建表语句再逐张打开表确认字段类型、宽度、索引确认没有从别的项目复制来的残留字段最后才看界面代码。库表设计这一步走稳了后面的表单和报表全是顺水推舟。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?