多用户图书管理系统

多用户图书管理系统

本系统是基于 C++ 开发的多用户图书管理系统,具有以下功能:

  1. 人机验证功能
  2. 用户注册和登录
  3. 用户名的修改和密码的修改
  4. 新增图书
  5. 修改图书信息
  6. 删除图书
  7. 图书查询
  8. 图书借阅
  9. 图书归还

系统从图书馆的视角出发,采用了面向对象的设计思想,使用了 C++ 的文件操作和类的封装等特性。其中使用到的 STL 在以往博客都有所涉猎。

系统的数据库采用了文本文件的形式,每个用户有一个专属的账号和密码,用于登录系统。图书信息包括图书的 ID、书名、作者、出版社、出版日期、库存数量等。用户可以根据图书的 ID、书名、作者等信息查询图书,也可以根据出版社、出版日期等信息进行查询。用户可以借出图书,系统会检查图书的库存数量,如果有库存,用户可以借出成功。用户还可以归还图书,系统会更新图书的库存数量。

系统的界面设计简洁明了,用户可以通过菜单选择不同的功能。系统的代码编写规范,易于理解和维护。

书库和用户库初始化

这里我们采用结构体来存储图书信息和用户信息。同时,利用 vector 来存储更多的图书信息,用数组来存储用户信息。为了区分不同用户的图书信息,每个元素都是一个 vector,且共有 10 个元素的数组,即vector<TuShu> tushu_list[10];,用来存储该用户的图书信息。后续可以根据需要修改为二维 vector 实现更灵活的用户管理。

代码实现:

cpp 复制代码
//书库

struct TuShu {
    int id;
    string shuming;
    string zuozhe;
    int kucun;
	int jiechu;
};

vector<TuShu> tushu_list[10];

//用户库

struct YongHu{
	string i;
	string password;
};

YongHu yonghu[10];

欢迎界面

实现效果:

复制代码
=== 欢迎使用多用户图书管理系统 ===
        当前版本:2.1.0
更新内容:	
	1.新增修改用户名功能
	2.修复了图书 ID 等数据输入非数字字符会死循环的问题
	3.修复了图书 ID 可能会重复的问题
	4.修复了部分bug
	5.新增了部分bug

产品特性:
	1.支持多用户功能(目前仅限 10 位用户且不可删改)
	2.支持图书借出和归还的功能
	3.首页提供馆存图书统计
	4.支持修改密码的功能
	5.支持人机验证的功能

该页面主要介绍了系统的版本更新内容和产品特性,没有代码难度和技术难度。

代码实现:

cpp 复制代码
void HuanYing(){
	cout << "\n=== 欢迎使用多用户图书管理系统 ===\n";
	cout << "        当前版本:2.1.0\n";
	cout << "更新内容:	\n";
	cout << "	1.新增修改用户名功能\n";
	cout << "	2.修复了图书 ID 等数据输入非数字字符会死循环的问题\n";
	cout << "	3.修复了图书 ID 可能会重复的问题\n";
	cout << "	4.修复了部分bug\n";
	cout << "	5.新增了部分bug\n\n";
	cout << "产品特性:\n";
	cout << "	1.支持多用户功能(目前仅限 10 位用户且不可删改)\n";
	cout << "	2.支持图书借出和归还的功能\n";
	cout << "	3.首页提供馆存图书统计\n";
	cout << "	4.支持修改密码的功能\n";
	cout << "	5.支持人机验证的功能\n";
}

人机验证

系统的人机验证功能可以防止恶意用户通过暴力破解等方式获取系统的权限。具体实现方法是在用户登录时要求用户输入验证码,系统随机生成一个验证码,用户输入验证码后,系统将用户输入的验证码和系统生成的验证码进行比对,如果比对成功,用户登录成功,否则登录失败。这里仅仅是简单的计算问题,实际上人机验证可以采用更多更复杂的方法实现。

实现效果:

复制代码
==人机验证==
请输入 9 + 2 = 

代码实现:

cpp 复制代码
void RenJiYanZheng(){
	int a,b,c;
	a = rand() % 10;
	b = rand() % 10;
	cout << "\n==人机验证==\n";
	cout << "请输入 " << a << " + " << b << " = ";
	cin >> c;
	if(a + b != c) {
		cout << "\n人机验证失败,即将退出程序!\n";
		sleep(1);
		exit(0);
	}
}

注意,这里需要提前预埋一个随机数种子,否则每次运行程序时生成的随机数都是一样的。

cpp 复制代码
srand(time(0));

这里使用了 time(0) 作为随机数种子,time(0) 会返回当前时间的秒数,每次运行程序的时间都不一样,所以生成的随机数也不一样。同时 srand() 函数需要在 rand() 函数之前调用,否则每次运行程序时生成的随机数都是一样的。而模 10 是为了保证生成的随机数在 0 到 9 之间。这里我在主函数中预埋了种子,后面会体现。

登录主菜单

实现效果:

复制代码
=== 用户登陆 ===
1. 登陆
2. 注册
3. 修改密码
4. 修改用户名
0. 退出
请输入选项: 

这部分的实现同样很简单,首先是输出菜单,然后通过switch-case()来实现选择。

代码实现:

cpp 复制代码
void DengLuCaiDan(int& choiceyh){
	cout << "\n=== 用户登陆 ===\n";
	cout << "1. 登陆\n";
	cout << "2. 注册\n";
	cout << "3. 修改密码\n";
	cout << "4. 修改用户名\n";
	cout << "0. 退出\n";
	cout << "请输入选项: ";
	
	cin >> choiceyh;
	
	switch(choiceyh) {
		case 1: DengLu(); break;
		case 2: ZhuCe(); break;
		case 3: XiuGaiMiMa();break;
		case 4: XiuGaiYongHuMing();break;
		case 0: cout << "\n感谢使用!\n";exit(0);
		default: cout << "\n无效输入!\n";
	}
}

