问题描述
根据依赖倒置的原则,编写一个能够实现以下功能的学生管理系统
- 增加一条学生信息
- 删除一条学生信息
- 修改一条学生信息
- 查询一条/全部学生信息
要注意依赖抽象而不是依赖具体!
参考解答
整体架构
首先是对C为MySQL提供的API的封装,属于底层实现,不需要考虑学生管理系统等等,只要考虑到读写数据即可。
最后是对整个学生管理系统的封装,是一种MVC设计模式,本项目中包括了Model、Dao、Service、Controller、View这几层:
-
Model层
模型层,定义学生管理系统要用到的具体对象的模型,这里就是根据学生表的字段封装出学生类Stu
-
Dao层
数据访问层,规定学生管理系统数据访问的方式,提供数据访问的接口(抽象类)
-
Service层
服务层,是对Dao层接口的具体实现,可以有多种实现,比如这里提供的就是用MySQL访问的接口
-
Controller层
请求转接层,根据请求调用Service层进行处理
-
View层
界面层,用于展示界面,将前端请求发送给Controller层处理
UML图
学生管理系统的UML图如下:
如果感觉不清晰可以到生成这张图片的源网站去查看☛点这里(需要翻墙)
整体代码
MySQL-C-API封装
还是沿用了上次作业的代码,并进行了一些修正如下:
- 将上次封装中的所有
cout
、cerr
输出信息删除 - 对query的返回值进行了修正,用
return 查询到的记录数
替代原本的return 0
表示查询成功 - 对exec的返回值进行了修正,用
return 受影响的行数
替代原本的return 0
表示写入成功 - 对query查询到的数据的展现方式进行了修正,用
vector<vector<string>>
类型的引用参数_res
来存储查询到的数据
注:本来是想用map<int, vector<string>>
类型的引用参数来存储的,但是考虑到map
容器的访问结果可能会和存储的顺序不符,因此改用了vector<vector<string>>
类型
以下是具体代码:
MyMySQL.h文件
#ifndef MYMYSQL_H
#define MYMYSQL_H
#include "mysql.h" // 需要有MYSQL等结构体的定义
#include <string>
#include <vector>
using namespace std;
class MyMySQL
{
public:
MyMySQL();
MyMySQL(const char* host, const char* user, const char* password, const char* db, unsigned int port);
int MyMySQL_connect(const char * host, const char * user, const char * password, const char * db, unsigned int port);
int MyMySQL_query(const char * sql, vector<vector<string>>& _res, const char * q = "gbk");
int MyMySQL_execute(const char * sql, const char * q = "gbk");
void MyMySQL_close();
private:
void MyMySQL_init();
MYSQL conn; // MySQL结构体
int init_state; // 初始化状态 —— 0代表未初始化、1代表已初始化
int conn_state; // 连接状态 —— 0代表未连接、1代表已连接
};
#endif // !MYMYSQL_H
MyMySQL.cpp文件
#include "pch.h"
#include "MyMySQL.h"
#include <iostream>
#include <string>
using namespace std;
/*
* 功能:
* MyMySQL的缺省构造函数
* 参数:
* void
* 返回值:
* 无
* 说明:
* 只是初始化了conn,并没有连接到某个数据库
* 创建时间:
* 2020-04-01 10:59:20
* 作者:
* Excious
**/
MyMySQL::MyMySQL()
:init_state(1), conn_state(0)
{
mysql_init(&this->conn);
}
/*
* 功能:
* MyMySQL的构造函数
* 参数:
* host in 主机
* user in 用户名
* password in 密码
* db in 要连接的数据库
* port in 端口
* 返回值:
* 无
* 说明:
* 使用成员函数MyMySQL_connect尝试连接(里面附带着初始化过程)
* 创建时间:
* 2020-04-01 14:36:46
* 作者:
* Excious
**/
MyMySQL::MyMySQL(const char * host, const char * user, const char * password, const char * db, unsigned int port)
:init_state(0), conn_state(0)
{
this->MyMySQL_connect(host, user, password, db, port);
}
/*
* 功能:
* [连接/重新连接]到某个数据库
* 参数:
* host in 主机
* user in 用户名
* password in 密码
* db in 要连接的数据库
* port in 端口
* 返回值:
* -1 连接失败
* 0 连接成功
* 说明:
* 如果conn尚未进行初始化,会对其进行初始化,再重新进行连接
* 如果conn已经连接上某个数据库,会先将其关闭,再重新初始化,再重新进行连接
* 如果重新连接失败,则之前的连接状态也会被清除,并且将会关闭连接
* 创建时间:
* 2020-04-01 14:19:03
* 作者:
* Excious
**/
int MyMySQL::MyMySQL_connect(const char * host, const char * user, const char * password, const char * db, unsigned int port)
{
// 根据状态进行相应的预处理
if (!this->init_state) // 未初始化
{
this->MyMySQL_init();
}
else if (this->conn_state) // 已初始化,并已连接某数据库
{
this->MyMySQL_close();
this->MyMySQL_init();
}
// 尝试连接到某个数据库
int flag = 0;
if (!mysql_real_connect(&this->conn, host, user, password, db, port, NULL, 0))
{
this->MyMySQL_close();
flag = -1;
}
else
{
this->conn_state = 1;
}
return flag;
}
/*
* 功能:
* 执行select语句
* 参数:
* sql in 要执行的select语句
* _res out vector<vector<string>>的查询结果集合
* q in 读取时的编码字符集(缺省值为"gbk")
* 返回值:
* -2 尚未连接
* -1 查询失败
* >=0 查询成功,返回查询到的数据个数
* 说明:
* 前三行代码设置了读取时的字符集(目前感觉读取字符集utf8的表,最靠谱的还是gbk)
* 创建时间:
* 2020-04-10 15:54:05
* 作者:
* Excious
**/
int MyMySQL::MyMySQL_query(const char * sql, vector<vector<string>>& _res, const char * q)
{
string q_s("set names ");
q_s += q;
mysql_query(&this->conn, q_s.c_str());
// 尚未初始化、连接
if (!this->init_state || !this->conn_state)
{
return -2;
}
// 尝试执行SQL-select语句
if (mysql_query(&this->conn, sql)) // 执行失败
{
return -1;
}
// 执行成功
MYSQL_RES* res = mysql_store_result(&this->conn);
MYSQL_ROW row;
vector<string> temp;
int num = mysql_num_fields(res);
int record_num = mysql_num_rows(res);
while ((row = mysql_fetch_row(res)) != NULL)
{
temp.clear();
for (int i = 0; i < mysql_num_fields(res); i++)
{
temp.push_back(row[i] != NULL ? row[i] : "NULL");
}
_res.push_back(temp);
}
mysql_free_result(res); //释放内存
return record_num;
}
/*
* 功能:
*
* 参数:
* sql in 要执行的update语句、insert语句、delete语句
* q in 读取时的编码字符集(缺省值为"gbk")
* 返回值:
* -2 尚未连接
* -1 执行失败
* >=0 执行成功,返回受影响的行数
* 说明:
* 前三行代码设置了写入时的字符集(目前感觉写入字符集utf8的表,最靠谱的还是gbk)
* 创建时间:
* 2020-04-01 15:25:46
* 作者:
* Excious
**/
int MyMySQL::MyMySQL_execute(const char * sql, const char * q)
{
string q_s("set names ");
q_s += q;
mysql_query(&this->conn, q_s.c_str());
// 尚未初始化、连接
if (!this->init_state || !this->conn_state)
{
return -2;
}
// 尝试执行
if (mysql_query(&this->conn, sql)) // 执行失败
{
return -1;
}
// 执行成功
int aff_num = mysql_affected_rows(&conn);
mysql_commit(&this->conn); // 提交事务
return aff_num;
}
/*
* 功能:
* 关闭连接conn
* 参数:
* void
* 返回值:
* void
* 说明:
* 如果初始化状态(init_state)为0,则不会关闭连接
* 如果初始化状态(init_state)为1,则对conn进行关闭,并将init_state、conn_state调整至0
* 创建时间:
* 2020-04-01 14:13:35
* 作者:
* Excious
**/
void MyMySQL::MyMySQL_close()
{
if (this->init_state)
{
mysql_close(&this->conn);
this->init_state = 0;
this->conn_state = 0;
}
}
/*
* 功能:
* 初始化连接conn,是连接数据库前的准备
* 参数:
* void
* 返回值:
* void
* 说明:
* 如果初始化状态(init_state)为1,则不会进行操作
* 如果初始化状态(init_state)为0,则对conn进行初始化,并将init_state调整至1
* 创建时间:
* 2020-04-01 14:00:58
* 作者:
* Excious
**/
void MyMySQL::MyMySQL_init()
{
if (!this->init_state)
{
mysql_init(&this->conn);
this->init_state = 1;
}
}
Model层
Model层定义了学生类Stu,除了构造函数、析构函数外,暂时没有成员函数,只包含成员数据int sno
和string sname
,这和数据库中学生表的字段相一致。
Stu.h文件
#include <string>
using namespace std;
class Stu
{
public:
Stu();
Stu(int _sno, string _sname);
~Stu();
int getSno()const;
string getSname()const;
void setSno(int _sno);
void setSname(string _sname);
private:
int sno;
string sname;
};
#endif // !STU_H
Stu.cpp文件
#include "pch.h"
#include "Stu.h"
Stu::Stu() { }
/*
* 功能:
* Stu类的构造函数
* 参数:
* sno in 学号
* sname in 姓名
* 返回值:
* 无
* 创建时间:
* 2020-04-10 10:55:37
* 作者:
* Excious
**/
Stu::Stu(int _sno, string _sname)
:sno(_sno), sname(_sname)
{
}
Stu::~Stu() { }
int Stu::getSno() const { return this->sno; }
string Stu::getSname() const { return this->sname; }
void Stu::setSno(int _sno) { this->sno = _sno; }
void Stu::setSname(string _sname) { this->sname = _sname; }
Dao层
这里的Dao层只提供数据访问的接口,不负责实现,是一个抽象类。
IStuDao.h文件
#ifndef ISTUDAO_H
#define ISTUDAO_H
#include "Stu.h"
#include <vector>
using namespace std;
class IStuDao
{
public:
IStuDao() { };
virtual ~IStuDao() { };
virtual int stuInsert(const Stu& _stu) = 0;
virtual int stuDelete(const int _sno) = 0;
virtual int stuUpdate(const int _sno, const Stu& _stu) = 0;
virtual int stuSelect(const int _sno, Stu& _res) = 0;
virtual int stuSelectAll(vector<Stu>& _resset) = 0;
};
#endif // !ISTUDAO_H
Service层
Service层是对Dao层接口的具体实现,可以有多种Service层实现,这里就是MySQL的Service实现。
StuServiceMySQL.h文件
#ifndef STUSERVICEMYSQL_H
#define STUSERVICEMYSQL_H
#include "IStuDao.h"
#include "MyMySQL.h"
class StuServiceMySQL :
public IStuDao
{
public:
StuServiceMySQL();
virtual ~StuServiceMySQL();
virtual int stuInsert(const Stu& _stu);
virtual int stuDelete(const int _sno);
virtual int stuUpdate(const int _sno, const Stu& _stu);
virtual int stuSelect(const int _sno, Stu& _res);
virtual int stuSelectAll(vector<Stu>& _resset);
private:
MyMySQL* pmysql;
};
#endif // !STUSERVICEMYSQL_H
StuServiceMySQL.cpp文件
#include "pch.h"
#include "StuServiceMySQL.h"
/*
* 功能:
* StuServiceMySQL的构造函数
* 参数:
* void
* 返回值:
* 无
* 说明:
* 用new出来的MyMysQL对象初始化成员指针pmysql,析构函数中delete释放
* 创建时间:
* 2020-04-10 15:43:47
* 作者:
* Excious
**/
StuServiceMySQL::StuServiceMySQL()
:pmysql(new MyMySQL("localhost", "tester", "test", "cpp_homework", 3306))
{
}
/*
* 功能:
* StuServiceMySQL的析构函数
* 参数:
* void
* 返回值:
* 无
* 说明:
* 释放成员指针pmysql
* 创建时间:
* 2020-04-10 15:51:39
* 作者:
* Excious
**/
StuServiceMySQL::~StuServiceMySQL()
{
if (this->pmysql != NULL)
delete this->pmysql;
}
/*
* 功能:
* 插入一条学生记录
* 参数:
* _stu in 学生对象(待插入)
* 返回值:
* 0 插入成功
* -1 插入失败
* 创建时间:
* 2020-04-10 15:13:13
* 作者:
* Excious
**/
int StuServiceMySQL::stuInsert(const Stu & _stu)
{
string sql("insert into stu_info values(" + to_string(_stu.getSno()) + ", \'" + _stu.getSname() + "\')");
if (this->pmysql->MyMySQL_execute(sql.c_str()) < 0)
{
return -1;
}
return 0;
}
/*
* 功能:
* 删除一条学生记录(根据学号)
* 参数:
* _sno in 学生学号(待删除)
* 返回值:
* 0 删除成功
* -1 删除失败
* 创建时间:
* 2020-04-10 15:21:41
* 作者:
* Excious
**/
int StuServiceMySQL::stuDelete(const int _sno)
{
string sql("delete from stu_info where sno = " + to_string(_sno));
if (this->pmysql->MyMySQL_execute(sql.c_str()) < 0)
{
return -1;
}
return 0;
}
/*
* 功能:
* 更新一条学生记录(根据学号)
* 参数:
* _sno in 学生学号(待更新)
* _stu in 学生对象(更新)
* 返回值:
* 0 更新成功
* -1 更新失败
* 创建时间:
* 2020-04-10 15:23:24
* 作者:
* Excious
**/
int StuServiceMySQL::stuUpdate(const int _sno, const Stu & _stu)
{
string sql("update stu_info set sno = " + to_string(_stu.getSno()) + ", sname = \'" + _stu.getSname() + "\' where sno = " + to_string(_sno));
if (this->pmysql->MyMySQL_execute(sql.c_str()) < 0)
{
return -1;
}
return 0;
}
/*
* 功能:
* 查询一条学生记录(根据学号)
* 参数:
* _sno in 学生学号
* _res out 查询结果——单个学生对象
* 返回值:
* 0 查询成功(找到记录)
* -1 查询失败(找不到记录)
* 创建时间:
* 2020-04-10 15:27:15
* 作者:
* Excious
**/
int StuServiceMySQL::stuSelect(const int _sno, Stu & _res)
{
string sql("select * from stu_info where sno = " + to_string(_sno));
vector<vector<string>> q_res;
if (this->pmysql->MyMySQL_query(sql.c_str(), q_res) <= 0)
{
return -1;
}
_res = Stu(stoi(q_res[0][0]), q_res[0][1]);
return 0;
}
/*
* 功能:
* 查询所有学生记录
* 参数:
* _sno in 学生学号
* _resset out 查询结果——所有学生对象构成的vector容器
* 返回值:
* 查询到的记录数
* 说明:
* 对于传入的参数_resset,会对其进行clear操作
* 创建时间:
* 2020-04-10 15:29:18
* 作者:
* Excious
**/
int StuServiceMySQL::stuSelectAll(vector<Stu>& _resset)
{
string sql("select * from stu_info");
vector<vector<string>> q_res;
if (this->pmysql->MyMySQL_query(sql.c_str(), q_res) < 0)
{
return -1;
}
_resset.clear();
for (vector<vector<string>>::const_iterator it = q_res.begin(); it != q_res.end(); ++it)
{
_resset.push_back(Stu(stoi((*it)[0]), (*it)[1]));
}
return _resset.size();
}
Controller层
收集从View层发送来的请求,通过相应的Service层进行调用()。
StuController.h文件
#ifndef STUCONTROLLER_H
#define STUCONTROLLER_H
#include "IStuDao.h"
class StuController
{
public:
StuController();
StuController(IStuDao* _pStuDao);
~StuController();
int stuAdd(const Stu& _stu);
int stuDel(const int _sno);
int stuUpd(const int _sno, const Stu& _stu);
int stuQueBySno(const int _sno, Stu& _res);
int stuQueAll(vector<Stu>& _resset);
private:
IStuDao* pStuDao;
};
#endif // !STUCONTROLLER_H
StuController.cpp文件
#include "pch.h"
#include "StuController.h"
StuController::StuController() { }
/*
* 功能:
* StuController的构造函数
* 参数:
* pStuDao in Dao层抽象类指针
* 返回值:
* 无
* 说明:
* 使用new XXX()的方式传入参数
* 不能以类似 &XXX()的方式传入参数
* 因为在析构函数阶段会进行delete
* 创建时间:
* 2020-04-10 11:31:11
* 作者:
* Excious
**/
StuController::StuController(IStuDao * _pStuDao)
:pStuDao(_pStuDao)
{
}
/*
* 功能:
* StuController的析构函数
* 参数:
* void
* 返回值:
* 无
* 说明:
* 释放成员指针p
* 创建时间:
* 2020-04-10 17:56:09
* 作者:
* Excious
**/
StuController::~StuController()
{
if (this->pStuDao != NULL)
delete this->pStuDao;
}
int StuController::stuAdd(const Stu & _stu) { return this->pStuDao->stuInsert(_stu); }
int StuController::stuDel(const int _sno) { return this->pStuDao->stuDelete(_sno); }
int StuController::stuUpd(const int _sno, const Stu & _stu) { return this->pStuDao->stuUpdate(_sno, _stu); }
int StuController::stuQueBySno(const int _sno, Stu & _res) { return this->pStuDao->stuSelect(_sno, _res); }
int StuController::stuQueAll(vector<Stu>& _resset) { return this->pStuDao->stuSelectAll(_resset); }
View层
View层用于展示前端界面,接受用户请求,发送给Controller层进行处理,将处理结果进行展示,与用户进行交互。
为了让请求指令更加容易理解,我在类中定义了一个枚举类型enum CMD
.
StuView.h文件
#ifndef STUVIEW_H
#define STUVIEW_H
#include "StuController.h"
#include "StuServiceMySQL.h"
class StuView
{
public:
StuView();
~StuView();
void run();
private:
enum CMD { CMD_Add, CMD_Del, CMD_Upd, CMD_Que_one, CMD_Que_all, CMD_Exit, CMD_NULL };
StuController* pstucon;
static string business[6];
void initGraph()const;
CMD getCMD()const;
int execCMD(CMD cmd);
int stuViewAdd();
int stuViewDel();
int stuViewUpd();
int stuViewQue_one();
int stuViewQue_all();
void showQueRes(const Stu& _res)const;
void showQueRes(const vector<Stu>& _resset)const;
};
#endif // !STUVIEW_H
StuView.cpp文件
#include "pch.h"
#include "StuView.h"
#include <iostream>
#include <iomanip>
#include <string>
#include <conio.h>
string StuView::business[6] = { "[1]增加学生", "[2]删除学生", "[3]更新学生", "[4]查询学生", "[5]查询全部", "[0]退出系统" };
/*
* 功能:
* StuView类的缺省构造函数
* 参数:
* void
* 返回值:
* 无
* 说明:
* new出Dao层的特定实现,比如这里就用MySQL的实现
* 如果要修改MySQL的数据表,比如改为从其他表中读取,请到StuServiceMySQL类的缺省构造函数进行修改
* 如果要修改数据的读取来源,比如改为从Oracle读取,请将new StuServiceMySQL()换成其他实现类对象
* 创建时间:
* 2020-04-10 19:52:03
* 作者:
* Excious
**/
StuView::StuView()
:pstucon(new StuController(new StuServiceMySQL()))
{
}
/*
* 功能:
* StuView类的缺省构造函数
* 参数:
* void
* 返回值:
* 无
* 说明:
* delete掉成员指针
* 创建时间:
* 2020-04-10 20:18:35
* 作者:
* Excious
**/
StuView::~StuView()
{
if (this->pstucon != NULL)
delete this->pstucon;
}
/*
* 功能:
* 运行学生管理系统
* 参数:
* void
* 返回值:
* void
* 说明:
* 设置了控制台的背景色,监听按键信息调用相应函数
* 创建时间:
* 2020-04-10 20:13:24
* 作者:
* Excious
**/
void StuView::run()
{
string progress("");
for (int i = 0; i < 6; ++i)
{
cout << setw(90) << "正在启动学生管理系统:|";
progress += (i ? "██" : "");
cout << progress;
cout << setw(20 - i * 4) << "|" << endl;
Sleep(1000);
system("cls");
}
system("color 9F");
this->initGraph();
CMD cmd;
while (cmd = this->getCMD(), cmd != CMD_Exit)
{
this->execCMD(cmd);
Sleep(50); // 降低CPU占用
}
system("cls");
}
/*
* 功能:
* 绘制初始主菜单界面
* 参数:
* void
* 返回值:
* void
* 创建时间:
* 2020-04-10 22:07:38
* 作者:
* Excious
**/
void StuView::initGraph() const
{
cout << setfill(' ') << right << setw(104) << "欢迎来到学生管理系统" << endl;
cout << setfill('*') << setw(189) << "" << endl;
for (int i = 0; i < sizeof(business) / sizeof(string); ++i)
{
cout << "****" << setfill(' ') << setw(185) << "****" << endl;
cout << left << setw(89) << "****";
cout << business[i];
cout << right << setw(89) << "****";
}
cout << "****" << setw(185) << "****" << endl;
cout << setfill('*') << setw(189) << "" << endl;
}
/*
* 功能:
* 接收主菜单按键消息,返回相应指令
* 参数:
* void
* 返回值:
* CMD_Exit 退出系统
* CMD_Add 增加学生
* CMD_Del 删除学生
* CMD_Upd 更新学生
* CMD_Que_one 查询学生
* CMD_Que_all 查询全部
* CMD_NULL 无操作
* 创建时间:
* 2020-04-10 22:14:33
* 作者:
* Excious
**/
StuView::CMD StuView::getCMD() const
{
if (_kbhit()) // 如果有按键消息
{
switch (_getch()) // 因为可以直接return,就不写break了
{
case '0':
return CMD_Exit;
case '1':
return CMD_Add;
case '2':
return CMD_Del;
case '3':
return CMD_Upd;
case '4':
return CMD_Que_one;
case '5':
return CMD_Que_all;
default:
break;
}
}
return CMD_NULL;
}
/*
* 功能:
* 处理前端页面(初始主菜单)的请求
* 参数:
* cmd in 前端界面得到的请求
* 返回值:
* 0 功能正常结束
* -1 执行异常结束
* -2 预判无效请求
* 说明:
* 对于数据的展现还是直接输出来实现的
* 如果有其他展示要求、结果集要求,可以在特定的case下修改具体展示方式
* 创建时间:
* 2020-04-10 23:22:17
* 作者:
* Excious
**/
int StuView::execCMD(CMD cmd)
{
// 无操作直接return
if (cmd == CMD_NULL)
{
return 0;
}
system("cls");
// 执行相应命令
int flag = 0;
switch (cmd)
{
case CMD_Add:
flag = this->stuViewAdd();
break;
case CMD_Del:
flag = this->stuViewDel();
break;
case CMD_Upd:
flag = this->stuViewUpd();
break;
case CMD_Que_one:
flag = this->stuViewQue_one();
break;
case CMD_Que_all:
flag = this->stuViewQue_all();
break;
default:
break;
}
system("cls");
this->initGraph();
return flag;
}
/*
* 功能:
* 界面接收增加学生的请求,并发送给成员指针处理,并进行反馈
* 参数:
* void
* 返回值:
* -2 学生已经存在
* -1 增加学生异常
* 0 增加学生成功
* 创建时间:
* 2020-04-11 01:38:27
* 作者:
* Excious
**/
int StuView::stuViewAdd()
{
HWND hwnd = GetConsoleWindow(); // 获取控制台窗口句柄
int _sno;
string _sname;
Stu _res;
cout << "请输入要增加的学生的学号和姓名(以空格分开):";
cin >> _sno >> _sname;
if (!this->pstucon->stuQueBySno(_sno, _res))
{
MessageBox(hwnd, L"该学号已被使用!", L"提示", MB_ICONERROR | MB_OK);
return -2;
}
if (this->pstucon->stuAdd(Stu(_sno, _sname)))
{
MessageBox(hwnd, L"增加学生信息失败!", L"提示", MB_ICONERROR | MB_OK);
return -1;
}
MessageBox(hwnd, L"增加学生信息成功!", L"提示", MB_ICONINFORMATION | MB_OK);
return 0;
}
/*
* 功能:
* 界面接收删除学生的请求,并发送给成员指针处理,并进行反馈
* 参数:
* void
* 返回值:
* -2 不存在的学生
* -1 删除学生异常
* 0 删除学生成功
* 创建时间:
* 2020-04-11 01:45:18
* 作者:
* Excious
**/
int StuView::stuViewDel()
{
HWND hwnd = GetConsoleWindow(); // 获取控制台窗口句柄
int _sno;
Stu _res;
cout << "请输入要删除的学生的学号:";
cin >> _sno;
if (this->pstucon->stuQueBySno(_sno, _res))
{
MessageBox(hwnd, L"不存在该学生!", L"提示", MB_ICONERROR | MB_OK);
return -2;
}
if (this->pstucon->stuDel(_sno))
{
MessageBox(hwnd, L"删除学生信息失败!", L"提示", MB_ICONERROR | MB_OK);
return -1;
}
MessageBox(hwnd, L"删除学生信息成功!", L"提示", MB_ICONINFORMATION | MB_OK);
return 0;
}
/*
* 功能:
* 界面接收更新学生的请求,并发送给成员指针处理,并进行反馈
* 参数:
* void
* 返回值:
* -2 不存在的学生
* -1 更新学生异常
* 0 更新学生成功
* 创建时间:
* 2020-04-11 01:48:09
* 作者:
* Excious
**/
int StuView::stuViewUpd()
{
HWND hwnd = GetConsoleWindow(); // 获取控制台窗口句柄
int _sno;
int _sno_2;
string _sname;
Stu _res;
cout << "请输入要更新的学生的学号、更新后学生的学号和姓名(以空格分开三者):";
cin >> _sno >> _sno_2 >> _sname;
if (this->pstucon->stuQueBySno(_sno, _res))
{
MessageBox(hwnd, L"不存在该学生!", L"提示", MB_ICONERROR | MB_OK);
return -2;
}
if (this->pstucon->stuUpd(_sno, Stu(_sno_2, _sname)))
{
MessageBox(hwnd, L"更新学生信息失败!", L"提示", MB_ICONERROR | MB_OK);
return -1;
}
MessageBox(hwnd, L"更新学生信息成功!", L"提示", MB_ICONINFORMATION | MB_OK);
return 0;
}
/*
* 功能:
* 界面接收查询学生的请求,并发送给成员指针处理,并进行反馈
* 参数:
* void
* 返回值:
* -1 查询学生异常 / 不存在的学生
* 0 查询学生成功
* 创建时间:
* 2020-04-11 01:52:13
* 作者:
* Excious
**/
int StuView::stuViewQue_one()
{
HWND hwnd = GetConsoleWindow(); // 获取控制台窗口句柄
int _sno;
Stu _res;
cout << "请输入要查询的学生的学号:";
cin >> _sno;
system("cls");
if (this->pstucon->stuQueBySno(_sno, _res))
{
MessageBox(hwnd, L"查询学生信息失败(查询异常或不存在该学生)!", L"提示", MB_ICONERROR | MB_OK);
return -1;
}
MessageBox(hwnd, L"查询学生信息成功!\n点击确定后查看具体信息", L"提示", MB_ICONINFORMATION | MB_OK);
this->showQueRes(_res);
return 0;
}
/*
* 功能:
* 界面接收查询所有学生的请求,并发送给成员指针处理,并进行反馈
* 参数:
* void
* 返回值:
* -1 查询全部学生异常
* >=0 查询全部学生成功,并返回结果集的记录数
* 创建时间:
* 2020-04-11 01:52:13
* 作者:
* Excious
**/
int StuView::stuViewQue_all()
{
HWND hwnd = GetConsoleWindow(); // 获取控制台窗口句柄
vector<Stu> _resset;
if (this->pstucon->stuQueAll(_resset) < 0)
{
MessageBox(hwnd, L"查询学生信息失败(查询异常)!", L"提示", MB_ICONERROR | MB_OK);
return -1;
}
MessageBox(hwnd, L"查询学生信息成功!\n点击确定后查看具体信息", L"提示", MB_ICONINFORMATION | MB_OK);
this->showQueRes(_resset);
return _resset.size();
}
/*
* 功能:
* 显示查询单个学生的输出结果
* 参数:
* _res in 查询到的学生
* 返回值:
* void
* 说明:
* 展现方式是在控制台上进行输出,如果有需要修改展现方式,请在此函数内进行修改
* 创建时间:
* 2020-04-11 00:14:15
* 作者:
* Excious
**/
void StuView::showQueRes(const Stu& _res) const
{
cout << setfill(' ') << right << setw(74) << "+" << setfill('-') << setw(17) << "+" << setw(25) << "+" << endl;
cout << setfill(' ') << setw(74) << "|" << setw(17) << "学号 |" << setw(25) << "姓名 |" << endl;
cout << setw(74) << "+" << setfill('-') << setw(17) << "+" << setw(25) << "+" << endl;
cout << setfill(' ') << setw(74) << "|" << setfill(' ') << setw(15) << _res.getSno() << " |" << setw(23) << _res.getSname() << " |" << endl;
cout << setw(74) << "+" << setfill('-') << setw(17) << "+" << setw(25) << "+" << endl;
cout << endl;
cout << setfill(' ') << setw(104) << "按下任意键返回主菜单" << endl;
_getch();
}
/*
* 功能:
* 显示查询全部学生的输出结果
* 参数:
* _resset in 查询到的学生的集合
* 返回值:
* void
* 说明:
* 展现方式是在控制台上进行输出,如果有需要修改展现方式,请在此函数内进行修改
* 创建时间:
* 2020-04-11 00:17:23
* 作者:
* Excious
**/
void StuView::showQueRes(const vector<Stu>& _resset) const
{
cout << setfill(' ') << right << setw(74) << "+" << setfill('-') << setw(17) << "+" << setw(25) << "+" << endl;
cout << setfill(' ') << setw(74) << "|" << setw(17) << "学号 |" << setw(25) << "姓名 |" << endl;
cout << setw(74) << "+" << setfill('-') << setw(17) << "+" << setw(25) << "+" << endl;
for (vector<Stu>::const_iterator it = _resset.begin(); it != _resset.end(); ++it)
{
cout << setfill(' ') << setw(74) << "|" << setw(15) << it->getSno() << " |" << setw(23) << it->getSname() << " |" << endl;
}
cout << setw(74) << "+" << setfill('-') << setw(17) << "+" << setw(25) << "+" << endl;
cout << endl;
cout << setfill(' ') << setw(104) << "按下任意键返回主菜单" << endl;
_getch();
system("cls");
this->initGraph();
}
主程序
main.cpp文件
#include "pch.h"
#include <iostream>
#include "StuView.h"
using namespace std;
int main()
{
StuView* sys = new StuView();
Sleep(1000);
sys->run();
delete sys;
return 0;
}
测试环境
- 将MySQL给C提供的API引入到项目中(方法可以参考上次作业)
- 主机为
loaclhost
、端口号为3306
的MySQL数据库中,有一个用户名为tester
、密码为test
的用户,在该数据库中有一个字符集为utf8
、排序规则为utf8_general_ci
的cpp_homework
数据库,此数据库下有一个stu_info
表,初始数据如下图所示:
- 控制台调整至全屏状态,屏幕宽度为189,即一行可以容纳最多189个英文字符
测试流程
测试流程如下:
测试效果
下面用gif来展示具体效果
Tips:在gif的右下角有按键显示,可以观察程序运行时的按键消息
- 启动程序
- 查询全部初始数据
- 查询一个已经存在的学生
- 更新一个已经存在的学生
- 删除一个已经存在的学生
- 增加一个不存在的学生
- 增加一个学号已经存在的学生
- 删除一个不存在的学生
- 更新一个不存在的学生
- 查询一个不存在的学生
- 查询全部最终数据
- 退出程序
这里本来可以实现按完直接退出程序的,但是我使用VS2017创建的“控制台应用程序”,包含了pch.h文件。如果创建空项目可以实现直接退出,自动关闭控制台窗口。
有待完善之处
程序在以下方面还有待完善
-
因为用户有可能无意中在输入时给出了超出数量的输入,因此很有可能导致下一次操作时出现一些意料之外的问题。
应该是可以通过清空输入区缓存来实现的,但是VS2017中
cin.clear()
、cin.sync()
、fflush(stdin)
等方法均无效,暂时也没有找到容易实现、安全稳定的清空方法,可能实现出来会有一点点麻烦,以后再修改吧。另外一种方法就是换成图形化界面,用成熟的输入控件去实现接收输入。
-
控制台需要手动调整至全屏,程序界面不能自适应控制台窗口大小,在非全屏状态下界面的展示很混乱。
项目打包链接
本项目的代码文件、测试视频打包到了百度云
- 链接:https://pan.baidu.com/s/1x01p6iwlrIcFlh6Piz3wzw
- 提取码:hd1p
转载:https://blog.csdn.net/qq_44220418/article/details/105436133