redis 缓存新闻列表

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Cpath/article/details/79165991
/**
 * 1、实现关于新闻列表缓存 、收藏 、点赞 、视频点击等功能使用redis缓存技术
 * 
 * 2、列表新闻数据缓存模式 , 采用sorted set + hash 
 *        使用zadd 缓存文章id , hset 缓存文章标题等详情信息
 *
 * 3、视屏点赞存储在hash 中
 *
 * 4、用户对单个视频的收藏和点赞使用hash 
 *
 * 5、视频点击统计使用hash ,一小时同步数据
 *
 * 6、后台抓取回来的数据缓存问题
 *    
          $videoId = $redis->ZREVRANGE('mi_new_video_id_' , 0 ,0)[0]; // 获取最新视频id
          $maxValue = $redis->ZSCORE('mi_new_video_id_' , $videoId);  // 获取视屏对应的score
          # 存入视屏id
          $redis->ZADD('mi_new_video_id_' , time()+1 ,$video_id );

 */



namespace app\api\controller;
use think\Controller;
use think\Cache;
use think\Error;
use think\Exception;
use think\Log;
use think\Request;
use app\api\common\RedisKeyConts;
use app\api\common\OutJson;
use app\api\common\Hint;
use app\api\common\Helper;
use app\api\model\VideoModel;
use app\api\model\SlideModel;
use app\api\model\UserPraiseModel;
use app\api\model\UserCollectionModel;

class Homepage extends Common
{
    public function __construct()
    {
        parent::__construct();
        $this->videoDao = new VideoModel();
    }


    /**
     * [getNewList 获取新闻列表]
     * @param  Request $request [description]
     * @return [type]           [description]
     */
    public function getNewList(Request $request)
    {

        $wr_applet  = $request->post('wr_applet' , 0);        // 系统请求规则

        $page       = $request->post('page' , 1);             // 默认第一页

        $limit      =  10;                                    // 每页显示

        $uid        = $request->post('uid' , 0);               //  用户id

        if (empty($wr_applet) ||  empty($page) || $wr_applet != Hint::WR_APPLET)
           OutJson::outJsonNoData(Hint::PARAM_ERROR); // 返回错误码


        $videoIdtablePrefix = RedisKeyConts::$sortSetKey['mi_new_video_id_']; //  视屏id

        $videoInfoTablePrefix =  RedisKeyConts::$hastKey['mi_new_video_info_']; // 视屏详情

        $sortCluser = self::$redis->ZREVRANGE($videoIdtablePrefix ,  ($page - 1)*$limit , ($page - 1)*$limit+$limit-1); // 获取最新的分页集合 

        //$sortCluser ='';
        if (!empty($sortCluser)) {

            // 格式化列表
            $resultVideoList = $this->formatVideoListData($sortCluser ,$videoInfoTablePrefix ,$page ,$limit , $videoIdtablePrefix ,$uid);

            OutJson::outJsonResult(Hint::SUCCESS , $resultVideoList);

        }else{
            //获取mysql 当前页数据

            $this->getMysqlVideoList($page ,$limit , $videoIdtablePrefix ,$videoInfoTablePrefix);

        }

    }


