服务器之家:专注于服务器技术及软件下载分享
分类导航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|数据库技术|

服务器之家 - 数据库 - Redis - 基于Redis位图实现系统用户登录统计

基于Redis位图实现系统用户登录统计

2020-12-30 17:21ノGHJ Redis

这篇文章主要介绍了基于Redis位图实现系统用户登录统计,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

项目需求,试着写了一个简单登录统计,基本功能都实现了,日志数据量小。具体性能没有进行测试~ 记录下开发过程与代码,留着以后改进!

1. 需求 

         实现记录用户哪天进行了登录,每天只记录是否登录过,重复登录状态算已登录。不需要记录用户的操作行为,不需要记录用户上次登录时间和IP地址(这部分以后需要可以单独拿出来存储) 区分用户类型 查询数据需要精确到天

2. 分析

  考虑到只是简单的记录用户是否登录,记录数据比较单一,查询需要精确到天。以百万用户量为前提,前期考虑了几个方案

2.1 使用文件

  使用单文件存储:文件占用空间增长速度快,海量数据检索不方便,Map/Reduce操作也麻烦

  使用多文件存储:按日期对文件进行分割。每天记录当天日志,文件量过大

2.2 使用数据库

不太认同直接使用数据库写入/读取

  • 频繁请求数据库做一些日志记录浪费服务器开销。 
  • 随着时间推移数据急剧增大 
  • 海量数据检索效率也不高,同时使用索引,易产生碎片,每次插入数据还要维护索引,影响性能

  所以只考虑使用数据库做数据备份。

2.3 使用Redis位图(BitMap)

  这也是在网上看到的方法,比较实用。也是我最终考虑使用的方法,

  首先优点:

  数据量小:一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身。我们知道8个bit可以组成一个Byte,所以bitmap本身会极大的节省储存空间。1亿人每天的登陆情况,用1亿bit,约1200WByte,约10M 的字符就能表示。

  计算方便:实用Redis bit 相关命令可以极大的简化一些统计操作。常用命令 SETBITGETBITBITCOUNTBITOP

  再说弊端:

  存储单一:这也算不上什么缺点,位图上存储只是0/1,所以需要存储其他信息就要别的地方单独记录,对于需要存储信息多的记录就需要使用别的方法了

3. 设计3.1 Redis BitMap

  Key结构:前缀_年Y-月m_用户类型_用户ID

标准Key: KEYS loginLog_2017-10_client_1001
检索全部: KEYS loginLog_*
检索某年某月全部: KEYS loginLog_2017-10_*
检索单个用户全部: KEYS loginLog_*_client_1001
检索单个类型全部: KEYS loginLog_*_office_*
...  

  每条BitMap记录单个用户一个月的登录情况,一个bit位表示一天登录情况。

设置用户1001,217-10-25登录: SETBIT loginLog_2017-10_client_1001 25 1
获取用户1001,217-10-25是否登录:GETBIT loginLog_2017-10_client_1001 25
获取用户1001,217-10月是否登录: GETCOUNT loginLog_2017-10_client_1001
获取用户1001,217-10/9/7月是否登录:BITOP OR stat loginLog_2017-10_client_1001 loginLog_2017-09_client_1001 loginLog_2017-07_client_1001
...

  关于获取登录信息,就得获取BitMap然后拆开,循环进行判断。特别涉及时间范围,需要注意时间边界的问题,不要查询出多余的数据

  获取数据Redis优先级高于数据库,Redis有的记录不要去数据库获取

  Redis数据过期:在数据同步中进行判断,过期时间自己定义(我定义的过期时间单位为“天”,必须大于31)。

  在不能保证同步与过期一致性的问题,不要给Key设置过期时间,会造成数据丢失。

上一次更新时间: 2107-10-02
下一次更新时间: 2017-10-09
Redis BitMap 过期时间: 2017-10-05

这样会造成:2017-10-09同步的时候,3/4/5/6/7/8/9 数据丢失 

 所以我把Redis过期数据放到同步时进行判断  

  我自己想的同步策略(定时每周一凌晨同步):

一、验证是否需要进行同步:

1. 当前日期 >= 8号,对本月所有记录进行同步,不对本月之前的记录进行同步

2. 当前日期 < 8号,对本月所有记录进行同步,对本月前一个月的记录进行同步,对本月前一个月之前的所有记录不进行同步

二、验证过期,如果过期,记录日志后删除[/code]3.2 数据库,表结构

  每周同步一次数据到数据库,表中一条数据对应一个BitMap,记录一个月数据。每次更新已存在的、插入没有的

基于Redis位图实现系统用户登录统计

基于Redis位图实现系统用户登录统计

3.3 暂定接口 

  •  设置用户登录
  •  查询单个用户某天是否登录过
  • 查询单个用户某月是否登录过
  •  查询单个用户某个时间段是否登录过
  •  查询单个用户某个时间段登录信息
  •  指定用户类型:获取某个时间段内有效登录的用户
  •  全部用户:获取某个时间段内有效登录的用户

4. Code

  TP3中实现的代码,在接口服务器内部库中,Application\Lib\

  ├─LoginLog

  │├─Logs 日志目录,Redis中过期的记录删除写入日志进行备份

  │├─LoginLog.class.php 对外接口

  │├─LoginLogCommon.class.php 公共工具类

  │├─LoginLogDBHandle.class.php 数据库操作类

  │├─LoginLogRedisHandle.class.php Redis操作类

4.1 LoginLog.class.php

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
<?php
 
namespace Lib\LoginLog;
use Lib\CLogFileHandler;
use Lib\HObject;
use Lib\Log;
use Lib\Tools;
 
/**
* 登录日志操作类
* User: dbn
* Date: 2017/10/11
* Time: 12:01
* ------------------------
* 日志最小粒度为:天
*/
 
