签到功能
我们可以采用类似这样的方案来实现我们的签到需求。
我们按月来统计用户签到信息,签到记录为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_RO :获取BitMap中bit数组,并以十进制形式返回
- BITOP :将多个BitMap的结果做位运算(与 、或、异或)
- BITPOS :查找bit数组中指定范围内第一个0或1出现的位置
@Override
public Result sign() {
//1.获取当前登录用户
Long userId = UserHolder.getUser().getId();
//2。获取日期
LocalDateTime now = LocalDateTime.now();
//3.拼接key
String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMMMM"));
String key=USER_SIGN_KEY+userId+keySuffix;
//4.获取今天是本月第几天
int dayOfMonth = now.getDayOfMonth();
//5.写入redis
stringRedisTemplate.opsForValue().setBit(key,dayOfMonth-1,true);
return Result.ok();
}
签到统计
我们可以先获取本月到目前为止的所有签到记录,返回一个十进制数字。我们可以让它与1做与运算,得到该数字的最后一位。判断最后一位是否为0,为0则终止,为1则计数器+1,继续便利。
@Override
public Result signCount() {
//1.获取当前登录用户
Long userId = UserHolder.getUser().getId();
//2。获取日期
LocalDateTime now = LocalDateTime.now();
//3.拼接key
String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMMMM"));
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);
}
//6.循环遍历
int count=0;
Long num = result.get(0);
//7.让这个数字与1做与运算,得到数字最后一个bit位
while (true){
//判断这个bit位是否为0
if ((num & 1)==0) {
//如果为0,未签到,结束
break;
}else {
//如果不为0,说明已经签到,计数器+1
count++;
}
//把数字右移一位,抛弃最后一个bit位,继续下一个bit位
num>>>=1;
}
return Result.ok(count);
}