签到领金币模块

  最近做了一个签到模块的需求,主要就是签到,根据连签天数提供不同和奖励并在首页展示不同的文案。奖励分为金币和话费,话费也是通过金币的形式发放,但是有效期只有1天。

 

签到需求

  1. 每日签到给奖励,七日一循环
  2. 若未签到,从第一天开始至后七天。若签到,从第一天签到返回至七日后。七天一循环
  3. 返回一个月内签到记录
  4. 若签到用户为新签到用户,则其连续签到第3天和第7天分别多领取有效期为一天的话费等额金币
  5. 新增首页签到入口文案展示,文案提示根据签到天数和是否是新人变化
  6. 给一个连续签到总天数,不是七日一循环。

 

解决方案

数据库设计:

签到表

字段设计包括自增id,用户userId,签到当天零点日期checkDate,奖励record,签到天数(一周)day,连签总天数alwaysDays

 

获取最近七天签到记录

通过sql

select * from 签到表 where userId = #{userId} order by date desc limit 7

优化:userId建立索引, 通过order by  … limit 7可以避免全表扫描 。

查询出结果后存入缓存,缓存时间只要大于24小时即可,但考虑大多数人连签不会坚持两天,所以可以将缓存失效时间设置72到96小时。时间太短则用户下次签到时需多次读取数据库增加数据库负载(我的签到逻辑是签到时先通过缓存查出最近一天签到记录,签到后清除缓存,展示时再访问数据库并存入缓存)。时间太长又会浪费redis空间。

 

签到逻辑

获取最近七天签到记录和签到总天数

如果最近一天签到时间等于当天,则直接返回;如果签到时间等于昨天,则将签到天数day和连签总天数alwaysDays增加1,若day大于7则重新置为1.存入数据库同时移除缓存并异步进行奖励金币入库。

如何判断是否是新签到用户(七天内)

若签到总天数为0或与昨天签到记录day相同(若不是昨天则发生断签,变成老用户,day永远小于等于7)则确定其为新签到用户,在进行奖励金币入库时额外增加新签到用户奖励。

 

获取一个月内签到记录

根据传入的日期参数date(转化为时间戳10位Integer类型)通过Calendar

类求出当月的所有日期(日期为当天零点)。

通过日期开始和结束时间范围求出当月所有签到记录并通过stream流转为以签到当天零点日期为key的map。循环当月日期并与map进行比较,若不为空则表示当天有签到,返回true;否则返回false。因该接口入口签到后才展示且一般用户很少点开,所以暂未加缓存。

 

展示一周签到记录

通过缓存查询出最近七天签到记录,判断最近一天签到是否是昨天或今天当天,若不是则发生断签,一周全部返回false。若未断签则根据1-7循环与day比较,day之前为true,day之后为false。

 

新签到用户额外领取金币过期

因新签到用户额外领取的金币只有一天的使用时间,所以需要有一个过期判定。

该需求可以通过mp消息进行过期扣减。在新用户额外奖励入库时发送一条延时24小时的消息,消息主体为userId+奖励金额。

消息消费时可根据userId和出账Type及时间范围查询出用户金币使用期内的出账总额。若出账小于奖励金额则进行扣减操作。

同时为防止消息丢失,可以每晚执行一个定时任务,扫描出当天获取额外奖励的新签到用户及其出账记录,通过stream流进行比对,将异常用户id返回(或直接在定时任务内进行处理)(因where条件均有索引且可以将定时任务设置在晚上3、4点等用户不活跃的时间,所以对数据库负载较小)。

因为产品要求签到日期取整点,可以通过 Calendar类获取当前整点时间进行运算后发送消息

public static Long getNowHour() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, 0);
        calendar.set(Calendar.HOUR_OF_DAY, calendar.get(HOUR_OF_DAT));
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        return calendar.getTimeInMillis()/1000;
    }

 

首页根据签到天数展示不同文案

通过是否是新签到用户和是否签到展示不同文案。

若是新签到用户且未签到,可根据昨日 签到天数day对应1-6展示不同的提示文案(若昨日也未签到则不可能是新签到用户)

 

 

如果有错误或者更优化的解决方案,欢迎大家在评论区留言探讨。

也可以给我的个人公众号私信留言。