C++图书馆管理系统——基于jsoncpp与windows.h

0. 演示视频

先直接上演示视频看看具体的实现效果

C++图书馆管理系统——基于jsoncpp和windows.h

系统的代码打包为百度云
链接:https://pan.baidu.com/s/1Lm40gKgYdHdnyz35GsxMWA
提取码:qu72

1. 系统概述

1.1 系统功能

编写一个图书管理系统对学生信息和图书信息进行管理。其中学生信息包括姓名,学号,所属学院,最大借阅量等属性,图书信息包括索取号,书名,作者,出版社,数量等属性,要求能输入、输出、修改、查询学生借阅图书的信息。 在该系统中,分为学生使用端和管理员端,学生端的主要功能有两种:查询书籍以及查看个人信息;管理员端的功能有:查询学生信息、查找书籍、查询书籍具体信息、修改书籍信息、借/还书、修改密码。

在学生信息中,包含学生姓名、学号、学院、最大借阅量、当前借阅量、借阅信息;在书籍信息中,包含书籍名称、ISBN号、作者、出版社、索引号、馆藏数、现有数、借阅信息;由于我们这个是图书馆管理系统,所以设置只能修改书籍信息以及学生的借阅信息,至于学生的个人信息,应该认为在学校学生管理系统中管理,在本系统中不可修改。关于登录密码,学生端的密码也认为存储在学校的学生信息数据库中,本系统只可查询这个数据库,不可修改,而管理员端的密码是可以修改的。

1.2 系统数据库

图书馆管理系统应该有独立存储书籍学生信息的数据库,理想情况下可以用mySQL等数据库进行数据存储,但考虑到实际情况,本系统采用的数据存放方式为json文件存储,json格式是良好的数据交换格式,其键值对的数据结构能够很好的表示图书信息以及学生信息。在本系统中,采用jsoncpp库进行json格式文件的读写

2.系统结构

2.1 代码结构

系统采用多文件来组织代码,代码文件分别为

  1. LibararyManSys.cpp – 主文件
  2. User.h User.cpp – 存放学生类以及管理员类
  3. DataBase.h DataBase.cpp – 存放数据库类
  4. CreatePage.cpp – 存放绘制系统的不同功能对应不同页面的函数

2.2 类结构

系统一共有4个类,其结构为
在这里插入图片描述

2.3 类及数据结构体数据成员及功能

2.3.1 DataBase.h DataBase.cpp 存放的类与数据结构

1) 存放书籍信息的结构体

struct bookInfo
{
	string ISBN;
	string name;
	string author;
	string press;
	string index;
	int totalAmounts;
	int amounts;
	vector<vector<string>> borrowers{ {},{} };
};

结构体存放着书籍的信息,其中借阅信息borrowers为二维的stirng数组,由于借阅人数位置,因此用vector容器来定义,方便动态存储,其中,borrowers的第一行存放借阅者的学号,如”292521”,第二行存放着借阅者的借阅日期,如2020年12月22号,则以”20201222”的形式存放,两行的索引一一对应为一个学生。

2) 存放学生信息的结构体

struct stuInfo
{
	string id;
	string name;
	string college;
	int maxLoan;
	int loan;
	vector<vector<string>> books{ {},{} };
};

同样,结构体存放着学生的各种信息,其中books的第一行存放书籍的ISBN号,第二行存放借阅日期,索引号也是一一对应为一本书。

3) 数据库类DataBase

class DataBase {
	string studentPswPath = "./Data/student_psw.json";
	string adminPswPath = "./Data/admin_psw.json";
	string studentInfoPath = "./Data/student_info.json";
	string bookInfoPath = "./Data/book_info.json";
	int maxMonth = 2;
public:
	int pswConfirm(string, string, int);  
	string getData();
	string getReturnData(string);
	void getBooks(vector<vector<string>> &);
	bookInfo getBookInfo(string);
	stuInfo getStuInfo(string);
	int returnBook(string, string);
	int borrowBook(string, string);
	int addBook(bookInfo);
	int deleteBook(string);
	int appendBook(string, int);
	int modifyBookInfo(bookInfo);
	int pswChange(string, string, string);
};

DataBase类的私有成员为各个数据库json文件的相对路径,以及定义的最大借阅日期,下面来看各个函数的输入输出及功能:

/*
获取当前日期
params:
None
return:
@data -- 当前的日期,如2020年12月19日,返回"20201219"
*/
string DataBase::getData()

/*
获取还书日期
params:
@date -- 借书日期
return:
@rdata -- 还书日期,为字符串形式
*/
string DataBase::getReturnData(string date)

