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");
}
}
});
})
}
});
});
支付宝开放平台登录
使用已有的支付宝账号即可
https://open.alipay.com/platform/home.htm
关于沙箱环境(开发模拟环境)
支付宝开发者文档
https://openhome.alipay.com/developmentDocument.htm
手机网站支付
https://docs.open.alipay.com/203/105285/
手机网站支付流程
发送给支付的请求都需要进行签名
https://docs.open.alipay.com/291/106118
# 从 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 查看公钥的内容
将-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----中间的内容保存在支付宝的用户配置中(沙箱或者正式)
https://openhome.alipay.com/platform/appDaily.htm?tab=info
下载支付宝的公钥文件
将公钥的内容复制保存到一个文本文件中(alipay_pubilc_key.pem),注意需要在文本的首尾添加标记位(-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----) ,形如:
将刚刚生成的私钥app_private_key.pem和支付宝公钥alipay_public_key.pem放到我们的项目目录中
使用支付宝 python包的初始化
# 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
工作流程
公司团队有几个人
我进公司之后负责哪一个业务
公司使用的技术栈
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/