项目——订单系统

目录

    • 项目描述
        • 功能
        • 技术点
        • 框架
    • 详细设计
        • 数据管理模块
        • 业务控制模块
        • 前端界面模块
    • 涉及知识点
        • JSON
            • Value
            • Writer
            • Reader
        • 正则表达式
        • MySQL语句
        • MySQL代码
        • httplib基础
    • 项目总结
    • 项目代码

项目描述

功能
  • 用户可以通过浏览器访问服务器获取菜品信息并进行点餐;
  • 管理员可以通过浏览器访问服务器实现订单以及菜品的管理;
技术点
  • 多线程、socket、http、json、mysql、stl;
框架
  • 框架:简单 MVC 框架;
  • M(Model):数据管理模块,管理菜品、订单信息,外界想要访问数据必须通过这个模块完成,不能直接访问;
  • V(View):前端界面模块,浏览器的前端所展示的界面,用户或者管理员的操作都是通过前端界面来完成的;
  • C(Controller):业务控制模块(服务器),针对前端的请求做出对应的处理;
    在这里插入图片描述

详细设计

数据管理模块
  1. 数据存储:MySQL 数据库,原因:免费、可跨主机访问、线程安全;
  2. 数据库表的设计:菜品表、订单表;
    菜品表:菜品序号、菜品名称、菜品单价、修改时间;
    订单表:订单序号、订单菜品、订单状态、修改时间;
  3. 代码设计:菜品类、订单类;
    菜品类:添加、删除、修改、查看(单个 / 全部);
    订单类:添加、删除、修改(菜品信息 / 菜品状态)、查看(单个 / 全部);
业务控制模块
  1. HTTP 服务器:基于 http 协议,使用 httplib 库搭建 http 服务器;
  2. 通信接口设计:借助 httplib 库编写请求与响应接口,什么样的请求对应什么样的业务处理和响应;
    • 静态页面请求:HTML 页面(CSS / JS 文件,在一些现成的模板上以修改为主),然后将该页面命名为 index.html,将该页面所在目录设置为静态资源路径;
    • 动态数据请求:菜品 / 订单数据增、删、改、查,通信接口采用 restful 风格接口设计,基于 HTTP 协议,使用 json 格式定义正文序列化方式,定义操作类型:新增-POST,删除-DELETE,修改-PUT,获取-GET;
请求方式请求资源响应结果
Get/ 或者 index.html展示菜品信息界面、下单界面
Post/dish管理员添加菜品
Delete/dish/数字删除指定数字序号的菜品
Put/dish/数字修改指定数字序号的菜品
Post/order添加新的订单
前端界面模块
  • 前端界面:基于简单的 html、css、vue.js 以及 ajax 实现前端界面的静态页面展示以及动态数据获取渲染功能;
  • html:完成页面的布局;
  • css:样式语言,对标签容器进行样式修饰,让简单的 html 页面更加好看;
  • vue.js:脚本语言,让页面可以动态渲染展示;

涉及知识点

JSON
Value
  • 这是 JSON 与外界进行数据转换的一个对象类,这其中重载了很多的运算符,包含了大量的类型转换函数;
//将下面的数据序列化
string name = "zhangjinrui";    
int age = 18;    
vector<double> score = {88.8, 99.9, 77.7};    
//定义Value对象
Json::Value val;    
//普通类型(字符串、整型)序列化
val["name"] = name;                                                             
val["age"] = age;    
//数组序列化需要使用到append接口
for(int i = 0; i < score.size(); i++){    val["score"].append(score[i]);    
}
//下面为将JSON数据格式转化为普通数据类型
string name2 = val["name"].asString();
int age2 = val["age"].asInt();
double math_score = val["score"][1].asDouble();
Writer
  • 实现序列化类,将 Json::Value 对象中的数据序列化成为 JSON 格式的字符串;
