connectUserId != $user_id) { $database = get_db_connect($this->table, $user_id); $this->setTable($database['table']); $this->connect($database); $this->connectUserId = $user_id; } return $this; } public function getone($user_id, $book_id) { return $this->setConnect($user_id)->where(['user_id' => $user_id, 'book_id' => $book_id])->find(); } /** * @deprecated * @param $recentId * @param $book_id * @param null $user_id * @return bool */ public function removeBook($recentId,$book_id,$user_id = null) { if(!$user_id) { $user_id = UserService::instance()->getUserInfo()->id; } $db_res = $this->setConnect($user_id)->where(['id'=>$recentId])->update(['flag' => 0]); if ($db_res) { $redis = Redis::instance(); $key = $this->getURKey($user_id); $ubKey = $this->getUBKey($user_id, $book_id); $redis->zrem($key, $ubKey); $redis->del($ubKey); return true; } return false; } /** * 得到最近阅读记录 * @param int $updatetime * @param int $pageSize * @param null $userId * @param bool $getBookInfo 是否获取书籍对象信息 * @return array totalNum用户总阅读记录数量 data查到的阅读记录对象 * @throws \Exception */ public function getRecentlyRead($updatetime = 0, $pageSize = 10, $userId = null, $getBookInfo = false) { $data = []; if (!$userId) { $userId = UserService::instance()->getUserInfo()->id; } $redis = Redis::instance(); $urKey = $this->getURKey($userId); if ($updatetime) { $pageBegin = $updatetime - 1; $resUR = $redis->zrevrangebyscore($urKey, $pageBegin, '-inf', array('limit' => array(0, $pageSize))); } else { $pageBegin = 0; $resUR = $redis->zrevrange($urKey, 0, $pageSize - 1); } if (empty($resUR)) { $res = $this->getRecentlyByDb($userId, $pageBegin, $pageSize); $result = $res['data']; $count = $res['count']; } else { $count = $redis->zCard($urKey); $result = $this->getRecentlyByRedis($userId, $resUR); } #region 向最近阅读结果中填充书籍信息 if ($getBookInfo) { $bookIds = array_column($result, 'book_id'); $booksInfo = model('Book')->getBooksInfo($bookIds); foreach ($result as &$item) { $bookId = $item['book_id']; if (isset($booksInfo[$bookId])) { $book = $booksInfo[$bookId]; $item['image'] = $book['image']; $item['book_name'] = $book['name']; $item['last_chapter_name'] = $book['last_chapter_name']; $item['last_chapter_utime'] = $book['last_chapter_utime']; $item['state'] = $book['state']; $item['free_stime'] = $book['free_stime']; $item['free_etime'] = $book['free_etime']; $item['first_chapter_id'] = $book['first_chapter_id']; } } } #endregion $data['totalNum'] = $count; $data['data'] = $result; return $data; } /** * 从数据库中获取最近阅读记录 * @param $userId * @param $pageBegin * @param $pageSize * @return array */ private function getRecentlyByDb($userId, $pageBegin, $pageSize) { $redis = Redis::instance(); $page = 0; $pageSz = 100; $recentlyData = []; #region 循环从数据库中获取用户阅读记录信息 while (true) { $offset = $page * $pageSz; $recentlyPageData = $this->setConnect($userId)->where(['user_id' => $userId, 'flag' => 1]) ->limit($offset, $pageSz) ->order('updatetime', 'desc')->select(); if (count($recentlyPageData) == 0) { break; } elseif (count($recentlyPageData) == $pageSz) { $recentlyData = array_merge($recentlyData, $recentlyPageData); $page++; } elseif (count($recentlyPageData) < $pageSz) { $recentlyData = array_merge($recentlyData, $recentlyPageData); break; } } $count = count($recentlyData); #endregion #region 阅读记录转成数组 $aRecentlyData = []; array_walk($recentlyData, function ($recentlyDatum) use (&$aRecentlyData) { $aRecentlyData[] = is_array($recentlyDatum) ? $recentlyDatum : $recentlyDatum->toArray(); }); #endregion #region 阅读记录列表插入key为U-R的zset结构的redis缓存中 $redisKey = $this->getURKey($userId); $aUR_Value = [$redisKey];//构造U-R的值列表,U-R为zset类型,0=>reidsKey,1=>score1,2=>value1,3=>score2,4=>value2... $aUB_Key = [];//U-B的key的列表,U-B为hash类型。 array_walk($aRecentlyData, function ($item) use (&$aUR_Value, &$aUB_Key, $userId) { $bookId = $item['book_id']; $userReadBookKey = $this->getUBKey($userId, $bookId); $aUR_Value[] = $item['updatetime']; $aUR_Value[] = $userReadBookKey; $aUB_Key[] = $userReadBookKey; }); call_user_func_array(array($redis, 'zadd'), $aUR_Value); $redis->expire($redisKey, 43200); #endregion #region 阅读记录详细信息写入key为U-B的hash结构的redis缓存中 $getUBRedisIndex = Redis::splitKeysByRule($aUB_Key); array_walk($getUBRedisIndex, function ($v, $k) use ($aRecentlyData) { $redis = Redis::getRedisConnect($k); $pipe = $redis->multi(\Redis::PIPELINE); array_walk($v, function ($UB_Key) use ($aRecentlyData, &$pipe) { $aKeys = explode(':', $UB_Key); $bookId = $aKeys[2]; $recentItem = ArrayHelper::array_column_search($aRecentlyData, 'book_id', $bookId); $pipe->hmset($UB_Key, $recentItem); $pipe->expire($UB_Key, 4320); }); $pipe->exec(); }); #endregion #region 按updatetime输出前$pageSize条数据 $result = []; foreach ($aRecentlyData as $recentlyItem) { if (count($result) >= $pageSize) { break; } if ($pageBegin == 0) { $result[] = $recentlyItem; } elseif ($recentlyItem['updatetime'] < $pageBegin) { $result[] = $recentlyItem; } } #endregion $r = ['data' => $result, 'count' => $count]; return $r; } /** * 从redis分页读取最近阅读记录 * @param $userId * @param $resUR * @return array|null * @throws \Exception */ private function getRecentlyByRedis($userId, $resUR) { $redis = Redis::instance(); #region 获取U-R中获取最近阅读书籍列表 $returnArr = []; $bookIdsFromUR = []; array_walk($resUR, function ($UB_key) use (&$bookIdsFromUR) { $aKeys = explode(':', $UB_key); $bookIdsFromUR[] = $aKeys[2]; }); #endregion #region 从redis中获取U-B信息 $getUBKeyRedisIndex = Redis::splitKeysByRule($resUR); array_walk($getUBKeyRedisIndex, function ($v, $k) use (&$returnArr) { $redis = Redis::getRedisConnect($k); $pipe = $redis->multi(\Redis::PIPELINE); array_walk($v, function ($UB_Key) use (&$pipe) { $pipe->hgetall($UB_Key); }); $aRecently = $pipe->exec(); $aRecently = array_filter($aRecently); $returnArr = array_merge($returnArr, $aRecently); }); #endregion $bookIdsFromUB = array_column($returnArr, 'book_id'); $diffBookIds = array_diff($bookIdsFromUR, $bookIdsFromUB);#在U-R中存在,但是在U-B中不存在的书籍id if (!empty($diffBookIds)) { #region 从数据库中获取当前用户不在U-B中的书籍的阅读记录 $recentListFromDb = $this->setConnect($userId)->where(['user_id' => $userId, 'flag' => 1]) ->whereIn('book_id', $diffBookIds)->select(); $aRecentFromDb = []; $aUBKeyWrite = [];//从数据库中获得的阅读记录,需要写入redis,rediskey array_walk($recentListFromDb, function ($item) use (&$aRecentFromDb, &$aUBKeyWrite, $userId) { $recentItem=$item->toArray(); $aRecentFromDb[] = $recentItem; $aUBKeyWrite[] = $this->getUBKey($userId, $recentItem['book_id']); }); #endregion #region 分片写入redis $getUBKeyWriteRedisIndex = Redis::splitKeysByRule($aUBKeyWrite); array_walk($getUBKeyWriteRedisIndex, function ($v, $k) use ($aRecentFromDb) { $redis = Redis::getRedisConnect($k); $pipe = $redis->multi(\Redis::PIPELINE); array_walk($v, function ($UB_key) use (&$pipe, $aRecentFromDb) { $aKeys = explode(':', $UB_key); $bookId = $aKeys[2]; $recentItem = ArrayHelper::array_column_search($aRecentFromDb, 'book_id', $bookId); $pipe->hmset($UB_key, $recentItem); $pipe->expire($UB_key, 43200); }); $pipe->exec(); }); #endregion $returnArr = array_merge($returnArr, $aRecentFromDb); } #region U-R中存在的记录,但是在U-B中不存在,需要在U-R中删除 $UB_BookIds = array_column($returnArr, 'book_id'); $removeBookIds = array_diff($bookIdsFromUR, $UB_BookIds); if (!empty($removeBookIds)) { $removeBookKeys = []; foreach ($removeBookIds as $removeBookId) { $removeBookKeys[] = $this->getUBKey($userId, $removeBookId); } array_unshift($removeBookKeys, $this->getURKey($userId)); call_user_func_array([$redis, 'zrem'], $removeBookKeys); } #endregion //结果集使用updatetime逆序排序 $sortArr = array_column($returnArr, 'updatetime'); array_multisort($sortArr, SORT_DESC, $returnArr); return $returnArr; } public function getUBKey($userId, $bookId) { return "U-B:$userId:$bookId"; } public function getURKey($userId) { return "U-R:$userId"; } public function getUSKey($userId) { return "U-S:" . $userId; } public function getShelfRecentlyByDB($userId, $pageBegin, $pageSize) { $redis = Redis::instance(); $page = 0; $pageSz = 100; $recentlyData = []; #region 循环从数据库中获取用户阅读记录信息 while (true) { $offset = $page * $pageSz; $recentlyPageData = $this->setConnect($userId)->where([ 'user_id' => $userId, 'book_shelf_add' => 1, 'book_shelf_flag' => 1 ]) ->limit($offset, $pageSz) ->order('updatetime', 'desc')->select(); if (count($recentlyPageData) == 0) { break; } elseif (count($recentlyPageData) == $pageSz) { $recentlyData = array_merge($recentlyData, $recentlyPageData); $page++; } elseif (count($recentlyPageData) < $pageSz) { $recentlyData = array_merge($recentlyData, $recentlyPageData); break; } } #endregion #region 阅读记录转成数组 $aRecentlyData = []; array_walk($recentlyData, function ($recentlyDatum) use (&$aRecentlyData) { $aRecentlyData[] = is_array($recentlyDatum) ? $recentlyDatum : $recentlyDatum->toArray(); }); #endregion #region 阅读记录列表插入key为U-S的zset结构的redis缓存中 $redisKey = $this->getUSKey($userId); $aUS_Value = [$redisKey];//构造U-S的值列表,U-S为zset类型,0=>reidsKey,1=>score1,2=>value1,3=>score2,4=>value2... $aUB_Key = [];//U-B的key的列表,U-B为hash类型。 array_walk($aRecentlyData, function ($item) use (&$aUS_Value, &$aUB_Key, $userId) { $bookId = $item['book_id']; $userReadBookKey = $this->getUBKey($userId, $bookId); $aUS_Value[] = $item['updatetime']; $aUS_Value[] = $userReadBookKey; $aUB_Key[] = $userReadBookKey; }); call_user_func_array(array($redis, 'zadd'), $aUS_Value); $redis->expire($redisKey, 43200); #endregion #region 阅读记录详细信息写入key为U-B的hash结构的redis缓存中 $getUBRedisIndex = Redis::splitKeysByRule($aUB_Key); array_walk($getUBRedisIndex, function ($v, $k) use ($aRecentlyData) { $redis = Redis::getRedisConnect($k); $pipe = $redis->multi(\Redis::PIPELINE); array_walk($v, function ($UB_Key) use ($aRecentlyData, &$pipe) { $aKeys = explode(':', $UB_Key); $bookId = $aKeys[2]; $recentItem = ArrayHelper::array_column_search($aRecentlyData, 'book_id', $bookId); $pipe->hmset($UB_Key, $recentItem); $pipe->expire($UB_Key, 4320); }); $pipe->exec(); }); #endregion #region 按updatetime输出前$pageSize条数据 $result = []; foreach ($aRecentlyData as $recentlyItem) { if (count($result) >= $pageSize) { break; } if ($pageBegin == 0) { $result[] = $recentlyItem; } elseif ($recentlyItem['updatetime'] < $pageBegin) { $result[] = $recentlyItem; } } #endregion return $result; } public function getShelfRecentlyByRedis($userId, $resUS) { $redis = Redis::instance(); #region 获取U-S中获取最近阅读书籍列表 $returnArr = []; $bookIdsFromUS = []; array_walk($resUS, function ($UB_key) use (&$bookIdsFromUS) { $aKeys = explode(':', $UB_key); $bookIdsFromUS[] = $aKeys[2]; }); #endregion #region 从redis中获取U-B信息 $getUBKeyRedisIndex = Redis::splitKeysByRule($resUS); array_walk($getUBKeyRedisIndex, function ($v, $k) use (&$returnArr) { $redis = Redis::getRedisConnect($k); $pipe = $redis->multi(\Redis::PIPELINE); array_walk($v, function ($UB_Key) use (&$pipe) { $pipe->hgetall($UB_Key); }); $aRecently = $pipe->exec(); $aRecently = array_filter($aRecently); $returnArr = array_merge($returnArr, $aRecently); }); #endregion $bookIdsFromUB = array_column($returnArr, 'book_id'); $diffBookIds = array_diff($bookIdsFromUS, $bookIdsFromUB);#在U-S中存在,但是在U-B中不存在的书籍id if (!empty($diffBookIds)) { #region 从数据库中获取当前用户不在U-B中的书籍的阅读记录 $recentListFromDb = $this->setConnect($userId)->where([ 'user_id' => $userId, 'book_shelf_add' => 1, 'book_shelf_flag' => 1 ])->whereIn('book_id', $diffBookIds)->select(); $aRecentFromDb = []; $aUBKeyWrite = [];//从数据库中获得的阅读记录,需要写入redis,rediskey array_walk($recentListFromDb, function ($item) use (&$aRecentFromDb, &$aUBKeyWrite, $userId) { $recentItem = $item->toArray(); $aRecentFromDb[] = $recentItem; $aUBKeyWrite[] = $this->getUBKey($userId, $recentItem['book_id']); }); #endregion #region 分片写入redis $getUBKeyWriteRedisIndex = Redis::splitKeysByRule($aUBKeyWrite); array_walk($getUBKeyWriteRedisIndex, function ($v, $k) use ($aRecentFromDb) { $redis = Redis::getRedisConnect($k); $pipe = $redis->multi(\Redis::PIPELINE); array_walk($v, function ($UB_key) use (&$pipe, $aRecentFromDb) { $aKeys = explode(':', $UB_key); $bookId = $aKeys[2]; $recentItem = ArrayHelper::array_column_search($aRecentFromDb, 'book_id', $bookId); $pipe->hmset($UB_key, $recentItem); $pipe->expire($UB_key, 43200); }); $pipe->exec(); }); #endregion $returnArr = array_merge($returnArr, $aRecentFromDb); } #region U-R中存在的记录,但是在U-B中不存在,需要在U-R中删除 $UB_BookIds = array_column($returnArr, 'book_id'); $removeBookIds = array_diff($bookIdsFromUS, $UB_BookIds); if (!empty($removeBookIds)) { $removeBookKeys = []; foreach ($removeBookIds as $removeBookId) { $removeBookKeys[] = $this->getUBKey($userId, $removeBookId); } array_unshift($removeBookKeys, $this->getURKey($userId)); call_user_func_array([$redis, 'zrem'], $removeBookKeys); } #endregion //结果集使用updatetime逆序排序 $sortArr = array_column($returnArr, 'updatetime'); array_multisort($sortArr, SORT_DESC, $returnArr); return $returnArr; } /* * 阅读记录记入cookie 只记录5条数据 */ public function recentCookie($book_id, $chapterinfo) { $read_log = Cookie::get('read') ? Cookie::get('read') : []; unset($read_log[$book_id]); if (count($read_log) >= 5) { $first = array_keys($read_log)[0]; unset($read_log[$first]); } $wait['book_id'] = $book_id; $wait['chapter_id'] = $chapterinfo['id']; $wait['chapter_name'] = $chapterinfo['name']; $wait['updatetime'] = time(); $read_log[$book_id] = $wait; Cookie::set('read', $read_log); } }