点餐系统-C++实现

整体架构

  1. 服务器
  2. 顾客客户端
  3. 商家客户端

预计实现功能:

  • 顾客扫描餐桌上的二维码进入顾客客户端, 进行点菜
  • 订单提交到服务器上由商家客户端获取到
  • 商家通过商家客户端可以进行订单及菜品的管理

数据库设计

创建数据库
create database if not exists order_system;
use order_system;
创建菜单表

每行记录一个菜品信息, price 单位为分,由商家维护, 由用户浏览

create table if not exists dish_table(dish_id int unsigned not null primary key 
auto_increment,
                               name varchar(50),
                               price int);

插入几个测试数据,可自定义

insert into dish_table values(null, '宫保鸡丁', 1800);
insert into dish_table values(null, '红烧肉', 1800);
insert into dish_table values(null, '土豆丝'1800);
创建订单表

每行是一个订单,信息由用户提交,商家进行浏览

create table if not exists order_table(order_id int unsigned not null primary key 
auto_increment,
                               table_id varchar(50),   
                                time varchar(50),       
                               dish_ids varchar(1024),
                               state int);                 // 表示订单状态. 0 表示订单进行中, 
1 表示订单完结
table_id 桌号
time 下单时间, 形如 2019-05-15 12:00
dish_ids 用户点的菜品 id 列表, 为 json 格式字符串, 形如 [1, 2, 3]

插入测试数据

insert into order_table values(null, '1', '2019-05-10 12:00', '[1,2,3]', 1);
insert into order_table values(null, '2', '2019-05-15 13:00', '[1,3]', 0);
使用 MySQL API操作数据库

注意:
MySQL 提供的 API 除了 mysql_real_connect, 其他的都是线程安全的.
创建一个 mysql_test 目录
Makefile 编译选项
// -L /usr/lib64/mysql -lmysqlclient
插入 mysql_insert.cc

#include <cstdio>
#include <cstdlib>
#include <mysql/mysql.h>
int main() {
  // 1. 初始化句柄
  MYSQL* connect_fd = mysql_init(NULL);
  // 2. 建立链接
  // mysql_init 返回的指针
  // 主机地址
  // 用户名
  // 密码
  // 数据库名
  // 端口号
  // unix_socket
  // client_flag
  if (mysql_real_connect(connect_fd, "127.0.0.1", "root", "",
                         "order_system", 3306, NULL, 0) == NULL) {
    printf("连接失败! %s\n", mysql_error(connect_fd));
    return 1;
 }
  // 3. 设置编码格式
  mysql_set_character_set(connect_fd, "utf8");
  // 4. 拼装 SQL 语句
        char sql[1024 * 4] = {0};
  char name[] = "京酱肉丝";
  int price = 2000;
        sprintf(sql, "insert into menu values(null, '%s', %d)", name, price);
  // 5. 执行 SQL 语句
  int ret = mysql_query(connect_fd, sql);
  if (ret < 0) {
    printf("执行 sql 失败! %s\n", mysql_error(connect_fd));
    return 1;
 }
  // 6. 关闭句柄
  mysql_close(connect_fd);
  printf("执行成功!\n");
  return 0; }

查询 mysql_select.cc

#include <cstdio>
#include <cstdlib>
#include <mysql/mysql.h>

