【Gin-Web】Bluebell社区项目梳理5:投票功能分析与实现

本文目录

  • 一、投票功能
    • 投票流程
    • 实现代码
    • redis投票

一、投票功能

投票流程

首先我们要明确,就是 谁(哪个用户:userID)哪个帖子(postID) 投了 什么票(赞成票or反对票)。

赞成票越多,热度越高,就会越展示在前面。

在redis中可以用zset存储帖子,那么有两种存储方式。

一是根据帖子的发布时间存储帖子(根据时间戳来,时间越新,时间戳越大,越在前面),或者是按照评分来存储帖子。

在这里插入图片描述

然后还可以设计一个zset,用来存储给某个帖子投票用户。哪个用户投了赞成票就记为+1,投了反对票就记为-1。

在这里插入图片描述

所以总的来设计了一个帖子算法,投一张赞成票就加对应的分数,比如400分。

时间戳+赞成票*400分=评分,评分越高越放前面。

实现代码

首先我们封装了投票数据的结构体。

在这里插入图片描述

然后是controller层。

在这里插入图片描述
然后就是具体投票的业务逻辑实现部分Logic.VoteForPost

strconv.Itoa 是一个函数,用于将整数转换为字符串。它要求输入必须是 int 类型,因此这里使用了 int(userId) 将 userId 转换为 int。将用户 ID 转换为字符串格式,因为 Redis 的键通常是以字符串形式存储的。

在这里插入图片描述

redis投票

来看看投票的情况:

v=1时,有两种情况1.之前没投过票,现在要投赞成票		 --> 更新分数和投票记录	差值的绝对值:1	+4322.之前投过反对票,现在要改为赞成票	 --> 更新分数和投票记录	差值的绝对值:2	+432*2v=0时,有两种情况1.之前投过反对票,现在要取消			 --> 更新分数和投票记录	差值的绝对值:1	+4322.之前投过赞成票,现在要取消			 --> 更新分数和投票记录	差值的绝对值:1	-432v=-1时,有两种情况1.之前没投过票,现在要投反对票		 --> 更新分数和投票记录	差值的绝对值:1	-4322.之前投过赞成票,现在要改为反对票	 --> 更新分数和投票记录	差值的绝对值:2	-432*2

除此之外,我们还有对投票的限制:

每个帖子自发起之日起,一个星期之内允许用户投票,超过一个星期就不允许投票了。同时到期之后将redis中保存的赞成票数及反对票数存储到mysql表中。到期之后删除 KeyPostVotedZSetPrefix

这里的 KeyPostVotedZSetPrefix 就是记录用户及投票类型。

在这里插入图片描述

我们来看看redis.go的相关代码,其中client就是redis客户端。

在这里插入图片描述
service层中,设置了相关的逻辑代码,来看看处理流程。

首先我们需要去redis中获取帖子的发布时间,从client中拿即可。并检查当前时间与帖子发布时间的差值是否超过一周(OneWeekInSeconds)。如果超过一周,返回错误 ErrorVoteTimeExpire,表示投票已过期。

在这里插入图片描述

我们在redis的目录路径下,封装了error错误,声明了几个自定义错误变量。这些错误变量用于在 Redis 相关的操作中表示特定的错误情况。errors.New 是一个常用的error函数,用于创建一个新的错误对象。

通过定义全局的错误变量,为 Redis 相关的操作提供了一致的错误处理机制。使用 errors.New 创建的错误对象可以在整个包中复用,避免了重复创建相同的错误信息,提高了代码的可维护性和一致性。

比如当投票时间已经过期了,我们就需要返回投票过期的错误。
在这里插入图片描述


postTime := client.ZScore(KeyPostTimeZSet, postID).Val()

ZScore 方法查询帖子 ID 对应的分数(发布时间),并通过 Val() 方法获取该分数的实际值。返回的 postTime 是一个浮点数,表示帖子的发布时间(通常是 Unix 时间戳)。

