Redis bitmap 构建签到功能

在很多时候我们会遇到用户签到的场景,每天用户进入应用时,需要获取用户当天的签到状态,如果没签到,用户可以进行签到,并且得到相关的奖励。我们可能需要每天的签到情况,必要的时候可能还需要统计一下每天用户签到人数。 我们用Redis的Set数据结构可以轻松实现这个功能——以日期为key,以用户ID(对应着数据库的primary id)组成的集合为value,每当需要查询某个用户的签到状态时,只需要使用命令SISMEMBER key member就可以轻易得到想要的结果;用户签到时,使用命令SADD key member把用户ID添加到相应的日期中;统计某天用户的签到人数,可以用命令SCARD key。 以上的做法操作简便,易于理解,但是本篇要介绍的是另一种做法,使用Redis的位操作(bitmap)。 我们都知道数据在机器上存储的最小单元是位(bit),1位可以存储0和1两种状态。这里的场景需要存储正是签到和未签到两种状态,因此一个用户只需要占用1位,也就是用位操作比用集合操作要省很多空间,下面先说一下位操作的方式,最后会给出两种方式的内存占用对比。

//redis签到
    /**
     * @param $date //日期格式 20001229
     * @param $user_id // 用户id
     * @return array //数组
     *  活动按照当天签到那些用户
     */
    public static function ActivitiesIn($date,$user_id){
        if($date==''){
            $times=date('Ymd');
        }else{
            $times=$date;
        }
        $redis_key='login:'.$times;
        $user_ture=Redisd::getInstance()->conn->getBit($redis_key,$user_id);
        if ($user_ture==1){
            $num=Redisd::getInstance()->conn->bitCount($redis_key);
            return ['code'=>200,'msg'=>'已经签到了不需要在签到了','data'=>$num];
        }
        Redisd::getInstance()->conn->setBit($redis_key,$user_id,1);
        $num=Redisd::getInstance()->conn->bitCount($redis_key);
        return ['code'=>200,'msg'=>'签到成功','data'=>$num];
    }

    /**
     * @param $user_id //用户id
     * @param $year_mouth //年月格式 202206
     * @param $day //当月天数
     * @return array //数组
     */
    public static function ASingleSignIn($user_id,$year_mouth,$day)
    {
        $Ymtimes=self::dateYm($year_mouth);
        $times=self::dateTimes($day);
        $redis_key='user:sing:'.$user_id.':'.$Ymtimes;
        $user_ture=Redisd::getInstance()->conn->getBit($redis_key,$times);
        if ($user_ture==1){
            $nums=self::asff($user_id);
            $num=Redisd::getInstance()->conn->bitCount($redis_key);
            return ['code'=>200,'msg'=>'已经签到了不需要在签到了','num'=>$num,'nums'=>$nums];
        }
        Redisd::getInstance()->conn->setBit($redis_key,$times,1);
        $num=Redisd::getInstance()->conn->bitCount($redis_key);
        $nums=self::asff($user_id);
        if ( $nums >= 3 ){
            IntegralService::IntegralIncrease($user_id,30);
        }
        if ( $nums < 3 ){
            IntegralService::IntegralIncrease($user_id,10);
        }
        return ['code'=>200,'msg'=>'签到成功','num'=>$num,'nums'=>$nums];
    }

    /**
     * 获取当前年月
     *
     * 需要参数:$data_time
     */
    public static function dateYm($data_time){

        if (empty($data_time)){
//            如果传入年月为空,返回当前年月
            return date('Ym');
        }
        // 不为空返回输入日期
        return $data_time;
    }

    /**
     * 获取日期 当月的日期 -1 为位图下标
     *
     * @param $data_time
     */
    public static function dateTimes($dated)
    {
        if (!empty($deted)){
//            如果传入日期为空那么就返回当前日期-1
            return  date('d',strtotime("-1 day"));
        }
//        不为空返回输入日期-1
        return $dated-1;
    }


    /**
     * 获取本月当前连续签到天数
     *通过逆循环判断当天连续签到多少天
     * 要判断当天是否签到来返回签到数
     *
     */
    public static function asff($user_id){
        $signCount=0;
//     获取当前年月
        $Ym=date('Ym');
//     获取当天天数
        $Day=date('d',strtotime("-1 day"));
//     拼接kid
        $redis_key='user:sing:'.$user_id.':'.$Ym;
//        逆循环得到当前连续签到天数
        /**
         * 做一个判断
         * 设定当前用户当天是否签到,如果签到了,那么连续签到返回,如果没签到会返回零,所以进行判断
         */
        if ( Redisd::getInstance()->conn->getBit($redis_key,$Day)!=1){
            //如果当天没签到 那么再次往前一天计算
            for ($i=$Day-1;$i>=0;$i--){
                $yue= Redisd::getInstance()->conn->getBit($redis_key,$i);
                if ($yue==1){
                    $signCount=$signCount+1;
                }else{
                    break;
                }
            }
            return $signCount;
        } else{
//         如果当天签到那么返回当天计算
            for ($i=$Day;$i>=0;$i--){
                $yue= Redisd::getInstance()->conn->getBit($redis_key,$i);
                if ($yue==1){
                    $signCount=$signCount+1;
                }else{
                    break;
                }
            }
            return $signCount;
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

舟子121

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值