开发实战:APP中的签到、补签、连续签到天数如何实现?

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.format拼装好单个用户的位图key
  String signKey = String.format(RedisKeyConstant.USER_SIGN_IN, LocalDate.now().getYear(), userId);
  //位图的偏移点为当天的日期,如今天,偏移值就是1010
  long monthAndDay = Long.parseLong(LocalDate.now().format(DateTimeFormatter.ofPattern("MMdd")));
  responseResult.setMessage("今日已签到");
  responseResult.setCode((byte) -1);
  //检测是否用户今日签到过,用getBit可以取出该用户具体日期的签到状态(位图的值只有两个,1或者0,这里1代表true)
  if (!cacheClient.getBit(signKey, monthAndDay)) {
      //位图的set方法会返回该位图未改变前的数值,这里如果之前没有签到过默认是0,也就是false
      boolean oldResult = cacheClient.setbit(signKey, monthAndDay);
      if (!oldResult) {
          //计算出这个月该用户的到今天的连续签到天数,此方法参照下方计算连续签到天数的代码块
          int signContinuousCount = getContinuousSignCount(userId);
          //此方法参照下方记录签到积分类型以及连续签到积分代码块
          doSaveUserIntegral(userId, signContinuousCount);
          responseResult.setCode((byte) 0);
      }
  }
  return responseResult;
}

计算连续签到天数

/**
 * @description: 获取连续签到天数
 * @author: chenyunxuan
 * @updateTime: 2020/8/25 4:43 下午
 */
private int getContinuousSignCount(I
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值