注册

效果模拟:

复制代码
请输入新用户名:exm-zem

请输入密码:666
请再次输入密码:666

exm-zem 注册成功

请输入新用户名:exm-zem

该用户名已注册!

请输入新用户名:zem

请输入密码:666
请再次输入密码:777

两次密码不一致,请重新输入!


请输入密码:777
请再次输入密码:777

zem 注册成功!

我们可以看到注册时可能出现两个问题:

  1. 用户名重复
  2. 两次密码输入不一致

针对这两个问题,我们分别进行处理:

验证用户名是否重复

因为我们这里用户采用数组存储且用户量限定为 10,数据量较小,所以我们可以通过遍历数组来判断用户名是否重复。

cpp 复制代码
void YZYongHuMing(){
	for (int i = 0;i <= yhs;i++) {
			if (yonghu[i].i == xinyh.i) {
				cout << "\n该用户名已注册!\n";
				flag = true;
				break;
			}
	}
}

验证两次密码是否一致

我们先分析需求,验证密码是否一致至少需要输入一次密码,在不一致时需要重新输入,所以我们可以采用do-while循环来实现,当两次密码不一致时,循环继续,当两次密码一致时,循环结束。

cpp 复制代码
void YZMiMa(){
	do{
			cout << "\n请输入密码:";
			cin >> xinyh.password;
			cout << "请再次输入密码:";
			cin >> re;
			if(re != xinyh.password) {
				cout << "\n两次密码不一致,请重新输入!\n\n";
			}
		}while(re != xinyh.password);
}

注册部分主要会出现的问题都已经解决,剩余其他部分只涉及输入输出或者简单的判断等操作,接下来呈现完整的执行代码。

代码实现

cpp 复制代码
void ZhuCe(){	
	if (yhs >= 10) {
		cout << "\n当前用户数已满!" << endl;
		return ;
	}
	while (1){
		YongHu xinyh;
		cout << "\n请输入新用户名:";
		cin >> xinyh.i;

		bool flag = false;

		for (int i = 0;i <= yhs;i++) {
			if (yonghu[i].i == xinyh.i) {
				cout << "\n该用户名已注册!\n";
				flag = true;
				break;
			}
		}

		if (flag) continue;

		string re;

		do{
			cout << "\n请输入密码:";
			cin >> xinyh.password;
			cout << "请再次输入密码:";
			cin >> re;
			if(re != xinyh.password) {
				cout << "\n两次密码不一致,请重新输入!\n\n";
			}
		}while(re != xinyh.password);

		cout << endl << xinyh.i << " 注册成功!\n";
		yonghu[yhs++] = xinyh;
		sleep(1);
		return ;
	}
}

登录

登录操作很简单,只需要查找用户输入的用户名和密码是否对应即可,同样这里我们限定了用户数,所以可以直接采取遍历即可。

效果模拟

cpp 复制代码
请输入用户名:exm-zem
请输入密码:777

用户不存在或密码错误!

//这里会直接返回登录主菜单,再次选择登录后:

请输入用户名:exm-zem
请输入密码:666

登录成功!

您好 exm-zem

代码实现

cpp 复制代码
void DengLu(){
	if (yhs == 0) {
		cout << "\n暂无用户!\n";
		return ;
	}
	
	string target_i,target_pw;
	
	cout << "\n请输入用户名:";
	cin >> target_i;
	cout << "请输入密码:";
	cin >> target_pw;
	
	for (int i = 0;i <= yhs;i++) {
		if (yonghu[i].password == target_pw && yonghu[i].i == target_i) {
			dq = i;
			cout << "登录成功!\n\n您好 " << yonghu[i].i << "\n";
			zt = true;
			sleep(1);
			return;
		}
	}
	cout << "\n用户不存在或密码错误!\n";
}

这里我们还需要提前定义int类型全局变量 dq 代表当前登录的用户编号和bool类型全局变量 zt 代表当前用户是否登录,初始值为 false

在上面的代码中,登录成功后,我们将 zt 设为 true,并将 dq 设为当前用户的编号。

同样,在后续代码中当用户选择退出登录时,我们将 zt 设为 false,并将 dq 设为 -1

cpp 复制代码
int dq = -1;
bool zt = false;

修改密码

这里与登录十分类似,同样是先查找用户输入的用户名和密码是否对应,然后将该结构体中的密码修改为新密码即可。

效果模拟

cpp 复制代码
请输入需要修改密码的用户名:exm-zem
请输入原密码:777

用户不存在或密码错误!

//这里同样会返回登录主菜单,再次选择修改密码后:

请输入需要修改密码的用户名:exm-zem
请输入原密码:666
请输入新密码:777

修改成功!

代码实现

cpp 复制代码
void XiuGaiMiMa(){
	if (yhs == 0) {
		cout << "\n暂无用户!\n";
		return ;
	}
	string target_i,target_pw;
	cout << "\n请输入需要修改密码的用户名:";
	cin >> target_i;
	cout << "请输入原密码:";
	cin >> target_pw;
	for (int i = 0;i <= yhs;i++) {
		if (yonghu[i].password == target_pw && yonghu[i].i == target_i) {
			cout << "请输入新密码:";
			cin >> yonghu[i].password;
			cout << "\n修改成功\n";
			return;
		}
	}
	cout << "\n用户不存在或密码错误!\n";
}

