<?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/