ZScore 是 Redis 客户端提供的一个方法,用于从有序集合(ZSet)中获取某个成员的分数。它的签名通常是:

func (c *Client) ZScore(key string, member string) *FloatCmd

key:有序集合的键名(唯一标识:键名是 Redis 数据库中唯一标识有序集合的字符串。通过键名,你可以访问和操作特定的有序集合。)

member:有序集合中的成员(在这里是帖子的 ID)。

在刚刚我们有提到,const KeyPostTimeZSet = "bluebell:post:time",这是一个常量,定义了存储帖子发布时间的有序集合的键名。


然后就是更新帖子分数,注意更新帖子分数+记录用户为该帖子投票的数据 是要放在一个redis事务中完成的。

在 Redis 中,Pipeline(管道) 是一种用于将多个命令发送到服务器的技术,而 事务(Transaction) 是一种将多个命令打包并一次性执行的机制。在 Redis 的上下文中,Pipeline 和事务经常结合使用,以提高性能和确保操作的原子性

事务 是一种将多个命令打包,并一次性、顺序地执行的机制。Redis 的事务通过 MULTI、EXEC、DISCARD 和 WATCH 命令实现。事务的主要特点包括:事务中的所有命令要么全部执行,要么全部不执行。这确保了操作的原子性,避免了部分执行导致的数据不一致问题。事务中的命令会按照顺序执行,不会被其他客户端的命令打断。

在这里插入图片描述

KeyPostVotedZSetPrefix    = "bluebell:post:voted:" // zset;记录用户及投票类型;参数是post_id

刚刚我们定义了redis的常量,所以我们需要进行下面的操作:

	key := KeyPostVotedZSetPrefix + postIDov := client.ZScore(key, userID).Val()

也就是从 redis中获取 某个帖子 的 用户投票类型,根据用户ID来获取Val值。

也就是下面这个图所示:
在这里插入图片描述

	ov := client.ZScore(key, userID).Val()// 更新:如果这一次投票的值和之前保存的值一致,就提示不允许重复投票if v == ov {return ErrVoteRepested}var op float64if v > ov {op = 1} else {op = -1}diffAbs := math.Abs(ov - v)                                                        // 计算两次投票的差值pipeline := client.TxPipeline()                                                    // 事务操作_, err = pipeline.ZIncrBy(KeyPostScoreZSet, VoteScore*diffAbs*op, postID).Result() // 更新分数

通过Redis事务来更新分数。

TxPipeline() 是 Redis 客户端提供的一个方法,用于创建一个事务性 Pipeline。这个 Pipeline 允许将多个命令打包在一起,并作为一个事务发送到 Redis 服务器。

