黑马点评-用户签到

假设我们用一个数据库表来存储用户的签到信息

所以,我们采取01的方式存储用户的签到信息

这样一个用户一个月的签到情况就可以用一个最多31bit,3字节的的二进制串表示,高效还节省空间

而这种每一个bit对应当月中的每一天,形成的映射关系,用0、1标示业务状态,叫做位图(BitMap),所以redis中利用String类型数据结构实现BitMap,存储的最大上限是512M。也就是2^32bit

BitMap相关命令

假设我们在第1、2、3、4、7、8天分别签到会得到一个二进制的字符串 ,中间没有签到的天数默认为0

基于BItMap的签到功能

由于签到功能的实现只牵扯当前用户和一个二进制字符串,所以这个方式实现的是无参的,只需要获取当前登陆用户的id,并记录签到即可,另外由于BitMap是基于redis的String数据结构,所以被封装在opsforValue方法中

    @Override
    public Result sign() {
        // 1.获取当前登录用户
        Long userId = UserHolder.getUser().getId();
        // 2.获取日期
        LocalDateTime now = LocalDateTime.now();
        // 3.拼接key,先将日期格式化在拼接
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyy-MM"));
        String key = USER_SIGN_KEY + userId + keySuffix;
        // 4.获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();
        // 5.写入Redis SETBIT key offset 1,因为二进制是从0开始,而dayofMonth是从1开始计算,所以要dayOfMonth - 1,true表示1
        stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
        return Result.ok();
    }

用户点击签到过后,redis中会自动存储1,今天是14号

为什么会有16bit?

因为这是底层的扩容逻辑,不足1字节,自动用0补全1字节

签到统计

问题一:统一连续签到天数?

从最后一次签到开始向前统计,直至遇到第一次未签到位置,计算总签到次数

问题二:如果获得一个月的总签到天数?

BITFIELD key GET u[dayofmonth] 0   计算从0的角标(即一个月的第一天,一个二进制串的第0位)到dayofmonth(今天)的1的数量

问题三:如何从后向前遍历bit位?

与1做与运算(1和任何数做与运算都是这个数本身),就能得到最后一个bit位,随后让字符串右移,下一个bit位成为最后一个bit位

num在代码中的作用:是通过 Redis 的 BITFIELD 命令获取的无符号整数,通过 num & 1 操作检查最后一位bit位,判断当前处理的这一天是否签到。

在计算机中,​所有数据(包括整数)在底层都是以二进制形式存储和运算的。因此,在代码中虽然看到的是十进制的 num(例如 731 等),但 ​Java 的位运算(如 &>>>)是直接操作其二进制位的,无需显式转换。

    @Override
    public Result signCount() {
        // 1.获取当前登录用户
        Long userId = UserHolder.getUser().getId();
        // 2.获取日期
        LocalDateTime now = LocalDateTime.now();
        // 3.拼接key
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = USER_SIGN_KEY + userId + keySuffix;
        // 4.获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();
        // 5.获取本月截止今天为止的所有的签到记录,返回的是一个十进制的数字
// BITFIELD sign:5:202203 GET u14 0,u14表示无符号的14bit位进行比较,因为今天是14号
        List<Long> result = stringRedisTemplate.opsForValue().bitField(
                key,
                BitFieldSubCommands.create()
                        .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
        );
        if (result == null || result.isEmpty()) {
            // 没有任何签到结果
            return Result.ok(0);
        }
        Long num = result.get(0);
        if (num == null || num == 0) {
            return Result.ok(0);
        }
        // 6.循环遍历
        int count = 0;
        while (true) {
            // 6.1.让这个数字与1做与运算,得到数字的最后一个bit位  // 判断这个bit位是否为0
            if ((num & 1) == 0) {
                // 如果为0,说明未签到,结束
                break;
            }else {
                // 如果不为0,说明已签到,计数器+1
                count++;
            }
            // 把数字右移一位,抛弃最后一个bit位,继续下一个bit位
//现右移一位,在赋值给num
            num >>>= 1;
        }
        return Result.ok(count);
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值