整体架构

  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_flagif (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_flagif (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 又包含若干个 dishLOG(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 中构造请求, 并验证即可

点餐系统-C++实现相关推荐

  1. Java项目:在线点餐系统(java+Springboot+Maven+mybatis+Vue+mysql+Redis)

    源码获取:博客首页 "资源" 里下载! 项目描述: 这是一个基于SpringBoot+Vue框架开发的在线点餐系统.首先,这是一个前后端分离的项目.具有一个在线点餐系统该有的所有功 ...

  2. java基础===点餐系统

    public class OrderMsg {public static void main(String[] args) throws Exception { /** * 订餐人姓名.选择菜品.送餐 ...

  3. Java外卖点餐系统

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 来源:blog.csdn.net/weixin_44219 ...

  4. WinFormreportViewer(rdlc)报表[列表]的使用(一)(附源码示例) 之配餐系统的开发

    紧接着"WinForm"reportViewer报表[矩阵]的使用(一)(附源码示例)" 之配餐系统的开发"这篇文章,此文与大家分享的是在 配餐系统的开发 中使 ...

  5. [课程设计]Scrum 2.5 多鱼点餐系统开发进度(下单一览页面-菜式添加框架设计)

    Scrum 2.5 多鱼点餐系统开发进度  (下单一览页面-菜式添加框架设计) 1.团队名称:重案组 2.团队目标:长期经营,积累客户充分准备,伺机而行 3.团队口号:矢志不渝,追求完美 4.团队选题 ...

  6. [课程设计]Scrum 多鱼点餐系统(团队交流日)

    [课程设计]Scrum  多鱼点餐系统(团队交流日) 1.团队名称:重案组 2.团队目标:长期经营,积累客户充分准备,伺机而行 3.团队口号:矢志不渝,追求完美 4.团队选题:餐厅到店点餐系统WEB ...

  7. android简单点餐系统_微信点餐和扫码点餐系统能为商家带来什么?

    近几年来,我们去餐厅吃饭的时候可能会发现,很多餐厅都安装扫码点餐系统.和微信点餐系统,相比传统餐饮软件及传统模式,这带来的不只是节省人工.一套系统稳定.功能齐全的微信点餐系统,对餐饮管理而言,能够降低 ...

  8. IOS开发高级之点餐系统的实现-01

    IOS开发高级之点餐系统的实现-01 // // ViewController.m // 01-点餐系统 // // Created by 鲁军 on 2021/2/11. //#import &qu ...

  9. WinForm立体饼状图实现(附源码示例) 之配餐系统的开发

    本文所要将的是在"配餐系统"开发中 立体饼状图效果的实现,直接贴出代码和附上示例,相信需要的朋友可以很容易使用!项目中的效果图,如下: 实现 应用的是 System.Drawing ...

  10. 外卖餐饮点餐系统,连锁餐饮,公众号小程序源码2.1.5

    介绍: 外卖餐饮点餐系统,连锁餐饮,公众号小程序源码2.1.5,适用于:奶茶店-杂食店-蛋糕店-水果店-鸭货店-零食店-冷饮店-麻辣烫面食店- 任何吃的玩的店都可以帮你做 功能: 1,扫码点餐 2,同 ...

最新文章

  1. Excel和数据库的导入与导出
  2. leetcode -- Balanced Binary Tree TODO
  3. 搜狗浏览器也可以直接安装Chrome插件,太棒了
  4. oracle中主键自增长,oracle 数据库主键自动增长方法
  5. 将MongoDB.NET驱动程序与.NET Core WebAPI一起使用
  6. $.getjson异常信息提示_8种信息类型,中后台产品功能自查清单
  7. Zoom 是如何击败科技巨头的?
  8. fiddler抓包第一课--手机数据抓包
  9. linux netstat java,Linux netstat介绍
  10. AMR在IP域中的编码(rfc3267,4867)
  11. LSI存储论坛:6Gb SAS让DAS焕发新活力?
  12. 永磁同步电机的直接转矩控制(二)一一一传统DTC仿真结果分析
  13. VIM技巧及使用vim开发android应用
  14. java解析pdf 图片文字_Java 读取PDF中的文本和图片
  15. 教程篇(7.0) 06. FortiGate基础架构 单点登录(FSSO) ❀ Fortinet 网络安全专家 NSE 4
  16. k8s 重要的学习网站
  17. 笔记本计算机盖,如何在关闭盖子的情况下运行笔记本电脑
  18. python搭建PyDev详细版
  19. 【千律】OpenCV基础:图像阈值分割 -- 自适应阈值分割 -- 代码实现
  20. php 读取 excel 文件并上传数据库

热门文章

  1. win10完整Tensorflow-GPU环境搭建教程-附CUDA+cuDNN安装过程
  2. Toronto Research Chemicals丨ACP-5197 方案
  3. 联想thinkpad E470无线网络无法使用问题解决方法
  4. 想在家挣钱,这几个项目可以让你月入过万
  5. 线上服务器崩溃,线下门店破万,社交新宠「剧本杀」是如何迅速爆火的?
  6. 知识分享·NLP中一些有趣的trick
  7. [ZJCTF 2019]NiZhuanSiWei
  8. c语言case什么,switch
  9. 【转载】透视“专利恶霸”系列之三 2017年,专利恶霸的中国行动元年
  10. Vlookup函数和Sumif函数详解