这里可以看作是登录的简化版,因此不再过多赘述。

修改用户名

这里与修改密码更为类似,仅仅是将修改密码改成修改用户名即可。

效果模拟

cpp 复制代码
请输入需要修改的用户名:exm-zem
请输入密码:777

用户不存在或密码错误!

//这里依旧会返回登录主菜单,再次选择修改用户名后:

请输入需要修改的用户名:exm-zem
请输入密码:666
请输入新用户名:exm-zem1

修改成功!

代码实现

cpp 复制代码
void XiuGaiYongHuMing(){
	if (yhs == 0) {
		cout << "\n暂无用户!\n";
		return ;
	}
	string target_i,target_pw;
	cout << "\n请输入需要修改的用户名:";
	cin >> target_i;
	cout << "请输入密码:";
	cin >> target_pw;
	for (int i = 0;i <= yhs;i++) {
		if (yonghu[i].password == target_pw && yonghu[i].i == target_i) {
			cout << "请输入新用户名:";
			cin >> yonghu[i].i;
			cout << "\n修改成功\n";
			return;
		}
	}
	cout << "\n用户不存在或密码错误!\n";
}

至此,所有注册登录相关函数均已讲完。

主菜单

这里与登录主菜单功能类似,实现也类似,在此不再赘述。

代码实现

cpp 复制代码
void ZhuCaiDan(int& choice){
	do {
		
		cout << "\n=== 您好!" << yonghu[dq].i << " ===\n";
		cout << "当前共有 " << tushu_list[dq].size() << " 种图书" << endl;
		cout << "当前馆存 " << gc[dq] << " 本图书" << endl;
		cout << "1. 添加图书\n";
		cout << "2. 显示列表\n";
		cout << "3. 删除图书\n";
		cout << "4. 修改信息\n";
		cout << "5. 查找图书\n";
		cout << "6. 借出图书\n";
		cout << "7. 归还图书\n";
		cout << "0. 退出登陆\n";
		cout << "请输入选项: ";
		
		cin >> choice;
		
		switch(choice) {
			case 1: TianJiaTuShu(); break;
			case 2: XianShiLieBiao(); break;
			case 3: ShanChuTuShu(); break;
			case 4: XiuGaiXinXi(); break;
			case 5: ChaZhaoTuShu(); break;
			case 6: JieChuTuShu();break;
			case 7:	GuiHuanTuShu();break;
			case 0: cout << "\n您已退出登录!\n" << "\n 再见 " << yonghu[dq].i << endl; zt = false;dq = -1;sleep(1);break;
			default: cout << "无效输入!\n";
		}
		sleep(1);
	} while (choice != 0);
}

我们注意到,这里在选择退出登录后,修改了zt和dq的值,因此在程序中,我们可以根据zt的值来判断用户是否登录,从而控制是否显示登录后的选项。依据 dq 的值来判断当前登录的用户,从而控制用户只能操作自己的信息。

在这里我还设计了一个小巧思,定义了一个全局数组 gc[10],用来记录每个用户当前的馆存图书数量。

all_of()

根据前面定义的书库结构体可知,我们图书信息中的 ID 和库存都应当是数字,为了防止用户输入非数字引起程序报错甚至于陷入循环,在此我们引入一个新的函数:all_of()函数。

  • 头文件:#include <algorithm>
  • 函数原型:bool all_of(InputIterator first, InputIterator last, UnaryPredicate pred);
  • 功能:判断容器中的所有元素是否都满足条件
  • 参数:
    • first:容器的起始迭代器
    • last:容器的结束迭代器
    • pred:判断条件,返回值为bool类型,接受一个参数,参数类型为容器中的元素类型
  • 返回值:如果所有元素都满足条件,返回true,否则返回false

之前我的博客中曾经写过,string类型提供了多种迭代器,因此我们可以十分方便的使用 all_of() 函数,在此我们示范一下如何判断字符串是否全部为数字:

cpp 复制代码
bool isdigit(string str){
	return all_of(str.begin(), str.end(), ::isdigit);
}

添加图书

有了前面的铺垫,在此就可以很容易理解后面的代码了,仍有不懂的部分可以回顾我前面发过的博客。

效果模拟

cpp 复制代码
输入图书ID: 123
输入书名: 数据结构
输入作者: 严蔚敏
输入库存量: 10

添加成功!

输入图书 ID:你好
图书ID应为数字,请重新输入!
输入图书ID: 1234
输入书名: 你好
输入作者: 你好
输入库存量: 你好
库存量应为数字,请重新输入!
输入库存量: 10

添加成功!

输入图书 ID:1234

该图书ID已注册存在!
//这里依旧会返回主菜单

代码实现

cpp 复制代码
void TianJiaTuShu() {
    TuShu new_book;
	string i;
	int num;
    cout << "\n输入图书ID: ";
    cin >> i;
	bool isdi;
	isdi = all_of(i.begin(), i.end(), ::isdigit);
	while(!isdi){
		cout << "图书ID应为数字,请重新输入!";
		cout << "\n输入图书ID: ";
		cin >> i;
		isdi = all_of(i.begin(), i.end(), ::isdigit);
	}

	for (const auto& book : tushu_list[dq]) {
		if (book.id == stoi(i)) {
			cout << "\n该图书ID已注册存在!\n";
			return ;
		}
	}

	new_book.id = stoi(i);
    cout << "输入书名: ";
	cin >> new_book.shuming;
    cout << "输入作者: ";
    cin >> new_book.zuozhe;
    cout << "输入库存量: ";
	string target_i;
	cin >> target_i;
    bool isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	while(!isdig){
		cout << "库存量应为数字,请重新输入!";
		cout << "\n输入库存量: ";
		cin >> target_i;
		isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	}
	new_book.kucun = stoi(target_i);
	gc[dq] += new_book.kucun;
	new_book.jiechu = 0;
    tushu_list[dq].push_back(new_book);
    cout << "\n添加成功!\n";
}

