需求分析
在很多互联网应用中存在签到送积分、签到领取奖励等的需求,比如:
- 如果连续签到中断,则重置计数,每月初重置计数。
- 显示用户某个月的签到次数
设计思路
最简单的设计思路就是利用关系型数据库保存签到数据,但是为了节省储存数据占用的空间,这里使用Redis的Bitmaps完成。Bitmaps叫位图,它不是Redis的基本数据类型,由一组bit位组成,每个bit位对应0和1两个状态,虽然内部还是采用String类型存储,但Redis提供了一些指令用于直接操作位图,可以把它看作是一个bit数组,数组的下标就是偏移量。它的优点是内存开销小、效率高且操作简单,很适合用于签到这类场景。
考虑到每月初需要重置连续签到次数,最简单的方式是按用户每月存一条签到数据(也可以每年存一条数据)。Key的格式为u:sign:userid:yyyyMM,Value则采用长度为4个字节(32位)的位图(最大月份只有31天)。位图的每一位代表一天的签到,1表示已签,0表示未签。从高位插入,也就是说左边位算是开始日期。
例如user:sign:96:202103表示用户id=96的用户在2021年3月的签到记录。
统计用户签到次数
用户需求:统计某月签到次数,默认是当月
SignService方法统计
/**
* 获取用户签到次数
*
* @param accessToken 登录token
* @param dateStr 查询的日期,默认当月 yyyy-MM-dd
* @return 当前的签到次数
*/
public long getSignCount(String accessToken, String dateStr) {
// 获取登录用户信息
SignInDinerInfo signInDinerInfo = loadSignInDinerInfo(accessToken);
// 获取日期
Date date = getDate(dateStr);
// 构建 Key
String signKey = buildSignKey(signInDinerInfo.getId(), date);
// e.g. BITCOUNT user:sign:5:202011
return (Long) redisTemplate.execute(
(RedisCallback<Long>) con -> con.bitCount(signKey.getBytes())
);
}
SignController方法
@GetMapping("count")
public ResultInfo<Long> getSignCount(String access_token, String date) {
Long count = signService.getSignCount(access_token, date);
return ResultInfoUtil.buildSuccess(request.getServletPath(), count);
}
Postman测试
访问:http://localhost/diners/sign/...
获取用户签到情况
获取用户某月签到情况,默认当前月,返回当前月的所有日期以及该日期的签到情况
SignService方法
- 获取登录用户信息
- 构建Redis保存的Key
- 获取月份的总天数(考虑2月闰、平年)
- 通过BITFIELD指令获取当前月的所有签到数据
- 遍历进行判断是否签到,并存入TreeMap方便排序
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。