/*
确认密码是否正确
params:
@account -- 账号
@psw -- 密码
@people -- 用户类型 0:学生 1:管理员
return:
@-2 -- 数据库文件丢失或解析错误
@-1 -- 没用该用户
@0 -- 密码错误
@1 -- 密码正确
*/
int DataBase::pswConfirm(string account, string psw, int people)

/*
获取所有书籍的ISBN号以及名字
params:
@&books -- 二维string vector变量的引用,在函数中,将会在第一维存放ISBN号,在第二维存放书籍名字,其索引号一一对应
return:
None
*/
void DataBase::getBooks(vector<vector<string>> &books)

/*
获取某本书籍的所有信息
params:
@tISBN -- 目标书籍的ISBN号
return:
@book -- 存放书籍所有信息的结构体
*/
bookInfo DataBase::getBookInfo(string tISBN)

/*
获取某个学生的所有信息
params:
@tID -- 目标学生的校园卡号
return:
@stu -- 存放目标学生所有信息的结构体
*/
stuInfo DataBase::getStuInfo(string tID)

/*
进行还书操作的函数
params:
@stuID -- 要还书的学生的校园卡号
@bookISBN -- 学生要还书籍的ISBN号
return:
@-1 -- 该学生不存在
@-2 -- 该书籍不存在
@0 -- 学生没有借这本书
@1 -- 还书成功
*/
int DataBase::returnBook(string stuID, string bookISBN)

/*
进行借书操作
params:
@stuID -- 借书学生校园卡号
@bookISBN -- 学生要借的书的ISBN号
return:
@-1 -- 学生不存在
@-2 -- 书籍不存在
@2 -- 书籍没有了
@1 -- 学生借书前已经达到最大借阅量,不可以再借
@0 -- 借书成功
*/
int DataBase::borrowBook(string stuID, string bookISBN)

/*
在书籍数据库中增加一本书
params:
@book -- 要增加书籍的书籍信息结构体
return:
@0 -- 这本书已经存在,增加书籍失败
@1 -- 增加书籍成功
*/
int DataBase::addBook(bookInfo book)

/*
删除一本书
params:
@bookISBN -- 要删除的书籍的ISBN号
return:
@-1 -- 书籍不存在,删除失败
@0 -- 书籍的馆藏数量(total_amounts)和现有数量(amounts)不相等,即有人把书借走了还没换,不允许删除书籍
@1 -- 删除书籍成功
*/
int DataBase::deleteBook(string bookISBN)

/*
调整书籍的数量(函数名用了append,因为设置增量可以为负数)
params:
@bookISBN -- 要调整数量的书籍的ISBN号
@num -- 增加的数量
return:
@-1 -- 书籍不存在
@0 -- 书籍现有数量加上增加数量后小于0,不允许数量调整操作
@1 -- 调整数量成功
*/
int DataBase::appendBook(string bookISBN, int num)

/*
修改书籍的信息
params:
@book -- 要修改信息的书籍的结构体
return:
@0 -- 书籍不存在
@1 -- 修改信息成功
*/
int DataBase::modifyBookInfo(bookInfo book)

/*
管理员密码修改
params:
@account -- 管理员账号
@rawPsw -- 管理员原密码
@newPSw -- 管理员新密码
return:
@0 -- 旧密码不正确,修改失败
@1 -- 密码修改成功
*/
int DataBase::pswChange(string account, string rawPsw, string newPsw)

在这些数据读写函数中,均返回状态码,而且每个函数都是相互独立的,因此开发者可以根据状态码判断数据的读写是否正常,若不正常,便可进行相应的异常响应。在获取学生信息与书籍信息的函数中,返回的不是状态码而是相应数据的结构体,在这两个函数中,我们可以通过判断返回学生结构体(书籍结构体)中的string类型的成员s是否为空(利用成员函数s.empty()或直接s==””),因为函数中,如果搜索不到对应的学生(书籍),将返回一个未赋值的结构体,而结构体中string类型数据初始值为空串,通过这个可以判断是否成功读到数据。

2.3.2 User.h User.cpp 存放的类及类函数

1) 基类baseUser

class baseUser {
protected:
	DataBase DB;
public:
	virtual void Login() {};
	virtual void Search() {};
	virtual void Home() {};
	string *_Search(string);
	int LCS(string, string);
};