这里我们将图书 ID 设计为图书唯一的标识符,所以不能重复,但书名和作者并不具有唯一性。

当我们在添加图书时,可以直接将库存量加入该用户的馆存中。

显示列表

遍历该用户的 vector 数组即可.

效果模拟

cpp 复制代码
=== 图书列表 ===
ID: 123
书名: 数据结构
作者: 严蔚敏
库存: 10
借出:0

ID: 1234
书名: 你好
作者: 你好
库存: 10
借出:0

代码实现

cpp 复制代码
void XianShiLieBiao() {
	if (tushu_list[dq].empty()) {
		cout << "\n暂无图书信息!\n";
		return;
	}
	
    cout << "\n=== 图书列表 ===\n";
	for (const auto& book : tushu_list[dq]) {
		cout << "ID: " << book.id << endl;
		cout << "书名: " << book.shuming << endl;
		cout << "作者: " << book.zuozhe << endl;
		cout << "库存: " << book.kucun << endl;
		cout << "借出:" << book.jiechu << endl << endl;
	}
	sleep(1);
}

删除图书

我们借助 vector 容器的 erase() 函数来删除指定的图书,erase() 函数的参数为一个迭代器,指向要删除的元素。我们可以通过遍历图书列表,找到要删除的图书的迭代器,然后调用 erase() 函数来删除。

效果模拟

cpp 复制代码
输入要删除的图书ID: 123
删除成功!

输入要删除的图书ID: 你好
图书ID应为数字,请重新输入!
输入要删除的图书ID: 1234
删除成功!

输入要删除的图书ID: 12345
未找到该ID的图书!

代码实现

cpp 复制代码
void ShanChuTuShu() {
	if (tushu_list[dq].empty()) {
		cout << "\n暂无图书信息!\n";
		return;
	}

	cout << "\n输入要删除的图书ID: ";
	string target_i;
	cin >> target_i;
	bool isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	while(!isdig){
		cout << "图书ID应为数字,请重新输入!";
		cout << "\n输入图书ID: ";
		cin >> target_i;
		isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	}
	const int target_id = stoi(target_i);
    
    for (auto it = tushu_list[dq].begin(); it != tushu_list[dq].end(); ++it) {
		int shanchuid = (*it).id;
        if (shanchuid == target_id) {
			gc[dq] -= (*it).kucun;
			gc[dq] += (*it).jiechu;
            tushu_list[dq].erase(it);
            cout << "\n删除成功!\n";
            return;
        }
    }
    cout << "\n未找到该ID的图书!\n";
}

修改信息

这里涉及的知识仍然是前面的增删查改,因为图书 ID 作为图书的唯一标识,所以不能进行修改。

效果模拟

cpp 复制代码
输入要修改的图书ID: 123
输入新书名: 数据结构2
输入新作者: 严蔚敏2
输入新库存: 10
确认借出量(当前借出 0 本):11
数值不合法,请重新输入!

确认借出量(当前借出 0 本):abc
借出量应为数字,请重新输入!

确认借出量(当前借出 0 本):10
修改成功!

输入要修改的图书ID: 1234
输入新书名: 你好2
输入新作者: 你好2
输入新库存: 10
确认借出量(当前借出 0 本):10
修改成功!

输入要修改的图书ID: 12345
未找到该ID的图书!

代码实现

cpp 复制代码
void XiuGaiXinXi() {
	if (tushu_list[dq].empty()) {
		cout << "\n暂无图书信息!\n";
		return;
	}
	
    cout << "\n输入要修改的图书ID: ";
	string target_i;
	cin >> target_i;
	bool isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	while(!isdig){
		cout << "图书ID应为数字,请重新输入!";
		cout << "\n输入图书ID: ";
		cin >> target_i;
		isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	}
	const int target_id = stoi(target_i);
    
    for (auto& book : tushu_list[dq]) {
        if (book.id == target_id) {
            cout << "输入新书名: ";
            cin >> book.shuming;
            cout << "输入新作者: ";
            cin >> book.zuozhe;
			gc[dq] -= book.kucun;
            cout << "输入新库存: ";
        	string target_i;
        	cin >> target_i;
            bool isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
        	while(!isdig){
        		cout << "库存应为数字,请重新输入!";
        		cout << "\n输入新库存: ";
        		cin >> target_i;
        		isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
        	}
        	book.kucun = stoi(target_i);
			gc[dq] += book.kucun;	
			gc[dq] += book.jiechu;
			cout << "确认借出量(当前借出 " << book.jiechu << " 本):";
        	string target_;
        	cin >> target_;
        	bool isdi = all_of(target_.begin(), target_.end(), ::isdigit);
        	while(!isdi) {
        		cout << "借出量应为数字,请重新输入!\n";
        		cout << "确认借出量(当前借出 " << book.jiechu << " 本):";
        		cin >> target_;
        		isdi = all_of(target_.begin(), target_.end(), ::isdigit);
        	}
			int jie = stoi(target_);
			while(jie > book.kucun){
				cout << "数值不合法,请重新输入!\n";
				cout << "确认借出量(当前借出 " << book.jiechu << " 本):";
				string target_;
				cin >> target_;
				bool isdi = all_of(target_.begin(), target_.end(), ::isdigit);
				while(!isdi) {
					cout << "借出量应为数字,请重新输入!\n";
					cout << "确认借出量(当前借出 " << book.jiechu << " 本):";
					cin >> target_;
					isdi = all_of(target_.begin(), target_.end(), ::isdigit);
				}
				jie= stoi(target_);
			}
			book.jiechu = jie;
			gc[dq] -= book.jiechu;
            cout << "\n修改完成!\n";
            return;
        }
    }
    cout << "\n未找到该ID的图书!\n";
}

