Django实现用户活跃状态动态检测(用户在线状态展示)

  • 前言:在特定的场景下,需要实现用户的活跃状态检测,如(在线,今日在线,近一周,近两周…),在接到这个需求之后,讨论过很多方案,其中最多提到的就是按照session的失效时间来做,但是,在后来的讨论中,发现在项目中,session的失效时间一般不能设置太短比如两小时,这样是不符合实际场景的,我们项目要求失效时间为24小时,因此如果想要实现动态的检测用户活跃状态就不能满足了。
    最后在问过群友后,万能的群友给去了一个很好的解决方案:
    在这里插入图片描述
    本次的解决方案也是依托这个思想来进行,方案如下:
  • [1] 数据库部分:在User表中新增用户在线状态字段在线状态更新时间字段,并且写一个获取用户状态的方法(因为此需求的难点在与当天在线当前在线的检测,而该方式是实现除当前在线之外的状态的检测)。
 # 新增字段
 class PersonalInfo(models.Model):
    id = models.OneToOneField(User, primary_key=True,verbose_name='选择用户', on_delete=models.CASCADE)
  	online_status = models.IntegerField(null=True, blank=True, verbose_name='在线状态', choices=ONLINE_STATUS)
 	online_time = models.DateTimeField(null=True, blank=True, verbose_name='在线状态更新时间')
 
 ONLINE_STATUS = (
    (0, "当前在线"),
    (1, "今日在线"),
    (2, "近一周"),
    (3, "近两周"),
    (4, "近一个月"),
    (5, "近两个月"),
    (6, "近三个月"),
    (7, "近六个月"),
    (8, "六个月以前"),
 )
 # 新增方法
 	def active_time(self):
      # 计算活跃时间
      try:
      	  # 因为在Django自带的User表中有最后登录时间(last_login)这个字段,因此,可以通过计算当前时间与最后登录时间的差来计算用户的在线状态(除当前在线)
          at = self.id.last_login		# 获取最后登录时间
          ds = datetime.datetime.now() - at		# 获取时间差
          year = int(time.strftime('%Y', time.localtime()))			# 获取当前年份
          month = int(time.strftime('%m', time.localtime()))		# 获取当前月份
          days = calendar.monthrange(year, month)[1]	# 计算当月的天数
          days_1 = calendar.monthrange(year - 1 if month == 1 else year, 12 if month == 1 else month - 1)[1]	# 1个月前
          days_2 = calendar.monthrange(year - 1 if month <= 2 else year, 12 if month <= 2 else month - 1)[1]	# 2个月前
          days_3 = calendar.monthrange(year - 1 if month <= 3 else year, 12 if month <= 3 else month - 1)[1]	# 3个月前
          days_4 = calendar.monthrange(year - 1 if month <= 4 else year, 12 if month <= 4 else month - 1)[1]	# 4个月前
          days_5 = calendar.monthrange(year - 1 if month <= 5 else year, 12 if month <= 5 else month - 1)[1]	# 5个月前
          if ds.days <= 0:
              return 1  # 当天
          elif ds.days <= 7:
              return 2  # 一周
          elif ds.days <= 14:
              return 3  # 两周
          elif ds.days <= days:
              return 4  # 近一个月
          elif ds.days <= (days + days_1):
              return 5  # 近两个月
          elif ds.days <= (days + days_1 + days_2):
              return 6  # 近三个月
          elif ds.days <= (days + days_1 + days_2 + days_3 + days_4 + days_5):
              return 7  # 近六个月
          else:
              return 8  # 六个月以上
      except Exception as e:
          raise e
  • [2] 用户本身的状态获取,采取固定时间Say Hi模式,而这个定时任务由前端来发起,如果在服务器端设置定时任务,权衡利弊后发现实现起来很耗资源。
