def get_areas(): """获取城区信息: 1.查询出所有城区信息 2.响应数据 """ # 1.查询所有城区信息 try: resp_json = redis_conn.get("area_info") except Exception as e: current_app.logger.error(e) else: if resp_json: current_app.logger.info("hit redis area_info") return resp_json.decode(), 200, {"Content-Type": "application/json"} try: areas = Area.query.all() except Exception as e: current_app.logger.debug(e) return jsonify(re_code=RET.DBERR, msg='查询城区信息失败') if not areas: return jsonify(re_code=RET.NODATA, msg='暂无城区') areas = [area.to_dict() for area in areas] resp_dict = dict(re_code=RET.OK, msg='查询城区成功', areas=areas) resp_json = json.dumps(resp_dict) try: redis_conn.set("area_info", resp_json, constants.AREA_INFO_REDIS_EXPIRES) except Exception as e: current_app.logger.error(e) return resp_json, 200, {"Content-Type": "application/json"}
def get_area_info(): """获取城区信息""" try: # 1.先尝试从redis中读取数据 area_info = redis_conn.get("area_info") except Exception as e: current_app.logger.error(e) else: if area_info: # redis有缓存数据 current_app.logger.info("hit redis area_info") # Content-Type默认是text/html return area_info, 200, {"Content-Type": "application/json"} try: # 2.redis没有数据再去查询数据库 area_li = Area.query.all() except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DBERR, errmsg="数据库异常") areas = [] # 将对象转换为字典 for area in area_li: areas.append(area.to_dict()) area_info = json.dumps(dict(areas)) try: # 3.将mysql查询数据保存到redis中 redis_conn.setex("area_info", constants.AREA_INFO_REDIS_CACHE_EXPIRES, area_info) except Exception as e: current_app.logger.error(e) return area_info, 200, {"Content-Type": "application/json"}
def get_areas(): """ 获取地区地址 :return: 返回地址信息 """ # 尝试从缓存里获取数据,如果没有从读数据库获取 try: areas_dict = redis_conn.get('areas_info') # string except Exception as e: current_app.logger.error(e) if areas_dict: areas_dict = eval(areas_dict) # string --> dict else: try: all_areas = Area.query.all() except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DATAERR,errmsg=u'数据库错误') areas_dict = {} for area in all_areas: areas_dict[area.id] = area.name # 将数据缓存到redis try: redis_conn.setex('areas_info',3600,areas_dict) except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.OK,data=areas_dict)
def login(): """ 登录 请求参数:手机号、密码 参数格式:json :return: """ # 接收参数 req_dict = request.get_json() mobile = req_dict.get('mobile') password = req_dict.get('password') # 校验参数完整性 if not all([mobile, password]): return jsonify(errno=RET.PARAMERR, errmsg='参数不完整') # 判断手机号格式 if not re.match(r"1[34578]\d{9}", mobile): return jsonify(errno=RET.PARAMERR, errmsg="手机号格式错误") # 判断错误次数是否超过限制 user_ip = request.remote_addr # 用户的ip地址 try: # redis记录: "access_nums_请求的ip": "次数" access_nums = redis_conn.get("access_num_%s" % user_ip) except Exception as e: current_app.logger.error(e) else: if access_nums and int(access_nums) >= constants.LOGIN_ERROR_MAX_TIMES: return jsonify(errno=RET.REQERR, errmsg="错误次数过多,请稍后重试") # 查询用户信息 try: user = User.query.filter_by(mobile=mobile).first() except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DBERR, errmsg="获取用户信息失败") # 密码比对 if user is None or not user.check_password(password): try: # 验证失败时记录错误次数:redis的incr方法可以对字符串类型的数字数据进行加一操作,如果数据一开始不存在,则会初始化为1 redis_conn.incr("access_num_%s" % user_ip) # 设置登录错误限制时间 redis_conn.expire("access_num_%s" % user_ip, constants.LOGIN_ERROR_FORBID_TIME) except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DATAERR, errmsg="用户名或密码错误") # 保存登录状态到session中 session["name"] = user.name session["mobile"] = user.mobile session["user_id"] = user.id # 返回结果 return jsonify(errno=RET.OK, errmsg="登录成功")
def get_hosue_details(): """ 获取房屋详情信息 """ # 接收路径参数 house_id = request.args.get('house_id') if not house_id: return jsonify(errcode=RET.PARAMERR, errmsg="缺少必须参数, 请检查路径是否正确") # 尝试获取访问者的id, 如果获取为空, 将使用默认值'-1'替代 call_user_id = session.get('user_id', '-1') # 尝试从redis中读取缓存数据 try: json_house = redis_conn.get('house_info_{}'.format(house_id)) except Exception as e: current_app.logger.error(e) else: if json_house is not None: return '{{"errcode": "0", "errmsg": "OK", "data": {0}, "call_user_id": {1}}}'.format(json_house.decode(), call_user_id), \ 200, {"Content-Type": "application/json"} # 查询数据库 try: house = House.query.filter_by(id=house_id).first() except Exception as e: current_app.logger.error(e) return jsonify(errcode=RET.DBERR, errmsg="数据库异常") if house is None: return jsonify(errcode=RET.NODATA, errmsg="没有查询到数据") try: # 先将数据转换为字典, 这是在模型中封装的方法 house_data_dict = house.to_full_dict() except Exception as e: current_app.logger.error(e) return jsonify(errcode=RET.SERVERERR, errmsg="数据提取失败, 请联系管理员") else: # 将数据存放到redis中 json_house = json.dumps(house_data_dict) # 获取设置的redis过期时间, 配置文件中 redis_expires = current_app.config.get('HOME_PAGE_DATA_REDIS_EXPIRES') try: redis_conn.set('house_info_{}'.format(house_id), json_house, ex=redis_expires) except Exception as e: current_app.logger.error(e) return jsonify(errcode=RET.DBERR, errmsg="设置缓存异常") # return jsonify(errcode=RET.OK, errmsg="OK", data=house.to_full_dict()) return '{{"errcode": "0", "errmsg": "OK", "data": {0}, "call_user_id": {1}}}'.format( json_house, call_user_id), 200, { "Content-Type": "application/json" }
def register(): """用户注册接口: 1.获取参数phone_num 手机号,phonecode 短信验证码,password 密码 2.校验数据 3.从Redis获取短信验证码,和传来的数据校验,如果正确 4.新增user对象, 5.跳转首页,保持登录状态 :return 返回注册信息{ 're_code':'0','msg':'注册成功'} """ # 1.获取参数phone_num 手机号,phonecode 短信验证码,password 密码 json_dict = request.json phone_num = json_dict.get('phone_num') phonecode_client = json_dict.get('phonecode') password = json_dict.get('password') # 2.校验数据 if not all([phone_num, phonecode_client, password]): return jsonify(re_code=RET.PARAMERR, msg='参数不完整') # 校验手机号是否正确 if not re.match( r'^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\d{8}$', phone_num): return jsonify(re_code=RET.PARAMERR, msg='手机号不正确') # 3.从Redis获取短信验证码,和传来的数据校验,如果正确 try: phonecode_server = redis_conn.get('PhoneCode:' + phone_num) except Exception as e: current_app.logger.debug(e) return jsonify(re_code=RET.DBERR, msg='查询短信验证码失败') if phonecode_server.decode() != phonecode_client: return jsonify(re_code=RET.PARAMERR, msg='短信验证码错误') # 4.新增user对象, user = User() user.name = phone_num user.phone_num = phone_num user.password_hash = password try: db.session.add(user) db.session.commit() except Exception as e: current_app.logger.debug(e) db.session.rollback() return jsonify(re_code=RET.DBERR, msg='注册失败') # 5.跳转首页,保持登录状态 session['user_id'] = user.id session['name'] = user.name session['phone_num'] = user.phone_num # 6.响应结果 return jsonify(re_code=RET.OK, msg='注册成功')
def get_house_index(): """ 获取主页幻灯片 """ # 设置首页获取首页的幻灯片数量 HOME_PAGE_MAX_IMAGE = 5 # 尝试从缓存中读取 try: json_houses = redis_conn.get('home_page_data') except Exception as e: current_app.logger.error(e) if json_houses is not None: return '{{"errcode": "0", "errmsg": "OK", "data": {}}}'.format( json_houses.decode()), 200, { "Content-Type": "application/json" } else: # 查询数据库 try: houses = House.query.order_by( House.order_count.desc()).limit(HOME_PAGE_MAX_IMAGE).all() except Exception as e: current_app.logger.error(e) return jsonify(errcode=RET.DBERR, errmsg="查询数据库失败") if not houses: return jsonify(errcode=RET.NODATA, errmsg="抱歉, 暂时没有查到数据") houses_list = [] # 遍历house表的查询集 for house in houses: # 如果房屋未设置主图片, 则跳过 if not house.index_image_url: continue houses_list.append(house.to_basic_dict()) # 将数据转换为json, 并保存到redis缓存中, 字符串类型 json_houses = json.dumps(houses_list) # 获取redis缓存时间 home_page_data_redis_expires = current_app.config.get( 'HOME_PAGE_DATA_REDIS_EXPIRES') try: redis_conn.set('home_page_data', json_houses, ex=home_page_data_redis_expires) except Exception as e: current_app.logger.error(e) return '{{"errcode": "0", "errmsg": "OK", "data": {}}}'.format( json_houses), 200, { "Content-Type": "application/json" }
def get_house_detail(house_id): """ 展示房屋的详细信息 前端在房屋详情页面展示时,如果浏览页面的用户不是该房屋的房东,则展示预定按钮,否则不展示, 所以需要后端返回登录用户的user_id 尝试获取用户登录的信息,若登录,则返回给前端登录用户的user_id,否则返回user_id=-1 :param house_id: int<house_id> :return: json """ user_id = session.get('user_id',0) # 检查参数 if not house_id: return jsonify(errno=RET.PARAMERR,errmsg=u'参数错误') # 尝试从redis读取缓存 try: cache_data = redis_conn.get('house_detail_%s' %house_id) except Exception as e: current_app.logger.error(e) if cache_data: print ('从redis中读取缓存数据') response = {'errno': RET.OK, 'errmsg': u'ok', 'data': {'user_id': user_id, 'house_info': eval(cache_data)}} return json.dumps(response), 200, {'Content-Type': 'application/json'} try: house = House.query.get(house_id) except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DBERR, errmsg=u'查询失败') if not house: return jsonify(errno=RET.DATAEXIST, errmsg=u'房屋不存在') # 获取详细信息(dict) house_data = house.to_full_dict() # json_data = json.dumps(house_data) # 设置缓存数据 try: redis_conn.setex('house_detail_%s' %house_id,3600,house_data) except Exception as e: current_app.logger.error(e) response = {'errno':RET.OK,'errmsg':u'ok','data':{'user_id':user_id,'house_info':house_data}} return json.dumps(response),200,{'Content-Type':'application/json'}
def register(): """用户注册接口""" # 1.获取参数phone_num 手机号,phonecode 短信验证码,password 密码 phone_num = request.json.get('phone_num') phonecode_client = request.json.get('phonecode') password = request.json.get('password') #2.校验数据 if not all([phone_num, phonecode_client, password]): return jsonify(re_code=RET.PARAMERR, msg='参数不完整') # 校验手机号是否正确 if not re.match(r'1[3456789]\d{9}$', phone_num): return jsonify(re_code=RET.PARAMERR, msg='手机号不正确') # 3.从Redis获取短信验证码,和传来的数据校验,如果正确 try: phonecode_server = redis_conn.get('PhoneCode:' + phone_num) except Exception as e: current_app.logger.debug(e) return jsonify(re_code=RET.DBERR, msg='查询短信验证码失败') if phonecode_server != phonecode_client: return jsonify(re_code=RET.PARAMERR, msg='短信验证码错误') #4.新增user对象, user = User() user.name = phone_num user.phone_num = phone_num user.password_hash = password try: db.session.add(user) db.session.commit() except Exception as e: current_app.logger.debug(e) db.session.rollback() return jsonify(re_code=RET.DBERR, msg='注册失败') #5.跳转首页,保持登录状态 session['user_id'] = user.id session['name'] = user.name session['phone_num'] = user.phone_num #6.响应结果 return jsonify(re_code=RET.OK, msg='注册成功')
def get_area_info(): """ 获取城区信息 """ # 尝试从redis中读取数据, 如果读取成功则使用缓存返回给前端, 否则从数据库获取 try: resp_json = redis_conn.get('area_info') except Exception as e: current_app.logger.error(e) else: if resp_json is not None: # 代表redis中已经具有缓存数据 current_app.logger.info('hit redis area_info') # 表示拿到了数据, 记录日志 return resp_json, 200, {'Content-Type': 'application/json'} # 查询数据库读取城区信息 try: area_li = Area.query.all() except Exception as e: current_app.logger.error(e) return jsonify(errcode=RET.DBERR, errmsg="数据库异常") area_dict_li = [] # 将对象转为字典 for area in area_li: # 得到每行数据的字段, to_dict是在model里封装的方法 area_dict_li.append(area.to_dict()) # 将返回的数据转换为json字符串 resp_dict = dict(errcode=RET.OK, errmsg='ok', data=area_dict_li) resp_json = json.dumps(resp_dict) # 将数据保存到redis中 try: # 注意这里一定要设置有效期, 否则哪怕数据库的记录更新了, 返回给前端的也是历史记录的缓存 redis_conn.set( 'area_info', resp_json, ex=current_app.config.get('AREA_INFO_REDIS_CACHE_EXPIRES')) except Exception as e: current_app.logger.error(e) return resp_json, 200, {'Content-Type': 'application/json'}
def house_detail(house_id): """房屋详情页面: 1.获取url栏中的house_id 2.根据house_id获取house详细信息 3.判断用户是否登录, 4.响应结果 """ # 1.获取url栏中的house_id # 2.根据house_id获取house详细信息 try: resp_json = redis_conn.get("house_info_{}".format(house_id)) except Exception as e: current_app.logger.error(e) else: if resp_json: current_app.logger.info("hit redis house_info_{}".format(house_id)) return resp_json.decode(), 200, {"Content-Type": "application/json"} try: house = House.query.get(house_id) except Exception as e: current_app.logger.debug(e) return jsonify(re_code=RET.DBERR, msg='查询房屋信息失败') if not house: return jsonify(re_code=RET.NODATA, msg='房屋不存在') house = house.to_full_dict() # 3. 获取user_id : 当用户登录后访问detail.html,就会有user_id,反之,没有user_id login_user_id = session.get('user_id', -1) resp_dict = dict(re_code=RET.OK, msg='查询成功', data={'house': house, 'login_user_id': login_user_id}) resp_json = json.dumps(resp_dict) try: redis_conn.set("house_info_{}".format(house_id), resp_json, constants.AREA_INFO_REDIS_EXPIRES) except Exception as e: current_app.logger.error(e) # 4.响应结果 return resp_json, 200, {"Content-Type": "application/json"}
def get_houses_index(): """ 获取房屋的index_image_url图片,并展示到主页 :return: image_urls """ # 尝试从redis获取缓存 try: cache_data = redis_conn.get('index_urls_data') except Exception as e: current_app.logger.error(e) if cache_data: print ('从redis读取数据') response = {'errno': RET.OK, 'errmsg': 'ok', 'data': {'houses_info': eval(cache_data)}} return json.dumps(response), 200, {'Content-Type': 'application/json'} try: houses = House.query.order_by(House.order_count.desc()).limit(5) except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DBERR,errmsg=u'获取数据失败') index_data = [] for house in houses: if not house.index_image_url: continue index_data.append({'img_url':current_app.config.get('IMAGE_STORAGE_URL') + house.index_image_url, 'title':house.title, 'house_id':house.id}) # 设置缓存 try: redis_conn.setex('index_urls_data',3600,index_data) except Exception as e: current_app.logger.error(e) response = {'errno':RET.OK,'errmsg':'ok','data':{'houses_info':index_data}} return json.dumps(response),200,{'Content-Type':'application/json'}
def register(): """ 用户注册 POST Request : 需要携带的参数: 手机号, 短信验证码, 密码 返回数据格式: json """ # 获取请求的json数据, 返回字典 req_dict = request.get_json() mobile = req_dict.get('mobile') sms_code = req_dict.get('sms_code') password = req_dict.get('password') password2 = req_dict.get('password2') # 1.效验参数 if not all([mobile, sms_code, password]): return jsonify(errcode=RET.PARAMERR, errmsg='参数不完整') if not re.match(r'^1[3456789]\d{9}$', mobile): # 表示格式不对 return jsonify(errcode=RET.PARAMERR, errmsg='手机号格式不正确') if password != password2: return jsonify(errcode=RET.PARAMERR, errmsg='两次输入的密码不一致') # 2. 从redis中取出短信验证码, 判断是是否过期 try: real_sms_code = redis_conn.get('sms_code_{}'.format(mobile)).decode() except Exception as e: # 记录错误日志 current_app.logger.error(e) return jsonify(errcode=RET.DBERR, errmsg='验证码读取异常') else: if real_sms_code is None: return jsonify(errcode=RET.NODATA, errmsg='短信验证码已过期') # 3. 判断用户填写短信验证码的正确性 if real_sms_code != sms_code: return jsonify(errcode=RET.DATAERR, errmsg='短信验证码输入错误') # 4. 删除已经使用的短信验证码 try: redis_conn.delete('sms_code_{}'.format(mobile)) except Exception as e: current_app.logger.error(e) # 4. 判断用户的手机号是否注册过, 如未注册则保存数据到数据库中 user = User(name=mobile, mobile=mobile) user.password = password try: # 因为mobile字段是唯一的, 如果用户提交的手机号被注册, 则会抛出异常 db.session.add(user) db.session.commit() except IntegrityError as e: # 事务回滚 db.session.rollback() # 记录错误日志, 该异常代表手机号出现重复(mobile字段) current_app.logger.error(e) return jsonify(errcode=RET.DATAEXIST, errmsg='该手机号已经注册') except Exception as e: # 数据库的其它异常 db.session.rollback() current_app.logger.error(e) return jsonify(errcode=RET.DBERR, errmsg='数据库异常') # 5. 保存登录状态到session中 session['name'] = mobile session['mobile'] = mobile session['user_id'] = user.id # 6. 返回结果 return jsonify(errcode=RET.OK, errmsg='用户注册成功')
def login(): """ 用户登录 POST Request : 需要携带的参数: 手机号, 密码 返回数据格式: json """ # 1. 获取参数 req_dict = request.get_json() mobile = req_dict.get('mobile') password = req_dict.get('password') # 2. 效验参数 if not all([mobile, password]): return jsonify(errcode=RET.PARAMERR, errmsg='参数不完整') if not re.match(r'1[3456789]\d{9}', mobile): # 表示格式不对 return jsonify(errcode=RET.PARAMERR, errmsg='手机号格式不正确') # 判断用户请求的次数有没有超过限制 # 获取请求的地址, 和最大的登录错误次数限制 user_ip = request.remote_addr login_error_max_nums = current_app.config.get('LOGIN_ERROR_MAX_NUMS') try: # 尝试获取用户redis中的请求次数记录 request_nums = redis_conn.get('request_nums_{}'.format(user_ip)) except Exception as e: current_app.logger.error(e) else: if request_nums is not None and int(request_nums.decode()) >= login_error_max_nums: return jsonify(errcode=RET.IPERR, errmsg='登录次数受限, 请于10分钟后再试') # 验证用户密码 try: user = User.query.filter_by(mobile=mobile).first() except Exception as e: current_app.logger.error(e) jsonify(errcode=RET.DBERR, errmsg='获取用户信息失败') # 判断用户名和密码 if user is None or not user.check_password(password): # 如果验证失败, 记录错误次数, 返回信息 try: # 该方法会key的值累加1, 如果key不存在则自动创建并设置初始值为0 redis_conn.incr('request_nums_{}'.format(user_ip)) # 设置登录错误记录的过期时间为600/S redis_conn.expire('request_nums_{}'.format(user_ip), 600) except Exception as e: current_app.logger.error(e) return jsonify(errcode=RET.DATAERR, errmsg='用户名或密码错误') # 3. 保存登录状态到session中 if user.name is None: # 如果用户昵称未设置, 则使用手机号 session['name'] = mobile else: session['name'] = user.name session['mobile'] = mobile session['user_id'] = user.id # 4. 返回结果 return jsonify(errcode=RET.OK, errmsg='登录成功')
def send_sms_code(): """发送手机短信息验证码: 1.接收参数,手机号,图片验证码,uuid 2.校验数据 3.判断图片验证码是否正确,如果正确 4.发送短信验证码 """ # 1.接收参数,手机号,图片验证码,uuid json_str=request.data json_dict=json.loads(json_str) phone_num=json_dict.get('phone_num') image_code_client=json_dict.get('image_code') uuid=json_dict.get('uuid') # 2.校验数据 if not all([phone_num,image_code_client,uuid]): return jsonify(re_code=RET.PARAMERR,msg='参数缺少') # 校验手机号是否正确 if not re.match(r'^0\d{2,3}\d{7,8}$|^1[358]\d{9}$|^147\d{8}$',phone_num): return jsonify(re_code=RET.PARAMERR,msg='手机号不正确') #判断用户是否已注册 try: user=User.query.filter(User.phone_num == phone_num).first() except Exception as e: current_app.logger.debug(e) return jsonify(re_code=RET.DBERR,msg='查询数据库错误') #用户存在,提示该账户已被注册 if user: return jsonify(re_code=RET.DATAEXIST,msg='该用户已被注册') # 3.判断图片验证码是否正确,如果正确 try: # 从Redis取出值图片验证码 image_code_server=redis_conn.get('ImageCode:'+uuid) except Exception as e: current_app.logger.debug(e) return jsonify(re_code=RET.DBERR,msg='获取服务器图片验证码失败') #判断为验证码空或者过期 if not image_code_server: return jsonify(re_code=RET.NODATA,msg='验证码已过期') #校验和前端传的验证码是否相等 if image_code_server.lower()!=image_code_client.lower(): return jsonify(re_code=RET.DATAERR,msg='验证码输入有误') # 4.生成验证码 sms_code='%06d' % random.randint(0,99999) current_app.logger.debug('短信验证码为:'+sms_code) # 5.发送短信验证码 验证码 过期时间:容联的时间单位为:分 短信模板1 # result = CCP().send_sms('15770633066',[sms_code,constants.SMS_CODE_REDIS_EXPIRES/60],'1') # if result != 1: # # 短信发送失败 # return jsonify(re_code=RET.THIRDERR,msg='发送短信验证码失败') # 6.发送成功,验证码存储到Redis try: redis_conn.set('PhoneCode:'+phone_num,sms_code,constants.SMS_CODE_REDIS_EXPIRES) except Exception as e: current_app.logger.debug(e) return jsonify(re_code=RET.DBERR,msg='存储短信验证码失败') #响应结果 return jsonify(re_code=RET.OK,msg='验证码发送成功')
def send_sms_code(): """发送手机短信息验证码: 1.接收参数,手机号,图片验证码,uuid 2.校验数据 3.判断图片验证码是否正确,如果正确 4.发送短信验证码 """ # 接收参数,手机号,图片验证码,uuid json_str = request.data json_dict = json.loads(json_str) phone_num = json_dict.get('phone_num') image_code_client = json_dict.get('image_code') uuid = json_dict.get('uuid') # 校验数据 if not all([phone_num, image_code_client, uuid]): return jsonify(re_code=RET.PARAMERR, msg='参数缺少') # 校验手机号是否正确 if not re.match( r'^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\d{8}$', phone_num): return jsonify(re_code=RET.PARAMERR, msg='手机号不正确') # 判断用户是否已注册 try: user = User.query.filter(User.phone_num == phone_num).first() except Exception as e: current_app.logger.debug(e) return jsonify(re_code=RET.DBERR, msg='查询数据库错误') # 用户存在,提示该账户已被注册 if user: return jsonify(re_code=RET.DATAEXIST, msg='该用户已被注册') # 判断图片验证码是否正确,如果正确 try: # 从Redis取出值图片验证码 image_code_server = redis_conn.get('ImageCode:' + uuid) except Exception as e: current_app.logger.debug(e) return jsonify(re_code=RET.DBERR, msg='获取服务器图片验证码失败') # 判断为验证码空或者过期 if not image_code_server: return jsonify(re_code=RET.NODATA, msg='验证码已过期') # 校验和前端传的验证码是否相等 if image_code_server.decode().lower() != image_code_client.lower(): return jsonify(re_code=RET.DATAERR, msg='验证码输入有误') # 4.生成验证码 sms_code = '%06d' % random.randint(0, 99999) current_app.logger.debug('短信验证码为:' + sms_code) # 5.发送短信验证码 验证码 # 后台设置 # 同一个手机号同一个验证码模板,每30秒只能获取1条 # 同一个手机号验证码类内容,每小时最多能获取3条 # 同一个手机号验证码类内容,24小时内最多能获取到10条 # result = SendSMS().send_sms(phone_num, sms_code) # if not result: # # 短信发送失败 # return jsonify(re_code=RET.THIRDERR, msg='发送短信验证码失败') # 6.发送成功,验证码存储到Redis try: redis_conn.set('PhoneCode:' + phone_num, sms_code, constants.SMS_CODE_REDIS_EXPIRES) except Exception as e: current_app.logger.debug(e) return jsonify(re_code=RET.DBERR, msg='存储短信验证码失败') # 响应结果 return jsonify(re_code=RET.OK, msg='验证码发送成功')
def register(): """ 注册 请求参数:手机号、短信验证码、密码、确认密码 参数格式:json :return: """ # 接收参数 req_dict = request.get_json() mobile = req_dict.get('mobile') sms_code = req_dict.get('sms_code') password = req_dict.get('password') password2 = req_dict.get('password2') # 校验参数完整性 if not all([mobile, sms_code, password, password2]): return jsonify(errno=RET.PARAMERR, errmsg='参数不完整') # 判断手机号格式 if not re.match(r"1[34578]\d{9}", mobile): return jsonify(errno=RET.PARAMERR, errmsg="手机号格式错误") # 判断密码 if password != password2: return jsonify(errno=RET.PARAMERR, errmsg="两次密码不一致") # 从redis中取出短信验证码 try: sms_code_redis = redis_conn.get("sms_code_%s" % mobile) except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DBERR, errmsg="读取短信验证码异常") # 判断短信验证码是否过期 if sms_code_redis is None: return jsonify(errno=RET.NODATA, errmsg="短信验证码失效") # 删除redis中的短信验证码,防止重复使用校验 try: redis_conn.delete("sms_code_%s" % mobile) except Exception as e: current_app.logger.error(e) # 判断用户填写短信验证码的正确性 if sms_code_redis != sms_code: return jsonify(errno=RET.DATAERR, errmsg="短信验证码错误") # 将新注册用户保存到数据库 user = User(name=mobile, mobile=mobile) # 设置属性 user.password = password try: # 添加数据 db.session.add(user) # 提交 db.session.commit() except IntegrityError as e: # 数据库操作异常时要回滚 db.session.rollback() # mobile字段出现重复值,表示该手机号已经注册过 current_app.logger.error(e) return jsonify(errno=RET.DATAEXIST, errmsg="手机号已存在") except Exception as e: db.session.rollback() current_app.logger.error(e) return jsonify(errno=RET.DBERR, errmsg="数据库操作异常") # 保存登录状态到session中 session["name"] = mobile session["mobile"] = mobile session["user_id"] = user.id # 返回结果 return jsonify(errno=RET.OK, errmsg="注册成功")
def get_sms_code(mobile): """ 获取短信验证码 Args: mobile: 注册人的手机号 URL Param: image_code_id: 图片验证码id image_code : 图片验证码 Retruns: 正常情况: 发送成功的json信息 错误情况: 异常的json信息 """ # 1. 业务逻辑处理 # 获取短信验证码的过期时间 sms_code_redis_expire = current_app.config.get('SMS_CODE_REDIS_EXPIRE') if sms_code_redis_expire % 60 != 0: return jsonify(errcode=RET.PARAMERR, errmsg='短信验证码过期时间不合法, 需为60的整除数') # 效验参数是否缺失 image_code_id = request.args.get('image_code_id') image_code = request.args.get('image_code') if not all([mobile, image_code_id, image_code]): return jsonify(errcode=RET.PARAMERR, errmsg='参数不全') # 效验手机号是否合法 res = re.match(r"^1[3456789]\d{9}$", mobile) if res is None: return jsonify(errcode=RET.DATAERR, errmsg='非法数据, 不是合法的手机号') # 效验图片验证码是否正确, 从redis中取出真实的验证码, 与用户输入的值对比 try: real_image_code = redis_conn.get('image_code_{}'.format(image_code_id)) except Exception as e: # 记录错误日志 current_app.logger.error(e) return jsonify(errcode=RET.DBERR, errmsg='读取Redis数据库异常') # 判断图片验证码是否过期 if real_image_code is None: return jsonify(errcode=RET.NODATA, errmsg='图片验证码已失效, 请点击更换重试') # 对比验证码 if real_image_code.decode().lower() != image_code.lower(): # 表示用户填写错误 return jsonify(errcode=RET.DATAERR, errmsg='图片验证码错误') # 删除已经使用的验证码, 防止对同一个验证码进行多次验证 try: redis_conn.delete('image_code_{}'.format(image_code_id)) except Exception as e: # 记录错误日志, 这里只是一个逻辑操作, 不要提前返回 current_app.logger.error(e) # 判断用户在短时间内是否发送过验证码, 如60s内, 如果有发送记录, 则在规定时间内不允许发送第二次 try: send_sign = redis_conn.get('send_sms_code_{}'.format(mobile)) except Exception as e: # 记录错误日志 current_app.logger.error(e) else: if send_sign is not None: # 表示用于在短时间内有发送短信的记录, RET.REQERR = "4201" return jsonify(errcode=RET.REQERR, errmsg='请求次数受限, 请于60秒后发送') # 判断手机号是否已存在, 如果不存在, 则生成短信验证码, 发送短信 try: user = User.query.filter_by(mobile=mobile).first() except Exception as e: # 记录错误日志, 这里不能return终止, 因为可能会出现用户信息正确, 但数据库异常的情况, 此时应该让用户继续注册 current_app.logger.error(e) else: if user is not None: # 表示手机号已存在 return jsonify(errcode=RET.DATAEXIST, errmsg='该手机号已经注册') # 设置短信验证码, 并保存在redis中 sms_code = '{0:0>6}'.format(random.randint(0, 999999)) try: # 在redis保存字符串数据 redis_conn.set('sms_code_{}'.format(mobile), sms_code, ex=sms_code_redis_expire) # 保存发送短信的手机号记录, 防止用户在短时间内(如60s)执行重复发送短信的操作 redis_conn.set('send_sms_code_{}'.format(mobile), 'yes', ex=current_app.config.get('SEND_SMS_CODE_INTERVAL')) except Exception as e: # 记录错误日志 current_app.logger.error(e) return jsonify(errcode=RET.DBERR, errmsg='短信验证码保存异常') # 保存短信验证码到redis中: 手机号(key): 验证码(value) 字符串类型 # ccp = CCP() # result = ccp.sendTemplateSMS(mobile, [sms_code, str(sms_code_redis_expire / 60)], '1') # if result == 0: # # 发送成功 # return jsonify(errcode=RET.OK, errmsg='短信发送成功') # else: # return jsonify(errcode=RET.THIRDERR, errmsg='短信发送失败, 第三方错误') # 使用celery进行异步请求, 发送短信 send_sms.delay(mobile, [sms_code, str(sms_code_redis_expire / 60)], '1') # 因为selery是异步操作的, 它的执行不会对这里产生阻塞, 所以我们设想, 只要发送了, 就代表成功了 return jsonify(errcode=RET.OK, errmsg='短信发送成功')
def get_sms_code(mobile): """ 获取短信验证码 :param mobile: :return: """ # 1.获取参数 image_code = request.args.get('image_code') image_code_id = request.args.get('image_code_id') # 参数校验 if not all([image_code, image_code_id]): return jsonify(errno=RET.PARAMERR, errmsg='参数不完整') # 2.校验图片验证码 try: # 从redis中取出图片验证码 image_code_redis = redis_conn.get('image_code_%s' % image_code_id) except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DATAERR, errmsg='redis连接异常') # 判断验证码是否过期 if image_code_redis is None: return jsonify(errno=RET.NODATA, errmsg='验证码失效') # 取完就删掉防止用户多次使用同一个验证码 try: redis_conn.delete('image_code_%s' % image_code_id) except Exception as e: current_app.logger.error(e) # 与用户填写的图片验证码比对 if image_code.lower() != image_code_redis.lower(): return jsonify(errno=RET.DATAERR, errmsg='验证码输入错误') # 3.校验手机号是否已注册 try: user = User.query.filter_by(mobile=mobile).first() except Exception as e: current_app.logger.error(e) else: if user: return jsonify(errno=RET.DATAERR, errmsg='手机号已存在') # 判断该手机号60秒内是否有过操作 try: flag = redis_conn.get(mobile) except Exception as e: current_app.logger.error(e) else: if flag: return jsonify(errno=RET.REQERR, errmsg='请求过于频繁,请稍后重试') # 4.未注册的话就生成短信验证码并保存 sms_code = "%06d" % random.randint(0, 999999) try: # 将验证码保存到redis redis_conn.setex('sms_code_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code) # 同时也保存发送给该手机号的记录,防止用户60s内再次发送发短信请求 redis_conn.setex(mobile, constants.SEND_SMS_CODE_INTERVAL, 1) except Exception as e: current_app.logger.eror(e) return jsonify(errno=RET.DBERR, errmsg='保存验证码失败') # 5.调用接口发送短信 try: ccp = CCP() result = ccp.send_template_sms(mobile, [sms_code, int(constants.SMS_CODE_REDIS_EXPIRES / 60)], 1) except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.THIRDERR, errmsg="发送异常") # 返回值 if result == 0: # 发送成功 return jsonify(errno=RET.OK, errmsg="发送成功") else: return jsonify(errno=RET.THIRDERR, errmsg="发送失败")