springboot +redis 实现点赞、浏览、收藏、评论等数量的增减操作

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: springboot +redis 实现点赞、浏览、收藏、评论等数量的增减操作

最近做了一个帖子的收藏、点赞数量的功能,其实之前也做过类似的功能,因为之前一直使用的mysql 总是感觉对于这种频繁需要改变的值,不应该给予Mysql过大的压力,本文章采用的是redis 做了持久化。下面贴出关键代码:DataResponse是项目中使用的结果封装实体类;forumDTO是此功能的参数实体,如果有需要请留言。

常量如下:

privatestaticfinalStringDEFAULT_VALUE="0:0:0:0:0:0";
publicstaticfinalByteBYTE_ZERO=0;
publicstaticfinalByteBYTE_ONE=1;
publicstaticfinalByteBYTE_TWO=2;
publicstaticfinalByteBYTE_THREE=3;
publicstaticfinalByteBYTE_FOUR=4;
publicstaticfinalByteBYTE_FIVE=5;
publicstaticfinalByteBYTE_SIX=6;
@OverridepublicDataResponsekeepNum(ForumDTOforumDTO) {
//将帖子id 设置为 keyStringkey=forumDTO.getPostId().toString();
//get 用户idStringuserId=forumDTO.getUserId();
Stringcount, newCount;
//绑定数据集keyBoundHashOperations<String, Object, Object>post=redisTemplate.boundHashOps("post:");
//获取hKey// count: 0论坛-点赞量  1评论量 2收藏量 3浏览 4评论-点赞量if (null==post.get(key)) {
//无则setpost.put(key, DEFAULT_VALUE);
//再取出来赋值给 countcount=post.get(key).toString();
        } else {
//有直接赋值 countcount=post.get(key).toString();
        }
// operationType 1 浏览 2 帖子点赞 3  收藏  4评论-点赞Stringprefix;
switch (forumDTO.getOperationType()) {
case1:
//记录浏览次数 OPERATIONTYPE 1 : 记录浏览次数newCount=resetValue(count, BYTE_THREE, true);
post.put(key, newCount);
break;
case2:
//记录帖子-点赞prefix="thumbs:post";
switch (forumDTO.getClickType()) {
case0:
/***  OPERATIONTYPE 2: + CLICKTYPE 0 = 给帖子点赞* 0点赞* 从redis中获取数量  帖子d 例如:177488r88t78r78r7*  count: 0论坛-点赞量  1评论量 2收藏量 3浏览 4评论-点赞量* 避免每种数量都去查询redis 直接通过 redis value 记录所有的数量* 获取加 +1 后的值*/if (redisTemplate.opsForSet().isMember(prefix+":"+key, prefix+":"+userId)) {
returnDataResponse.fail("不能重复点赞哦");
                        } else {
redisTemplate.opsForSet().add(prefix+":"+key, prefix+":"+userId);
                        }
newCount=resetValue(count, BYTE_ZERO, true);
//set to redispost.put(key, newCount);
break;
case1:
//OPERATIONTYPE 2: + CLICKTYPE 1 = 取消帖子点赞//1取消帖子点赞if (!redisTemplate.opsForSet().isMember(prefix+":"+key, prefix+":"+userId)) {
//重复处理returnDataResponse.fail("不能重复取消哦");
                        } else {
//删除redisTemplate.opsForSet().remove(prefix+":"+key, prefix+":"+userId);
                        }
newCount=resetValue(count, BYTE_ZERO, false);
post.put(key, newCount);
break;
                }
break;
case3:
prefix="collection:post";
List<MqMessage>sendList=newLinkedList<>();
MqMessagemqMessage=newMqMessage();
switch (forumDTO.getClickType()) {
//OPERATIONTYPE 3 + CLICKTYPE 0 = 记录收藏case0:
//数量+1//根据用户id + 帖子id 查询redis 数据if (redisTemplate.opsForSet().isMember(prefix+":"+key, prefix+":"+userId)) {
//重复处理returnDataResponse.fail("不能重复收藏哦");
                        }