int main() {
  // 1. 初始化句柄
  MYSQL* connect_fd = mysql_init(NULL);
  // 2. 建立链接
  // mysql_init 返回的指针
  // 主机地址
  // 用户名
  // 密码
  // 数据库名
  // 端口号
  // unix_socket
  // client_flag
  if (mysql_real_connect(connect_fd, "127.0.0.1", "root", "",
                         "order_system", 3306, NULL, 0) == NULL) {
    printf("连接失败! %s\n", mysql_error(connect_fd));
    return 1;
 }
  // 3. 设置编码格式
  mysql_set_character_set(connect_fd, "utf8");
  // 4. 拼装 SQL 语句
        char sql[1024 * 4] = {0};
  int price = 2000;
        sprintf(sql, "select * from menu");
  // 5. 执行 SQL 语句
  int ret = mysql_query(connect_fd, sql);
  if (ret < 0) {
    printf("执行 sql 失败! %s\n", mysql_error(connect_fd));
    return 1;
 }
  // 6. 遍历查询结果
  MYSQL_RES* result = mysql_store_result(connect_fd);
  if (result == NULL) {
    printf("获取结果失败! %s\n", mysql_error(connect_fd));
    return 1;
 }
  // a) 获取行数和列数
  int rows = mysql_num_rows(result);
  int fields = mysql_num_fields(result);
  printf("rows: %d, fields: %d\n", rows, fields);
  // b) 打印结果
  for (int i; i < rows; ++i) {
    MYSQL_ROW row = mysql_fetch_row(result);
    for (int j = 0; j < fields; ++j) {
      printf("%s\t", row[j]);
   }
    printf("\n");
 }
  // 7. 关闭句柄
  mysql_close(connect_fd);
  printf("执行成功!\n");
  return 0; }

服务器端设计

菜品管理 API 设计

新增菜品(商家)

//请求: 
POST /dish 
{ 
 	"name": "宫保鸡丁", 
 	"price": 1800 
} 
//响应: 
HTTP/1.1 200 OK 
{ 
 	"ok": true, 
 	"dish_id": 1, 
}

查看所有菜品(商家/用户)

//请求: 
GET /dish 
//响应: 
HTTP/1.1 200 OK 
[ 
 { 
 	dish_id: 1, 
	name: "宫保鸡丁", 
 	price: 1800 
 } 
]

删除菜品(商家)

//请求: 
DELETE /dish/:dish_id 
//响应: 
HTTP/1.1 200 OK 
{ 
 	"ok": true 
}

修改菜品(商家)

//请求: 
PUT /dish/:dish_id 
{ 
 	"name": "京酱肉丝", 
 	"price": 1900 
} 
//响应: 
HTTP/1.1 200 OK 
{ 
 	"ok": true 
}
订单管理 API 设计

提交订单(用户)

//请求: 
POST /order 
{ 
 	"table_id": "1", 
 	"time": "2019-05-15 12:00", 
 	"dish_ids": [1, 2] 
} 
//响应: 
HTTP/1.1 200 OK 
{ 
 	"ok": true 
}

修改订单状态(商家)

//请求: 
PUT /order/:order_id 
{ 
 	"state": 0, 
} 
//响应: 
HTTP/1.1 200 OK 
{ 
 	"ok": true 
}

获取订单(商家)

  • 在实际的生活场景中,我们应该补充上相应的过滤功能(例如按时间, 桌号, 显示页数等规则过滤)。
  • 否则由于和 MySQL 的交互量过大就会导致服务器相应速度很慢。
//请求: 
GET /order 
//响应: 
HTTP/1.1 200 OK 
[ 
 { 
 	"order_id": 1, 
 	"table_id": "1", 
 	"time": "2019-05-15 12:00", 
 	"dishes": [ 
 	  { 
 	  	"dish_id": 1, 
 		"name": "宫保鸡丁", 
 		"price": 1800 
 		}, 
 		{ 
 			"dish_id": 2, 
 			"name": "京酱肉丝", 
 			"price": 1900 
 		} 
 	], 
 	"state": 0, 
 	"consume": 3700, // 表示该订单的价格
 	}, 
 	{ 
 		"order_id": 2, 
		"table_id": "2", 
 	"time": "2019-05-16 12:00", 
 	"dishs": [ 
	 {
 		"dish_id": 1, 
 		"name": "宫保鸡丁", 
 		"price": 1800 
 	  }, 
 	], 
 	"state": 1, 
 	"consume": 1800, // 表示该订单的价格
	} 
]

服务端实现(一)

封装数据库操作
基本代码框架

db.hpp

