一、项目需求
(一)、用户登录(可选择后台管理员或前台管理员)
(二)、后台管理员身份
1、书籍录入(存入书籍文件)
2、借阅人信息录入(存入借阅人信息文件)
(三)、前台管理员身份
1、记录借书(存入借书信息文件)
2、记录还书(存入还书信息文件)
二、方案
1、设计一个登录菜单
2、设计两个界面(前台、后台)
3、进入前台或后台之后显示一个子菜单
4、设计三个类
4.1、书籍类Book
4.2、人类People
4.3、初始化类Init
三、框架
四、源代码
1、主程序
#include<Windows.h>
#include<iostream>
#include"Book.h"
#include"People.h"
#include"FUNS.h"
#include"Init.h"
using namespace std;
int main(void)
{
WinSize(WIN_WIDTH, WIN_HEIGHT);
system("color B1");
Init init;//初始化
init.Initfrombookfile();
init.Initfrompeoplefile();
People people;
bool choice = Menu();
while (1)
{
while (choice == true)//进入后台
{
Login(choice);//登录
int tag = SecondMenu(choice);//二级菜单
while (tag == BOOKINPUT || tag == PEOPLEINPUT)
{
if (tag == BOOKINPUT)
{
init.Initbookfile();//书籍录入
}
else
{
init.Initpeoplefile();//借书人信息录入
}
tag = SecondMenu(choice);//退出循环之后重新显示二级菜单
}
choice = Menu();//,退出后台操作后,重新显示主菜单
}
while (choice == false)//进入前台
{
Login(choice);//登录
int tag = SecondMenu(choice);//二级菜单
while (tag != RETURN_FROM_FRONT)
{
switch (tag)
{
case BORBOOK://借书
{
cout << "请输入姓名:";
string name;
cin >> name;
cout << "请输入id:";
int id=0;
CheckCin(id);
People people(name, id);
if (people.BorrowBook())
{
cout << "借书成功!\n";
system("pause");
}
else
{
cout << "书籍不存在或库存不足或等级不够!\n";
system("pause");
}
break;
}
case GIVEBOOK://还书
{
cout << "请输入姓名:";
string name;
cin >> name;
cout << "请输入id:";
int id=0;
CheckCin(id);
People people(name, id);
if (people.Giveback())
{
cout << "还书成功!\n";
system("pause");
}
else
{
cout << "你没有借这本书!\n";
system("pause");
}
break;
}
case QUERY: //查询
{
people.Query();
break;
}
}
tag = SecondMenu(choice);
}
choice = Menu();
}
}
system("pause");
return 0;
}
2、函数声明和一些常量
#pragma once
#include<string>
using namespace std;
//常量
const int WIN_WIDTH = 80;//窗口宽度
const int WIN_HEIGHT = 30;//窗口高度
enum { BOOKINPUT = 1, PEOPLEINPUT, RETURN_FROM_BACK};
enum { BORBOOK = 1, GIVEBOOK, QUERY, RETURN_FROM_FRONT };
#define BOOKFILE "书籍信息.txt"
#define PEOPLEFILE "借书人信息.txt"
#define BORROWFILE "借书信息.txt"
#define GIVEBOOKFILE "还书信息.txt"
const string BACK_ADMINISTRATOR_ACCOUNT = "abcdefg123"; //后台管理员账号
const string BACK_ADMINISTRATOR_PASSWORD = "123456"; //后台管理员密码
const string FRONT_ADMINISTRATOR_ACCOUNT = "abcdefg"; //前台管理员账号
const string FRONT_ADMINISTRATOR_PASSWORD = "654321"; //前台管理员密码
//函数声明
void WinSize(int cols, int lines);//设置窗口
void CheckCin(int& NUM);//检查输入流
bool Menu();//true进入后台界面,false进入前台界面
void Login(bool bl);//登录
int SecondMenu(bool bl);//二级菜单
void IfNoOpen(ofstream& fout);//处理文件打开失败的情况
void IfNoOpen(ifstream& fin);
3、实现
#include<iostream>
#include<fstream>
#include<conio.h>
#include<vector>
#include<Windows.h>
#include"FUNS.h"
#include"Book.h"
void WinSize(int cols, int lines)
{
char ch[255];
sprintf_s(ch, "mode con cols=%d lines=%d", cols, lines);
system(ch);
}
void CheckCin(int& NUM)
{
cin >> NUM;
while (!cin)
{
cin.clear();//重置输入流
while (cin.get() != '\n')
{
//显示一个空白,再退格
//cout << "\b \b";
continue;
}
cin >> NUM;
}
}
bool Menu()
{
while (1)
{
system("cls");
cout << "1.后台管理员登录\n2.前台管理员登录\n3.退出\n";
cout << "请选择:";
int choice;
//cin >> choice;
CheckCin(choice);
switch (choice)
{
case 1:
{
system("cls");
cin.get();//读取输入流中留下的换行符,防止它影响接下来的输入
return true;
}
case 2:
{
system("cls");
cin.get();
return false;
}
case 3:exit(0);
default:break;
}
}
}
void Login(bool bl)
{
int count = 3;//最大输入次数
while (count != 0)
{
bl ? cout << "后台管理员登陆界面\n" : cout << "前台管理员登陆界面\n";
cout << "请输入账号:";
string name;
getline(cin, name);
cout << "请输入密码:";
string pwd = "";
while (1)//逐字符输入密码
{
char c;
c = _getch();
if (c != 13)//按下回车结束输入
{
if (c == 8)//8是退格键的字符编码
{
if (pwd.length() > 0)//如果有输入
{
pwd = pwd.substr(0, pwd.length() - 1);//删除字符串末尾的一个字符
cout << "\b \b";//退格输出空格再退格(覆盖原来的*)
continue;
}
continue;//如果字符串本来就是空的,则按下退格键不做任何操作
}
pwd += c;
_putch('*');//如果输入正确,用*隐藏密码
}
else
{
cout << endl;
break;
}
}
count--;
if (name == (bl ? BACK_ADMINISTRATOR_ACCOUNT : FRONT_ADMINISTRATOR_ACCOUNT) &&
pwd == (bl ? BACK_ADMINISTRATOR_PASSWORD : FRONT_ADMINISTRATOR_PASSWORD))
{
system("cls");
break;
}
else
{
cout << "账号或密码错误,请重新输入!\n";
cout << "剩余输入次数" << count << "次\n";
if (count == 0)
{
system("pause");
system("cls");
cout << "今日次数已用完,请明日再试!\n";
exit(EXIT_FAILURE);//终止程序
}
system("pause");
system("cls");
}
}
}
int SecondMenu(bool bl)
{
system("cls");
if (bl)//后台选项
{
while (1)
{
system("cls");
cout << "1.书籍录入\n2.借阅人信息录入\n3.返回\n";
cout << "请选择:";
int choice;
//cin >> choice;
CheckCin(choice);
switch (choice)
{
case BOOKINPUT:
{
system("cls");
cin.get();//读取输入流中留下的换行符,防止它影响接下来的输入
return BOOKINPUT;
}
case PEOPLEINPUT:
{
system("cls");
cin.get();
return PEOPLEINPUT;
}
case RETURN_FROM_BACK:
{
system("cls");
cin.get();
return RETURN_FROM_BACK;
}
default:break;
}
}
}
else//前台选项
{
while (1)
{
system("cls");
cout << "1.借书\n2.还书\n3.查询\n4.返回\n";
cout << "请选择:";
int choice;
//cin >> choice;
CheckCin(choice);
switch (choice)
{
case BORBOOK:
{
system("cls");
cin.get();//读取输入流中留下的换行符,防止它影响接下来的输入
return BORBOOK;
}
case GIVEBOOK:
{
system("cls");
cin.get();
return GIVEBOOK;
}
case QUERY:
{
system("cls");
cin.get();
return QUERY;
}
case RETURN_FROM_FRONT:
{
system("cls");
cin.get();
return RETURN_FROM_FRONT;
}
default:break;
}
}
}
}
void IfNoOpen(ofstream& fout)
{
if (!fout.is_open())
{
cout << "文件打开失败!\n";
exit(1);
}
}
void IfNoOpen(ifstream& fin)
{
if (!fin.is_open())
{
cout << "文件打开失败!\n";
exit(1);
}
}
4、Book类
#pragma once
#include<string>
using namespace std;
class Book
{
public:
Book();
Book(string bookname, int sernum, string category, unsigned inventory);
~Book();
string GetBookname()const;//查看书名
int GetSernum()const;//查看编号
string GetCategory()const;//查看类别
unsigned GetInventory()const;//查看库存
bool Addbook();//库存+1(还书)
bool Subbook();//库存-1(借书)
friend class People;
friend ostream& operator<<(ostream& os, const Book& book);//重载<<输出到控制台
friend ofstream& operator<<(ofstream& fout, const Book& book);//重载<<输出到文件
private:
string bookname;//书名
int sernum;//编号
string category;//类别
unsigned inventory;//库存
};
#include "Book.h"
#include<iostream>
#include<fstream>
Book::Book()
{
bookname = "空";
sernum = 0;
category = "空";
inventory = 0;
}
Book::Book(string bookname, int sernum, string category, unsigned inventory)
{
this->bookname = bookname;
this->sernum = sernum;
this->category = category;
this->inventory = inventory;
}
Book::~Book()
{
}
string Book::GetBookname() const
{
return bookname;
}
int Book::GetSernum() const
{
return sernum;
}
string Book::GetCategory() const
{
return category;
}
unsigned Book::GetInventory() const
{
return inventory;
}
bool Book::Addbook()
{
inventory++;
return true;
}
bool Book::Subbook()
{
if (inventory == 0)
{
cout << "库存不足!\n";
return false;
}
inventory--;
return true;
}
ostream& operator<<(ostream& os, const Book& book)
{
os << "书名:" << book.bookname << " 编号:" << book.sernum << " 类别:" << book.category << " 库存:" << book.inventory << endl;
return os;
}
ofstream& operator<<(ofstream& fout, const Book& book)
{
fout << "书名:" << book.bookname << " 编号:" << book.sernum << " 类别:" << book.category << " 库存:" << book.inventory << endl;
return fout;
}
5、People类
#pragma once
#include<string>
#include<vector>
#include"Book.h"
using namespace std;
class People
{
public:
People();
People(string name, int id, unsigned booknums=0, int level = 1);
~People();
string GetName()const;
int GetId()const;
int GetLevel()const;
unsigned GetBooknums()const;
vector<Book>havebooks;//保存借到的书
string GetTime()const;//获取时间
unsigned MaxBooks();//当前等级最大借书数量
bool BorrowBook();//借书
bool Giveback();//还书
void Query();//查询
friend ostream& operator<<(ostream& os, const People& people);//重载<<输出到控制台
friend ofstream& operator<<(ofstream& fout, const People& people);//重载<<输出到文件
private:
string name;
int id;
unsigned booknums;
int level;
};
#include<iostream>
#include<fstream>
#include<sstream>
#include<ctime>
#include<Windows.h>
#include "People.h"
#include"FUNS.h"
#include"Init.h"
People::People()
{
name = "空";
id = 0;
level = 0;
booknums = 0;
}
People::People(string name, int id, unsigned booknums, int level)
{
this->name = name;
this->id = id;
this->level = level;
this->booknums = booknums;
}
People::~People()
{
}
string People::GetName() const
{
return name;
}
int People::GetId() const
{
return id;
}
int People::GetLevel() const
{
return level;
}
unsigned People::GetBooknums() const
{
return booknums;
}
string People::GetTime() const
{
stringstream str;
time_t t = time(NULL);//当前时间戳
tm localt;//保存本地时间戳
localtime_s(&localt, &t);//将当前时间戳转换成本地时间戳
str << localt.tm_year + 1900 << "年 "
<< localt.tm_mon + 1 << "月 "
<< localt.tm_mday << "日 "
<< localt.tm_hour << "时 "
<< localt.tm_min << "分 "
<< localt.tm_sec << "秒" << endl;
return str.str();
}
unsigned People::MaxBooks()
{
unsigned maxbooks;
maxbooks = level * 3;
return maxbooks;
}
bool People::BorrowBook()//借书
{
int sernum = 0;
cout << "请输入编号:";
//cin >> sernum;
CheckCin(sernum);
//判断是否有这个编号
Init init;
init.Initfrombookfile();//筛选编号和修改书籍信息会用到这个初始化
init.Initfrompeoplefile();//修改借书人信息会用到这个初始化
for (auto book = (init.books).begin(); book != (init.books).end(); book++)//从所有书籍中筛选
{
if (sernum == book->sernum)//编号相同
{
if (book->Subbook()&& booknums<MaxBooks())//能否借书(考虑库存和等级),如果能借书Subbook()方法会把库存-1
{
//添加借书记录
ofstream foutborrow;
foutborrow.open(BORROWFILE, ios::out | ios::app);//打开文件,只追加
IfNoOpen(foutborrow);
//把这本书的信息,借书时间,借书人输入到借书信息
string line(100, '-');
foutborrow << "书名:" << (*book).bookname << endl << GetTime() << "借书人:" << name << endl << line << endl;
foutborrow.close();
//修改书籍信息
//待优化:要改的只是库存,不需要把所有数据都覆盖*******************************************优化:只修改指定数据
ofstream foutbook;
foutbook.open(BOOKFILE, ios::out | ios::trunc);//打开文件,截短
IfNoOpen(foutbook);
for (auto it = (init.books).begin(); it != (init.books).end(); it++)
{
foutbook << (*it);
}
foutbook.close();
//修改借书人信息
ofstream foutpeople;
foutpeople.open(PEOPLEFILE, ios::out | ios::trunc);//打开文件,截短
IfNoOpen(foutpeople);
bool tag = false;//一个临时标记
for (auto it = (init.people).begin(); it != (init.people).end(); it++)
{
if (id == it->id)//如果有这个人(借过书)
{
//保存已借到的书
(it->havebooks).push_back(*book);//这里的Book类库存已经改了
(it->booknums)++;
tag = true;
}
foutpeople << (*it);
}
if (!tag)
{
//进入这里说明遍历了容器没找到这个人
//把这个人添加进去
booknums++;
havebooks.push_back(*book);
(init.people).push_back(*this);
foutpeople << *this;
}
foutpeople.close();
return true;
}
}
}
//5.是否可升级**********************************************还没添加升级功能
return false;
}
bool People::Giveback()//还书
{
int sernum = 0;
cout << "请输入编号:";
//cin >> sernum;
CheckCin(sernum);
//判断是否有这个编号
Init init;
init.Initfrombookfile();//筛选编号和修改书籍信息会用到这个初始化
init.Initfrompeoplefile();//修改借书人信息会用到这个初始化
//for (auto it = havebooks.begin(); it != havebooks.end(); it++)//从已借到的书里面筛选
for (auto it = init.people.begin(); it != init.people.end(); it++)//从借书人里面筛选
{
if (id == it->id)//找到这个人
{
for (auto book = it->havebooks.begin(); book != it->havebooks.end(); book++)
{
if (sernum == book->sernum)//编号相同
{
//添加还书记录
ofstream foutgive;
foutgive.open(GIVEBOOKFILE, ios::out | ios::app);//打开文件,只追加
IfNoOpen(foutgive);
//把这本书的信息,还书时间,还书人输入到还书信息
string line(100, '-');
foutgive << "还书人:" << name << endl << "还书:" << book->bookname << endl << GetTime() << line << endl;
foutgive.close();
//修改书籍信息
ofstream foutbook;
foutbook.open(BOOKFILE, ios::out | ios::trunc);//打开文件,截短
IfNoOpen(foutbook);
for (auto it = (init.books).begin(); it != (init.books).end(); it++)
{
if (sernum == it->sernum)//找到这本书(一定有!)
{
it->Addbook();//库存+1
}
foutbook << (*it);
}
foutbook.close();
//修改已借到的书
it->havebooks.erase(book);//删除
it->booknums--;
//修改借书人信息
ofstream foutpeople;
foutpeople.open(PEOPLEFILE, ios::out | ios::trunc);//打开文件,截短
IfNoOpen(foutpeople);
for (auto it = (init.people).begin(); it != (init.people).end(); it++)
{
//if (id == it->id)//找到这个人(一定有!)
//{
// (it->booknums)--;
//}
foutpeople << (*it);
}
foutpeople.close();
return true;
}
}
}
}
return false;
}
void People::Query()
{
Init init;
init.Initfrombookfile();
while (1)
{
system("cls");
cout << "1.按书名查找\n2.按编号查找\n3.返回\n";
cout << "请选择:";
int choice;
//cin >> choice;
CheckCin(choice);
switch (choice)
{
case 1:
{
system("cls");
cout << "请输入书名:";
string bookname;
cin >> bookname;
for (auto it = init.books.begin(); it != init.books.end(); it++)
{
if (bookname == (*it).bookname)
{
system("cls");
cout << (*it);
}
}
system("pause");
break;
}
case 2:
{
system("cls");
cout << "请输入编号:";
int serialnum;
cin >> serialnum;
for (auto it = init.books.begin(); it != init.books.end(); it++)
{
if (serialnum == (*it).sernum)
{
system("cls");
cout << (*it);
}
}
system("pause");
break;
}
case 3:return;
default:break;
}
}
}
ostream& operator<<(ostream& os, const People& people)
{
os << "姓名:" << people.name << " id:" << people.id << " 已借书数量:" << people.booknums << " 等级:" << people.level << endl;
return os;
}
ofstream& operator<<(ofstream& fout, const People& people)
{
fout << "姓名:" << people.name << " id:" << people.id << " 已借书数量:" << people.booknums << " 等级:" << people.level << endl;
fout << "已借到的书:\n";
for (auto it = people.havebooks.begin(); it != people.havebooks.end(); it++)
{
fout << *it;
}
string line(100, '-');
fout << line << endl;
return fout;
}
6、Init类
#pragma once
#include<vector>
#include"Book.h"
#include"People.h"
class Init
{
public:
friend class People;
void Initfrombookfile();//从文件导出书籍信息
void Initfrompeoplefile();//从文件导出借书人信息
void Initbookfile();//初始化书籍信息
void Initpeoplefile();//初始化借书人信息
vector<Book> books;
vector<People> people;
};
#include<fstream>
#include<Windows.h>
#include<iostream>
#include<conio.h>
#include"FUNS.h"
#include "Init.h"
using namespace std;
void Init::Initfrombookfile()
{
//从书籍信息中导出数据
ifstream finbook;
finbook.open(BOOKFILE);
if (!finbook.is_open())
{
//cout << "书籍不存在!\n";
return;
}
while (1)
{
string line;
getline(finbook, line);
if (finbook.eof())
{
break;
}
//解析这一行数据
char bookname[256];//书名
int sernum;//编号
char category[256];//类别
unsigned inventory;//库存
sscanf_s(line.c_str(), "书名:%s 编号:%d 类别:%s 库存:%d", bookname, sizeof(bookname), &sernum, category, sizeof(category), &inventory);
Book book(bookname, sernum, category, inventory);
books.push_back(book);
}
}
void Init::Initfrompeoplefile()
{
//从借书人信息中导出数据
ifstream finpeople;
finpeople.open(PEOPLEFILE);
if (!finpeople.is_open())
{
//cout << "没有借书人!\n";
return;
}
while (1)
{
string line;
getline(finpeople, line);
if (finpeople.eof())
{
break;
}
//解析这一行数据
char name[256];//姓名
int id;
unsigned booknums;//已借书数量
int level;
sscanf_s(line.c_str(), "姓名:%s id:%d 已借书数量:%d 等级:%d", name, sizeof(name), &id, &booknums, &level);
People peo(name, id, booknums, level);
getline(finpeople, line);//读取无用行(“已借到的书:”)
for (int i = 0; i < booknums; i++)
{
string line;
getline(finpeople, line);
char name[256];//书名
int sernum;//编号
char category[256];//类别
unsigned inventory;//库存
sscanf_s(line.c_str(), "书名:%s 编号:%d 类别:%s 库存:%d", name, sizeof(name), &sernum, category, sizeof(category), &inventory);
Book book(name, sernum, category, inventory);
peo.havebooks.push_back(book);
}
people.push_back(peo);
getline(finpeople, line);//读取无用行:分割线
}
}
void Init::Initbookfile()
{
//将信息保存到books容器
char key = 0;//保存按键
while (key != VK_RETURN)
{
system("cls");
cout << "书籍录入\n";
string bookname;//书名
int sernum = 0;//编号
string category;//书籍类别
int nums = 0;//库存
cout << "请输入书名:";
cin >> bookname;
cout << "请输入编号:";
cin >> sernum;
cout << "请输入书籍类别:";
cin >> category;
cout << "请输入书籍数量:";
cin >> nums;
Book tempbook(bookname, sernum, category, nums);//创建一个临时书籍对象
//考虑重复添加
bool tag = false;//临时标记
for (auto it = books.begin(); it != books.end(); it++)
{
if (sernum == it->GetSernum())
{
cout << "书籍已存在!\n";
tag = true;
}
}
if (!tag)
{
books.push_back(tempbook);//把对象添加到容器中
//写入文件
ofstream fout;
fout.open(BOOKFILE, ios::out | ios::app);//打开文件,只追加
IfNoOpen(fout);
fout.seekp(0, ios::end);//定位到文件尾
fout << tempbook;
fout.close();
}
cout << "按任意键继续(回车键结束)\n";
key = _getch();
}
}
void Init::Initpeoplefile()
{
//将信息保存到people容器
char key = 0;//保存按键
while (key != VK_RETURN)
{
system("cls");
cout << "借书人录入\n";
string name;//姓名
int id = 0;
cout << "请输入姓名:";
cin >> name;
cout << "请输入id:";
//cin >> id;
CheckCin(id);
People temppeople(name, id);//创建一个临时借书人对象
//考虑重复添加
bool tag = false;//临时标记
for (auto it = people.begin(); it != people.end(); it++)
{
if (id == it->GetId())
{
cout << "借书人已存在!\n";
tag = true;
}
}
if (!tag)
{
people.push_back(temppeople);//把对象添加到容器中
//写入文件
ofstream fout;
fout.open(PEOPLEFILE, ios::out | ios::app);//打开文件,只追加
IfNoOpen(fout);
fout << temppeople;
fout.close();
}
cout << "按任意键继续(回车键结束)\n";
key = _getch();
}
}
五、其他
还有一些地方可以优化:
1、修改文件的时候应该只修改指定数据,覆盖所有数据并不合理。
2、借书/还书的时候输入了姓名和id,但是后面的判断语句只用到了id,这样会导致一个问题:张三借了书,还书的时候输入李四也能完成还书,因为只判断了id。
3、People类还有一个增加等级的功能没实现。
可能还有其他的问题,1.0版本先这样吧。