项目——订单系统

项目描述

功能
  • 用户可以通过浏览器访问服务器获取菜品信息并进行点餐;
  • 管理员可以通过浏览器访问服务器实现订单以及菜品的管理;
技术点
  • 多线程、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代码
httplib基础
  1. 存在一张路由表,map<pair<string, string>, 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<iostream>
#include<string>
#include<mutex>
#include<mysql/mysql.h>
#include<jsoncpp/json/json.h>

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* 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值