这里我们需要注意:因为我们这套系统还涉及到图书的借出量,所以在修改图书信息时,需要注意借出量不能大于库存量。

这部分代码中借出量的合法性判断是最为复杂的,既涉及到数字判断,还涉及到数量是否超出当前库存量的判断,所以需要多加注意。

还需要注意区分馆存库存的区别:馆存 = 库存 - 借出量,所以我们在这里修改总馆存时:

  • 对于库存:先减去原库存,再加上新库存
  • 对于借出量:先加上原借出量,再减去新借出量

查找图书

遍历所有图书,查找图书 ID 与输入的 ID 相同的图书。

如果找到图书,输出图书信息。

如果未找到图书,输出未找到该 ID 的图书。

效果模拟

cpp 复制代码
输入要查找的图书ID: 123
查询结果:
ID: 123
书名: 数据结构
作者: 严蔚敏
库存: 10
借出:0

输入要查找的图书ID: 1234
未找到该图书!

代码实现

cpp 复制代码
void ChaZhaoTuShu() {
	if (tushu_list[dq].empty()) {
		cout << "\n暂无图书信息!\n";
		return;
	}
	
    cout << "\n输入要查找的图书ID: ";
	string target_i;
	cin >> target_i;
	bool isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	while(!isdig){
		cout << "图书ID应为数字,请重新输入!";
		cout << "\n输入图书ID: ";
		cin >> target_i;
		isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	}
	const int target_id = stoi(target_i);
    
    for (const auto& book : tushu_list[dq]) {
        if (book.id == target_id) {
            cout << endl << "查询结果:" << endl;
            cout << "ID: " << book.id << endl;
            cout << "书名: " << book.shuming << endl;
            cout << "作者: " << book.zuozhe << endl;
            cout << "库存: " << book.kucun << endl;
			cout << "借出:" << book.jiechu << endl << endl;
            return;
        }
    }
    cout << "\n未找到该图书!\n";
}

借出图书

目前版本借出图书仅限于单本借出,我们只需要遍历并修改该图书的借出量即可,如果借出量大于库存量则报错。

效果模拟

cpp 复制代码
输入要借出的图书ID: 123
借出成功!

输入要借出的图书ID: 1234
未找到该ID的图书!

代码实现

cpp 复制代码
void JieChuTuShu(){
	if (tushu_list[dq].empty()) {
		cout << "\n暂无图书信息!\n";
		return;
	}
	
	cout << "\n输入要借出的图书ID: ";
	string target_i;
	cin >> target_i;
	bool isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	while(!isdig){
		cout << "图书ID应为数字,请重新输入!";
		cout << "\n输入图书ID: ";
		cin >> target_i;
		isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	}
	const int target_id = stoi(target_i);

	for (auto& book : tushu_list[dq]) {
		if (book.id == target_id) {
			if(book.jiechu == book.kucun){
				cout << "\n当前已无库存!" << endl;
			}
			else{
				book.jiechu++;
				gc[dq]--;
				cout << "\n借出成功!" << endl;
			}
			return;
		}
	}
	cout << "\n未找到该ID的图书!\n";
}

归还图书

同样,当前版本的归还图书仅限于单本归还,我们只需要遍历并修改该图书的借出量即可,如果归还时借出量小于等于零则报错。

效果模拟

cpp 复制代码
输入要归还的图书ID: 123
归还成功!

输入要归还的图书ID: 1234
未找到该ID的图书!

代码实现

cpp 复制代码
void GuiHuanTuShu(){
	if (tushu_list[dq].empty()) {
		cout << "\n暂无图书信息!\n";
		return;
	}
	
	cout << "\n输入要归还的图书ID: ";
	string target_i;
	cin >> target_i;
	bool isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	while(!isdig){
		cout << "图书ID应为数字,请重新输入!";
		cout << "\n输入要归还的图书ID: ";
		cin >> target_i;
		isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	}
	const int target_id = stoi(target_i);

	for (auto& book : tushu_list[dq]) {
		if (book.id == target_id) {
			if(book.jiechu <= 0){
				cout << "\n当前暂无借出,请确认是否为我馆图书!" << endl;
			}
			else{
				book.jiechu--;
				gc[dq]++;
				cout << "\n归还成功!" << endl;
			}
			return;
		}
	}
	cout << "\n未找到该ID的图书!\n";
}

主函数

程序主干均以函数形式呈现,增加程序可读性和可维护性,因此主函数仅有基本的函数调用和登录状态判断。

代码实现

cpp 复制代码
int main() {
	
	/*made by exm-zem
	CSDN:https://blog.csdn.net/zemexm?spm=1018.2118.3001.5148
	gitee:https://gitee.com/exm-zem/freshman-internal-competition-plan*/
	
	srand(time(0));

	HuanYing();
	
	sleep(1); 
	
	RenJiYanZheng();
	
	int choiceyh,choice;
	do {
		
		DengLuCaiDan(choiceyh);
		
		if (zt == false) {
			continue;
		}
	
		ZhuCaiDan(choice);
	    
	} while (choiceyh != 0);
	
    return 0;
}