在基类中,有唯一的保护成员,数据库类的实例DB,因为无论是学生还是管理员都需要对数据库进行读或写,设置称保护成员是为了派生类可以访问,在函数中,设置了3个虚函数,分别是登录函数,搜索函数和主页函数,因为这几个都是学生端和管理员端都应该有的页面,但是它们的形式都不一样,这里先给出一个概念,后面第四节会详细介绍。基类还有两个函数,_Search()和LCS(),这两个函数是配合使用的,设置这两个函数的目的是为了可以对书名进行模糊搜索,在本个系统中,由于书籍数受限,因此只根据用户输入的书名,给出最符合的3本书,开发者只需要调用前者,便可获得存放这3本书ISBN号的数组。

2) 派生类Student

class Student :public baseUser {
protected:
	string name;
	string id;
	string college;
	int max_loan;
	int loan;
	vector<vector<string>> books{ {},{} };
public:
	void Login(HANDLE);
	void Search(HANDLE);
	void Home(HANDLE);
	void getInfo(HANDLE);
};

派生类学生类的保护成员为学生的各种信息,在学生登录成功后便会进行初始化,成员函数为学生端的各个界面,后面会介绍

3) 派生类Admin

class Admin :public baseUser {
protected:
	string account;
public:
	void Login(HANDLE);
	void Search(HANDLE);
	void Home(HANDLE);
	void getBookInfo(HANDLE, string);
	void getStuInfo(HANDLE);
	void returnBook(HANDLE);
	void borrowBook(HANDLE);
	void addBook(HANDLE);
	void deleteBook(HANDLE, string);
	void appendBook(HANDLE, string);
	void modifyBookInfo(HANDLE, bookInfo);
	void pswChange(HANDLE);
};

管理员类也是本系统的核心类之一,因为图书馆管理系统的大部分功能都继承在管理员类中,可以看到,管理员类的数据成员只有管理员的账号,这是登陆成功后初始化的,类的成员函数便是各个功能的操作界面。

4) 函数的输入输出及功能

/*
获得最长公共子序列的长度(因为在系统中这个函数主要为了模糊搜索函数提供一个score,即LCS的长度
因此不需要找出最长公共子序列的具体内容)
params:
@sStr -- searchStr 搜索时的字符
@tStr -- targetStr 目标字符
return:
@lenLCS -- 搜索时字符与目标字符的LCS长度
*/
int baseUser::LCS(string sStr, string tStr)

/*
对用户输入的书名进行模糊搜索,获得最匹配的3本书
params:
@sStr -- 用户搜索书名时输入的字符串
return:
@topBooks -- string数组 ,存放top3的书籍ISBN号,由匹配程度由高到低排列
*/
string *baseUser::_Search(string sStr)

/*
学生端登录功能部分
params:
@hOut -- console窗口句柄
*/
void Student::Login(HANDLE hOut)

/*
学生端主页
params:
@hOut -- console窗口句柄
*/
void Student::Home(HANDLE hOut)

/*
学生端查看个人信息页面
params:
@hOut -- console窗口句柄
*/
void Student::getInfo(HANDLE hOut)

/*
学生端查找书籍页面
params:
@hOut -- console窗口句柄
*/
void Student::Search(HANDLE hOut)


/*
管理员端查找书籍页面
params:
@hOut -- console窗口句柄
*/
void Admin::Search(HANDLE hOut)

/*
管理员端主页
params:
@hOut -- console窗口句柄
*/
void Admin::Home(HANDLE hOut)

/*
管理员登录页面
params:
@hOut -- console窗口句柄
*/
void Admin::Login(HANDLE hOut)

/*
管理员端查看书籍具体信息页面
params:
@hOut -- console窗口句柄
@ISBN -- 被查书籍的ISBN号
*/
void Admin::getBookInfo(HANDLE hOut, string ISBN)

/*
管理员端查看学生信息页面
params:
@hOut -- console窗口句柄
*/
void Admin::getStuInfo(HANDLE hOut)

/*
管理员端还书界面
params:
@hOut -- console窗口句柄
*/
void Admin::returnBook(HANDLE hOut)


/*
管理员端借书界面
params:
@hOut -- console窗口句柄
*/
void Admin::borrowBook(HANDLE hOut)

/*
管理员端增加书籍界面
params:
@hOut -- console窗口句柄
*/
void Admin::addBook(HANDLE hOut)

/*
管理员端删除书籍界面
params:
@hOut -- console窗口句柄
*/
void Admin::deleteBook(HANDLE hOut, string ISBN)

/*
管理员端调整书籍数量界面
params:
@hOut -- console窗口句柄
*/
void Admin::appendBook(HANDLE hOut, string ISBN)

/*
管理员端修改书籍信息界面
params:
@hOut -- console窗口句柄
*/
void Admin::modifyBookInfo(HANDLE hOut, bookInfo book)