namespace order_system { 
static MYSQL* MySQLInit() { 
 MYSQL* connect_fd = mysql_init(NULL); 
 if (mysql_real_connect(connect_fd, "127.0.0.1", "root", "", 
 "order_system", 3306, NULL, 0) == NULL) { 
 printf("连接失败! %s\n", mysql_error(connect_fd)); 
 return NULL; 
 } 
 mysql_set_character_set(connect_fd, "utf8"); 
 return connect_fd; 
} 
static void MySQLRelease(MYSQL* mysql) { 
 mysql_close(mysql); 
} 
class DishTable { 
public: 
 DishTable(MYSQL* mysql) : mysql_(mysql) { } 
 bool Insert(const Json::Value& dish) { 
 } 
 bool SelectAll(Json::Value* dishes) { 
 } 
 bool SelectOne(int32_t dish_id, Json::Value* dish) { 
 } 
 bool Update(const Json::Value& dish) {
 } 
 bool Delete(int dish_id) { 
 } 
private: 
 MYSQL* mysql_; 
}; 
class OrderTable { 
public: 
 OrderTable(MYSQL* mysql) : mysql_(mysql) { } 
 bool SelectAll(Json::Value* orders) { 
 } 
 bool Insert(const Json::Value& order) { 
 } 
 bool ChangeState(const Json::Value& order) { 
 } 
private: 
 MYSQL* mysql_; 
}; 
} // end order_system
使用 JSON 作为数据交互格式

json 出自 JavaScript, 是一种非常方便的键值对数据组织格式
C++ 中可以使用 jsoncpp这个库来解析和构造json数据
yum install jsoncpp-devel

