点餐项目的实现

项目名称:点餐系统

项目描述:使用户可以通过浏览器访问服务器获取菜品信息并进行点餐;以及可以使管理员通过浏览访问服务器实现订单以及菜品的管理。
概要设计:框架设计——不太严谨的MVC框架
model:数据管理模块——管理数据(菜品,订单),外界想要访问数据必须通过这个模块完成,不能直接访问。
view:视图界面模块——浏览器前端界面,用户或者管理员的操作都是通过前端界面完成。
controller:业务控制模块——搭建服务器针对前端的请求进行对应业务处理。
详细设计
数据管理模块:
1.数据的存储:MySQL数据库
2.数据库表的设计:
菜品信息表:菜品ID;菜品名称;菜品单价;添加时间
订单信息表:订单ID;订单菜品;订单状态;修改时间
3.数据管理模块代码的设计
菜品数据类:添加菜品,删除菜品,修改菜品,获取菜品(单个、所有)
订单数据类:添加订单,删除订单,修改订单(菜单,状态),获取订单(所有、指定ID)
业务控制模块:
接收客户端请求,进行处理满足用户需求
1.搭建服务器:HTTP服务器(采用httplib库搭建)
2.通信接口设计:什么样的请求对应什么样的业务处理和响应
1.静态页面请求:html页面(以及依赖的css/js文件)
index.html——菜品信息的展示以及下单界面
2.动态数据请求:菜品数据,订单数据
通信接口采用restful风格接口设计
基于http协议,使用json格式定义正文序列化方式
定义操作类型:新增—POST;删除—DELETE;修改—PUT;获取—GET
前端界面模块:
html的编写渲染
实现方法:html+css+js

环境搭建

1.MySQL服务安装:按照博客一步步走下去即可
博客链接:https://zhuanlan.zhihu.com/p/49046496
2.gcc/g++升级

sudo yum -y install epel-release
sudo yum install centos-release
yum install devtoolset-7-gcc devtoolset-7-gcc-c++
source /opt/rh/devtoolset-7/enable

最好将source加载配置这句指令,写入到~/.bashrc这个文件中
3.安装jsoncpp开发包

sudo yum install jsoncpp-devel

MySQL代码操作

初始化操作:
1.初始化操作句柄

//初始化句柄,mysql_init(句柄地址)
                MYSQL* mysql = NULL;
                mysql = mysql_init(NULL);
                if(mysql == NULL)
                {
                        std::cout << "mysql init failed!\n";
                        return NULL;
                }

2.通过句柄连接mysql服务器

#define MYSQL_SERVER "127.0.0.1"
#define MYSQL_USER "root"
#define MYSQL_PASSWD "158470"
#define MYSQL_DBNAME "order_sys"
//连接服务器,mysql_real_connect(句柄,服务器ip,用户名,密码,数据库名称,端口(默认为0),套接字文件(通常置NULL),客户端标志(通常置0))
                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;
                }

3.设置客户端字符集(utf8)

//设置字符集,mysql_set_char_set(句柄,字符集名称)
                if (mysql_set_character_set(mysql,"utf8")!=0){
                        std::cout << mysql_error(mysql)<<std::endl;
                        return NULL;
                }

4.选择使用的数据库

//选择数据库,mysql_select_db(句柄,数据库名称)
//mysql_select_db(mysql,MYSQL_DBNAME),连接服务器时已经默认选择了,所以不需要执行该语句

数据操作(以菜品管理为例):
5.执行语句

static bool MysqlQuery(MYSQL *mysql,const std::string &sql)
{
//mysql_query(句柄,sql语句)
//函数c_str()就是将C++的string转化为C的字符串数组,生成一个const char*指针,指向字符串的首地址
			if(mysql_query(mysql,sql.c_str())!=0)
                {
                        std::cout<<sql<<std::endl;
                        std::cout<<mysql_error(mysql)<<std::endl;
                        return false;
                }
                return true;
}

新增

bool Insert(const Json::Value &dish){
                                //组织sql语句,insert tbname values()
#define DISH_INSERT "insert tb_dish values(null,'%s',%d,now());"
                                char str_sql[4096]={0};
                                //sprintf将dish的name和price放入DISH_INSERT中,再将DISH_INSERT保存在str_sql内。
                                sprintf(str_sql,DISH_INSERT,dish["name"].asCString(),
                                                dish["price"].asInt());
                                //执行sql语句,_mysql为初始化完成的句柄
                                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"].asCString(),
                                                dish["price"].asInt(),
                                                dish["id"].asInt());
                                return MysqlQuery(_mysql,str_sql);
                        }

