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/
thinkphp redis实现文章点赞功能并同步入mysql的更多相关文章
- 文章点赞功能(Ajax)
一.文章点赞样式构建 1.将base.html的css样式改为外部引入 将base.html的内嵌样式删除,改为使用 HTML 头部的 <head> 标签对中使用<link>标 ...
- Redis实现文章投票功能
Redis的具体操作这里就不说了,说一下需求和设计思路. 需求:自己实现一个文章投票的功能1.能够按照时间分页倒叙查看文章信息2.能够给文章投票,一个用户给一篇文章只能投票一次3.需要记录分值.每次投 ...
- 点赞功能与redis
转:https://edu.aliyun.com/a/20538 摘要: 前言点赞其实是一个很有意思的功能.基本的设计思路有大致两种, 一种自然是用mysql等数据库直接落地存储, 另外一种就是利用点 ...
- redis系列:通过文章点赞排名案例学习sortedset命令
前言 这一篇文章将讲述Redis中的sortedset类型命令,同样也是通过demo来讲述,其他部分这里就不在赘述了. 项目Github地址:https://github.com/rainbowda/ ...
- thinkphp+redis实现秒杀功能
好久没来整理文章了,闲了没事写篇文章记录下php+redis实现商城秒杀功能. 1,安装redis,根据自己的php版本安装对应的redis扩展(此步骤简单的描述一下) 1.1,安装 php_igbi ...
- 基于redis实现的点赞功能设计思路详解
点赞其实是一个很有意思的功能.基本的设计思路有大致两种, 一种自然是用mysql等 数据库直接落地存储, 另外一种就是利用点赞的业务特征来扔到redis(或memcache)中, 然后离线刷回mysq ...
- JavaWeb中点赞功能的实现及完整实例
实现原理1.功能描述:一个用户对同一文章只能点赞一次,第二次就是取消赞2.建立一个点赞表great,字段有文章ID(aid),点赞用户ID(uid)3.当有用户进行点赞行为时,使用aid和uid搜索点 ...
- 基于SpringBoot如何实现一个点赞功能?
基于SpringBoot如何实现一个点赞功能? 解析: 基于 SpringCloud, 用户发起点赞.取消点赞后先存入 Redis 中,再每隔两小时从 Redis 读取点赞数据写入数据库中做持久化存储 ...
- 利用redis分布式锁的功能来实现定时器的分布式
文章来源于我的 iteye blog http://ak478288.iteye.com/blog/1898190 以前为部门内部开发过一个定时器程序,这个定时器很简单,就是配置quartz,来实现定 ...
随机推荐
- js监测页面是否切换到后台
最近做个弹幕,用的是第三方的插件,在浏览器页面切换到后台,返回后发现数据有堆叠卡死的情况,如何解决这个问题?网上参考了些demo,大致可以实现 1.document.hidden( Boolean值, ...
- 总括订单Blanket order
总括订单Blanket order是客户向其供应方发出的采购订单,但其中包含一段时间内的多个交货日期,通常使用谈判时的预定价格.大多数情况下,它用于对消耗性商品有经常性需求的情况.总括订单通常用于客户 ...
- 题解 P5401 [CTS2019]珍珠
蒟蒻语 这题太玄学了,蒟蒻写篇题解来让之后复习 = = 蒟蒻解 假设第 \(i\) 个颜色有 \(cnt_i\) 个珍珠. \(\sum\limits_{i=1}^{n} \left\lfloor\f ...
- Java并发编程的艺术(十二)——并发容器和框架
ConcurrentHashMap 为什么需要ConcurrentHashMap HashMap线程不安全,因为HashMap的Entry是以链表的形式存储的,如果多线程操作可能会形成环,那样就会死循 ...
- 模块urllib requests json xml configparser 学习笔记
发起http请求 获取返回值 返回值是字符串 第三方模块安装 pip install requests 返回值格式 xml html jaon json 功能 loads 字符串>&g ...
- C++ 虚函数表与多态 —— 关键字 final 的用法
final 字面上最终.最后.不可改变的意思,final 这个关键字在 Jave PHP C++中都有用到,其作用也基本一致. C++中的 final 是C++11新增,他可以用来修饰类,让类无法被继 ...
- adb devices查看不到Android模拟器的设备
1.先开启模拟器,后执行adb devices命令: List of devices attached 2.找到SDK的安装目录下platform-tools文件夹下adb.exe文件,将其复制到桌面 ...
- Unity2D 人物移动切换人物图片
勾选Constraints_freeze Rotation_z轴锁定,防止碰撞偏移. public float moveSpeed = 3f;//定义移动速度 priv ...
- jmeter的一些知识目录
1.JDK安装及环境变量配置2.Jmeter安装及环境变量配置3.如何启动 jmeter 4.下载并安装mysql驱动5.创建JDBC连接池及配置6 .新建线程组及参数配置7.http默认请求及参数配 ...
- matplotlib的学习2-基本用法
import matplotlib.pyplot as plt import numpy as np x = np.linspace(-1, 1, 50)#范围-1 到 1,个数是50 y = 2*x ...