//使用FastWriter定义的对象进行序列化,序列化结果是比较紧凑的,没有空格,不便观赏,但是传输效率高
Json::FastWriter writer1;
string str1 = writer1.write(val);
//使用StyledWriter定义的对象进行序列化,序列化的结果是便于观看的,但是由许多多余的空格,传输效率不高
Json::StyledWriter writer2;
string str2 = writer2.write(val);

在这里插入图片描述

Reader
  • 实现反序列化类,将 JSON 格式字符串转换为多个数据对象,存储在 Json::Value 对象中;
//使用parse接口将序列化的字符串转换为JSON数据对象,成功返回true,失败返回false
Json::Reader read;
Json::Value val2;
//反序列化结果存放在val2中,然后进行使用
read.parse(str, val2);
正则表达式
  • R"()":去转义用途,正则表达式搭配该 C++11 新特性语法,可以去除括号中一些特殊字符的特殊含义;
  • 关于正则表达式这里就不过多介绍,附上一个大佬的博客,感兴趣的可以看看:最全常用正则表达式大全
  • ():在 httplib 中,请求信息const Request& req中存在一个matches数组,当我们使用()将请求资源路径中的某些信息括起来时,该数组中就存放了这些使用括号捕捉到的信息,matches[0]存放的是整个请求,然后数组后续元素是从括号中捕捉到的信息,按顺序排列好的;
MySQL语句
  • MySQL数据库基础
  • MySQL表的增删改查
MySQL代码
  • MySQL代码操作
httplib基础
  • cpp-httplib库的原理
  1. 存在一张路由表,map, function> route,请求与处理一一对应;
  2. 使用Server创建一个服务端,然后向路由表中添加请求与处理的键值对;
  3. 服务端调用listen接口进行监听;
  4. 当服务端收到客户端的请求后,将该请求抛入线程池中,线程池中的线程负责与指定客户端进行通信;
  5. 实例化httplib::Requert对象 req,线程按照 http 协议格式解析请求信息,将解析结果填充到 req 对象中;
  6. 根据请求信息在路由表中查找是否有对应处理函数,如果没有则返回 404(请求资源不存在),如果有,则再实例化一个httplib::Response对象 res,然后将 req 与 res 传入处理函数中,进行处理;
  7. 处理结束后,将返回信息填入 res 中,然后服务端根据 res 对象组织响应,将响应结果返回给客户端;
  1. 注册路由函数:Get()、Post()、Put()、Delete();
  2. 设置静态资源默认路径接口:Server::set_base_dir(char* path);
    设置这个路径后,当前端在请求静态资源的时候,就会自动先到这个路径下查找有没有对应的静态资源文件,如果有则自动读取文件数据进行恢复;

项目总结

  • 这个订单系统中,用户通过浏览器与后台服务器进行交互,实现查看菜品信息以及下单功能,管理员通过浏览器与后台服务器进行交互,实现对菜品和订单的管理功能;
  • 这个项目在实现的时候采用了一个不太严谨的 mvc 框架实现,将项目实现总体分为三个模块:数据管理、业务处理、前端页面;
    1. 数据管理模块:基于 MySQL 数据库实现数据存储管理,并且封装数据库访问类,向外提供与业务分离的数据信息;
    2. 业务处理模块:基于 http 协议,使用 httplib 库搭建服务器与前端进行交互,实现菜品以及订单的数据业务处理功能;
    3. 前端界面模块:基于简单的 html、css、vue.js 和 ajax 实现前端的静态页面展示,以及动态数据获取渲染功能;

项目代码

  • 数据库创建代码:
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,    price 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    
); 
  • 数据库管理代码:
