Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化

Flask项目

  • 房屋列表页
  • 订单
  • 对接支付宝
  • 数据库优化

房屋列表页

Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第1张图片
Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第2张图片
Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第3张图片

from . import api
from flask import g, current_app, jsonify, request, session
from ihome.utils.response_code import RET
from ihome.models import Area, House, Facility, HouseImage, User, Order
from ihome import db, constants, redis_store
from ihome.utils.commons import login_required
from ihome.utils.image_storage import storage
from datetime import datetime
import json


# GET /api/v1.0/houses?sd=2017-12-01&ed=2017-12-31&aid=10&sk=new&p=1
@api.route("/houses")
def get_house_list():
    """获取房屋的列表信息(搜索页面)"""
    start_date = request.args.get("sd", "")  # 用户想要的起始时间
    end_date = request.args.get("ed", "")  # 用户想要的结束时间
    area_id = request.args.get("aid", "")  # 区域编号
    sort_key = request.args.get("sk", "new")  # 排序关键字
    page = request.args.get("p")  # 页数

    # 处理时间
    try:
        if start_date:
            start_date = datetime.strptime(start_date, "%Y-%m-%d")

        if end_date:
            end_date = datetime.strptime(end_date, "%Y-%m-%d")

        if start_date and end_date:
            assert start_date <= end_date
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg="日期参数有误")

    # 判断区域id
    if area_id:
        try:
            area = Area.query.get(area_id)
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.PARAMERR, errmsg="区域参数有误")

    # 处理页数
    try:
        page = int(page)
    except Exception as e:
        current_app.logger.error(e)
        page = 1

    # 获取缓存数据
    redis_key = "house_%s_%s_%s_%s" % (start_date, end_date, area_id, sort_key)
    try:
        resp_json = redis_store.hget(redis_key, page)
    except Exception as e:
        current_app.logger.error(e)
    else:
        if resp_json:
            return resp_json, 200, {"Content-Type": "application/json"}

    # 过滤条件的参数列表容器
    filter_params = []

    # 填充过滤参数
    # 时间条件
    conflict_orders = None

    try:
        if start_date and end_date:
            # 查询冲突的订单
            conflict_orders = Order.query.filter(Order.begin_date <= end_date, Order.end_date >= start_date).all()
        elif start_date:
            conflict_orders = Order.query.filter(Order.end_date >= start_date).all()
        elif end_date:
            conflict_orders = Order.query.filter(Order.begin_date <= end_date).all()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="数据库异常")

    if conflict_orders:
        # 从订单中获取冲突的房屋id
        conflict_house_ids = [order.house_id for order in conflict_orders]

        # 如果冲突的房屋id不为空,向查询参数中添加条件
        if conflict_house_ids:
            filter_params.append(House.id.notin_(conflict_house_ids))

    # 区域条件
    if area_id:
        filter_params.append(House.area_id == area_id)

    # 查询数据库
    # 补充排序条件
    if sort_key == "booking":  # 入住做多
        house_query = House.query.filter(*filter_params).order_by(House.order_count.desc())
    elif sort_key == "price-inc":
        house_query = House.query.filter(*filter_params).order_by(House.price.asc())
    elif sort_key == "price-des":
        house_query = House.query.filter(*filter_params).order_by(House.price.desc())
    else:  # 新旧
        house_query = House.query.filter(*filter_params).order_by(House.create_time.desc())

    # 处理分页
    try:
        #                               当前页数          每页数据量                              自动的错误输出
        page_obj = house_query.paginate(page=page, per_page=constants.HOUSE_LIST_PAGE_CAPACITY, error_out=False)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="数据库异常")

    # 获取页面数据
    house_li = page_obj.items
    houses = []
    for house in house_li:
        houses.append(house.to_basic_dict())

    # 获取总页数
    total_page = page_obj.pages

    resp_dict = dict(errno=RET.OK, errmsg="OK", data={"total_page": total_page, "houses": houses, "current_page": page})
    resp_json = json.dumps(resp_dict)

    if page <= total_page:
        # 设置缓存数据
        redis_key = "house_%s_%s_%s_%s" % (start_date, end_date, area_id, sort_key)
        # 哈希类型
        try:
            # redis_store.hset(redis_key, page, resp_json)
            # redis_store.expire(redis_key, constants.HOUES_LIST_PAGE_REDIS_CACHE_EXPIRES)

            # 创建redis管道对象,可以一次执行多个语句
            pipeline = redis_store.pipeline()

            # 开启多个语句的记录
            pipeline.multi()

            pipeline.hset(redis_key, page, resp_json)
            pipeline.expire(redis_key, constants.HOUES_LIST_PAGE_REDIS_CACHE_EXPIRES)

            # 执行语句
            pipeline.execute()
        except Exception as e:
            current_app.logger.error(e)

    return resp_json, 200, {"Content-Type": "application/json"}


