【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 + "次");
    }
}
相关推荐
CryptoRzz3 小时前
StockTV API 对接全攻略(股票、期货、IPO)
java·javascript·git·web3·区块链·github
iReachers3 小时前
为什么HTML打包安卓APP安装时会覆盖或者报错?
android·java·html·html打包apk·网页打包
纟 冬3 小时前
Flutter & OpenHarmony 运动App运动模式选择组件开发
android·java·flutter
毕设源码-赖学姐3 小时前
【开题答辩全过程】以 基于Springboot的智慧养老系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
jamesge20103 小时前
限流之漏桶算法
java·开发语言·算法
jvstar3 小时前
JAVA面试题和答案
java
冷雨夜中漫步3 小时前
OpenAPITools使用——FAQ
android·java·缓存
9坐会得自创3 小时前
使用marked将markdown渲染成HTML的基本操作
java·前端·html
Hello.Reader4 小时前
Flink ML 线性 SVM(Linear SVC)入门输入输出列、训练参数与 Java 示例解读
java·支持向量机·flink