//addredisTemplate.opsForSet().add(prefix+":"+key, prefix+":"+userId);
//set to redisnewCount=resetValue(count, BYTE_TWO, true);
post.put(key, newCount);
mqMessage.setType(newByte("9"));
mqMessage.setSenderId(userId);
mqMessage.setPostId(forumDTO.getPostId());
sendList.add(mqMessage);
this.sendMq.send(sendList);
break;
//OPERATIONTYPE 3 + CLICKTYPE 1 = 取消收藏case1:
//取消收藏//尝试从redis取出当前用户是否已经收藏if (!redisTemplate.opsForSet().isMember(prefix+":"+key, prefix+":"+userId)) {
//重复处理returnDataResponse.fail("不能重复取消哦");
                        }
//删除redisTemplate.opsForSet().remove(prefix+":"+key, prefix+":"+userId);
newCount=resetValue(count, BYTE_TWO, false);
post.put(key, newCount);
mqMessage.setType(newByte("10"));
mqMessage.setSenderId(userId);
mqMessage.setPostId(forumDTO.getPostId());
sendList.add(mqMessage);
this.sendMq.send(sendList);
break;
                }
break;
case4:
//记录评论-点赞// OPERATIONTYPE 4: + CLICKTYPE 0 = 给评论点赞if (null==forumDTO.getCommentId()) {
returnDataResponse.fail("评论id不能为空");
                }
StringcommentNum, ckey=forumDTO.getCommentId().toString();
BoundHashOperations<String, Object, Object>comment=redisTemplate.boundHashOps("post:comment");
if (null==comment.get(ckey)) {
//无则setcomment.put(ckey, "0");
//再取出来赋值给 countcommentNum=comment.get(ckey).toString();
                } else {
//有直接赋值 countcommentNum=comment.get(ckey).toString();
                }
//赞评论prefix="thumbs:comment";
switch (forumDTO.getClickType()) {
case0:
/*** 0点赞* 从redis中获取数量  帖子d 例如:177488r88t78r78r7*  count: 0论坛-点赞量  1评论量 2收藏量 3浏览 4评论-点赞量* 避免每种数量都去查询redis 直接通过 redis value 记录所有的数量* 获取加 + 后的值*/if (redisTemplate.opsForSet().isMember(prefix+":"+ckey, prefix+":"+userId)) {
returnDataResponse.fail("不能重复点赞哦");
                        } else {
redisTemplate.opsForSet().add(prefix+":"+ckey, prefix+":"+userId);
                        }
//set to rediscomment.put(ckey, cResetValue(commentNum, true));
break;
case1:
//1取消评论点赞if (!redisTemplate.opsForSet().isMember(prefix+":"+ckey, prefix+":"+userId)) {
//重复处理returnDataResponse.fail("不能重复取消哦");
                        } else {
//删除redisTemplate.opsForSet().remove(prefix+":"+ckey, prefix+":"+userId);
                        }
newCount=cResetValue(commentNum, false);
comment.put(ckey, newCount);
break;
                }
break;
default:
DataResponse.fail(ResponseEnum.FAILED);
        }
returnDataResponse.success(ResponseEnum.SUCCESS);
    }

resetValue代码:

/*** 功能描述: <br>* 〈点赞数、收藏数等数量重置〉* @param val    数组* @param type   0帖子点赞量  1评论量 2收藏量 3浏览 4评论点赞量* @param isPlus 是否增加数量 true +   false -* @Return: java.lang.String* @Author:王震* @Date: 2020/8/5 10:27* StringUtils包:import org.apache.commons.lang3.StringUtils;* 可以使用jdk的包替代split方法;但jdk的包需要验证正则,效率较低。*/privateStringresetValue(Stringval, intj, booleanisPlus) {
String[] value=StringUtils.split(val, ":");
Longtemp=Long.valueOf(value[j]);
StringBuffersb=newStringBuffer(16);
if (isPlus) {
temp+=1;
        } else {
temp-=1;
        }
value[j] =temp.toString();
for (inti=0, len=value.length; i<len; i++) {
if (i!=len-1) {
sb.append(value[i]).append(":");
            }else {
sb.append(value[i]);
            }
        }
returnsb.toString();
    }