class OnlineStatus(APIView):
    def get(self, request):
        # 当前用户的心跳检测(定时由前端做)
        '''
        校验session(封装过的):
        session_dict = session_exist(request)
        if session_dict["code"] is 0:
            return JsonResponse(session_dict, safe=False, json_dumps_params={'ensure_ascii': False})
        '''
        session_key = request.META.get("HTTP_AUTHORIZATION")
        session = Session.objects.get(session_key=session_key)
        uid = session.get_decoded().get('_auth_user_id')
        back_dir = dict(code=200, msg="", data=dict())
        try:
            user = User.objects.get(id=uid)
            p1 = PersonalInfo.objects.filter(id=user.id).exists()
            now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
            # 这里判断是为了PersonInfo表数据初始化
            if p1:
            	# 设置用户在线为当前在线
                pi = PersonalInfo.objects.get(id=user.id)
                pi.online_status = 0
                pi.online_time = now
                pi.save()
            else:
            	# 创建数据,设置用户在线为当前在线,因为这是在心跳检测,所以直接设置
                PersonalInfo.objects.create(id=user, online_status=0, online_time=now)
        except Exception as e:
            back_dir['msg'] = str(e)
        return Response(back_dir)
  • [3] 用户获取好友活跃状态,为了防止用户心跳检测之后时间超出没有再次检测从而没有改变在线状态,因此需要先调用刚刚自定义active_time方法,检测用户是否是当前在线状态,(如果是则要判断用户距离上次心跳检测时间是否超过预期时间,如果是则****修改为当天在线,如果否则直接返回当前在线状态如果当前状态为非当天在线直接,修改用户状态,返回检测结果
 # 反序列化器
 class OnlineStatusSerializer(serializers.Serializer):
    user_id = serializers.IntegerField(required=True, help_text='用户id')


 def post(self, request):
     """
     检测其它用户的活跃状态
     ONLINE_STATUS = (
         (0, "当前在线"),
         (1, "今日在线"),
         (2, "近一周"),
         (3, "近两周"),
         (4, "近一个月"),
         (5, "近两个月"),
         (6, "近三个月"),
         (7, "近六个月"),
         (8, "六个月以前"),
     )
     """
     session_dict = session_exist(request)
     '''
     if session_dict["code"] is 0:
         return JsonResponse(session_dict, safe=False, json_dumps_params={'ensure_ascii': False})
     '''
     data = request.data
     back_dir = dict(code=200, msg="", data=dict())
     # drf参数校验
     src = OnlineStatusSerializer(data=data)
     bool = src.is_valid()
     now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
     if bool:
         try:
             p1 = PersonalInfo.objects.filter(id=data['user_id']).first()
             # PersonInfo可能未同步,因此检测一下
             if p1:
                 # 检测一下用户现在应该是是状态
                 sta = p1.active_time()
                 # 如果是当天在线,则再判断是否是当前在线
                 if sta <= 1:
                     ds = datetime.datetime.now() - p1.id.last_login
                     # 如果心跳检测时间超过规定检测时间则说明用户已经下线
                     if ds.seconds >= ONLINE_STATUS_CHECK_TIME * 60:
                         p1.online_status = 1
                         p1.online_time = now
                         p1.save()
                     else:
                         p1.online_status = 0
                         # 这里不改变时间,应该这里应该就是0 ,之所以修改完全是为了加一层保证
                         p1.save()
                 # 如果超过一天在线则不管是否是当前在线
                 else:
                     # 可能心跳检测完后没有再此按照时间判断是说明状态,因此要改变
                     p1.online_status = sta
                     p1.online_time = now
                     p1.save()
             else:
                 u1 = User.objects.filter(id=data['user_id']).first()
                 p1 = PersonalInfo.objects.create(id=u1)
                 p1.online_status = p1.active_time()
                 p1.save()
             back_dir['data'] = p1.online_status
         except Exception as e:
             back_dir['msg'] = str(e)
     else:
         back_dir['msg'] = bool.errors
     return Response(back_dir)

END!
【文章编写不易,如需转发请联系作者!】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值