(根据居然老师直播课内容整理)
一、所在城区信息
- 本项目城区未进行省市县三级联动,仅考虑一个固定区域
- 城区信息有多个地方使用,如:首页、房屋信息等页面都会使用
- 所在城区调用时,不一定要登录,如首页时,未登录,就有可能访问所在城区信息
- 城区信息基本不变动,并且频繁需要使用,建议将此数据存放到redis中,设置失效时间,失效后,再到数据库中查询
1、城区信息接口设计
1.1 请求方式
选项 |
方案 |
请求方法 |
GET |
请求地址 |
/areas |
1.2 请求参数:路径参数
参数名 |
类型 |
是否必传 |
说明 |
title |
字符串 |
是 |
标题 |
price |
字符串 |
是 |
单价,单位:分 |
area_id |
字符串 |
是 |
归属地的区域编号 |
address |
字符串 |
是 |
地址 |
room_count |
字符串 |
是 |
房间数目 |
acreage |
字符串 |
是 |
房屋面积 |
unit |
字符串 |
是 |
房屋单元, 如几室几厅 |
capacity |
字符串 |
是 |
房屋容纳的人数 |
beds |
字符串 |
是 |
房屋床铺的配置 |
deposit |
字符串 |
是 |
房屋押金 |
min_days |
字符串 |
是 |
最少入住天数 |
max_days |
字符串 |
是 |
最多入住天数,0表示不限制 |
facility |
列表 |
是 |
房屋的设施 |
1.3 响应结果:JSON
名字 |
类型 |
是否必须 |
说明 |
errno |
字符串 |
是 |
错误代码 |
errmsg |
字符串 |
是 |
错误内容 |
data |
列表 |
否 |
每个列表元素都一个字典:{} |
2、城区信息实现
- 查询 redis里面是否有城区信息 ,如果有,将redis中保存的数据返回
- redis中保存的是json格式字符串
- {data:[{aid:area_id,anmae:area_name},{aid:area_id,anmae:area_name},…]}
- 从数据库中获取所有城区信息
- 生成城区信息列表:列表元素
- 为方便存取,在Area类中定义to_dict 方法
- 对取出来的所有记录,用to_dict生成字典,再添加到列表中
- 将数据保存redis中
- 生成字典{data:[{aid:area_id,anmae:area_name},{aid:area_id,anmae:area_name},…]}
- 将字典用json.dumps()转换成字符串
- 将字符串保存到redis中
- 返回数据
@api.route("/areas",methods=["GET"])
def get_area_info():
"""获取城区信息"""
try:
response_json = redis_store.get("area_info")
except Exception as e:
logging.error(e)
else:
if response_json is not None:
response_json = json.loads(response_json)
logging.info('redis cache')
return jsonify(errno=RET.OK, errmsg='OK', data=response_json['data'])
try:
area_li = Area.query.all()
except Exception as e:
logging.error(e)
return jsonify(errno=RET.DBERR, errmsg='数据库异常')
area_dict_li = []
for area in area_li:
area_dict_li.append(area.to_dict())
response_dict = dict(data=area_dict_li)
response_json = json.dumps(response_dict)
try:
redis_store.setex("area_info", constants.AREA_INFO_REDIS_CACHE_EXPIRES, response_json)
except Exception as e:
logging.error(e)
return jsonify(errno=RET.OK, errmsg='OK', data=area_dict_li)
二、保存房屋的基本信息
- 保存房屋信息,必须是登录后才能操作,且是身份认证过的客户才能发布信息
1、保存房屋基本信息接口设计
1.1 请求方式
选项 |
方案 |
请求方法 |
POST |
请求地址 |
/houses/info |
1.2 请求参数:路径参数
1.3 响应结果:JSON
响应结果 |
响应内容 |
errno |
错误编号 |
errmsg |
错误信息 |
data |
房屋信息 |
2、保存房屋基本信息实现
- 首先应用登录装饰器
- 如果没有登录,直接返回 sessionerr,跳转到登录界面
- 进入函数后,首先获取g对象中的user_id
- 获取参数
- 参数校验
- 房屋名称标题:应该防止敏感字符
- 单价:(数据库一般保存为分)
- 区域编号:判断是否存在
- 房间数:>0 <最大数 (根据需求来)
- 房屋面积:大于最小值
- 房屋布局:(根据需求来)
- 容纳人数:(根据需求来)
- 卧床数:(根据需求来)
- 押金: 同单价
- 最小入住天数:
- 最大入住天数:>=最小入住天数 <=(根据需求来)
- 创建house实例,并赋值
- 校验房屋设施列表:判断设施编号是否存在
- select * from h_facility_info where id in “1,3,5”
- 将有效设施列表赋值给house实例中
- 保存数据
三、保存房屋的图片
1、保存房屋图片接口设计
1.1 请求方式
选项 |
方案 |
请求方法 |
POST |
请求地址 |
/houses/image |
1.2 请求参数:路径参数
参数名 |
类型 |
是否必传 |
说明 |
house_image |
files |
是 |
房屋图片 |
house_id |
string |
是 |
房屋ID |
1.3 响应结果:JSON
响应结果 |
响应内容 |
errno |
错误编号 |
errmsg |
错误信息 |
data |
房屋图片URL地址 |
2、保存房屋图片实现
- 首先应用登录装饰器
- 如果没有登录,直接返回 sessionerr,跳转到登录界面
- 获取参数
- 参数校验
- 从数据库中检查房屋编号是否在
- 图片信息(本项目未校验)
- 保存图片到七牛云平台,获得保存图片的名称
- 保存数据
- 创建房屋图片对象,并赋值
- 判断房屋是否有首页图片
- 提交保存数据,失败回滚
- 返回
四、获取房东发布的房源信息
1、获取房东发布的房源信息接口设计
1.1 请求方式
选项 |
方案 |
请求方法 |
GET |
请求地址 |
/user/houses |
1.2 请求参数:g对象中user_id
参数名 |
类型 |
是否必传 |
说明 |
user_id |
string |
是 |
用户ID |
1.3 响应结果:JSON
响应结果 |
响应内容 |
errno |
错误编号 |
errmsg |
错误信息 |
data |
房屋列表信息 |
2、获取房东发布的房源信息实现
@api.route("user/houses",methods=["GET"])
@login_required
def get_user_houses():
"""
获取用户发布的房源
:return: 发布的房源信息
"""
user_id = g.user_id
try:
user = User.query.get(user_id)
houses = user.houses
except Exception as e:
logging.error(e)
return jsonify(errno=RET.DBERR, errmsg='获取数据失败')
houses_list = []
if houses:
for house in houses:
houses_list.append(house.to_basic_dict())
return jsonify(errno=RET.OK, errmsg="OK", data={
"houses": houses_list})
五、获取主页展示的房屋基本信息(轮播图)
- 首页以轮播图的方式,从房源信息中筛选的部分房屋基本信息(图片)进行轮播展示
- 数量有限制,不会太多
- 具有一定排序规则,如竞价规则等(本项目采用定单多的顺序从高到低筛选)
1、获取主页展示的房屋基本信息接口设计
1.1 请求方式
选项 |
方案 |
请求方法 |
GET |
请求地址 |
/houses/index |
1.2 请求参数:无
1.3 响应结果:JSON
|响应结果|响应内容|
|errno|错误编号|
|errmsg|错误信息|
|data|房屋列表信息|
2、获取主页展示的房屋基本信息实现
- 查询数据库
- 按订单数量排序查询房屋信息,根据最大数据限制记录数
- 将记录插入到空列表中,
- 返回数据
@api.route("/houses/index",methods=["GET"])
def get_user_info():
"""
获取首页房屋信息
:return: 排序后的房屋信息
"""
try:
houses = House.query.order_by(House.order_count.desc()).limit(constants.HOME_PAGE_MAX_NUMS).all()
except Exception as e:
return jsonify(errno=RET.DBERR, errmsg="查询数据库失败")
if not houses:
return jsonify(errno=RET.NODATA, errmsg="查询没有数据")
houses_list = []
for house in houses:
houses_list.append(house.to_basic_dict())
house_dict = dict(errno=RET.OK, errmsg="OK", data=houses_list)
json_houses = json.dumps(house_dict)
return json_houses, 200, {
"Content-Type": "application/json"}
3、获取主页展示的房屋基本信息优化
- 首页轮播图数据变动不用太频繁
- 每次重新生成对系统资源消耗较大
- 建议将轮播图存在到缓存中,在有效期内,直接使用
3.1优化实现
- 首先查询缓存数据
- 如果存在,即非None,直接返回
- 如果不存在,
- 查询数据库
- 按订单数量排序查询房屋信息,根据最大数据限制记录数
- 将记录插入到空列表中
- 生成返回数据字典
- 将数据转换成json格式
- 将数据保存到redis中
- 返回数据
@api.route("/houses/index",methods=["GET"])
def get_user_info():
"""
获取首页房屋信息
:return: 排序后的房屋信息
"""
try:
result=redis_store.get("home_page_data")
except Exception as e:
logging.error(e)
result=None
if result:
return result.decode(), 200, {
"Content-Type": "application/json"}
return jsonify(errno=RET.OK, errmsg="OK", data=houses_list)
else:
try:
houses = House.query.order_by(House.order_count.desc()).limit(constants.HOME_PAGE_MAX_NUMS).all()
except Exception as e:
return jsonify(errno=RET.DBERR, errmsg="查询数据库失败")
if not houses:
return jsonify(errno=RET.NODATA, errmsg="查询没有数据")
houses_list = []
for house in houses:
houses_list.append(house.to_basic_dict())
house_dict = dict(errno=RET.OK, errmsg="OK", data=houses_list)
json_houses = json.dumps(house_dict)
return json_houses, 200, {
"Content-Type": "application/json"}
六、获取房屋详情信息
1、获取房屋详细信息接口设计
1.1 请求方式
选项 |
方案 |
请求方法 |
GET |
请求地址 |
/user/houses |
1.2 请求参数:g对象中user_id
参数名 |
类型 |
是否必传 |
说明 |
house_id |
整形 |
是 |
房屋ID |
1.3 响应结果:JSON
响应结果 |
响应内容 |
errno |
错误编号 |
errmsg |
错误信息 |
data |
{“house”:房屋详细信息,“user_id”:房东_id} |
2、获取房屋详细信息实现
@api.route("/houses/<int:house_id>",methods=["GET"])
def get_house_detail(house_id):
"""
获取房屋详情
:param house_id: 房屋的ID
:return: 房屋的详细信息
"""
user_id=session.get("user_id","-1")
if not house_id:
return jsonify(errno=RET.PARAMERR,errmsg="参数错误")
try:
house=House.query.get(house_id)
except Exception as e:
logging.error(e)
return jsonify(errno=RET.DBERR,errmsg="查询数据库失败")
if not house:
return jsonify(errno=RET.NODATA,errmsg="房屋信息不存在")
house_data=house.to_full_dict()
return jsonify(errno=RET.OK,errmsg="ok",data={
"house":house_data,"user_id":user_id})
3、获取房屋详细信息优化
- 将图片数据直接存放入缓存,(首页轮播图一般点击率较高,并且数据变动不大)
3.1 优化实现
- 首先判断缓存是否存在图片
- 获取到图片信息后,存入缓存后再返回
try:
result=redis_store.get("house_info_%s"% house_id)
except Exception as e:
logging.error(e)
result=None
if result:
return '{"errno":%s, "errmsg":"OK", "data":{"house": %s, "user_id": %s}}' % (RET.OK, result.decode(), user_id), 200, {
"Content-Type": "application/json"}
json_house = json.dumps(house_data)
try:
redis_store.setex("house_info_%s"% house_id,constants.HOUSE_DETAIL_REDIS_EXPIRE, json_house)
except Exception as e:
logging.error(e)
print(e)
七、检索房源
- 首页显示房屋轮播图,并提供房源检索功能
- 检索条件:区域、入住时间
- 显示有排序选择
- 检索房源可以不登录,也可以登录
1、检索房屋信息接口设计
1.1 请求方式
选项 |
方案 |
请求方法 |
GET |
请求地址 |
/houses |
1.2 请求参数:g对象中user_id
参数名 |
类型 |
是否必传 |
说明 |
aid |
整形 |
否 |
区域的id |
sd |
string |
否 |
开始时间 |
ed |
string |
否 |
结束时间 |
sk |
整形 |
否 |
排序 |
p |
整形 |
否 |
页码 |
1.3 响应结果:JSON
响应结果 |
响应内容 |
errno |
错误编号 |
errmsg |
错误信息 |
data |
{“total_page”:total_page,“house”:房屋信息} |
2、检索房屋信息实现
- 接收参数
- 校验参数
- 如果sd、ed不为空,要能转换日期,否则返回参数错
- 如果sd、ed都不为空,ed>=sd,否则返回参数错
- 如果 aid 不为空,aid要存在,否则返回参数错
- 如果sk不为空,sk取值必须在约定类型中,如果不在,赋值最常用的一个
- 如果 p 不为空,p必须为整形,如果不是,直接赋为1
- 设定检索起止时间内的房屋条件
- 房屋信息表里没有订房的时间,所以要从定单表中找到可以入住的房间,即排除已经预订出去房间号即可
- 从订单表中取出冲突的房间号
- 房屋表中排除冲突的房间号
- 如果开始和结束时间都有,按上面条件处理
- 如果仅有开始时间:order.end_date>=start_date
- 如果仅有结束时间:order.begin_date<=end_data
- 设定检索区域的房屋条件
- 将条件放到列中,对房屋表进行组合查询
- 按排序参数,进行设定排序
- 创建分页实例:利用BaseQuery.paginate方法进行创建
- 获得总页码
- 获取分页的房屋数据
- 将每一个房屋信息转换成字典,插入到列表中,形成房屋数组
- 组合成返回数据,返回
3、检索房屋信息优化
- 搜索数据放到redis中,以提高处理速度
- 因为条件较多,按页用hash保存
3.1 优化实现
- 数据校验完后,直接查询缓存
- 如果有,直接返回
- 检索出来后,保存到缓存
@api.route("/houses", methods=["GET"])
def get_house_list():
"""
房屋的搜索页面
:param: aid 区域的id sd 开始时间 ed 结束时间 sk 排序 p 页码
:return: 符合条件的房屋
"""
start_date = request.args.get("sd")
end_date = request.args.get("ed")
area_id = request.args.get("aid")
sort_key = request.args.get("sk")
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:
logging.error(e)
return jsonify(errno=RET.PARAMERR, errmsg="日期参数有误")
if area_id:
try:
area = Area.query.get(area_id)
except Exception as e:
logging.error(e)
return jsonify(errno=RET.PARAMERR, errmsg="区域参数有误")
if sort_key not in ["new", "booking", "price-inc", "price-des"]:
sort_key = "new"
try:
page = int(page)
except Exception as e:
logging.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:
logging.error(e)
else:
if resp_json:
return resp_json.decode(), 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:
logging.error(e)
return jsonify(errno=RET.DBERR, errmsg='数据库异常')
if conflict_orders:
conflict_house_id = [order.house_id for order in conflict_orders]
if conflict_house_id:
filter_params.append(House.id.notin_(conflict_house_id))
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())
page_obj = house_query.paginate(page=page, per_page=constants.HOUSE_LIST_PAGE_NUMS, error_out=False)
total_page = page_obj.pages
house_li = page_obj.items
houses = []
for house in house_li:
houses.append(house.to_basic_dict())
resp_dict = dict(errno=RET.OK, errmsg="OK", data={
"total_page": total_page, "houses": houses})
resp_json = json.dumps(resp_dict)
redis_key = "house_%s_%s_%s_%s" % (start_date, end_date, area_id, sort_key)
try:
pipeline = redis_store.pipeline()
pipeline.hset(redis_key, page, resp_json)
pipeline.expire(redis_key, constants.HOUSE_LIST_PAGE_REDIS_CACHE_EXPIRES)
pipeline.execute()
except Exception as e:
logging.error(e)
return jsonify(errno=RET.OK, errmsg="OK", data={
"total_page": total_page, "houses": houses})