实现 DishTable 类封装对菜品的数据库操作
class DishTable { 
public: 
 DishTable(MYSQL* mysql) : mysql_(mysql) { } 
 bool Insert(const Json::Value& dish) { 
 char sql[1024 * 4] = {0}; 
 sprintf(sql, "insert into dish_table values(null, '%s', %d)", dish["name"].asCString(), 
dish["price"].asInt()); 
 int ret = mysql_query(mysql_, sql); 
 if (ret != 0) { 
 printf("执行 sql 失败! sql=%s, %s\n", sql, mysql_error(mysql_)); 
 return false;
 } 
 return true; 
 } 
 bool SelectAll(Json::Value* dishes) { 
 char sql[1024 * 4] = {0}; 
 sprintf(sql, "select * from dish_table"); 
 int ret = mysql_query(mysql_, sql); 
 if (ret != 0) { 
 printf("执行 sql 失败! %s\n", mysql_error(mysql_)); 
 return false; 
 } 
 MYSQL_RES* result = mysql_store_result(mysql_); 
 if (result == NULL) { 
 printf("获取结果失败! %s\n", mysql_error(mysql_)); 
 return false; 
 } 
 int rows = mysql_num_rows(result); 
 for (int i = 0; i < rows; ++i) { 
 MYSQL_ROW row = mysql_fetch_row(result); 
 Json::Value dish; 
 dish["dish_id"] = atoi(row[0]); 
 dish["name"] = row[1]; 
 dish["price"] = atoi(row[2]); 
 // 遍历结果依次加入到 dishes 中
 dishes->append(dish); 
 } 
 return true; 
 } 
 bool SelectOne(int32_t dish_id, Json::Value* dish) { 
 char sql[1024 * 4] = {0}; 
 sprintf(sql, "select * from dish_table where dish_id = %d", dish_id); 
 int ret = mysql_query(mysql_, sql); 
 if (ret != 0) { 
 printf("执行 sql 失败! %s\n", mysql_error(mysql_)); 
 return false; 
 } 
 MYSQL_RES* result = mysql_store_result(mysql_); 
 if (result == NULL) { 
 printf("获取结果失败! %s\n", mysql_error(mysql_)); 
 return false; 
 } 
 int rows = mysql_num_rows(result); 
 if (rows != 1) { 
 printf("查找结果不为 1 条. rows = %d!\n", rows); 
 return false; 
 } 
 for (int i = 0; i < rows; ++i) { 
 MYSQL_ROW row = mysql_fetch_row(result); 
 (*dish)["dish_id"] = atoi(row[0]); 
 (*dish)["name"] = row[1]; 
 (*dish)["price"] = atoi(row[2]);
 break; // 只获取一条数据. 按理说数据库中相同 id 的就一个
 } 
 return true; 
 } 
 bool Update(const Json::Value& dish) { 
 char sql[1024 * 4] = {0}; 
 sprintf(sql, "update dish_table SET name='%s', price=%d where dish_id=%d", 
 dish["name"].asCString(), 
 dish["price"].asInt(), dish["dish_id"].asInt()); 
 // DEBUG 用于调试
 // printf("[SQL] %s\n", sql); 
 int ret = mysql_query(mysql_, sql); 
 if (ret != 0) { 
 printf("执行 sql 失败! sql=%s, %s\n", sql, mysql_error(mysql_)); 
 return false; 
 } 
 return true; 
 } 
 bool Delete(int dish_id) { 
 char sql[1024 * 4] = {0}; 
 sprintf(sql, "delete from dish_table where dish_id=%d", dish_id); 
 int ret = mysql_query(mysql_, sql); 
 if (ret != 0) { 
 printf("执行 sql 失败! sql=%s, %s\n", sql, mysql_error(mysql_)); 
 return false; 
 } 
 return true; 
 } 
private: 
 MYSQL* mysql_; 
};
创建 OrderTable 类封装对订单的数据库操作
class OrderTable { 
public: 
 OrderTable(MYSQL* mysql) : mysql_(mysql) { } 
 bool SelectAll(Json::Value* orders) { 
 char sql[1024 * 4] = {0}; 
 sprintf(sql, "select * from order_table"); 
 int ret = mysql_query(mysql_, sql); 
 if (ret != 0) { 
 printf("执行 sql 失败! %s\n", mysql_error(mysql_)); 
 return false; 
 } 
 MYSQL_RES* result = mysql_store_result(mysql_); 
 if (result == NULL) { 
 printf("获取结果失败! %s\n", mysql_error(mysql_));
 return false; 
 } 
 int rows = mysql_num_rows(result); 
 for (int i = 0; i < rows; ++i) { 
 MYSQL_ROW row = mysql_fetch_row(result); 
 Json::Value order; 
 order["order_id"] = atoi(row[0]); // 注意, order_id 是数字, table_id 是字符串
 order["table_id"] = row[1]; 
 order["time"] = row[2]; 
 // [重要] 需要在上层把 dish_ids 替换成 dishes(不光是菜品 ID 还有菜品详细信息) 
 order["dish_ids_str"] = row[3]; 
 order["state"] = row[4]; 
 // 遍历结果依次加入到 dishes 中
 orders->append(order); 
 } 
 return true; 
 } 
 bool Insert(const Json::Value& order) { 
 char sql[1024 * 4] = {0}; 
 // 此处 dish_ids 需要先转成字符串(本来是一个对象, 
 // 形如 [1, 2, 3]. 如果不转, 是无法 asCString) 
 sprintf(sql, "insert into order_table values(null, '%s', '%s', '%s', %d)", 
 order["table_id"].asCString(), order["time"].asCString(), 
 order["dish_ids_str"].asCString(), order["state"].asInt()); 
 int ret = mysql_query(mysql_, sql); 
 if (ret != 0) { 
 printf("执行 sql 失败! sql=%s, %s\n", sql, mysql_error(mysql_)); 
 return false; 
 } 
 return true; 
 } 
 bool ChangeState(const Json::Value& order) { 
 char sql[1024 * 4] = {0}; 
 sprintf(sql, "update order_table set state = %d where order_id = %d", 
 order["state"].asInt(), order["order_id"].asInt()); 
 int ret = mysql_query(mysql_, sql); 
 if (ret != 0) { 
 printf("执行 sql 失败! sql=%s, %s\n", sql, mysql_error(mysql_)); 
 return false; 
 } 
 return true; 
 } 
private: 
 MYSQL* mysql_; 
};
测试数据库操作

db_test.cc 测试上述数据库操作是否封装正确

