1. BitMap的常用命令

  1. SETBIT 向指定位置存入0或1
127.0.0.1:6379> SETBIT demo 0 1
(integer) 0
  1. GETBIT 获取指定位置的bit值
127.0.0.1:6379> GETBIT demo 0 
(integer) 1
  1. BITFIELD 操作(修改、查询、自增)BitMap中bit数组中的指定位置的值
127.0.0.1:6379> BITFIELD demo GET u1 0    // BITFIELD key GET type offset -u 无符号 -i有符号
1) (integer) 1
127.0.0.1:6379> BITFIELD demo GET i1 0
1) (integer) -1
  1. BITCOUNT 统计BitMap中值为1的bit位数量
127.0.0.1:6379> BITCOUNT demo 
(integer) 2
  1. BITPOS 查找bit数组中指定范围内第一个0或1出现的位置
127.0.0.1:6379> BITPOS demo 0
(integer) 1

2. 实现用户签到功能

将用户id和年月作为BitMap的key,将签到的记录保存到一个BitMap中,每次签到就修改对应位上的数字(0 -> 1)

@Override  
public Result sign() {  
    // 1. 获取当前用户id  
    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. 写入redis  
    stringRedisTemplate.opsForValue().setBit(key, dayOfMonth-1, true);  
    return Result.ok();  
}

3. 连续签到统计

定义一个计数器,循环让签到数据(一个十进制的数)与1进行与运算,结果为1这表明签到了,让计数器+1,数据需要右移一位。结果为0表明连续签到断掉了,可以结束循环返回计数器结果了。

@Override  
public Result signCount() {  
    // 1. 获取当前用户id  
    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. 获取到截至现在所有的签到记录,返回的结果是十进制数字  
    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);  
    }  
  
    // 循环遍历  
    int count = 0;  
    while (true) {  
        // 与数字1相与,得到数字的最后一位的bit位  
        if ((num & 1) == 0) {  
            break;  
        }else{  
            count++;  
        }  
  
        // 将数字右移一位  
        num >>>= 1;  
    }  
    return Result.ok(count);  
}