查(ALL)

 bool SelectAll(Json::Value *dishes){
#define DISH_SELECTALL "select * from tb_dish;"
                                _mutex.lock();//互斥锁
                                bool ret = MysqlQuery(_mysql,DISH_SELECTALL);
                                if(ret == NULL){
                                        _mutex.unlock();
                                        return false;
                                }
                                MYSQL_RES *res = mysql_store_result(_mysql);//保存查询结果到本地,_mysql为初始化完成的句柄
                                _mutex.unlock();
                                if(res == NULL){
                                        std::cout<<"store result failed!\n";
                                        return false;
                                }
                                int num = mysql_num_rows(res);//获取结果集中数据的条数(行数),res为保存到本地的结果集地址
                                for(int i = 0;i<num;i++){
                                        MYSQL_ROW row = mysql_fetch_row(res);//遍历结果集
                                        //返回值是个char**的指针,将每一条数据做成了字符串指针数组,例如row[0]为第0列;
                                        //并且这个接口会保存当前读取结果位置,每次获取的都是下一条数据
                                        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);//把遍历到的结果放入dishes中
                                }
                                mysql_free_result(res);//释放结果集
                                return true;
                        }

查(ONE)

 bool SelectOne(int dish_id,Json::Value *dish){
#define DISH_SELECTONE "select * from tb_dish where id = %d;"
                                char sql_str[4096] = {0};
                                sprintf(sql_str,DISH_SELECTONE,dish_id);
                                _mutex.lock();
                                bool ret = MysqlQuery(_mysql,sql_str);
                                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;
                        }
        };

6.关闭句柄,释放资源

static void MysqlRelease(MYSQL *mysql){
                if(mysql != NULL){
                        mysql_close(mysql);
                }
                return;
        }

7.获取接口执行失败原因

const char *mysql_error(MYSQL *mysql)
//例如连接服务器时就使用了这个接口

业务处理模块

使用httplib搭建服务器框架(以菜品管理为例)

Server server;//用httplib构建服务器
        auto ret = server.set_mount_point("/","./www");//设置静态文件目录
        server.Post("/dish",DishInsert);
        server.Delete(R"(/dish/(\d+))",DishDelete);
        //正则表达式,\d表示0-9,+表示匹配多个字符,\d+表示匹配一个数字字符一次或多次
        //正则表达式  ()表示捕捉匹配括号中规则的文本
        //R"()" 表示括号中的字符串去除特殊字符的特殊含义,比如"\" 
        server.Put(R"(/dish/(\d+))",DishUpdate);
        server.Get(R"(/dish)",DishGetAll);
        server.Get(R"(/dish/(\d+))",DishGetOne);

新增

void DishInsert(const Request &req,Response &rsp){
        //1.业务处理
        //   (1)解析正文(json反序列化),得到菜品信息
        Json::Value dish;
        Json::Reader reader;
        bool ret = reader.parse(req.body,dish);//将请求中的正文反序列化,输入到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");//设置数据类型为Json格式
                std::cout<<"insert dish parse error\n";
        }
        //   (2)将菜品信息插入数据库
        ret = tbdish->Insert(dish);//tbdish为菜品信息表
        if(ret == false){
                Json::Value reason;
                Json::FastWriter writer;
                reason["result"] = false;
                reason["reason"] = "mysql insert dish failed";
                rsp.status = 500;
                rsp.body = writer.write(reason);
                rsp.set_header("Content-Type","application/json");
        }
        //2.设置响应信息,响应状态码httplib默认设置200
        rsp.status = 200;
}

删除

void DishDelete(const Request &req,Response &rsp){
        //1.获取要删除的菜品ID    /dish/id
        int dish_id = std::stoi(req.matches[1]);//matches[0]里放的是/dish/id,matches[1]里放的是/id
        //2.数据库删除操作
        bool ret = tbdish->Delete(dish_id);
        if(ret == false){
                std::cout << "delete dish mysql error\n";
                rsp.status = 500;
        }
}