srand(time(0))就是前面提到的种下的时间种子。

完整程序

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
// #include <bits/stdc++.h>
using namespace std;

//书库

struct TuShu {
    int id;
    string shuming;
    string zuozhe;
    int kucun;
	int jiechu;
};

vector<TuShu> tushu_list[10];

//用户库

struct YongHu{
	string i;
	string password;
};

YongHu yonghu[10];

//登陆状态

bool zt = false;

//总用户数

int yhs;

//当前账户地址

int dq;

//各馆馆存信息

int gc[10] = {0};

//欢迎界面

void HuanYing(){
	cout << "\n=== 欢迎使用多用户图书管理系统 ===\n";
	cout << "        当前版本:2.1.0\n";
	cout << "更新内容:	\n";
	cout << "	1.新增修改用户名功能\n";
	cout << "	2.修复了图书 ID 等数据输入非数字字符会死循环的问题\n";
	cout << "	3.修复了图书 ID 可能会重复的问题\n";
	cout << "	4.修复了部分bug\n";
	cout << "	5.新增了部分bug\n\n";
	cout << "产品特性:\n";
	cout << "	1.支持多用户功能(目前仅限 10 位用户且不可删改)\n";
	cout << "	2.支持图书借出和归还的功能\n";
	cout << "	3.首页提供馆存图书统计\n";
	cout << "	4.支持修改密码的功能\n";
	cout << "	5.支持人机验证的功能\n";
}

//人机验证

void RenJiYanZheng(){
	int a,b,c;
	a = rand() % 10;
	b = rand() % 10;
	cout << "\n==人机验证==\n";
	cout << "请输入 " << a << " + " << b << " = ";
	cin >> c;
	if(a + b != c) {
		cout << "\n人机验证失败,即将退出程序!\n";
		sleep(1);
		exit(0);
	}
}

//注册

void ZhuCe(){	
	if (yhs >= 10) {
		cout << "\n当前用户数已满!" << endl;
		return ;
	}
	while (1){
		YongHu xinyh;
		cout << "\n请输入新用户名:";
		cin >> xinyh.i;

		bool flag = false;

		for (int i = 0;i <= yhs;i++) {
			if (yonghu[i].i == xinyh.i) {
				cout << "\n该用户名已注册!\n";
				flag = true;
				break;
			}
		}

		if (flag) continue;

		string re;

		do{
			cout << "\n请输入密码:";
			cin >> xinyh.password;
			cout << "请再次输入密码:";
			cin >> re;
			if(re != xinyh.password) {
				cout << "\n两次密码不一致,请重新输入!\n\n";
			}
		}while(re != xinyh.password);

		cout << endl << xinyh.i << " 注册成功!\n";
		yonghu[yhs++] = xinyh;
		sleep(1);
		return ;
	}
}

//登陆

void DengLu(){
	if (yhs == 0) {
		cout << "\n暂无用户!\n";
		return ;
	}
	
	string target_i,target_pw;
	
	cout << "\n请输入用户名:";
	cin >> target_i;
	cout << "请输入密码:";
	cin >> target_pw;
	
	for (int i = 0;i <= yhs;i++) {
		if (yonghu[i].password == target_pw && yonghu[i].i == target_i) {
			dq = i;
			cout << "登录成功!\n\n您好 " << yonghu[i].i << "\n";
			zt = true;
			sleep(1);
			return;
		}
	}
	cout << "\n用户不存在或密码错误!\n";
}

//修改密码

void XiuGaiMiMa(){
	if (yhs == 0) {
		cout << "\n暂无用户!\n";
		return ;
	}
	string target_i,target_pw;
	cout << "\n请输入需要修改密码的用户名:";
	cin >> target_i;
	cout << "请输入原密码:";
	cin >> target_pw;
	for (int i = 0;i <= yhs;i++) {
		if (yonghu[i].password == target_pw && yonghu[i].i == target_i) {
			cout << "请输入新密码:";
			cin >> yonghu[i].password;
			cout << "\n修改成功\n";
			return;
		}
	}
	cout << "\n用户不存在或密码错误!\n";
}

//修改用户名

void XiuGaiYongHuMing(){
	if (yhs == 0) {
		cout << "\n暂无用户!\n";
		return ;
	}
	string target_i,target_pw;
	cout << "\n请输入需要修改的用户名:";
	cin >> target_i;
	cout << "请输入密码:";
	cin >> target_pw;
	for (int i = 0;i <= yhs;i++) {
		if (yonghu[i].password == target_pw && yonghu[i].i == target_i) {
			cout << "请输入新用户名:";
			cin >> yonghu[i].i;
			cout << "\n修改成功\n";
			return;
		}
	}
	cout << "\n用户不存在或密码错误!\n";
}

//登陆菜单

void DengLuCaiDan(int& choiceyh){
	cout << "\n=== 用户登陆 ===\n";
	cout << "1. 登陆\n";
	cout << "2. 注册\n";
	cout << "3. 修改密码\n";
	cout << "4. 修改用户名\n";
	cout << "0. 退出\n";
	cout << "请输入选项: ";
	
	cin >> choiceyh;
	
	switch(choiceyh) {
		case 1: DengLu(); break;
		case 2: ZhuCe(); break;
		case 3: XiuGaiMiMa();break;
		case 4: XiuGaiYongHuMing();break;
		case 0: cout << "\n感谢使用!\n";exit(0);
		default: cout << "\n无效输入!\n";
	}
}

