目录
项目介绍
1、项目描述
使用户可以通过浏览器访问服务器获取菜品信息并进行点餐;以及可以使管理员通过浏览访问服务器实现订单以及菜品的管理。
2、市场调研
减少人力资源开销,便利点餐环节。
3、技术调研
用到以下技术:
线程,socket,http,json,mysql,STL
项目设计
1、概要设计
- 框架设计MVC框架
model(数据管理模块):–管理数据(菜品,订单),外界想要访问数据必须通过这个模块完成,不能直接访问。
view(视图界面模块):–浏览器前端界面,用户或者管理员的操作都是通过前端界面完成。
controller(业务控制模块):–搭建服务器针对前端的请求进行对应业务处理 - 项目流程图
2、详细设计
① 数据管理模块:
菜单数据管理和订单数据管理通过MySQL数据库进行数据管理,首先来介绍数据库表的设计:
数据库表包括以下两个表:
菜品信息表:菜品ID,菜品名称,菜品单价,修改时间
订单信息表:订单ID,订单菜品,订单状态,修改时间
1、 MYSQL基本操作
(1)首先在Linux系统下完成对mariadb的配置,我使用的基于Ubuntu的Linux系统,对于mariadb配置的过程见该文mariadb配置
(2)配置完成之后就进行对数据表的设计,进入项目文件,创建一个db.sql文件,用于执行创建数据库和数据库表的操作,并对表中内容进行指定。
//创建数据库
create database if not exists order_sys;
use order_sys;
//创建菜品信息表
create table if not exists tb_dish(
id int primary key auto_increment,
name varchar(32) unique not null,
prece int not null,
ctime datetime
);
//创建订单信息表
create table if not exists tb_order(
id int primary key auto_increment,
dishes varchar(255) comment '[1, 2]',
status int comment '0-未完成,1-完成',
mtime datetime
);
//测试插入信息
insert tb_dish values(null, "百威", 2800, now()),
(null, "科罗娜", 1800, now());
insert tb_order values(null, "[1,2]", 0, now()),
(null, "[2]", 0, now());
(3)编写完成db.sql后,在当前终端执行mysql -uroot -p > db.sql操作,将其连上数据库并执行db.sql里面的语句命令,此时进入mysql数据库查看内容。
如上:成功创建order_sys数据库,并在order_sys数据库内创建两个表单。
如上查看两个表单的信息。
2、数据管理
数据库操作时,在业务模块中我们并不直接操作数据库,是通过数据管理模块来进行操作,在数据管理模块实现所有数据库的操作,向外提供接口功能即可;
(1)菜单数据管理模块:添加菜品,删除菜品,修改菜品,获取菜品(所有,单个)
class DishesTable{
public:
bool Insert(单个菜品信息);//向数据库中添加菜品信息
bool SelectOne(int dish_id,单个菜品信息);//通过菜单ID查询单个菜品信息,通过第二个参数返回
bool SelectAll(多个菜品信息的返回);//从数据库中查询所有菜品信息,通过参数返回
bool Update(单个菜品信息);//修改数据库中的菜品信息
bool Delete(int dish_id);//删除指定菜品
private:
MYSQL* _mysql;
};
(2)订单数据管理模块:添加订单删除订单,修改订单(菜品,状态),获取订单(所有,指定id)
class OrdersTable{
public:
bool Insert(单个订单信息);//向数据库中添加订单信息
bool SelectOne(int order_id,单个订单信息);//通过订单ID查询单个订单信息,通过第二个参数返回
bool SelectAll(多个订单信息的返回);//从数据库中查询所有订单信息,通过参数返回
bool Update(单个订单信息);//修改数据库中的订单信息
bool Delete(int order_id);//删除指定订单
private:
MYSQL* _mysql;
};
② 业务控制模块:
业务处理主要包括:接收客户端请求,进行处理满足用户需求
1>接收客户端请求数据,并解析数据;
2>根据解析得到的请求方法和路径,进行业务处理,
3>组织http响应数据,发送给客户端;
- 搭建服务器:HTTP服务器(这里采用httplib库搭建)
- 通信接口设计:什么样的请求对应什么样的业务处理和响应 ①静态页面请求:html页面(以及依赖的css/js文件) ②动态数据请求:菜品数据,订单数据
通信接口采用restful风格接口设计:基于http协议,适应xml或者json格式定义正文序列化方式。定义操作类型:新增-POST;删除-DELETE;修改-PUT;获取-GET
这里使用Restful风格的通信数据格式即正文采用json串组织数据,json串是轻量级的数据交换格式。json数据库的组织和解析也通过第三方库jsoncpp实现以此来节省时间成本,具体定义以及操作见该文jsoncpp基本应用认识
1、httplib如何搭建HTTP服务器,以及httplib搭建服务器之后对于请求的处理流程
httplib工作流程:
httplib::Server map<pair<string,string>, function> route—请求与处理的路由表:当服务器收到一个客户端连接,则将新建连接抛入线程池,线程池中的线程负责与指定客户端进行通信(http通信)
- 接收请求数据,按照http请求协议格式进行解析(请求方法,资源路径,查询字符串,头部字段,正文…) 实例化httplib::Request对象,将解析的信息填入其中;
- 根据请求信息,在route路由表中查找针对这个请求有没有对应的处理函数: (1)如果没有则直接返回404–请求的资源不存在; (2)如果有,则使用对应函数指针执行这个处理函数(程序员自定义的处理函数),①传入请求信息;②实例化一个httplib::Response对象,传入函数,在处理函数内部,用户实现针对请求的业务处理,在业务处理完毕后填充Response对象。
3.线程中执行完处理函数之后,得到了一个填充完毕的Response对象,根据其中的数据(响应状态码,正文数据,头部字段)组织http响应协议格式的数据,回复给客户端。
4.等待还有没有请求需要处理(没有则关闭套接字g)
httplib::Request{
method:请求方法
path:资源路径
version:协议版本
body:正文
headers:头部信息的键值对
params:url中查询字符串信息的键值对
}
httplib::Response{
status:响应状态码
headers:头部信息键值对
body:响应正文
}
在httplib中有一个Server类,主要是针对各个请求方法来处理相应的路由信息,含有的内容如下:
httplib::Server{
Get(资源路径,回调函数);
Put(资源路径,回调函数);
Post(资源路径,回调函数); srv.Post(/dish,InsertDish)
Delete(资源路径,回调函数);
listen(绑定的IP地址,端口);—搭建TCP服务器
}
因此搭建HTTP服务器就是在main函数中实现以下:
main()
{
httplib::Server server;
server.Get(…);//添加路由信息
server.listen(ip,port);//开始TCP服务器
return 0;
}
2、实现对菜品数据管理通信接口:
(1)菜品信息的上传通信接口设计
http请求信息构建,然后发起请求,服务器进行相应,如图(Restful风格):
(2)获取所有菜品信息
如图:
(3)获取单个菜品信息
(4)更新菜品信息,如图:
(5)删除菜品信息
订单数据管理通信接口与菜单信息类似:
③ 前端界面模块:
html的编写渲染 实现:html+css+js(vue.js)
首先使用html编写出一个静态页面, 然后通过css进行样式渲染,最后通过vue.js实现数据的动态渲染。
3、代码实现
实现数据管理模块
mysql是一个客户端/服务端模式的数据库,如图:
mysql在代码中进行操作的目的就是自己搭建一个mysql客户端完成我们想要的操作
mysql代码操作:
初始化操作:
1.初始化操作句柄
2.通过句柄链接mysql服务器
3.设置客户端字符集(utf8)
4.选择使用的数据库
数据操作:
5.执行语句:
增删改:执行语句–执行语句成功即可
查询:执行语句-将查询结果获取到本地;获取结果中数据条数、列数;遍历结果集获取每一条数据的每一列;获取本地结果集
6.关闭句柄释放资源
7.获取接口执行失败原因
mysql的相关操作具体介绍在该链接mysql接口认识
关于jsoncpp的使用在该链接jsoncpp介绍
了解上述两个内容后,实现工程db.hpp,实现mysql的初始化(包含初始化句柄,建立数据库连接,设置字符编码集)、mysql的销毁、mysql的执行语句以及实现对菜单信息的增删改查和订单信息的增删改查,代码如下:
/*
*db.hpp
*数据管理模块
*基于mysql封装数据对象操作类
* 1、菜品信息表的操作类
* 2、订单信息表的操作类
* 注意事项:菜品信息/订单信息的通信格式--采用json进行数据传输
*/
#include<iostream>
#include<string>
#include<mysql/mysql.h>
#include<jsoncpp/json/json.h>
#include<mutex>
namespace order_sys
{
#define MYSQL_SERVER "127.0.0.1"
#define MYSQL_USER "root"
#define MYSQL_PASSWD "******"
#define MYSQL_DBNAME "order_sys"
static MYSQL * MysqlInit()
{
//初始化句柄
//mysql_init(句柄地址);
MYSQL *mysql = NULL;
mysql = mysql_init(NULL);
if(mysql == NULL)
{
std::cout << "mysql init failed!\n";
return NULL;
}
//连接服务器
//mysql_real_connect(句柄,服务器IP,用户名,密码,数据库名称,端口,套接字文件,客户端>
if(mysql_real_connect(mysql, MYSQL_SERVER, MYSQL_USER, MYSQL_PASSWD, MYSQL_DBNAME, 0, NU
{
std::cout << mysql_error(mysql) << std::endl;
return NULL;
}
//设置字符集
//mysql_set_character_set(句柄,字符集名称);
if(mysql_set_character_set(mysql, "utf8mb4") != 0)
{
std::cout << mysql_error(mysql) << std::endl;
return NULL;
}
//选择数据库(连接服务器时已经默认选择数据库了)
//mysql_select_db(句柄,数据库名称);
//mysql_select_db(mysql, MYSQL_DBNAME);
return mysql;
}
static void MysqlRelease(MYSQL *mysql)
{
if(mysql != NULL)
{
mysql_close(mysql);
}
}
static bool MysqlQuery(MYSQL *mysql, const std::string &sql)
{
//mysql_query(句柄,sql语句);
if(mysql_query(mysql, sql.c_str()) != 0)
{
std::cout << sql << std::endl;
std::cout << mysql_error(mysql) << std::endl;
return false;
}
return true;
}
class TableDish
{
private:
MYSQL *_mysql;
std::mutex _mutex;
public:
//mysql初始化
TableDish()
{
_mysql = MysqlInit();
if(_mysql == NULL)
{
exit(-1);
}
}
~TableDish()
{
MysqlRelease(_mysql);
_mysql = NULL;
}
bool Insert(const Json::Value &dish)
{
//组织sql语句
#define DISH_INSERT "insert tb_dish values(null, '%s', %d, now());"
char str_sql[4096] = {
0};
sprintf(str_sql, DISH_INSERT, dish["name"].asCString(), dish["price"].asInt());
if(MysqlQuery(_mysql, str_sql) == false)
{
return false;
}
return true;
}
bool Delete(int dish_id)
{
#define DISH_DELETE "delete from tb_dish where id = %d;"
char str_sql[4096] = {
0};
sprintf(str_sql, DISH_DELETE, dish_id);
return MysqlQuery(_mysql, str_sql);
}
bool Update(const Json::Value &dish)
{
#define DISH_UPDATE "update tb_dish set name = '%s', price = %d where id = %d;"
std::string priceStr = dish["price"].asString();
int price = std::stoi(priceStr);
char str_sql[4096] = {
0};
sprintf(str_sql, DISH_UPDATE,
dish["name"].asCString(), price,
dish["id"].asInt());
return MysqlQuery(_mysql, str_sql);
}
bool SelectAll(Json::Value *dishes)
{
#define DISH_SELECTALL "select * from tb_dish;"
_mutex.lock();
bool ret = MysqlQuery(_mysql, DISH_SELECTALL);
if(ret = false)
{
_mutex.unlock();
return false;
}
MYSQL_RES *res = mysql_store_result(_mysql);
_mutex.unlock();
if(res == NULL)
{
std::cout << "store result failed!\n";
return false;
}
int num = mysql_num_rows(res);
for(int i = 0; i < num; i++)
{
MYSQL_ROW row = mysql_fetch_row(res);
Json::Value dish;
dish["id"] = std::stoi(row[0]);
dish["name"] = row[1];
dish["price"] = std::stoi(row[2]);
dish["ctime"] = row[3];
dishes->append(dish);
}
mysql_free_result(res);
return true;
}
bool SelectOne(int dish_id, Json::Value *dish)
{
#define DISH_SELECTONE "select * from tb_dish where id = %d;"
char str_sql[4096] = {
0};
sprintf(str_sql, DISH_SELECTONE, dish_id);
_mutex