基于32位bit位操作,实现用户签到工具类

2021-06-10  本文已影响0人  每天都是奥利给

之前项目需要做用户签到,便查阅了相关资料,整理了一个工具类

/**
 * 基于bit位运算 实现用户签到功能类
 */
public class UserSginUtils {

    private static  final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
    /**
     * 用户签到
     * @param sginTag 本月已签到标识 未签到则为0
     * @param monthOfDay 当前天 如 07-25 则传25
     * @return 返回新 签到标识 -> sginTag
     */
    public int doSgin(int sginTag, int monthOfDay){
        int offset = monthOfDay - 1; //偏移量从0开始 所以day-1
        int sgin_offset = 1 << offset; //日期从1号开始 所以从1开始左偏移offset位
        return sginTag + sgin_offset;
    }

    /**
     * 该月累计签到次数
     *  如 该月 1,2,3,6号用户签到了  则 sginTag = 39 则用户该月供签到4次
     * @param sginTag
     * @return
     */
    public int sginCount(int sginTag){
        return Integer.bitCount(sginTag);
    }


    /**
     * 该月首次签到日期
     * 向左取bit 第一位为1的偏移量
     * @param sginTag
     * @return
     */
    public int monthTopSginDate(int sginTag){
        int r = 1; //偏移量从0开始 所以+1
        if ((sginTag & 0xFFFF) == 0) {
            sginTag >>= 16;
            r += 16;
        }
        if ((sginTag & 0xFF) == 0) {
            sginTag >>= 8;
            r += 8;
        }
        if ((sginTag & 0x0F) == 0) {
            sginTag >>= 4;
            r += 4;
        }
        if ((sginTag & 0x03) == 0) {
            sginTag >>= 2;
            r += 2;
        }
        if ((sginTag & 0x01) == 0) {
            r += 1;
        }
        return r;

    }

    /**
     * 该月最后签到日期
     * 向右取第一个位1的偏移量
     * @param sginTag
     * @return
     */
    public int monthLastSginDate(int sginTag){
        int r = 0;
        if ((sginTag & 0xffff0000) != 0) {
            sginTag >>>= 16;
            r += 16;
        }
        if ((sginTag & 0xff00) != 0) {
            sginTag >>>= 8;
            r += 8;
        }
        if ((sginTag & 0xf0) != 0) {
            sginTag >>>= 4;
            r += 4;
        }
        if ((sginTag & 0x0c) != 0) {
            sginTag >>>= 2;
            r += 2;
        }
        if ((sginTag & 0x02) != 0) {
            sginTag >>>= 1;
            r += 1;
        }
        if ((sginTag & 0x01) != 0) {
            r += 1;
        }
        return r;
    }

    /**
     * 判断当天是否签到
     * @param sginTag 签到标识值
     * @param monthOfDay 天
     * @return
     */
    public boolean isSgin(int sginTag, int monthOfDay){
        int offset = monthOfDay - 1; //偏移量从0开始
        int daySgin = 1 << offset; //每月1号 bit偏移 offset 为monthday的bit位
        return (sginTag & daySgin) > 0;
    }

    /**
     * 当月连续签到的天数(按传入日期 往前推算)
     * @param sginTag       当月签到标识
     * @param checkDate     检查日期
     * @return
     */
    public int continuousNum(int sginTag, Date checkDate){
        int sginCount = 0; //签到次数
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(checkDate);
        int day = calendar.get(Calendar.DAY_OF_MONTH); //获取当前月份天数
//        day = day - 1; //不考虑当天
        while (day > 0){
            if (isSgin(sginTag, day)){
                sginCount++;
            }else { //只要有一次未签到 则退出循环
                break;
            }
            day--;
        }
        return sginCount;
    }

    /**
     * 获取当月1号开始 连续签到次数
     * @param sginTag
     * @param year
     * @param month
     * @return
     */
    public int continuousFirstDay(int sginTag,int year, int month){
        int sginCount = 0;
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, month-1);
        calendar.set(Calendar.DAY_OF_MONTH,calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        //获取当月最大日期
        int maxDay = calendar.get(Calendar.DAY_OF_MONTH);
        for (int day = 1; day <= maxDay; day++){
            if (isSgin(sginTag, day)){
                sginCount++;
            }else { //只要有一次未签到 则退出循环
                break;
            }
        }
        return sginCount;
    }