ZIncrBy 是 Redis 的一个命令,用于在有序集合(ZSet)中增加某个成员的分数。

	if v == 0 {_, err = client.ZRem(key, userID).Result()} else {pipeline.ZAdd(key, redis.Z{ // 记录已投票Score:  v, // 赞成票还是反对票Member: userID,})}_, err = pipeline.Exec()  //执行pipeline中的所有命令

如果v=0,那么从有序集合中移除指定的成员。

client.ZRem:Redis 客户端提供的方法,用于从有序集合中移除指定的成员。

pipeline.ZAdd:Redis 客户端提供的方法,用于将一个成员及其分数添加到有序集合中。这里使用了事务性 Pipeline,确保操作的原子性。

redis.Z:一个结构体,包含成员(Member)和分数(Score)。
Score:用户的投票值(v),表示赞成票(1)或反对票(-1)。
Member:用户的唯一标识符(userID)。


在 Redis 中,有序集合(ZSet)相关的命令都以 Z 开头,例如 ZADD、ZSCORE、ZINCRBY、ZREM 等。

Redis 的有序集合命令都以 Z 开头,例如:
ZADD:将一个或多个成员及其分数添加到有序集合中。
ZSCORE:获取有序集合中成员的分数。
ZINCRBY:增加有序集合中成员的分数。
ZREM:从有序集合中移除成员。


可以看到,再投出一票之后,在原先的redis基础上加了432分。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/896163.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

XUnity.AutoTranslator-deepseek——调用腾讯的DeepSeek V3 API,实现Unity游戏中日文文本的自动翻译

XUnity.AutoTranslator-deepseek 本项目通过调用腾讯的DeepSeek V3 API,实现Unity游戏中日文文本的自动翻译。 准备工作 1. 获取API密钥 访问腾讯云API控制台申请DeepSeek的API密钥(限时免费)。也可以使用其他平台提供的DeepSeek API。 …

Python爬虫-批量爬取股票数据猫各股票代码

前言 本文是该专栏的第47篇,后面会持续分享python爬虫干货知识,记得关注。 本文笔者以股票数据猫为例子,基于Python爬虫,批量获取各股票代码数据。 具体实现思路和详细逻辑,笔者将在正文结合完整代码进行详细介绍。废话不多说,下面跟着笔者直接往下看正文详细内容。(附…

《Keras 3 :使用 Vision Transformers 进行物体检测》:此文为AI自动翻译

《Keras 3 :使用 Vision Transformers 进行物体检测》 作者:Karan V. Dave 创建日期:2022 年 3 月 27 日最后修改时间:2023 年 11 月 20 日描述:使用 Vision Transformer 进行对象检测的简单 Keras 实现。 (i) 此示例使用 Keras 3 在 Colab 中查看 GitHub 源 介绍 A…

vue-treeselect显示unknown的问题及解决

问题 解决办法 去node-modules包里面找到这个组件的源码,在它dist文件里面找到这个文件,然后搜索unknown,把它删掉就可以解决了。

深入剖析抽象工厂模式:设计模式中的架构利器

深入剖析抽象工厂模式:设计模式中的架构利器 在软件开发领域,设计模式是解决常见问题的通用方案,而抽象工厂模式作为创建型设计模式的重要一员,在构建复杂软件系统时发挥着关键作用。它为创建一系列相关或相互依赖的对象提供了一…

python获取网页内容 靠谱的做法

获取网页内容 response requests.get(url, verifyFalse) 通过这种方式下载网址不太靠谱, 容易出 ssl错误 requests.exceptions.SSLError: HTTPSConnectionPool(hostagri.hainan.gov.cn, port443): Max retries exceeded with url: /hnsnyt/xxgk/gfxwj/index_1.html (Caused by…

MFC中CString的Format、与XML中的XML_SETTEXT格式化注意

1、在MFC中导入 "msxml6.dll",并使用其中的XML_SETTEXT函数,此调用在进行格式化的时候,调用的还是CString.Format()函数! 2、用double类型的数据,格式化整形数%d之前,必须将double强转为int&…

Linux-C-函数栈-SP寄存器

sp(Stack Pointer,栈指针)是计算机体系结构中一个非常重要的寄存器,下面将详细介绍其作用和原理。 作用 1. 管理栈内存 栈是一种后进先出(LIFO,Last In First Out)的数据结构,在程…

从零开始用react + tailwindcs + express + mongodb实现一个聊天程序(一)

项目包含5个模块 1.首页 (聊天主页) 2.注册 3.登录 4.个人资料 5.设置主题 一、配置开发环境 建立项目文件夹 mkdir chat-project cd chat-project mkdir server && mkdir webcd server npm init cd web npm create vitelatest 创建前端项目时我们选择javascrip…

深入理解 QObject的作用

QObject 作为 Qt 库中所有对象的基类,其地位无可替代。几乎 Qt 框架内的每一个类,无论是负责构建用户界面的 QWidget,还是专注于数据处理与呈现的 QAbstractItemModel,均直接或间接继承自 QObject。这种继承体系赋予 Qt 类库高度的…

22爬虫:使用Drission Page的两个案例

案例一:使用DrissionPage抓取BOSS上的招聘信息 使用requests获取BOSS网站上的内容是非常困难的,但是通过网页自动化工具DrissionPage或者是Playwright或者是Seleenium是非常容易的,接下来我们就给出使用DrissionPage爬取BOSS网站python招聘的…

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_atoi 函数

ngx_atoi 声明在 src/core/ngx_string.h ngx_int_t ngx_atoi(u_char *line, size_t n); 定义在 src/core/ngx_string.c ngx_int_t ngx_atoi(u_char *line, size_t n) {ngx_int_t value, cutoff, cutlim;if (n 0) {return NGX_ERROR;}cutoff NGX_MAX_INT_T_VALUE / 10;cutlim…

具有整合各亚专科医学领域知识能力的AI智能体开发纲要(2025版)

整合各亚专科医学领域知识能力的AI代理的开发与研究 一、引言 1.1 研究背景 在科技飞速发展的当下,人工智能(AI)已成为推动各行业变革的关键力量,医疗领域也不例外。近年来,AI 在医疗行业的应用取得了显著进展,从医学影像诊断到疾病预测,从药物研发到个性化医疗,AI 技…

如何设计app测试用例

功能测试 测试方法:等价类划分法、边界值法、场景法、因果图法。优先级设定:核心业务功能设为高优先级。需求覆盖 正向场景、反向场景、关联接口串场景 与后端开发确认测试用例是否全面覆盖后端逻辑。和产品确认用例是否覆盖本次需求,以及是否…

YOLO11 【四】 【DNF制作自己的数据集,切割视频以及labelimg 闪退问题】

一、问题labelimg 闪退 一点w打标 labelimg就闪退 **原因 : python 版本太高 ** 解决办法:单独创建一个虚拟环境用于打标 conda create -n labelimg python3.9 二、使用python脚本切割视频 # -*- coding: utf-8 -*- import cv2 import osdef video_…

[MDM 2024]Spatial-Temporal Large Language Model for Traffic Prediction

论文网址:[2401.10134] Spatial-Temporal Large Language Model for Traffic Prediction 论文代码:GitHub - ChenxiLiu-HNU/ST-LLM: Official implementation of the paper "Spatial-Temporal Large Language Model for Traffic Prediction" …

k2路由器登录校园网

教程1刷入Breed,并手动刷入Padavan固件:斐讯K1、K2、K2P 刷机、刷入Breed 辅助工具 | tb (tbvv.net) Padavan下载网址: 我用的是: Padavan 登录的网址是 192.168.123.1 Padavan配置教程: 先用网线连上校园网&#…

多源 BFS 算法详解:从原理到实现,高效解决多源最短路问题

多源 BFS 是一种解决 边权为 1 的多源最短路问题 的高效算法。其核心思想是将所有源点视为一个“超级源点”,通过一次 BFS 遍历即可计算所有节点到最近源点的最短距离。以下从原理、实现和代码示例三个方面深入讲解: 目录 一、原理分析 1. 单源 BFS vs…

【蓝桥杯集训·每日一题2025】 AcWing 6123. 哞叫时间 python

6123. 哞叫时间 Week 1 2月18日 农夫约翰正在试图向埃尔茜描述他最喜欢的 USACO 竞赛,但她很难理解为什么他这么喜欢它。 他说「竞赛中我最喜欢的部分是贝茜说 『现在是哞哞时间』并在整个竞赛中一直哞哞叫」。 埃尔茜仍然不理解,所以农夫约翰将竞赛以…

C++,设计模式,【工厂方法模式】

文章目录 如何用汽车生产线理解工厂方法模式?一、传统生产方式的困境二、工厂方法模式解决方案三、模式应用场景四、模式优势分析五、现实应用启示✅C++,设计模式,【目录篇】 如何用汽车生产线理解工厂方法模式? 某个早晨,某车企CEO看着会议室里堆积如面的新车订单皱起眉…