/*
管理员端修改密码界面
params:
@hOut -- console窗口句柄
*/
void Admin::pswChange(HANDLE hOut)

5) LCS最长公共子序列算法简介
详见这篇博客https://blog.csdn.net/weixin_40673608/article/details/84262695
一般求解LCS使用动态规划法

问题描述:设X=<x1, x2, …, xm>,Y=<y1,y2,…,yn>,Z=<z1,z2,…,zk>是X和Y的公共子序列,那么:
1、如果xm = yn,则zk = xm = yn 且 Zk-1是Xm-1和Yn-1的一个LCS
2、如果xm != yn 且 zk != xm,则Z是Xm-1和Y的一个LCS
3、如果xm != yn 且 zk != yn,则Z是X和Yn-1的一个LCS

使用二维数组c[i,j]来表示字符串X,Y的前i,j个字符的LCS长度,可以得到:

因此,我们只需将c从第一项到最后一项填满,便可得到LCS的长度,算法的伪代码为:

由于在该系统中,我们希望得到某本书籍名字相对于输入内容的分数,因此我们只需要得到LCS的长度,而不需要获得其内容。在实际操作中,由于输入字符长度未知,因此需要对数组c进行动态内存分配:
初始化与销毁:

int **c;
int i, j;
c = new int *[m];

for (i = 0;i < m;i++)
	c[i] = new int[n];
	
for (i = 0;i < m;i++)
	c[i][0] = 0;
for (i = 0;i < n;i++)
	c[0][i] = 0;

for (i = 0;i < m;i++)
	delete [] c[i];
delete[]c;

刚刚说到,我们是根据搜索书籍相对于搜索串的分数来给出搜索结果的,而这个分数在系统中定义为:
假设搜索时输入的串为a,书籍名的串为b,他们LCS的长度为L,那么我们定义分数为:
在这里插入图片描述
在这种朴素LCS算法中,对英文字符串的搜索效果是很好的,但对于中文字符串有一定的局限性,因为一个汉字占两个字符位,所以这会对搜索结果产生影响。

3. 数据库读写及绘制界面用到的库文件说明

3.1 Jsoncpp

jsoncpp的入门教程可以参考这篇博客https://blog.csdn.net/shuiyixin/article/details/89330529
在VS包含目录设置中,最好设置为绝对路径
在这里插入图片描述
前面谈到,系统是用json格式来存储数据的,由于自己写一个json读写的程序很耗费精力而且没有必要,因此选用了开发者比较常用的对json进行读写的库jsoncpp作为系统开放的辅助。在github上下载这个库后,将必要的文件放入工程中,配置好include项,便可以使用这个库
我们先来看看系统需要用到的各种数据在json中以怎样一种格式存放:
学生密码库:

{
    "292521":"123abc",
    "291234":"246efg"
}

键为学生的学号,值为学生的密码

管理员密码库:

{
   "admin1" : "123456",
   "admin2" : "456edc"
}

键为管理员账号,值位管理员密码

学生信息库:

{
   "291234" : {
      "books" : [
         [ "9787115461476", "20201111" ]
      ],
      "college" : "计算机与软件工程学院",
      "loan" : 1,
      "max_loan" : 3,
      "name" : "张三"
   },
   "292521" : {
      "books" : [
         [ "9787111632887", "20201006" ],
         [ "9787115461476", "20201218" ]
      ],
      "college" : "电子与信息工程学院",
      "loan" : 2,
      "max_loan" : 3,
      "name" : "李四"
   }
}

其中,键位学生学号,该键下的子节点是学生的各种信息,其中借阅信息以列表格式存放,列表中的一个子列表对应借阅的一本书,第一项位ISBN号,第二项位借阅日期

图书信息库:

{
   "9787111632887" : {
      "amounts" : 2,
      "author" : "理查德哈特利",
      "borrowers" : [
         [ "292521", "20201006" ]
      ],
      "index" : "c147",
      "name" : "计算机视觉中的多视图几何",
      "press" : "机械工业出版社",
      "total_amounts" : 3
   },
	"9787115461476" : {
      "amounts" : 1,
      "author" : "Yoshua Bengio",
      "borrowers" : [
         [ "291234", "20201111" ],
         [ "292521", "20201218" ]
      ],
      "index" : "b246",
      "name" : "深度学习",
      "press" : "人民邮电出版社",
      "total_amounts" : 3
   }
}