//添加图书

void TianJiaTuShu() {
    TuShu new_book;
	string i;
	int num;
    cout << "\n输入图书ID: ";
    cin >> i;
	bool isdi;
	isdi = all_of(i.begin(), i.end(), ::isdigit);
	while(!isdi){
		cout << "图书ID应为数字,请重新输入!";
		cout << "\n输入图书ID: ";
		cin >> i;
		isdi = all_of(i.begin(), i.end(), ::isdigit);
	}

	for (const auto& book : tushu_list[dq]) {
		if (book.id == stoi(i)) {
			cout << "\n该图书ID已注册存在!\n";
			return ;
		}
	}

	new_book.id = stoi(i);
    cout << "输入书名: ";
	cin >> new_book.shuming;
    cout << "输入作者: ";
    cin >> new_book.zuozhe;
    cout << "输入库存量: ";
	string target_i;
	cin >> target_i;
    bool isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	while(!isdig){
		cout << "库存量应为数字,请重新输入!";
		cout << "\n输入库存量: ";
		cin >> target_i;
		isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	}
	new_book.kucun = stoi(target_i);
	gc[dq] += new_book.kucun;
	new_book.jiechu = 0;
    tushu_list[dq].push_back(new_book);
    cout << "\n添加成功!\n";
}

//显示列表

void XianShiLieBiao() {
	if (tushu_list[dq].empty()) {
		cout << "\n暂无图书信息!\n";
		return;
	}
	
    cout << "\n=== 图书列表 ===\n";
	for (const auto& book : tushu_list[dq]) {
		cout << "ID: " << book.id << endl;
		cout << "书名: " << book.shuming << endl;
		cout << "作者: " << book.zuozhe << endl;
		cout << "库存: " << book.kucun << endl;
		cout << "借出:" << book.jiechu << endl << endl;
	}
	sleep(1);
}

//删除图书

void ShanChuTuShu() {
	if (tushu_list[dq].empty()) {
		cout << "\n暂无图书信息!\n";
		return;
	}

	cout << "\n输入要删除的图书ID: ";
	string target_i;
	cin >> target_i;
	bool isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	while(!isdig){
		cout << "图书ID应为数字,请重新输入!";
		cout << "\n输入图书ID: ";
		cin >> target_i;
		isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	}
	const int target_id = stoi(target_i);
    
    for (auto it = tushu_list[dq].begin(); it != tushu_list[dq].end(); ++it) {
		int shanchuid = (*it).id;
        if (shanchuid == target_id) {
			gc[dq] -= (*it).kucun;
			gc[dq] += (*it).jiechu;
            tushu_list[dq].erase(it);
            cout << "\n删除成功!\n";
            return;
        }
    }
    cout << "\n未找到该ID的图书!\n";
}

//修改信息

void XiuGaiXinXi() {
	if (tushu_list[dq].empty()) {
		cout << "\n暂无图书信息!\n";
		return;
	}
	
    cout << "\n输入要修改的图书ID: ";
	string target_i;
	cin >> target_i;
	bool isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	while(!isdig){
		cout << "图书ID应为数字,请重新输入!";
		cout << "\n输入图书ID: ";
		cin >> target_i;
		isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	}
	const int target_id = stoi(target_i);
    
    for (auto& book : tushu_list[dq]) {
        if (book.id == target_id) {
            cout << "输入新书名: ";
            cin >> book.shuming;
            cout << "输入新作者: ";
            cin >> book.zuozhe;
			gc[dq] -= book.kucun;
            cout << "输入新库存: ";
        	string target_i;
        	cin >> target_i;
            bool isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
        	while(!isdig){
        		cout << "库存应为数字,请重新输入!";
        		cout << "\n输入新库存: ";
        		cin >> target_i;
        		isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
        	}
        	book.kucun = stoi(target_i);
			gc[dq] += book.kucun;	
			gc[dq] += book.jiechu;
			cout << "确认借出量(当前借出 " << book.jiechu << " 本):";
        	string target_;
        	cin >> target_;
        	bool isdi = all_of(target_.begin(), target_.end(), ::isdigit);
        	while(!isdi) {
        		cout << "借出量应为数字,请重新输入!\n";
        		cout << "确认借出量(当前借出 " << book.jiechu << " 本):";
        		cin >> target_;
        		isdi = all_of(target_.begin(), target_.end(), ::isdigit);
        	}
			int jie = stoi(target_);
			while(jie > book.kucun){
				cout << "数值不合法,请重新输入!\n";
				cout << "确认借出量(当前借出 " << book.jiechu << " 本):";
				string target_;
				cin >> target_;
				bool isdi = all_of(target_.begin(), target_.end(), ::isdigit);
				while(!isdi) {
					cout << "借出量应为数字,请重新输入!\n";
					cout << "确认借出量(当前借出 " << book.jiechu << " 本):";
					cin >> target_;
					isdi = all_of(target_.begin(), target_.end(), ::isdigit);
				}
				jie= stoi(target_);
			}
			book.jiechu = jie;
			gc[dq] -= book.jiechu;
            cout << "\n修改完成!\n";
            return;
        }
    }
    cout << "\n未找到该ID的图书!\n";
}

//查找图书

