注:需求不一样则业务逻辑不一样
不说废话,直接干!!!!
Mysql:
CREATE TABLE `t_user_sign` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '用户ID',
`days` int(11) NOT NULL COMMENT '累计签到天数',
`year` int(11) NOT NULL COMMENT '年',
`month` tinyint(4) NOT NULL COMMENT '月',
`sign_record` varchar(255) NOT NULL COMMENT '签到记录',
`last_sign_time` char(19) NOT NULL COMMENT '上次签到时间',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0-无效,1-有效',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4;
1、控制器代码
/**
* 页面初始化,返回结果给前端
*/
public function index()
{
$user_id = Request::input('user_id');
$year = date('Y');
$month = date('n');
$signKey = 'sign_' . $year . '_' . $month . '_' . $user_id;
$data = UserSignService::signDays($user_id,$signKey);
$data['year'] = $year;
$data['month'] = $month;
return Utils::success($data);
}
/**
* 签到/补签 这里使用了两种Redis数据类型,bitmap、hash
*/
public function sign()
{
$user_id = Request::input('user_id');
$type = Request::input('type',1); // 1、签到 , 2、补签
$day = Request::input('day',0);
if (!Utils::isValue($user_id)){
return Utils::fail('无效user_id');
}
if ($type == 2){
if (!Utils::isValue($day)){
return Utils::fail('无效day');
}
}else{
$day = Utils::getNowDay();
}
if ($day > Utils::getNowDay()){
return Utils::fail('补签的日期不能大于等于今天');
}
$year = date('Y');
$month = date('n');
$signKey = 'sign_' . $year . '_' . $month . '_' . $user_id;
$sign = Redis::getbit($signKey,$day);
if ($type == 2){
if (Utils::isValue($sign)){
return Utils::success('您已补签,无需补签');
}
}else{
if (Utils::isValue($sign)){
return Utils::success('您今天已签到');
}
}
Redis::setbit($signKey,0,1);
Redis::setbit($signKey,$day,1);
$data = UserSignService::signDays($user_id,$signKey);
$datas = [
'user_id' => $user_id,
'days' => Utils::getValue($data,'days'),
'year' => $year,
'month' => $month,
'sign_record' => Utils::getValue($data,'signDay'),
'last_sign_time' => date('Y-m-d H:i:s')
];
// 将用户的记录存放hash,到下个月初跑一次记录放进DB。注:需求不一样则业务逻辑不一样
Redis::hset('sign_record',$user_id,json_encode($datas));
// 缓存过期时间
Redis::expire($signKey,3024000);
Redis::expire('sign_record',3024000);
if ($type == 2){
return Utils::success('补签成功');
}else{
return Utils::success('签到成功');
}
}
2、Service 代码
class UserSignService
{
/**
* 获取用户哪一天签到的? 和 签到总天数
* @param $user_id '用户ID'
* @param $signKey 'key'
* @return array
*/
public static function signDays($user_id,$signKey): array
{
$bitmap_bin_str = self::strToBin(Redis::get($signKey));
//调试使用,输出本月每天签到情况
$month = date('t');
$signDay = 0;
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 "
";
} else {
$signDay += 1;
// echo "$i 日签到";
// echo "
";
}
}
return [
'signDay' => substr($bitmap_bin_str,1),
'days' => $signDay
];
}
/**
* 转化数据
* @param $str
* @return string
*/
public static function strToBin($str)
{
$arr = preg_split('/(?, $str);
foreach($arr as &$v){
$temp = unpack('H*', $v);
$v = base_convert($temp[1], 16, 2);
unset($temp);
}
return join(' ',$arr);
}
}
3、脚本command
public function handle()
{
$datas = Redis::hgetall('sign_record');
foreach ($datas as $data){
$data = json_decode($data);
$user_id = Utils::getValue($data,'user_id');
$where = [
'user_id' => $user_id,
'year' => date('Y'),
'month' => date('n'),
];
$userSignModel = UserSign::where($where)->first();
if (Utils::isValue($userSignModel)){
$userSignModel->toArray();
}
$userSignRecord = [
'days' => Utils::getValue($data,'days'),
'sign_record' => Utils::getValue($data,'sign_record'),
'last_sign_time' => Utils::getValue($data,'last_sign_time')
];
if (Utils::isValue($userSignModel)){
DB::table('user_sign')->where($where)->update($userSignRecord);
}else{
$userSignRecord['user_id'] = Utils::getValue($data,'user_id');
$userSignRecord['year'] = Utils::getValue($data,'year');
$userSignRecord['month'] = Utils::getValue($data,'month');
DB::table('user_sign')->insert($userSignRecord);
}
}
echo '执行成功';
}
如有Command脚本不会的,自己私下百度。
列出执行命令:php artisan list
发现存在,即可执行,php artisan lesson:sign