# redis_store
#
# "house_起始_结束_区域id_排序_页数"
# (errno=RET.OK, errmsg="OK", data={"total_page": total_page, "houses": houses, "current_page": page})
#
#
#
# "house_起始_结束_区域id_排序": hash
# {
#     "1": "{}",
#     "2": "{}",
# }




前端js

var cur_page = 1; // 当前页
var next_page = 1; // 下一页
var total_page = 1;  // 总页数
var house_data_querying = true;   // 是否正在向后台获取数据

// 解析url中的查询字符串
function decodeQuery(){
    var search = decodeURI(document.location.search);
    return search.replace(/(^\?)/, '').split('&').reduce(function(result, item){
        values = item.split('=');
        result[values[0]] = values[1];
        return result;
    }, {});
}

// 更新用户点选的筛选条件
function updateFilterDateDisplay() {
    var startDate = $("#start-date").val();
    var endDate = $("#end-date").val();
    var $filterDateTitle = $(".filter-title-bar>.filter-title").eq(0).children("span").eq(0);
    if (startDate) {
        var text = startDate.substr(5) + "/" + endDate.substr(5);
        $filterDateTitle.html(text);
    } else {
        $filterDateTitle.html("入住日期");
    }
}


// 更新房源列表信息
// action表示从后端请求的数据在前端的展示方式
// 默认采用追加方式
// action=renew 代表页面数据清空从新展示
function updateHouseData(action) {
    var areaId = $(".filter-area>li.active").attr("area-id");
    if (undefined == areaId) areaId = "";
    var startDate = $("#start-date").val();
    var endDate = $("#end-date").val();
    var sortKey = $(".filter-sort>li.active").attr("sort-key");
    var params = {
        aid:areaId,
        sd:startDate,
        ed:endDate,
        sk:sortKey,
        p:next_page
    };
    $.get("/api/v1.0/houses", params, function(resp){
        house_data_querying = false;
        if ("0" == resp.errno) {
            if (0 == resp.data.total_page) {
                $(".house-list").html("暂时没有符合您查询的房屋信息。");
            } else {
                total_page = resp.data.total_page;
                if ("renew" == action) {
                    cur_page = 1;
                    $(".house-list").html(template("house-list-tmpl", {houses:resp.data.houses}));
                } else {
                    cur_page = next_page;
                    $(".house-list").append(template("house-list-tmpl", {houses: resp.data.houses}));
                }
            }
        }
    })
}