#include
#include
#include
#include
#includenamespace 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* mysql = NULL;//初始化句柄mysql = mysql_init(NULL);if(mysql == NULL){std::cout << "mysql init failed!\n";return NULL;}//连接服务器if(mysql_real_connect(mysql, MYSQL_SERVER, MYSQL_USER, MYSQL_PASSWD, MYSQL_DBNAME, 0, NULL, 0) == NULL){std::cout << mysql_error(mysql) << std::endl;return NULL;}//设置字符集if(mysql_set_character_set(mysql, "utf8") != 0){std::cout << mysql_error(mysql) << std::endl;return NULL;}//选择数据库if(mysql_select_db(mysql, MYSQL_DBNAME) != 0){std::cout << mysql_error(mysql) << std::endl;return NULL;}return mysql;}//数据库释放操作static void MysqlRelease(MYSQL* mysql){if(mysql != NULL){mysql_close(mysql);}}//语句执行接口static bool MysqlQuery(MYSQL* mysql, const std::string& 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://构造函数TableDish(){_mysql = MysqlInit();if(_mysql == NULL)exit(-1);}//析构函数~TableDish(){if(_mysql != NULL){MysqlRelease(_mysql);_mysql = NULL;}}//插入数据bool Insert(const Json::Value& dish){
#define DISH_INSERT "insert tb_dish values(null, '%s', %d, now());"char str_sql[4096] = {0};sprintf(str_sql, DISH_INSERT, dish["name"].asString(), dish["price"].asInt());return MysqlQuery(_mysql, str_sql);}//删除数据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;"char str_sql[4096] = {0};sprintf(str_sql, DISH_UPDATE, dish["name"].asString(), dish["price"].asInt(), dish["id"].asInt());return MysqlQuery(_mysql, str_sql);}//查询所有数据bool SelectAll(Json::Value* dishes){
#define DISH_SELECTALL "select * from tb_dish;"_mutex.lock();if(MysqlQuery(_mysql, DISH_SELECTALL) == 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();if(MysqlQuery(_mysql, str_sql) == 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 << "store result failed!\n";mysql_free_result(res);return false;}MYSQL_ROW row = mysql_fetch_row(res);(*dish)["id"] = std::stoi(row[0]);(*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://构造函数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 = %d;"char str_sql[4096] = {0};Json::FastWriter writer;std::string dishes = writer.write(order["dishes"]);dishes[dishes.size() - 1] = '\0';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();if(MysqlQuery(_mysql, ORDER_SELECTALL) == 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, dishes;Json::Reader reader;order["id"] = std::stoi(row[0]);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();if(MysqlQuery(_mysql, str_sql) == 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);if(num != 1){std::cout << "store result failed!\n";mysql_free_result(res);return false;}Json::Reader reader;Json::Value dish;MYSQL_ROW row = mysql_fetch_row(res);(*order)["id"] = std::stoi(row[0]);reader.parse(row[1], dish);(*order)["name"] = dish;(*order)["price"] = std::stoi(row[2]);(*order)["ctime"] = row[3];mysql_free_result(res);return true;}};
}
  • 服务器执行代码:
