当前位置: 首页 > news >正文

【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分。

在这里插入图片描述

相关文章:

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

本文目录 一、投票功能投票流程实现代码redis投票 一、投票功能 投票流程 首先我们要明确,就是 谁(哪个用户:userID) 给 哪个帖子(postID) 投了 什么票(赞成票or反对票)。 赞成票…...

多人协同创作gitea

多人协同创作gitea 在多台设备上协同使用Gitea,主要是通过网络访问Gitea服务器上的仓库来进行代码管理和协作。以下是一些关键步骤和建议,帮助你在多台设备上高效地使用Gitea进行协作: 1. 确保Gitea服务可访问 首先,你需要确保…...

Java 大视界 -- Java 大数据未来十年的技术蓝图与发展愿景(95)

💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...

idea连接gitee完整教程

文章目录 idea连接gitee(使用idea远程兼容gitee) 使用idea远程兼容gitee并反向创建仓库和分支...

问卷数据分析|SPSS实操之相关分析

皮尔逊还是斯皮尔曼的选取主要看数据的分布 当数据满足正态分布且具有线性关系时,用皮尔逊相关系数 当有一个不满住时,用斯皮尔曼相关系数 1. 选择分析--相关--双变量 2. 将Z1-Y2加入到变量中,选择皮尔逊 3. 此处为结果,可看我案…...

汽配出库软件助力企业数字化转型,佳易王汽配出入库管理系统操作教程

一、概述 本实例以佳易王汽配出入库管理系统V17.1版本为例说明,其他版本可参考本实例。试用版软件资源可到文章最后了解,下载的文件为压缩包文件,请使用免费版的解压工具解压即可试用。 软件特点: 1、软件为绿色免安装版&#…...

二叉树(中等题)

1、先序,中序遍历确定二叉树 105 方法一、 前提 ① 必须不能有重复元素② 只有先序+中序和后序+中序才能实现唯一树 思考要点: 不要想着用for循环,递归一定更好解决输入是vector,递归就得考虑传入索…...

Versal - 基础6(Linux 开发 AIE-ML + 自动化脚本解析)

目录 1. 简介 2. 步骤解析 2.1 概览 2.1.1 步骤依赖关系 2.1.2 总目录结构 2.2 Vitis XPFM 2.2.1 Dir 2.2.2 Makefile 2.2.3 vitis_pfm.py 2.3 Kernels 2.3.1 Dir 2.3.2 Makefile 2.3.3 config 文件 2.4 AIE_app 2.4.1 Dir 2.4.2 Makefile 2.4.3 aie 要点 2.…...

什么是矩阵账号?如何高效运营tiktok矩阵账号

‍‌​​‌‌​‌​‍‌​​​‌‌​​‍‌​​​‌​‌​‍‌​​‌​​‌​‍‌​‌‌​‌‌‌‍‌​‌​‌​​​‍‌​​‌​‌‌​‍‌​​​​‌‌​‍‌​‌​​‌‌‌‍‌​​‌‌​‌​‍‌​‌​​‌‌‌‍‌​‌‌‌​​‌‍‌‌​​‌‌‌​‍‌‌​​‌‌​​‍‌…...

tortoiseSVN 如何克隆项目到本地

导入项目成功,如下图:...

趣解PostGet请求的原理、应用场景及区别

趣解Post&Get请求的原理、应用场景及区别 POST 和 GET 的「身份之谜」:快递员与侦探的终极对决 一、角色设定:快递员(POST) vs 侦探(GET) GET(侦探): 任务&#xff…...

在PHP Web开发中,实现异步处理有几种常见方式的优缺点,以及最佳实践推荐方法

1. 消息队列 使用消息队列(如RabbitMQ、Beanstalkd、Redis)将任务放入队列,由后台进程异步处理。 优点: 任务持久化,系统崩溃后任务不丢失。 支持分布式处理,扩展性强。 实现步骤: 安装消息…...

深入解析过滤器模式:数据筛选与处理的高效工具

过滤器模式:数据筛选与处理的高效工具 在软件开发的复杂领域中,数据的筛选与处理是常见的任务。过滤器模式作为一种实用的设计模式,为解决这类问题提供了有效的解决方案。它允许开发者根据不同的标准对一组对象进行过滤操作,从而…...

DeepSeek R1/V3满血版——在线体验与API调用

前言:在人工智能的大模型发展进程中,每一次新模型的亮相都宛如一颗投入湖面的石子,激起层层波澜。如今,DeepSeek R1/V3 满血版强势登场,为大模型应用领域带来了全新的活力与变革。 本文不但介绍在线体验 DeepSeek R1/…...

排序链表--字节跳动

少年的书桌上没有虚度的光阴 题目描述 请你对链表进行排序 思路分析 核心思想:归并排序 有三个部分 链表排序实现 1. merge 函数 21.见 合并两个有序链表, 首先创建一个虚拟头节点 newhead,并使用指针 tail 来构建合并后的链表。 通过…...

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

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

打开Firefox自动打开hao360.hjttif.com标签解决方案

现象 打开Firefox自动打开hao360.hjttif.com标签,同时用户自己设置的主页也会在一个新标签打开。点击hjttif这个标签,就会跳转到hao.360.com 打开Edge不会出现上述现象。搜遍全网都找不到解决方法。博客园上有一篇文章2025-02-14.防流氓软件篡改主页提到…...

【爬虫基础】第一部分 网络通讯-编程 P3/3

上节内容回顾:【爬虫基础】第一部分 网络通讯 P1/3-CSDN博客 【爬虫基础】第一部分 网络通讯-Socket套接字 P2/3-CSDN博客 相关文档,希望互相学习,共同进步 风123456789~-CSDN博客 前言 1.知识点碎片化:每个网站实现…...

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…...

PG:ERROR: cannot freeze committed xmax

目录 原因**问题原因****PostgreSQL 底层逻辑** 解决方案1**问题分析****排查步骤****1. 检查长时间运行的事务****2. 检查未提交的事务****3. 检查 autovacuum 配置****4. 检查事务 ID 使用情况****5. 检查表的 relfrozenxid** **解决方法****1. 手动运行 VACUUM FREEZE****2.…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

Java数组Arrays操作全攻略

Arrays类的概述 Java中的Arrays类位于java.util包中&#xff0c;提供了一系列静态方法用于操作数组&#xff08;如排序、搜索、填充、比较等&#xff09;。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序&#xff08;sort&#xff09; 对数组进行升序…...

归并排序:分治思想的高效排序

目录 基本原理 流程图解 实现方法 递归实现 非递归实现 演示过程 时间复杂度 基本原理 归并排序(Merge Sort)是一种基于分治思想的排序算法&#xff0c;由约翰冯诺伊曼在1945年提出。其核心思想包括&#xff1a; 分割(Divide)&#xff1a;将待排序数组递归地分成两个子…...

Appium下载安装配置保姆教程(图文详解)

目录 一、Appium软件介绍 1.特点 2.工作原理 3.应用场景 二、环境准备 安装 Node.js 安装 Appium 安装 JDK 安装 Android SDK 安装Python及依赖包 三、安装教程 1.Node.js安装 1.1.下载Node 1.2.安装程序 1.3.配置npm仓储和缓存 1.4. 配置环境 1.5.测试Node.j…...