栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

基于Redis位图实现用户签到功能

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于Redis位图实现用户签到功能

场景需求

适用场景如签到送积分、签到领取奖励等,大致需求如下:
签到1天送1积分,连续签到7天,15天送,30天以上送不同积分等。
如果连续签到中断,则重置计数,每月初重置计数。
显示用户某个月的签到次数和首次签到时间。
在日历控件上展示用户每月签到情况,可以切换年月显示……等等

@Service
public class DailyCheckInServiceImpl implements DailyCheckInService {

    @Autowired
    private RedisService redisTemplate;


    
    @Override
    public boolean doSign(Long aid, LocalDate date) {
        int offset = date.getDayOfMonth() - 1;

        return redisTemplate.opsForValue().setBit(buildSignKey(aid, date), offset, true);
    }


    
    @Override
    public boolean checkSign(Long aid, LocalDate date) {
        int offset = date.getDayOfMonth() - 1;
        return redisTemplate.opsForValue().getBit(buildSignKey(aid, date), offset);
    }


    
    @Override
    public long getSignCount(Long aid, LocalDate date) {
        return redisTemplate.execute((RedisCallback) con -> con.bitCount(buildSignKey(aid, date).getBytes(StandardCharsets.UTF_8)));
    }

    @Override
    public long getContinuousSignCount(Long aid, LocalDate date) {
        int signCount = 0;
        List list = redisTemplate.opsForValue().bitField(buildSignKey(aid, date), BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType
                .unsigned(date.getDayOfMonth())).valueAt(0));
        if (list != null && list.size() > 0) {
            // 取低位连续不为0的个数即为连续签到次数,需考虑当天尚未签到的情况
            long v = list.get(0) == null ? 0 : list.get(0);
            for (int i = 0; i < date.getDayOfMonth(); i++) {
                if (v >> 1 << 1 == v) {
                    // 低位为0且非当天说明连续签到中断了
                    if (i > 0) {
                        break;
                    }
                } else {
                    signCount += 1;
                }
                v >>= 1;
            }
        }
        return signCount;
    }

    
    
    @Override
    public LocalDate getFirstSignDate(Long aid, LocalDate date) {
        long bitPosition = (Long) redisTemplate.execute((RedisCallback) cbk -> cbk.bitPos(buildSignKey(aid, date).getBytes(), true));
        return bitPosition < 0 ? null : date.withDayOfMonth((int) bitPosition + 1);
    }


    

    @Override
    public Map getSignInfo(Long aid, LocalDate date) {
        Map signMap = new HashMap<>(date.getDayOfMonth());
        List list = redisTemplate.opsForValue().bitField(buildSignKey(aid, date), BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType
                .unsigned(date.lengthOfMonth())).valueAt(0));

        if (!CollectionUtils.isEmpty(list)) {
            // 由低位到高位,为0表示未签,为1表示已签
            long v = list.get(0) == null ? 0 : list.get(0);
            for (int i = date.lengthOfMonth(); i > 0; i--) {
                LocalDate d = date.withDayOfMonth(i);
                signMap.put(formatDate(d, "yyyy-MM-dd"), v >> 1 << 1 != v);
                v >>= 1;
            }
        }
        return new TreeMap<>(signMap);

    }

    
    private static String buildSignKey(Long aid, LocalDate date) {
        return String.format("u:sign:%d:%s", aid, formatDate(date));
    }


    
    private static String formatDate(LocalDate date) {
        return formatDate(date, "yyyyMM");
    }


    
    private static String formatDate(LocalDate date, String pattern) {
        return date.format(DateTimeFormatter.ofPattern(pattern));
    }
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/885177.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号