void TestDishTable() { 
 bool ret = false; 
 // 更友好的格式化显示 Json 
 Json::StyledWriter writer; 
 MYSQL* mysql = MySQLInit(); 
 Json::Value dish; 
 dish["name"] = "红烧肉"; 
 dish["price"] = 2300; 
 std::cout << "==============测试插入=====================" << std::endl; 
 DishTable dish_table(mysql); 
 ret = dish_table.Insert(dish); 
 std::cout << "Insert: " << ret << std::endl; 
 std::cout << "==============测试查找=====================" << std::endl; 
 Json::Value dishes; 
 ret = dish_table.SelectAll(&dishes); 
 std::cout << "SelectAll: " << ret << std::endl 
 << writer.write(dishes) << std::endl; 
 std::cout << "==============测试更新=====================" << std::endl; 
 dish["dish_id"] = 5; 
 dish["name"] = "毛家红烧肉"; 
 dish["price"] = 2700; 
 Json::Value dish_out; 
 ret = dish_table.Update(dish); 
 std::cout << "Update: " << ret << std::endl; 
 ret = dish_table.SelectOne(5, &dish_out); 
 std::cout << "SelectOne: " << ret << std::endl 
 << writer.write(dish_out) << std::endl; 
 std::cout << "==============测试删除=====================" << std::endl; 
 int dish_id = 6; 
 ret = dish_table.Delete(dish_id); 
 std::cout << "Delete: " << ret << std::endl; 
 MySQLRelease(mysql); 
} 
void TestOrderTable() { 
 bool ret = false; 
 Json::StyledWriter writer; 
 MYSQL* mysql = MySQLInit(); 
 OrderTable order_table(mysql); 
 std::cout << "==============测试插入=====================" << std::endl; 
 Json::Value order; 
 order["table_id"] = "忠义堂"; 
 order["time"] = "2019-05-17 12:00"; 
 order["dish_ids"] = "1,2,3"; 
 order["state"] = 0; 
 ret = order_table.Insert(order);
 std::cout << "Insert: " << ret << std::endl; 
 std::cout << "==============测试查看=====================" << std::endl; 
 Json::Value orders; 
 ret = order_table.SelectAll(&orders); 
 std::cout << "SelectAll: " << ret << std::endl 
 << writer.write(orders) << std::endl; 
 std::cout << "==============测试修改状态=====================" << std::endl; 
 Json::Value state; 
 state["order_id"] = 3; 
 state["state"] = 1; 
 ret = order_table.ChangeState(state); 
 std::cout << "ChangeState ret:" << ret << std::endl; 
 MySQLRelease(mysql); 
} 
int main() { 
 // TestDishTable(); 
 TestOrderTable(); 
 return 0; 
}

Makefile

FLAGS=-std=c++11 -L/usr/lib64/mysql -lmysqlclient -ljsoncpp -lpthread -g 
.PHONY:all 
all:db_test 
db_test:db_test.cc db.hpp 
 g++ db_test.cc -o db_test $(FLAGS) 
.PHONY:clean 
clean: 
 rm db_test

服务器端实现(二)

使用 httplib搭建服务器框架

使用 cpp-httplib实现一个 “hello world”

#include "httplib.h" 
int main() { 
 	using namespace httplib; 
 	Server server; 
 	server.Get("/", [](const Request& req,Response& resp) { 
 		(void)req; 
 		resp.set_content("<html>hello</html>", "	text/html"); 
 	}); 
 	server.set_base_dir("./wwwroot"); 
 	server.listen("0.0.0.0", 9092); 
 	return 0; 
}

编译选项
g++ main.cc -lpthread -std=c++11

