【java】redis的bitmap实现签到功能

功能:

1.签到

2.判断某天是否签到

3.统计某月的签到情况

4.统计某月连续签到了多少天

5.统计某月一共签到了多少次

代码

1.依赖

复制代码
<dependencies>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>4.0.1</version>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.18</version>
    </dependency>
</dependencies>

2.用户签到

复制代码
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import redis.clients.jedis.Jedis;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class UserSign {

    Jedis jedis;

    public UserSign(Jedis jedis) {
        this.jedis = jedis;
    }

    public String buildKey(int userId, Date date) {
        return Constant.SING_KEY + userId + ":" + simpleFormatDate(date);
    }

    /**
     * 用户签到
     *
     * @param userId 用户id
     * @return 签到情况
     */
    public boolean sign(int userId, Date date) {
        DateTime now = (DateTime) date;
        String key = buildKey(userId, now);
        //这里需要减一是因为偏移量是从0开始的
        int offset = now.dayOfMonth() - 1;
        return jedis.setbit(key, offset, true);
    }

    /**
     * 检测用户是否签到
     *
     * @param userId
     * @param date
     * @return
     */
    public boolean isSign(int userId, Date date) {
        DateTime dateTime = (DateTime) date;
        return jedis.getbit(buildKey(userId, date), dateTime.getDay() - 1);
    }


    /**
     * 统计签到情况
     *
     * @param userId 用户id
     * @param date   日期对象
     * @return
     */
    public Map<String, Boolean> getSignInfo(int userId, Date date) {
        Map<String, Boolean> map = new LinkedHashMap<String, Boolean>();
        //当月最后一天
        DateTime dateTime = (DateTime) date;
        String type = String.format("u%d", dateTime.getLastDayOfMonth());
        //查询当月所签到的二进制值
        List<Long> longs = jedis.bitfield(buildKey(userId, date), "get", type, "0");
        //判断当月是否有签到情况
        if (longs == null || longs.size() == 0) return null;

        Long aLong = longs.get(0);
        for (int i = dateTime.getLastDayOfMonth(); i > 0; i--) {
            DateTime now = DateTime.now();
            now.setField(DateField.DAY_OF_MONTH, i);
             /*
                如果二进制右移一位再左移一位等于原来的值,证明低位有0,断签
                   例如  6的二进制 0110
                    0110 >>右移一位 = 0011 <<左移回来 0110 还是不变,证明低位有0
             */
            if (aLong >> 1 << 1 == aLong) {
                map.put(simpleFormatDate(now, "yyyy-MM-dd"), false);
            } else {
                map.put(simpleFormatDate(now, "yyyy-MM-dd"), true);
            }
            //右移继续判断下一天
            aLong >>= 1;
        }
        return map;
    }

    /**
     * 统计连续签到
     *
     * @param userId 用户id
     * @param date   日期对象
     * @return
     */
    public int getContinuousSignNCount(int userId, Date date) {
        int count = 0;
        int nowDay = ((DateTime) date).dayOfMonth();
        //当月最后一天
        String type = String.format("u%d", nowDay);
        //查询当月所签到的二进制值
        List<Long> l = jedis.bitfield(buildKey(userId, date), "get", type, "0");
        //当月未签到
        if (l == null || l.size() == 0) return 0;
        //统计
        Long aLong = l.get(0);
        for (int i = 0; i < nowDay; i++) {
            /*
                如果二进制右移一位再左移一位等于原来的值,证明低位有0,断签
                   例如  6的二进制 0110
                    0110 >>右移一位 = 0011 <<左移回来 0110 还是不变,证明低位有0
             */
            if (aLong >> 1 << 1 == aLong) {
                //这里需要考虑当天没有签到的情况
                if (i > 0) break;
            } else {
                count += 1;
            }
            //右移继续判断下一天
            aLong >>= 1;
        }
        return count;
    }


    /**
     * 用户某月内一共签到了多少次
     *
     * @param userId 用户id
     * @param date   日期对象
     * @return
     */
    public long signCount(int userId, Date date) {
        return jedis.bitcount(buildKey(userId, date));
    }


    /**
     * 固定日期格式
     *
     * @param date
     * @return
     */
    public static String simpleFormatDate(Date date) {
        return new SimpleDateFormat("yyyyMM").format(date);
    }

    /**
     * 用户自定义日期格式
     *<>java的开闭原则</>
     * @param date
     * @param format
     * @return
     */
    public static String simpleFormatDate(Date date, String format) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
        return simpleDateFormat.format(date);
    }
}

3.测试

复制代码
import cn.hutool.core.date.DateTime;
import redis.clients.jedis.Jedis;

import java.util.Map;
import java.util.Set;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Jedis jedis = new Jedis("192.168.57.128", 6379);
        UserSign userSign = new UserSign(jedis);
        System.out.println("-----------------签到-----------------");
        DateTime now = DateTime.now();
        for (int i = 1; i <= 4; i++) {
            now.setDate(i);
            System.out.println(now);
            userSign.sign(100, now);
        }
        System.out.println("-----------------查看某一天是否签到-----------------");
        DateTime now1 = DateTime.now();
        boolean sign = userSign.isSign(100, now1);
        System.out.println(sign ? UserSign.simpleFormatDate(now1, "yyyy-MM-dd") + ":已签到" : "未签到");

        System.out.println("-----------------查看连续签到了几天-----------------");
        DateTime now2 = DateTime.now();
        int continuousSignNCount = userSign.getContinuousSignNCount(100, now2);
        System.out.println(UserSign.simpleFormatDate(now2) + "月连续签到了" + continuousSignNCount + "天");

        System.out.println("-----------------总体签到情况-----------------");
        Map<String, Boolean> signInfo = userSign.getSignInfo(100, DateTime.now());
        Set<Map.Entry<String, Boolean>> entries = signInfo.entrySet();
        for (Map.Entry<String, Boolean> entry : entries) {
            System.out.println(entry.getKey() + ":" + (entry.getValue() ? "√" : "X"));
        }
        System.out.println("-----------------统计一个月签到了多少天-----------------");
        DateTime now3 = DateTime.now();
        long l = userSign.signCount(100, now3);
        System.out.println(UserSign.simpleFormatDate(now3) + "月签到了" + l + "次");
    }
}
相关推荐
Cachel wood1 分钟前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Code哈哈笑4 分钟前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
gb42152877 分钟前
springboot中Jackson库和jsonpath库的区别和联系。
java·spring boot·后端
程序猿进阶7 分钟前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
zfoo-framework15 分钟前
【jenkins插件】
java
风_流沙20 分钟前
java 对ElasticSearch数据库操作封装工具类(对你是否适用嘞)
java·数据库·elasticsearch
亽仒凣凣28 分钟前
Windows安装Redis图文教程
数据库·windows·redis
ProtonBase1 小时前
如何从 0 到 1 ,打造全新一代分布式数据架构
java·网络·数据库·数据仓库·分布式·云原生·架构
乐之者v1 小时前
leetCode43.字符串相乘
java·数据结构·算法