#***下面附上DataResponseDTO代码:***DataResponsepackagecom.ehl.developerplatform.pojo;
importcom.ehl.developerplatform.euem.ResponseEnum;
importlombok.Data;
@DatapublicfinalclassDataResponse<T> {
privatebooleansuccess;
privateTdata;
publicDataResponse<T>setData(Tdata) {
this.data=data;
returnthis;
    }
privateIntegercode;
privateStringmessage;
publicDataResponse<T>setMessage(Stringmessage) {
this.message=message;
returnthis;
    }
publicDataResponse() {
    }
publicDataResponse(booleansuccess, Tdata, Integercode, Stringmessage) {
this.code=code;
this.data=data;
this.message=message;
this.success=success;
    }
publicstatic<T>DataResponse<T>success(Stringmessage) {
returnnewDataResponse<T>(true, null, ResponseEnum.SUCCESS.getStatus(), message);
    }
publicstatic<T>DataResponse<T>success() {
returnnewDataResponse<>(true, null, ResponseEnum.SUCCESS.getStatus(), ResponseEnum.SUCCESS.getMessage());
    }
publicstatic<T>DataResponse<T>success(ResponseEnumresponseEnum) {
returnnewDataResponse<>(true, null, responseEnum.getStatus(), responseEnum.getMessage());
    }
publicstatic<T>DataResponse<T>success(Tdata) {
returnnewDataResponse<>(true, data, ResponseEnum.SUCCESS.getStatus(), ResponseEnum.SUCCESS.getMessage());
    }
publicstatic<T>DataResponse<T>success(ResponseEnumresponseEnum, Tdata) {
returnnewDataResponse<>(true, data, responseEnum.getStatus(), responseEnum.getMessage());
    }
publicstatic<T>DataResponse<T>success(ResponseEnumresponseEnum, Tdata, Object... args) {
Strings=String.format(responseEnum.getMessage(), args);
returnnewDataResponse<>(true, data, responseEnum.getStatus(), s);
    }
publicstatic<T>DataResponse<T>fail() {
returnnewDataResponse<>(false, null, ResponseEnum.FAILED.getStatus(), ResponseEnum.FAILED.getMessage());
    }
publicstatic<T>DataResponse<T>fail(ResponseEnumresponseEnum) {
returnnewDataResponse<>(false, null, responseEnum.getStatus(), responseEnum.getMessage());
    }
publicstatic<T>DataResponse<T>fail(Tdata) {
returnnewDataResponse<>(false, data, ResponseEnum.FAILED.getStatus(), ResponseEnum.FAILED.getMessage());
    }
publicstatic<T>DataResponse<T>fail(Stringmessage) {
returnnewDataResponse<>(false, null, ResponseEnum.FAILED.getStatus(), message);
    }
publicstatic<T>DataResponse<T>fail(Integercode, Stringmessage) {
returnnewDataResponse<>(false, null, code, message);
    }
publicstatic<T>DataResponse<T>fail(ResponseEnumresponseEnum, Tdata) {
returnnewDataResponse<>(false, data, responseEnum.getStatus(), responseEnum.getMessage());
    }
publicstatic<T>DataResponse<T>fail(ResponseEnumresponseEnum, Tdata, Object... args) {
Strings=String.format(responseEnum.getMessage(), args);
returnnewDataResponse<>(false, data, responseEnum.getStatus(), s);
    }
}
DTO:
packagecom.ehl.developerplatform.pojo.dto;
importcom.ehl.developerplatform.anno.EnumValue;
importlombok.Data;
importjavax.validation.constraints.Min;
importjavax.validation.constraints.NotEmpty;
importjavax.validation.constraints.NotNull;
importjavax.validation.constraints.Size;
importjava.io.Serializable;
@DatapublicclassForumDTOimplementsSerializable {
@Min(groups= {ForumDTO.SearchComment.class,SearchMyReply.class,searchMyLikePost.class,SearchMyCreatePost.class,SearchPostByBlogId.class,SearchCommentList.class,SearchPostList.class},value=1, message="每页数据最少一条")
@NotNull(groups= {ForumDTO.SearchComment.class,SearchMyReply.class,searchMyLikePost.class,SearchMyCreatePost.class,SearchPostByBlogId.class,SearchCommentList.class,SearchPostList.class},message="[pageSize]字段不能为空")
privateIntegerpageSize;
@Min(groups= {ForumDTO.SearchComment.class,SearchMyReply.class,searchMyLikePost.class,SearchMyCreatePost.class,SearchCommentList.class,SearchPostByBlogId.class,SearchPostList.class},value=1, message="页码必须大于零")
@NotNull(groups= {ForumDTO.SearchComment.class,SearchMyReply.class,searchMyLikePost.class,SearchMyCreatePost.class,SearchCommentList.class,SearchPostByBlogId.class,SearchPostList.class},message="[pageNum]字段不能为空")
privateIntegerpageNum;
/*** 排序 1 更新日期  2 最近一天 3 最近三天  4 最近三个月  默认 1*/@EnumValue(groups= {ForumDTO.SearchPostList.class}, byteValues= {1, 2, 3, 4},message="更新日期必须为指定值")
privateByteupdateTime;
/*** 帖子名称*/@NotEmpty(groups= {AddTopic.class},message="帖子名称不能为空")
@Size(groups= {AddTopic.class},max=30,min=1,message="帖子名称长度最长不能超过30个字符 最短不能小于1个字符")
privateStringname;
/*** 专题父id*/@NotNull(groups= {Comment.class,DeleteMyComment.class},message="父id不能为空")
privateLongpid;
/*** postId 帖子id*/@NotNull(groups= {SearchCommentList.class,UpdateInit.class,DeleteMyPost.class,KeepNum.class,Comment.class,SearchLatestNum.class,SearchPostById.class,SearchPostDetailById.class},message="贴子id不能为空")
privateLongpostId;
/*** 专区id*/@NotNull(groups= {AddTopic.class,Comment.class,SearchPostByBlogId.class},message="所属专题id不能为空")
privateLongblogId;
/***帖子分类 1 技术问答 2 经验分享 3 大赛公告 4 大赛组队 5 全部 6 精华区  注:大赛组队只有选择大赛专区才能选择*/@NotNull(groups= {AddTopic.class},message="帖子分类不能为空")
@EnumValue(groups= {SearchPostList.class,SearchPostByBlogId.class},byteValues= {1,2,3,4,5,6},message="帖子分类必须为指定值")
privateByteclassFy;
/*** 专区名称*/@NotEmpty(groups= {AddTopic.class},message="所属专题名不能为空")
privateStringtitle;
/*** 评论内容*/@NotEmpty(groups= {Comment.class,AddTopic.class},message="评论内容不能为空")
privateStringcontent;
privateStringmarkText;
/*** 用户id  发帖人id*/@NotEmpty(groups= {AddTopic.class,SearchMyReply.class,KeepNum.class,searchMyLikePost.class,SearchMyCreatePost.class,UpdatePostById.class,DeleteMyComment.class,Comment.class,DeleteMyPost.class,SearchLatestNum.class},message="用户id不能为空")
privateStringuserId;
/*** operationType 1 浏览 2 帖子点赞 3  收藏  4评论-点赞*/@EnumValue(groups= {ForumDTO.KeepNum.class},byteValues= {1, 2, 3, 4, 5, 6},message="[operationType]字段必须为指定值")
@NotNull(groups= {ForumDTO.KeepNum.class},message="[operationType]字段不能为空")
privateByteoperationType;
/*** 评论id*/@NotNull(groups= {DeleteMyComment.class},message="评论id不能为空")
privateLongcommentId;
/*** 0 增加操作  1 取消操作*/@EnumValue(groups= {ForumDTO.KeepNum.class},byteValues= {0, 1})
privateByteclickType;
/*** 模糊查询字段*/privateStringkeyword;
/*** ########################### 评论使用*//***  头像地址*/privateStringphotoPath;
/*** 用户名*/privateStringuserName;
publicinterfaceAddTopic {
    }
publicinterfaceSearchPostDetailById {
    }
publicinterfaceSearchComment {}
publicinterfaceSearchCommentList {
    }
publicinterfaceKeepNum {
    }
publicinterfaceSearchPostList {
    }
publicinterfaceSearchBlogs {
    }
publicinterfaceComment {
    }
publicinterfaceSearchLatestNum {
    }
publicinterfaceSearchAddTopicData {
    }
publicinterfaceDeleteMyPost {
    }
publicinterfaceDeleteMyComment {
    }
publicinterfaceSearchPostById {
    }
publicinterfaceUpdatePostById {
    }
publicinterfaceSearchMyReply {
    }
publicinterfaceSearchMyCreatePost {
    }
publicinterfaceSearchPostByBlogId {
    }
publicinterfacesearchMyLikePost {
    }
publicinterfaceUpdateInit {
    }
}
枚举:packagecom.ehl.developerplatform.euem;
importlombok.Getter;
/*** @author 王震* @date 2020/3/17 21:03* 自定义枚举*/@GetterpublicenumResponseEnum {
/*** 200+*/SUCCESS(200, "操作成功"),
DELETE_SUCCESS(200, "删除成功"),
COLLECTION_SUCCESS(200, "收藏成功"),
CANCEL_SUCCESS(200, "取消成功"),
FAILED(202, "操作失败,请稍后重试"),
CREATED(200, "创建成功"),
UPDATED(200, "修改成功"),
UPDATE_ERROR(202, "修改失败"),
CREATE_ERROR(202, "创建失败"),
INVALID_VERIFY_CODE(202, "验证码错误!"),
VERIFICATION_CODE_EXPIRED(202, "验证码过期!"),
INVALID_USERNAME_PASSWORD(202, "无效的用户名和密码!"),
USER_NOT_EXIST(202, "用户不存在"),
PHONE_CODE_ERROR(202, "手机号验证码错误"),
PHONE_CODE_EXPIRED(202, "验证码过期"),
INVALID_SERVER_ID_SECRET(202, "无效的服务id和密钥!"),
INSERT_OPERATION_FAIL(202, "新增操作失败!"),
UPDATE_OPERATION_FAIL(202, "更新操作失败!"),
DELETE_OPERATION_FAIL(202, "删除操作失败!"),
FILE_UPLOAD_ERROR(202, "文件上传失败!"),
DIRECTORY_WRITER_ERROR(202, "目录写入失败!"),
FILE_WRITER_ERROR(202, "文件写入失败!"),
SEND_MESSAGE_ERROR(202, "短信发送失败!"),
REPEAT_PROCESS(202, "重复处理!"),
REPEAT_PROCESS_USER(202, "重复邀请!"),
SECKILL_ALREADY_JOIN_ERROR(203, "当前参与用户过多,请求稍后重试!"),
TOO_MANY_VISITS(203, "访问太频繁,请稍后重试"),
DATA_NOT_FOUNT(204, "暂无数据"),
FILE_NOT_FOUNT(204, "未查询到文件"),
ALGORITHM_NOT_FOUND(204, "未找到算法信息!"),
DELETE_ERROR_DATA_NOTFOUND(204, "删除失败!请确认是否有此数据"),
/*** 300+*/USER_NOT_LOGIN(300, "用户未登录"),
/*** 400+*/PAGE_NOTNULL(400, "当前页和每页展示条数页码不能为空且页码必须大于等于1"),
PARAM_FORMAT_ERROR(400, "参数格式错误"),
CONTENT_TYPE_ERROR(400, "请检查Content type类型"),
JSON_PARAM_FORMAT_ERROR(400, "JSON参数格式错误"),
JSON_INPUT_ERROR(412, "JSON文件解析失败!"),
SIGN_CHANNEL_NOTNULL(400, "报名渠道不能为空"),
INVALID_FILE_TYPE(400, "无效的文件类型!"),
INVALID_PARAM_ERROR(400, "无效的请求参数!"),
INVALID_PHONE_NUMBER(400, "无效的手机号码"),
LONG_SIZE(400, "长度不合法"),
FILE_TO_LONG(400, "文件大小超出规定"),
INVALID_NOTIFY_PARAM(401, "回调参数有误!"),
INVALID_NOTIFY_SIGN(401, "回调签名有误!"),
APPLICATION_NOT_FOUND(404, "应用不存在!"),
/*** 比赛*/TEAM_IS_NULL(404, "暂无此团队"),
COMPETITION_TEAM_ID_NOTNULL(400, "修改团队时id不能为空"),
COMPETITION_IS_NULL(404, "暂无此比赛"),
/*** 数据集*/CANNOT_UPDATE(401, "公开数据集不能改为私有数据集"),
DATA_SET_NOT_FOUND(404, "暂无此数据集"),
/*** 500+*/DATA_TRANSFER_ERROR(500, "数据转换异常!"),
INVOKING_ERROR(500, "接口调用失败!"),
SQL_EXCEPTION(500, "SQL异常"),
/*** 600+*/UNKNOWN_ERROR(600, "服务器错误"),
REQUEST_METHOD_ERROR(600, "请求方式错误"),
/*** 700+ 702已被用户中心使用,不定义702*/USER_CENTER_ERROR(700, "用户中心异常");
privatefinalintstatus;
privatefinalStringmessage;
ResponseEnum(intstatus, Stringmessage) {
this.status=status;
this.message=message;
    }
}
EnumValue是校验参数的注解,可以注释掉。
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
21天前
|
NoSQL Java Redis
SpringBoot集成Redis解决表单重复提交接口幂等(亲测可用)
SpringBoot集成Redis解决表单重复提交接口幂等(亲测可用)
52 0
|
1月前
|
消息中间件 NoSQL Java
springboot redis 实现消息队列
springboot redis 实现消息队列
36 1
|
26天前
|
NoSQL Java Redis
SpringBoot集成Redis
SpringBoot集成Redis
159 0
|
4天前
|
NoSQL 数据可视化 Java
Springboot整合redis
Springboot整合redis
|
5天前
|
人工智能 前端开发 Java
Java语言开发的AI智慧导诊系统源码springboot+redis 3D互联网智导诊系统源码
智慧导诊解决盲目就诊问题,减轻分诊工作压力。降低挂错号比例,优化就诊流程,有效提高线上线下医疗机构接诊效率。可通过人体画像选择症状部位,了解对应病症信息和推荐就医科室。
30 10
|
14天前
|
NoSQL Java Redis
Springboot整合redis
Springboot整合redis
|
22天前
|
NoSQL Java Redis
SpringBoot集成Redis
SpringBoot集成Redis
42 1
|
26天前
|
存储 NoSQL Java
Redis 数据结构操作入门
Redis 数据结构操作入门
15 0
|
1月前
|
缓存 NoSQL Java
springboot中集成redis,二次封装成工具类
springboot中集成redis,二次封装成工具类
169 0
|
1月前
|
NoSQL Java Redis
springboot实现复杂业务下的更新操作
springboot实现复杂业务下的更新操作
14 0

热门文章

最新文章