void ChaZhaoTuShu() {
	if (tushu_list[dq].empty()) {
		cout << "\n暂无图书信息!\n";
		return;
	}
	
    cout << "\n输入要查找的图书ID: ";
	string target_i;
	cin >> target_i;
	bool isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	while(!isdig){
		cout << "图书ID应为数字,请重新输入!";
		cout << "\n输入图书ID: ";
		cin >> target_i;
		isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	}
	const int target_id = stoi(target_i);
    
    for (const auto& book : tushu_list[dq]) {
        if (book.id == target_id) {
            cout << endl << "查询结果:" << endl;
            cout << "ID: " << book.id << endl;
            cout << "书名: " << book.shuming << endl;
            cout << "作者: " << book.zuozhe << endl;
            cout << "库存: " << book.kucun << endl;
			cout << "借出:" << book.jiechu << endl << endl;
            return;
        }
    }
    cout << "\n未找到该图书!\n";
}

//借出图书

void JieChuTuShu(){
	if (tushu_list[dq].empty()) {
		cout << "\n暂无图书信息!\n";
		return;
	}
	
	cout << "\n输入要借出的图书ID: ";
	string target_i;
	cin >> target_i;
	bool isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	while(!isdig){
		cout << "图书ID应为数字,请重新输入!";
		cout << "\n输入图书ID: ";
		cin >> target_i;
		isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	}
	const int target_id = stoi(target_i);

	for (auto& book : tushu_list[dq]) {
		if (book.id == target_id) {
			if(book.jiechu == book.kucun){
				cout << "\n当前已无库存!" << endl;
			}
			else{
				book.jiechu++;
				gc[dq]--;
				cout << "\n借出成功!" << endl;
			}
			return;
		}
	}
	cout << "\n未找到该ID的图书!\n";
}

//归还图书

void GuiHuanTuShu(){
	if (tushu_list[dq].empty()) {
		cout << "\n暂无图书信息!\n";
		return;
	}
	
	cout << "\n输入要归还的图书ID: ";
	string target_i;
	cin >> target_i;
	bool isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	while(!isdig){
		cout << "图书ID应为数字,请重新输入!";
		cout << "\n输入要归还的图书ID: ";
		cin >> target_i;
		isdig = all_of(target_i.begin(), target_i.end(), ::isdigit);
	}
	const int target_id = stoi(target_i);

	for (auto& book : tushu_list[dq]) {
		if (book.id == target_id) {
			if(book.jiechu <= 0){
				cout << "\n当前暂无借出,请确认是否为我馆图书!" << endl;
			}
			else{
				book.jiechu--;
				gc[dq]++;
				cout << "\n归还成功!" << endl;
			}
			return;
		}
	}
	cout << "\n未找到该ID的图书!\n";
}

//主菜单

void ZhuCaiDan(int& choice){
	do {
		
		cout << "\n=== 您好!" << yonghu[dq].i << " ===\n";
		cout << "当前共有 " << tushu_list[dq].size() << " 种图书" << endl;
		cout << "当前馆存 " << gc[dq] << " 本图书" << endl;
		cout << "1. 添加图书\n";
		cout << "2. 显示列表\n";
		cout << "3. 删除图书\n";
		cout << "4. 修改信息\n";
		cout << "5. 查找图书\n";
		cout << "6. 借出图书\n";
		cout << "7. 归还图书\n";
		cout << "0. 退出登陆\n";
		cout << "请输入选项: ";
		
		cin >> choice;
		
		switch(choice) {
			case 1: TianJiaTuShu(); break;
			case 2: XianShiLieBiao(); break;
			case 3: ShanChuTuShu(); break;
			case 4: XiuGaiXinXi(); break;
			case 5: ChaZhaoTuShu(); break;
			case 6: JieChuTuShu();break;
			case 7:	GuiHuanTuShu();break;
			case 0: cout << "\n您已退出登录!\n" << "\n 再见 " << yonghu[dq].i << endl; zt = false;dq = -1;sleep(1);break;
			default: cout << "无效输入!\n";
		}
		sleep(1);
	} while (choice != 0);
}


int main() {
	
	/*made by exm-zem
	CSDN:https://blog.csdn.net/zemexm?spm=1018.2118.3001.5148
	gitee:https://gitee.com/exm-zem/freshman-internal-competition-plan*/
	
	srand(time(0));

	HuanYing();
	
	sleep(1); 
	
	RenJiYanZheng();
	
	int choiceyh,choice;
	do {
		
		DengLuCaiDan(choiceyh);
		
		if (zt == false) {
			continue;
		}
	
		ZhuCaiDan(choice);
	    
	} while (choiceyh != 0);
	
    return 0;
}
相关推荐
l1t1 小时前
借助DeepSeek编写输出漂亮表格的chdb客户端
开发语言·数据库·c++·github
lzb_kkk2 小时前
【C++】多线程同步三剑客介绍
c语言·c++·条件变量·互斥锁·信号量
盒马盒马2 小时前
C++11:constexpr & 编译期性质
c++
2301_803554523 小时前
网络协议和基础通信原理
linux·c++
liulilittle3 小时前
基于UDP/IP网络游戏加速高级拥塞控制算法(示意:一)
开发语言·c++·网络协议·tcp/ip·udp
oioihoii5 小时前
深入理解C++11 std::iota:从原理到实践
开发语言·c++
霖006 小时前
C++学习笔记五
开发语言·c++·笔记·学习·青少年编程·个人开发
山河木马7 小时前
前端学C++可太简单了:指针
前端·javascript·c++
落羽的落羽8 小时前
【C++】神奇的AVL树
开发语言·数据结构·c++·学习
麻辣长颈鹿Sir8 小时前
【Keil】C/C++混合编程的简单方法
c语言·c++·keil·c/c++融合编程·多语言混合编程