使用Redis的Bitmap实现了签到功能

思路分析

我们可以把 年和月 作为BitMap的key ,然后保存到一个BitMap中 ,每次签到就到对应的位上把数字从0 变为1,只要是1,就代表是这一天签到了,反之咋没有签到。

关键问题

问题一: 什么叫做连续签到天数?

从最后一次签到开始向前统计,直到遇到第一次未签到为止,计算总的签到次数,就是连续签到天数。

获得当前这个月的最后一次签到数据,定义一个计数器,然后不停的向前统计,直到获得第一个非0的数字即可,每得到一个非0的数字计数器+1,直到遍历完所有的数据,就可以获得当前月的签到总天数了

问题二: 如何得到本月到今天为止的所有签到数据?

java 复制代码
BITFIELD key GET u[dayOfMonth] 0

假设今天是7号,那么我们就可以从当前月的第一天开始,获得到当前这一天的位数,是7号,那么就是7位,去拿这段时间的数据,就能拿到所有的数据了,那么这7天里边签到了多少次呢?统计有多少个1即可。

问题三:如何从后向前遍历每个Bit位?

注意:bitMap返回的数据是10进制,哪假如说返回一个数字8,那么我哪儿知道到底哪些是0,哪些是1呢?

我们只需要让得到的10进制数字和1做与运算就可以了,因为1只有遇见1 才是1,其他数字都是0 ,我们把签到结果和1进行与操作,每与一次,就把签到结果向右移动一位,依次内推,我们就能完成逐个遍历的效果了。

核心代码逻辑

接下来,我们创建一个签到服务来管理用户的签到逻辑。假设用户 ID 是一个字符串,签到日期则用位图的位置来表示。

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

@Service
public class SignInService {

    private static final String SIGN_KEY_PREFIX = "sign:";
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    public SignInService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    // 用户签到方法
    public void sign() {
        //1. 获取登录用户
        Long userId = UserHolder.getUser().getId();
        //2. 获取日期
        LocalDateTime now = LocalDateTime.now();
        //3. 拼接key
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = RedisConstants.USER_SIGN_KEY + userId + keySuffix;
        //4. 获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();
        //5. 写入redis setbit key offset 1
        redisTemplate.opsForValue().setBit(key, dayOfMonth -1, true);
}


    // 获取用户当月签到数据
    public boolean[] getCurrentMonthSignInData(String userId) {
        String key = SIGN_KEY_PREFIX + userId;
        LocalDate today = LocalDate.now();
        int daysInMonth = today.lengthOfMonth();
        boolean[] signData = new boolean[daysInMonth];
        
        for (int day = 0; day < daysInMonth; day++) {
            signData[day] = redisTemplate.opsForValue().getBit(key, day);
        }
        return signData;
    }

    public int signCount() {
        //1. 获取登录用户
        Long userId = UserHolder.getUser().getId();
        //2. 获取日期
        LocalDateTime now = LocalDateTime.now();
        //3. 拼接key
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = RedisConstants.USER_SIGN_KEY + userId + keySuffix;
        //4. 获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();
        //5. 获取本月截至今天为止的所有的签到记录,返回的是一个十进制的数字 BITFIELD sign:5:202301 GET u3 0
        List<Long> result = redisTemplate.opsForValue().bitField(
            key,
            BitFieldSubCommands.create()
            .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        //没有任务签到结果
        if (result == null || result.isEmpty()) {
            return 0;
        }
        Long num = result.get(0);
        if (num == null || num == 0) {
            return 0;
        }
        //6. 循环遍历
        int count = 0;
        while (true) {
            //6.1 让这个数字与1 做与运算,得到数字的最后一个bit位 判断这个数字是否为0
            if ((num & 1) == 0) {
                //如果为0,签到结束
                break;
            } else {
                count ++;
            }
            num >>>= 1;
        }
        return count;
    }


    // 获取用户当月签到总数
    public long getTotalSignInDays(String userId) {
        String key = SIGN_KEY_PREFIX + userId;
        LocalDate today = LocalDate.now();
        // 计算今天是当月的第几天
        int daysInMonth = today.getDayOfMonth();
        return redisTemplate.opsForValue().bitCount(key, 0, daysInMonth - 1);
    }
}
相关推荐
复园电子17 分钟前
企业级PDF批量盖章方案选型指南
数据库·pdf
ffqws_6 小时前
Redis 分布式锁:基于 Redisson 的实现与面试高频问答
redis·分布式·面试
AI-好学者7 小时前
阶段一-图数据库基础与PropertyGraph模型
数据库·rag·knowledge graph·graphrag
其实防守也摸鱼8 小时前
运维--学习阶段问题解答(1)(自测)
linux·运维·服务器·数据库·学习·自动化·命令模式
懒鸟一枚9 小时前
深入理解 Linux 内存、Swap 交换分区与分页机制的关系
java·linux·数据库
龙仔7259 小时前
SQL Server 创建只读账号完整操作(分两种场景:SSMS图形界面 + T-SQL脚本)
数据库·sql·oracle
霁月的小屋9 小时前
生产环境中的事务实践——银行系统上线记(四)
数据库
Database_Cool_9 小时前
AI 应用数据底座首选:阿里云 PolarDB 为大模型 RAG 提供一体化支撑
数据库·阿里云
玖玥拾9 小时前
C# 语言进阶(十五)C# 游戏服务端 MySQL 数据库
服务器·开发语言·网络·数据库·mysql·c#
IvorySQL11 小时前
PG 日报|社区讨论重构 pg_hba 配置文件格式
数据库·人工智能·postgresql·重构·ivorysql