    /**
     * @param $sortCluser sort set 存放的视频id
     * @param $videoInfoTablePrefix 视频详情表前缀
     * @param $page                当前页
     * @param $limit               每页显示条数
     * @param $videoIdtablePrefix  当前页获取到sort set 中的video_id 集合
     * @return array
     */
    public function formatVideoListData($sortCluser ,$videoInfoTablePrefix ,$page ,$limit , $videoIdtablePrefix ,$uid )
    {
        $videoPraiseNumTablePrefix  = RedisKeyConts::$hastKey['mi_new_video_praise_num_']; // 视屏点赞前缀

        $videoPlayNumTablePrefix  = RedisKeyConts::$hastKey['mi_new_video_play_num_'];     // 视屏点击前缀

        $pieTablePrefix  = RedisKeyConts::$hastKey['mi_new_user_video_praise_history_'];   // 获取用户对当前视频赞历史

        $cloTablePrefix  = RedisKeyConts::$hastKey['mi_new_user_video_collect_history_'];  // 获取用户对当前视频收藏历史

        $resultVideoList = []; // 输出集合

        foreach ($sortCluser as $key => $value) { // $value => $video_id

            $setTable = Helper::getSplitTable($videoInfoTablePrefix , $value , 10); // 获取视屏存储的表

            $resultVideoList[$key] = unserialize(self::$redis->hget($setTable,$value));

            $resultVideoList[$key]['video_praise_num'] = $this->getSingleVideoFieldInfo($value ,$videoPraiseNumTablePrefix ,'video_praise_num'); // 获取点赞

            $resultVideoList[$key]['video_play_num']   = $this->getSingleVideoFieldInfo($value ,$videoPlayNumTablePrefix ,'video_play_num'); // 获取播放

            $resultVideoList[$key]['check_praise'] = 0; // 标记当前用户对当前视屏是否点过赞

            $resultVideoList[$key]['check_collect'] = 0; // 标记当前用户对当前视屏是否点收藏

            if(!empty($uid)){ //检测用户是否收藏 和点赞 当前

                $resultVideoList[$key]['check_praise']  =  $this->getUserAtPresentVideoCheck( $pieTablePrefix, $value , $uid);

                $resultVideoList[$key]['check_collect'] =  $this->getUserAtPresentVideoCheck($cloTablePrefix , $value , $uid); // 标记用户是否收藏
            }

            $resultVideoList[$key]['ls_time']           =   date('Y-m-d',$value['ls_time']);                                                                // 格式化时间
        }

        if (empty($resultVideoList)) {
            // 异常
            Log::write( date('Y-m-d H:i:s').'get sort set article_id exist , hset article_id null' ,  implode(',',$sortCluser) );
            // 删除sort set 中的article_id
            $this->unsetSortSetArticleId($sortCluser);
            // 重写生成缓存
            $this->getMysqlVideoList($page ,$limit , $videoIdtablePrefix ,$videoInfoTablePrefix);

        }
        return $resultVideoList;
    }

    /**
     * @desc  移除sorted set 中的文章id
     * @param $sortCluser article_ids 集合
     */
    public function unsetSortSetArticleId($sortCluser)
    {
        $articleColTable = RedisKeyConts::$hastKey['mi_new_video_id_'];

        foreach ($sortCluser as $k=>$v)
        {
            self::$redis->zrem($articleColTable , $v);
        }
    }

    /**
     * @desc 从表中获取视屏列表
     * @param $page
     * @param $limit
     * @param $videoIdtablePrefix
     * @param $videoInfoTablePrefix
     */
    public function getMysqlVideoList($page ,$limit , $videoIdtablePrefix ,$videoInfoTablePrefix )
    {
        $where =  [
            'status'=>1,  
            'is_index'=>0 // 显示首页
        ];
        $offset =  ($page-1) * $limit;

        $order_by = ['sort'=>'asc' , 'ls_time'=>'desc'];

        // 获取首页列表
        $videoList =  $this->videoDao->getPageVideoList( $where , $order_by ,$offset , $limit);

        if (!empty($videoList)) {

            foreach($videoList as $key=>$value){
                // 缓存sort set video id
                self::$redis->zadd($videoIdtablePrefix , time()+$key  ,   $value['id']); // name score value

                // 缓存video info 分表存储
                $setTable = Helper::getSplitTable($videoInfoTablePrefix , $value['id'] , 10); // 分成10 张表

                self::$redis->hset($setTable , $value['id'] , serialize($value));  

            }

            OutJson::outJsonResult(Hint::SUCCESS , $videoList);

        } else {

            OutJson::outJsonNoData(Hint::HOMEPAGE_VIDEO_LIST_NULL);

        }
    }

