Bootstrap

干巴巴的签到!

签到送积分

签到需求:

按月签到
进行连续签到的判断,根据签到规则设置连续签到的天数
每日签到送2积分 连续签到3天送10积分 连续签到5天送20积分

签到记录不存储到数据,使用bitmap存储某个用户的签到信息
bitfield获取签到数据,进行连续签到判断
通过线程池执行送积分任务

bitmap

位图,本质上也是字符串

以二进制方式存储0/1数据

# 设置指定offset的值,offset是表示比特位的偏移量,从0开始
127.0.0.1:6379> setbit sign:zhangsan 1 1
(integer) 0
127.0.0.1:6379> setbit sign:zhangsan 3 1
(integer) 0
# 获取指定offset的值
127.0.0.1:6379> getbit sign:zhangsan 1
(integer) 1
127.0.0.1:6379> getbit sign:zhangsan 2
(integer) 0
127.0.0.1:6379> getbit sign:zhangsan 4
(integer) 0
# 统计指定范围的bit位的值是1的个数,以字节范围进行统计,start end表示字节,从0开始
127.0.0.1:6379> bitcount sign:zhangsan
(integer) 2
127.0.0.1:6379> bitcount sign:zhangsan 1 3
bitfield 位域
# 本例中 u4 表示获取的数据转为无符号数,4表示获取的bit位的长度
# 最后的0,表示偏移量,表示从哪一个偏移量开始获取数据
# 表示从0位开始,获取4位bit位的数据,将获取的二进制数据转为10机制数据返
127.0.0.1:6379> bitfield sign:zhangsan get u4 0
1) (integer) 5
127.0.0.1:6379> bitfield sign:zhangsan get u5 1
1) (integer) 20

设计积分记录表

在这里插入图片描述

实现思路:

1、判断当前是否签到

获取当前时间 对日期格式化

获取用户id 设置key的过期时间 计算过期的天数(当前月总天数–当前日期+1)

获取当天的签到状态,使用Bitmap存储签到记录,offset=1表示1号

2、若未签到,进行签到操作

3、送积分,积分存储数据库

根据积分类型获取规则数据

通过线程池异步送积分

4、判断连续签到天数

根据日期获取指定范围内的签到数据 方案一:将数据转为二进制字符串 方案二:位运算

(在此将用方案一)从后往前遍历,获取连续为1的个数

5、根据连续签到的结果判断是否送积分

使用Stream流的filter()进行查询

核心代码:

public void sign() {
        // 1 判断当前是否签到
        // 获取年月
        LocalDate localDate = LocalDate.now();
        // 日期格式化
        String yearAndMonth = DateUtils.formatDate(localDate, "yyyyMM");
        // 获取用户id
        Integer uid = UserUtils.getId();
        // sign:年月:用户id,例如sign:202408:1
        String signKey = String.format(RedisKeyEnums.KEY_SIGN.getKey(), yearAndMonth, uid);
        // 设置key的过期时间
        if (!redisUtil.hasKey(signKey)) {
            redisUtil.setBit(signKey, 0);
            // 计算需要过期的天数
            int remainDay = localDate.lengthOfMonth() - localDate.getDayOfMonth() + 1;
            redisUtil.expire(signKey, remainDay * 24 * 3600);
        }
        // 获取当天的签到状态, 使用bitmap存储签到记录,offet=1表示1号,offset=2表示2号,依次类推
        boolean ret = redisUtil.getBit(signKey, localDate.getDayOfMonth());
        if (ret) {
            throw new RuntimeException("今天已签到,不能重复签到");
        }
        // 2 如果没有,进行签到操作
        redisUtil.setBit(signKey, localDate.getDayOfMonth());

        // 3 送积分,积分存储到数据库
        // 根据积分类型获取规则数据
        PointRule pointRule = pointRuleService.ruleByPointType(PointTypeEnums.TYPE_SIGN.getValue());
        // 异步送积分
        threadPoolExecutor.execute(() -> {
            Point point = new Point();
            point.setPoint(pointRule.getPoint());
            point.setPointType(PointTypeEnums.TYPE_SIGN.getValue());
            point.setUid(uid);
            pointService.addPoint(point);
        });

        // 4 获取连续签到天数
        // 根据日期,获取指定范围的签到数据
        Long signValue = redisUtil.bitfield(signKey, localDate.getDayOfMonth(), 1);
        // 两个方案:1 将数据转为二进制字符串, 2 使用位运算
        int continueDays = 0;
        // 类似 1100111
        // 将数据转为二进制字符串
        String s = Long.toBinaryString(signValue);
        // 从后往前进行遍历,获取连续的1的个数
        for (int i = s.length() - 1; i >= 0; i--) {
            if (s.charAt(i) == '1') {
                continueDays++;
            } else {
                break;
            }
        }

        // 5 根据连续签到结果,判断是否需要送积分
        PointRule pointRule3 = pointRuleService.ruleByPointType(PointTypeEnums.TYPE_CONTINUE_SIGN.getValue());
        String continueRule = pointRule3.getContinueDays();
        List<ContinuePointRule> continuePointRules = JSON.parseArray(continueRule, ContinuePointRule.class);
        // boolean test(T t);
        //int finalContinueDays = continueDays;
        int finalContinueDays = continueDays;
        // filter() 过滤查询
        ContinuePointRule continuePointRule = continuePointRules.stream()
                .filter(item -> item.getDays().equals(finalContinueDays))
                .findFirst()
                .orElse(null);

        if (continuePointRule != null) {
            threadPoolExecutor.execute(() -> {
                Point point = new Point();
                point.setPoint(continuePointRule.getPoint());
                point.setPointType(PointTypeEnums.TYPE_CONTINUE_SIGN.getValue());
                point.setUid(uid);
                pointService.addPoint(point);
            });
        }

    }

    public static void main(String[] args) {
        String s = Long.toBinaryString(103);
        System.out.println(s);
    }
         });
        }

    }

    public static void main(String[] args) {
        String s = Long.toBinaryString(103);
        System.out.println(s);
    }

;