class LoginLog extends HObject
{
private $_redisHandle; // Redis登录日志处理
private $_dbHandle// 数据库登录日志处理
 
public function __construct()
{
$this->_redisHandle = new LoginLogRedisHandle($this);
$this->_dbHandle  = new LoginLogDBHandle($this);
 
// 初始化日志
$logHandler = new CLogFileHandler(__DIR__ . '/Logs/del.log');
Log::Init($logHandler, 15);
}
 
/**
* 记录登录:每天只记录一次登录,只允许设置当月内登录记录
* @param string $type 用户类型
* @param int  $uid 唯一标识(用户ID)
* @param int  $time 时间戳
* @return boolean
*/
public function setLogging($type, $uid, $time)
{
$key = $this->_redisHandle->getLoginLogKey($type, $uid, $time);
if ($this->_redisHandle->checkLoginLogKey($key)) {
return $this->_redisHandle->setLogging($key, $time);
}
return false;
}
 
/**
* 查询用户某一天是否登录过
* @param string $type 用户类型
* @param int  $uid 唯一标识(用户ID)
* @param int  $time 时间戳
* @return boolean 参数错误或未登录过返回false,登录过返回true
*/
public function getDateWhetherLogin($type, $uid, $time)
{
$key = $this->_redisHandle->getLoginLogKey($type, $uid, $time);
if ($this->_redisHandle->checkLoginLogKey($key)) {
 
// 判断Redis中是否存在记录
$isRedisExists = $this->_redisHandle->checkRedisLogExists($key);
if ($isRedisExists) {
 
// 从Redis中进行判断
return $this->_redisHandle->dateWhetherLogin($key, $time);
} else {
 
// 从数据库中进行判断
return $this->_dbHandle->dateWhetherLogin($type, $uid, $time);
}
}
return false;
}
 
/**
* 查询用户某月是否登录过
* @param string $type 用户类型
* @param int  $uid 唯一标识(用户ID)
* @param int  $time 时间戳
* @return boolean 参数错误或未登录过返回false,登录过返回true
*/
public function getDateMonthWhetherLogin($type, $uid, $time)
{
$key = $this->_redisHandle->getLoginLogKey($type, $uid, $time);
if ($this->_redisHandle->checkLoginLogKey($key)) {
 
// 判断Redis中是否存在记录
$isRedisExists = $this->_redisHandle->checkRedisLogExists($key);
if ($isRedisExists) {
 
// 从Redis中进行判断
return $this->_redisHandle->dateMonthWhetherLogin($key);
} else {
 
// 从数据库中进行判断
return $this->_dbHandle->dateMonthWhetherLogin($type, $uid, $time);
}
}
return false;
}
 
/**
* 查询用户在某个时间段是否登录过
* @param string $type 用户类型
* @param int  $uid 唯一标识(用户ID)
* @param int  $startTime 开始时间戳
* @param int  $endTime  结束时间戳
* @return boolean 参数错误或未登录过返回false,登录过返回true
*/
public function getTimeRangeWhetherLogin($type, $uid, $startTime, $endTime){
$result = $this->getUserTimeRangeLogin($type, $uid, $startTime, $endTime);
if ($result['hasLog']['count'] > 0) {
return true;
}
return false;
}
 
/**
* 获取用户某时间段内登录信息
* @param string $type   用户类型
* @param int  $uid    唯一标识(用户ID)
* @param int  $startTime 开始时间戳
* @param int  $endTime  结束时间戳
* @return array 参数错误或未查询到返回array()
* -------------------------------------------------
* 查询到结果:
* array(
*   'hasLog' => array(
*     'count' => n,                 // 有效登录次数,每天重复登录算一次
*     'list' => array('2017-10-1', '2017-10-15' ...) // 有效登录日期
*   ),
*   'notLog' => array(
*     'count' => n,                 // 未登录次数
*     'list' => array('2017-10-1', '2017-10-15' ...) // 未登录日期
*   )
* )
*/
public function getUserTimeRangeLogin($type, $uid, $startTime, $endTime)
{
$hasCount  = 0;    // 有效登录次数
$notCount  = 0;    // 未登录次数
$hasList  = array(); // 有效登录日期
$notList  = array(); // 未登录日期
$successFlg = false;  // 查询到数据标识
 
if ($this->checkTimeRange($startTime, $endTime)) {
 
// 获取需要查询的Key
$keyList = $this->_redisHandle->getTimeRangeRedisKey($type, $uid, $startTime, $endTime);
 
if (!empty($keyList)) {
foreach ($keyList as $key => $val) {
 
// 判断Redis中是否存在记录
$isRedisExists = $this->_redisHandle->checkRedisLogExists($val['key']);
if ($isRedisExists) {
 
// 存在,直接从Redis中获取
$logInfo = $this->_redisHandle->getUserTimeRangeLogin($val['key'], $startTime, $endTime);
} else {
 
// 不存在,尝试从数据库中读取
$logInfo = $this->_dbHandle->getUserTimeRangeLogin($type, $uid, $val['time'], $startTime, $endTime);
}
 
if (is_array($logInfo)) {
$hasCount += $logInfo['hasLog']['count'];
$hasList = array_merge($hasList, $logInfo['hasLog']['list']);
$notCount += $logInfo['notLog']['count'];
$notList = array_merge($notList, $logInfo['notLog']['list']);
$successFlg = true;
}
}
}
}
 
if ($successFlg) {
return array(
'hasLog' => array(
'count' => $hasCount,
'list' => $hasList
),
'notLog' => array(
'count' => $notCount,
'list' => $notList
)
);
}
 
return array();
}
 
/**
* 获取某段时间内有效登录过的用户 统一接口
* @param int  $startTime 开始时间戳
* @param int  $endTime  结束时间戳
* @param array $typeArr  用户类型,为空时获取全部类型
* @return array 参数错误或未查询到返回array()
* -------------------------------------------------
* 查询到结果:指定用户类型
* array(
*   'type1' => array(
*     'count' => n,           // type1 有效登录总用户数
*     'list' => array('111', '222' ...) // type1 有效登录用户
*   ),
*   'type2' => array(
*     'count' => n,           // type2 有效登录总用户数
*     'list' => array('333', '444' ...) // type2 有效登录用户
*   )
* )
* -------------------------------------------------
* 查询到结果:未指定用户类型,全部用户,固定键 'all'
* array(
*   'all' => array(
*     'count' => n,           // 有效登录总用户数
*     'list' => array('111', '222' ...) // 有效登录用户
*   )
* )
*/
public function getOrientedTimeRangeLogin($startTime, $endTime, $typeArr = array())
{
if ($this->checkTimeRange($startTime, $endTime)) {
 
// 判断是否指定类型
if (is_array($typeArr) && !empty($typeArr)) {
 
// 指定类型,验证类型合法性
if ($this->checkTypeArr($typeArr)) {
 
// 依据类型获取
return $this->getSpecifyTypeTimeRangeLogin($startTime, $endTime, $typeArr);
}
} else {
 
// 未指定类型,统一获取
return $this->getSpecifyAllTimeRangeLogin($startTime, $endTime);
}
}
return array();
}
 
/**
* 指定类型:获取某段时间内登录过的用户
* @param int  $startTime 开始时间戳
* @param int  $endTime  结束时间戳
* @param array $typeArr  用户类型
* @return array
*/
private function getSpecifyTypeTimeRangeLogin($startTime, $endTime, $typeArr)
{
$data = array();
$successFlg = false; // 查询到数据标识
 
// 指定类型,根据类型单独获取,进行整合
foreach ($typeArr as $typeArrVal) {
 
// 获取需要查询的Key
$keyList = $this->_redisHandle->getSpecifyTypeTimeRangeRedisKey($typeArrVal, $startTime, $endTime);
if (!empty($keyList)) {
 
$data[$typeArrVal]['count'] = 0;    // 该类型下有效登录用户数
$data[$typeArrVal]['list'] = array(); // 该类型下有效登录用户
 
foreach ($keyList as $keyListVal) {
 
// 查询Kye,验证Redis中是否存在:此处为单个类型,所以直接看Redis中是否存在该类型Key即可判断是否存在
// 存在的数据不需要去数据库中去查看
$standardKeyList = $this->_redisHandle->getKeys($keyListVal['key']);
if (is_array($standardKeyList) && count($standardKeyList) > 0) {
 
// Redis存在
foreach ($standardKeyList as $standardKeyListVal) {
 
// 验证该用户在此时间段是否登录过
$redisCheckLogin = $this->_redisHandle->getUserTimeRangeLogin($standardKeyListVal, $startTime, $endTime);
if ($redisCheckLogin['hasLog']['count'] > 0) {
 
// 同一个用户只需记录一次
$uid = $this->_redisHandle->getLoginLogKeyInfo($standardKeyListVal, 'uid');
if (!in_array($uid, $data[$typeArrVal]['list'])) {
$data[$typeArrVal]['count']++;
$data[$typeArrVal]['list'][] = $uid;
}
$successFlg = true;
}
}
 
} else {
 
// 不存在,尝试从数据库中获取
$dbResult = $this->_dbHandle->getTimeRangeLoginSuccessUser($keyListVal['time'], $startTime, $endTime, $typeArrVal);
if (!empty($dbResult)) {
foreach ($dbResult as $dbResultVal) {
if (!in_array($dbResultVal, $data[$typeArrVal]['list'])) {
$data[$typeArrVal]['count']++;
$data[$typeArrVal]['list'][] = $dbResultVal;
}
}
$successFlg = true;
}
}
}
}
}
 
if ($successFlg) { return $data; }
return array();
}
 
/**
* 全部类型:获取某段时间内登录过的用户
* @param int  $startTime 开始时间戳
* @param int  $endTime  结束时间戳
* @return array
*/
private function getSpecifyAllTimeRangeLogin($startTime, $endTime)
{
$count   = 0;    // 有效登录用户数
$list    = array(); // 有效登录用户
$successFlg = false;  // 查询到数据标识
 
// 未指定类型,直接对所有数据进行检索
// 获取需要查询的Key
$keyList = $this->_redisHandle->getSpecifyAllTimeRangeRedisKey($startTime, $endTime);
 
if (!empty($keyList)) {
foreach ($keyList as $keyListVal) {
 
// 查询Kye
$standardKeyList = $this->_redisHandle->getKeys($keyListVal['key']);
 
if (is_array($standardKeyList) && count($standardKeyList) > 0) {
 
// 查询到Key,直接读取数据,记录类型
foreach ($standardKeyList as $standardKeyListVal) {
 
// 验证该用户在此时间段是否登录过
$redisCheckLogin = $this->_redisHandle->getUserTimeRangeLogin($standardKeyListVal, $startTime, $endTime);
if ($redisCheckLogin['hasLog']['count'] > 0) {
 
// 同一个用户只需记录一次
$uid = $this->_redisHandle->getLoginLogKeyInfo($standardKeyListVal, 'uid');
if (!in_array($uid, $list)) {
$count++;
$list[] = $uid;
}
$successFlg = true;
}
}
}
 
// 无论Redis中存在不存在都要尝试从数据库中获取一遍数据,来补充Redis获取的数据,保证检索数据完整(Redis类型缺失可能导致)
$dbResult = $this->_dbHandle->getTimeRangeLoginSuccessUser($keyListVal['time'], $startTime, $endTime);
if (!empty($dbResult)) {
foreach ($dbResult as $dbResultVal) {
if (!in_array($dbResultVal, $list)) {
$count++;
$list[] = $dbResultVal;
}
}
$successFlg = true;
}
}
}
 
if ($successFlg) {
return array(
'all' => array(
'count' => $count,
'list' => $list
)
);
}
return array();
}
 
/**
* 验证开始结束时间
* @param string $startTime 开始时间
* @param string $endTime  结束时间
* @return boolean
*/
private function checkTimeRange($startTime, $endTime)
{
return $this->_redisHandle->checkTimeRange($startTime, $endTime);
}
 
/**
* 批量验证用户类型
* @param array $typeArr 用户类型数组
* @return boolean
*/
private function checkTypeArr($typeArr)
{
$flg = false;
if (is_array($typeArr) && !empty($typeArr)) {
foreach ($typeArr as $val) {
if ($this->_redisHandle->checkType($val)) {
$flg = true;
} else {
$flg = false; break;
}
}
}
return $flg;
}
 
/**
* 定时任务每周调用一次:从Redis同步登录日志到数据库
* @param int  $existsDay 一条记录在Redis中过期时间,单位:天,必须大于31
* @return string
* 'null':  Redis中无数据
* 'fail':  同步失败
* 'success':同步成功
*/
public function cronWeeklySync($existsDay)
{
 
// 验证生存时间
if ($this->_redisHandle->checkExistsDay($existsDay)) {
$likeKey = 'loginLog_*';
$keyList = $this->_redisHandle->getKeys($likeKey);
 
if (!empty($keyList)) {
foreach ($keyList as $keyVal) {
 
if ($this->_redisHandle->checkLoginLogKey($keyVal)) {
$keyTime     = $this->_redisHandle->getLoginLogKeyInfo($keyVal, 'time');
$thisMonth    = date('Y-m');
$beforeMonth   = date('Y-m', strtotime('-1 month'));
 
// 验证是否需要进行同步:
// 1. 当前日期 >= 8号,对本月所有记录进行同步,不对本月之前的记录进行同步
// 2. 当前日期 < 8号,对本月所有记录进行同步,对本月前一个月的记录进行同步,对本月前一个月之前的所有记录不进行同步
if (date('j') >= 8) {
 
// 只同步本月数据
if ($thisMonth == $keyTime) {
$this->redis2db($keyVal);
}
} else {
 
// 同步本月或本月前一个月数据
if ($thisMonth == $keyTime || $beforeMonth == $keyTime) {
$this->redis2db($keyVal);
}
}
 
// 验证是否过期
$existsSecond = $existsDay * 24 * 60 * 60;
if (strtotime($keyTime) + $existsSecond < time()) {
 
// 过期删除
$bitMap = $this->_redisHandle->getLoginLogBitMap($keyVal);
Log::INFO('删除过期数据[' . $keyVal . ']:' . $bitMap);
$this->_redisHandle->delLoginLog($keyVal);
}
}
}
return 'success';
}
return 'null';
}
return 'fail';
}
 
/**
* 将记录同步到数据库
* @param string $key 记录Key
* @return boolean
*/
private function redis2db($key)
{
if ($this->_redisHandle->checkLoginLogKey($key) && $this->_redisHandle->checkRedisLogExists($key)) {
$time = $this->_redisHandle->getLoginLogKeyInfo($key, 'time');
$data['id']   = Tools::generateId();
$data['user_id'] = $this->_redisHandle->getLoginLogKeyInfo($key, 'uid');
$data['type']  = $this->_redisHandle->getLoginLogKeyInfo($key, 'type');
$data['year']  = date('Y', strtotime($time));
$data['month']  = date('n', strtotime($time));
$data['bit_log'] = $this->_redisHandle->getLoginLogBitMap($key);
return $this->_dbHandle->redis2db($data);
}
return false;
}
}