由于书籍的ISBN号是唯一的,因此最适合作为进行索引书籍的键名,书籍ISBN键下子节点下是书籍的各种信息,其中借阅者列表第一项位借阅学生的学号,第二项位借阅日期。

了解完数据存放的格式,再来看看在本系统的代码中,如何使用jsoncpp:
由于jsoncpp是作用于已经读入系统内存中字符串,对其进行解析,因此我们需要C++文件的读写来配合使用

我们需要包含两个头文件

#include <fstream>
#include <json.h>

由于jsoncpp有两套API,我下载的jsoncpp库默认使用新API,但由于旧的API可读性更好,也更容易操作,因此需要在文件开始加入几行代码说明使用旧API

#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#elif defined(_MSC_VER)
#pragma warning(disable : 4996)
#endif

一般从json文件中解析json数据的步骤为:

  1. 实例化两个对象,reader是解析器,root是jsoncpp中的值对象,可以将它看作json文件中一个节点
Json::Reader reader;
Json::Value root;
  1. 读取文件流
ifstream in(pswPath, ios::binary);

第一个参数为已经赋值的json文件路径,在第二个参数中,由于json文件是二进制文件,因此需要用读二进制

  1. 将流解析到节点中
reader.parse(in, root)

经过这三步,便可以处理解析后的json文件,要对节点下的键进行索引,操作也很方便,比如要索引图书数据库中深度学习的第一个借阅者学号,便使用
root[ISBN(假设已经赋值)][“borrowers”][0][0]

在实际应用中,会用到的jsoncpp的用法有:

  1. 判断节点的某个键是否存在
root[account].isString()

如果键存在,那么它会返回true

  1. 将某个解析得到的值转换为C++可处理的数据类型
root[account].asString()
root[account].asInt()

等等

  1. 生成可迭代对象,在系统中可用于遍历得到数据库中所有书籍的ISBN及名字
Json::Value::Members mem = root.getMemberNames();

for (auto iter = mem.begin(); iter != mem.end(); iter++)
{
	books[0].push_back(*iter);
	books[1].push_back(root[*iter]["name"].asString());
}
  1. 创建一个节点,在系统中可用于管理员在数据库中添加书籍
    在这个操作中,我们先创建节点对象:
Json::Value bookNode;

再往节点中喂想要的键值对,比如

bookNode["name"] = Json::Value(book.name);

如果想要某个键的值为空列表,则

bookNode["borrowers"].resize(0);

将创建的节点挂载到已有的节点上

root[book.ISBN] = bookNode;
  1. 刷新数据库
    由于我们对数据的修改是操作在读入到内存中的数据上的,因此我们修改完数据后,需要将数据重新写入文件中,以实现数据库的刷新,在此操作中,我们需要

建立jsoncpp的writer对象以及文件输出流

Json::StyledWriter sw;
ofstream os;

jsoncpp常用的写对象有Writer和StyledWriter,前者对数据直接进行写入,后者在写入时会遵循一定的格式,会让文件可读性更高,因此采用后者

用输出流打开文件

os.open(bookInfoPath, ios::out | ios::trunc);

由于我们要刷新数据库,因此我们采用trunc模式,对文件内容进行清除

将数据输出,关闭文件

os << sw.write(root);
os.close();

到这里jsoncpp最常用的用法介绍完毕

3.2 Windows.h conio.h

由于这是一个图书馆管理系统,因此希望用户与应用之间的交互更简便,界面更直观,而我们这个系统是控制台应用,因此可以采用Windows.h来创建多界面,可刷新,交互性更好的系统。至于conio.h,它里面的函数可以检测键盘事件,可以配合前者进行更多可编程的操作。

在Windows.h中,是根据句柄来标识窗体的,因此我们需要获得一个句柄,后续的所有操作均建立在此句柄上

HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);

我们可以设置窗口名称

SetConsoleTitleA("图书馆管理系统");

在Windows.h中,有几个常用的结构体,这些结构体是几个常用函数中的参数
CONSOLE_CURSOR_INFO
这个结构体存储着光标的信息,可以设置它使得光标可见(不可见)

COORD
SHORT
COORD结构体是存放坐标信息的,它里面的数据是SHORT类型的,在窗体中,左上角为原点(0,0),x轴往右,y轴往下

SMALL_RECT
此结构体代表一个矩形,它的四个值依次为矩形左上角的x,y坐标以及右下角的x,y坐标,可以用于调整窗体大小

WORD
这个数据类型的数据大小为1个字节,通常用来表示颜色,字节的高4位表示背景色,低4位表示前景色

在本系统中,用到的Windows.h的操作有
1) 清空控制台内容

system("cls");

