使用 BitMap 实现用户月签到,可以通过将用户的签到情况映射到一个二进制位图中,进行高效的存储和查询。BitMap 可以节省存储空间,并且提供快速的 bit 操作。以下是使用 BitMap 实现用户月签到的一般思路:
今日签到实现
使用setBit()对dayOfMonth-1位置设置成1代表签到:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public Result sign() {
Long userId = UserHolder.getUser().getId();
LocalDateTime now = LocalDateTime.now();
String keySuffix = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
String key = "sign:" + userId + keySuffix;
// 获取今天是本月第几天
int dayOfMonth = now.getDayOfMonth();
stringRedisTemplate.opsForValue().setBit(key , dayOfMonth - 1, Boolean.TRUE);
return Result.ok();
}
}
如何计算连续签到天数?
获取本月到今天为止的所有签到数据:BITFELD key GET u[dayOfMonth] 0 (返回十进制数据)
从后向前遍历每个bit位:与1做与运算,就能得到最后一个bit位。随后右移一位可以使原来的最后一个bit位因超出而抛弃,下一个bit位就成为了最后一个bit位。
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public Result signCount() {
Long userId = UserHolder.getUser().getId();
LocalDateTime now = LocalDateTime.now();
String keySuffix = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
String key = "sign:" + userId + keySuffix;
// 获取今天是本月第几天
int dayOfMonth = now.getDayOfMonth();
// 获取本月截至到今天位置的所有签到记录,返回的是一个十进制数字
// BITFIELD sign:5:202502 GET u[?] 0
List<Long> result = stringRedisTemplate.opsForValue().bitField(
key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
); // 返回值是集合是因为可以在bitField()方法内同时做get或者set等多个命令获取多个结果
if(result == null || result.size() == 0){
return Result.ok();
}
// 获取十进制签到天数结果
Long num = result.get(0);
// 使用循环遍历
int count = 0;
while(true){
if((num & 1) == 0){
break; // 为0未签到,结束
} else {
count++; // 不为零则已签到,计数器+1
}
// 把数字右移一位,抛弃最后一个bit位,继续下一个bit位
num >>>= 1;
}
return Result.ok(count);
}
}
通过 Redis 的 BitMap 操作,可以非常高效地存储和查询用户的签到信息。常用的 Redis 命令如 SETBIT
、GETBIT
和 BITCOUNT
可以帮助我们方便地操作用户的签到状态。BitMap 适用于大量数据的高效存储与查询,特别适合这种 "签到" 等具有固定天数、每位表示一天的场景。