在设计签到的同时,需要考虑补签问题。
在技术选型方面我选择了redis的bitmap。bitmap占用空间少,运行速度快。
我这里使用的是laravel。
laravel操作redis需要先composer
composer require predis/predis
首先是签到的代码
/**
*用户签到接口
* @param int $uid
*/
public static function sign($uid)
{
//获取当前时间
$now = date('Ym');
$day = date('d');
//拼接bitmap的key
$key = $uid.':'.$now;
//查看是否签到过
$date = Redis::getbit($key,$day);
if($date){
return showMsg(200,'今天签到过了');
}
//获取昨天时间
$lastDay = date("d", strtotime("-1 day"));
//查看昨天是否签到
$lastDate = Redis::getbit($key,$lastDay);
$user = \App\Models\EbUser::getOne($uid);
Redis::setbit($key,0,1);
//昨天签到(连签)
if($lastDate){
//积分叠加 bitmap存已签到
$integral = $user['integral'] + 10 + ($user['sign_num']*15);
$update['integral'] = $integral;
$update['sign_num'] = $user['sign_num'] + 1;
$result = EbUser::updateUser($uid,$update);
Redis::setbit($key,$day,1);
return showMsg(200,'连续签到成功');
}
//昨天未签到(断签)
$update['sign_num'] = 1;
$update['integral'] = $user['integral'] + 10;
$result = EbUser::updateUser($uid,$update);
Redis::setbit($key,$day,1);
return showMsg(200,'断签了,签到成功');
}
查询当月签到情况
/**
* 查询用户本月签到情况
* @param $uid
*/
public static function getMonthSign($uid,$date)
{
//获取用户签到信息
$key = $uid.':'.$date;
$sign = Redis::get($key);
//处理数据
$bitmap_bin_str = self::strToBin($sign);
//调试使用,输出本月每天签到情况
// $month = date('t');
// for($i=1;$i<=$month;$i++){
// if(empty($bitmap_bin_str[$i])){
// $bitmap_bin_str[$i] = 0;
// }
// if($bitmap_bin_str[$i]!=1){
// echo "$i 日未签到";
// echo "<br>";
// }else{
// echo "$i 日签到";
// echo "<br>";
// }
// }
return showMsg(200,'用户签到信息',$bitmap_bin_str);
}
数据转数值
/**
* 数据转换为数字
* @param $str
* @return string
*/
public static function strToBin($str){
$arr = preg_split('/(?<!^)(?!$)/u', $str);
foreach($arr as &$v){
$temp = unpack('H*', $v);
$v = base_convert($temp[1], 16, 2);
unset($temp);
}
return join(' ',$arr);
}
用户补签,并重新计算连签次数
/**
* 补签并重新计算连续签到
* @param $uid
* @param $date
* @return false|string
*
*/
public static function retroactive($uid, $date)
{
$now = date('Ymd');
if ($date >= $now) {
return showMsg('500', '补签的日期不能大于等于今天');
}
$day = substr($date, 6, 2);
$month = substr($date, 0, 6);
$key = $uid . ':' . $month;
$sign = Redis::getbit($key, $day);
if ($sign == 1) {
return showMsg('200', '你已经签到过了不需要补签.');
}
Redis::setbit($key, $day, 1);
$month = date('Ym');
$day = date('d') - 1;
$signKey = $uid . ':' . $month;
$sign = Redis::getbit($signKey, $day);
$i = 1;
if ($sign == 1) {
self::getNum($uid, $month, $day,$i);
//获取当前时间
$now = date('Ym');
$day = date('d');
//拼接bitmap的key
$key = $uid . ':' . $now;
//查看是否签到过
$date = Redis::getbit($key, $day);
if($date == 1){
EbUser::updateUser($uid,['sign_num'=>$i+1]);
return showMsg(200,'补签成功');
}
EbUser::updateUser($uid,['sign_num'=>$i]);
return showMsg(200,'补签成功');
}
//获取当前时间
$now = date('Ym');
$day = date('d');
//拼接bitmap的key
$key = $uid . ':' . $now;
//查看是否签到过
$date = Redis::getbit($key, $day);
if($date == 1){
EbUser::updateUser($uid,['sign_num'=>1]);
return showMsg(200,'补签成功');
}
EbUser::updateUser($uid,['sign_num'=>0]);
return showMsg(200,'补签成功');
}
通过递归获取连签天数
/**
* 获取用户连续签到天数
* @param $uid
* @param $date
* @param $day
* @param $i
*/
public static function getNum($uid, $date, $day,&$i)
{
try {
// if (strlen($day) == 1) $day = '0' . $day;
$key = $uid . ':' . $date;
$sign = Redis::getbit($key, $day);
if ($sign == 1) {
$i ++;
if ($day - 1 > 0) {
self::getNum($uid, $date, $day - 1,$i);
}else{
// \xff\xf4
$time = strtotime($date . '0' . $day) - 86400;
$mytime = date('Ymd', $time);
$day = substr($mytime, 6, 2);
$month = substr($mytime, 0, 6);
self::getNum($uid, $month, $day,$i);
}
}
// return '连续签到'.$i.'天';
}catch (\Exception $e){
return showMsg(500,$e->getMessage());
}
}