2) 设置光标是否可见 true为可见 false为不可见

CONSOLE_CURSOR_INFO cursorInfo = { 1, false };
SetConsoleCursorInfo(hOut, &cursorInfo);

3) 设置颜色

WORD color = 0x02;
SetConsoleTextAttribute(hOut, color);

在颜色中,前景色和背景色均有15种(4位),颜色与值的对应关系为:
(前者是背景色,值乘以了16,后者是字体颜色)

在这里插入图片描述
在本系统中采用的背景色均为黑,用的前景色有
7 —— 白色,用于正常字体打印
1、2 —— 蓝色和绿色,用于打印页面的标题和有关名称的栏
6 —— 黄色,用于打印警告信息
4 —— 红色,用于打印错误信息

4) 设置光标位置

pos = { x, y };
SetConsoleCursorPosition(hOut, pos);

可以将光标移动到(x,y)处
该操作是系统用得最多的操作,因为我们要想打印一个理想的画面,就要在指定的位置输出指定的内容,我们要用户在指定的位置输入,就要先将光标移到那里。

5) 检测键盘活动(conio.h的函数)

while (1)
{
	if (_kbhit())
	{
		i = _getch();
		if (i == 72 || i == 80)
		{
			
		}
		else if (i == 13)
		{
			
		}
	}
}

这是监测键盘事件的代码结构,我们设置死循环一直等待键盘敲下,如果有键盘被敲下kbhit()函数(在VS2017中,要求使用_kbhit())返回真,便可用整形变量获得输入(getch(),_getch() in VS2017),键盘的每个键都对应着一个数,通过判断便可进入相应的操作循环中。对于获得键盘每个键对应的数,我编写了一个小程序以获取想要的键的对应

while(1)
{
    if (kbhit())
    {
        i = getch();
        cout << i << " ";
    }
}

用得最多的键为:上箭头——72;下箭头——80;回车——13

4. 系统操作逻辑

该系统由多个页面组成,每个页面操作的是系统中不同的功能,页面的进入退出关系由下图所示
在这里插入图片描述
对于界面的构造,我们将区分为静态界面及动态界面,在以上的界面中,静态界面有:学生查看个人信息界面、管理员修改书籍信息界面、管理员删除书籍界面;
在静态界面中,用户不需要与系统进行互动,或者只与系统发生一次互动(一次的意思是不会产生循环互动行为),这些静态页面会由Student类和Admin类相应的函数进行构造

相对于提及的三个静态界面,其余所有界面都是动态界面,动态画面的意思是,交互界面会与用户循环互动,直至用户给出退出界面的命令,在系统中,每个动态页面的交互程序都是Student类和Admin类中的函数,因为这些页面需要不断地刷新,因此我们需要一个基本画面,这个画面是静态的(与上面提到的静态有所不同,这里的静态是顾名思义的静态),动态画面建立在静态画面的基础上,因此在代码文件中,建立了 createPage.cpp createPage.h 来存放创造静态画面的函数,Student端和Admin端在与用户的动态互动中可以调用相应的函数来刷新页面,因此Student和Admin里的类函数就可以专注与实现图书馆管理系统的本质功能:根据用户的行为来对数据库进行增删查改。

createPage文件里面的函数的函数名是与User文件里面学生类、管理员类下的函数的函数名有直接关系的,可以很快地找到哪个操作功能对应着哪个静态页面构造函数
比如:

void Student::Login(HANDLE hOut)void createStuLoginPage(HANDLE hOut)
void Admin::Login(HANDLE hOut)void createAdminLoginPage(HANDLE hOut)

在页面的嵌套转换中,我们使用while(1)循环嵌套来实现,比如,页面2是页面1的子页面,我们可以用这种结构来实现页面间的转换:

页面1动态交互函数(功能函数){
	创造页面1静态页面;
	操作…;
	while(1){
		if (获得进入页面2的命令){
		页面2功能函数();
		创造页面1静态页面;//在每次页面2返回后都要刷新回父页面
		}
	}
}

关于如何进入子页面和返回父页面,系统根据不同的场景提供了两种不同的方式,一种是光标选择式,一种是输入命令式,
在光标选择式中,可以上下移动光标,然后回车选择需要进入(或返回)页面:
在这里插入图片描述
在这种方式中,每个光标(=>)的x坐标都是固定的,而每个不同的y坐标都对应着不同的子页面,在用户按下上下箭头时,会根据键位修改y坐标,刷新光标位置,当用户按下回车键后,根据当时的y坐标来判断进入哪个子页面。

