说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢你们!javascript
接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/86599482html
目录前端
一丶保存订单后端接口编写java
二丶预订页面前端编写以及接口测试python
四丶订单模块(个人订单)前端编写redis
五丶个人订单功能测试数据库
1.分析:当用户点击房屋图片进入房间信息页面时,在页面最下面会出现便可预约功能按钮(左图),当用户点击预约时,即跳转到预约页面(右图),在该页面中,首选是获取用户点击的房屋信息显示到此页面上,而后须要用户选择入住的时间和离开的时间,当用户选择完时间后那么在界面左下角即显示出订单的价格,点击提交订单后,此时须要从后端来判断此房间在用户选择入住离开期间有没有冲突订单,若是有则提示用户房屋被抢订,从新选择日期的一个提示,若是提交订单成功,则在个人订单中显示出该订单,这是整个业务逻辑
2.逻辑编写以下
@api.route("/orders", methods=["POST"]) @login_required def save_order(): """保存用户的订单""" pass
user_id = g.user_id
order_data = request.get_json()
if not order_data: return jsonify(errno=RET.PARAMERR, errmsg="参数错误")
house_id = order_data.get("house_id") start_date_str = order_data.get("start_date") end_date_str = order_data.get("end_date")
if not all([house_id, start_date_str, end_date_str]): return jsonify(errno=RET.PARAMERR, errmsg="参数不完整")
try: # 将str格式的日期数据转换成datetime格式的日期数据 start_date = datetime.strptime(start_date_str, "%Y-%m-%d") end_date = datetime.strptime(end_date_str, "%Y-%m-%d") assert start_date <= end_date #使用断言就行判断 # 计算预订的天数 days = (end_date-start_date).days + 1 except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.PARAMERR, errmsg="日期格式不正确")
try: house = House.query.get(house_id) except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DBERR, errmsg="获取房屋信息失败")
if not house: return jsonify(errno=RET.NODATA, errmsg="房屋不存在")
if user_id == house.user_id: return jsonify(errno=RET.ROLEERR, errmsg="不能预订本身发布的房屋")
try: # 查询时间冲突的订单数 select count(*) from ih_order_info where () count = Order.query.filter(Order.house_id == house_id, Order.begin_date <= end_date, Order.end_date >= start_date).count() except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DBERR, errmsg="系统繁忙,请稍候重试")
if count > 0: return jsonify(errno=RET.DATAERR, errmsg="房屋已被预订")
amount = house.price * days
order = Order( house_id=house_id, user_id=user_id, begin_date=start_date, end_date=end_date, days=days, house_price=house.price, amount=amount )
try: db.session.add(order) db.session.commit() except Exception as (e): current_app.logger.error(e) db.session.rollback() return jsonify(errno=RET.DBERR, errmsg="保存订单失败") 返回正确响应数据 return jsonify(errno=RET.OK, errmsg="OK", data={"order_id": order.id})
1.在booking.js中进行以下编写
$.get("/api/v1.0/session", function(resp) { if ("0" != resp.errno) { location.href = "/login.html"; } }, "json");
var queryData = decodeQuery(); var houseId = queryData["id"];
$.get("/api/v1.0/houses/" + houseId, function(resp){ if (0 == resp.errno) { $(".house-info>img").attr("src", resp.data.house.img_urls[0]); $(".house-text>h3").html(resp.data.house.title); $(".house-text>p>span").html((resp.data.house.price/100.0).toFixed(0)); } });
$(".submit-btn").on("click", function(e) { if ($(".order-amount>span").html()) { $(this).prop("disabled", true); var startDate = $("#start-date").val(); var endDate = $("#end-date").val(); var data = { "house_id":houseId, "start_date":startDate, "end_date":endDate }; $.ajax({ url:"/api/v1.0/orders", type:"POST", data: JSON.stringify(data), contentType: "application/json", dataType: "json", headers:{ "X-CSRFTOKEN":getCookie("csrf_token"), }, success: function (resp) { if ("4101" == resp.errno) { location.href = "/login.html"; } else if ("4004" == resp.errno) { showErrorMsg("房间已被抢定,请从新选择日期!"); } else if ("0" == resp.errno) { location.href = "/orders.html"; } } }); } });
2.测试
1.分析:第一当用户成功提交订单后,则会跳转到个人订单页面,此时在这个页面就应该显示出用户刚才预约房屋的订单状况,第二就是须要对角色进行一个判断,若是是下单人那么在个人订单中查看订单,若是是房东那么就在客户订单中,查看别人预约我发布的房屋的订单,不管是哪一种状况,对于后端来讲都是查询数据库订单信息状况而已,因此这两个功能能够用一个查询订单接口在后端进行实现
2.说明:之因此无论哪一种角色在个人爱家页面都出现这两个有关订单的功能(个人订单和客户订单),那是由于对于任何注册网站的用户来讲,我既能够订房,也能够发布房源,即一个帐号能够切换成两种角色
3.逻辑编写
# /api/v1.0/user/orders?role=(custom/landlord) @api.route("/user/orders", methods=["GET"]) @login_required def get_user_orders(): """查询用户的订单信息""" pass
user_id = g.user_id
role = request.args.get("role", "")
try: # 以房东的身份在数据库中查询本身发布过的房屋 if "landlord" == role: houses = House.query.filter(House.user_id == user_id).all() # 经过列表生成式方式保存房东名下的全部房屋的id houses_ids = [house.id for house in houses] # 在Order表中查询预约了本身房子的订单,并按照建立订单的时间的倒序排序,也就是在此页面显示最新的订单信息 orders = Order.query.filter(Order.house_id.in_(houses_ids)).order_by(Order.create_time.desc()).all() else: # 以房客的身份查询订单,则查询的是个人订单 orders = Order.query.filter(Order.user_id == user_id).order_by(Order.create_time.desc()).all() except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DBERR, errmsg="获取订单信息失败")
def to_dict(self): """将订单信息转换为字典数据""" order_dict = { "order_id": self.id, "title": self.house.title, "img_url": constants.QINIU_URL_DOMAIN + self.house.index_image_url if self.house.index_image_url else "", "start_date": self.begin_date.strftime("%Y-%m-%d"), "end_date": self.end_date.strftime("%Y-%m-%d"), "ctime": self.create_time.strftime("%Y-%m-%d %H:%M:%S"), "days": self.days, "amount": self.amount, "status": self.status, "comment": self.comment if self.comment else "" }
orders_dict_list = [] if orders: for order in orders: orders_dict_list.append(order.to_dict())
return jsonify(errno=RET.OK, errmsg="OK", data={"orders": orders_dict_list})
1.个人订单orders.js中进行以下编写,需注意的是当客户预约的房间后,会引导到支付连接去
// 查询房客订单 $.get("/api/v1.0/user/orders?role=custom", function(resp){ if ("0" == resp.errno) { $(".orders-list").html(template("orders-list-tmpl", {orders:resp.data.orders})); $(".order-pay").on("click", function () { var orderId = $(this).parents("li").attr("order-id"); $.ajax({ url: "/api/v1.0/orders/" + orderId + "/payment", type: "post", dataType: "json", headers: { "X-CSRFToken": getCookie("csrf_token"), }, success: function (resp) { if ("4101" == resp.errno) { location.href = "/login.html"; } else if ("0" == resp.errno) { // 引导用户跳转到支付宝链接 location.href = resp.data.pay_url; } } }); });
2.在orders.html中进行以下编写
<script id="orders-list-tmpl" type="text/html"> {{if orders}} {{each orders as order}} <li order-id={{order.order_id}}> <div class="order-title"> <h3>订单编号:{{order.order_id}}</h3> {{ if "WAIT_COMMENT" == order.status }} <div class="fr order-operate"> <button type="button" class="btn btn-success order-comment" data-toggle="modal" data-target="#comment-modal">发表评价</button> </div> {{ else if "WAIT_PAYMENT" == order.status }} <div class="fr order-operate"> <button type="button" class="btn btn-success order-pay">去支付</button> </div> {{/if}} </div> <div class="order-content"> <img src="{{order.img_url}}"> <div class="order-text"> <h3>{{order.title}}</h3> <ul> <li>建立时间:{{order.ctime}}</li> <li>入住日期:{{order.start_date}}</li> <li>离开日期:{{order.end_date}}</li> <li>合计金额:¥{{(order.amount/100.0).toFixed(0)}}(共{{order.days}}晚)</li> <li>订单状态: <span> {{if "WAIT_ACCEPT" == order.status}} 待接单 {{else if "WAIT_PAYMENT" == order.status}} 待支付 {{else if "WAIT_COMMENT" == order.status}} 待评价 {{else if "COMPLETE" == order.status}} 已完成 {{else if "REJECTED" == order.status}} 已拒单 {{/if}} </span> </li> {{if "COMPLETE" == order.status}} <li>个人评价: {{order.comment}}</li> {{else if "REJECTED" == order.status}} <li>拒单缘由: {{order.comment}}</li> {{/if}} </ul> </div> </div> </li> {{/each}} {{else}} 暂时没有订单。 {{/if}} </script>
1.登陆18022222222(Hellotaogang)帐户后,直接进入个人订单页面,成功显示个人订单信息,以下
2.以客户的角色进行预约房间,选择锦江区,价格由高到低,选择最贵的房间
3.进入房间信息后,点击便可预约(左图),而后提交成功后跳转到个人订单页(右图),成功按照最新预约时间进行排序显示
4.查看数据库ih_user_profile用户信息表以及ih_order_info订单信息表,当前的全部订单都是由18022222222(Hellotaogang)帐号进行的预约
5.如今博主登陆18033333333(张三)帐号进行测试,测试房东能不能预约本身发布的房屋(刷单)
6.上一篇博客中有一个搜索条件未进行测试,由于当时并无编写订单模块接口,因此没法查询到冲突订单,如今我使用18111111111(taogang123)帐号进行相同房间时间冲突为条件进行搜索测试
1.分析:当房东进入客户订单后,即显示出该房东的房屋被客户预约的订单信息,在每一个订单上都会有接单以及拒单的功能按钮,不论是接单仍是拒单都是改变订单的一个状态,只是房东在选择拒单时必需填写拒绝缘由,因此这关于接单和拒单这两个功能接口能够进行复用
2.接口逻辑编写
@api.route("/orders/<int:order_id>/status", methods=["PUT"]) @login_required def accept_reject_order(order_id): """接单拒单""" pass
user_id = g.user_id
req_data = request.get_json()
if not req_data: return jsonify(errno=RET.PARAMERR, errmsg="参数错误")
action = req_data.get("action")
if action not in ("accept", "reject"): return jsonify(errno=RET.PARAMERR, errmsg="参数错误")
try: order = Order.query.filter(Order.id == order_id, Order.status == "WAIT_ACCEPT").first() # 获取order订单对象中的house对象 house = order.house except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DBERR, errmsg="没法获取订单数据") # 若是order对象不存在或者订单中的房屋id不等于用户id 则说明房东在修改不属于本身房屋订单 if not order or house.user_id != user_id: return jsonify(errno=RET.REQERR, errmsg="操做无效")
if action == "accept": # 接单 order.status = "WAIT_PAYMENT" elif action == "reject": # 拒单 reason = req_data.get("reason") if not reason: return jsonify(errno=RET.PARAMERR, errmsg="参数错误") order.status = "REJECTED" order.comment = reason
try: db.session.add(order) db.session.commit() except Exception as e: current_app.logger.error(e) db.session.rollback() return jsonify(errno=RET.DBERR, errmsg="操做失败")
return jsonify(errno=RET.OK, errmsg="OK")
1.在客户订单页lorders.js中进行以下编写
$.get("/api/v1.0/user/orders?role=landlord", function(resp){ if ("0" == resp.errno) { $(".orders-list").html(template("orders-list-tmpl", {orders:resp.data.orders})); $(".order-accept").on("click", function(){ var orderId = $(this).parents("li").attr("order-id"); $(".modal-accept").attr("order-id", orderId); });
$(".modal-accept").on("click", function(){ var orderId = $(this).attr("order-id"); $.ajax({ url:"/api/v1.0/orders/"+orderId+"/status", type:"PUT", data:'{"action":"accept"}', contentType:"application/json", dataType:"json", headers:{ "X-CSRFTOKEN":getCookie("csrf_token"), }, success:function (resp) { if ("4101" == resp.errno) { location.href = "/login.html"; } else if ("0" == resp.errno) { $(".orders-list>li[order-id="+ orderId +"]>div.order-content>div.order-text>ul li:eq(4)>span").html("已接单"); $("ul.orders-list>li[order-id="+ orderId +"]>div.order-title>div.order-operate").hide(); $("#accept-modal").modal("hide"); } } }) }); $(".order-reject").on("click", function(){ var orderId = $(this).parents("li").attr("order-id"); $(".modal-reject").attr("order-id", orderId); });
$(".modal-reject").on("click", function(){ var orderId = $(this).attr("order-id"); var reject_reason = $("#reject-reason").val(); if (!reject_reason) return; var data = { action: "reject", reason:reject_reason }; $.ajax({ url:"/api/v1.0/orders/"+orderId+"/status", type:"PUT", data:JSON.stringify(data), contentType:"application/json", headers: { "X-CSRFTOKEN":getCookie("csrf_token") }, dataType:"json", success:function (resp) { if ("4101" == resp.errno) { location.href = "/login.html"; } else if ("0" == resp.errno) { $(".orders-list>li[order-id="+ orderId +"]>div.order-content>div.order-text>ul li:eq(4)>span").html("已拒单"); $("ul.orders-list>li[order-id="+ orderId +"]>div.order-title>div.order-operate").hide(); $("#reject-modal").modal("hide"); } } }); })
2.在lorders.html中进行以下编写
<script id="orders-list-tmpl" type="text/html"> {{if orders}} {{each orders as order}} <li order-id={{order.order_id}}> <div class="order-title"> <h3>订单编号:{{order.order_id}}</h3> {{ if "WAIT_ACCEPT" == order.status }} <div class="fr order-operate"> <button type="button" class="btn btn-success order-accept" data-toggle="modal" data-target="#accept-modal">接单</button> <button type="button" class="btn btn-danger order-reject" data-toggle="modal" data-target="#reject-modal">拒单</button> </div> {{/if}} </div> <div class="order-content"> <img src="{{order.img_url}}"> <div class="order-text"> <h3>{{order.title}}</h3> <ul> <li>建立时间:{{order.ctime}}</li> <li>入住日期:{{order.start_date}}</li> <li>离开日期:{{order.end_date}}</li> <li>合计金额:¥{{(order.amount/100.0).toFixed(0)}}(共{{order.days}}晚)</li> <li>订单状态: <span> {{if "WAIT_ACCEPT" == order.status}} 待接单 {{else if "WAIT_COMMENT" == order.status}} 待评价 {{else if "COMPLETE" == order.status}} 已完成 {{else if "REJECTED" == order.status}} 已拒单 {{/if}} </span> </li> {{if "COMPLETE" == order.status}} <li>个人评价: {{order.comment}}</li> {{else if "REJECTED" == order.status}} <li>拒单缘由: {{order.comment}}</li> {{/if}} </ul> </div> </div> </li> {{/each}} {{else}} 暂时没有订单。 {{/if}} </script>
3.测试
1.分析:由于订单评价接口和接单拒单功能接口同样,也是对数据库进行修改操做,因此在定义接口路由的时候请求方式也是选择的是PUT,原本订单评价接口是在支付接口后面才去写的,但由于此接口与拒单接单接口大同小异,因此便一块接着客户订单接口在orders.py中一块儿写了,须要注意的时这个接口是在订单状态变成待评价时,才能触发这个功能接口进行评价
2.逻辑编写
@api.route("/orders/<int:order_id>/comment", methods=["PUT"]) @login_required def save_order_comment(order_id): """保存订单评价信息""" pass
user_id = g.user_id
req_data = request.get_json() comment = req_data.get("comment")
if not comment: return jsonify(errno=RET.PARAMERR, errmsg="参数错误")
try: order = Order.query.filter(Order.id == order_id, Order.user_id == user_id, Order.status == "WAIT_COMMENT").first() house = order.house except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DBERR, errmsg="没法获取订单数据")
if not order: return jsonify(errno=RET.REQERR, errmsg="操做无效")
try: # 将订单的状态设置为已完成 order.status = "COMPLETE" # 保存订单的评价信息 order.comment = comment # 将房屋的完成订单数增长1 house.order_count += 1 db.session.add(order) db.session.add(house) db.session.commit() except Exception as e: current_app.logger.error(e) db.session.rollback() return jsonify(errno=RET.DBERR, errmsg="操做失败")
try: redis_store.delete("house_info_%s" % order.house.id) except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.OK, errmsg="OK")
3.在order.js中补充处理评论的逻辑
$.ajax({ url:"/api/v1.0/orders/"+orderId+"/comment", type:"PUT", data:JSON.stringify(data), contentType:"application/json", dataType:"json", headers:{ "X-CSRFTOKEN":getCookie("csrf_token"), }, success:function (resp) { if ("4101" == resp.errno) { location.href = "/login.html"; } else if ("0" == resp.errno) { $(".orders-list>li[order-id="+ orderId +"]>div.order-content>div.order-text>ul li:eq(4)>span").html("已完成"); $("ul.orders-list>li[order-id="+ orderId +"]>div.order-title>div.order-operate").hide(); $("#comment-modal").modal("hide"); } } });
注:此接口在客户支付订单后,订单状态变成待评价时,才能进行测试