def rewind_last_swipe(uid): '''反悔上一次滑动 (每天允许反悔 3 次,反悔的记录只能是五分钟之内的)''' now = datetime.datetime.now() # 检查今天是否已经反悔 3 次 rewind_key = keys.REWIND_TIMES_K % (now.date(), uid) rewind_times = rds.get(rewind_key, 0) if rewind_times >= config.REWIND_TIMES: raise errors.RewindLimit # 找到最后一次的滑动 # select * from swiped where uid=1001 order by stime desc limit 1; last_swipe = Swiped.objects.filter(uid=uid).latest('stime') # 检查最后一次滑动是否在 5 分钟内 time_past = (now - last_swipe.stime).total_seconds() if time_past >= config.REWIND_TIMEOUT: raise errors.RewindTimeout with atomic(): # 将多次数据修改在事务中执行 if last_swipe.stype in ['like', 'superlike']: # 如果之前匹配成了好友,则删除好友关系 Friend.breakoff(uid, last_swipe.sid) # 如果上一次是超级喜欢,则从对方的优先推荐队列中删除自己的 UID if last_swipe.stype == 'superlike': rds.lrem(keys.FIRST_RCMD_Q % last_swipe.sid, 0, uid) # 删除最后一次的滑动 last_swipe.delete() # 今日反悔次数加一 rds.set(rewind_key, rewind_times + 1, 86460) # 缓存过期时间为一天零60秒
def rewind_last_swipe(uid): '''反悔上一次滑动 (每天允许反悔 3 次, 反悔的记录只能是五分钟之内的)''' now = datetime.datetime.now() # 检查今天是否已经反悔 3 次 rewind_key = keys.REWIND_TIMES_K % (now.date(), uid) rewind_times = rds.get(rewind_key, 0) if rewind_times >= config.REWIND_TIMES: raise errors.RewindLimit # 找到最后一次滑动 last_swipe = Swiped.objects.filter(uid=uid).latest('stime') # 检查最后一次滑动是否在 5 分钟之内 time_past = (now - last_swipe.stime).total_seconds() if time_past >= config.REWIND_TIMEOUT: raise errors.RewindTimeout with atomic(): # 将多次数据修改在事务中执行 if last_swipe.stype in ['like', 'superlike']: # 如果之前匹配成了好友,则删除好友关系 Friend.breakoff(uid, last_swipe.sid) # 如果上一次是超级喜欢,则删除优先推荐队列中的数据 if last_swipe.stype == 'superlike': rds.lrem(keys.FIRST_RCMD_Q % last_swipe.sid, 0, uid) # 撤销被滑动者改变的积分 score = config.SWIPE_SCORE[last_swipe.stype] rds.zincrby(keys.HOT_RANK, -score, last_swipe.sid) # 删除最后一次的滑动 last_swipe.delete() # 今日反悔次数加一 rds.set(rewind_key, rewind_times + 1, 86460) # 缓存过期时间为一天零60秒
def rewind_swipered(user): '''反悔一次滑动记录''' # 获取今天的反悔次数 rewind_times = rds.get(keys.REWIND_KEY % user.id, 0) # 检查今天返回是否达到了反悔上限 if rewind_times >= cfg.DAILY_REWIND: raise stat.RewindLimit # 找到最近的一次滑动记录 latest_swiped = Swiped.objects.filter(uid=user.id).latest('stime') # 检查反悔的记录是否是五分钟之内的 now = datetime.datetime.now() if (now - latest_swiped.stime).total_seconds() >= cfg.REWIND_TIMEOUT: raise stat.RewindTimeout # 检查上一次滑动是否有可能匹配了好友关系 if latest_swiped.stype in ['like', 'superlike']: # 删除好友关系 Friend.break_off(user.id, latest_swiped.sid) # 如果上一次是超级喜欢 将自身的uid从对方的优先推荐队列里面删除 if latest_swiped.stype == 'superlike': rds.zrem(keys.SUPERLIKED_KEY % latest_swiped.sid, user.id) # 删除滑动记录 latest_swiped.delete() # 更新当天的滑动词数,同时设置过期时间到下一个凌晨 next_zero = datetime.datetime(now.year, now.month, now.day) + datetime.timedelta(1) remain_seconds = (next_zero - now).total_seconds() rds.set(keys.REWIND_KEY % user.id, rewind_times + 1, int(remain_seconds))
def send_sms(mobile): '''发送短信验证码''' key = keys.VCODE_K % mobile # 检查短信发送状态,防止短时间内给用户重复发送短信 if rds.get(key): return True # 之前发送过验证码,直接返回 True vcode = gen_rand_code() # 产生验证码 inf_log.debug('验证码: %s' % vcode) args = conf.YZX_SMS_ARGS.copy() # 原型模式 args["param"] = vcode args["mobile"] = mobile response = requests.post(conf.YZX_SMS_API, json=args) if response.status_code == 200: result = response.json() inf_log.debug('短信发送状态: %s' % result.get('msg')) if result.get('code') == '000000': rds.set(key, vcode, 600) return True else: return False return False
def get_or_create(cls, defaults=None, **kwargs): """ Look up an object with the given kwargs, creating one if necessary. Return a tuple of (object, created), where created is a boolean specifying whether an object was created. """ # 检查参数中是否有 主键 if 'id' in kwargs or 'pk' in kwargs: # 创建缓存 key pk = kwargs.get('id') or kwargs.get('pk') model_key = MODEL_KEY % (cls.__name__, pk) # 先从缓存获取数据 model_obj = rds.get(model_key) if model_obj is not None and isinstance(model_obj, cls): return model_obj, False # 缓存中未取到,直接从数据库获取 model_obj, created = cls.objects.get_or_create(defaults, **kwargs) # 将数据库取出的数据写入缓存 model_key = MODEL_KEY % (cls.__name__, model_obj.pk) rds.set(model_key, model_obj, ex=86400 * 7) # 设置缓存过期时间为 一周 return model_obj, created
def rewind_swiper(uid): """ 反悔一次滑动 每天允许反悔3次,反悔记录只能是5分子之内的 """ now = datetime.datetime.now() # 取出当前时间 # 取出当天的反悔次数 rewind_k = keys.REWIND_k % (now.date(), uid) rewind_times = rds.get(rewind_k, 0) # 取出当天反悔次数 取不到的时候默认为0 # 检查当前反悔次数 if rewind_times >= config.REWIND_TIMES: raise stat.RewindLimit # 取出最后一次滑动记录 latest_swipe = Swiped.objects.filter(uid=uid).latest('time') # 检查滑动记录时间是否超过5分钟 pass_time = now - latest_swipe.time if pass_time.total_seconds() >= config.REWIND_TIMEOUT: raise stat.RewindTimeout # 如果是超级喜欢,需要将自己从对方的优先队列中删除 # 如果之前是喜欢或者超级喜欢,需要撤销好友关系 if latest_swipe.stype == 'superlike': rds.lrem(keys.FIRST_RCMD_K % latest_swipe.sid, 1, uid) Friend.break_off(uid, latest_swipe.sid) elif latest_swipe.stype == 'like': Friend.break_off(uid, latest_swipe.sid) # 删除滑动记录 latest_swipe.delete() # 更新反悔次数 rds.set(rewind_k, rewind_times + 1, 86400)
def save_with_cache(self, force_insert=False, force_update=False, using=None, update_fields=None): """ Saves the current instance. Override this in a subclass if you want to control the saving process. The 'force_insert' and 'force_update' parameters can be used to insist that the "save" must be an SQL insert or update (or equivalent for non-SQL backends), respectively. Normally, they should not be set. """ # Ensure that a model instance without a PK hasn't been assigned to # a ForeignKey or OneToOneField on this model. If the field is # nullable, allowing the save() would result in silent data loss. # 执行原来的save方法 # 将数据存到数据库里面 result = self._save(force_insert, force_update, using, update_fields) # print("已经保存到数据库") # 将数据存到缓存里面 key = MODEL_KEY % (self.__class__.__name__, self.pk) rds.set(key, self) # print("已经保存到缓存") return result
def set_profile(request): '''修改个人资料''' user_form = UserForm(request.POST) profile_form = ProfileForm(request.POST) # 检查 User 的数据 if not user_form.is_valid(): raise stat.UserDataErrr(user_form.errors) # 检查 Profile 的数据 if not profile_form.is_valid(): raise stat.ProfileDataErrr(profile_form.errors) user = request.user # 保存用户的数据 user.__dict__.update(user_form.cleaned_data) user.save() # 保存交友资料的数据 user.profile.__dict__.update(profile_form.cleaned_data) user.profile.save() # 修改缓存 key = keys.PROFILE_KEY % request.user.id rds.set(key, user.profile.to_dict()) return render_json()
def rewind_last_swiped(user_id): ''' 撤销最后⼀次滑动,每天允许反悔三次,反悔时间距上一次滑动时间不超过5分钟''' now = datetime.datetime.now() rewind_key = keys.REWIND_TIMES_K % (now.date(), user_id) # 1检查今天的反悔次数是否超过三次 rewind_times = rds.get(rewind_key, 0) if rewind_times > config.REWIND_TIMES: raise errors.RewindLimit() # 2找到最后一次滑动 last_swpied = Swiped.objects.filter(user_id=user_id).latest('stime') # 3检查最后一次滑动时间距离现在是否在5分钟内 past_time = (now - last_swpied.stime).total_seconds() if past_time > config.REWIND_TIMEOUT: raise errors.RewindTimeout() with atomic(): # 4删除好友关系(只要上一次滑动类型是喜欢或超级喜欢,都有可能匹配为好友) if last_swpied.stype in ['like', 'superlike']: Friend.remove_relation(user_id, last_swpied.sid) # 5如果最后一次滑动是超级喜欢删除自己在对方的优先推荐列表 if last_swpied.stype == 'superlike': rds.lrem(keys.PRIOR_RCMD_LIST % last_swpied.sid, value=user_id) # 6删除滑动记录 last_swpied.delete() # 今日反悔次数加一 rds.set(rewind_key, rewind_times + 1, 86500) # 缓存过期时间为一天零100秒 '''撤回最后一次滑动所改变的对方的积分,''' rds.zincrby(keys.HOT_RANK, last_swpied.sid, -config.RANK_SCORE[last_swpied.stype])
def get_profile(request): '''获取用户配置''' key = keys.MODEL_K % (Profile.__name__, request.uid) profile = rds.get(key) # 先从缓存获取数据 if profile is None: profile, _ = Profile.objects.get_or_create(id=request.uid) # 缓存中没有,直接从数据库获取 rds.set(key, profile) # 将 profile 存入缓存 return render_json(profile.to_dict())
def save(self, force_insert=False, force_update=False, using=None, update_fields=None): '''带缓存处理的 save方法''' # 调用原save将数据保存到数据库 self._save(force_insert, force_update, using, update_fields) # 将 model 对象写入缓存 model_key = keys.MODEL_K %(self.__class__.__name__, self.pk) rds.set(model_key, self)
def save(self, force_insert=False, force_update=False, using=None, update_fields=None): """自定义 save 处理缓存""" # 调用原方法将数据保存至数据库 self._save(force_insert, force_update, using, update_fields) # 将数据保存至缓存中 model_key = MODEL_K % (self.__class__.__name__, self.pk) rds.set(model_key, self)
def save(self, force_insert=False, force_update=False, using=None, update_fields=None): # 使用原 save() 函数将数据写入数据库 self._save(force_insert, force_update, using, update_fields) # 将 model_obj 保存到缓存 key = MODEL_KEY % (self.__class__.__name__, self.pk) rds.set(key, self)
def save(self, force_insert=False, force_update=False, using=None, update_fields=None): # 使用原来save函数写入数据库 self._save(force_insert, force_update, using, update_fields) # 将Model对象保存至缓存 # self 相当于user,获取类名 key = MODEL_KEY % (self.__class__.__name__, self.pk) rds.set(key, self)
def send_code(phonenum): '''给用户发送验证码''' if not is_phonenum(phonenum): return False key = keys.VCODE_K + str(f'{phonenum}') if rds.get(key): return True vcode = make_code() info_log.debug(f'验证码:{phonenum}-{vcode}') rds.set(key, vcode, 600) return send_msg(phonenum, vcode)
def get_profile(request: HttpRequest): '''获取用户资料''' # request对象,新增user属性 # 考虑性能问题:request.user.profile都是需要从磁盘中进行读取 key = keys.PROFILE_KEY % request.user.id profile_data = rds.get(key) if profile_data is None: # 缓存与数据库的操作 profile_data = request.user.profile.to_dict() # 将取出的数据添加至缓存 rds.set(key, profile_data) return render_json(data=profile_data, code=OK)
def get_profile(request): '''获取个人资料''' key = keys.PROFILE_KEY % request.uid result = rds.get(key) print('从缓存获取:%s' % result) if result is None: profile, _ = Profile.objects.get_or_create(id=request.uid) result = profile.to_dict() rds.set(key, result, 1000) # 将数据写入到缓存中 return render_json(result)
def get_profile(request): '''获取个人资料''' key = keys.PROFILE_KEY % request.user.id profile_data = rds.get(key) print('先从Redis缓存获取数据: %s' % profile_data) if profile_data is None: profile_data = request.user.profile.to_dict() print('缓存中没有,从数据库获取: %s' % profile_data) rds.set(key, profile_data) print('将取出的数据添加到缓存') return render_json(profile_data)
def rewind_last_slide(uid): '''返回上一次滑动(每天允许返回三次,返回的记录只能是五分钟以内的)''' now = datetime.datetime.now() # 检查今天是否已经反悔3次 ---> 适合缓存 rewind_key = keys.REWIND_LIMIT_K % (now.date(), uid) # now.date() 是日期 rewind_limit = rds.get(rewind_key, 0) ''' 每日更新三次 # 错误方式 1.过期时间 2.定时任务 ---> 人数多了,异步任务太多处理不了 3.建立redis库 ---> 保存key,到时间清空,在清空的时候也会出现临界点问题 # 正确方式 将日期拼接在用户的key上面 ※时间临界点: 23:59:59.123 使用了2次反悔,现在要准备用第三次 当用了之后时间变成 00:00:00.321了,此时变成了3 用户一天都不能用返回了,这就是临界点可能出现的问题 ''' if rewind_limit >= config.REWIND_LIMIT: raise errors.RewindLimit # 找当最后一次的滑动 # 对应的SQL语句 select * from slider where uid=1001 order by stime desc limit 1; last_slide = Slider.objects.filter(uid=uid).latest('stime') # 不分对方和方向 # latest() ---> 最新的(按照时间最新的),这个latest强调时间 # 检查最后一次滑动是否在5分钟以内 # (now - last_slide.stime)两个相减的值是datetime.timedelta(天,秒,毫秒),是一个特殊的对象 # (now - last_slide.stime).seconds 忽略了天和毫秒,单位是秒 # (now - last_slide.stime).total_seconds() 天秒毫秒全加起来,单位是秒 time_past = (now - last_slide.stime).total_seconds() # 已经过去的时间 if time_past >= config.REWIND_TIMEOUT: raise errors.RewindTimeout with atomic(): # 将多次数据修改在事务中执行 # 衍生的功能 + # ↓ # 1.如果之前匹配成了好友,则删除好友关系 if last_slide.stype in ['like', 'superlike']: Friend.breakoff(uid, last_slide.sid) # 2.如果之前是超级喜欢,则找到对方的优先推荐队列把我的数据删除 if last_slide.stype == 'superlike': rds.lrem(keys.FIRST_RCMD_Q % last_slide.sid, 0, uid) # 3.删除最后一次的滑动 last_slide.delete() # 4.今日返回次数加一,缓存过期时间一天(86400秒),+ N秒 是为了避免时间临界点问题 rds.set(rewind_key, rewind_limit + 1, 86400 + 1)
def send_vcode(phonenum): if not is_phonenum(phonenum): return False key = keys.VCODE_K % phonenum if rds.get(key): return True # 产生验证码 vcode = random_code() print('随机码:', vcode) inf_log.debug(f'验证码:{phonenum}-{vcode}') rds.set(key, vcode, 600)
def save(self, force_insert=False, force_update=False, using=None, update_fields=None): """ Save the current instance. Override this in a subclass if you want to control the saving process. The 'force_insert' and 'force_update' parameters can be used to insist that the "save" must be an SQL insert or update (or equivalent for non-SQL backends), respectively. Normally, they should not be set. """ # 调用原 save 方法将数据保存到 数据库 self._save(force_insert, force_update, using, update_fields) # 将对象保存到 缓存 key = MODEL_K % (self.__class__.__name__, self.pk) rds.set(key, self)
def show_profile(request): """查看个人资料""" uid = request.session['uid'] key = keys.PROFILE_K % uid profile = rds.get(key) inf_log.debug(f'从数据库中获取数据:{profile}') if profile is None: profile, _ = Profile.objects.get_or_create(id=uid) inf_log.debug(f'从数据库中获取数据:{profile}') rds.set(key, profile) inf_log.debug('将数据写入到缓存') return render_json(profile.to_dict())
def send_vcode(phone): if not is_phonenum(phone): return False key = keys.VCODE_K % phone # 检查缓存是否存在,防止在有效时间内频繁发送验证码 if rds.get(key): return True # 手机号正确,发送验证码 code = make_code(6) inf_log.debug(f'验证码:{phone}-{code}') # 设置缓存,设置验证码的有效时间 rds.set(key, code, 600) return send_sms(phone, code)
def show_profile(request): uid = request.session.get('uid') key = keys.PROFILE_K % uid # 先从缓存获取数据,没有就冲数据库获取,然后存在缓存里面 profile = rds.get(key) inf_log.debug(f'从缓存中获取数据: {profile}') if profile is None: profile, _ = Profile.objects.get_or_create(id=uid) inf_log.debug(f'从数据库中获取数据: {profile}') rds.set(key, profile) inf_log.debug('将数据写入到缓存') return render_json(data=profile.to_dict())
def rewind_swiped(user): ''' 1.获取反悔次数 ---存储位置 2.返回一次滑动记录 --- 找到最近一次的滑动记录;[返回的记录只能为5min之内] 3.每天允许反悔3次 --- 需要对当天返回次数进行记录;并作出检查确定哪一次是最近一次; 4.返回记录只能是5min以内 --- 找到最近的滑动记录,检查反悔记录是否为5mins以内,判断当前时间与滑动时的时间操作; 5.检查上一次滑动是否匹配为好友,如果是,则需要先删除好友记录; 6.如果上一次是超级喜欢,将自身uid从对方的右滑推荐队列中删除 7.删除滑动记录; --- 需要更新滑动记录 ''' # 获取今天的反悔次数 # 默认0次反悔次数,参数最好写入配置文件; rewind_times = rds.get(keys.REWWIND_KEY % user.id, 0) # 检查是否达到限制次数 if rewind_times >= cfg.DAILY_REWIND: raise stat.RewindLimit # 根据时间找到最近的一次的滑动记录:filter的内容无顺序; # 取最近的一次滑动的方法:last(),latest()对比区别 latest_swiped = Swiper.objects.filter(uid=user.id).latest('stime') # 检查返回记录在五分钟以内:当前时间与滑动时间的差值 now = datetime.datetime.now() if (now - latest_swiped.stime).total_seconds() >= cfg.REWIND_TIMEOUT: raise stat.RewindTimeout # 检查上一次滑动是否匹配成好友 if latest_swiped.stype in ['like', 'superlike']: # 如果是好友,删除好友关系 Friend.break_off(user.id, latest_swiped.sid) # 如果上一次超级喜欢,将自身uid从对方的优先推荐队列中删除 if latest_swiped.stype == 'superlike': rds.zrem(keys.SUPERLIKED_KEY % latest_swiped.sid, user.id) # 如果反悔的话,需要还原用户的滑动积分 score = -cfg.SWIPE_SCORE[latest_swiped.stype] # 查找反悔的滑动积分 rds.zincrby(keys.HOT_RANK_KEY, score, latest_swiped.sid) # 删除滑动记录 latest_swiped.delete() # 更新当天的滑动次数 rds.set(keys.REWWIND_KEY % user.id, rewind_times + 1) # 更新过期过期时间(次日零点过期),需要计算过期时间,由于date在月末临界点,不够安全,采用timedelta() next_zero = datetime.datetime(now.year, now.month, now.day) + datetime.timedelta() remain_seconds = next_zero - now rds.set(keys.REWWIND_KEY % user.id, rewind_times + 1, remain_seconds)
def get(self, *args, **kwargs): """自定义 get 方法处理缓存""" model_cls_name = self.model.__name__ # 模型的类名 pk = kwargs.get('id') or kwargs.get('pk') if pk is not None: model_key = MODEL_K % (model_cls_name, pk) model_obj = rds.get(model_key) if isinstance(model_obj, self.model): return model_obj # 如果缓存里面找不到,再到数据库中查找,然后写入缓存 model_obj = self._get(*args, **kwargs) model_key = MODEL_K % (model_cls_name, model_obj.pk) rds.set(model_key, model_obj) return model_obj
def get(self, *args, **kwargs): '''带缓存处理的 objects.get() 方法''' # 从缓存获取数据 pk = kwargs.get('pk') or kwargs.get('id') if pk is not None: key = MODEL_KEY % (self.model.__name__, pk) model_obj = rds.get(key) if isinstance(model_obj, self.model): return model_obj # 缓存中没有,从数据库获取 model_obj = self._get(*args, **kwargs) # 将取出的数据写入缓存 key = MODEL_KEY % (self.model.__name__, model_obj.pk) rds.set(key, model_obj) return model_obj
def send_vcode(phonenum): '''给用户发送短信验证码''' # 验证手机号 if not is_phonenum(phonenum): return False key = keys.VCODE_K % phonenum # 检查缓存中是否已有验证码,防止用户频繁调用接口 if rds.get(key): return True # 产生验证码 vcode = random_code() print('随机码:', vcode) rds.set(key, vcode, 600) # 将验证码添加到缓存,并多设置一些时间 return send_sms(phonenum, vcode) # 向用户手机发送验证码
def save(self, force_insert=False, force_update=False, using=None, update_fields=None): """ Save the current instance. Override this in a subclass if you want to control the saving process. The 'force_insert' and 'force_update' parameters can be used to insist that the "save" must be an SQL insert or update (or equivalent for non-SQL backends), respectively. Normally, they should not be set. """ '''调用原来的save正常将对象保存进数据库里''' self._save(force_insert=False, force_update=False, using=None, update_fields=None) '''将刚刚保存的对象加入到缓存''' print('数据保存好了') key = MODEL_K % (self.__class__.__name__, self.pk) rds.set(key, self) print('数据保存好了,我把对象加到缓存里')
def show_profile(request): '''查看个人资料、交友资料''' key = keys.USER_RPROFILE_K % request.uid # 先从缓存获取数据 result = rds.get(key, {}) inflog.debug('从缓存获取: %s' % result) # 判断 result 是否是空值,如果为空,则从数据库获取数据 if not result: user = User.objects.get(id=request.uid) # 从数据库获取 user result.update(user.to_dict()) result.update(user.profile.to_dict()) # 从数据库获取 profile inflog.debug('从数据库获取: %s' % result) rds.set(key, result) # 将结果保存到 缓存 inflog.debug('将数据写入缓存') return render_json(result)