整体 API 框架
MYSQL* mysql = NULL; 
int main() { 
 using namespace httplib; 
 using namespace order_system; 
 Server server; 
 // 1. 数据库客户端初始化和释放
 mysql = MySQLInit(); 
 signal(SIGINT, [](int) { MySQLRelease(mysql); exit(0);}); 
 DishTable dish_table(mysql); 
 OrderTable order_table(mysql); 
 // 2. 设置路由
 // 新增菜品
 server.Post("/dish", [&dish_table](const Request& req, Response& resp) { 
 
 }); 
 // 查看所有菜品
 server.Get("/dish", [&dish_table](const Request& req, Response& resp) { 
 
 }); 
 // 删除菜品
 // raw string(c++ 11), 转义字符不生效. 用来表示正则表达式正好合适
 // 关于正则表达式, 只介绍最基础概念即可. \d+ 表示匹配一个数字
 // http://help.locoy.com/Document/Learn_Regex_For_30_Minutes.htm 
 server.Delete(R"(/dish/(\d+))", [&dish_table](const Request& req, Response& resp) { 
 
 }); 
 // 修改菜品
 server.Put(R"(/dish/(\d+))", [&dish_table](const Request& req, Response& resp) { 
 
 });
 // 新增订单
 server.Post("/order", [&order_table](const Request& req, Response& resp) { 
 
 }); 
 // 修改订单状态
 server.Put(R"(/order/(\d+))", [&order_table](const Request& req, Response& resp) { 
 
 }); 
 // 获取订单
 server.Get("/order", [&order_table, &dish_table](const Request& req, Response& resp) { 
 
 }); 
 // 获取用户客户端, 匹配桌子 id 
 server.Get(R"(/client/table/(\S+))", [](const Request& req, Response& resp) { 
 }); 
 // 设置静态文件目录
 server.set_base_dir("./wwwroot"); 
 server.listen("0.0.0.0", 9092); 
 return 0; 
}

实现菜品管理 API

实现新增菜品
 server.Post("/dish", [&dish_table](const Request& req, Response& resp) { 
 LOG(INFO) << "新增菜品: " << req.body << std::endl; 
 Json::Reader reader; 
 Json::FastWriter writer; 
 Json::Value req_json; 
 Json::Value resp_json; 
 // 1. 请求解析成 Json 格式
 bool ret = reader.parse(req.body, req_json); 
 if (!ret) { 
 // 请求解析出错, 返回一个400响应
 resp_json["ok"] = false; 
 resp_json["reason"] = "parse Request failed!\n"; 
 resp.status = 400; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 } 
 // 2. 进行参数校验
 if (req_json["name"].empty() || req_json["price"].empty()) { 
 resp_json["ok"] = false; 
 resp_json["reason"] = "Request has no name or price field!\n"; 
 resp.status = 400; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 }
 // 3. 调用数据库接口进行操作数据
 ret = dish_table.Insert(req_json); 
 if (!ret) { 
 resp_json["ok"] = false; 
 resp_json["reason"] = "Insert failed!\n"; 
 resp.status = 500; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 } 
 // 4. 封装正确的返回结果
 resp_json["ok"] = true; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 })
实现获取所有菜品
 server.Get("/dish", [&dish_table](const Request& req, Response& resp) { 
 LOG(INFO) << "查看所有菜品: " << req.body << std::endl; 
 Json::Reader reader; 
 Json::FastWriter writer; 
 Json::Value resp_json; 
 // 对于查看菜品来说 API 没有请求参数, 不需要解析参数和校验了, 直接构造结果即可
 // 1. 调用数据库接口查询数据
 Json::Value dishes; 
 bool ret = dish_table.SelectAll(&dishes); 
 if (!ret) { 
 resp_json["ok"] = false; 
 resp_json["reason"] = "SelectAll failed!\n"; 
 resp.status = 500; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 } 
 // 2. 构造响应结果
 resp.set_content(writer.write(dishes), "application/json"); 
 return; 
 });
实现删除菜品
// 删除菜品
 // raw string(c++ 11), 转义字符不生效. 用来表示正则表达式正好合适
 // 关于正则表达式, 只介绍最基础概念即可. \d+ 表示匹配一个数字
 // http://help.locoy.com/Document/Learn_Regex_For_30_Minutes.htm 
 server.Delete(R"(/dish/(\d+))", [&dish_table](const Request& req, Response& resp) { 
 Json::Value resp_json; 
 Json::FastWriter writer; 
 // 1. 解析获取 dish_id 
 // 使用 matches[1] 就能获取到 dish_id 
 // LOG(INFO) << req.matches[0] << "," << req.matches[1] << "\n"; 
 int dish_id = std::stoi(req.matches[1]); 
 LOG(INFO) << "删除指定菜品: " << dish_id << std::endl;
 // 2. 调用数据库接口删除菜品
 bool ret = dish_table.Delete(dish_id); 
 if (!ret) { 
 resp_json["ok"] = false; 
 resp_json["reason"] = "SelectAll failed!\n"; 
 resp.status = 500; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 } 
 // 3. 包装正确的响应
 resp_json["ok"] = true; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 });