#include"db.hpp"
#include"httplib.h"
using namespace httplib; //因为这个命名空间中的名字与标准命名空间中的名字冲突少,所以打开命名空间来简化代码
//资源路径
#define WWWROOT "./wwwroot"//定义两个指向数据库中两张表的指针
order_sys::TableDish* dishptr = NULL;
order_sys::TableOrder* orderptr = NULL;//菜品插入
void DishInsert(const Request& req, Response& rsp){//业务处理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 << "dish insert parse failed!\n";return;}//解析成功则插入数据ret = dishptr->Insert(dish);//如果插入信息失败,则需要返回错误信息if(ret == false){ rsp.status = 500;Json::Value reason;Json::FastWriter writer;reason["result"] = false;reason["reason"] = "mysql insert failed!";rsp.body = writer.write(reason);rsp.set_header("Content-Type", "application/json");std::cout << "mysql dish insert failed!\n";return;}//设置响应信息//响应状态码httplib默认设置为200,所以下面这行可写可不写rsp.status = 200;return;
}
//菜品删除
void DishDelete(const Request& req, Response& rsp){//matches是一个数组,用来存放请求中括号捕捉到的信息,matches[0]存放的就是整个请求,然后是排好的捕捉的信息int dish_id = std::stoi(req.matches[1]);bool ret = dishptr->Delete(dish_id);if(ret == false){std::cout << "mysql dish delete failed!\n";rsp.status = 500;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 << "dish update parse failed!\n";return;}dish["id"] = dish_id;ret = dishptr->Update(dish);if(ret == false){rsp.status = 500;std::cout << "mysql dish update failed!\n";return;}return;
}
//菜品查询(所有)
void DishGetAll(const Request& req, Response& rsp){Json::Value dishes;bool ret = dishptr->SelectAll(&dishes);if(ret == false){rsp.status = 500;std::cout << "mysql dish selectall failed!\n";return;}Json::FastWriter writer;rsp.body = writer.write(dishes);return;
}
//菜品查询(单个)
void DishGetOne(const Request& req, Response& rsp){int dish_id = std::stoi(req.matches[1]);Json::Value dish;bool ret = dishptr->SelectOne(dish_id, &dish);if(ret == false){rsp.status = 500;std::cout << "mysql dish selectone failed!\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;Json::Value reason;Json::FastWriter writer;reason["result"] = false;reason["reason"] = "order info parse failed!";rsp.body = writer.write(reason);rsp.set_header("Content-Type", "application/json");std::cout << "order insert parse failed!\n";return;}//解析成功则插入数据ret = orderptr->Insert(order);//如果插入信息失败,则需要返回错误信息if(ret == false){ rsp.status = 500;Json::Value reason;Json::FastWriter writer;reason["result"] = false;reason["reason"] = "mysql insert failed!";rsp.body = writer.write(reason);rsp.set_header("Content-Type", "application/json");std::cout << "mysql order insert failed!\n";return;}//设置响应信息//响应状态码httplib默认设置为200,所以下面这行可写可不写rsp.status = 200;return;
}
//订单删除
void OrderDelete(const Request& req, Response& rsp){//matches是一个数组,用来存放请求中括号捕捉到的信息,matches[0]存放的就是整个请求,然后是排好的捕捉的信息int order_id = std::stoi(req.matches[1]);bool ret = orderptr->Delete(order_id);if(ret == false){std::cout << "mysql order delete failed!\n";rsp.status = 500;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 << "order update parse failed!\n";return;}order["id"] = order_id;ret = orderptr->Update(order);if(ret == false){rsp.status = 500;std::cout << "mysql order update failed!\n";return;}return;
}
//订单查询(所有)
void OrderGetAll(const Request& req, Response& rsp){Json::Value orders;bool ret = orderptr->SelectAll(&orders);if(ret == false){rsp.status = 500;std::cout << "mysql order selectall failed!\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 = orderptr->SelectOne(order_id, &order);if(ret == false){rsp.status = 500;std::cout << "mysql order selectone failed!\n";return;}Json::FastWriter writer;rsp.body = writer.write(order);return;
}int main(){dishptr = new order_sys::TableDish();orderptr = new order_sys::TableOrder();Server server;server.set_base_dir(WWWROOT);//菜品请求server.Post("/dish", DishInsert);//正则表达式用来校验一些特殊字符server.Delete(R"(/dish/(\d+))", DishDelete); server.Put(R"(/dish/(\d+))", DishUpdate);server.Get("/dish", DishGetAll);server.Get(R"(/dish/(\d+))", DishGetOne);//订单请求server.Post("/order", OrderInsert);//正则表达式用来校验一些特殊字符server.Delete(R"(/order/(\d+))", OrderDelete); server.Put(R"(/order/(\d+))", OrderUpdate);server.Get("/order", OrderGetAll);server.Get(R"(/order/(\d+))", OrderGetOne);server.listen("0.0.0.0", 9000);return 0;
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部