Springboot redis bitMap实现用户签到以及统计,保姆级教程

发布于:2025-03-11 ⋅ 阅读:(21) ⋅ 点赞:(0)

项目架构,这是作为demo展示使用:
在这里插入图片描述

在这里插入图片描述

Redis config:

package com.zy.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @Author: zy
 * @Date: 2025-03-08-13:22
 * @Description:
 */
@Configuration
public class RedisConfig {

    /**
     * RedisTemplate配置
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

Controller:

package com.zy.controller;

import com.zy.config.RedisConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.Optional;

/**
 * @Author: zy
 * @Date: 2025-03-08-12:43
 * @Description:
 */
@RestController
@Slf4j
@RequestMapping("/user")
public class SignController {

    @Autowired
    private RedisTemplate redisTemplate;

    @PostMapping("/sign")
    public Boolean sign() {
        try {
            //1. 获取登录用户
            //Long userId = SSOThreadHolder.getUserAndCheck().getUserId();
            Long userId = 1001012345L;
            //2. 获取日期
            LocalDateTime now = LocalDateTime.now();
            for (int i = 0; i < 100; i++) {
                LocalDate date = LocalDateTime.now().minusDays(i).toLocalDate();
                String format = date.format(DateTimeFormatter.ofPattern(":yyyyMM"));

                //3. 拼接key
                String keySuffix = format;
                String key = RedisConstants.USER_SIGN_KEY + userId + keySuffix;
                log.info("redis key:" + key);
                //4. 获取今天是本月的第几天
                int dayOfMonth = date.getDayOfMonth();
                //5. 写入redis setbit key offset 1
                System.out.println("dayOfMonth:" + dayOfMonth);
                Boolean aBoolean = redisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
                System.out.println("aboolean:" + aBoolean);

            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }

        return true;
    }

    @GetMapping("/signCount")
    public String signCount() {

        long l = countSignInDays("sign:1001012345:202503");
        System.out.println("l:" + l);
        return String.valueOf(l);
    }

    /**
     * 统计当前月的签到总数
     *
     * @param key Redis键
     * @return 签到总数
     */
    public long countSignInDays(String key) {
        key = "sign:1001012345:202503";
        // 获取当前月的总天数
        YearMonth currentYearMonth = YearMonth.now();
        int daysInMonth = currentYearMonth.lengthOfMonth();

        // 使用 execute 方法执行 BITCOUNT 命令
        String finalKey = key;
        Long bitCount = (Long) redisTemplate.execute((RedisConnection connection) -> {
            // 检查键是否存在
            if (!connection.exists(finalKey.getBytes())) {
                return 0L;
            }
            // 执行 BITCOUNT 命令,统计指定范围内的 1 的数量
            return connection.bitCount(finalKey.getBytes(), 0, daysInMonth - 1);
        });

        return Optional.ofNullable(bitCount).orElse(0L);
    }

}

yml文件:

server:
  port: 8090


spring:
  #redis哨兵配置
  redis:
    sentinel:
      master: mymaster
      nodes: # 我这里使用的是哨兵模式,不影响,根据你自己的来,但是如果没有,为啥不搭建一下,自己使用呢
        - localhost:26379
        - localhost:26380
        - localhost:26381
      password: 你的密码
    password: 你的密码



接口测试:
签到接口,在代码中模拟了100天的签到
在这里插入图片描述

统计接口:统计的什么,具体看代码
在这里插入图片描述