连续签到送积分
1.数据表设计
积分表
CREATE TABLE `yixiang_users_coinrecord` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` varchar(20) NOT NULL COMMENT '用户的uid',
`type` varchar(20) NOT NULL COMMENT '收支类型',
`totalcoin` int(11) NOT NULL COMMENT '总数量',
`action` varchar(20) NOT NULL COMMENT '收支行为',
`addtime` varchar(255) NOT NULL COMMENT '添加时间',
PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=7160 DEFAULT CHARSET=utf8
签到表
CREATE TABLE `yixiang_signin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` varchar(100) NOT NULL COMMENT '用户id',
`cont_days` varchar(100) NOT NULL COMMENT '连续签到天数',
`last_signin_time` int(11) NOT NULL COMMENT '最后一次签到时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8
2.代码
/**
* 签到
*/
public function signin()
{
$this->uid_exist();
// $this->uid = 505;
$time = getdate();
$today_zero = mktime(0,0,0,$time['mon'],$time['mday'],$time['year']);
$signin_log = M('signin')->where(array('uid'=>$this->uid))->order("id desc")->find();
//一个月有多少天
$month_have_day = date("t",strtotime($time['year']."-".$time['mon']));
//如果今天的日期小于最后一次签到时间 最后一次签到时间小于明天
if ($today_zero < $signin_log['last_signin_time'] && $signin_log['last_signin_time'] < ($today_zero+(60*60*24)) )
{
$this->weberror(self::THIS_ACTION_ERROR,"你今日已经签过到咯");
}else{
M()->startTrans();
//如果是1号,将其设置为0
if ($time['mday'] ==1){
$updateData = array(
'uid'=>$this->uid,
'cont_days' => 1,
'last_signin_time' => time()
);
$update = M('signin')->add($updateData);
$gold = $this->get_reward_goldcoin($this->uid,'sign_days');
}else{
// 如果不是1号判断有没有连续签到,具体方法是判断昨天有没有签到,如果签到了,连续签到次数加一,如果没有签到,连续次数归一
if ( ($today_zero-24*60*60) < $signin_log['last_signin_time'] && $signin_log['last_signin_time'] < $today_zero )
{
$updateData = array(
'uid'=>$this->uid,
'cont_days' => $signin_log['cont_days']+1,
'last_signin_time' => time()
);
$update = M('signin')->add($updateData);
$cont_days = $signin_log['cont_days']+1; //连续签到天数
switch ($cont_days)
{
case 7 : $gold = $this->get_reward_goldcoin($this->uid,'sign_7days');break;
case 15 : $gold = $this->get_reward_goldcoin($this->uid,'sign_15days');break;
case $month_have_day : $gold = $this->get_reward_goldcoin($this->uid,'sign_1month');break;
default:$gold = $this->get_reward_goldcoin($this->uid,'sign_days');
}
}else{ //昨天没有签到
$updateData = array(
'uid'=>$this->uid,
'cont_days' => 1,
'last_signin_time' => time()
);
$update = M('signin')->add($updateData);
$gold = $this->get_reward_goldcoin($this->uid,'sign_days');
}
}
if($update && $gold){
M()->commit();
$LastData = M('signin')->where(array('uid'=>$this->uid))->order("id desc")->find();
if ($LastData['cont_days'] == $month_have_day){
$returnGold = M("m_config")->where(array("ckey"=>"sign_1month"))->getField("cvalue");
}elseif ($LastData['cont_days'] == 15){
$returnGold = M("m_config")->where(array("ckey"=>"sign_15days"))->getField("cvalue");
}elseif ($LastData['cont_days'] == 7){
$returnGold = M("m_config")->where(array("ckey"=>"sign_7days"))->getField("cvalue");
}else{
$returnGold = M("m_config")->where(array("ckey"=>"sign_days"))->getField("cvalue");
}
$this->websuccess(self::OUTPUT_SUCCESS,'恭喜你获得 '.$returnGold.'享金');
}else{
M()->rollback();
$this->weberror(self::THIS_ACTION_ERROR,"签到失败");
}
}
}
/**
* 获得签到记录
*/
public function getsignin()
{
$this->uid_exist();
// $this->uid = 505;
$time = getdate();
$today_zero = mktime(0,0,0,$time['mon'],$time['mday'],$time['year']);
$signin_log = M('signin')->where(array('uid'=>$this->uid))->order("id desc")->find();
$count = M('signin')->where(array('uid'=>$this->uid))->count();
//一个月有多少天
$month_have_day = date("t",strtotime($time['year']."-".$time['mon']));
//今天是否已经签到
if ($today_zero < $signin_log['last_signin_time'] && $signin_log['last_signin_time'] < ($today_zero+(60*60*24)) )
{
$issignin = true;
}else{
$issignin = false;
}
$records = M('signin')->where(array('uid'=>$this->uid))->select();
$golds = 0;
foreach ($records as $record)
{
$month_have_day = date("t",$record['last_signin_time']);
switch ($record['cont_days'])
{
case 7:$golds+=100;break;
case 15:$golds+=200;break;
case $month_have_day:$golds+=300;break;
default:$golds+=20;
}
}
$data = array(
'year'=>$time['year'],
'month'=>$time['mon'],
'cont_days' =>$signin_log['cont_days'],
'month_have_days'=>$month_have_day,
'count'=>$count,
'issignin'=>$issignin,
'gold'=>$golds
);
$this->websuccess(self::OUTPUT_SUCCESS,"返回的数据",$data);
}
另外一种实现方法:redis的bitmap
通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身。8个bit可以组成一个Byte,所以bitmap本身会极大的节省储存空间。
-
考虑到每月初需要重置连续签到次数,按用户每月存一条签到数据(也可以每年存一条数据)。
-
Key的格式为
u:sign:uid:yyyyMM
,Value则采用长度为4个字节(32位)的位图
(最大月份只有31天)。
是32个0101011111111111
的字母,也可以设置40位(也就是40个0,1) -
位图的每一位代表一天的签到,1表示已签,0表示未签。
例如u:sign:1000:201902
表示ID=1000的用户在2019年2月的签到记录。
# 用户2月17号签到
SETBIT u:sign:1000:201902 16 1 # 偏移量是从0开始,所以要把17减1
# 检查2月17号是否签到
GETBIT u:sign:1000:201902 16 # 偏移量是从0开始,所以要把17减1
# 统计2月份的签到次数
BITCOUNT u:sign:1000:201902
# 获取2月份前28天的签到数据
BITFIELD u:sign:1000:201902 get u28 0
# 获取2月份首次签到的日期
BITPOS u:sign:1000:201902 1 # 返回的首次签到的偏移量,加上1即为当月的某一天