累计连续签到 设计和实现
-
最近公司业务上需要实现一个累计连续打卡的功能,现在把打卡设计问题和思路整理一下发给大家
-
目前搜集到一些基于 Redis 位图 / 关系型数据库的一些方案,可以参考一下,做出最优方案的选择
-
由于需求的复杂,本文还是选择使用关系型数据库实现和存储,因为关系型数据库查询无所不能,哈哈哈哈
功能要求
- 签到
- 补签
- 统计某用户截至今天连续打卡天数
- 统计某用户在某一天打卡排名
- 统计某用户截至到某天连续打卡天数
- 最高连续签到记录
下面直接上一个需求图
问题难点
- 怎么用比较好方式去统计连续打卡天数
- 怎么实现补卡功能以达到连续签到的效果
- 怎么实现补签后连续天数的统计功能
数据库设计
以下是打卡记录表的设计和实现,我已经去掉了一些业务字段,剩下都是表结构的核心字段
CREATE TABLE mark_record (
id BIGINT NOT NULL COMMENT 'ID'
PRIMARY KEY,
create_time DATETIME NOT NULL COMMENT '创建时间',
update_time DATETIME NOT NULL COMMENT '更新时间',
user_id BIGINT NOT NULL COMMENT '用户ID',
mark_day_time INT NOT NULL COMMENT '打卡日期 yyyyMMdd',
day_continue BIGINT DEFAULT 0 NOT NULL COMMENT '距离上次打卡相差天数',
mark_type TINYINT DEFAULT 0 NOT NULL COMMENT '补签 0否 1是',
CONSTRAINT uidx_user_id_mark_day_time
UNIQUE (user_id, mark_day_time)
)
COMMENT '打卡签到表';
id
/create_time
/update_time
表结构的常规字段,简单提醒一下,业务上这些字段也比较重要
-
id
表的唯一主键 -
create_time
/update_time
比较重要数据信息字段一般都保留
列举一个比较实用业界数据分页案例:
数据分页翻页时候,防止新增数据导致分页加载出现重复数据,一般做法是当客户端打卡当前页面那瞬间时间戳传过来,上下翻页都是用同一个时间戳,后端查询数据时候只查询小于这个时间戳的数据,大于这个时间戳的数据就不会加载出来了
其他用途就不一一列举了
user_id
&mark_day_time
组成一个唯一索引
一个用户一天只允许打卡一次,加唯一索引保证数据唯一防止脏数据
mark_type
记录打卡类型
区分正常打卡和补卡
day_continue
冗余字段 距离上次打卡记录相差天数
以方便统计相关打卡记录数据
代码实现
打卡功能实现
markDayTime
当前打卡签到日期,userId
当前打卡用户 ID
签到功能 SQL 实现
使用 INSERT INTO SELECT
查询小于当前签到日期(markDay