「黑马点评」十一、用户签到

22 天前
1

「黑马点评」十一、用户签到

BitMap 用法

如果使用数据库记录用户签到信息,每条签到记录一次,数据量将会极其庞大,占用损耗都无法接受,所以可以利用二进制压缩,签到记录为 1,未签到记录为 0

image.png|500

image.png|500

位图 BitMap,利用 0 和 1 标示业务状态,位映射到具体含义,这里的场景就是某一位对应了现实某一天

image.png|500

image.png|500

存入 0 或 1SETBIT key offset value 查询 GETBIT key offset value 共有多少 1 BITCOUNT key 从 offset 开始查 count 并返回十进制 BITFIELD key GET [u无符号/i带符号]count offset 查找第一个 0 或 1 出现的位置BITPOS key [0/1]

注:BitMap 底层基于 Redis String,所以操作可以在字符串的操作中找到,即 bitField、getBit、setBit

签到功能

实现签到:POST /api/user/sign

@GetMapping("/sign")
public Result<String> sign(){
    return userService.sign();
}

@Override
public Result<String> sign() {
    Long userId = UserHolder.getUser().getId();
    LocalDateTime now = LocalDateTime.now();
    String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
    String key = RedisConstants.USER_SIGN_KEY + userId + keySuffix;
    int dayOfMonth = now.getDayOfMonth();
    stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
    return Result.success("签到成功");
}

签到统计

连续签到天数:从最后一次向前统计,直到遇见第一次未签到成功,计算总的签到次数,也就是最长右 1 序列

拿到本月到今天为止的所有签到数据 BITFIELD key u[dayOfMonth] 0

如何从右开始遍历每个 bit 位:与 1 位运算可以得到最右的 bit 位,随后右移 1 位

统计用户当前用户截止当前时间在本月的连续签到天数:GET /api/user/sign/count

@GetMapping("/sign/count")
public Result<Integer> signCount(){
    return userService.signCount();
}

@Override
public Result<Integer> signCount() {
    Long userId = UserHolder.getUser().getId();
    LocalDateTime now = LocalDateTime.now();
    String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
    String key = RedisConstants.USER_SIGN_KEY + userId + keySuffix;
    int dayOfMonth = now.getDayOfMonth();
    List<Long> bitField = stringRedisTemplate.opsForValue().bitField(
        key,
        BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
    );
    if (bitField == null || bitField.isEmpty()) {
        return Result.success(0);
    }
    Long bitValue = bitField.get(0); // 获取当前查询出来的 bitmap 的十进制
    if (bitValue == null || bitValue == 0) {
        return Result.success(0);
    }
    int count = 0;
    while (true) {
        if ((bitValue & 1) == 0) {
            break;
        }
        count++;
        bitValue >>>= 1; // >>> 无符号右移,高位补 0; >> 有符号右移,高位补符号位
    }
    return Result.success(count);
}

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...