    /**
     * @desc 获取单个视频的点赞数
     * @param $id 视频id
     */
    public function getSingleVideoFieldInfo($id , $videoFieldTablePrefix , $field)
    {
        $videoFieldTablePrefix = Helper::getSplitTable($videoFieldTablePrefix , $id , 10);

        $rdsVideoFieldNum  = self::$redis->hget($videoFieldTablePrefix , $id); // redis 视屏赞数量
        //$rdsVideoPraiseNum ='';
        if (empty($rdsVideoFieldNum))
        {
            $rdsVideoFieldNum  = $this->videoDao->getVideoInfo($id)[$field];

            $rdsVideoFieldNum =  $rdsVideoFieldNum == 0 ? rand(1 , 1000) : $rdsVideoFieldNum ;

            self::$redis->hsetnx( $videoFieldTablePrefix, $id , $rdsVideoFieldNum);

        }

        return $rdsVideoFieldNum;

    }

    /**
     * @param $checkTablePrefix 表前缀
     * @param $video_id
     * @param $uid
     * @return int
     */
    public function getUserAtPresentVideoCheck( $checkTablePrefix , $video_id , $uid)
    {
        //单用户对首页单个视频 的收藏只缓存30天
        // 对用户进行分表
        $tablePrefix =  RedisKeyConts::$hastKey[$checkTablePrefix];

        $setTable = Helper::getSplitTable($tablePrefix , $uid , 10); // 分成10 张表

        $hisResult= self::$redis->hget($setTable , $uid.'_video_'.$video_id);

        return $hisResult = $hisResult == '' || 0  ?  0 : $hisResult;

    }

    /**
     * @desc 增加视频点击  定时脚本统计入库
     */
    public function addVideoClick(Request $request)
    {
        $wr_applet  = $request->post('wr_applet' , 0);

        $video_id  = $request->post('video_id' , 0); // 视频id

        $uid  = $request->post('uid' , 0);             //  用户id

        if (empty($wr_applet) || empty($video_id) || empty($uid) || $wr_applet != Hint::WR_APPLET)
            OutJson::outJsonNoData(Hint::PARAM_ERROR);


        $videoTablePrefix = RedisKeyConts::$hastKey['mi_new_video_play_num_']; // 视屏点击前缀

        $videoTable  = Helper::getSplitTable($videoTablePrefix , $video_id , 10);

        if (self::$redis->HEXISTS($videoTable , $video_id)) {

            self::$redis->HINCRBY($videoTable , $video_id , 1);

        }else{

            self::$redis->hset( $videoTable, $video_id , 1); // 第一次点击

        }

        // 统计有更新的视频 ,用于同步mysql
        $videoClickTablePrefix  = RedisKeyConts::$hastKey['mi_new_video_click_update'];
        self::$redis->Hsetnx($videoClickTablePrefix , $video_id , $video_id); 

        if(!self::$redis->EXISTS($videoClickTablePrefix)){

            self::$redis->expire($videoClickTablePrefix , RedisKeyConts::$expireTime[$videoClickTablePrefix]);

        }

        OutJson::outJsonResult(Hint::SUCCESS); //
    }