$(document).ready(function(){
    var queryData = decodeQuery();
    var startDate = queryData["sd"];
    var endDate = queryData["ed"];
    $("#start-date").val(startDate);
    $("#end-date").val(endDate);
    updateFilterDateDisplay();
    var areaName = queryData["aname"];
    if (!areaName) areaName = "位置区域";
    $(".filter-title-bar>.filter-title").eq(1).children("span").eq(0).html(areaName);


    // 获取筛选条件中的城市区域信息
    $.get("/api/v1.0/areas", function(data){
        if ("0" == data.errno) {
            // 用户从首页跳转到这个搜索页面时可能选择了城区,所以尝试从url的查询字符串参数中提取用户选择的城区
            var areaId = queryData["aid"];
            // 如果提取到了城区id的数据
            if (areaId) {
                // 遍历从后端获取到的城区信息,添加到页面中
                for (var i=0; i<data.data.length; i++) {
                    // 对于从url查询字符串参数中拿到的城区,在页面中做高亮展示
                    // 后端获取到城区id是整型,从url参数中获取到的是字符串类型,所以将url参数中获取到的转换为整型,再进行对比
                    areaId = parseInt(areaId);
                    if (data.data[i].aid == areaId) {
                        $(".filter-area").append('
  • + data.data[i].aid+'" class="active">'+ data.data[i].aname+'
  • '
    ); } else { $(".filter-area").append('
  • + data.data[i].aid+'">'+ data.data[i].aname+'
  • '
    ); } } } else { // 如果url参数中没有城区信息,不需要做额外处理,直接遍历展示到页面中 for (var i=0; i<data.data.length; i++) { $(".filter-area").append('
  • + data.data[i].aid+'">'+ data.data[i].aname+'
  • '
    ); } } // 在页面添加好城区选项信息后,更新展示房屋列表信息 updateHouseData("renew"); // 获取页面显示窗口的高度 var windowHeight = $(window).height(); // 为窗口的滚动添加事件函数 window.onscroll=function(){ // var a = document.documentElement.scrollTop==0? document.body.clientHeight : document.documentElement.clientHeight; var b = document.documentElement.scrollTop==0? document.body.scrollTop : document.documentElement.scrollTop; var c = document.documentElement.scrollTop==0? document.body.scrollHeight : document.documentElement.scrollHeight; // 如果滚动到接近窗口底部 if(c-b<windowHeight+50){ // 如果没有正在向后端发送查询房屋列表信息的请求 if (!house_data_querying) { // 将正在向后端查询房屋列表信息的标志设置为真, house_data_querying = true; // 如果当前页面数还没到达总页数 if(cur_page < total_page) { // 将要查询的页数设置为当前页数加1 next_page = cur_page + 1; // 向后端发送请求,查询下一页房屋数据 updateHouseData(); } else { house_data_querying = false; } } } } } }); $(".input-daterange").datepicker({ format: "yyyy-mm-dd", startDate: "today", language: "zh-CN", autoclose: true }); var $filterItem = $(".filter-item-bar>.filter-item"); $(".filter-title-bar").on("click", ".filter-title", function(e){ var index = $(this).index(); if (!$filterItem.eq(index).hasClass("active")) { $(this).children("span").children("i").removeClass("fa-angle-down").addClass("fa-angle-up"); $(this).siblings(".filter-title").children("span").children("i").removeClass("fa-angle-up").addClass("fa-angle-down"); $filterItem.eq(index).addClass("active").siblings(".filter-item").removeClass("active"); $(".display-mask").show(); } else { $(this).children("span").children("i").removeClass("fa-angle-up").addClass("fa-angle-down"); $filterItem.eq(index).removeClass('active'); $(".display-mask").hide(); updateFilterDateDisplay(); } }); $(".display-mask").on("click", function(e) { $(this).hide(); $filterItem.removeClass('active'); updateFilterDateDisplay(); cur_page = 1; next_page = 1; total_page = 1; updateHouseData("renew"); }); $(".filter-item-bar>.filter-area").on("click", "li", function(e) { if (!$(this).hasClass("active")) { $(this).addClass("active"); $(this).siblings("li").removeClass("active"); $(".filter-title-bar>.filter-title").eq(1).children("span").eq(0).html($(this).html()); } else { $(this).removeClass("active"); $(".filter-title-bar>.filter-title").eq(1).children("span").eq(0).html("位置区域"); } }); $(".filter-item-bar>.filter-sort").on("click", "li", function(e) { if (!$(this).hasClass("active")) { $(this).addClass("active"); $(this).siblings("li").removeClass("active"); $(".filter-title-bar>.filter-title").eq(2).children("span").eq(0).html($(this).html()); } }) })

    订单

    # coding:utf-8
    
    import datetime
    
    from flask import request, g, jsonify, current_app
    from ihome import db, redis_store
    from ihome.utils.commons import login_required
    from ihome.utils.response_code import RET
    from ihome.models import House, Order
    from . import api
    
    
    @api.route("/orders", methods=["POST"])
    @login_required
    def save_order():
        """保存订单"""
        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:
            # 将请求的时间参数字符串转换为datetime类型
            start_date = datetime.datetime.strptime(start_date_str, "%Y-%m-%d")
            end_date = datetime.datetime.strptime(end_date_str, "%Y-%m-%d")
            assert start_date <= end_date
            # 计算预订的天数
            days = (end_date - start_date).days + 1  # datetime.timedelta
        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:
            # 查询时间冲突的订单数
            count = Order.query.filter(Order.house_id == house_id, Order.begin_date <= end_date,
                                       Order.end_date >= start_date).count()
            #  select count(*) from order where ....
        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 = days * house.price
    
        # 保存订单数据
        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})
    
    
    # /api/v1.0/user/orders?role=custom     role=landlord
    @api.route("/user/orders", methods=["GET"])
    @login_required
    def get_user_orders():
        """查询用户的订单信息"""
        user_id = g.user_id
    
        # 用户的身份,用户想要查询作为房客预订别人房子的订单,还是想要作为房东查询别人预订自己房子的订单
        role = request.args.get("role", "")
    
        # 查询订单数据
        try:
            if "landlord" == role:
                # 以房东的身份查询订单
                # 先查询属于自己的房子有哪些
                houses = House.query.filter(House.user_id == user_id).all()
                houses_ids = [house.id for house in houses]
                # 再查询预订了自己房子的订单
                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="查询订单信息失败")
    
        # 将订单对象转换为字典数据
        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})
    
    
    @api.route("/orders//status", methods=["PUT"])
    @login_required
    def accept_reject_order(order_id):
        """接单、拒单"""
        user_id = g.user_id
    
        # 获取参数
        req_data = request.get_json()
        if not req_data:
            return jsonify(errno=RET.PARAMERR, errmsg="参数错误")
    
        # action参数表明客户端请求的是接单还是拒单的行为
        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()
            house = order.house
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg="无法获取订单数据")
    
        # 确保房东只能修改属于自己房子的订单
        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")
    
    
    @api.route("/orders//comment", methods=["PUT"])
    @login_required
    def save_order_comment(order_id):
        """保存订单评论信息"""
        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="操作失败")
    
        # 因为房屋详情中有订单的评价信息,为了让最新的评价信息展示在房屋详情中,所以删除redis中关于本订单房屋的详情缓存
        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")
    
    

    前端部分
    order.js

    
    //模态框居中的控制
    function centerModals(){
        $('.modal').each(function(i){   //遍历每一个模态框
            var $clone = $(this).clone().css('display', 'block').appendTo('body');    
            var top = Math.round(($clone.height() - $clone.find('.modal-content').height()) / 2);
            top = top > 0 ? top : 0;
            $clone.remove();
            $(this).find('.modal-content').css("margin-top", top-30);  //修正原先已经有的30个像素
        });
    }
    
    function getCookie(name) {
        var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
        return r ? r[1] : undefined;
    }
    
    $(document).ready(function(){
        $('.modal').on('show.bs.modal', centerModals);      //当模态框出现的时候
        $(window).on('resize', centerModals);
        // 查询房客订单
        $.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;
                            }
                        }
                    });
                });
                $(".order-comment").on("click", function(){
                    var orderId = $(this).parents("li").attr("order-id");
                    $(".modal-comment").attr("order-id", orderId);
                });
                $(".modal-comment").on("click", function(){
                    var orderId = $(this).attr("order-id");
                    var comment = $("#comment").val()
                    if (!comment) return;
                    var data = {
                        order_id:orderId,
                        comment:comment
                    };
                    // 处理评论
                    $.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");
                            }
                        }
                    });
                });
            }
        });
    
    
    });
    

    lorder.js

    //模态框居中的控制
    function centerModals(){
        $('.modal').each(function(i){   //遍历每一个模态框
            var $clone = $(this).clone().css('display', 'block').appendTo('body');    
            var top = Math.round(($clone.height() - $clone.find('.modal-content').height()) / 2);
            top = top > 0 ? top : 0;
            $clone.remove();
            $(this).find('.modal-content').css("margin-top", top-30);  //修正原先已经有的30个像素
        });
    }
    
    function getCookie(name) {
        var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
        return r ? r[1] : undefined;
    }
    
    $(document).ready(function(){
        $('.modal').on('show.bs.modal', centerModals);      //当模态框出现的时候
        $(window).on('resize', centerModals);
        // 查询房东的订单
        $.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");
                            }
                        }
                    });
                })
            }
        });
    });
    

    对接支付宝

    1. 支付宝开放平台登录
      使用已有的支付宝账号即可
      https://open.alipay.com/platform/home.htm

    2. 关于沙箱环境(开发模拟环境)

    3. Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第4张图片
      https://docs.open.alipay.com/200/105311

    登录后,在页面顶部可以选择进入沙箱环境设置页面
    Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第5张图片

    1. 支付宝开发者文档
      https://openhome.alipay.com/developmentDocument.htm
      手机网站支付
      https://docs.open.alipay.com/203/105285/

    2. 手机网站支付流程

    Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第6张图片
    5. 签名

    发送给支付的请求都需要进行签名
    https://docs.open.alipay.com/291/106118
    Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第7张图片

    1. 使用python工具包
      https://github.com/fzlee/alipay/blob/master/README.zh-hans.md
      安装
    # 从 1.3.0升级上来的用户, 请先卸载pycrypto:
    pip uninstall pycrypto
    # 安装python-alipay-sdk
    pip install python-alipay-sdk --upgrade
    

    生成密钥文件

    openssl
    OpenSSL> genrsa -out app_private_key.pem   2048  # 私钥
    OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 导出公钥
    OpenSSL> exit
    

    cat app_publict_key.pem 查看公钥的内容
    Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第8张图片
    将-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----中间的内容保存在支付宝的用户配置中(沙箱或者正式)
    https://openhome.alipay.com/platform/appDaily.htm?tab=info

    Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第9张图片
    Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第10张图片
    Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第11张图片
    下载支付宝的公钥文件Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第12张图片
    Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第13张图片
    将公钥的内容复制保存到一个文本文件中(alipay_pubilc_key.pem),注意需要在文本的首尾添加标记位(-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----) ,形如:
    在这里插入图片描述
    将刚刚生成的私钥app_private_key.pem和支付宝公钥alipay_public_key.pem放到我们的项目目录中Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第14张图片
    使用支付宝 python包的初始化
    Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第15张图片

    1. 支付接口
      Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第16张图片
      Flask项目(四)房屋列表页、订单、对接支付宝、数据库优化_第17张图片
    # coding:utf-8
    
    from . import api
    from ihome.utils.commons import login_required
    from ihome.models import Order
    from flask import g, current_app, jsonify, request
    from ihome.utils.response_code import RET
    from alipay import AliPay
    from ihome import constants, db
    import os
    
    
    @api.route("/orders//payment", methods=["POST"])
    @login_required
    def order_pay(order_id):
        """发起支付宝支付"""
        user_id = g.user_id
    
        # 判断订单状态
        try:
            order = Order.query.filter(Order.id == order_id, Order.user_id == user_id, Order.status == "WAIT_PAYMENT").first()
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg="数据库异常")
    
        if order is None:
            return jsonify(errno=RET.NODATA, errmsg="订单数据有误")
    
        # 创建支付宝sdk的工具对象
        alipay_client = AliPay(
            appid="2016081600258081",
            app_notify_url=None,  # 默认回调url
            app_private_key_path=os.path.join(os.path.dirname(__file__), "keys/app_private_key.pem"),  # 私钥
            alipay_public_key_path=os.path.join(os.path.dirname(__file__), "keys/alipay_public_key.pem"),  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            sign_type="RSA2",  # RSA 或者 RSA2
            debug=True  # 默认False
        )
    
        # 手机网站支付,需要跳转到https://openapi.alipaydev.com/gateway.do? + order_string
        order_string = alipay_client.api_alipay_trade_wap_pay(
            out_trade_no=order.id,  # 订单编号
            total_amount=str(order.amount/100.0),   # 总金额
            subject=u"爱家租房 %s" % order.id,  # 订单标题
            return_url="http://127.0.0.1:5000/payComplete.html",  # 返回的连接地址
            notify_url=None  # 可选, 不填则使用默认notify url
        )
    
        # 构建让用户跳转的支付连接地址
        pay_url = constants.ALIPAY_URL_PREFIX + order_string
        return jsonify(errno=RET.OK, errmsg="OK", data={"pay_url": pay_url})
    
    
    @api.route("/order/payment", methods=["PUT"])
    def save_order_payment_result():
        """保存订单支付结果"""
        alipay_dict = request.form.to_dict()
    
        # 对支付宝的数据进行分离  提取出支付宝的签名参数sign 和剩下的其他数据
        alipay_sign = alipay_dict.pop("sign")
    
        # 创建支付宝sdk的工具对象
        alipay_client = AliPay(
            appid="2016081600258081",
            app_notify_url=None,  # 默认回调url
            app_private_key_path=os.path.join(os.path.dirname(__file__), "keys/app_private_key.pem"),  # 私钥
            alipay_public_key_path=os.path.join(os.path.dirname(__file__), "keys/alipay_public_key.pem"),
            # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            sign_type="RSA2",  # RSA 或者 RSA2
            debug=True  # 默认False
        )
    
        # 借助工具验证参数的合法性
        # 如果确定参数是支付宝的,返回True,否则返回false
        result = alipay_client.verify(alipay_dict, alipay_sign)
    
        if result:
            # 修改数据库的订单状态信息
            order_id = alipay_dict.get("out_trade_no")
            trade_no = alipay_dict.get("trade_no")  # 支付宝的交易号
            try:
                Order.query.filter_by(id=order_id).update({"status": "WAIT_COMMENT", "trade_no": trade_no})
                db.session.commit()
            except Exception as e:
                current_app.logger.error(e)
                db.session.rollback()
    
        return jsonify(errno=RET.OK, errmsg="OK")
    
    
    
    

    数据库优化

    a. 表结构设计 扩展 查询的快慢
    三范式 https://www.zhihu.com/question/24696366
    设计的时候就考虑可能会用到的查询,为方便查询而设计,比如用空间换时间,适当增加冗余字段节省查询开销

    b. 建索引 主键 唯一(unique) 索引(key index) (外键)
    http://www.jianshu.com/p/2b541c028157
    提升 查询速度 复合索引 where k1 k2 k3 k4
    降低 增删改

    c. sql语句优化
    使用索引 注意关键字顺序 最左原则 where

    不要select *
    
    能使用联合查询,不使用嵌套(子查询)
    
    select  from tbl_a a inner join tbl_b b on a.field=b.filed where b.=
    
    select from tbl_a where filed=(select field from tbl_b where b.=)
    
    能不使用联合查询,尽量不用联合查询
    
    外键 cascade 级联 (维护外键有额外开销,影响性能)  数据量大的时候,不再使用外键
    
    使用分析工具分析效率低的sql语句   慢查询工具
    https://flyerboy.github.io/2016/12/23/mysql_slow/
    https://yemengying.com/2016/05/24/mysql-tuning/
    

    d. 缓存
    redis memcached

    e. 读写分离
    主从热备份 主(写 增删改) 从(查)

    f. 分库分表 水平分库分表
    http://www.infoq.com/cn/articles/key-steps-and-likely-problems-of-split-table

    工作流程

    1. email
    2. git gitlab 账号 rsa
    3. 虚拟私有网络 阿里云 腾讯云 aws 亚马逊
    4. 数据库

    公司团队有几个人
    我进公司之后负责哪一个业务
    公司使用的技术栈

    https://moshuqi.github.io/2017/07/20/%E8%87%AA%E5%B7%B1%E6%90%AD%E5%BB%BAVPN%E6%9C%8D%E5%8A%A1%E5%99%A8/

    你可能感兴趣的:(后端,flask,后端,数据库)