目录
- 项目介绍
- 1、项目描述
- 2、市场调研
- 3、技术调研
- 项目设计
- 1、概要设计
- 2、详细设计
- ① 数据管理模块:
- ② 业务控制模块:
- ③ 前端界面模块:
- 3、代码实现
- 实现数据管理模块
- 实现业务控制模块
- 实现前端界面模块
项目介绍
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.lock();
bool ret = MysqlQuery(_mysql, DISH_SELECTONE);
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);
if(num != 1)
{
std::cout << "result error\n";
mysql_free_result(res);
return false;
}
MYSQL_ROW row = mysql_fetch_row(res);
(*dish)["id"] = dish_id;
(*dish)["name"] = row[1];
(*dish)["price"] = std::stoi(row[2]);
(*dish)["ctime"] = row[3];
mysql_free_result(res);
return true;
}
};
class TableOrder
{
private:
MYSQL *_mysql;
std::mutex _mutex;
public:
//mysql初始化
TableOrder()
{
_mysql = MysqlInit();
if(_mysql == NULL)
{
exit(-1);
}
}
~TableOrder()
{
if(_mysql != NULL)
{
MysqlRelease(_mysql);
_mysql = NULL;
}
}
bool Insert(const Json::Value &order)
{
#define ORDER_INSERT "insert tb_order values(null, '%s', 0, now());"
char str_sql[4096] = {0};
Json::FastWriter writer;
std::string dishes = writer.write(order["dishes"]);
dishes[dishes.size() - 1] = '\0';
sprintf(str_sql, ORDER_INSERT, dishes.c_str());
return MysqlQuery(_mysql, str_sql);
}
bool Delete(int order_id)
{
#define ORDER_DELETE "delete from tb_order where id = %d;"
char str_sql[4096] = {0};
sprintf(str_sql, ORDER_DELETE, order_id);
return MysqlQuery(_mysql, str_sql);
}
bool Update(const Json::Value &order)
{
#define ORDER_UPDATE "update tb_order set dishes = '%s', status = %d where id =
char str_sql[4096] = {0};
Json::FastWriter writer;
std::string dishes = writer.write(order["dishes"]);
sprintf(str_sql, ORDER_UPDATE,
dishes.c_str(), order["status"].asInt(),
order["id"].asInt());
return MysqlQuery(_mysql, str_sql);
}
bool SelectAll(Json::Value *orders)
{
#define ORDER_SELECTALL "select * from tb_order;"
_mutex.lock();
bool ret = MysqlQuery(_mysql, ORDER_SELECTALL);
if(ret == false)
{
_mutex.unlock();
return false;
}
MYSQL_RES *res = mysql_store_result(_mysql);
_mutex.unlock();
if(res == NULL)
{
std::cout << mysql_error(_mysql) << std::endl;
return false;
}
int num = mysql_num_rows(res);
for(int i = 0; i < num; i++)
{
MYSQL_ROW row = mysql_fetch_row(res);
Json::Value order;
order["id"] = std::stoi(row[0]);
Json::Reader reader;
Json::Value dishes;
reader.parse(row[1], dishes);
order["dishes"] = dishes;
order["status"] = std::stoi(row[2]);
order["mtime"] = row[3];
orders->append(order);
}
mysql_free_result(res);
return true;
}
bool SelectOne(int order_id, Json::Value *order)
{
#define ORDER_SELECTONE "select * from tb_order where id = %d;"
char str_sql[4096] = {0};
sprintf(str_sql, ORDER_SELECTONE, order_id);
_mutex.lock();
bool ret = MysqlQuery(_mysql, str_sql);
if(ret == false)
{
_mutex.unlock();
return false;
}
MYSQL_RES *res = mysql_store_result(_mysql);
_mutex.unlock();
if(res == NULL)
{
std::cout << "store failed!\n";
return false;
}
int num = mysql_num_rows(res);
if(num != 1)
{
std::cout << "one result error\n";
mysql_free_result(res);
return false;
}
MYSQL_ROW row = mysql_fetch_row(res);
(*order)["id"] = order_id;
Json::Reader reader;
Json::Value dishes;
reader.parse(row[1], dishes);
(*order)["dishes"] = dishes;
(*order)["status"] = std::stoi(row[2]);
(*order)["mtime"] = row[3];
mysql_free_result(res);
return true;
}
};
}
首先初始化数据库,也就是进行句柄初始化、连接数据库,设置字符编码集等操作,然后实现销毁句柄的函数,然后实现执行语句的函数,之后就是实现菜单信息的增删改查和订单信息的增删改查。
此时可以在test.cpp中进行局部测试,代码如下:
#include "db.hpp"
int main()
{
dish_test();
order_test();
}
void dish_test()
{
order_sys::TableDish dish;
/*
Json::Value val;
val["id"] = 3;
val["name"] = "烧茄子";
val["price"] = 1300;
//dish.Insert(val);
//dish.Update(val);
//dish.Delete(3);
*/
Json::Value val;
Json::StyledWriter writer;
//dish.SelectAll(&val);
dish.SelectOne(2, &val);
std::cout << writer.write(val) <<std::endl;
}
void order_test()
{
order_sys::TableOrder order;
/*
Json::Value val;
val["id"] = 7;
val["dishes"].append(2);
val["dishes"].append(1);
//order.Insert(val);
val["status"] = 1;
//order.Update(val);
order.Delete(7);
*/
Json::Value val;
Json::StyledWriter writer;
order.SelectAll(&val);
std::cout << writer.write(val) << std::endl;
}
实现业务控制模块
1、搭建HTTP服务器(采用httplib来实现,httplib的流程前面已经介绍过,即:(1)实例化Server对象,(2)添加请求方法/资源路径对应回调接口信息—告诉Server对象对于什么请求使用哪个回调接口进行业务处理,(3)回调用户业务接口会传入请求信息(根据请求进行业务处理),需要用户填充的响应信息(组织HTTP响应),(4)回调接口调用完毕,httplib获取到填充完毕的http响应信息,然后根据http协议格式组织响应数据)
2、根据HTTP的请求进行业务逻辑处理(菜品信息和订单信息的增删改查)
3、组织HTTP响应返回给客户端
我们先将httplib.h导入到工程中(在github上有很多开源文件,直接克隆过来即可)
例如实现代码:
#include "httplib.h"
#include <iostream>
void HelloWorld(const httplib::Request &req,httplib::Response &rsp)
{
std::cout<<"method:"<<req.method<<std::endl;
std::cout<<"path:"<<req.path<<std::endl;
std::cout<<"body:"<<req.body<<std::endl;
rsp.status=200;//响应状态码
rsp.body="<html><body><h1>HelloWorld</h1></body></html>";
rsp.set_header("Content-Type","text/html");
//std::string body="<html><body><h1>HelloWorld<</h1></body></html>";
//rsp.set_content(body,body.size(),"text/html");
return ;
}
int main()
{
int a = 100;
httplib::Server server;//实例化Server对象
//server.Get(请求的资源路径,回调函数地址);
//针对指定资源的GET请求,采用指定的回调函数---添加请求路由信息
//server.Put(请求的资源路径,回调函数地址)
//这些接口主要作用是添加了一张路由表---什么请求对应什么处理函数
//请求来了就会从路由表中查看,有没有这个请求的处理函数,没有就返回404,有就进行相应请求
server.Get("/helloworld",HelloWorld);
server.Get("/hi",[&](const httplib::Request &req,httplib::Response &rsp){
std::cout<<"a:"<<a<<std::endl;
std::string body="<html><body><h1>Hi</h1></body></html>";
rsp.set_content(body.c_str(),body.size(),"text/html");
});
server.listen("0.0.0.0",9000);//搭建http服务器
return 0;
}
通过浏览器访问:
服务器IP:端口号/HelloWorld
服务器IP:端口号/HI
使用函数调用的方式来实现一个简单的http服务器,即在浏览器界面会显示HelloWorld,也可以使用lambda表达式来实现,会在浏览器上显示Hi。
上述这就是使用httplib来实现http服务器
接下来正式编写业务逻辑模块代码;
我们主要是要实现收到请求向数据库执行相应操作(增删改查),从而响应到客户端,首先是对菜单信息的请求:
(1)插入菜品:获取菜品信息正文—json字符串,将json字符串解析成为json::Value对象,通过json::Value信息向数据库中插入菜品信息。
(2)删除菜品:获取菜品ID—id=req.matches[1],通过菜品id从数据库中删除一个菜品信息。
(3)更新菜品:获取菜品信息正文—json字符串,将json字符串解析成为json::Value对象,通过json::Value信息向数据库中更新菜品信息。
(4)查询菜品:(若是查询单个菜品,要先获取菜品id)从数据库中获取到菜品信息,存放到json::Value对象中,将json::Value对象序列化成为json字符串—json::FastWriter.write(json::Value),将序列化的字符串放到http响应正文中(通过set_content),响应给客户端。
接着是对订单信息的请求(与菜单信息相似):
(1)插入订单:获取订单信息正文,将json字符串解析成为json::Value对象,通过json::Value信息向数据库中插入订单信息。
(2)删除订单:获取订单id—id=req.matches[1],通过订单id从数据库中删除一个订单信息。
(3)更新订单:获取订单信息正文—json字符串,将json字符串解析为json::Value对象,通过json::Value信息向数据库中更新菜品信息。
(4)查询订单:(若是查询单个订单,要先获取订单id)从数据库中获取到菜品信息,存放到json::Value对象中,将json::Value对象序列化为json字符串—json::FastWriter.write(json::Value),将序列化的字符串放到http响应正文中(通过set_content),响应给客户端。
以下为业务逻辑模块main.cpp代码:
#include "db.hpp"
#include "httplib.h"
#define WWWROOT "./wwwroot"
using namespace httplib;
order_sys::TableDish *tb_dish = NULL;
order_sys::TableOrder *tb_order = NULL;
void DishInsert(const Request &req, Response &rsp)
{
//1.业务处理
//①.解析正文(json反序列化),得到菜品信息
Json::Value dish;
Json::Reader reader;
bool ret = reader.parse(req.body, dish);
if(ret == false)
{
rsp.status = 400;
Json::Value reason;
Json::FastWriter writer;
reason["result"] = false;
reason["reason"] = "dish info parse failed!";
rsp.body = writer.write(reason);
rsp.set_header("Content-Type", "application/json");
std::cout << "insert- dish parse error\n";
return ;
}
//②.将菜品信息插入数据库
ret = tb_dish->Insert(dish);
if(ret == false)
{
Json::Value reason;
Json::FastWriter writer;
reason["result"] = false;
reason["reason"] = "mysql insert failed!";
rsp.status = 500;
rsp.body = writer.write(reason);
rsp.set_header("Content-Type", "application/json");
std::cout << "mysql insert dish error\n";
return ;
}
//2.设置响应信息:响应状态码mttplib默认设置200
//rsp.status = 200;
return ;
}
void DishDelete(const Request &req, Response &rsp)
{
//1.获取要删除的菜品id /dish/id
int dish_id = std::stoi(req.matches[1]);
//2.数据库删除操作
bool ret = tb_dish->Delete(dish_id);
if(ret == false)
{
rsp.status = 500;
std::cout << "mysql delete dish error\n";
return ;
}
return ;
}
void DishUpdate(const Request &req, Response &rsp)
{
int dish_id = std::stoi(req.matches[1]);
Json::Value dish;
Json::Reader reader;
bool ret = reader.parse(req.body, dish);
if(ret == false)
{
rsp.status = 400;
std::cout << "update- parse dish info failed\n";
return ;
}
dish["id"] = dish_id;
ret = tb_dish->Update(dish);
if(ret == false)
{
rsp.status = 500;
std::cout << "mysql update dish error\n";
return ;
}
return ;
}
void DishGetAll(const Request &req, Response &rsp)
{
Json::Value dishes;
bool ret = tb_dish->SelectAll(&dishes);
if(ret == false)
{
rsp.status = 500;
std::cout << "selectall- mysql select dish error\n";
return ;
}
Json::FastWriter writer;
rsp.body = writer.write(dishes);
rsp.set_header("Content-Type", "application/json");
return ;
}
void DishGetOne(const Request &req, Response &rsp)
{
int dish_id = std::stoi(req.matches[1]);
Json::Value dish;
bool ret = tb_dish->SelectOne(dish_id, &dish);
if(ret == false)
{
rsp.status = 500;
std::cout << "selecone- mysql select dish error\n";
return ;
}
Json::FastWriter writer;
rsp.body = writer.write(dish);
return ;
}
void OrderInsert(const Request &req, Response &rsp)
{
Json::Value order;
Json::Reader reader;
bool ret = reader.parse(req.body, order);
if(ret == false)
{
rsp.status = 400;
std::cout << "insert- parse order info failed\n";
return ;
}
ret = tb_order->Insert(order);
if(ret == false)
{
rsp.status = 500;
std::cout << "insert- mysql insert order error\n";
return ;
}
return ;
}
void OrderDelete(const Request &req, Response &rsp)
{
int order_id = std::stoi(req.matches[1]);
bool ret = tb_order->Delete(order_id);
if(ret == false)
{
rsp.status = 500;
std::cout << "delete- mysql delete order error\n";
return ;
}
return ;
}
void OrderUpdate(const Request &req, Response &rsp)
{
int order_id = std::stoi(req.matches[1]);
Json::Value order;
Json::Reader reader;
bool ret = reader.parse(req.body, order);
if(ret == false)
{
rsp.status = 400;
std::cout << "update- parse order info failed\n";
return ;
}
order["id"] = order_id;
ret = tb_order->Update(order);
if(ret == false)
{
rsp.status = 500;
std::cout << "update- mysql update order error\n";
return ;
}
return ;
}
void OrderGetAll(const Request &req, Response &rsp)
{
Json::Value orders;
bool ret = tb_order->SelectAll(&orders);
if(ret == false)
{
rsp.status = 500;
std::cout << "selectAll- mysql get order error\n";
return ;
}
Json::FastWriter writer;
rsp.body = writer.write(orders);
return ;
}
void OrderGetOne(const Request &req, Response &rsp)
{
int order_id = std::stoi(req.matches[1]);
Json::Value order;
bool ret = tb_order->SelectOne(order_id, &order);
if(ret == false)
{
rsp.status = 500;
std::cout << "selectOne- mysql getone order error\n";
return ;
}
Json::FastWriter writer;
rsp.body = writer.write(order);
return ;
}
int main()
{
tb_dish = new order_sys::TableDish();
tb_order = new order_sys::TableOrder();
Server server;
//server.set_base_dir(静态资源默认根路径)
//设置静态资源根目录之后,如果浏览器请求的是根目录下的静态资源,httplib就会自动的将资源数据读取出来返回给浏览器
server.set_base_dir(WWWROOT);//设置前端页面相对的根目录
server.Post("/dish", DishInsert);
//正则表达式:(\d+)表示匹配一个数字字符一次或多次
//正则表达式:()表示捕捉匹配括号中规则的文本
//R"()"表示括号中的字符串去除特殊字符的特殊含义
server.Delete(R"(/dish/(\d+))", DishDelete);
server.Put(R"(/dish/(\d+))", DishUpdate);
server.Get(R"(/dish/(\d+))", DishGetOne);
server.Get(R"(/dish)", DishGetAll);
server.Post("/order", OrderInsert);
server.Delete(R"(/order/(\d+))", OrderDelete);
server.Put(R"(/order/(\d+))", OrderUpdate);
server.Get(R"(/order/(\d+))", OrderGetOne);
server.Get(R"(/order)", OrderGetAll);
server.listen("0.0.0.0", 9000);
return 0;
}
执行命令g++ -std=c++11 main.cpp -o main -L/usr/lib/x86_64-linux-gnu -lmysqlclient -ljsoncpp -lpthread
也可以定义makefile文件执行make命令
//makefile
LFLAG =-L/usr/lib/x86_64-linux-gnu -lmysqlclient -ljsoncpp -lpthread
main:main.cpp db.hpp
g++ -std=c++11 $^ -o $@ $(LFLAG)
生成main后执行,此时可以通过局部测试工具PostMan对各个操作进行测试
实现前端界面模块
在视图模块,也就是前端页面,主要实现菜品信息的浏览以及下单操作,如图为最终界面:
菜单界面:
管理界面:
前端主要是使用html/css/js/vue.js/jquery等来进行实现,
前端代码过于冗杂,这里只放了重点代码内容:
index.html如下:
<!--index.html-->
<!-- Menu Area (Start) -->
<section class="menu" id="menu-gallery">
<div class="heading">
<h2><span>Menu</span></h2>
</div>
<div class="content">
<div class="box-container">
<div class="menu-gallery active" id="Starters">
<div class="menu-item" id="myorder">
<img src="static/picture/11.jpg" alt="">
<div class="content">
<div class="intro">
<h3>ysy的小酒馆</h3>
<div class="price">菜单</div>
</div>
<div class="text" id="myorder">
<table class="table" style="width:100%;">
<thead>
<tr class="tou">
<th>名称</th>
<th>单价</th>
<th>操作</th>
</tr>
</thead>
<tfoot>
<tr>
<th colspan="3" style="text-align:right">
<button type="button" class="btn btn-primary" v-on:click="insert_order()">下单</button>
总价:{{get_total()}}
</th>
</tr>
</tfoot>
<tbody>
<tr v-for="dish in dishes" style="text-align:center">
<td>{{dish.name}}</td>
<td>{{dish.price/100}}</td>
<td>
<div class="form-check">
<label class="form-check-label">
<input class="form-check-input" type="checkbox" v-model="dish.selected" />选中下单
</label>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var myorder = new Vue({
el: '#myorder',
data: {
dishes: []
},
methods: {
get_total:function()
{
var total = 0;
for(i=0; i<this.dishes.length; i++)
{
if(this.dishes[i].selected)
{
total += (this.dishes[i].price / 100);
}
}
return total;
},
insert_order:function()
{
var order =
{
dishes:[]
}
for(i=0; i<this.dishes.length; i++)
{
if (this.dishes[i].selected)
{
order.dishes.push(this.dishes[i].id);
}
}
//ajax-其实就是一个http客户端
$.ajax({
url: "/order",
type: "post",
data: JSON.stringify(order),
success:function()
{
alert("下单成功");
}
})
},
get_alldishes:function()
{
$.ajax({
url: "/dish",
type: "get",
context:this,
success:function(res, status, xhr)
{
this.dishes = res;
}
})
}
}
})
myorder.get_alldishes();
</script>
admin.html如下
<!-- admin.html -->
<!-- Menu Area (Start) -->
<section class="menu" id="menu-gallery">
<div class="heading">
<h2><span>Menu</span></h2>
</div>
<div class="content">
<div class="box-container">
<div class="menu-gallery active" id="Starters">
<div class="menu-item" id="myorder">
<img src="static/picture/11.jpg" alt="">
<div class="content">
<div class="intro">
<h3>ysy的小酒馆</h3>
<div class="price">
<button type="button" class="btn btn-primary" v-on:click="insert_show()">新增</button>
</div>
</div>
<div class="text" id="myorder">
<table class="table" style="width:100%;">
<thead>
<tr class="tou">
<th>名称</th>
<th>单价</th>
<th>操作</th>
</tr>
</thead>
<tfoot v-show="show_flag==true">
<tr>
<th>
<input type="text" v-model="dish.name"/>
</th>
<th>
<input type="text" v-model="dish.price"/>
</th>
<th style="text-align:right">
<button type="button" class="btn btn-primary" v-on:click="insert_dish()">提交</button>
</th>
</tr>
</tfoot>
<tbody>
<tr v-for="dish in dishes" style="text-align:center">
<td>
<span v-show="update_flag==false">{{dish.name}}</span>
<span><input type="text" v-show="update_flag==true" v-model="dish.name" /></span>
</td>
<td >
<span v-show="update_flag==false">{{dish.price/100}}</span>
<span><input type="text" v-show="update_flag==true" v-model="dish.price" /></span>
</td>
<td>
<button type="button" class="btn btn-primary" v-show="update_flag==false" v-on:click="update_dishes">修改</button>
<button type="button" class="btn btn-primary" v-show="update_flag==true" v-on:click="update_dish(dish.id)">提交</button>
<button type="button" class="btn btn-primary" v-on:click="delete_dish(dish.id)">删除</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var myorder = new Vue({
el: '#myorder',
data: {
dishes: [],
update_flag: false,
show_flag: false,
dish: {
name: "",
price:0
}
},
methods: {
get_total:function()
{
var total = 0;
for(i=0; i<this.dishes.length; i++)
{
if(this.dishes[i].selected)
{
total += (this.dishes[i].price / 100);
}
}
return total;
},
insert_order:function()
{
var order =
{
dishes:[]
}
for(i=0; i<this.dishes.length; i++)
{
if (this.dishes[i].selected)
{
order.dishes.push(this.dishes[i].id);
}
}
//ajax-其实就是一个http客户端
$.ajax({
url: "/order",
type: "post",
data: JSON.stringify(order),
success:function()
{
alert("下单成功");
}
})
},
get_alldishes:function()
{
$.ajax({
url: "/dish",
type: "get",
context:this,
success:function(res, status, xhr)
{
this.dishes = res;
}
})
},
//增
insert_show:function()
{
this.show_flag = true;
},
insert_dish:function()
{
this.dish.price *= 100;
$.ajax({
url: "/dish",
type: "post",
data: JSON.stringify(this.dish),
context:this,
success: function (res, status, xhr) {
this.get_alldishes();
this.show_flag = false;
}
})
},
//删
delete_dish:function(dish_id)
{
$.ajax({
url: "/dish/" + dish_id,
type: "delete",
data: JSON.stringify(this.dish),
context: this,
success: function (res, status, xhr) {
alert("删除成功");
this.get_alldishes();
this.show_flag = false;
}
})
},
//改
update_dishes: function () {
this.update_flag = true;
},
update_dish:function(dish_id)
{
//this.dish.price *= 100;
for (i = 0; i < this.dishes.length; i++) {
if (this.dishes[i].id==dish_id) {
console.log("开始提交菜品更新,菜品ID:", dish_id);
console.log("待提交的菜品信息:", this.dishes[i]);
$.ajax({
url: "/dish/" + dish_id,
type: "put",
data: JSON.stringify(this.dishes[i]),
context: this,
success: function (res, status, xhr) {
alert("修改成功");
this.get_alldishes();
this.update_flag = false;
},
error: function (xhr, status, error) {
console.error("AJAX 请求失败:", error);
alert("修改失败,请稍后重试");
}
})
}
}
}
}
})
myorder.get_alldishes();
</script>
为了可以让前端界面与后台建立连接,只需要为main.cpp的main函数中添加server.set_base_dir("./wwwroot);代码(wwwroot是前端工程文件名),具体实现在业务处理模块中:这里仅截出前端界面链接代码:
Server server;
//server.set_base_dir(静态资源默认根路径)
//设置静态资源根目录之后,如果浏览器请求的是根目录下的静态资源,httplib就会自动的将资源数据读取出来返回给浏览器
server.set_base_dir(WWWROOT);//设置前端页面相对的根目录