点赞操作实现

问题:

我准备实现一个点赞功能,如何实现点赞的增加和取消?

思考:

1.首先增加和取消所需要的限制条件是{一个用户一个文章/评论 进行点赞操作}

2.我需要判断当前用户是否已经进行了点赞操作

3.我并不希望一个用户永远只能给一篇文章点赞一次。所以我可以给用户点赞记录做一个有效期:如一星期后,该用户还可以给该文章点赞

思路:

1.首先我并不想将点赞操作放入文章,评论中各写一份逻辑。所以我将点赞操作独自抽离出来。

type LikeService struct {}

2.我们需要获取用户ID 文章/评论ID 判断是文章还是评论

//点赞请求体
type AddLike struct {
	Id				uint					`json:"id"`
	UserId			uint					`json:"userId"`
	IsArt			bool					`json:"isArt"` //判断是文章还是评论,方便存储到缓存中,增加不同前缀
}

3.我们使用redis缓存记录

对于点赞数:我们直接设置两个hash集合来区分文章和评论(ArtLikeNum,ComLikeNum)所以直接存储id就行。
对于点赞用户:我们本身就会创建很多set集,所以无法用上面hash的结构去存储和区分。所以我们使用key:id + 前缀art_  com_来区分文章和评论

  • 每个文章的点赞数(会持久化)     
    • 使用hash存储 key:"art" or "com"(对应类型前缀) + id,val:点赞数
  • 该文章点赞用户的ID集合(会过期)
    • 使用set存储 key:"art" or "com"(对应类型前缀) + id, val : 用户Id

贴上点赞逻辑

package system

import (
	"GOGOGO/global"
	"GOGOGO/model/system"
	systemReq "GOGOGO/model/system/request"
	"bytes"
	"context"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"strconv"
	"time"
)


type LikeService struct {
	/**
	点赞的逻辑是:使用set或hash 存储文章/评论 的点赞数、和点赞用户id集合
	点赞数:hashkey:ArtLikeNum ,  key:帖子id,val:点赞数   hash
	点赞用户id:key:art前缀+帖子id,val:用户id		set
		对于点赞数:我们直接设置两个hash集合来区分文章和评论(ArtLikeNum,ComLikeNum)所以直接存储id就行。
		点赞用户我们本身就会创建很多set集,所以无法用上面的结构去存储和区分。我们使用key:id + 前缀art_  com_来区分文章和评论
	 */
}
var LikeServiceApp = new(LikeService)

//将点赞相关的操作分离出来



//点赞事件
func (*LikeService)LikeEvent (artReq systemReq.AddLike)(err error) {
	//不想重复传参,思考:直接在LikeService添加artReq变量。但会不会有数据问题。待优化
	//缓存中查询
	ok := LikeServiceApp.isLike(artReq)
	if ok{	//已经点赞,取消点赞
		err := LikeServiceApp.cancelLike(artReq)
		if err != nil {
			global.G_LOG.Error("点赞取消失败",zap.Error(err))
			return err
		}
	}else { //点赞
		err := LikeServiceApp.addLike(artReq)
		if err != nil {
			global.G_LOG.Error("点赞失败",zap.Error(err))
			return err
		}
	}
	return nil
}

//获取点赞数据列表
func (*LikeService)GetLikeList(setKey string)(reslist []string,err error){
	//setKey 是 存储用户列表的set集合
	reslist,err = global.G_REDIS.SMembers(context.Background(),setKey).Result()
	return reslist,err
}

//点赞
func (*LikeService)addLike(artReq systemReq.AddLike) error {
	LikeServiceApp.likeSynch(artReq,1)	//点赞数同步
	setKey := LikeServiceApp.artorcom(artReq)	//记录点赞用户的当前文章set

	//在缓存中添加该用户点赞信息
	return global.G_REDIS.SAdd(context.Background(), setKey, artReq.UserId).Err()
}

//取消点赞
func (*LikeService)cancelLike(artReq systemReq.AddLike)error {
	LikeServiceApp.likeSynch(artReq,-1)	//点赞数同步
	setKey := LikeServiceApp.artorcom(artReq)	//记录点赞用户的当前文章set

	//在缓存中删除该用户点赞信息
	return global.G_REDIS.SRem(context.Background(), setKey, artReq.UserId).Err()
}

//点赞信息同步数据库并获取点赞数 (点赞数操作)
func (*LikeService)likeSynch(artReq systemReq.AddLike,incr int64)string {
	//防止重复点赞
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()
	key := strconv.Itoa(int(artReq.Id))			//hashname的key,记录点赞数
	var mod interface{}							//存储类型
	var liketype string
	if artReq.IsArt {	//判断是文章还是评论
		mod = &system.SysArticle{}
		liketype = global.RedisC.ArtLikeNum
	}else {
		mod = &system.SysComment{}
		liketype = global.RedisC.ComLikeNum
	}
	//读取点赞数信息,若没有,则数据库读取,并添加进redis
	ok := global.G_REDIS.HExists(ctx,liketype, key).Val()
	if ok{		//在缓存中,则直接更新缓存中点赞数,并同步
		global.G_REDIS.HIncrBy(ctx,liketype, key, incr)
		err := global.G_DB.Model(mod).Where("id = ?", artReq.Id).Update("like_num", gorm.Expr("like_num + ?", incr)).Error
		if err != nil {
			global.G_LOG.Error("同步点赞数据失败!", zap.Error(err))
		}
		return global.G_REDIS.HGet(ctx,liketype,key).Val()
	}else {
		var likeNum int64
		//缓存中没有数据,从数据库中获取该 文章、评论点赞数,放入缓存中
		err := global.G_DB.Model(mod).Select("like_num").Where("id = ?", artReq.Id).Find(likeNum).Error
		if err != nil {
			global.G_LOG.Error("获取点赞数据失败!", zap.Error(err))
		}
		//更新缓存
		global.G_REDIS.HIncrBy(ctx,liketype,key,incr + likeNum)
		//再同步数据库
		err = global.G_DB.Model(mod).Where("id = ?", artReq.Id).Update("like_num", gorm.Expr("like_num + ?", incr)).Error
		if err != nil {
			global.G_LOG.Error("同步点赞数据失败!", zap.Error(err))
		}
		return strconv.FormatInt(likeNum, 10)
	}

}

//获取点赞数
func (*LikeService)GetlikeNum(artReq systemReq.AddLike)string  {
	return LikeServiceApp.likeSynch(artReq,0)
}

//测试用户是否已经点过赞
func (*LikeService)isLike(artReq systemReq.AddLike)bool {
	//若集合本身不存在,返回为0.所以不需要判断是否存在
	return global.G_REDIS.SIsMember(context.Background(),LikeServiceApp.artorcom(artReq),artReq.UserId).Val()
}

//判断当前文章、评论缓存是否存在
//func (*LikeService)IsExit(artReq systemReq.AddLike)bool {
//	global.G_REDIS.Keys(context.Background(),artorcom(artReq))
//}

//返回前缀字符串
func (*LikeService)artorcom(artReq systemReq.AddLike)string  {
	istype := artReq.IsArt
	var bt bytes.Buffer
	if istype{	//是文章,拼接字符串,下面写法是效率优化问题
		bt.WriteString("art_")
		bt.WriteString(strconv.Itoa(int(artReq.Id)))
		return bt.String()
	}else {
		bt.WriteString("com_")
		bt.WriteString(strconv.Itoa(int(artReq.Id)))
		return bt.String()
	}
}


  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值