修改

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";
        }
        dish["id"] = dish_id;
        ret = tbdish->Update(dish);
        if(ret == false){
                std::cout << "update mysql dish error\n";
                rsp.status = 500;
        }
}

查看(ALL)

void DishGetAll(const Request &req,Response &rsp){
        Json::Value dishes;
        bool ret = tbdish->SelectAll(&dishes);
        if(ret == false){
                std::cout<<"selectall mysql dish error\n";
                        rsp.status = 500;
        }
        Json::FastWriter writer;
        rsp.body = writer.write(dishes);
        rsp.set_header("Content-Type","application/json");
}

查看(ONE)

void DishGetOne(const Request &req,Response &rsp){
        int dish_id = std::stoi(req.matches[1]);//获取请求中的菜品id
        Json::Value dishes;
        bool ret = tbdish->SelectOne(dish_id,&dishes);//将菜品id为dish_id的菜品信息输入到dishes中
        if(ret == false){
                std::cout<<"selectone mysql dish error\n";
                        rsp.status = 500;
        }
        Json::FastWriter writer;
        rsp.body = writer.write(dishes);//把dishes中的信息序列化为Json格式到回应的正文中

}

下面是用postman进行的测试。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

前端界面模块

这里下载了一个免费的html模板。下面是主要改动的代码,以用户端为例。

<h1 style="text-align:center">菜馆</h1>
                        <div class="mt-3 mb-5">
                            <h3 style="text-align:center">菜单</h3>
                            <table class="table table-hover">
                                <thead>
                                    <tr>
                                        <th>菜名</th>
                                        <th>单价</th>
                                        <th>勾选下单</th>
                                    </tr>
                                </thead>
                                <tfoot>
                                    <tr>
                                        <th colspan="3" style="text-align:right">//合并3个单元格
                                            <button type="button" class="btn btn-primary" v-on:click="insert_order()">下单</button>//创建下单按钮,按下后即执行insert_order函数
                                            总价:{{get_total()}}//计算总价函数
                                        </th>
                                    </tr>
                                </tfoot>
                                <tbody>
                                    <tr v-for="dish in dishes">//v-for循环指令,dishes会在data中查找对应名字的属性,dish代表每个访问到的元素,通过该指令将dishes对象绑定到表格内容中
                                        <td>{{dish.name}}</td>
                                        <td>{{dish.price/100}}</td>
                                        <td>
                                            <div class="from-check">
                                                <label class="from-check-label">
                                                    <input class="from-check-input" type="checkbox" v-model="dish.selected">下单
                                                </label>//创建复选框,v-model为表单绑定指令,该指令表示将下单按钮与表单内的菜品数据绑定,勾选下单后会将对应菜品数据也选上,为get_total函数提供参数
                                            </div>
                                        </td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
 <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>//引入Vue.js
        <script>
        //实例化vue对象
            var app = new Vue({
                el:'#myorder',//选择器,提供一个页面上已存在的DOM元素作为Vue实例的挂载目标
                data:{
                    dishes:[
                        {id:1,name:"红烧肉",price:"1700",ctime:"2021-9-9 8:00:00"},
                        {id:2,name:"红烧茄子",price:"1200",ctime:"2021-9-9 8:00:00"},
                        {id:3,name:"宫保鸡丁",price:"1700",ctime:"2021-9-9 8:00:00"},
                    ]
                },
                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),//序列化成为json格式的字符串
                            //res:包含来自请求的结果数据
                            //status:包含请求的状态
                            //xhr:包含 XMLHttpRequest 对象,该对象用于在后台与服务器交换数据
                            success:function(res,status,xhr){
                                if(xhr.status==200)
                                alert("下单成功");
                            }
                        })
                    },
                    get_alldishes:function(){
                        $.ajax({
                            url:"/dish",
                            type:"get",
                            context:this,
                            //success方法是一个回调函数,获取从后台传来的数据
                            success:function(res,status,xhr){
                                this.dishes=JSON.parse(res);
                            }
                        })
                    }
                }
            });
            app.get_alldishes();//在全局增加一个获取菜单的调用,使网页加载的时候就可获得菜品信息
        </script>

页面展示如下
在这里插入图片描述
在数据库中查看订单信息表验证下单成功。
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值