4.2 LoginLogCommon.class.php

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
<?php
 
namespace Lib\LoginLog;
 
use Lib\RedisData;
use Lib\Status;
 
/**
* 公共方法
* User: dbn
* Date: 2017/10/11
* Time: 13:11
*/
class LoginLogCommon
{
protected $_loginLog;
protected $_redis;
 
public function __construct(LoginLog $loginLog)
{
$this->_loginLog = $loginLog;
$this->_redis  = RedisData::getRedis();
}
 
/**
* 验证用户类型
* @param string $type 用户类型
* @return boolean
*/
protected function checkType($type)
{
if (in_array($type, array(
Status::LOGIN_LOG_TYPE_ADMIN,
Status::LOGIN_LOG_TYPE_CARRIER,
Status::LOGIN_LOG_TYPE_DRIVER,
Status::LOGIN_LOG_TYPE_OFFICE,
Status::LOGIN_LOG_TYPE_CLIENT,
))) {
return true;
}
$this->_loginLog->setError('未定义的日志类型:' . $type);
return false;
}
 
/**
* 验证唯一标识
* @param string $uid
* @return boolean
*/
protected function checkUid($uid)
{
if (is_numeric($uid) && $uid > 0) {
return true;
}
$this->_loginLog->setError('唯一标识非法:' . $uid);
return false;
}
 
/**
* 验证时间戳
* @param string $time
* @return boolean
*/
protected function checkTime($time)
{
if (is_numeric($time) && $time > 0) {
return true;
}
$this->_loginLog->setError('时间戳非法:' . $time);
return false;
}
 
/**
* 验证时间是否在当月中
* @param string $time
* @return boolean
*/
protected function checkTimeWhetherThisMonth($time)
{
if ($this->checkTime($time) && $time > strtotime(date('Y-m')) && $time < strtotime(date('Y-m') . '-' . date('t'))) {
return true;
}
$this->_loginLog->setError('时间未在当前月份中:' . $time);
return false;
}
 
/**
* 验证时间是否超过当前时间
* @param string $time
* @return boolean
*/
protected function checkTimeWhetherFutureTime($time)
{
if ($this->checkTime($time) && $time <= time()) {
return true;
}
return false;
}
 
/**
* 验证开始/结束时间
* @param string $startTime 开始时间
* @param string $endTime  结束时间
* @return boolean
*/
protected function checkTimeRange($startTime, $endTime)
{
if ($this->checkTime($startTime) &&
$this->checkTime($endTime) &&
$startTime < $endTime &&
$startTime < time()
) {
return true;
}
$this->_loginLog->setError('时间范围非法:' . $startTime . '-' . $endTime);
return false;
}
 
/**
* 验证时间是否在指定范围内
* @param string $time   需要检查的时间
* @param string $startTime 开始时间
* @param string $endTime  结束时间
* @return boolean
*/
protected function checkTimeWithinTimeRange($time, $startTime, $endTime)
{
if ($this->checkTime($time) &&
$this->checkTimeRange($startTime, $endTime) &&
$startTime <= $time &&
$time <= $endTime
) {
return true;
}
$this->_loginLog->setError('请求时间未在时间范围内:' . $time . '-' . $startTime . '-' . $endTime);
return false;
}
 
/**
* 验证Redis日志记录标准Key
* @param string $key
* @return boolean
*/
protected function checkLoginLogKey($key)
{
$pattern = '/^loginLog_\d{4}-\d{1,2}_\S+_\d+$/';
$result = preg_match($pattern, $key, $match);
if ($result > 0) {
return true;
}
$this->_loginLog->setError('RedisKey非法:' . $key);
return false;
}
 
/**
* 获取月份中有多少天
* @param int $time 时间戳
* @return int
*/
protected function getDaysInMonth($time)
{
return date('t', $time);
}
 
/**
* 对没有前导零的月份或日设置前导零
* @param int $num 月份或日
* @return string
*/
protected function setDateLeadingZero($num)
{
if (is_numeric($num) && strlen($num) <= 2) {
$num = (strlen($num) > 1 ? $num : '0' . $num);
}
return $num;
}
 
/**
* 验证过期时间
* @param int   $existsDay 一条记录在Redis中过期时间,单位:天,必须大于31
* @return boolean
*/
protected function checkExistsDay($existsDay)
{
if (is_numeric($existsDay) && ctype_digit(strval($existsDay)) && $existsDay > 31) {
return true;
}
$this->_loginLog->setError('过期时间非法:' . $existsDay);
return false;
}
 
/**
* 获取开始日期边界
* @param int $time   需要判断的时间戳
* @param int $startTime 起始时间
* @return int
*/
protected function getStartTimeBorder($time, $startTime)
{
$initDay = 1;
if ($this->checkTime($time) && $this->checkTime($startTime) &&
date('Y-m', $time) === date('Y-m', $startTime) && false !== date('Y-m', $time)) {
$initDay = date('j', $startTime);
}
return $initDay;
}
 
/**
* 获取结束日期边界
* @param int $time   需要判断的时间戳
* @param int $endTime  结束时间
* @return int
*/
protected function getEndTimeBorder($time, $endTime)
{
$border = $this->getDaysInMonth($time);
if ($this->checkTime($time) && $this->checkTime($endTime) &&
date('Y-m', $time) === date('Y-m', $endTime) && false !== date('Y-m', $time)) {
$border = date('j', $endTime);
}
return $border;
}
}