    /**
     * @desc 增加视频点赞 , +1 -1 定时脚本统计入库
     */
    public function addMinusVideoPraise(Request $request)
    {
        $wr_applet  = $request->post('wr_applet' , 0);

        $video_id  = $request->post('video_id' , 0); // 视频id

        $uid  = $request->post('uid' , 0);             //  用户id

        $is_type  = intval($request->post('is_type' , 1));             // is_type 点赞类型  默认为[1 +赞   0 - 赞 ]

        if (empty($wr_applet) || empty($video_id) || empty($uid)  || $wr_applet != Hint::WR_APPLET)
            OutJson::outJsonNoData(Hint::PARAM_ERROR);


        $videoPraiseNumTablePrefix  = RedisKeyConts::$hastKey['mi_new_video_praise_num_']; // 视屏点赞前缀

        $videoPraise = Helper::getSplitTable($videoPraiseNumTablePrefix , $video_id , 10);


        $userVideoPraiseHistoryPrefix = RedisKeyConts::$hastKey['mi_new_user_video_praise_history_'];

        $userVideoPraiseHistoryTable = Helper::getSplitTable($userVideoPraiseHistoryPrefix , $uid , 10); // 分成10 张表

        // +1
        if($is_type == 1){

            if (self::$redis->HEXISTS($videoPraise , $video_id)) {

                self::$redis->HINCRBY($videoPraise , $video_id , 1);

            }else{

                self::$redis->hset( $videoPraise, $video_id , 1); // 第一次点赞

                self::$redis->expire($videoPraise , RedisKeyConts::$expireTime[$videoPraiseNumTablePrefix]); // 设置单张表的缓存时间

            }
            // 增加用户赞历史
            self::$redis->hset($userVideoPraiseHistoryTable , $uid.'_video_'.$video_id  ,1);

        }else{
            if (self::$redis->HEXISTS($videoPraise , $video_id)) {

                // 视频总赞-1
                self::$redis->HINCRBY($videoPraise , $video_id , -1);

                // 移除当前用户 当前赞历史
                self::$redis->Hdel($userVideoPraiseHistoryTable , $uid.'_video_'.$video_id);

                // 更新数据表 , 用户取消赞
                //$userPraise = new UserPraiseModel();

                //$userPraise -> updateUserVideoPraise( $uid ,$video_id ,  $is_type);

            }

        }

        $videoClickUpdateTablePrefix  = RedisKeyConts::$hastKey['mi_new_video_praise_update']; // 一个小时视屏点赞前缀

        self::$redis->Hsetnx($videoPraiseNumTablePrefix , $video_id , $video_id); // 缓存一个小时内,有点击的视频 , 定时脚本同步mysql video_praise_num

        if(!self::$redis->EXISTS($videoClickUpdateTablePrefix)){

            self::$redis->expire($videoClickUpdateTablePrefix , RedisKeyConts::$expireTime[$videoClickUpdateTablePrefix]);

        }

        OutJson::outJsonResult(Hint::SUCCESS); //
    }


    /**
     * @desc 用户点击收藏
     */
    public function userClickVideoCollect(Request $request)
    {
        $wr_applet  = $request->post('wr_applet' , 0);

        $video_id  = $request->post('video_id' , 0); // 视频id

        $uid  = $request->post('uid' , 0);             //  用户id

        $is_type = $request->post('is_type' , 1); // 默认收藏 , [1 收藏 , 0 取消收藏]


        if (empty($wr_applet) || empty($video_id) || empty($uid) || $wr_applet != Hint::WR_APPLET)
            OutJson::outJsonNoData(Hint::PARAM_ERROR);


        $userVideoCollectHistoryPrefix = RedisKeyConts::$hastKey['mi_new_user_video_collect_history_'];

        $userVideoPraiseHistoryTable = Helper::getSplitTable($userVideoCollectHistoryPrefix , $uid , 10); // 分成10 张表

        try{
            if($is_type == 1){
                // 设置收藏
                self::$redis->Hsetnx($userVideoPraiseHistoryTable , $uid.'_video_'.$video_id , 1);

            }else{

                // 取消收藏
                self::$redis->hdel($userVideoPraiseHistoryTable ,  $uid.'_video_'.$video_id);
                // 更新用户收藏表

                $userCollectionModel = new UserCollectionModel();


                $userCollectionModel->updateUserVideoCollect( $uid ,$video_id ,  $is_type);

            }
            OutJson::outJsonNoData(Hint::SUCCESS);

        }catch (\Exception $e){

            Log::write('userClickVideoCollect Exception' , array('uid'=>$uid , 'video'=>$video_id , 'is_type'=>$is_type));

        }

    }


}

猜你喜欢

转载自blog.csdn.net/Cpath/article/details/79165991