用户签到
- BitMap用法
- 签到功能
- 签到统计
BitMap用法:
我们按月来统计用户签到信息,签到记录为1,未签到则记录为0.(布隆过滤器就是采用这种结构)
把每一个bit位对应当月的每一天,形成了映射关系。用0和1标示业务状态,这种思路就称为位图(BitMap)。
Redis是利用string类型数据结构实现BitMap,因此最大上限512M,转换为bit则是2^32个bit位。
BitMap的操作命令有:‘
SETBIT:向指定位置(offset)存入一个0或1
GETBIT:获取指定位置(offset)的bit值
BITCOUNT:统计BitMap中值为1的bit位
BITFIELD:操作(查询,修改,自增)BitMap中bit数组中的指定位置(offset)的值
BITFIELD_RD:获取BitMap中bit数组,并以十进制形式返回
BITOP:将多个BitMap的结果做位运算(与,或,异或)
BITPOS:查找bit数组中指定范围内第一个0或1出现的位置
签到功能
需求:实现签到接口,将当前用户当天签到信息保存到Redis中
提示:因为BitMap底层是基于String数据结构,因此其操作也都封装在字符串相关操作中了。
代码实现
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
stringRedisTemplate.opsForValue().setBit(key,dayOfMonth-1,true);
return Result.ok();
}
统计连续签到
什么叫连续签到天数?
从最后一次签到开始向前统计,直到遇到第一次未签到为止,计算总的签到次数,就是连续签到天数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BTQliNoL-1669736765623)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221129225245656.png)]
如何得到本月到今天为止的所有签到数据?
BITFELD key GET u[dayOfMonth]0
如何从后向前遍历每一个bit位?
与1做运算,就能得到最后一个bit位
随后右移1位,下一个bit为就成为最后一个bit为。
代码实现:
public Result signCount() {
//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.获取本月截止今天为止的所有的签到记录,返回的是一个十进制的数字
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();
}
//6.循环遍历
int count=0;
while (true) {
//6.1让这个数字与1做与运算,得到数字的最后一个bit位//判断这个bit是否为0
if ((num & 1) == 0){
//如果为0,说明未签到,结束
break;
}
//如果不为0,说明已签到,计数器+1
count++;
//把数字右移一位,抛弃最后一个bit位,继续下一个bit位
num=num >>1;
}
return Result.ok(count);
}