    /**
     * 获取当月1号开始 连续签到次数
     * @param sginTag
     * @param year
     * @param month
     * @return
     */
    public List<String> continuousFirstDayOfDays(int sginTag,int year, int month){
        List<String> list = new ArrayList<>();
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, month-1);
        calendar.set(Calendar.DAY_OF_MONTH,calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        //获取当月最大日期
        int maxDay = calendar.get(Calendar.DAY_OF_MONTH);
        for (int day = 1; day <= maxDay; day++){
            if (isSgin(sginTag, day)){
                calendar.set(Calendar.DAY_OF_MONTH, day);
                list.add(format.format(calendar.getTime()));
            }else { //只要有一次未签到 则退出循环
                break;
            }
        }
        return list;
    }

    /**
     * 获取当月(连续) 连续签到 指定次数的日期集合 如指定连续签到 contiousDay = 7 则 连续签到 7天或以后的所有日期会返回 否则返回空集合
     * @param sginTag
     * @param year
     * @param month
     * @return
     */
    public List<String> getContinousDays(int sginTag, int lastSginTag, int year, int month, int contiousDay){
        List<String> list = new ArrayList<>();
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, month - 1);
        calendar.set(Calendar.DAY_OF_MONTH,calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        //上月连续签到天数
        int lastSignCount = this.monthLastDateSginNum(lastSginTag, year, month);
        //获取当月最大日期
        int maxDay = calendar.get(Calendar.DAY_OF_MONTH);
        int sginCount = lastSignCount;

        for (int day = 1; day <= maxDay; day++){
            if (isSgin(sginTag, day)){
                sginCount++;
            }else { //只要有一次未签到 则重置
                sginCount = 0;
            }
            if (sginCount >= contiousDay){ //连续签到超过指定天数的 加入集合
                calendar.set(Calendar.DAY_OF_MONTH, day);
                list.add(format.format(calendar.getTime()));
            }
        }
        return list;
    }

    /**
     * 当月最后一天往前推 连续签到天数
     * @param sginTag
     * @param year
     * @param month
     * @return
     */
    public int monthLastDateSginNum(int sginTag, int year, int month){
        int sginCount = 0;
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, month - 1);
        calendar.set(Calendar.DAY_OF_MONTH,calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        while (day > 0){
            if (isSgin(sginTag, day)){
                sginCount++;
            }else { //只要有一次未签到 则退出循环
                break;
            }
            day--;
        }
        return sginCount;
    }




    /**
     * 获取用户签到视图数据
     * @param sginTag 签到标识
     * @param month   签到月份
     * @param year   签到年份
     * @return
     */
    public List<Map<String,Object>> sginMapDetails(int sginTag, int year, int month){
        List<Map<String,Object>> sginListMap = new ArrayList<>();
        //设置日期
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        month = month - 1; //日历类 月份从0开始
        calendar.set(Calendar.MONTH, month);
        //当月最大日期
        int maxMonthOfday = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        int day = 1; //月份第一天
        String yearOfMonth = String.valueOf(year).concat("-".concat(month+1 < 10 ? "0".concat(String.valueOf(month+1)) : String.valueOf(month+1))); // yyyy-MM
        while (day <= maxMonthOfday){
            Map<String, Object> dateMap = new HashMap<>();
            dateMap.put("yearOfMonth", yearOfMonth);
            String monthOfDay = String.valueOf(day);
            if (day < 10){
                monthOfDay = "0" + day;
            }
            String yearOfMonthDay = yearOfMonth
                    .concat("-")
                    .concat(monthOfDay);
            dateMap.put("yearOfMonthDay", yearOfMonthDay);
            dateMap.put("monthOfDay", monthOfDay);
            boolean isSgin = false;
            if (this.isSgin(sginTag, day)){
                isSgin = true;
            }
            dateMap.put("isSgin", isSgin);
            sginListMap.add(dateMap);
            day ++;
        }
        return sginListMap;
    }

