zoukankan      html  css  js  c++  java
  • thinkphp redis实现文章点赞功能并同步入mysql

    <?php
    
    namespace appcommoncontroller;
    
    use thinkApp;
    use thinkfacadeCache;
    use thinkfacadeDb;
    
    
    /**
     * redis 点赞/收藏模块
     * @package appadmincontroller
     * @author 宁佳兵
     */
    class Praise
    {
        private $redis = null;
        private $member_id;   //用户id
        private $customer_id;   //客户id
        private $article_id;   //文章id
        private $article_type;  //文章类型
        private $status;    //点赞状态  1 点赞 0 取消点赞
        private $article_user_like;    //hash存放用户点赞记录
        private $article_user_like_set;    //集合存放该文章下点赞用户ID
        private $list_article; //队列存放article_id
        private $article_counts;   //文章点赞数
        private $praise_table = 'praise';
    
        /**
         * Praise constructor.
         * @param string $customer_id   客户id
         * @param string $member_id   用户id
         * @param string $article_id    文章id
         * @param string $article_type  文章类型/表名
         */
        public function __construct( $customer_id = '',  $member_id = '',  $article_id = '', $article_type = '')
        {
            $this->customer_id = $customer_id;
            $this->member_id = $member_id;
            $this->article_id = $article_id;
            $this->article_type = $article_type;
            $this->redis = Cache::store("redis");
            $this->list_article = 'list_article';
        }
    
        /**
         * 点赞入口
         * @return bool
         * @throws PsrSimpleCacheInvalidArgumentException
         * @throws 	hinkdbexceptionDataNotFoundException
         * @throws 	hinkdbexceptionDbException
         * @throws 	hinkdbexceptionModelNotFoundException
         * @date 2020-07-17
         * @author 宁佳兵
         */
        public function giveFavour()
        {
            //判断用户点赞状态
            //先从缓存中读取数据,没有 则从mysql中读取数据
            $articleUserData = $this->getPraiseStatus();
    
            //存储点赞数据到redis
            $this->addArticleHash($articleUserData);
    
            //存储当前文章下的点赞用户id
            $this->addUser();
    
            //存储文章点赞数 自增/自减
            $this->articleLikeCount();
    
            //文章点赞数量
            $dbCount = $this->findMysqlCounts(); //mysql点赞数
            $redisCount = $this->findRedisCounts(); //redis点赞数
            $data['counts'] = (int)$dbCount + (int)$redisCount; //点赞总数
            $data['status'] = $this->status;
    
            //文章进入队列  等待同步到mysql
            $this->queueInto();
    
            return $data;
        }
    
        /**
         * 将redis 缓存 同步到 mysql中
         * @throws Exception
         * @date 2020-07-17
         * @author 宁佳兵
         */
        public function syncInsertArticle()
        {
            $limit = input("limit");
            $limit = isset($limit) ? $limit - 1 : '-1';
    
            //取出队列
            $article = $this->queueOut($limit);
    
            //开启事务
            Db::startTrans();
    
            try {
                foreach ($article as $key => $val) {
                    list($this->article_id, $this->article_type, $this->customer_id, $this->member_id) = explode('_', $val);
                    //获取redis中该文章下所有用户id
                    $user = $this->findUserByNews();
                    //获取redis中文章点赞数
                    $redisNum = $this->findRedisCounts();
    
                    //更新mysql中文章点赞数
                    $this->updateDbCount($redisNum);
                    //获取该文章中的用户,循环更新数据到mysql
    
                    foreach ($user as $k => $v) {
                        $this->member_id = $v;
                        //查询该文章hash数据
                        $userSatateData = $this->findArticleHash();
                        //更新mysql中eda_zan表数据
                        $this->updateDbArticle($userSatateData);
                        //删除该文章下redis中的用户
                        $this->unsetRedisUserCounts();
                        //删除redis中该文章hash数据
                        $this->unsetRedisArticleHash();
                    }
                }
            } catch (Exception $exception) {
                //事务回滚
                Db::rollback();
                //写入日志
                $this->writeLog();
                throw new Exception($exception->getMessage());
            }
            //提交事务
            Db::commit();
            //TODO 这里暂时删除全部队列,后续需要完善
            $this->redis->del($this->list_article);
            echo 'success';
        }
    
        /**
         * 点赞总数
         * @return array
         * @throws PsrSimpleCacheInvalidArgumentException
         * @throws 	hinkdbexceptionDataNotFoundException
         * @throws 	hinkdbexceptionDbException
         * @throws 	hinkdbexceptionModelNotFoundException
         * @date 2020-07-20
         * @author 宁佳兵
         */
        public function getPraiseCounts()
        {
            $this->getPraiseStatus();
            return [
                'counts' => ((int)$this->findMysqlCounts() + (int)$this->findRedisCounts()),
                'status' => $this->status ? 0 : 1
            ]; //点赞总数
        }
    
        /**
         * 判断用户点赞状态  先从缓存中读取数据,没有 则从mysql中读取数据
         * @return bool
         * @throws 	hinkdbexceptionDataNotFoundException
         * @throws 	hinkdbexceptionDbException
         * @throws 	hinkdbexceptionModelNotFoundException
         * @date 2020-07-20
         * @author 宁佳兵
         */
        private function getPraiseStatus()
        {
            if ($articleUserData = $this->findArticleHash()) {
                $this->status = $articleUserData['status'] ? 0 : 1;
            } else {
                if ($result = $this->findDbArticle()) {
                    $this->status = $result['status'] ? 0 : 1;
                } else {
                    $this->status = 1;
                }
            }
            return $articleUserData;
        }
    
        /**
         * 获取redis中用户点赞状态数据
         * @return bool
         * @date 2020-07-17
         * @author 宁佳兵
         */
        private function findArticleHash()
        {
            $this->article_user_like = 'article_user_like_' . $this->customer_id . '_' . $this->member_id . '_' . $this->article_id . '_' . $this->article_type;
            return $this->redis->hGetAll($this->article_user_like) ?: false;
        }
    
        /**
         * 获取mysql中用户点赞状态
         * @return array|bool|	hinkModel|null
         * @throws 	hinkdbexceptionDataNotFoundException
         * @throws 	hinkdbexceptionDbException
         * @throws 	hinkdbexceptionModelNotFoundException
         * @date 2020-07-17
         * @author 宁佳兵
         */
        private function findDbArticle()
        {
    
            if (empty($this->article_type)) {
                return false;
            }
    
            $data = Db::name($this->praise_table)
                ->where([
                    "c_id" => $this->customer_id,
                    "member_id" => $this->member_id,
                    "article_id" => $this->article_id,
                    "type" => $this->article_type,
                ])
                ->find();
    
            return $data ?: false;
        }
    
        /**
         * 点赞数据写入hash
         * @param array $articleUserData
         * @return bool
         * @date 2020-07-17
         * @author 宁佳兵
         */
        private function addArticleHash($articleUserData = [])
        {
            $this->article_user_like = 'article_user_like_' . $this->customer_id . '_' . $this->member_id . '_' . $this->article_id . '_' . $this->article_type;
    
            if (!$articleUserData || empty($articleUserData)) {
                $this->redis->hSet($this->article_user_like, 'article_id', $this->article_id);  //文章id
                $this->redis->hSet($this->article_user_like, 'member_id', $this->member_id);    //用户id
                $this->redis->hSet($this->article_user_like, 'customer_id', $this->customer_id);    //客户id
                $this->redis->hSet($this->article_user_like, 'article_type', $this->article_type);  //点赞类型
                $this->redis->hSet($this->article_user_like, 'create_time', time());    //点赞时间
            }
            $this->redis->hSet($this->article_user_like, 'update_time', time());    //更新时间
            $this->redis->hSet($this->article_user_like, 'status', $this->status);  //点赞状态
            return true;
        }
    
        /**
         * 当前文章下的点赞用户id
         * @return mixed
         * @date 2020-07-17
         * @author 宁佳兵
         */
        private function addUser()
        {
            $this->article_user_like_set = 'article_user_like_set_' . $this->article_id . '_' . $this->article_type;
            return $this->redis->sAdd($this->article_user_like_set, $this->member_id);
        }
    
        /**
         * 文章点赞数计数
         * @return mixed
         * @date 2020-07-17
         * @author 宁佳兵
         */
        private function articleLikeCount()
        {
            $this->article_counts = 'article_counts_' . $this->article_id . '_' . $this->article_type;
    
            if ($this->status) {
                //点赞数自增
                $counts = $this->redis->incr($this->article_counts);
            } else {
                //点赞数自减
                $counts = $this->redis->decr($this->article_counts);
            }
    
            return $counts;
        }
    
        /**
         * 文章入队列
         * @date 2020-07-17
         * @author 宁佳兵
         */
        private function queueInto()
        {
            return $this->redis->rPush($this->list_article, $this->article_id . '_' . $this->article_type . '_' . $this->customer_id . '_' . $this->member_id);
        }
    
        /**
         * 文章出队列
         * @param $limit
         * @return mixed
         * @date 2020-07-17
         * @author 宁佳兵
         */
        private function queueOut($limit = '-1')
        {
            return $this->redis->Lrange($this->list_article, 0, $limit);
        }
    
        /**
         * 获取redis中 文章的点赞数量
         * @return int|mixed
         * @throws PsrSimpleCacheInvalidArgumentException
         * @date 2020-07-17
         * @author 宁佳兵
         */
        private function findRedisCounts()
        {
            $this->article_counts = 'article_counts_' . $this->article_id . '_' . $this->article_type;
            return $this->redis->get($this->article_counts) ?: 0;
        }
    
        /**
         * 获取mysql文章的点赞数量
         * @return mixed
         * @throws 	hinkdbexceptionDataNotFoundException
         * @throws 	hinkdbexceptionDbException
         * @throws 	hinkdbexceptionModelNotFoundException
         * @date 2020-07-20
         * @author 宁佳兵
         */
        private function findMysqlCounts()
        {
            return Db::name($this->article_type)
                ->where([
                    'id' => $this->article_id,
                ])
                ->find()['fabulous'];
        }
    
        /**
         * 获取redis中该文章下所有用户id
         * @return mixed
         * @date 2020-07-17
         * @author 宁佳兵
         */
        private function findUserByNews()
        {
            $this->post_user_like_set = 'article_user_like_set_' . $this->article_id . '_' . $this->article_type;
            return $this->redis->sMembers($this->post_user_like_set);
        }
    
    
        /**
         * 更新mysql文章点赞数
         * @param $redisNum
         * @return bool|mixed
         * @throws 	hinkdbexceptionDbException
         * @date 2020-07-17
         * @author 宁佳兵
         */
        private function updateDbCount($redisNum)
        {
            //inc 方法不起作用  暂时注释
    //        $result = Db::name($this->article_type)
    //            ->where([
    //                'id' => $this->article_id,
    //            ])
    //            ->inc('fabulous', (int)$redisNum);
    
            $fabulous = Db::name($this->article_type)
                ->where([
                    'id' => $this->article_id,
                ])
                ->find()['fabulous'];
    
            $result = Db::name($this->article_type)
                ->where([
                    'id' => $this->article_id,
                ])
                ->update(['fabulous' => (int)$fabulous + (int)$redisNum]);
    
            return $result ? $this->unsetRedisArticleCounts() : false;
        }
    
        /**
         * 更新mysql点赞表
         * @param array $userSatateData
         * @return bool
         * @throws 	hinkdbexceptionDataNotFoundException
         * @throws 	hinkdbexceptionDbException
         * @throws 	hinkdbexceptionModelNotFoundException
         * @date 2020-07-17
         * @author 宁佳兵
         */
        private function updateDbArticle($userSatateData = [])
        {
            //判断用户原来是否点赞过
            if ($this->findDbArticle() === false) {
                $data = Db::name($this->praise_table)
                    ->insert([
                        'article_id' => $userSatateData['article_id'],
                        'type' => $userSatateData['article_type'],
                        'c_id' => $userSatateData['customer_id'],
                        'member_id' => $userSatateData['member_id'],
                        'modified' => $userSatateData['update_time'],
                        'status' => $userSatateData['status'],  //点赞
                    ]);
            }else{
                $data = Db::name($this->praise_table)
                    ->where([
                        'article_id' => $userSatateData['article_id'],
                        'type' => $userSatateData['article_type'],
                        'c_id' => $userSatateData['customer_id'],
                        'member_id' => $userSatateData['member_id'],
                    ])
                    ->update([
                        'modified' => $userSatateData['update_time'],
                        'status' => $userSatateData['status'],  //取消点赞
                    ]);
            }
    
            return ! empty($data) ? $data : false;
        }
    
    
        /**
         * 删除redis文章点赞数
         * @return mixed
         * @date 2020-07-17
         * @author 宁佳兵
         */
        private function unsetRedisArticleCounts()
        {
            $this->article_counts = 'article_counts_' . $this->article_id . '_' . $this->article_type;
            return $this->redis->del($this->article_counts);
        }
    
        /**
         * 删除该文章下redis中的用户
         * @return mixed
         * @date 2020-07-17
         * @author 宁佳兵
         */
        private function unsetRedisUserCounts()
        {
            $this->article_user_like_set = 'article_user_like_set_' . $this->article_id . '_' . $this->article_type;
            return $this->redis->sRem($this->article_user_like_set, $this->member_id);
        }
    
        /**
         * 删除redis中该文章hash数据
         * @return mixed
         * @date 2020-07-17
         * @author 宁佳兵
         */
        private function unsetRedisArticleHash()
        {
            $this->article_user_like = 'article_user_like_' . $this->customer_id . '_' . $this->member_id . '_' . $this->article_id . '_' . $this->article_type;
            return $this->redis->del($this->article_user_like);
        }
    
    
        /**
         * 记录错误日志
         * @date 2020-07-17
         * @author 宁佳兵
         */
        private function writeLog()
        {
            $file = '../runtime/log/redis.log';
            $content = "操作失败,文章:" . $this->article_id . '---用户:' . $this->member_id . '
    ';
            if (is_file($file)) {
                file_put_contents($file, $content, FILE_APPEND);
            }
        }
    
    }

     可参考https://blog.csdn.net/MyDream229/article/details/107363287/

  • 相关阅读:
    服务器IIS禁止通过IP访问
    如何自定义Kubernetes资源
    敏捷 | 无处不在的敏捷思想应用
    敏捷 | 如何做好服务型Scrum Master?
    敏捷 | 如何填好推进的坑?
    敏捷 | 如何正确推进敏捷?
    敏捷 | 如何正确理解敏捷?
    管理 |《技术管理案例课》学习总结(下)
    管理 |《技术管理案例课》学习总结(上)
    《ArcGIS 从基础到实战》书正式出版
  • 原文地址:https://www.cnblogs.com/ningjiabing/p/13345058.html
Copyright © 2011-2022 走看看