4.3 LoginLogDBHandle.class.php

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
<?php
 
namespace Lib\LoginLog;
use Think\Model;
 
/**
* 数据库登录日志处理类
* User: dbn
* Date: 2017/10/11
* Time: 13:12
*/
class LoginLogDBHandle extends LoginLogCommon
{
 
/**
* 从数据库中获取用户某月记录在指定时间范围内的用户信息
* @param string $type   用户类型
* @param int   $uid    唯一标识(用户ID)
* @param int   $time   需要查询月份时间戳
* @param int   $startTime 开始时间戳
* @param int   $endTime  结束时间戳
* @return array
* array(
*   'hasLog' => array(
*     'count' => n,                 // 有效登录次数,每天重复登录算一次
*     'list' => array('2017-10-1', '2017-10-15' ...) // 有效登录日期
*   ),
*   'notLog' => array(
*     'count' => n,                 // 未登录次数
*     'list' => array('2017-10-1', '2017-10-15' ...) // 未登录日期
*   )
* )
*/
public function getUserTimeRangeLogin($type, $uid, $time, $startTime, $endTime)
{
$hasCount = 0;    // 有效登录次数
$notCount = 0;    // 未登录次数
$hasList = array(); // 有效登录日期
$notList = array(); // 未登录日期
 
if ($this->checkType($type) && $this->checkUid($uid) && $this->checkTimeWithinTimeRange($time, $startTime, $endTime)) {
 
$timeYM = date('Y-m', $time);
 
// 设置开始时间
$initDay = $this->getStartTimeBorder($time, $startTime);
 
// 设置结束时间
$border = $this->getEndTimeBorder($time, $endTime);
 
$bitMap = $this->getBitMapFind($type, $uid, date('Y', $time), date('n', $time));
for ($i = $initDay; $i <= $border; $i++) {
 
if (!empty($bitMap)) {
if ($bitMap[$i-1] == '1') {
$hasCount++;
$hasList[] = $timeYM . '-' . $this->setDateLeadingZero($i);
} else {
$notCount++;
$notList[] = $timeYM . '-' . $this->setDateLeadingZero($i);
}
} else {
$notCount++;
$notList[] = $timeYM . '-' . $this->setDateLeadingZero($i);
}
}
}
 
return array(
'hasLog' => array(
'count' => $hasCount,
'list' => $hasList
),
'notLog' => array(
'count' => $notCount,
'list' => $notList
)
);
}
 
/**
* 从数据库获取用户某月日志位图
* @param string $type 用户类型
* @param int   $uid  唯一标识(用户ID)
* @param int   $year 年Y
* @param int   $month 月n
* @return string
*/
private function getBitMapFind($type, $uid, $year, $month)
{
$model = D('Home/StatLoginLog');
$map['type']  = array('EQ', $type);
$map['user_id'] = array('EQ', $uid);
$map['year']  = array('EQ', $year);
$map['month']  = array('EQ', $month);
 
$result = $model->field('bit_log')->where($map)->find();
if (false !== $result && isset($result['bit_log']) && !empty($result['bit_log'])) {
return $result['bit_log'];
}
return '';
}
 
/**
* 从数据库中判断用户在某一天是否登录过
* @param string $type 用户类型
* @param int   $uid  唯一标识(用户ID)
* @param int   $time 时间戳
* @return boolean 参数错误或未登录过返回false,登录过返回true
*/
public function dateWhetherLogin($type, $uid, $time)
{
if ($this->checkType($type) && $this->checkUid($uid) && $this->checkTime($time)) {
 
$timeInfo = getdate($time);
$bitMap = $this->getBitMapFind($type, $uid, $timeInfo['year'], $timeInfo['mon']);
if (!empty($bitMap)) {
if ($bitMap[$timeInfo['mday']-1] == '1') {
return true;
}
}
}
return false;
}
 
/**
* 从数据库中判断用户在某月是否登录过
* @param string $type 用户类型
* @param int   $uid  唯一标识(用户ID)
* @param int   $time 时间戳
* @return boolean 参数错误或未登录过返回false,登录过返回true
*/
public function dateMonthWhetherLogin($type, $uid, $time)
{
if ($this->checkType($type) && $this->checkUid($uid) && $this->checkTime($time)) {
 
$timeInfo = getdate($time);
$userArr = $this->getMonthLoginSuccessUser($timeInfo['year'], $timeInfo['mon'], $type);
if (!empty($userArr)) {
if (in_array($uid, $userArr)) {
return true;
}
}
}
return false;
}
 
/**
* 获取某月所有有效登录过的用户ID
* @param int   $year 年Y
* @param int   $month 月n
* @param string $type 用户类型,为空时获取全部类型
* @return array
*/
public function getMonthLoginSuccessUser($year, $month, $type = '')
{
$data = array();
if (is_numeric($year) && is_numeric($month)) {
$model = D('Home/StatLoginLog');
$map['year']  = array('EQ', $year);
$map['month']  = array('EQ', $month);
$map['bit_log'] = array('LIKE', '%1%');
if ($type != '' && $this->checkType($type)) {
$map['type']  = array('EQ', $type);
}
$result = $model->field('user_id')->where($map)->select();
if (false !== $result && count($result) > 0) {
foreach ($result as $val) {
if (isset($val['user_id'])) {
$data[] = $val['user_id'];
}
}
}
}
return $data;
}
 
/**
* 从数据库中获取某月所有记录在指定时间范围内的用户ID
* @param int   $time   查询的时间戳
* @param int   $startTime 开始时间戳
* @param int   $endTime  结束时间戳
* @param string $type 用户类型,为空时获取全部类型
* @return array
*/
public function getTimeRangeLoginSuccessUser($time, $startTime, $endTime, $type = '')
{
$data = array();
if ($this->checkTimeWithinTimeRange($time, $startTime, $endTime)) {
 
$timeInfo = getdate($time);
 
// 获取满足时间条件的记录
$model = D('Home/StatLoginLog');
$map['year']  = array('EQ', $timeInfo['year']);
$map['month']  = array('EQ', $timeInfo['mon']);
if ($type != '' && $this->checkType($type)) {
$map['type']  = array('EQ', $type);
}
 
$result = $model->where($map)->select();
if (false !== $result && count($result) > 0) {
 
// 设置开始时间
$initDay = $this->getStartTimeBorder($time, $startTime);
 
// 设置结束时间
$border = $this->getEndTimeBorder($time, $endTime);
 
foreach ($result as $val) {
 
$bitMap = $val['bit_log'];
for ($i = $initDay; $i <= $border; $i++) {
 
if ($bitMap[$i-1] == '1' && !in_array($val['user_id'], $data)) {
$data[] = $val['user_id'];
}
}
}
}
}
return $data;
}
 
/**
* 将数据更新到数据库
* @param array $data 单条记录的数据
* @return boolean
*/
public function redis2db($data)
{
$model = D('Home/StatLoginLog');
 
// 验证记录是否存在
$map['user_id'] = array('EQ', $data['user_id']);
$map['type']  = array('EQ', $data['type']);
$map['year']  = array('EQ', $data['year']);
$map['month']  = array('EQ', $data['month']);
 
$count = $model->where($map)->count();
if (false !== $count && $count > 0) {
 
// 存在记录进行更新
$saveData['bit_log'] = $data['bit_log'];
 
if (!$model->create($saveData, Model::MODEL_UPDATE)) {
 
$this->_loginLog->setError('同步登录日志-更新记录,创建数据对象失败:' . $model->getError());
logger()->error('同步登录日志-更新记录,创建数据对象失败:' . $model->getError());
return false;
} else {
 
$result = $model->where($map)->save();
 
if (false !== $result) {
return true;
} else {
$this->_loginLog->setError('同步登录日志-更新记录,更新数据失败:' . json_encode($data));
logger()->error('同步登录日志-更新记录,更新数据失败:' . json_encode($data));
return false;
}
}
} else {
 
// 不存在记录插入一条新的记录
if (!$model->create($data, Model::MODEL_INSERT)) {
 
$this->_loginLog->setError('同步登录日志-插入记录,创建数据对象失败:' . $model->getError());
logger()->error('同步登录日志-插入记录,创建数据对象失败:' . $model->getError());
return false;
} else {
 
$result = $model->add();
 
if (false !== $result) {
return true;
} else {
$this->_loginLog->setError('同步登录日志-插入记录,插入数据失败:' . json_encode($data));
logger()->error('同步登录日志-插入记录,插入数据失败:' . json_encode($data));
return false;
}
}
}
}
}