实现修改菜品
 // 修改菜品
 server.Put(R"(/dish/(\d+))", [&dish_table](const Request& req, Response& resp) { 
 Json::Reader reader; 
 Json::FastWriter writer; 
 Json::Value req_json; 
 Json::Value resp_json; 
 // 1. 获取到菜品 id 
 int dish_id = std::stoi(req.matches[1]); 
 LOG(INFO) << "修改菜品 " << dish_id << "|" << req.body << std::endl; 
 // 2. 解析菜品信息
 bool ret = reader.parse(req.body, req_json); 
 if (!ret) { 
 // 请求解析出错, 返回一个400响应
 resp_json["ok"] = false; 
 resp_json["reason"] = "parse Request failed!\n"; 
 resp.status = 400; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 } 
 // [注意!!] 一定要记得补充上 dish_id 
 req_json["dish_id"] = dish_id; 
 // 3. 校验菜品信息
 if (req_json["name"].empty() || req_json["price"].empty()) { 
 // 请求解析出错, 返回一个400响应
 resp_json["ok"] = false; 
 resp_json["reason"] = "Request has no name or price!\n"; 
 resp.status = 400; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 } 
 // 4. 调用数据库接口进行修改
 ret = dish_table.Update(req_json); 
 if (!ret) { 
 resp_json["ok"] = false; 
 resp_json["reason"] = "Update failed!\n"; 
 resp.status = 500; 
 resp.set_content(writer.write(resp_json), "application/json");
 return; 
 } 
 // 5. 封装正确的数据
 resp_json["ok"] = true; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 })

实现订单管理 API

实现新增订单
 server.Post("/order", [&order_table](const Request& req, Response& resp) { 
 LOG(INFO) << "新增订单: " << req.body << std::endl; 
 Json::Reader reader; 
 Json::FastWriter writer; 
 Json::Value req_json; 
 Json::Value resp_json; 
 // 1. 请求解析成 Json 格式
 bool ret = reader.parse(req.body, req_json); 
 if (!ret) { 
 // 请求解析出错, 返回一个400响应
 resp_json["ok"] = false; 
 resp_json["reason"] = "parse Request failed!\n"; 
 resp.status = 400; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 } 
 // 2. 校验订单格式
 if (req_json["table_id"].empty() || req_json["time"].empty() 
 || req_json["dish_ids"].empty()) { 
 // 请求解析出错, 返回一个400响应
 resp_json["ok"] = false; 
 resp_json["reason"] = "Request has no table_id or time or dish_ids!\n"; 
 resp.status = 400; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 } 
 // 3. 转换接口, 把 dish_ids 先转成字符串, 存到 dish_ids_str 中. 
 // 后续的数据库插入拿着这个 dish_ids_str 来进行插入
 const Json::Value& dish_ids = req_json["dish_ids"]; 
 req_json["dish_ids_str"] = writer.write(dish_ids); 
 // 4. 调用数据库接口, 插入订单数据
 ret = order_table.Insert(req_json); 
 if (!ret) { 
 resp_json["ok"] = false; 
 resp_json["reason"] = "OrderTable Insert failed!\n"; 
 resp.status = 500; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 } 
 // 5. 返回正确的结果
 resp_json["ok"] = true; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 })
