做一件事,无论大小,倘无恒心,是很不好的。而看一切太难,固然能使人无成,但若看得太容易,也能使事情无结果。
前言
每日签到这个功能相当常见,不管什么app都有可能碰到这个需求,那么我们如何去实现呢?其实很简单,我们可以通过bitmap去实现
什么是bitmap
Redis的Bitmap(位图)是一种数据结构,它可以用来存储和操作一组二进制位。Redis将位图作为整数数组来处理,每个数组元素都表示一个二进制位,可以存储0或1。Redis支持对位图进行位与、位或、位异或、位取反等操作。
以下是Redis官方对Bitmap的解释:
- Bitmap可以用来存储二进制数据,每个bit都可以独立地设置、读取和删除。
- Bitmap可以作为普通的数据类型进行存储和操作,也可以和其他数据类型进行组合使用。
- Bitmap支持对位进行各种操作,包括位与、位或、位异或、位取反等,这些操作都可以在O(1)时间复杂度内完成。
- Bitmap可以用来实现一些常见的数据结构,如哈希表、有序集合等。
在Redis中,可以使用以下命令对Bitmap进行操作:
BITCOUNT key
:统计指定key中设置为1的bit数量。BITOP operation destkey key
:对一个或多个key进行位操作,并将结果保存到destkey中。GETBIT key offset
:获取指定key中指定offset位置的bit值。SETBIT key offset bit
:设置指定key中指定offset位置的bit值。
想要具体了解,网上有很多文章,这里就不多赘述了,下面就是直接上代码,实现一个每日签到功能
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v9"
"strconv"
"time"
)
var ctx = context.TODO()
func CheckIn(client *redis.Client, userId int, day string) error {
now := time.Now()
key := fmt.Sprintf("user:%s_checkin:%d", fmt.Sprintf("%d%d", now.Year(), now.Month()), userId)
return client.SetBit(ctx, key, dateToInt64(day), 1).Err()
}
func dateToInt64(str string) int64 {
// 使用ParseInt函数进行转换
num, _ := strconv.ParseInt(str, 10, 64)
return num
}
func IsCheckIn(client *redis.Client, userId int, day string) bool {
now := time.Now()
key := fmt.Sprintf("user:%s_checkin:%d", fmt.Sprintf("%d%d", now.Year(), now.Month()), userId)
return client.GetBit(ctx, key, dateToInt64(day)).Val() == 1
}
func CountCheckIn(client *redis.Client, userId int) int64 {
now := time.Now()
key := fmt.Sprintf("user:%s_checkin:%d", fmt.Sprintf("%d%d", now.Year(), now.Month()), userId)
return client.BitCount(ctx, key, &redis.BitCount{
Start: 0,
End: -1,
}).Val()
}
func main() {
client := redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
DB: 0,
})
// 模拟签到
fmt.Println("Simulated check-in")
for i := 1; i <= 31; i++ {
if i%2 == 0 {
fmt.Println("day: ", i)
err := CheckIn(client, 1, strconv.Itoa(i))
if err != nil {
fmt.Println(err)
}
}
}
fmt.Println()
// 判断签到
fmt.Println("Determine whether to check-in or not")
for i := 1; i <= 5; i++ {
fmt.Println("day: ", i, " ", IsCheckIn(client, 1, strconv.Itoa(i)))
}
fmt.Println()
// 统计签到
fmt.Println("count check-in")
fmt.Println(fmt.Sprintf("Users check-in for a total of %d days", CountCheckIn(client, 1)))
}
打印结果: