說明:該篇博客是博主一字一碼編寫的,實屬不易,請尊重原創,謝謝你們!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"); } } });
注:此接口在客戶支付訂單後,訂單狀態變成待評價時,才能進行測試