在命令输入式下,系统设置为:只要用户能输入字符,则只要用户输入exit,就回返回上一页面,在输入命令进入子页面的过程中,可以输入特殊的命令来进入子界面

如,我们在搜索书籍得到结果后,会有如下界面
在这里插入图片描述
如果我们想返回,则在搜索栏输入exit,就会返回前图(管理员主页)的页面,如果我们要查看书籍的具体信息(管理员查找书籍页面只有一个子页面——书籍具体信息页面),我们可以输入more命令来得到,比如我想看第1本书的信息,则输入”more1”,如果想看第3本,则”more3”,而在按ISBN检索时,检索成功只会出现一本书,此时输入任意more命令都可以进入更多信息

我们在输入more1命令后,进入深度学习这本书的具体信息页面:
在这里插入图片描述

5. 系统不足

1.模糊搜索的算法可以优化,理想的情况是,算法能区别中英文字符,将一个汉字看作串的一个成员,而不是以两个连续字节来表示,其次,英文可能需要将一个单词看作一个个体,这就牵扯到更复杂的字符串相似度算法了。

2.由于是初次接触使用Windows.h的编程,因此对这个库的更高级的用法还不是很熟悉,在项目中只使用了较简单的几个用法,这可能是导致编写交互画面的代码量巨大的原因(因为在一个画面中需要对放置在特定位置的字进行逐一的光标坐标转换,打印,改变颜色),相信如果能使用更高级的用法,编写人性化控制台交互程序的代码会变得更清爽;还有一点是,交互画面中,每个字符串的位置都是绝对位置(直接给定坐标),不是相对位置(比如用比例来表示),这就导致用户不能对控制台进行缩放,缩放后字符串的位置会混乱

