微博不能关注人了是怎么回事(微博不能关注怎么回事20566)
微博不能关注人了是怎么回事(微博不能关注怎么回事20566)
需求分析
好友功能是目前社交场景的必备功能之一,一般好友相关的功能包含有:关注/取关、我(他)的关注、我(他)的粉丝、共同关注、我关注的人也关注他等这样一些功能。

类似于这样的功能我们如果采用数据库做的话只是单纯得到用户的一些粉丝或者关注列表的话是很简单也很容易实现, 但是如果我想要查出两个甚至多个用户共同关注了哪些人或者想要查询两个或者多个用户的共同粉丝的话就会很麻烦,效率也不会很高。
但是如果你用redis去做的话就会相当的简单而且效率很高。原因是redis自己本身带有专门针对于这种集合的交集、并集、差集的一些操作。

设计思路
总体思路我们采用MySQL + Redis的方式结合完成。MySQL主要是保存落地数据,而利用Redis的Sets数据类型进行集合操作。
Sets拥有去重(我们不能多次关注同一用户)功能。一个用户我们存贮两个集合:一个是保存用户关注的人 另一个是保存关注用户的人。
SADD 添加成员:
命令格式:
SADD key member [member …]
—– 关注
SREM 移除某个成员:
命令格式:
SREM key member [member …]
——-取关
SCARD 统计集合内的成员数:
命令格式:
SCARD key
——-关注/粉丝个数
SISMEMBER 判断是否是集合成员:
命令格式:
SISMEMBER key member
———判断是否关注(如果关注那么只可以点击取关)
SMEMBERS 查询集合内的成员:
命令格式:
SMEMBERS key
——-列表使用(关注列表和粉丝列表)
SINTER 查询集合的交集:
命令格式:
SINTER key [key …]
——–共同关注、我关注的人关注了他
数据库表设计
这个数据库表的结构比较简单,主要记录了用户id、用户关注的id和关注状态。
CREATETABLE`t_follow`( `id`int(11)NOTNULLAUTO_INCREMENT, `user_id`int(11)DEFAULTNULLCOMMENT'当前登录用户的id', `follow_user_id`int(11)DEFAULTNULLCOMMENT'当前登录用户关注的用户的id', `is_valid`tinyint(1)DEFAULTNULLCOMMENT'关注状态,0-没有关注,1-关注了', `create_date`datetimeDEFAULTNULL, `update_date`datetimeDEFAULTNULL, PRIMARYKEY(`id`) )ENGINE=InnoDBDEFAULTCHARSET=utf8ROW_FORMAT=COMPACTCOMMENT='用户和用户关注表';
新建好友功能微服务
添加依赖和配置
pom依赖如下:
redis-seckillcom.zjq1.0-SNAPSHOT4.0.0ms-followorg.springframework.cloudspring-cloud-starter-netflix-eureka-clientorg.springframework.bootspring-boot-starter-webmysqlmysql-connector-javaorg.springframework.bootspring-boot-starter-data-redisorg.mybatis.spring.bootmybatis-spring-boot-startercom.zjqcommons1.0-SNAPSHOTcom.battcnswagger-spring-boot-starter
springboot配置如下:
server: port:7004#端口 spring: application: name:ms-follow#应用名 #数据库 datasource: driver-class-name:com.mysql.cj.jdbc.Driver username:root password:root url:jdbc:mysql://127.0.0.1:3306/seckill?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false #Redis redis: port:6379 host:localhost timeout:3000 password:123456 database:2 #Swagger swagger: base-package:com.zjq.follow title:好用功能微服务API接口文档 #配置EurekaServer注册中心 eureka: instance: prefer-ip-address:true instance-id:${spring.cloud.client.ip-address}:${server.port} client: service-url: defaultZone:http://localhost:7000/eureka/ service: name: ms-oauth-server:http://ms-oauth2-server/ ms-diners-server:http://ms-users/ mybatis: configuration: map-underscore-to-camel-case:true#开启驼峰映射 logging: pattern: console:'%d{HH:mm:ss}[%thread]%-5level%logger{50}-%msg%n'
添加配置类
redis配置类:
packagecom.zjq.seckill.config; importcom.fasterxml.jackson.annotation.JsonAutoDetect; importcom.fasterxml.jackson.annotation.PropertyAccessor; importcom.fasterxml.jackson.databind.ObjectMapper; importorg.springframework.context.annotation.Bean; importorg.springframework.context.annotation.Configuration; importorg.springframework.core.io.ClassPathResource; importorg.springframework.data.redis.connection.RedisConnectionFactory; importorg.springframework.data.redis.core.RedisTemplate; importorg.springframework.data.redis.core.script.DefaultRedisScript; importorg.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; importorg.springframework.data.redis.serializer.StringRedisSerializer; /** *RedisTemplate配置类 *@authorzjq */ @Configuration publicclassRedisTemplateConfiguration{ /** *redisTemplate序列化使用的jdkSerializeable,存储二进制字节码,所以自定义序列化类 * *@paramredisConnectionFactory *@return */ @Bean publicRedisTemplateredisTemplate(RedisConnectionFactoryredisConnectionFactory){ RedisTemplateredisTemplate=newRedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); //使用Jackson2JsonRedisSerialize替换默认序列化 Jackson2JsonRedisSerializerjackson2JsonRedisSerializer=newJackson2JsonRedisSerializer(Object.class); ObjectMapperobjectMapper=newObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY); jackson2JsonRedisSerializer.setObjectMapper(objectMapper); //设置key和value的序列化规则 redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.setKeySerializer(newStringRedisSerializer()); redisTemplate.setHashKeySerializer(newStringRedisSerializer()); redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); redisTemplate.afterPropertiesSet(); returnredisTemplate; } }
REST配置类:

关注/取关实现
业务逻辑

Mapper实现
Mapper比较简单主要是查询关注信息、添加关注信息、取关或者再次关注。

Service层实现
packagecom.zjq.seckill.service;importcn.hutool.core.bean.BeanUtil;importcom.zjq.commons.constant.ApiConstant;importcom.zjq.commons.constant.RedisKeyConstant;importcom.zjq.commons.exception.ParameterException;importcom.zjq.commons.model.domain.ResultInfo;importcom.zjq.commons.model.pojo.Follow;importcom.zjq.commons.model.vo.SignInUserInfo;importcom.zjq.commons.utils.AssertUtil;importcom.zjq.commons.utils.ResultInfoUtil;importcom.zjq.seckill.mapper.FollowMapper;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.stereotype.Service;importorg.springframework.web.client.RestTemplate;importjavax.annotation.Resource;importjava.util.LinkedHashMap;/***关注/取关业务逻辑层*@authorzjq*/@ServicepublicclassFollowService{@Value("${service.name.ms-oauth-server}")privateStringoauthServerName;@Value("${service.name.ms-diners-server}")privateStringdinersServerName;@ResourceprivateRestTemplaterestTemplate;@ResourceprivateFollowMapperfollowMapper;@ResourceprivateRedisTemplateredisTemplate;/***关注/取关**@paramfollowUserId关注的食客ID*@paramisFollowed是否关注1=关注0=取关*@paramaccessToken登录用户token*@parampath访问地址*@return*/publicResultInfofollow(IntegerfollowUserId,intisFollowed,StringaccessToken,Stringpath){//是否选择了关注对象AssertUtil.isTrue(followUserId==null||followUserId<1,"请选择要关注的人");//获取登录用户信息(封装方法)SignInUserInfodinerInfo=loadSignInDinerInfo(accessToken);//获取当前登录用户与需要关注用户的关注信息Followfollow=followMapper.selectFollow(dinerInfo.getId(),followUserId);//如果没有关注信息,且要进行关注操作--添加关注if(follow==null&&isFollowed==1){//添加关注信息intcount=followMapper.save(dinerInfo.getId(),followUserId);//添加关注列表到Redisif(count==1){addToRedisSet(dinerInfo.getId(),followUserId);}returnResultInfoUtil.build(ApiConstant.SUCCESS_CODE,"关注成功",path,"关注成功");}//如果有关注信息,且目前处于关注状态,且要进行取关操作--取关关注if(follow!=null&&follow.getIsValid()==1&&isFollowed==0){//取关intcount=followMapper.update(follow.getId(),isFollowed);//移除Redis关注列表if(count==1){removeFromRedisSet(dinerInfo.getId(),followUserId);}returnResultInfoUtil.build(ApiConstant.SUCCESS_CODE,"成功取关",path,"成功取关");}//如果有关注信息,且目前处于取关状态,且要进行关注操作--重新关注if(follow!=null&&follow.getIsValid()==0&&isFollowed==1){//重新关注intcount=followMapper.update(follow.getId(),isFollowed);//添加关注列表到Redisif(count==1){addToRedisSet(dinerInfo.getId(),followUserId);}returnResultInfoUtil.build(ApiConstant.SUCCESS_CODE,"关注成功",path,"关注成功");}returnResultInfoUtil.buildSuccess(path,"操作成功");}/***添加关注列表到Redis**@paramdinerId*@paramfollowUserId*/privatevoidaddToRedisSet(IntegerdinerId,IntegerfollowUserId){redisTemplate.opsForSet().add(RedisKeyConstant.following.getKey()+dinerId,followUserId);redisTemplate.opsForSet().add(RedisKeyConstant.followers.getKey()+followUserId,dinerId);}/***移除Redis关注列表**@paramdinerId*@paramfollowUserId*/privatevoidremoveFromRedisSet(IntegerdinerId,IntegerfollowUserId){redisTemplate.opsForSet().remove(RedisKeyConstant.following.getKey()+dinerId,followUserId);redisTemplate.opsForSet().remove(RedisKeyConstant.followers.getKey()+followUserId,dinerId);}/***获取登录用户信息**@paramaccessToken*@return*/privateSignInUserInfoloadSignInDinerInfo(StringaccessToken){//必须登录AssertUtil.mustLogin(accessToken);Stringurl=oauthServerName+"user/me?access_token={accessToken}";ResultInforesultInfo=restTemplate.getForObject(url,ResultInfo.class,accessToken);if(resultInfo.getCode()!=ApiConstant.SUCCESS_CODE){thrownewParameterException(resultInfo.getMessage());}SignInUserInfodinerInfo=BeanUtil.fillBeanWithMap((LinkedHashMap)resultInfo.getData(),newSignInUserInfo(),false);returndinerInfo;}}
Controller实现
packagecom.zjq.seckill.controller;importcom.zjq.commons.model.domain.ResultInfo;importcom.zjq.seckill.service.FollowService;importorg.springframework.web.bind.annotation.*;importjavax.annotation.Resource;importjavax.servlet.http.HttpServletRequest;/***关注/取关控制层*@authorzjq*/@RestControllerpublicclassFollowController{@ResourceprivateFollowServicefollowService;@ResourceprivateHttpServletRequestrequest;/***关注/取关**@paramfollowUserId关注的用户ID*@paramisFollowed是否关注1=关注0=取消*@paramaccess_token登录用户token*@return*/@PostMapping("/{followUserId}")publicResultInfofollow(@PathVariableIntegerfollowUserId,@RequestParamintisFollowed,Stringaccess_token){ResultInforesultInfo=followService.follow(followUserId,isFollowed,access_token,request.getServletPath());returnresultInfo;}}
网关配置路由规则
spring:application:name:ms-gatewaycloud:gateway:discovery:locator:enabled:true#开启配置注册中心进行路由功能lower-case-service-id:true#将服务名称转小写routes:#好友功能微服务-id:ms-followuri:lb://ms-followpredicates:-Path=/follow/**filters:-StripPrefix=1
测试验证
依次启动,注册中心、网关、认证中心、好友功能微服务。
测试id为5的用户,关注id为1的用户。


查看redis可以看到有两个集合,一个粉丝集合,一个关注集合。

查看数据库,id为5的用户关注了id为1的用户


共同关注列表
从Redis中读取登录用户的关注列表与查看用户的关注列表,然后进行交集操作,获取共同关注的用户id
然后通过用户服务传入用户id数据获取用户基本信息
Controller添加方法
/** *共同关注列表 * *@paramuserId *@paramaccess_token *@return */ @GetMapping("commons/{userId}") publicResultInfofindCommonsFriends(@PathVariableIntegeruserId, Stringaccess_token){ returnfollowService.findCommonsFriends(userId,access_token,request.getServletPath()); }
Service添加方法
/** *共同关注列表 * *@paramuserId *@paramaccessToken *@parampath *@return */ @Transactional(rollbackFor=Exception.class) publicResultInfofindCommonsFriends(IntegeruserId,StringaccessToken,Stringpath){ //是否选择了查看对象 AssertUtil.isTrue(userId==null||userId<1, signinuserinfouserinfo="loadSignInuserInfo(accessToken);" stringloginuserkey="RedisKeyConstant.following.getKey()+userInfo.getId();" stringuserkey="RedisKeyConstant.following.getKey()+userId;" setuserids="redisTemplate.opsForSet().intersect(loginuserKey,userKey);" userids="=null||userIds.isEmpty()){" resultinforesultinfo="restTemplate.getForObject(usersServerName+" access_token="{accessToken}&ids={ids}" listdinnerinfomaps="(ArrayList)resultInfo.getData();" listuserinfos="dinnerInfoMaps.stream()" user-="">BeanUtil.fillBeanWithMap(user,newShortUserInfo(),true)) .collect(Collectors.toList()); returnResultInfoUtil.buildSuccess(path,userInfos); }
用户服务新增根据ids查询用户集合
Controller:
/** *根据ids查询用户信息 * *@paramids *@return */ @GetMapping("findByIds") publicResultInfo
findByIds(Stringids){ ListdinerInfos=userService.findByIds(ids); returnResultInfoUtil.buildSuccess(request.getServletPath(),dinerInfos); }
Service:
/** *根据ids查询食客信息 * *@paramids主键id,多个以逗号分隔,逗号之间不用空格 *@return */ publicListfindByIds(Stringids){ AssertUtil.isNotEmpty(ids); String[]idArr=ids.split(","); ListdinerInfos=usersMapper.findByIds(idArr); returndinerInfos; }
Mapper:
/** *根据ID集合查询多个食客信息 *@paramids *@return */ @Select(""+ "#{id}"+ ""+ ""_ue_custom_node_="true">") ListfindByIds(@Param("ids")String[]ids);
上面测试已经让id5和7的用户关注了id为1的用户,我们继续让id5的用户关注id为3的用户,让id5、6、7的用户关注了id为2的用户。
redis和数据库信息如下:




测试验证
查询当前登录用户id为5和id为7的共同关注信息:

查询当前登录用户id为6和id为7的共同关注信息:

可以看出来5和7共同关注了1和2,6和7只共同关注了2,符合预期。
感谢阅读,希望对你有所帮助 :)
来源:zhanjq.blog.csdn.net/article/details/128208047
-
- 圣诞节应该送点什么礼物好呢(圣诞节最用心的送礼list清单)
-
2025-11-09 13:52:57
-
- 冒险小虎队解密卡(冒险小虎队解密卡文字)
-
2025-11-09 13:50:51
-
- 发的食物有哪些,有哪些食物是发物
-
2025-11-09 13:48:45
-
- 朱元璋多少岁(我24岁朱元璋多少岁)
-
2025-11-09 13:46:40
-
- 世说新语的作者是谁写的(试说刘义庆《世说新语》编成于扬州)
-
2025-11-08 23:12:24
-
- 广州东方新世界二手房最新价格(广州东方新世界 阳性)
-
2025-11-08 23:10:18
-
- 人造卫星的作用和工作原理
-
2025-11-08 23:08:13
-
- 在电脑上如何进行屏幕录制(分享4个电脑屏幕录制方法)
-
2025-11-08 23:06:07
-
- 如果发现自己老公婚前出轨怎么办
-
2025-11-08 23:04:01
-
- 容易婚外情的女人都有哪些特点(智慧的女人该如何对待婚外情)
-
2025-11-08 23:01:55
-
- 大白菜起源是哪里?大白菜产地分布有哪些
-
2025-11-08 22:59:50
-
- 邋遢长发,不修边幅,发哥新“赌神”形象让观众惊呼颠覆自我!
-
2025-11-08 22:57:44
-
- 挂羊头卖狗肉是怎么来的?背后有什么历史故事?
-
2025-11-08 22:55:38
-
- “离过婚的女人,没资格要20万彩礼”婆婆羞辱儿媳,毁了儿子婚姻
-
2025-11-08 22:53:33
-
- 怎么样制做珍珠奶茶制作一杯珍珠奶茶的成本是多少钱
-
2025-11-05 16:28:28
-
- 中国新型无人战车亮相,或将改变战争规则,颠覆未来!
-
2025-11-05 16:26:22
-
- 实木复合地板十大品牌排行榜(实木复合地板品牌排名)
-
2025-11-05 16:24:17
-
- 萨特存在主义基本原则(萨特存在主义三个原则)
-
2025-11-05 16:22:11
-
- 宝可梦每个属性最强的招式(宝可梦奇闻趣事)
-
2025-11-05 16:20:05
-
- 巴金和冰心奶奶是什么关系(巴金和冰心奶奶是情侣吗)
-
2025-11-05 16:17:59



农村40万存款算穷吗
ins什么意思,女生ins是什么意思?