前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis 每日签到功能·双十一预热活动

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

作者头像
Nian糕
修改2024-03-16 17:04:32
1.6K0
修改2024-03-16 17:04:32

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

需求分析

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

每日签到
每日签到

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

Model

代码语言:javascript
复制
// 构造函数
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 文件中设置两个新的方法,一个用来记录用户今日签到的天数是第几天,一个用来获取用户的所有签到天数

代码语言:javascript
复制
// 记录用户签到天数
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 中,因为签到的天数有多个,所以我们在这里使用队列进行签到天数的记录,该方法通过点击 “添加燃料” 按钮时触发,需要注意的是,当用户点击按钮多次时,将会记录多个数据到队列中去

代码语言:javascript
复制
// 获取用户签到的天数
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 调用该方法,获取用户的签到天数

运行结果
运行结果

Router

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

代码语言:javascript
复制
<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

代码语言:javascript
复制
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 的队列中只会记录多个当日天数,不会影响循环判断签到天数的结果

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.10.31 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 需求分析
  • Model
  • Router
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档