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