本案例取材于我院 2008 届信息与计算科学专业陈俊俊同学的毕业设计,相关的代码已包 含在配套的光盘中。我们希望通过该案例的学习,使大家了解 Visual C++的强大开发功能以及 程序开发中的一些技巧,激励大家的学习兴趣。
图书管理系统是一个典型的管理信息系统。该案例综合运用了开发管理信息系统的相关 知识,从中小型图书馆的实际需求出发,进行设计和具体实现。主要功能有:管理员权限验证;
用户信息管理,包括操作人员的增删和密码修改;图书借还流通管理;读者信息维护管理;图 书信息维护管理,包括图书信息的增删和检索;查看历史记录。
1.简易图书管理系统的模块划分如图 1.22 所示。
图 1.22 简易图书管理系统模块划分图 简易图书管理系统
系统登录与注销 用户信息管理 图书信息管理 读者信息管理 图书流通管理 历史查询
2.简易图书管理系统的业务处理流程如图 1.23 所示。
主体框架:使用 MFC AppWizard 创建一个单文档结构的应用程序工程 Library,建立起系 统主体框架,生成应用程序类(CLibraryApp)、文档类(CLibraryDoc)、视图类(CLibraryView)
Operators 表 职工号 姓名 密码
流水号 读者编号 图书编号 操作员编号 借出日期 归还日期 History 表
图 1.25 简易图书管理系统主控制台界面
另外,为了减少等待程序加载过程中的枯燥感本系统使用了启动画面,在程序启动时自 动加载显示欢迎画面登录窗。启动画面可以利用组件库中的 Splash Screen 组件实现,但本系 统经过一次修改,为增强交互性与安全性,采用了无标题栏的对话框(DIALOG)实现并扩展 了该功能。在应用程序类(CLibraryApp)的 InitInstance()函数中加入如下代码,在主框架创 建之前显示此对话框:
BOOL CLibraryApp::InitInstance() {
…
CWelLoginDlg loginDlg;
if(loginDlg.DoModal()!=IDOK) return FALSE;
m_bUserName = loginDlg.m_strName; // 记录登录者姓名,以便传给主框架 …
}
(1)使用 MFC ADO 连接数据库。
使用 ADO 前在工程的 stdafx.h 头文件里用直接引入符号#import 引入 ADO 库文件,以使 编译器能正确编译。代码如下:
#import "c:\program files\common files\system\ado\msado15.dll" no_namespaces rename("EOF" adoEOF") //将常数 EOF 重命名为 adoEOF,以避免与文件结尾常数冲突
ADO 库包含 3 个基本接口:_ConnectionPtr 接口、_CommandPtr 接口和_RecordsetPtr 接口。
_ConnectionPtr 接口主要是一个连接接口,取得与数据库的连接,返回一个记录集或一个空指 针,通常使用它来创建一个数据连接或执行一条不返回任何结果的 SQL 语句,如插入、删除
记录等;_CommandPtr 接口返回一个记录集,它提供了一种简单的方法来执行返回记录集的 存储过程和 SQL 语句;_RecordsetPtr 是一个记录集对象,与以上两种对象相比,它对记录集 提供了更多的控制功能,如记录锁定、游标控制等。
在具体使用时,按照如下流程来对数据库进行操作:
1)用_ConnectionPtr 建立连接指针。
2)执行 SQL,按需要使用_CommandPtr 返回一个记录集。
3)调用 close()函数关闭记录集,释放记录集指针。
4)调用 close()函数关闭连接,释放连接指针。
代码如下:
CoInitialize(NULL); //初始化 OLE/COM 库环境 _ConnectionPtr m_pConnection;
m_pConnection.CreateInstance(__uuidof(Connection)); //建立数据库连接指针 try
{
// 打开数据库 db_lib.mdb
m_pConnection -> Open( "Provider = Microsoft.Jet.OLEDB.4.0; Persist Security Info=False;
Jet OLEDB: Database Password = 123456; Data Source = db_lib.mdb" , "" , "" , adModeUnknown );
}
catch(_com_error e) //异常捕捉 {
AfxMessageBox("数据库连接失败!");
return;
}
CString strSQL; //构建 SQL 查询字符串 strSQL = "……";
_variant_t RecordsAffected;
_RecordsetPtr m_pRecordset;
m_pRecordset.CreateInstance(__uuidof(Recordset)); //建立记录集指针 //执行 SQL 语句,并返回记录集
m_pRecordset = m_pConnection -> Execute( ( _bstr_t )strSQL, &RecordsAffected, adCmdText );
…
m_pRecordset->Close(); //关闭记录集 m_pRecordset.Release(); //释放指针 m_pConnection->Close(); //关闭连接 m_pConnection.Release(); //释放连接指针 CoUninitialize();
为数据库数据的安全及数据同步着想,本系统在每个功能模块中一旦结束该模块的操作
图 1.26 权限验证模块
在本系统中使用 BOOL 型全局变量 m_bLogin 来判断用户权限,登录成功则为 TRUE,开 启功能模块,并在主控制台的状态栏中显示当前操作人员,否则为 FALSE。在登录失败或未 登录的情况下,所有的功能模块都无法使用。代码如下:
CLoginDlg dlg;
if (dlg.DoModal() == IDOK){
m_bLogin = TRUE;
m_strUserName = dlg.m_strName ;
// 设置状态栏
CString strUser;
strUser = "当前操作员:" + m_strUserName;
m_wndStatusBar.SetPaneText(0,strUser);
} else{
CString strUser;
strUser = "当前操作员:" + m_strUserName;
m_wndStatusBar.SetPaneText(0,strUser);
}
同时,针对操作人员基本信息的建立与维护,开发用户管理信息管理模块,以实现对管 理人员进行增加、删除及密码修改。该模块主要划分为 3 个子模块:新增用户、修改密码、删 除用户,具体界面如图 1.27 所示。
图 1.27 用户管理模块
根据用户的选择,使用控件的 EnableWindow() 函数实现右侧所需编辑框的开启与关闭,
以引导用户正确完成操作。如单击“修改密码”按钮时,则开启旧密码、新密码、确认密码 3 个编辑框,当旧密码通过认证,并且新密码与确认密码一致时单击“保存”按钮,将会弹出消
息框提示修改密码成功。
在这个界面中,新增用户和修改密码两个按钮的响应函数只负责实现右侧编辑框的开启,
具体功能在“保存”按钮中实现,以下是“保存”按钮实现修改密码的部分代码:
void COManageDlg::OnOmgSave() {
UpdateData();
// 用户填写信息判断
if (m_ctrUserID.IsWindowEnabled()){ //新增用户 …
m_ctrOldPwd.SetFocus();
return;
}
}
if(m_strPwd == ""){
MessageBox("密码不能为空!");
m_ctrPwd.SetFocus();
return;
}
if(m_strPwd != m_strRePwd){
MessageBox("密码不匹配,请重新输入!");
m_strPwd = "";
m_strRePwd = "";
UpdateData(FALSE);
m_ctrPwd.SetFocus();
return; if (m_ctrUserID.IsWindowEnabled()){ //新增用户 …
}
else{ //修改用户密码
_RecordsetPtr m_pRecordset; //创建记录集指针 m_pRecordset.CreateInstance(__uuidof(Recordset));
strSQL.Format("select * from operators where operator_ID='%s' and passwd='%s'", m_strUserID, m_strOldPwd );
m_pRecordset = m_pConnection -> Execute( ( _bstr_t )strSQL, &RecordsAffected, adCmdText );
if(m_pRecordset->adoEOF) { //对旧密码进行认证
MessageBox("旧密码不正确,请重新输入!");
…
return;
}
m_pRecordset->Close(); // 关闭记录集 m_pRecordset.Release();
// 更新记录,完成密码修改
strSQL.Format("update operators set passwd='%s' where operator_ID='%s'", m_strPwd, m_strUserID);
m_pConnection->Execute((_bstr_t)strSQL,&RecordsAffected,adCmdText);
MessageBox("密码修改成功,请记住你的新密码!");
}
//更新用户列表 RefreshData();
… //关闭链接 }
(3)书库管理模块和读者管理模块开发。
书库管理模块主要实现对所有图书信息的管理功能,包括新书的入库登记、图书信息检 索和浏览等。读者管理模块主要实现对所有读者信息的管理功能,包括新读者的登记、读者信 息检索和浏览、读者注销等,同书库管理模块在实现上极为相似,因此这里只以书库管理模块 为例。
书库管理模块主要划分为 4 个子模块:新书登记、显示所有图书信息、据书名检索图书、
删除图书信息。同样也是根据用户的不同选择开启不同的编辑框让用户操作,如单击“书名检 索”按钮时,仅开启“图书名称”编辑框,单击“提交”按钮后,将在列表框中显示出检索到 的图书信息,其界面设计如图 1.28 所示。
图 1.28 书库管理模块 以“删除”按钮为例,以下是部分代码:
void CBManageDlg::OnBmgDelete() UpdateData();
if( m_strBookID == "" ){
MessageBox("请先选择一本图书!");
return;
}
int checkdel = MessageBox("确认删除该图书吗?","警告",MB_ICONWARNING | MB_YESNO);
if( checkdel != 6 ) {
return;
}
//链接数据库 …
CString strSQL; //构建 SQL 查询字符串
strSQL.Format("delete from books where book_ID='%s'",m_strBookID);
m_pConnection->Execute((_bstr_t)strSQL,&RecordsAffected,adCmdText);
…
//关闭链接
MessageBox("已成功删除指定图书!请刷新图书列表!");
//清空用户列表
m_ctrList.DeleteAllItems();
}
(4)借阅图书模块和还书模块。
这两个模块应该是图书管理系统的核心模块,但它们是以前面几个模块为基础建立的。
借阅图书模块要求完成读者信息认证和图书信息认证,若读者录入信息不正确或图书已被借 出,则立即中止借书,防止数据错误;否则正常借出,并生成借书记录备档。还书模块相对简 单,更新借书读者的借书状态和图书借出状态,并生成历史记录备档即可。此处仅以借阅图书 模块为例。
借阅图书模块,要求在输入完读者编号后,立即显示用户信息、图书信息及已借图书情 况。同样,在输入图书编号后,立即调出该书信息,以便确认。这里采用了 OnKillfocus***() 功能响应函数来完成这一功能,效果十分理想。借书模块的界面设计如图 1.29 所示。
图 1.29 借阅图书模块 以下为“确定借出”按钮响应函数的部分代码:
void CBorrowDlg::OnBowOk()
{
// 连接数据库 UpdateData();
if (m_strReaderID.IsEmpty()) {
AfxMessageBox("读者编号不能为空!");
m_ctrReaderID.SetFocus();
return;
}
if (m_strBookID.IsEmpty()) {
AfxMessageBox("图书编号不能为空!");
m_ctrBookID.SetFocus();
return;
}
// 创建记录集指针
CString strSQL; //构建 SQL 查询字符串,判断图书在馆情况
strSQL.Format("select isborrowed from books where book_ID='%s'",m_strBookID);
m_pRecordset = m_pConnection -> Execute( ( _bstr_t )strSQL, &RecordsAffected, adCmdText);
_variant_t visBow;
visBow = m_pRecordset->GetCollect(_variant_t((long)0));
if( visBow.lVal >= 1 ) {
AfxMessageBox("该图书已被借出,请查找其他副本");
return;
}
//关闭记录集
//确定借出,完成数据库的更新
strSQL.Format( "insert into borrow( reader_ID,book_ID,operator_ID ) values( '%s', '%s', '%s' )", m_strReaderID,m_strBookID,m_strOperatorID );
m_pConnection->Execute((_bstr_t)strSQL,&RecordsAffected,adCmdText);
strSQL.Format("update readers set bbcount=bbcount+1 where reader_ID='%s'", m_strReaderID );
m_pConnection->Execute((_bstr_t)strSQL,&RecordsAffected,adCmdText);
strSQL.Format("update books set isborrowed=1 where book_ID='%s'", m_strBookID);
m_pConnection->Execute((_bstr_t)strSQL,&RecordsAffected,adCmdText);
//关闭链接
MessageBox("图书借出成功!");
RefreshData();
}
(5)历史查看模块。
历史查看模块是一个附加模块,并不是该体系的必需模块。但其功能却不小,主要用来 查看图书流通的历史记录信息,方便图书流通过程中对图书流向的把握,以及图书馆整体状态、
图书利用率等宏观信息的把握。
历史查看模块主要分为两个子模块:借出历史查看和归还历史查看,借出历史显示所有 借出未还图书的历史;归还历史则显示已归还的图书流通记录。
历史查看模块界面设计如图 1.30 所示。代码相对简单,仅仅是一个简单的数据库查询,
在前面几个模块的代码中也有体现,这里不再重复。
图 1.30 历史查看模块
(6)系统界面美化。
为增强系统界面的友好,本系统在功能全部实现后,替换精致的图标,并采用 SkinMagic 对本系统的界面进行了皮肤加载,全面美化。在应用程序类(CLibraryApp)的 InitInstance() 函数中加入如下代码:
BOOL CLibraryApp::InitInstance() {
VERIFY( 1 == InitSkinMagicLib( AfxGetInstanceHandle(), _T("ReadUI") ,NULL,NULL ) );
VERIFY( 1 == LoadSkinFile("Skins\\xplus.smf")); //加载皮肤文件 xplus.smf VERIFY( 1 == SetDialogSkin( _T("Dialog") ) );
}
美化效果对比如图 1.31 和图 1.32 所示。
图 1.31 界面美化后 图 1.32 界面美化前
5.系统的主要优点。
(1)采用 Visual C++ 6.0 开发,运行效率高。
(2)采用 Access 2003 作为后台数据库,小巧灵活。
(3)界面友好美观,操作简单。
(4)采用 SkinMagic 对本系统界面进行美化,简洁大方。