第5章 今天的瓶子已经用完啦 - nswbmw/N-drifter GitHub Wiki

我们在玩漂流瓶的时候,会遇到瓶子用完的情况,下面我们来实现这个功能。

我们设定:每个用户一天只能扔 10 个瓶子,捡 10 个瓶子。

今天扔瓶子的机会已经用完啦~

首先我们来实现限制扔瓶次数的功能。打开 redis.js ,添加如下代码:

// 检查用户是否超过扔瓶次数限制
function checkPickTimes(owner, callback) {
  pool.acquire(function (err, client) {
    if (err) {
      return callback({code: 0, msg: err});
    }
    // 到2号数据库检查用户是否超过扔瓶次数限制
    client.SELECT(2, function() {
      // 获取该用户捡瓶次数
      client.GET(owner, function (err, result) {
        if (result >= 10) {
          return callback({code: 0, msg: "今天扔瓶子的机会已经用完啦~"});
        }
        // 捡瓶次数加 1
        client.INCR(owner, function() {
          // 检查是否是当天第一次扔瓶子
          // 若是,则设置记录该用户扔瓶次数键的生存期为 1 天
          // 若不是,生存期保持不变
          client.TTL(owner, function (err, ttl) {
            if (ttl === -1) {
              client.EXPIRE(owner, 86400, function () {
                // 释放连接
                pool.release(client);
              });
            } else {
              // 释放连接
              pool.release(client);
            }
            callback({code: 1, msg: ttl});
          });
        });
      });
    });
  });
}

将:

exports.throw = function(bottle, callback) {
  throwOneBottle(bottle, function (result) {
    callback(result);
  });
}

修改为:

exports.throw = function(bottle, callback) {
  checkThrowTimes(bottle.owner, function (result) {
    if (result.code === 0) {
      return callback(result);
    }
    throwOneBottle(bottle, function (result) {
      callback(result);
    });
  });
}

我们将记录用户扔瓶次数的键(字符串类型)保存到了 2 号数据库。

以上代码的意思是:在用户扔瓶子前,首先到 2 号数据库中检查该用户对应的值(即扔瓶次数)是否超过 10 。如果超过 10 ,返回错误信息,如果没有超过 10 ,则将该用户对应的扔瓶次数加 1 ,并设置用户键的生存期。

Redis 的 TTL 命令是以秒为单位,返回给定 key 的剩余生存时间,若没有设置生存时间则返回 -1 。若 ttl 为 -1 ,则说明该用户今天第一次扔瓶子,则设置该用户对应 key 的生存期为 1 天,若 ttl 不为 -1 说明该用户不是今天第一次扔瓶子,则生存期不做改变。

今天捡瓶子的机会已经用完啦~

现在我们来实现限制用户捡瓶次数的功能。

同理,打开 redis.js ,添加如下代码:

// 检查用户是否超过捡瓶次数限制
function checkPickTimes(owner, callback) {
  pool.acquire(function (err, client) {
    if (err) {
      return callback({code: 0, msg: err});
    }
    // 到 3 号数据库检查用户是否超过捡瓶次数限制
    client.SELECT(3, function() {
      // 获取该用户捡瓶次数
      client.GET(owner, function (err, result) {
        if (result >= 10) {
          return callback({code: 0, msg: "今天捡瓶子的机会已经用完啦~"});
        }
        // 捡瓶次数加 1
        client.INCR(owner, function() {
          // 检查是否是当天第一次捡瓶子
          // 若是,则设置记录该用户捡瓶次数键的生存期为 1 天
          // 若不是,生存期保持不变
          client.TTL(owner, function (err, ttl) {
            if (ttl === -1) {
              client.EXPIRE(owner, 86400, function () {
                // 释放连接
                pool.release(client);
              });
            } else {
              // 释放连接
              pool.release(client);
            }
            callback({code: 1, msg: ttl});
          });
        });
      });
    });
  });
}

将:

exports.pick = function(info, callback) {
  // 20% 概率捡到海星
  if (Math.random() <= 0.2) {
    return callback({code: 1, msg: "海星"});
  }
  pickOneBottle(info, function (result) {
    callback(result);
  });
}

修改为:

exports.pick = function(info, callback) {
  checkPickTimes(info.user, function (result) {
    if (result.code === 0) {
      return callback(result);
    }
    // 20% 概率捡到海星
    if (Math.random() <= 0.2) {
      return callback({code: 1, msg: "海星"});
    }
    pickOneBottle(info, function (result) {
      callback(result);
    });
  });
}

这里我们使用 3 号数据库存储每个用户捡瓶的次数。

现在,我们完成了限制用户扔瓶子和捡瓶子次数的功能。