美文网首页Web前端之路程序员让前端飞
Redis 每日签到功能·双十一预热活动

Redis 每日签到功能·双十一预热活动

作者: Nian糕 | 来源:发表于2017-10-31 17:14 被阅读160次
    Unsplash

    Redis 常用于跨进程、跨服务器的数据缓存服务,我们通常会使用 Redis 来存储 Session 会话数据,而不会在程序重启、多进程运行、负载均衡、跨域等情况时,会出现 Session 丢失或多进程、多个负载站点间状态不能共享的情况,而在 Node.js 中,连接 Redis 要使用 node_redis 模块,关于更多的 node_redis 模块的详细介绍,可以戳 node_redis 的 Github 主页

    1. 需求分析

    双十一预热活动,活动时间一共为 10 天,在活动期间,每位用户每天有一次签到机会,签到成功后,会点亮签到界面中对应的天数,若是当天没有签到,则在第二天显示未签到样式

    每日签到

    我们把今天的样式命名为 todayCheck,如上图的第五天样式,把已签到的样式命名为 hasCheck,如上图的第一天样式,把未签到的样式命名为 notCheck,如上图的第三天样式,确定好每种状态的命名之后,我们先来列出需要实现的功能,分别是:① 用户未登录时,所有天数的样式为 todayCheck;② 用户登陆之后,将所有已签到的天数,所对应的日期样式更新为 hasCheck,未签到的天数更新为 notCheck;③ 用户今日首次点击添加燃料按钮后,更新今天的日期样式为 hasCheck,之后的点击都将提示 “今日已签到” 的提示语

    2. Model

    // 构造函数
    function act20171027rand () {}
    
    // 继承randBaseModel
    act20171027rand.prototype = new randBaseModel({
        actName: 'act20171027rand',
        startTime: 1508774400000, // 测试开始时间 2017-10-24 00:00:00 
        // startTime: 1509379200000, // todo 活动开始时间 2017-10-31 00:00:00
        endTime: 1510243199000, // 活动结束时间  2017-11-09 23:59:59
    })
    

    randBaseModel 抽奖模块基类是跟抽奖相关的,在这里没有涉及到,就不详细描述了,在活动正式上线时,需要将测试时间改成正式时间,需要修改的地方比较多,为了避免遗漏,建议大家在注释中加上 todo 进行标致,上线前 Ctrl + F 搜索一下所有的 todo 就好了,我们需要在 Model 文件中设置两个新的方法,一个用来记录用户今日签到的天数是第几天,一个用来获取用户的所有签到天数

    // 记录用户签到天数
    act20171027rand.prototype.getGetTimesPromise = function(userId){
        var that = this;
        var redisClient = this.redisClient;
        var userCheckInKey = this.actName + '_check_' + userId;
        
        // todo 测试时间 2017-10-24 00:00:00 1508774400
        // 活动正式开始时间 2017-10-31 00:00:00 1509379200
        var date = +new Date();
        var checkToday = Math.ceil((date - 1508774400000)/86400000)
        // var checkToday = Math.ceil((date - 1509379200000)/86400000)
        // console.log("抽奖1 今天是第几天" + checkToday);
    
        return new Promise(function(resolve, reject){
            // todo 写入今日签到的天数
            redisClient.rpush(userCheckInKey, checkToday, function(err, reply){
                if(err) return reject({code: 5005, result: 'redis写入签到天数失败'});
                resolve(checkToday);
            })
        })
    }
    

    通过时间戳来获取今天是第几天,并将其记录在 key 中,因为签到的天数有多个,所以我们在这里使用队列进行签到天数的记录,该方法通过点击 “添加燃料” 按钮时触发,需要注意的是,当用户点击按钮多次时,将会记录多个数据到队列中去

    // 获取用户签到的天数
    act20171027rand.prototype.getCheckInPromise = function(userId){
        console.log("获取签到天数 getCheckInPromise");
        var that = this;
        var redisClient = this.redisClient;
        var userCheckInKey = this.actName + '_check_' + userId;    
        return new Promise(function(resolve, reject){
            redisClient.lrange(userCheckInKey, 0, -1, function(err, reply){
                if(err) return reject({code: 5004, result: 'redis读取次数失败'});
                console.log("返回的签到天数 " + reply);
                resolve(reply);
            })
        })
    }
    

    当用户成功登陆后,router 调用该方法,获取用户的签到天数

    运行结果

    3. Router

    在 Model 文件中定义好相关的方法后,我们需要通过 Router 去读取相应的方法,不过在此之前,先来给大家看下签到界面的 HTML 结构

    <ul class="check clearfix">
        <li class="check_1">
            {{if check_1 == 'todayCheck'}}
           <img src="http://act.cycangcdn.com/20171027/171027/171027_pc_img/todayCheck_1.png">
            {{else if check_1 == 'hasCheck'}}
            <img src="http://act.cycangcdn.com/20171027/171027/171027_pc_img/hasCheck_1.png">
            {{else}}
            <img src="http://act.cycangcdn.com/20171027/171027/171027_pc_img/notCheck_1.png">
            {{/if}}
        </li>
        <li class="check_2">
            {{if check_2 == 'todayCheck'}}
           <img src="http://act.cycangcdn.com/20171027/171027/171027_pc_img/todayCheck_1.png">
            {{else if check_2 == 'hasCheck'}}
            <img src="http://act.cycangcdn.com/20171027/171027/171027_pc_img/hasCheck_1.png">
            {{else}}
            <img src="http://act.cycangcdn.com/20171027/171027/171027_pc_img/notCheck_1.png">
            {{/if}}
        </li>
        
        <!-- 一共10个 -->
        
        <li class="check_10">
            {{if check_10 == 'todayCheck'}}
           <img src="http://act.cycangcdn.com/20171027/171027/171027_pc_img/todayCheck_1.png">
            {{else if check_10 == 'hasCheck'}}
            <img src="http://act.cycangcdn.com/20171027/171027/171027_pc_img/hasCheck_1.png">
            {{else}}
            <img src="http://act.cycangcdn.com/20171027/171027/171027_pc_img/notCheck_1.png">
            {{/if}}
        </li>
    </ul>
    

    可以看到,我们是通过 check 的值来更换相应的日期样式,当用户未登录时,我们将所有日期样式设为 todayCheck

    var events = require('events');
    var emitter = new events.EventEmitter();
    var userModel = require(MODEL_PATH + '/userModel');
    var _userModel = new userModel();
    var act20171027RandModel = require(MODEL_PATH + '/act20171027RandModel.js');
    var _mod = new act20171027RandModel();
    
    module.exports = function act20171027(req, res) {
        res.setHeader('Content-Type', 'text/html; charset=utf-8');
        var data = {};
        data.username = '';
        data.times = '?';
        data.logState = 0;
        data.check_1 = 'todayCheck';
        data.check_2 = 'todayCheck';
        data.check_3 = 'todayCheck';
        data.check_4 = 'todayCheck';
        data.check_5 = 'todayCheck';
        data.check_6 = 'todayCheck';
        data.check_7 = 'todayCheck';
        data.check_8 = 'todayCheck';
        data.check_9 = 'todayCheck';
        data.check_10 = 'todayCheck';
        var checkAllDay = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; //签到的所有天数
    
        var _userModel = new userModel();
        _userModel.checkLogin(req, function(user_info, err, errCode){
            if (errCode == 200) {
                data.logState = 1;
                data.userName = user_info.username !== '未命名的用户' ? user_info.username : user_info.phone;
    
                // todo 获取已签到的天数
                // user_info.user_id 测试的时候使用固定的id
                _mod.getCheckInPromise(user_info.user_id).then(function(result){
                    var hasCheckDay = result; // 已签到的天数
    
                    // 更新已签到天数的img
                    for(var h = 0; h < hasCheckDay.length; h++){
                        if(hasCheckDay[h] == checkAllDay[0]){
                            data.check_1 = 'hasCheck';
                        }else if(hasCheckDay[h] == checkAllDay[1]){
                            data.check_2 = 'hasCheck';
                        }else if(hasCheckDay[h] == checkAllDay[2]){
                            data.check_3 = 'hasCheck';
                        }else if(hasCheckDay[h] == checkAllDay[3]){
                            data.check_4 = 'hasCheck';
                        }else if(hasCheckDay[h] == checkAllDay[4]){
                            data.check_5 = 'hasCheck';
                        }else if(hasCheckDay[h] == checkAllDay[5]){
                            data.check_6 = 'hasCheck';
                        }else if(hasCheckDay[h] == checkAllDay[6]){
                            data.check_7 = 'hasCheck';
                        }else if(hasCheckDay[h] == checkAllDay[7]){
                            data.check_8 = 'hasCheck';
                        }else if(hasCheckDay[h] == checkAllDay[8]){
                            data.check_9 = 'hasCheck';
                        }else if(hasCheckDay[h] == checkAllDay[9]){
                            data.check_10 = 'hasCheck';
                        }
                    }
                    
                    // 更新未签到天数的img
                    // todo 测试时间 2017-10-24 00:00:00 1508774400
                    // 活动正式开始时间 2017-10-31 00:00:00 1509379200
                    var date = +new Date();
                    var Today = Math.ceil((date - 1508774400000)/86400000);
                    // var Today = Math.ceil((date - 1509379200000)/86400000);
                    
                    var pastAllDay = checkAllDay.slice(0, Today - 1);               
                    
                    function differentNum(arr1, arr2){
                        var C = new Array();
                        var E = new Array();
                        var arrString = "," + arr2.toString() + ",";
                        for(var i in arr1 ){
                            if(arrString.indexOf("," + arr1[i] + ",") >= 0){
                                
                            }else{
                                C.push(arr1[i]);
                            }
                        }
                        return C;
                    }
                    
                    var noCheckDay = differentNum(pastAllDay, hasCheckDay);
                    for(var p in noCheckDay) {
                        if(noCheckDay[p] == pastAllDay[0]){
                            data.check_1 = 'notCheck';
                        }else if(noCheckDay[p] == pastAllDay[1]){
                            data.check_2 = 'notCheck';
                        }else if(noCheckDay[p] == pastAllDay[2]){
                            data.check_3 = 'notCheck';
                        }else if(noCheckDay[p] == pastAllDay[3]){
                            data.check_4 = 'notCheck';
                        }else if(noCheckDay[p] == pastAllDay[4]){
                            data.check_5 = 'notCheck';
                        }else if(noCheckDay[p] == pastAllDay[5]){
                            data.check_6 = 'notCheck';
                        }else if(noCheckDay[p] == pastAllDay[6]){
                            data.check_7 = 'notCheck';
                        }else if(noCheckDay[p] == pastAllDay[7]){
                            data.check_8 = 'notCheck';
                        }else if(noCheckDay[p] == pastAllDay[8]){
                            data.check_9 = 'notCheck';
                        }else if(noCheckDay[p] == pastAllDay[9]){
                            data.check_10 = 'notCheck';
                        }
                    }
                    
                    emitter.emit('show', req, res, data);
                }).catch(function(err){
                    res.write('发生错误了!~');
                    res.end();
                });
            }else{
                data.logState = 0;
                emitter.emit('show', req, res, data);
            }
        });
    }
    

    用户登录之后,调用 getCheckInPromise 方法,获取用户已签到天数,并将对应天数的 todayCheck 样式更换为 hasCheck 样式,接下来我们需要判断今天是第几天,假如今天是第三天,我只签了今天,前两天都没有签到,那我们需要将第一第二天的 todayCheck 样式更换为 notCheck 样式,具体实现可以看代码

    反复点击签到按钮

    而当用户反复点击签到按钮时,redis 的队列中只会记录多个当日天数,不会影响循环判断签到天数的结果

    最后,欢迎大家来签到 二次元购物节前哨-次元福利小火箭 effective time. 2017.10.31—2017.11.9

    End of File

    行文过程中出现错误或不妥之处在所难免,希望大家能够给予指正,以免误导更多人,最后,如果你觉得我的文章写的还不错,希望能够点一下喜欢关注,为了我能早日成为简书优秀作者献上一发助攻吧,谢谢!^ ^

    相关文章

      网友评论

        本文标题:Redis 每日签到功能·双十一预热活动

        本文链接:https://www.haomeiwen.com/subject/pqarpxtx.html