实现修改订单状态
server.Put(R"(/order/(\d+))", [&order_table](const Request& req, Response& resp) { 
 Json::Reader reader; 
 Json::FastWriter writer; 
 Json::Value req_json; 
 Json::Value resp_json; 
 // 1. 获取到订单 id 
 int order_id = std::stoi(req.matches[1]); 
 LOG(INFO) << "修改菜品 " << order_id << "|" << req.body << std::endl; 
 // 2. 解析菜品信息
 bool ret = reader.parse(req.body, req_json); 
 if (!ret) { 
 // 请求解析出错, 返回一个400响应
 resp_json["ok"] = false; 
 resp_json["reason"] = "parse Request failed!\n"; 
 resp.status = 400; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 } 
 // [注意!!] 一定要记得补充上 order_id 
 req_json["order_id"] = order_id; 
 // 3. 校验请求格式
 if (req_json["order_id"].empty() || req_json["state"].empty()) { 
 resp_json["ok"] = false; 
 resp_json["reason"] = "Request has no order_id or state!\n"; 
 resp.status = 400; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 } 
 // 4. 调用数据库接口进行修改
 ret = order_table.ChangeState(req_json); 
 if (!ret) { 
 resp_json["ok"] = false; 
 resp_json["reason"] = "ChangeState failed\n"; 
 resp.status = 500; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 } 
 resp_json["ok"] = true; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 })
实现获取订单
server.Get("/order", [&order_table, &dish_table](const Request& req, Response& resp) { 
 // [重要!!] orders 中包含很多 order. 每个 order 又包含若干个 dish
 LOG(INFO) << "查看所有订单: " << req.body << std::endl; 
 Json::Reader reader; 
 Json::FastWriter writer; 
 Json::Value resp_json; 
 // 对于查看菜品来说 API 没有请求参数, 不需要解析参数和校验了, 直接构造结果即可
 // 1. 调用数据库接口查询数据
 Json::Value orders; 
 bool ret = order_table.SelectAll(&orders); 
 if (!ret) { 
 resp_json["ok"] = false; 
 resp_json["reason"] = "SelectAll failed!\n"; 
 resp.status = 500; 
 resp.set_content(writer.write(resp_json), "application/json"); 
 return; 
 } 
 for (uint32_t order_index = 0; order_index < orders.size(); ++order_index) { 
 LOG(INFO) << "处理第 " << order_index << " 个订单 ing" << std::endl; 
 // 循环一次, 处理一个订单
 Json::Value& order = orders[order_index]; 
 // 2. 转换格式
 Json::Value dish_ids; 
 ret = reader.parse(order["dish_ids_str"].asString(), dish_ids); 
 if (!ret) { 
 // 用宽松一点的方式处理. 此处不要因为一个订单的错误就影响大部分
 LOG(ERROR) << "order_id: " << order["order_id"].asInt() 
 << " has error dish_ids_str!" << std::endl; 
 continue; 
 } 
 // 3. 将 order 中的 dish_ids 构造成 dishes (再次查询数据库, 获取到每个菜品的详细信息) 
 GetDishes(dish_ids, &order["dishes"], dish_table); 
 // 4. 构造 consume 字段, 求这个订单的总价
 order["consume"] = GetConsume(order["dishes"]); 
 } 
 // 5. 构造响应结果
 resp.set_content(writer.write(orders), "application/json"); 
 return; 
 })

实现辅助函数

// 把一个订单中的 dish_ids 转换成详细信息 dishes 
// order 作为输入输出参数
bool GetDishes(const Json::Value& dish_ids, Json::Value* dishes, 
 order_system::DishTable& dish_table) { 
 for (uint32_t i = 0; i < dish_ids.size(); ++i) { 
 LOG(INFO) << "处理第 " << i << " 个菜品" << std::endl; 
 int dish_id = dish_ids[i].asInt(); 
 Json::Value dish_info; 
 bool ret = dish_table.SelectOne(dish_id, &dish_info); 
 if (!ret) { 
 LOG(ERROR) << "dish_id = " << dish_id << " not found!\n"; 
 continue;
 } 
 dishes->append(dish_info); 
 } 
 return true; 
} 
int GetConsume(const Json::Value& dishes) { 
 int consume = 0; 
 for (uint32_t i = 0; i < dishes.size(); ++i) { 
 consume += dishes[i]["price"].asInt(); 
 } 
 return consume; 
}
使用 Postman 测试

在 Postman 中构造请求, 并验证即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值