4.4 LoginLogRedisHandle.class.php

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
<?php
 
namespace Lib\LoginLog;
 
/**
* Redis登录日志处理类
* User: dbn
* Date: 2017/10/11
* Time: 15:53
*/
class LoginLogRedisHandle extends LoginLogCommon
{
/**
* 记录登录:每天只记录一次登录,只允许设置当月内登录记录
* @param string $key 日志记录Key
* @param int  $time 时间戳
* @return boolean
*/
public function setLogging($key, $time)
{
if ($this->checkLoginLogKey($key) && $this->checkTimeWhetherThisMonth($time)) {
 
// 判断用户当天是否已经登录过
$whetherLoginResult = $this->dateWhetherLogin($key, $time);
if (!$whetherLoginResult) {
 
// 当天未登录,记录登录
$this->_redis->setBit($key, date('d', $time), 1);
}
return true;
}
return false;
}
 
/**
* 从Redis中判断用户在某一天是否登录过
* @param string $key 日志记录Key
* @param int  $time 时间戳
* @return boolean 参数错误或未登录过返回false,登录过返回true
*/
public function dateWhetherLogin($key, $time)
{
if ($this->checkLoginLogKey($key) && $this->checkTime($time)) {
$result = $this->_redis->getBit($key, date('d', $time));
if ($result === 1) {
return true;
}
}
return false;
}
 
/**
* 从Redis中判断用户在某月是否登录过
* @param string $key 日志记录Key
* @return boolean 参数错误或未登录过返回false,登录过返回true
*/
public function dateMonthWhetherLogin($key)
{
if ($this->checkLoginLogKey($key)) {
$result = $this->_redis->bitCount($key);
if ($result > 0) {
return true;
}
}
return false;
}
 
/**
* 判断某月登录记录在Redis中是否存在
* @param string $key 日志记录Key
* @return boolean
*/
public function checkRedisLogExists($key)
{
if ($this->checkLoginLogKey($key)) {
if ($this->_redis->exists($key)) {
return true;
}
}
return false;
}
 
/**
* 从Redis中获取用户某月记录在指定时间范围内的用户信息
* @param string $key    日志记录Key
* @param int   $startTime 开始时间戳
* @param int   $endTime  结束时间戳
* @return array
* array(
*   'hasLog' => array(
*     'count' => n,                 // 有效登录次数,每天重复登录算一次
*     'list' => array('2017-10-1', '2017-10-15' ...) // 有效登录日期
*   ),
*   'notLog' => array(
*     'count' => n,                 // 未登录次数
*     'list' => array('2017-10-1', '2017-10-15' ...) // 未登录日期
*   )
* )
*/
public function getUserTimeRangeLogin($key, $startTime, $endTime)
{
$hasCount = 0;    // 有效登录次数
$notCount = 0;    // 未登录次数
$hasList = array(); // 有效登录日期
$notList = array(); // 未登录日期
 
if ($this->checkLoginLogKey($key) && $this->checkTimeRange($startTime, $endTime) && $this->checkRedisLogExists($key)) {
 
$keyTime = $this->getLoginLogKeyInfo($key, 'time');
$keyTime = strtotime($keyTime);
$timeYM = date('Y-m', $keyTime);
 
// 设置开始时间
$initDay = $this->getStartTimeBorder($keyTime, $startTime);
 
// 设置结束时间
$border = $this->getEndTimeBorder($keyTime, $endTime);
 
for ($i = $initDay; $i <= $border; $i++) {
$result = $this->_redis->getBit($key, $i);
if ($result === 1) {
$hasCount++;
$hasList[] = $timeYM . '-' . $this->setDateLeadingZero($i);
} else {
$notCount++;
$notList[] = $timeYM . '-' . $this->setDateLeadingZero($i);
}
}
}
 
return array(
'hasLog' => array(
'count' => $hasCount,
'list' => $hasList
),
'notLog' => array(
'count' => $notCount,
'list' => $notList
)
);
}
 
/**
* 面向用户:获取时间范围内可能需要的Key
* @param string $type   用户类型
* @param int  $uid    唯一标识(用户ID)
* @param string $startTime 开始时间
* @param string $endTime  结束时间
* @return array
*/
public function getTimeRangeRedisKey($type, $uid, $startTime, $endTime)
{
$list = array();
 
if ($this->checkType($type) && $this->checkUid($uid) && $this->checkTimeRange($startTime, $endTime)) {
 
$data = $this->getSpecifyUserKeyHandle($type, $uid, $startTime);
if (!empty($data)) { $list[] = $data; }
 
$temYM = strtotime('+1 month', strtotime(date('Y-m', $startTime)));
 
while ($temYM <= $endTime) {
$data = $this->getSpecifyUserKeyHandle($type, $uid, $temYM);
if (!empty($data)) { $list[] = $data; }
 
$temYM = strtotime('+1 month', $temYM);
}
}
return $list;
}
private function getSpecifyUserKeyHandle($type, $uid, $time)
{
$data = array();
$key = $this->getLoginLogKey($type, $uid, $time);
if ($this->checkLoginLogKey($key)) {
$data = array(
'key' => $key,
'time' => $time
);
}
return $data;
}
 
/**
* 面向类型:获取时间范围内可能需要的Key
* @param string $type   用户类型
* @param string $startTime 开始时间
* @param string $endTime  结束时间
* @return array
*/
public function getSpecifyTypeTimeRangeRedisKey($type, $startTime, $endTime)
{
$list = array();
 
if ($this->checkType($type) && $this->checkTimeRange($startTime, $endTime)) {
 
$data = $this->getSpecifyTypeKeyHandle($type, $startTime);
if (!empty($data)) { $list[] = $data; }
 
$temYM = strtotime('+1 month', strtotime(date('Y-m', $startTime)));
 
while ($temYM <= $endTime) {
$data = $this->getSpecifyTypeKeyHandle($type, $temYM);
if (!empty($data)) { $list[] = $data; }
 
$temYM = strtotime('+1 month', $temYM);
}
}
return $list;
}
private function getSpecifyTypeKeyHandle($type, $time)
{
$data = array();
$temUid = '11111111';
 
$key = $this->getLoginLogKey($type, $temUid, $time);
if ($this->checkLoginLogKey($key)) {
$arr = explode('_', $key);
$arr[count($arr)-1] = '*';
$key = implode('_', $arr);
$data = array(
'key' => $key,
'time' => $time
);
}
return $data;
}
 
/**
* 面向全部:获取时间范围内可能需要的Key
* @param string $startTime 开始时间
* @param string $endTime  结束时间
* @return array
*/
public function getSpecifyAllTimeRangeRedisKey($startTime, $endTime)
{
$list = array();
 
if ($this->checkTimeRange($startTime, $endTime)) {
 
$data = $this->getSpecifyAllKeyHandle($startTime);
if (!empty($data)) { $list[] = $data; }
 
$temYM = strtotime('+1 month', strtotime(date('Y-m', $startTime)));
 
while ($temYM <= $endTime) {
$data = $this->getSpecifyAllKeyHandle($temYM);
if (!empty($data)) { $list[] = $data; }
 
$temYM = strtotime('+1 month', $temYM);
}
}
return $list;
}
private function getSpecifyAllKeyHandle($time)
{
$data = array();
$temUid = '11111111';
$temType = 'office';
 
$key = $this->getLoginLogKey($temType, $temUid, $time);
if ($this->checkLoginLogKey($key)) {
$arr = explode('_', $key);
array_pop($arr);
$arr[count($arr)-1] = '*';
$key = implode('_', $arr);
$data = array(
'key' => $key,
'time' => $time
);
}
return $data;
}
 
/**
* 从Redis中查询满足条件的Key
* @param string $key 查询的Key
* @return array
*/
public function getKeys($key)
{
return $this->_redis->keys($key);
}
 
/**
* 从Redis中删除记录
* @param string $key 记录的Key
* @return boolean
*/
public function delLoginLog($key)
{
return $this->_redis->del($key);
}
 
/**
* 获取日志标准Key:前缀_年-月_用户类型_唯一标识
* @param string $type 用户类型
* @param int  $uid 唯一标识(用户ID)
* @param int  $time 时间戳
* @return string
*/
public function getLoginLogKey($type, $uid, $time)
{
if ($this->checkType($type) && $this->checkUid($uid) && $this->checkTime($time)) {
return 'loginLog_' . date('Y-m', $time) . '_' . $type . '_' . $uid;
}
return '';
}
 
/**
* 获取日志标准Key上信息
* @param string $key  key
* @param string $field 需要的参数 time,type,uid
* @return mixed 返回对应的值,没有返回null
*/
public function getLoginLogKeyInfo($key, $field)
{
$param = array();
if ($this->checkLoginLogKey($key)) {
$arr = explode('_', $key);
$param['time'] = $arr[1];
$param['type'] = $arr[2];
$param['uid'] = $arr[3];
}
return $param[$field];
}
 
/**
* 获取Key记录的登录位图
* @param string $key key
* @return string
*/
public function getLoginLogBitMap($key)
{
$bitMap = '';
if ($this->checkLoginLogKey($key)) {
$time = $this->getLoginLogKeyInfo($key, 'time');
$maxDay = $this->getDaysInMonth(strtotime($time));
for ($i = 1; $i <= $maxDay; $i++) {
$bitMap .= $this->_redis->getBit($key, $i);
}
}
return $bitMap;
}
 
/**
* 验证日志标准Key
* @param string $key
* @return boolean
*/
public function checkLoginLogKey($key)
{
return parent::checkLoginLogKey($key);
}
 
/**
* 验证开始/结束时间
* @param string $startTime 开始时间
* @param string $endTime  结束时间
* @return boolean
*/
public function checkTimeRange($startTime, $endTime)
{
return parent::checkTimeRange($startTime, $endTime);
}
 
/**
* 验证用户类型
* @param string $type
* @return boolean
*/
public function checkType($type)
{
return parent::checkType($type);
}
 
/**
* 验证过期时间
* @param int $existsDay 一条记录在Redis中过期时间,单位:天,必须大于31
* @return boolean
*/
public function checkExistsDay($existsDay)
{
return parent::checkExistsDay($existsDay);
}
}

