@Override public Result signCount() { //1.获取当前登录用户 Long userId = UserHolder.getUser().getId(); //2.获取日期 LocalDateTime now = LocalDateTime.now(); //3.拼接key String keySuffix = now.format(DateTimeFormatter.ofPattern("yyyyMM")); String key = USER_SIGN_KEY + userId + keySuffix; //4.获取今天是本月第几天 int dayOfMonth = now.getDayOfMonth(); //5.获取本月截止今天为止的所有的签到记录,返回的是一个十进制的数字 BITFIELD sign:5:202203 GET u14 0 List<Long> result = stringRedisTemplate.opsForValue().bitField( key, BitFieldSubCommands.create() .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0) ); if(result==null || result.isEmpty()){ //没有任何签到结果 return Result.ok(0); } Long num = result.get(0); if(num==null || num==0){ return Result.ok(0); } //6.循环遍历 int count=0; while(true){ //6.1.让这个数字与1做与运算,得到数字的最后一个bit位 if((num&1)==0){//6.2.判断这个bit位是否为0 //6.3.如果为0,说明未签到结束 break; }else{ //6.4.如果不为0,说明已签到,计数器+1 count++; } //6.5.把数字右移一位,抛弃最后一个bit位,继续下一个bit位 num >>>= 1; } return Result.ok(count); }仔细分析每一行代码
时间: 2025-06-26 08:07:36 浏览: 10
下面是对这段代码的逐行详细解析:
```java
@Override
public Result signCount() {
```
- **`@Override`**:这是一个注解,表示该方法覆盖了父类或接口中的同名方法。这里表明 `signCount()` 方法是从某个父类或接口继承来的。
---
```java
// 1. 获取当前登录用户
Long userId = UserHolder.getUser().getId();
```
- 这一行通过 `UserHolder.getUser()` 调用了某种工具类(可能是 Spring 容器管理的对象),获取当前用户的 ID,并将其存储在变量 `userId` 中。
- 假设这是从 JWT 或会话信息中提取的用户标识符。
---
```java
// 2. 获取日期
LocalDateTime now = LocalDateTime.now();
```
- 使用 Java 的时间 API (`LocalDateTime`) 来获取系统当前的时间点,存储在变量 `now` 中。
- 此处用于后续构造 Redis Key 和计算当天是当月的第几天。
---
```java
// 3. 拼接 key
String keySuffix = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
String key = USER_SIGN_KEY + userId + keySuffix;
```
- 将当前年份和月份格式化为字符串形式(如 `"202309"`)并赋值给 `keySuffix` 变量。
- 然后将全局常量 `USER_SIGN_KEY`、用户 ID 和年月部分拼接成一个完整的 Redis Key 字符串,例如:`"user:sign:5:202309"`。
---
```java
// 4. 获取今天是本月第几天
int dayOfMonth = now.getDayOfMonth();
```
- 提取当前日期对应的天数(即本月中的一天)。比如如果是 9 月 8 日,则返回值为 `8`。
---
```java
// 5. 获取本月截止今天为止的所有签到记录
List<Long> result = stringRedisTemplate.opsForValue().bitField(
key, BitFieldSubCommands.create()
.get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
);
```
- 使用 Redis 的 `BITFIELD` 指令来读取指定键的数据位操作结果。
- 具体步骤:
- 首先创建了一个 `BitFieldSubCommand` 对象,指定了无符号整型类型(长度等于 `dayOfMonth`)。
- `.valueAt(0)` 表示从偏移量 `0` 开始截取数据位。
- 结果是一个包含单元素的列表,代表当前用户截至今日的连续签到状态(按比特位存储的结果)。
---
```java
if (result == null || result.isEmpty()) {
// 没有任何签到结果
return Result.ok(0);
}
```
- 如果查询结果为空或者不存在有效记录,则直接返回 JSON 格式的成功响应,同时携带连续签到次数为 `0` 的消息。
---
```java
Long num = result.get(0);
if (num == null || num == 0) {
return Result.ok(0);
}
```
- 从上一步的结果集中取出第一个数值存入 `num` 中。
- 若此值为零或者是空,也立即退出函数,返回连续签到次数为 `0` 的结果。
---
```java
// 6. 循环遍历每个 bit 位置的状态
int count = 0; // 初始化计数器
while (true) {
// 6.1 让这个数字与 1 做与运算,得到数字的最后一个 bit 位
if ((num & 1) == 0) {
// 6.2 判断这个 bit 是否为 0
break; // 一旦遇到未签到的情况就终止循环
} else {
count++; // 否则累计已经完成的签到天数
}
num >>>= 1; // 把数字右移一位,丢弃最后的一个 bit 位,准备检查下一个
}
```
- 设置初始变量 `count` 用来累加实际有效的签到日次序。
- 当前算法的核心在于利用二进制的“与”(`&`) 和 “无符号右移”(`>>>`) 查找每一位是否为 `1`(代表已签到)、如果不是,则停止统计;若是,则增加对应统计数据直到所有合法项都被计入。
---
```java
return Result.ok(count);
```
- 最终封装好最终得出的有效签到总天数至统一对外的标准回复结构内再交还调用方处理即可。
---
###
阅读全文
相关推荐









