'上架', '0' => '下架', '-1' => '入库']; } public function getCanseeList() { return ['1' => '可见', '0' => '不可见']; } public function getSexList() { return ['1' => '男频', '2' => '女频']; } public function getBillingTypeList() { return ['1' => '章', '2' => '本']; } public function getIsFinishList() { return ['0' => '连载', '1' => '完本']; } public function getCornerMarkList() { return ['hot' => '火热在推', 'exclusive' => '独家', 'new' => '新书']; } public function getIsExpireAttr($value,$data) { if(empty($data['expire_time'])){ return 0; } if ($data['expire_time'] - time() < 3600*24*30){ return 1; } return 2; } public function getExpireTimeTextAttr($value,$data) { return date('Y-m-d H:i:s',$data['expire_time']); } public function getRankTextAttr($value, $data) { $status = ['1'=>'不通过','2'=>'一级敏感','3'=>'二级敏感','4'=>'通过','5'=>'不收录','6'=>'二级可上架','0'=>'没有评级']; return $status[$data['rank']]; } public function getCheckRankTextAttr($value, $data) { $status = ['1'=>'不合格','2'=>'合格','0'=>'未质检']; return $status[$data['check_rank']]; } public function getCategoryName($book_category_id) { $category = model('BookCategory')->info($book_category_id); return $category['name']; } public function getCategoryTextAttr($value, $data) { $value = $value ? $value : $data['book_category_id']; $category = model('BookCategory')->info($value); return $category['name']; } public function getStateTextAttr($value, $data) { $value = $value ? $value : $data['state']; $list = $this->getStateList(); return isset($list[$value]) ? $list[$value] : ''; } public function getCornerMarkTextAttr($value, $data) { $value = $value ? $value : $data['corner_mark']; $list = $this->getCornerMarkList(); return isset($list[$value]) ? $list[$value] : ''; } public function getFreeStimeTextAttr($value, $data) { $value = $value ? $value : $data['free_stime']; return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; } public function getFreeEtimeTextAttr($value, $data) { $value = $value ? $value : $data['free_etime']; return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; } public function getLastChapterUtimeTextAttr($value, $data) { $value = $value ? $value : $data['last_chapter_utime']; return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; } public function getSexTextAttr($value, $data) { $value = $value ? $value : $data['sex']; $list = $this->getSexList(); return isset($list[$value]) ? $list[$value] : ''; } public function getCanSeeTextAttr($value, $data) { $value = $value ? $value : $data['cansee']; $list = $this->getCanseeList(); return isset($list[$value]) ? $list[$value] : ''; } public function getBillingTypeTextAttr($value, $data) { $value = $value ? $value : $data['billing_type']; $list = $this->getBillingTypeList(); return isset($list[$value]) ? $list[$value] : ''; } public function getIsFinishTextAttr($value, $data) { $value = $value ? $value : $data['is_finish']; $list = $this->getIsFinishList(); return isset($list[$value]) ? $list[$value] : ''; } protected function setFreeStimeAttr($value) { return $value && !is_numeric($value) ? strtotime($value) : $value; } protected function setFreeEtimeAttr($value) { return $value && !is_numeric($value) ? strtotime($value) : $value; } protected function setLastChapterUtimeAttr($value) { return $value && !is_numeric($value) ? strtotime($value) : $value; } public function BookCategory() { return $this->belongsTo('BookCategory', 'book_category_id', 'id')->setEagerlyType(0); } /* * 检查书籍信息 */ public static function checkBookInfo($book) { $msg = ''; //如果是按本收费,必须有价格。 if ($book['billing_type'] == '2') { if (empty($book['price']) || $book['price'] <= 0) { return $msg = '书籍' . $book['id'] . '按本收费书籍必须填写价格'; } } //名称、作者、封面、字数、描述、完结状态、首末章节信息 if (empty($book['name'])) { return $msg = '书籍' . $book['id'] . '接口获取不到书籍名称'; } if (empty($book['author'])) { return $msg = '书籍' . $book['id'] . '接口获取不到作者信息'; } if (empty($book['image'])) { return $msg = '书籍' . $book['id'] . '接口获取不到封面'; } if (empty($book['description'])) { return $msg = '书籍' . $book['id'] . '接口获取不到书籍描述'; } if (empty($book['chapter_num'])) { return $msg = '书籍' . $book['id'] . '接口获取不到章节数'; } if (empty($book['first_chapter_id']) || $book['first_chapter_id'] <= 0) { return $msg = '书籍' . $book['id'] . '接口获取不到章节数'; } return $msg; } /** * 书籍信息 Redis缓存永久 */ public function BookInfo($id) { $redis = Redis::instance(); $key = 'B:' . $id; if ($redis->exists($key)) { $info = $redis->hgetall($key); if (!is_array($info) || count($info) < 5 ) { $redis->del($key); $info = $this->get($id); if ($info) { $info = $info->toArray(); //维护书籍互斥关系 wudd 《前台不做维护-20200115》 || !isset($info['relation_id']) //$info['relation_id']= BookRelationService::instance()->getBookRelationById( $id ); $redis->hmset($key, $info); } else { return ''; } } } else { $info = $this->get($id); if ($info) { $info = $info->toArray(); //维护书籍互斥关系 wudd 《前台不做维护-20200115》 //$info['relation_id']= BookRelationService::instance()->getBookRelationById( $id ); $redis->hmset($key, $info); } else { return ''; } } return $info; } /**************************通过API获取书籍信息***********************/ /** * 根据书籍id获取存储路径 * * @param $book_id * return string */ protected static function getPath($book_id) { $path = '/' . substr($book_id, 0, 1) . 'x' . substr($book_id, 1, 1) . '/' . substr($book_id, 0, 2) . 'x' . substr($book_id, 2, 1) . '/' . substr($book_id, 0, 3) . 'x' . substr($book_id, 3, 1) . '/' . $book_id . '/'; return $path; } /** * 获取单本书籍信息 * * @param int $book_id 书籍ID * @return array */ public static function getBookInfo($book_id) { $lastChapter = self::getChapterLimit($book_id, -1, -1); if (empty($lastChapter)) { Log::notice('获取末章信息失败!书籍ID:' . $book_id); return ['code' => 100]; } $lastChapter = current($lastChapter); $firstChapter = self::getChapterLimit($book_id, 0, 0); if (empty($firstChapter)) { Log::notice('获取首章信息失败!书籍ID:' . $book_id); return ['code' => 100]; } $firstChapter = current($firstChapter); $chapterCount = self::getChapterCount($book_id); if(ConfigAs::get('redis.change') == 1){ $code = self::getCode('A03'.$book_id); $redis = Redis::instanceBookChange($code); $book = $redis->get("A03{$book_id}"); $book = json_decode($book, true); }else { $redis = Redis::instanceBook(); $book = $redis->get("basedata_book_{$book_id}"); $book = json_decode(gzdecode($book), true); } if (strpos($book['coverWap'], 'cppartner') === false) { $cover = ConfigAs::get('api.cdn') . "/cppartner" . self::getPath($book['bookId']) . $book['coverWap']; } else { $cover = ConfigAs::get('api.cdn') . $book['coverWap']; } $extend = []; if (isset($book['extend'])){ $extend = !is_array($book['extend'])?json_decode($book['extend'],true):$book['extend']; } $data = [ "author" => $book['author'], "word_num" => $book['totalWordSize'] ?: 0, "first_chapter_id" => $firstChapter['id'], "first_chapter_name" => $firstChapter['name'], "last_chapter_id" => $book['lastChapterId'] ?? $lastChapter['id'], "last_chapter_name" => $book['lastChapterName'] ?? $lastChapter['name'], "last_chapter_utime" => empty($book['lastChapterUtime']) ? time() : strtotime($book['lastChapterUtime']), "description" => $book['introduction'], "chapter_num" => $chapterCount, "cover"=>$cover, "read_num" => $book['clickNum'], "name" => $book['bookName'], "is_finish" => strstr($book['status'], '连') === false ? 1 : 0, "id" => $book['bookId'], //wud 书籍互斥关系 "childBookId"=>$book['childBookId'] ?? [], "parentBookId"=>$book['parentBookId'] ?? '', //同步书籍评级和质检状态 及质检备注 "rank"=> $extend['grade'] ?? 0, "check_rank"=> !isset($extend['qaStatus'])? 0 : ($extend['qaStatus'] == 1 ? 2 : 1), 'check_remark'=>isset($extend['remark'])?substr($extend['remark'],0,140):'', 'cp_name'=>$book['cpPartnerName']??'', 'cp_id'=>$book['cpPartnerId']??0, 'tags' => $book['tag'] ?? '', ]; return [ 'data' => $data, 'code' => 0, 'msg' => 'success' ]; // return self::getApi('bookinfo.do', ['book_id' => $book_id]); } /** * 获取多本书籍信息 * * @param string $book_ids 书籍ID,书籍ID,书籍ID * @return array */ public static function getBookInfos($book_ids) { $data = []; $ids = explode(',', $book_ids); foreach ($ids as $id) { if ($id) { $book = self::getBookInfo($id); if ($book['code'] != 0) { Log::notice('获取书籍信息失败!书籍ID:' . $id); return ['code' => 100]; } $data[] = $book['data']; } } return [ 'data' => $data, 'code' => 0, 'msg' => 'success' ]; // return self::getApi('bookinfos.do', ['book_ids' => $book_ids]); } public static function getCode($val){ $bKey = md5($val,true); $rv = (ord($bKey[3]) & 0xFF) << 24 | (ord($bKey[2]) & 0xFF) << 16 | (ord($bKey[1]) & 0xFF) << 8 | ord($bKey[0]) & 0xFF; return $rv & 0xffffffff; } /** * 获取书籍章节信息 * @deprecated use searchChapterByName getChapterList getChapterLimit * @param int $book_id 书籍ID * @param int $page_no 第几页 * @param int $limit 单页条数 * @return array */ public static function getChapterListOld($book_id, $page_no = 1, $limit = 20, $search = '') { if(ConfigAs::get('redis.change') == 1){ //Log::write('redis.change = 1','cctest'); $code = self::getCode('A04'.$book_id); //Log::write('测试code = '.$code,'cctest'); $redis = Redis::instanceBookChange($code); $list = $redis->zRange('A04' . $book_id, 0, -1); //dump($list);die; }else{ $redis = Redis::instanceBook(); $list = $redis->lRange("basedata_chapter_owchcp_{$book_id}", 0, -1); } $all = []; //全部章节 $data = [ 'data' => [], 'pageNo' => $page_no, 'limit' => $limit, 'totalNum' => 0, 'totalPage' => 0 ]; foreach ($list as $key => $value) { if(ConfigAs::get('redis.change') == 1){ $json = json_decode($value, true); }else{ $json = json_decode(gzdecode($value), true); } $chapter = [ 'id' => $json[4] ?? $json['chapterId'], 'idx' => count($all) + 1, 'name' => $json[5] ?? $json['chapterName'] ]; if ($search) { if (!strstr($chapter['name'], $search)) { continue; } } $all[] = $chapter; } if ($limit > 0) { $data['data'] = array_slice($all, ($page_no - 1) * $limit, $limit); } else { //获取全部 $data['data'] = $all; $data['limit'] = count($all); } if (!count($data['data'])) { Log::notice('获取章节列表失败!书籍ID:' . $book_id . ' 分页:' . $page_no . ' 条数:' . $limit); return $data; } $data['totalNum'] = count($all); $data['totalPage'] = ceil(count($all) / $limit); return [ 'data' => $data, 'code' => 0, 'msg' => 'success' ]; // return self::getApi('chapterlist.do', ['book_id' => $book_id, 'page_no' => $page_no, 'limit' => $limit]); } /** * 书籍章节列表redis的key * @param string $bookId 书籍id * @return string */ private static function getChapterKey($bookId) { return 'A04' . $bookId; } /** * 通过章节名称搜索章节,此方法需要获取书籍所有章节列表,请不要在前台使用 * @param $bookId * @param int $pageNo * @param int $limit * @param $name * @return array */ public static function searchChapterByName($bookId, $pageNo = 1, $limit = 20, $name) { if (empty($name)) { return self::getChapterList($bookId, $pageNo, $limit); } $chapterKey = self::getChapterKey($bookId); $code = self::getCode($chapterKey); $redis = Redis::instanceBookChange($code); $list = $redis->zRange($chapterKey, 0, -1); $all = []; $data = [ 'data' => [], 'pageNo' => $pageNo, 'limit' => $limit, 'totalNum' => 0, 'totalPage' => 0 ]; foreach ($list as $key => $value) { $json = json_decode($value, true); $chapter = [ 'id' => $json[4] ?? $json['chapterId'], 'idx' => $key + 1, 'name' => $json[5] ?? $json['chapterName'] ]; if (!strstr($chapter['name'], $name)) { continue; } $all[] = $chapter; } if ($limit > 0) { $data['data'] = array_slice($all, ($pageNo - 1) * $limit, $limit); } else { //获取全部 $data['data'] = $all; $data['limit'] = count($all); } if (!count($data['data'])) { Log::notice('获取章节列表失败!书籍ID:' . $bookId . ' 分页:' . $pageNo . ' 条数:' . $limit); return ['code' => 100]; } $data['totalNum'] = count($all); $data['totalPage'] = ceil(count($all) / $limit); return [ 'data' => $data, 'code' => 0, 'msg' => 'success' ]; } /** * 获取书籍章节列表 * @param $bookId * @param int $pageNo 当前页码 * @param int $limit 每页显示条数 * @return array */ public static function getChapterList($bookId, $pageNo = 1, $limit = 20) { $chapterKey = self::getChapterKey($bookId); $code = self::getCode($chapterKey); $redis = Redis::instanceBookChange($code); $start = ($pageNo - 1) * $limit; $stop = $start + $limit - 1; $list = $redis->zRange($chapterKey, $start, $stop); $all = []; $data = [ 'data' => [], 'pageNo' => $pageNo, 'limit' => $limit, 'totalNum' => 0, 'totalPage' => 0 ]; foreach ($list as $key => $value) { $json = json_decode($value, true); $chapter = [ 'id' => $json[4] ?? $json['chapterId'], 'idx' => $start + $key + 1, 'name' => $json[5] ?? $json['chapterName'] ]; $all[] = $chapter; } $data['data'] = $all; $data['limit'] = count($all); if (!count($data['data'])) { Log::notice("获取章节列表失败!书籍ID:$bookId 分页:$pageNo 条数:$limit"); return ['code' => 100]; } $totalNum = self::getChapterCount($bookId); $data['totalNum'] = $totalNum; $data['totalPage'] = ceil($totalNum / $limit); return [ 'data' => $data, 'code' => 0, 'msg' => 'success' ]; } /** * 获取书籍章节数量 * @param $bookId * @return int */ public static function getChapterCount($bookId) { $chapterKey = self::getChapterKey($bookId); $code = self::getCode($chapterKey); $redis = Redis::instanceBookChange($code); $count = $redis->zCard($chapterKey); return $count; } /** * 按章节索引获取书籍章节列表 * @param $bookId * @param $startIdx 起始索引,第一章的索引为0 * @param $stopIdx 结束索引,最后一章的索引为-1,倒数第二章为-2 * @return array */ public static function getChapterLimit($bookId, $startIdx, $stopIdx) { $chapterKey = self::getChapterKey($bookId); $code = self::getCode($chapterKey); $redis = Redis::instanceBookChange($code); $list = $redis->zRange($chapterKey, $startIdx, $stopIdx); if (empty($list)) { Log::notice("获取章节列表失败!书籍ID:$bookId startIdx:$startIdx stopIdx:$stopIdx"); return []; } foreach ($list as $key => $value) { $json = json_decode($value, true); $chapter = [ 'id' => $json[4] ?? $json['chapterId'], 'idx' => $startIdx + $key + 1, 'name' => $json[5] ?? $json['chapterName'] ]; $all[] = $chapter; } return $all; } /** * 获取章节详情 * * @param int $book_id 书籍ID * @param int $chapter_id 章节ID * @param int $type 分割格式 默认1
2数组 * @return array */ public static function getChapterInfo($book_id, $chapter_id, $type = 1) { $chapterKey = self::getChapterKey($book_id); $code = self::getCode($chapterKey); $redis = Redis::instanceBookChange($code); $chapterList = $redis->zRangeByScore($chapterKey, $chapter_id, $chapter_id, array('limit' => array(0, 1))); if (empty($chapterList)) { Log::notice('获取章节列表失败!书籍ID:' . $book_id); return ['code' => 100]; } $chapterInfoStr = current($chapterList); $chapterInfo = json_decode($chapterInfoStr, true); $data = [ 'name' => $chapterInfo[5], 'idx' => $chapterInfo['k'] + 1, 'id' => $chapterInfo[4], ]; $data['pre_id'] = $chapterInfo['l'] ?? ''; $data['next_id'] = $chapterInfo['n'] ?? ''; $redis = Redis::instance(); $keyChapter = 'BCCT:' . $chapter_id; if ($type == 1 && $redis->exists($keyChapter)) { if (BookService::instance()->getChapterEditedModel()->getChapterFromDb($chapter_id)) { $data['from'] = 'db'; } else { $data['from'] = 'cache'; } $data['content'] = $redis->get($keyChapter); } else { $chapter_result = BookService::instance()->getChapter($book_id, $chapter_id)->data; $data['from'] = $chapter_result['from']; if (array_key_exists('name', $chapter_result)) { $data['name'] = $chapter_result['name']; } if ($content = $chapter_result['content']) { // if (false) { $content = str_replace("\r", '', $content); $arr = explode("\n", $content); $str = ''; $newArr = []; foreach ($arr as &$value) { $value = ltrim($value); $value = mb_ereg_replace('^( | )+','', $value); $value = mb_convert_encoding($value, "UTF-8", "UTF-8"); //强制转换一次UTF-8编码 if ($value) { if ($type == 1) { $str .= "
{$value}
"; } else { $newArr[] = $value; } } } unset($value); if ($type == 1) { $data['content'] = $str; if (!empty($data['content'])) { $keyNum = 'BCN:' . $chapter_id . ':' . date('i'); if ($redis->incr($keyNum) > 10) { $redis->setex($keyChapter, 86400, $data['content']); } $redis->expire($keyNum, 60); } } else { $data['content'] = $arr; } } else { Log::error('获取章节内容失败!书籍ID:' . $book_id . ' 章节ID:' . $chapter_id); if ($type == 1) { $data['content'] = ''; } else { $data['content'] = []; } } } $json = json_decode($data['content'], true); if ($json) { $content = array_map(function ($item) { return '' . $item . '
'; }, $json); $data['content'] = implode('', $content); } return [ 'data' => $data, 'code' => 0, 'msg' => 'success' ]; // return self::getApi('chapterinfo.do', ['book_id' => $book_id, 'chapter_id' => $chapter_id]); } /** * 获取书籍头几章 * * @param int $book_id 书籍ID * @param int $head 章节序号 * @return array */ public static function getHeadChapters($book_id, $head) { if ($head > 10) { //最多读取10章 $head = 10; } $chapterList = self::getChapterLimit($book_id, 0, $head - 1); $data = []; foreach ($chapterList as $chapter) { $info = \app\main\service\BookService::instance()->getChapterInfo($book_id, $chapter['id']); if ($info->code != 0) { Log::error('获取章节内容失败!书籍ID:' . $book_id . ' 章节ID:' . $chapter['id']); return ['code' => 100]; } if(is_string($info->data['content'])){ preg_match_all('/(.*?)<\/p>/', $info->data['content'], $match); if($match){ $info->data['content'] = $match[1]; $data[] = $info->data; }else{ $data[] = $info->data; } }else{ $data[] = $info->data; } } return [ 'data' => $data, 'code' => 0, 'msg' => 'success' ]; // return self::getApi('chapterinfos.do', ['book_id' => $book_id, 'head' => $head]); } /** * 批量获取书籍信息 * @param array $ids 书籍id列表 * @return array */ public function getBooksInfo(array $ids) { if (empty($ids)) { return []; } $result = []; #region 从redis中获取书籍信息 $keys = []; $dbIds = []; foreach ($ids as $id) { $keys[] = 'B:' . $id; } $getBooksInfoRedisIndex = Redis::splitKeysByRule($keys); $rdsBooksInfo = []; foreach ($getBooksInfoRedisIndex as $k => $v) { $redis = Redis::getRedisConnect($k); $pipe = $redis->multi(\Redis::PIPELINE); foreach ($v as $redisKey) { $pipe->hGetAll($redisKey); } $_rdsBooksInfo = $pipe->exec(); $_rdsBooksInfo = array_filter($_rdsBooksInfo); $rdsBooksInfo = array_merge($rdsBooksInfo, $_rdsBooksInfo); } $rdsBookIds = []; foreach ($rdsBooksInfo as $item) { if (!is_array($item) || count($item) < 5) { continue; } $result[$item['id']] = $item; $rdsBookIds[] = $item['id']; } $dbIds = array_diff($ids, $rdsBookIds); #endregion #region 没有在redis中找到的书籍,从数据库中获取,并插入redis if (count($dbIds) > 0) { $dbBooksInfo = $this->whereIn('id', $dbIds)->select(); if (count($dbBooksInfo) > 0) { $originBookInfo = []; $booksIdKey = []; foreach ($dbBooksInfo as $dbBookInfo) { $bookInfo = $dbBookInfo->toArray(); //获取书籍的互斥关系 wdd 《前台不做维护-20200115》 //$bookInfo['relation_id'] = BookRelationService::instance()->getBookRelationById( $bookInfo['id'] ); $originBookInfo[$bookInfo['id']] = $bookInfo; $booksIdKey[] = 'B:' . $bookInfo['id']; } $writeBooksInfoRedisIndex = Redis::splitKeysByRule($booksIdKey); foreach ($writeBooksInfoRedisIndex as $k => $v) { $redis = Redis::getRedisConnect($k); $pipe = $redis->multi(\Redis::PIPELINE); foreach ($v as $redisKey) { $bookId = str_replace('B:', '', $redisKey); $result[$bookId] = $originBookInfo[$bookId]; $pipe->hmset($redisKey, $originBookInfo[$bookId]); } $pipe->exec(); } } } return $result; } /** * 获取作者其他书籍(随机选择一本书) * @param $expectBookid 书籍ID,排重 * @param $author 作者名称 * @param null $expectBookid */ public function getBookIdByAuthor($author, $expectBookid = null, $sex = '1',$isWater=false) { $gussNovels = GussNovelService::instance()->getLikedNovels($sex,$isWater); // 跟猜你喜欢中的书籍进行滤重 $gussBookIds = array_column($gussNovels, 'id'); $waterWhere = $isWater ? ['classify_white_list'=>1] : []; $books = $this->where('author', 'eq', $author) ->where('state', 'eq', '1') ->where($waterWhere) ->select(); if (!empty($expectBookid)) { foreach ($books as $k => $item) { if ($item['id'] == $expectBookid || in_array($item['id'],$gussBookIds)) { unset($books[$k]); } } } $books = array_values($books); if(empty($books)){ return ; }else{ $rand = rand(0, (count($books)-1)); return $books[$rand]['id']; } } //获取当前渠道不能查看的书籍id public function getExclusiveBookIds($channel_id){ $sql = 'select aeb.bid from exclusive_book aeb inner join exclusive ae on aeb.eid = ae.id and ae.endtime > '.time().' and ae.status = 1 where aeb.bid not in( select distinct eb.bid from `exclusive_channel` as ec inner join `exclusive` as e on ec.eid = e.id and e.endtime > '.time().' AND e.status = 1 inner join `exclusive_book` as eb on eb.eid = e.id where ec.cid = '.$channel_id.' ) and aeb.bid not in( select eb.bid from exclusive_book eb left join exclusive_channel ec on ec.eid=eb.eid where ec.id is null )'; $rs = Db::query($sql); if(empty($rs)){ return []; } return array_column($rs,'bid'); } public function getVipExclusiveBookIds($channel_ids){ $sql = 'select aeb.bid from exclusive_book aeb inner join exclusive ae on aeb.eid=ae.id and endtime>'.time().' and status=1 and aeb.bid not in( select eb.bid from exclusive_book eb inner join exclusive e on e.id=eb.eid and endtime>'.time().' and status=1 inner join exclusive_channel ec on ec.eid=eb.eid where ec.cid in('.implode(',',$channel_ids).') ) inner join exclusive_channel ec on ec.eid = ae.id'; $rs = Db::query($sql); if(empty($rs)){ return []; } return array_column($rs,'bid'); } }