    /**
     * 测试
     * @param args
     */
//    public static void main(String[] args) {
//        UserSginUtils userSginUtils = new UserSginUtils();
//        //签到
//        //假设用户A 首次签到
//        int sginTag = 0; //首次签到 签到标示值为0 该值存于数据库 或 redis缓存 key-val key:sgin:userId:yearMonthOfDay value:sginTag
//        int monthOfDay = 1;// 签到日期
//        int newSginTag = userSginUtils.doSgin(sginTag, monthOfDay);
//        System.out.println("1. 用户1号签到,sginTag:"+newSginTag);
//        //本月签到次数
//        System.out.println("2. 用户本月签到:"+userSginUtils.sginCount(newSginTag)+"次");
//        //用户A 在2号签到
//        monthOfDay = 2;
//        newSginTag = userSginUtils.doSgin(newSginTag, monthOfDay);
//        System.out.println("3. 用户2号签到,sginTag:"+newSginTag);
//        //本月签到次数
//        System.out.println("4. 用户本月签到:"+userSginUtils.sginCount(newSginTag)+"次");
//        //用户A 在4号签到
//        monthOfDay = 4;
//        newSginTag = userSginUtils.doSgin(newSginTag, monthOfDay);
//        System.out.println("5. 用户4号签到,sginTag:"+newSginTag);
//        //本月签到次数
//        System.out.println("6. 用户本月签到:"+userSginUtils.sginCount(newSginTag)+"次");
//        //用户A 在4号签到
//        monthOfDay = 5;
//        newSginTag = userSginUtils.doSgin(newSginTag, monthOfDay);
//        System.out.println("5. 用户5号签到,sginTag:"+newSginTag);
//        //本月签到次数
//        System.out.println("6. 用户本月签到:"+userSginUtils.sginCount(newSginTag)+"次");
//        //用户A 在4号签到
//        monthOfDay = 6;
//        newSginTag = userSginUtils.doSgin(newSginTag, monthOfDay);
//        System.out.println("7. 用户6号签到,sginTag:"+newSginTag);
//        //本月签到次数
//        System.out.println("8. 用户本月签到:"+userSginUtils.sginCount(newSginTag)+"次");
//        //用户A 在4号签到
//        monthOfDay = 7;
//        newSginTag = userSginUtils.doSgin(newSginTag, monthOfDay);
//        System.out.println("9. 用户7号签到,sginTag:"+newSginTag);
//        //本月签到次数
//        System.out.println("10. 用户本月签到:"+userSginUtils.sginCount(newSginTag)+"次");
//        //用户A 在4号签到
//        monthOfDay = 8;
//        newSginTag = userSginUtils.doSgin(newSginTag, monthOfDay);
//        System.out.println("11. 用户8号签到,sginTag:"+newSginTag);
//        //本月签到次数
//        System.out.println("12. 用户本月签到:"+userSginUtils.sginCount(newSginTag)+"次");
//        //判断用户是否在 5号签到
//        System.out.println("13. 用户在5号签到了?结果:"+userSginUtils.isSgin(newSginTag, 5));
//        //判断用户是否在 11号签到
//        System.out.println("14. 用户在11号签到了?结果:"+userSginUtils.isSgin(newSginTag, 11));
//        //检测连续签到
//        //假设业务 连续签到7天有奖励
//        // 用户A 准备在9号 准备签到 需要判断9号之前连续签到的情况 则:
//        Calendar calendar = Calendar.getInstance();
//        calendar.setTime(new Date());
//        calendar.set(Calendar.DAY_OF_MONTH, 9);
//        //4 5 6 7 8签到了 所以连续签到为 5
//        System.out.println("15. 用户A在9号之前连续签到了:"+ userSginUtils.continuousNum(newSginTag, calendar.getTime())+"天");
//        calendar.set(Calendar.DAY_OF_MONTH, 10);
//        //9号未签到 所以10号之前连续签到为0
//        System.out.println("16. 用户A在10号之前连续签到了:"+ userSginUtils.continuousNum(newSginTag, calendar.getTime())+"天");
//        //本月签到视图
//        System.out.println(userSginUtils.sginMapDetails(newSginTag, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH)+1));
//    }

}
上一篇下一篇

猜你喜欢

热点阅读