公共方法

构建健

设置redis的key,例如用户A获取2021-03的key返回user:sign:A:202103
private function buildSignKey($user, $date = '')
{
    return "user:sign:" . $user . ':' . $this->buildDate($date);
}

构建日期格式

将日期转换成指定的日期格式

  • 当$data=10说明位时间戳格式,直接转换成指定的日期格式
  • 否则将日期格式转换成时间戳后转成指定格式
private function buildDate($date = '', $conversion = 'Ym')
{
    if (empty($date)) {
        return '';
    }
    if (strlen($date) == 10 && is_numeric($date)) {
        return date($conversion, $date);
    }
    return date($conversion, strtotime($date));
}

构建日期位

打卡记录的位置位日期-1.例如03-01的在bitmap中的偏移量位1
private function getDateOffset($date)
{
    return intval(date("d", strtotime($date))) - 1;
}

流转位图

通过get获取bitmap的流文件,将留文件转换成位图,示例: 1000001000.
private function dealBinary($bitmap_str)
{
    // 对数据流使用网络字节序(大端)解包拿到16进制数据的字符串形式
    $hex_str = unpack("H*", $bitmap_str)[1];
    // hex str 的长度
    $hex_str_len = strlen($hex_str);
    // 为了防止 hex to dec 时发生溢出
    // 我们需要切分 hex str,使得每一份 hex str to dec 时都能落在 int 类型的范围内
    // 因为 2 位 16 进制表示一个字节,所以用系统 int 类型的字节长度去分组是绝对安全的
    $chunk_size = PHP_INT_SIZE;
    // 对 hex str 做分组对齐,否则 str 的最后几位可能会被当作低位数据处理
    // 比如 fffff 以 4 位拆分 'ffff', 'f' 后 最后一组 'f' 就被低位数据处理了
    // 对齐后 fffff000 分组 'ffff', 'f000' 就能保证 'f' 的数据位了
    $hex_str = str_pad($hex_str, $hex_str_len + ($chunk_size - ($hex_str_len % $chunk_size)), 0, STR_PAD_RIGHT);
    // 防止 hexdec 时溢出 使用 PHP_INT_SIZE 个 16 进制字符一组做拆分
    // 因 16 进制 2 位标识一个字节 所以 PHP_INT_SIZE 是绝对不会溢出的
    $hex_str_arr = str_split($hex_str, $chunk_size);
    // 位数据的二进制字符串
    $bitmap_bin_str = '';
    array_walk($hex_str_arr, function ($hex_str_chunk) use (&$bitmap_bin_str, $chunk_size) {
        $bitmap_bin_str .= str_pad(decbin(hexdec($hex_str_chunk)), $chunk_size * 4, 0, STR_PAD_LEFT);
    });
    return $bitmap_bin_str;
}

功能

签到

public function sign()
{
    $redis = RedisBase::getInstance();
    $date = !empty($this->params['date']) ? $this->params['date'] : date("Y-m-d");
    $offset = $this->getDateOffset($date);
    $signKey = $this->buildSignKey($this->user, $date);
    // 第一次设置值返回0,在设置返回1
    $res = $redis->setBit($signKey, $offset, true);
    if ($res) {
        return $this->error('不可重复签到');
    }
    return $this->success(['date' => $date], '签到成功');
}

验证签到

public function checkSign()
{
    $redis = RedisBase::getInstance();
    $date = !empty($this->params['date']) ? $this->params['date'] : date("Y-m-d");
    $offset = $this->getDateOffset($date);
    $signKey = $this->buildSignKey($this->user, $date);
    $res = $redis->getBit($signKey, $offset);
    if (empty($res)) {
        return $this->success(['status' => false]);
    }
    return $this->success(['status' => true]);
}

获取月签到次数

public function getSignNumByMonth()
{
    $redis = RedisBase::getInstance();
    $date = !empty($this->params['date']) ? $this->params['date'] : date("Y-m-d");
    $signKey = $this->buildSignKey($this->user, $date);
    $count = $redis->bitCount($signKey);
    return $this->success([
        'date' => $this->buildDate($date),
        'count' => $count
    ]);
}

获取签到日历

public function getSignContinuousNumByMonth()
{
    $redis = RedisBase::getInstance();
    $date = !empty($this->params['date']) ? $this->params['date'] : date("Y-m-d");
    $signKey = $this->buildSignKey($this->user, $date);
    // 计算该月有多少天
    $days = cal_days_in_month(CAL_GREGORIAN, date('m', strtotime($date)), date('Y', strtotime($date)));
    $stream = $redis->get($signKey);
    $cardInfo = $this->dealBinary($stream);
    $res = [];
    $month = substr($date, 0, 8);// 截取年-月- 例:2021-03-
    for ($i = 0; $i < $days; $i++) {
        $day = $i + 1;
        if ($day < 10) { // 当日期小于10用0补齐
            $day = '0' . $day;
        }
        $arrayDate = $month . $day;
        $res[$arrayDate] = 0;
        if ($cardInfo{$i} == 1) {
            $res[$arrayDate] = 1;
        }
    }
    return $this->success($res);
}

返回结果

{
    "code": 0,
    "msg": "ok",
    "data": {
        "2021-03-01": 0,
        "2021-03-02": 0,
        "2021-03-03": 0,
        "2021-03-04": 0,
        "2021-03-05": 0,
        "2021-03-06": 0,
        "2021-03-07": 0,
        "2021-03-08": 0,
        "2021-03-09": 1, // 已签到
        "2021-03-10": 0,
        "2021-03-11": 0,
        "2021-03-12": 0,
        "2021-03-13": 0,
        "2021-03-14": 0,
        "2021-03-15": 0,
        "2021-03-16": 0,
        "2021-03-17": 0,
        "2021-03-18": 0,
        "2021-03-19": 0,
        "2021-03-20": 0,
        "2021-03-21": 0,
        "2021-03-22": 0,
        "2021-03-23": 0,
        "2021-03-24": 0,
        "2021-03-25": 0,
        "2021-03-26": 0,
        "2021-03-27": 0,
        "2021-03-28": 0,
        "2021-03-29": 0,
        "2021-03-30": 0,
        "2021-03-31": 0
    }
}
Last modification:March 9th, 2021 at 02:31 pm