5. 参考资料

  https://segmentfault.com/a/1190000008188655

  http://blog.csdn.net/rdhj5566/article/details/54313840

  http://www.redis.net.cn/tutorial/3508.html

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://www.cnblogs.com/ghjbk/p/7681724.html

延伸 · 阅读

精彩推荐
  • RedisRedis存取序列化与反序列化性能问题详解

    Redis存取序列化与反序列化性能问题详解

    这篇文章主要给大家介绍了关于Redis存取序列化与反序列化性能问题的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参...

    这名字已经存在9742021-02-24
  • Redis就这?Redis持久化策略——AOF

    就这?Redis持久化策略——AOF

    今天为大家介绍Redis的另一种持久化策略——AOF。注意:AOF文件只会记录Redis的写操作命令,因为读命令对数据的恢复没有任何意义...

    头发茂密的刘叔4052021-12-14
  • RedisLinux Redis 的安装步骤详解

    Linux Redis 的安装步骤详解

    这篇文章主要介绍了 Linux Redis 的安装步骤详解的相关资料,希望大家通过本文能掌握如何安装Redis,需要的朋友可以参考下 ...

    carl-zhao3822019-11-08
  • RedisRedis数据结构之链表与字典的使用

    Redis数据结构之链表与字典的使用

    这篇文章主要介绍了Redis数据结构之链表与字典的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友...

    白泽来了4052021-08-03
  • Redis在ssm项目中使用redis缓存查询数据的方法

    在ssm项目中使用redis缓存查询数据的方法

    本文主要简单的使用Java代码进行redis缓存,即在查询的时候先在service层从redis缓存中获取数据。如果大家对在ssm项目中使用redis缓存查询数据的相关知识感...

    caychen8962019-11-12
  • Redis聊一聊Redis与MySQL双写一致性如何保证

    聊一聊Redis与MySQL双写一致性如何保证

    一致性就是数据保持一致,在分布式系统中,可以理解为多个节点中数据的值是一致的。本文给大家分享Redis与MySQL双写一致性该如何保证,感兴趣的朋友一...

    mind_programmonkey6432021-08-12
  • RedisRedis分布式锁升级版RedLock及SpringBoot实现方法

    Redis分布式锁升级版RedLock及SpringBoot实现方法

    这篇文章主要介绍了Redis分布式锁升级版RedLock及SpringBoot实现,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以...

    等不到的口琴7802021-07-25
  • Redisredis启动,停止,及端口占用处理方法

    redis启动,停止,及端口占用处理方法

    今天小编就为大家分享一篇redis启动,停止,及端口占用处理方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧 ...

    澄海单挑狂5152019-11-14