thinkphp redis實現文章點贊功能並同步入mysql


<?php

namespace app\common\controller;

use think\App;
use think\facade\Cache;
use think\facade\Db;


/**
 * redis 點贊/收藏模塊
 * @package app\admin\controller
 * @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 \Psr\SimpleCache\InvalidArgumentException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * @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 \Psr\SimpleCache\InvalidArgumentException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * @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 \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * @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|\think\Model|null
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * @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 \Psr\SimpleCache\InvalidArgumentException
     * @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 \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * @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 \think\db\exception\DbException
     * @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 \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * @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 . '\r\n';
        if (is_file($file)) {
            file_put_contents($file, $content, FILE_APPEND);
        }
    }

}

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM