本工程是一个图书管理系统,是 OOP 课程的大作业。
-
Ubuntu
-
需要安装
MongoDB
-
依赖
mongo-cxx-driver
(git 仓库在这里)
在根目录下执行
make
即可
先启动 MongoDB。
在根目录下执行
./main
即可
本工程大体分为 5 部分。分别是:
-
核心类
-
数据库(
Database
):负责直接与数据库交互,提供数据库接口 -
服务器(
Server
):所有需要在服务器跑的代码 -
控制器(
Controller
):所有在用户本地需要计算、与Server
交互的流程 -
客户端(
Client
):负责直接与用户交互
整个架构使用代理/委托模式,Server
包装Database
的接口,Controller
包装Server
的接口,Client
包装Controller
的接口,并最终呈现给用户。
程序的运行流程即是上面的顺序(Client
,Controller
,Server
,Database
)
本文档将分这 5 部分来进行介绍。
这部分包含了贯穿其他所有部分的核心类,包括:
-
Field
:“字段”类,是一个与std::pair<string,string>
作用类似的类。- 重载了
=
运算符,两个对象键相同即判定为相等。(每个对象每个键最多对应一个值)
- 重载了
-
AbstractObject<Container>
(及其派生类):使用模板实现,用容器Container
存放所有Field
。派生出Object
类和Search
类。-
Object
类继承自AbstractObject<set>
,将派生出User
、Book
、PracticalBook
、Record
四个类。分别为用户类、书本信息类(相同书本的集合,存储相同书本的信息,如ISBN、出版社等)、实体书本类(存储每一本具体书籍,与借阅记录对应)、记录类(包含借、还、浏览、预览记录)-
对于每一个类,我们都设置了成员函数
explicitKey()
、implicityKey()
、uniqueKey()
(使用虚函数实现),分别返回显示给用户看的键、隐藏的键(如Status
等)、可以用于确定唯一对象的类(如用户的用户名) -
我们并未去设计用户、书籍各字段的格式规则,但
Object
预留了判断格式的相关接口。
-
-
Search
类继承自AbstractObject<multiset>
(考虑到可能对同一字段有多个检索要求),将派生出CompleteMatchingSearch
(完全匹配检索)类、ReSearch
(正则表达式检索)类。- 这两个派生类本身没有任何实现上的区别,在检索信息时,会根据不同的传入类型(通过虚函数判断)执行对应的检索策略。
-
-
Password
:封装密码的类(用户密码使用 MD5 加密进行存储,保证安全性)。 -
Content
:封装预览内容的基类。派生出TxtContent
用于预览txt
格式存储的文件。(这么设计的目的是为了方便添加对新文件类型的支持)- 预览使用虚函数实现。
这是包装数据库的类(数据库使用 MongoDB),包装了mongocxx
(MongoDB 的 c++ 驱动)。负责直接与数据库交互
这个类包含如下接口(以下所有“对象”均包括Object
的 4 个派生类。使用虚函数判断派生类类型):
-
userExist
:判断用户是否存在并检查权限。(检查权限的目的是为了防止用户在前端做骚操作) -
isAdmin
:判断用户是否为管理员。 -
isRoot
:判断用户是否为 Root。 -
findOne
:利用 uniqueKey 精确查找唯一的对象(如根据用户名查找对应用户)。 -
objectExist
:判断对象是否存在。
(上面的所有函数都会判断对象是否被冻结)
-
newRecordId
:在新建一条记录时会调用,返回新记录的编号。 -
modifyPassword
:修改用户密码。
(下面4个函数都使用模板实现)
-
add
:添加对象。 -
update
:更新对象。 -
remove
:删除对象。 -
search
:检索对象。(传入Search
类对象,会根据虚函数判断其类型,并执行对应检索策略)
此外,为了方便将一个AbstractObject
的派生类转换为mongocxx
需要的document
类,我们设置了toDocument
以及一些类似的私有成员函数,来支持Object
、Search
类的转换。
包装Database
类,主要负责权限判断(防止用户在前端乱搞)和一些其他运算、处理。
下面将对接口做冗长的列举,这些接口与需求有着较大联系,本质上是使用代理/委托模式对数据库接口的扩充:
-
search
:检索对象。 -
add
:添加对象。 -
update
:更新对象。 -
remove
:删除对象(与需求中的要求不同的是,我们认为这个操作是十分危险的,因此这个接口只对 Root 开放。作为替代品,我们提供了“冻结”用户/书本的接口,在下面可以看到)
(以上接口均使用模板实现)
-
userLogin
:用户登录(查找用户、检查密码、返回用户信息) -
modifyPassword
:修改密码 -
borrowBook
:提出借阅申请(同时生成对应的 Record) -
returnBook
:提出归还申请(同时生成对应的 Record) -
acceptRequest
:接受申请(包括了借阅申请和归还申请)(同时更新对应记录的状态) -
rejectRequest
:接受申请(包括了借阅申请和归还申请)(同时更新对应记录的状态)
(以下两个冻结/解冻的接口使用模板实现,并只针对User
、Book
、PracticalBook
类,不支持冻结一个Record
)
-
freeze
:冻结对象。 -
unfreeze
:解冻对象。 -
browseBook
:浏览一本书籍的信息,用于提示数据库生成对应浏览记录。 -
previewBookContent
:预览一本书籍。(同时生成对应的 Record)
特别说明:由于Controller
的负责人的时间原因,并没有完全利用好Server
的所有接口
这是包装 Server
的类,主要负责直接与客户端进行交互(接受客户端的不同命令)并且调用相应的Server
类接口进行后台计算。包括:
- *
AbstractController
*类作为所有控制器的基类,保存所有Controller
的Server
指针和User
类用户对象,接口如下: getSelf()
:返回用户对象。LoginController
类继承自AbstractController
,负责和登录页面交互,新增接口如下:ReaderController
类继承自AbstractController
,为Reader权限的客户端提供接口。接口如下:type
:返回权限。getBook
:从列表返回一类书。getPracticalBook
:从列表返回一本书。getRecord
:从列表返回一条记录。searchBook
:搜索书籍。browseBook
:浏览书籍。borrowBook
:借阅书籍。bookToPractical
:对于一类书找到其对应不同的实体书。show
:显示列表对象的信息。listBorrowingBooks
:展示在借书籍。returnBook
:归还书籍。readrecord
:阅览记录。
AdminController
类继承自ReaderController
,为Administrator权限的客户端提供接口。新实现接口如下:getUser
:从列表返回用户。findUser
:搜索用户。registerUser
:注册用户。addBook
:通过一类书来添加具体的书。addNewBook
:添加一本新书 (无同类书存在的书)。showPendingBook
:显示尚未处理的借换请求。deal
:处理一个请求。editBook
:编辑一本书的信息。showFreezeBook
:显示冻结中的书。showFreezeUser
:显示冻结中的用户。readBookRecord
:显示一本书的记录。
下面的函数使用模板实现,包括:
freeze
:冻结一个对象,函数freezeBook
和freezeUser
分别负责使用freeze
冻结书和用户unfreeze
:解冻一个对象,函数unfreezeBook
和unfreezeUser
分别负责使用unfreeze
解冻书和用户RootController
:类继承自AbstractController
,为Reader权限的客户端提供接口。新实现接口如下:removeObject
:由模板形式实现,用来删除一个对象,函数removeUser
和removePracticalBook
分别用来负责删除用户和书。
此外,在AdminController,RootController中新实现的接口均使用虚函数,在其基类里以空函数的形式实现,从而在实现多态的时候体现不同权限的差别。
这是包装Controller
的类,主要负责读入客户的指令并且输出相应的结果。
下面将对命令行界面的函数成员做冗长的列举,这些接口与需求有着较大联系,本质上是使用代理/委托模式调用控制器中接口并可视化。
searchBook
:实现搜索书籍的可视化。higherSearchBook
:实现高级搜索并可视化。afterSearchBook
:管理搜索书籍后的操作。browseBook
:实现浏览书籍的可视化。editBook
:实现编辑书籍信息的可视化。borrowBook
:实现借阅书籍的可视化。listBorrowingBooks
:显示在借书籍列表的可视化。returnBook
:实现归还书籍的可视化。modifyPassword
:实现修改密码的可视化。searchUser
:实现搜索用户的可视化。afterSearchUser
:管理搜索用户之后的操作。showPending
:将代处理的请求列表并可视化。deal
:实现处理请求的可视化。freezeUser
:实现冻结书籍的可视化。freezeUser
:实现冻结用户的可视化。listFreezeBook
:将冻结中的书籍列表并可视化。listFreezeUser
:将冻结中的用户列表并可视化。unfreezeBook
:实现解冻书籍的可视化。unfreezeUser
:实现解冻用户的可视化。readUserRecord
:实现阅读列表中读者的记录的可视化。readBookRecord
:实现阅读列表中书籍的记录的可视化。readSelfRecord
:实现阅读本人记录的可视化。switchRecord
:管理阅读记录列表后的操作。addNewBook
:添加新书的可视化。addBookFromExist
:添加有同类书的可视化。registerUser
:注册用户的可视化。removeBook
:删除列表书籍的可视化。removeUser
:删除列表用户的可视化。
为了复用输出信息的代码,该类还包含了以下私有函数成员:
showBook
:输出一本书的具体信息。showRecord
:输出一项记录的具体信息。showUser
:输出一个用户的具体信息。outputInfo
:输出列表信息。
同时,该类还给出了唯一的接口main,用来读入用户的一级指令从而调用上述函数,实现图书检索和管理的目的。
王聿中:核心类、Database、Server
沈诣博:Controller、Client(命令行界面)
俞宸:Client(图形界面)
同时,所有人各自负责自己部分的文档。
这是我们的 git 仓库,工作量可以大致在这里体现。
感谢刘知远老师、黄民烈老师在课堂上的精彩授课。
感谢本课程的所有助教的课后指导。
感谢徐良钦同学小组、邢健开同学小组的git仓库给我们的一些启发与帮助。
感谢所有与我们讨论过本课程内容或大作业相关问题的同学。