1. 签到定义以及作用
签到,指在规定的簿册上签名或写一“到”字,表示本人已经到达。在APP中使用此功能,可以增加用户粘性和活跃度。
一个有签到功能的APP,往往会提供补签功能,连续签到多少天会给予相关的奖励;而为了进一步增加用户粘性,还会提供签到任务功能,完成任务也可获取对应的奖励。
功能用例
本文带你实现一个包含上述用例的签到功能,看完以后你会发现,签到,没有你想的那么复杂!
2. 技术选型
redis为主写入查询,mysql辅助查询。传统签到多数都是直接采用mysql为存储DB,在大数据的情况下数据库的压力较大。查询速率也会随着数据量增大而增加。所以在需求定稿以后查阅了很多签到实现方式,发现用redis做签到会有很大的优势。
本功能主要用到redis位图[1],后面我会详细讲解实现过程。
3. 实现效果
这里抛砖引玉,展示一下我们app的签到实现效果
4 功能实现
功能大致分为两个大模块
签到流程(签到,补签,连续,签到记录)
签到任务(每日任务,固定任务)
签到流程图如下:
4.1.1 表设计
因为大部分功能使用redis存储,使用到mysql主要是为了存储用户总积分以及积分记录,便于查询签到记录和用户总积分
CREATE TABLE t_user_integral
(
id
varchar(50) NOT NULL COMMENT ‘id’,
user_id
int(11) NOT NULL COMMENT ‘用户id’,
integral
int(16) DEFAULT ‘0’ COMMENT ‘当前积分’,
integral_total
int(16) DEFAULT ‘0’ COMMENT ‘累计积分’,
create_time
datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT ‘创建时间’,
update_time
datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT ‘修改时间’,
PRIMARY KEY (id
) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT=‘用户积分总表’
CREATE TABLE t_user_integral_log
(
id
varchar(50) NOT NULL COMMENT ‘id’,
user_id
int(11) NOT NULL COMMENT ‘用户id’,
integral_type
int(3) DEFAULT NULL COMMENT ‘积分类型 1.签到 2.连续签到 3.福利任务 4.每日任务 5.补签’,
integral
int(16) DEFAULT ‘0’ COMMENT ‘积分’,
bak
varchar(100) DEFAULT NULL COMMENT ‘积分补充文案’,
operation_time
date DEFAULT NULL COMMENT ‘操作时间(签到和补签的具体日期)’,
create_time
datetime DEFAULT NULL COMMENT ‘创建时间’,
PRIMARY KEY (id
) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT=‘用户积分流水表’
4.1.2 redis key设计
//人员签到位图key,一个位图存一个用户一年的签到状态,以userSign为标识,后面的两个参数是今年的年份和用户的id
public final static String USER_SIGN_IN = “userSign:%d:%d”;
//人员补签key,一个Hash列表存用户一个月的补签状态,以userSign:retroactive为标识,后面的两个参数是当月的月份和用户的id
public final static String USER_RETROACTIVE_SIGN_IN = “userSign:retroactive:%d:%d”;
//人员签到总天数key,以userSign:count为标识,后面的参数是用户的id
public final static String USER_SIGN_IN_COUNT = “userSign:count:%d”;
4.1.3 实现签到
接口restful的形式,头信息里传入用户id
@ApiOperation(“用户签到”)
@PostMapping("/signIn")
@LoginValidate
public ResponseResult saveSignIn(@RequestHeader Integer userId) {
return userIntegralLogService.saveSignIn(userId);
}
sevice实现层
public ResponseResult saveSignIn(Integer userId) {
//这里是我们的公司统一返回类
ResponseResult responseResult = ResponseResult.newSingleData();
//用String.fo