【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 + "次");
    }
}
相关推荐
考虑考虑3 小时前
Jpa使用union all
java·spring boot·后端
用户3721574261354 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊5 小时前
Java学习第22天 - 云原生与容器化
java
渣哥6 小时前
原来 Java 里线程安全集合有这么多种
java
间彧7 小时前
Spring Boot集成Spring Security完整指南
java
间彧7 小时前
Spring Secutiy基本原理及工作流程
java
Java水解8 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆10 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学10 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole11 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端