3.用户输入时的交互需要优化,在系统的代码中,用户输入是直接使用cin输入的,这导致如果用户什么都没有输入,按下了回车,光标会跳到下一行,虽然用户可以继续输入,但是这不符合交互逻辑。

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
开题报告 经过大学四年理论课程的学习,以及校内校外的实践,极大的丰富了自身的理论基础,并且也具备了一些处理简单问题的能力,但即将踏上社会的我深知这还远远不够,因此我利用了毕业设计的机会,在老师的指导下去完成一个具有挑战性的,其具体应用及社会服务相结合的项目,深知通过对它的开发,将对我的能力有更高层次的突破。 开发软件的一个目的是针对目前普遍的图书管理系统存在的功能不全,操作复杂,系统要求高等一系列问题,而设想一个具有个性化的图书管理系统。该管理系统与MIS系统相联系,在图书馆内部建成可靠,方便,并且功能齐全的MIS系统。从而在图书馆对新旧书的反应;对书籍借阅的管理能力;对读者和图书馆工作人员的管理能力;对图书馆管理人员软件操作的适应时间和操作感觉这些方面都将大大的提高。当然对该软件的态度是渴望获得显著的社会效益。 开发软件的另一个目的是使其具有强大的实用价值,即它可以满足中小型图书馆的借阅与管理的需要。在一般的学校与科研机构,其下属的很多分支的研究中心,试验中心,各个学院,它们往往都有着自己的规模较大的图书资料室。而这些图书资料室由于本身规模不大因此其对书籍或资料的管理模式一般都比较旧,而且在没有能力也没有必要引进大型的图书馆管理软硬件的基础上,寻求一个针对中小型图书资料室的管理软件是必要的。 这种管理软件对硬件的要求很低,一般有一个比较简单的服务器与PC机组成的网络即可,再加上使用比较廉价、性能不错的软件,这样就可以以较低的成本来实现一个足够使用的功能,而这种模式也正满足了那种中小型资料室,图书馆的要求。但是目前对这种管理软件的开发还处于一种比较原始的阶段。开发者往往都是出于资金和时间的顾虑使用比较陈旧的技术,并且各为己见,并且很少涉及网络。可是当今是一个网络化的社会,像资料室,图书馆这样的信息机构不能与网络联系起来岂不可惜,再加上如今单机版的图书管理软件多如牛毛,去开发一个类似的软件无疑是一种在时间上、精神上和资源上的极大浪费。所以出于多方面的考虑觉得有必要为这种有需要的中小型图书馆,资料室开发一种基于网络的图书资料管理软件。 项目的具体目的: 1. 实现图书馆对外借书,还书的简易操作,提高图书馆对最平凡工作的效率。 2. 实现图书馆对所藏图书的按类别,书名等多方面的查询,最大的方便读者和图书馆工作人员对所需图书的查询。 3. 建立图书馆外借读者数据库,方便工作人员对读者进行有效管理。 4. 建立图书馆工作人员数据库,限定每个工作人员对软件操作的权限,最大限度的保护数据库。 5. 实现图书馆对新书入库,旧书注销的简单处理,并且建立书籍档案,方便进货。
图书馆流通管理软件 作为面向对象程序设计的第一步,在实际系统中,识别对象和类是很重要的。在这里,我们以图书馆流通管理系统为例,介绍识别对象与类的一般方法。 1、对象和类的识别 在一个信息系统内识别对象最简单也是最主要的方法是对具体事物的属性和功能进行分析,一旦识别到一个对象,就能识别出相同类型的所有对象,把它们归纳为类。 图书馆流通系统内,有书、读者、借书证、管理人员、借书还书行为等对象和事件,他们分别各是一个群体。例如每个图书馆都有几万甚至几十万册图书,每册图书都是一个对象,它们形成图书类(在此暂时用Item款目表示)。相应的,在图书流通系统内,还可以得到以下类: 读者(Reader)类,在图书馆注册的每位借书人都是一个读者类的对象; 图书借阅信息类(Loan),每次发生借书行为,都产生一个借阅信息的对象; 管理人员类(Manager),是借书还书行为的操作人。 借书证对象虽然是实实在在的对象,但每位读者对应一本借书证,且只需要知道其编号,因此不需要另外定义类,只作为读者类的一个数据成员。 同一个信息系统,从不同的角度分析,或根据要求的不同,有不同的侧重面,这样建立的对象模型不同,可能得出的分类方法也不同。 2、对象属性的识别 每个对象的情况称为对象的属性,同类型的对象具有共同的属性,只是每个对象的属性值不一定相同。属性是对一个对象状态的描述,如“在馆图书类”,从流通管理的角度来看,应包含书名Title、作者名Author、分类号IndexCode、册数Number、条码号BarCode等属性。 其它类的属性如下: 读者类,包含姓名Name、职务Position、年龄Age、借书证编号Code等。一个读者允许借阅若干册书,在此用一个Item的数组items保存相应信息。另外对读者所借书册数要统计,定义一个计数的成员Counter; 图书借阅信息类,包含所借书item、借书人reader、借书操作员manager等; 管理人员类,包含姓名Name、年龄Age、工号Code等; 管理人员类只有在发生借书行为时才作为操作人员记录下来,可以作为借书信息类的一个数据成员记录备查,程序中未定义其对象。 同一个类(对象),从不同的角度分析,或根据要求的不同,描述它的属性也可能不一致。 3、对象功能的确定 对象的功能指为了达到目的必须执行的动作,或是对象对所发生事件的反应。功能也可称为对象的操作。 在图书馆流通管理系统内,图书类应包含为各属性赋值(Set…)的操作、读取条码(GetCode)和显示图书基本信息(Show)的操作,另外还定义了缺省构造函数和拷贝构造函数。 读者类,需定义为各属性赋值(Set…)的操作、读取借书证号的操作,借书和还书需要向所借书数组中添加或减少书,定义AddBook和DelBook两个操作,还有显示所借书的操作ShowBooks。 4、对象和类的标记 在这里采用科德(Coad)定义的面向对象方法的描述符号,这一方法是Peter Coad和Ed YourDon于1990年提出的,这一方法的优点是图形简单、易于理解和掌握,但对类和对象属性的访问权限无法进行有效的描述。 科德标记法中,使用一个圆角矩形表示类,矩形内部分为三个部分,上部写类名,中部写属性(数据成员),下部表示该类的操作(函数成员)。对象是类的实例,在科德标记法中,在相应类标记外加一个圆角矩形框表示对象,并将矩形内部的表示类名的地方写上对象名,如图4.15。 科德表示法可形象地用扑克牌示意图来表示。如图4.16,一个类可能有0个或1个2个3个甚至更多对象,但没有实例化以前,它们在图上只画一个圆角矩形,就像一叠扑克牌,只看见最上面的一张牌;当把这叠牌散开时,它们是若干个对象,就像类被实例化了。 科德标记法还有表示连接的符号,如图4.17,一个带线段的半圆弧表示A是通用类,B是特殊类,中间带空心三角形的线段表示A类包含B类即整体与部分的关系,中间带实心箭头的线段表示A向B发送消息,一段实线表示对象之间的连接线。 根据上面对的图书馆流通管理系统的分析,在图4.18中,用科德标记法表示Item, Reader, Manager, Loan四个类,它们的数据成员及其类型、函数成员的原型都在图中标出,但无法标出它们的访问权限。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值