php+redise用户连续签到

业务需求:

![用户签到.png](https://upload-images.jianshu.io/upload_images/8916618-28d8a4d2f6ba820c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

问题点:

  • 积分签到有循环周期,连续签到的天数达到了循环的天数,那么重新开始新一轮签到。
  • 积分签到因本项目的也无需求,需要区分添加到用户积分中,还是会员积分中。
  • 若未设置用户签到赠送的积分、连续签到的积分时,只更新签到的天数不添加用户积分、也不写入积分赠送记录。
  • 若当日赠送积分已经达到当日赠送的积分上限,那么不赠送(当日赠送积分已到99积分,当日最大赠送积分100,签到赠送10积分,意思再送1积分就达到上限,那么也不赠送)

功能实现:

  • 数据库对积分赠送记录储存:包含每日赠送积分记录、连续签到额外赠送积分记录
  • redis储存用户签到的天数、最后签到的时间
  • 队列处理赠送积分到用户

签到业务思维导图

用户签到.png

签到业务核心代码

<?php

namespace app\user\service;


/*
 *   用户签到模块
 */

use app\general\model\spreadPlatformUserModel;
use app\platform\model\spreadCardModel;
use app\platform\service\spreadCardScoreLogService;
use app\platform\service\spreadCardScoreRuleService;
use app\platform\service\spreadCardService;
use app\platform\service\spreadPlatformUserService;
use app\user\cache\signCache;
use think\Db;

class userSignService
{
    /*
     *todo 用户签到,分发到对应的用户角色中(区分用户、会员积分)
     */
    public static function userSign($spreadId, $userId)
    {
        if (empty($spreadId) || empty($userId)) {
            recordError('参数缺失');
            return false;
        }
        //todo 获取平台设置的积分规则设置
        $spreadRule = spreadCardScoreRuleService::getCardScoreRule($spreadId, true);
        //todo 查看用户是否是平台用户
        $userInfo = spreadPlatformUserService::getPlatformUserInfoByUserId($spreadId, $userId);
        if (empty($userInfo)) {
            //todo 不存在平台用户,则新增平台用户
            $addNewPlatUserData = [
                'user_id' => $userId,
                'spread_id' => $spreadId,
                'recommend_num' => '',
                'user_type' => spreadPlatformUserModel::USER_TYPE_REG,
                'reg_time' => time(),
                'latest_time' => time(),
                'last_order' => time(),
            ];
            spreadPlatformUserService::addPlatUser($addNewPlatUserData);
        } else {
            if ($userInfo['status'] == spreadPlatformUserModel::STATE_DISABLE) {
                recordError('抱歉,您已被禁用,不能签到!');
                return false;
            }
            $cardUser = spreadCardService::getCardByUserIdV3($userId, $spreadId);
        }
        //todo 开始分发签到到对应角色
        if (isset($cardUser) && $cardUser['status'] != spreadCardModel::STATUS_HIDDEN) {
            return self::addScoreToCard($spreadId, $userId, $cardUser, $spreadRule);
        } else {
            return self::addScoreToUser($spreadId, $userId, $userInfo, $spreadRule);
        }
    }


    /*
     * todo 逻辑处理用户是否签到,是否是连续签到,是否超过循环周期
     */
    public static function addScoreToUser($spreadId, $userId, $userInfo, $spreadRule)
    {
        //todo 查询缓存的签到天数与最后签到时间
        $oldSignDay = signCache::getUserSign($spreadId, $userId);
        //初始签到天数
        $newSignDay = [
            'day' => 1,
            'last_time' => time()
        ];
        //获取循环周期
        $defaultDay = empty($spreadRule['cycle']) ? 7 : $spreadRule['cycle'];
        // 计算上一天的开始时间
        $lastDayStartTime = strtotime(date('Y-m-d') . ' -1 day');
        //todo 判断是否是缓存的最后签到时间
        if (empty($oldSignDay) || $oldSignDay == false) {
            //如果没有设置相关签到的积分赠送规则,那么返回缓存签到的天数
            if($spreadRule['day_get_score'] == 0 && empty($spreadRule['continue_info'])){
                if (!signCache::setUserSIgn($spreadId, $userId, $newSignDay)) {
                    recordError('签到失败');
                    return false;
                }
                return $spreadRule['day_get_score'];
            }
            return self::setUserSignScore($spreadId, $userId, $newSignDay, $userInfo, $spreadRule);
        } else {
            //判断用户当天是否已经签到
            if ($oldSignDay['last_time'] >= strtotime(date("Y-m-d", time())) && $oldSignDay['last_time'] <= strtotime(date("Y-m-d " . '23:59:59', time()))) {
                recordError('抱歉,已经签到过了');
                return false;
            }
            //判断最后的签到天数是否等于循环的周期
            if($oldSignDay['day'] == $defaultDay){
                //如果没有设置相关签到的积分赠送规则,直接写入缓存,返回每日赠送的积分
                if($spreadRule['day_get_score'] == 0 && empty($spreadRule['continue_info'])){
                    if (!signCache::setUserSIgn($spreadId, $userId, $newSignDay)) {
                        recordError('签到失败');
                        return false;
                    }
                    return $spreadRule['day_get_score'];
                }
                return self::setUserSignScore($spreadId, $userId, $newSignDay, $userInfo, $spreadRule);
            }
            //判断用户的最后签到时间是否在上一天之内
            if ($oldSignDay['last_time'] < strtotime(date("Y-m-d", time())) && $oldSignDay['last_time'] >=
                $lastDayStartTime) {
                $newSignDay = [
                    'day' => $oldSignDay['day'] + 1,
                    'last_time' => time(),
                ];
                //如果没有设置相关签到的积分赠送规则,直接写入缓存,返回每日赠送的积分
                if($spreadRule['day_get_score'] == 0 && empty($spreadRule['continue_info'])){
                    if (!signCache::setUserSIgn($spreadId, $userId, $newSignDay)) {
                        recordError('签到失败');
                        return false;
                    }
                    return $spreadRule['day_get_score'];
                }
                return self::setUserSignScore($spreadId, $userId, $newSignDay, $userInfo, $spreadRule);
            } else {
                //如果没有设置相关签到的积分赠送规则,直接写入缓存,返回每日赠送的积分
                if($spreadRule['day_get_score'] == 0 && empty($spreadRule['continue_info'])){
                    if (!signCache::setUserSIgn($spreadId, $userId, $newSignDay)) {
                        recordError('签到失败');
                        return false;
                    }
                    return $spreadRule['day_get_score'];
                }
                return self::setUserSignScore($spreadId, $userId, $newSignDay, $userInfo, $spreadRule);
            }
        }
    }

    /*
      * todo 逻辑处理会员是否签到,是否是连续签到,是否超过循环周期
      */
    public static function addScoreToCard($spreadId, $userId, $cardUser, $spreadRule)
    {
        //todo 查询缓存的签到天数与最后签到时间
        $oldSignDay = signCache::getUserSign($spreadId, $userId);
        //初始签到天数
        $newSignDay = [
            'day' => 1,
            'last_time' => time()
        ];
        //获取循环周期
        $defaultDay = empty($spreadRule['cycle']) ? 7 : $spreadRule['cycle'];
        // 计算上一天的开始时间
        $lastDayStartTime = strtotime(date('Y-m-d') . ' -1 day');
        //todo 判断是否是缓存的最后签到时间
        if (empty($oldSignDay) || $oldSignDay == false) {
            //如果没有设置相关签到的积分赠送规则,直接写入缓存,返回每日赠送的积分
            if($spreadRule['day_get_score'] == 0 && empty($spreadRule['continue_info'])){
                if (!signCache::setUserSIgn($spreadId, $userId, $newSignDay)) {
                    recordError('签到失败');
                    return false;
                }
                return $spreadRule['day_get_score'];
            }
            return self::setCardSignScore($spreadId, $userId, $newSignDay, $cardUser, $spreadRule);
        } else {
            //判断用户当天是否已经签到
            if ($oldSignDay['last_time'] >= strtotime(date("Y-m-d", time())) && $oldSignDay['last_time'] <= strtotime(date("Y-m-d " . '23:59:59', time()))) {
                recordError('抱歉,已经签到过了');
                return false;
            }
            //判断最后的签到天数是否等于循环的周期
            if($oldSignDay['day'] == $defaultDay){
                //如果没有设置相关签到的积分赠送规则,直接写入缓存,返回每日赠送的积分
                if($spreadRule['day_get_score'] == 0 && empty($spreadRule['continue_info'])){
                    if (!signCache::setUserSIgn($spreadId, $userId, $newSignDay)) {
                        recordError('签到失败');
                        return false;
                    }
                    return $spreadRule['day_get_score'];
                }
                return self::setCardSignScore($spreadId, $userId, $newSignDay, $cardUser, $spreadRule);
            }
            //判断最后签到的时间是否在上一天的时间内
            if ($oldSignDay['last_time'] < strtotime(date("Y-m-d", time())) && $oldSignDay['last_time'] >=
                $lastDayStartTime) {
                $newSignDay = [
                    'day' => $oldSignDay['day'] + 1,
                    'last_time' => time(),
                ];
                //如果没有设置相关签到的积分赠送规则,直接写入缓存,返回每日赠送的积分
                if($spreadRule['day_get_score'] == 0 && empty($spreadRule['continue_info'])){
                    if (!signCache::setUserSIgn($spreadId, $userId, $newSignDay)) {
                        recordError('签到失败');
                        return false;
                    }
                    return $spreadRule['day_get_score'];
                }
                return self::setCardSignScore($spreadId, $userId, $newSignDay, $cardUser, $spreadRule);
            } else {
                //如果没有设置相关签到的积分赠送规则,直接写入缓存,返回每日赠送的积分
               if($spreadRule['day_get_score'] == 0 && empty($spreadRule['continue_info'])){
                   if (!signCache::setUserSIgn($spreadId, $userId, $newSignDay)) {
                       recordError('签到失败');
                       return false;
                   }
                   return $spreadRule['day_get_score'];
               }
               return self::setCardSignScore($spreadId, $userId, $newSignDay, $cardUser, $spreadRule);
            }
        }
    }


    /*
     * todo 获取所有必备的增加用户积分的参数
     */
    public static function setUserSignScore($spreadId, $userId, $newSignDay, $userInfo, $spreadRule)
    {
        if(empty($spreadId) || empty($userId) || empty($newSignDay) || empty($userInfo) || empty($spreadRule)){
            recordError('参数错误');
            return false;
        }
        $scoreRule = isset($spreadRule['continue_info']) ? json_decode($spreadRule['continue_info'], true) : [];
        //判断后台是否设置的有连续签到规则
        if (!empty($scoreRule)) {
            foreach ($scoreRule as $rule) {
                if ($rule['day'] == $newSignDay['day']) {
                    $extraScore = $rule['score'];
                }
            }
        }
        $userTodayScore = spreadCardScoreLogService::getTodayPlatUserGetScore($spreadId, $userId);
        return self::addScoreUserScore($spreadId, $userId, $newSignDay, $userInfo, $spreadRule, $userTodayScore, isset
        ($extraScore) ? $extraScore : 0);
    }

    /*
     * todo 获取所有必备的增加会员积分的参数
     */
    public static function setCardSignScore($spreadId, $userId, $newSignDay, $cardInfo, $spreadRule)
    {
        if(empty($spreadId) || empty($userId) || empty($newSignDay) || empty($cardInfo) || empty($spreadRule)){
            recordError('参数错误');
            return false;
        }
        $scoreRule = isset($spreadRule['continue_info']) ? json_decode($spreadRule['continue_info'], true) : [];
        //判断后台是否设置的有连续签到规则
        if (!empty($scoreRule)) {
            foreach ($scoreRule as $rule) {
                if ($rule['day'] == $newSignDay['day']) {
                    $extraScore = $rule['score'];
                }
            }
        }
        $userTodayScore = spreadCardScoreLogService::getTodayPlatUserGetScore($spreadId, $userId);
        return self::addScoreCardScore($spreadId, $userId, $newSignDay, $cardInfo, $spreadRule, $userTodayScore, isset
        ($extraScore) ? $extraScore : 0);
    }

    /*
     * todo  事务新增用户签到积分
     */
    public static function addScoreUserScore($spreadId, $userId, $newSignDay, $userInfo, $spreadRule,
                                             $userTodayScore, $extraScore)
    {
        Db::startTrans();
        $where = [
            'spread_id' => $spreadId,
            'user_id' => $userId,
            'status' => ['in', [spreadCardModel::STATUS_NORMAL, spreadCardModel::STATUS_FORBIDDEN]]
        ];
        if (!empty($newSignDay) && $newSignDay['day'] > 0) {
            if ($userTodayScore >= $spreadRule['today_get_score_limit']) {
                if (!signCache::setUserSIgn($spreadId, $userId, $newSignDay)) {
                    recordError('签到失败');
                    return false;
                }
                return $spreadRule['day_get_score'] + $extraScore;
            } else {
                if($spreadRule['day_get_score'] > 0 || $extraScore > 0){
                    if (!spreadPlatformUserService::incScore($where, $spreadRule['day_get_score'] + $extraScore)) {
                        recordError('增加用户积分失败');
                        Db::rollback();
                        return false;
                    }
                }
                if($spreadRule['day_get_score'] > 0){
                    if (!spreadCardScoreLogService::addUserSignScoreLog($spreadId, $userInfo, $spreadRule['day_get_score'])) {
                        recordError('增加用户签到赠送积分记录失败');
                        Db::rollback();
                        return false;
                    }
                }
                if($extraScore > 0){
                    if (!spreadCardScoreLogService::addUserSignExtraScoreLog($spreadId, $userInfo, $extraScore,
                        $newSignDay['day'], $spreadRule['day_get_score'])) {
                        recordError('增加用户连续签到赠送积分记录失败');
                        Db::rollback();
                        return false;
                    }
                }
                if (!signCache::setUserSIgn($spreadId, $userId, $newSignDay)) {
                    recordError('签到失败');
                    Db::rollback();
                    return false;
                }
            }
        }else{
            recordError('签到失败');
            return false;
        }
        Db::commit();
        return $spreadRule['day_get_score'] + $extraScore;
    }

    /*
     * todo  事务新增会员签到积分
     */
    public static function addScoreCardScore($spreadId, $userId, $newSignDay, $cardInfo, $spreadRule,
                                             $userTodayScore, $extraScore)
    {
        Db::startTrans();
        $where = [
            'spread_id' => $spreadId,
            'user_id' => $userId,
            'status' => ['in', [spreadCardModel::STATUS_NORMAL, spreadCardModel::STATUS_FORBIDDEN]]
        ];
        if (!empty($newSignDay) && $newSignDay['day'] > 0) {
            if ($userTodayScore >= $spreadRule['today_get_score_limit']) {
                if (!signCache::setUserSIgn($spreadId, $userId, $newSignDay)) {
                    recordError('签到失败');
                    return false;
                }
                return $spreadRule['day_get_score'] + $extraScore;
            } else {
                if($spreadRule['day_get_score'] > 0 || $extraScore > 0){
                    if (!spreadCardService::incScore($where, $spreadRule['day_get_score'] + $extraScore)) {
                        recordError('增加会员积分失败');
                        Db::rollback();
                        return false;
                    }
                }
                if($spreadRule['day_get_score'] > 0){
                    if (!spreadCardScoreLogService::addUserSignScoreLog($spreadId, $cardInfo, $spreadRule['day_get_score'])) {
                        recordError('增加会员签到赠送积分记录失败');
                        Db::rollback();
                        return false;
                    }
                }
                if($extraScore > 0){
                    if (!spreadCardScoreLogService::addUserSignExtraScoreLog($spreadId, $cardInfo, $extraScore,
                        $newSignDay['day'], $spreadRule['day_get_score'])) {
                        recordError('增加会员连续签到赠送积分记录失败');
                        Db::rollback();
                        return false;
                    }
                }
                if (!signCache::setUserSIgn($spreadId, $userId, $newSignDay)) {
                    recordError('签到失败');
                    Db::rollback();
                    return false;
                }
            }
        }else{
            recordError('签到失败');
            return false;
        }
        Db::commit();
        return $spreadRule['day_get_score'] + $extraScore;
    }

    /*
     *todo 获取用户的签到天数
     */
    public static function getUserSignDay($spreadId, $userId)
    {
        $newSignDay = [
            'day' => 0,
            'last_time' => 0
        ];
        if (empty($spreadId) || empty($userId)) {
            return $newSignDay;
        }
        //todo 查询缓存的签到天数与最后签到时间
        $oldSignDay = signCache::getUserSign($spreadId, $userId);
        //获取平台设置的积分规则设置
        $spreadRule = spreadCardScoreRuleService::getCardScoreRule($spreadId, true);
        //平台设置的签到天数 默认7
        $defaultDay = isset($spreadRule['cycle']) ? $spreadRule['cycle'] : 7;
        //todo 判断是有缓存的最后签到时间
        if (empty($oldSignDay) || $oldSignDay == false) {
            return $newSignDay;
        } else {
            //todo 判断缓存的最后时间是否已经超出了循环签到的周期
            if ($oldSignDay['last_time'] <= time() && $oldSignDay['last_time'] > (time() - $defaultDay * 24 * 60 *
                    60)) {
                return $oldSignDay;
            } else {
                return $newSignDay;
            }
        }
    }
}

redis缓存用户签到的天数和最后签到时间

<?php

namespace app\user\cache;

use app\platform\service\spreadCardScoreRuleService;
use think\cache\driver\Redis;

/**
 * All rights reserved.
 * 用户签到缓存
 */
class signCache
{

    const SPREAD_USER_SIGN_DAY = 'SPREAD:USER:SIGN:DAY';

    /*
     * 获取用户签到缓存
     * @param $unityId
     * @return string
     */
    private static function getSignCacheKey($spreadId, $userId)
    {
        return self::SPREAD_USER_SIGN_DAY . $spreadId . "_" . $userId;
    }

    /*
     * 获取平台签到缓存
     * @param $unityId
     * @return string
     */
    private static function getSpreadSignCacheKey($spreadId)
    {
        return self::SPREAD_USER_SIGN_DAY . $spreadId;
    }

    /*
     * 获取用户签到缓存
     * @param $unityId
     * @return mixed
     */
    public static function getUserSign($spreadId, $userId)
    {
        $key = self::getSignCacheKey($spreadId, $userId);
        return Redis::make()->get($key);
    }

    /*
     * 设置用户签到缓存 缓存一周
     * @param $unityId
     * @param array $data
     * @return bool
     */
    public static function setUserSign($spreadId, $userId, $day)
    {
        $key = self::getSignCacheKey($spreadId, $userId);
        //获取平台设置的积分规则设置
        $spreadRule = spreadCardScoreRuleService::getCardScoreRule($spreadId, true);
        $defaultDay = empty($spreadRule['cycle']) ? 7 : $spreadRule['cycle'];
        $time = strtotime(date('Y-m-d', strtotime($defaultDay . ' day'))) - time();
        return Redis::make()->set($key, $day, $time);
    }

    /*
     * 清除平台签到缓存
     * @return bool
     */
    public static function clearSpreadSign($spreadId)
    {
        $redis = new Redis();
        $key = self::getSpreadSignCacheKey($spreadId);
        $pattern = $key . "_*";
        $time = strtotime(date('Y-m-d', time()) . "23:59:59");
        $keys = $redis->getKeys($pattern);
        if ($keys) {
            foreach ($keys as $k) {
                $redis->expireAt($k, $time);
            }
        }
        return true;
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容