需要阐发
密友功用是今朝交际场景的必备功用之一,一般密友相干的功用包罗有:存眷/与闭、尔(他)的存眷、尔(他)的粉丝、配合存眷、尔存眷的人也存眷他等如许一点儿功用。
类似于如许的功用咱们假设接纳数据库干的话不过纯真获得用户的一点儿粉丝大概存眷列表的话是很简朴也很简单完毕, 可是假设尔念要查出二个以至少个用户配合存眷了哪些人大概念要盘问二个大概多个用户的配合粉丝的话便会很省事, 服从也没有会很下。可是假设您用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 …] --------配合存眷、尔存眷的人存眷了他
- 需要保守邪版IDEA的能够联系尔,79元一年,邪版受权,民网可查有用期,有需要的减尔微疑:poxiaozhiai6,备注:126。
数据库表设想
那个数据库表的构造比力简朴,主要记载了用户id、用户存眷的id战存眷形状。- CREATE TABLE `t_follow` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `user_id` int(11) DEFAULT NULL COMMENT '目前登任命户的id',
- `follow_user_id` int(11) DEFAULT NULL COMMENT '目前登任命户存眷的用户的id',
- `is_valid` tinyint(1) DEFAULT NULL COMMENT '存眷形状,0-不存眷,1-存眷了',
- `create_date` datetime DEFAULT NULL,
- `update_date` datetime DEFAULT NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='用户战用户存眷表';
复造代码 新修密友功用微效劳
增加依靠战设置
pom依靠以下:- <?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?>
- <project xmlns=&#34;http://maven.apache.org/POM/4.0.0&#34;
- xmlns:xsi=&#34;http://www.w3.org/2001/XMLSchema-instance&#34;
- xsi:schemaLocation=&#34;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&#34;>
- <parent>
- <artifactId>redis-seckill</artifactId>
- <groupId>com.zjq</groupId>
- <version>1.0-SNAPSHOT</version>
- </parent>
- <modelVersion>4.0.0</modelVersion>
- <artifactId>ms-follow</artifactId>
- <dependencies>
- <!-- eureka client -->
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
- </dependency>
- <!-- spring web -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <!-- mysql -->
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- </dependency>
- <!-- spring data redis -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
- <!-- mybatis -->
- <dependency>
- <groupId>org.mybatis.spring.boot</groupId>
- <artifactId>mybatis-spring-boot-starter</artifactId>
- </dependency>
- <!-- co妹妹ons 大众名目 -->
- <dependency>
- <groupId>com.zjq</groupId>
- <artifactId>co妹妹ons</artifactId>
- <version>1.0-SNAPSHOT</version>
- </dependency>
- <!-- swagger -->
- <dependency>
- <groupId>com.battcn</groupId>
- <artifactId>swagger-spring-boot-starter</artifactId>
- </dependency>
- </dependencies>
- </project>
复造代码 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交心文档
- # 设置 Eureka Server 备案中间
- 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: &#39;%d{HH:妹妹:ss} [%thread] %-5level %logger{50} - %msg%n&#39;
复造代码 增加设置类
redis设置类:- package com.zjq.seckill.config;
- import com.fasterxml.jackson.annotation.JsonAutoDetect;
- import com.fasterxml.jackson.annotation.PropertyAccessor;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.core.io.ClassPathResource;
- import org.springframework.data.redis.connection.RedisConnectionFactory;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.script.DefaultRedisScript;
- import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
- import org.springframework.data.redis.serializer.StringRedisSerializer;
- /**
- * RedisTemplate设置类
- * @author zjq
- */
- @Configuration
- public class RedisTemplateConfiguration {
- /**
- * redisTemplate 序列化使用的jdkSerializeable, 保存两退造字节码, 以是自界说序列化类
- *
- * @param redisConnectionFactory
- * @return
- */
- @Bean
- public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
- RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
- redisTemplate.setConnectionFactory(redisConnectionFactory);
- // 使用Jackson2JsonRedisSerialize交流 默认序列化
- Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
- ObjectMapper objectMapper = new ObjectMapper();
- objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
- jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
- // 树立key战value的序列化划定规矩
- redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
- redisTemplate.setKeySerializer(new StringRedisSerializer());
- redisTemplate.setHashKeySerializer(new StringRedisSerializer());
- redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
- redisTemplate.afterPropertiesSet();
- return redisTemplate;
- }
-
- }
复造代码 REST设置类:
存眷/与闭完毕
营业逻辑
Mapper完毕
Mapper比力简朴主要是盘问存眷疑息、增加存眷疑息、与闭大概再次存眷。
Service层完毕
- package com.zjq.seckill.service;
- import cn.hutool.core.bean.BeanUtil;
- import com.zjq.co妹妹ons.constant.ApiConstant;
- import com.zjq.co妹妹ons.constant.RedisKeyConstant;
- import com.zjq.co妹妹ons.exception.ParameterException;
- import com.zjq.co妹妹ons.model.domain.ResultInfo;
- import com.zjq.co妹妹ons.model.pojo.Follow;
- import com.zjq.co妹妹ons.model.vo.SignInUserInfo;
- import com.zjq.co妹妹ons.utils.AssertUtil;
- import com.zjq.co妹妹ons.utils.ResultInfoUtil;
- import com.zjq.seckill.mapper.FollowMapper;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.stereotype.Service;
- import org.springframework.web.client.RestTemplate;
- import javax.annotation.Resource;
- import java.util.LinkedHashMap;
- /**
- * 存眷/与闭营业逻辑层
- * @author zjq
- */
- @Service
- public class FollowService {
- @Value(&#34;${service.name.ms-oauth-server}&#34;)
- private String oauthServerName;
- @Value(&#34;${service.name.ms-diners-server}&#34;)
- private String dinersServerName;
- @Resource
- private RestTemplate restTemplate;
- @Resource
- private FollowMapper followMapper;
- @Resource
- private RedisTemplate redisTemplate;
- /**
- * 存眷/与闭
- *
- * @param followUserId 存眷的门客ID
- * @param isFollowed 可否存眷 1=存眷 0=与闭
- * @param accessToken 登任命户token
- * @param path 拜访 地点
- * @return
- */
- public ResultInfo follow(Integer followUserId, int isFollowed,
- String accessToken, String path) {
- // 可否挑选了存眷工具
- AssertUtil.isTrue(followUserId == null || followUserId < 1,
- &#34;请挑选要存眷的人&#34;);
- // 获得登任命户疑息 (启拆办法)
- SignInUserInfo dinerInfo = loadSignInDinerInfo(accessToken);
- // 获得目前登任命户取需要存眷用户的存眷疑息
- Follow follow = followMapper.selectFollow(dinerInfo.getId(), followUserId);
- //假设 不存眷疑息,且要截至存眷操纵 -- 增加存眷
- if (follow == null && isFollowed == 1) {
- // 增加存眷疑息
- int count = followMapper.save(dinerInfo.getId(), followUserId);
- // 增加存眷列表到 Redis
- if (count == 1) {
- addToRedisSet(dinerInfo.getId(), followUserId);
- }
- return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE,
- &#34;存眷胜利&#34;, path, &#34;存眷胜利&#34;);
- }
- //假设 相关注疑息,且今朝处于存眷形状,且要截至与闭操纵 -- 与闭存眷
- if (follow != null && follow.getIsValid() == 1 && isFollowed == 0) {
- // 与闭
- int count = followMapper.update(follow.getId(), isFollowed);
- // 移除 Redis 存眷列表
- if (count == 1) {
- removeFromRedisSet(dinerInfo.getId(), followUserId);
- }
- return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE,
- &#34;胜利与闭&#34;, path, &#34;胜利与闭&#34;);
- }
- //假设 相关注疑息,且今朝处于与闭形状,且要截至存眷操纵 -- 从头存眷
- if (follow != null && follow.getIsValid() == 0 && isFollowed == 1) {
- // 从头存眷
- int count = followMapper.update(follow.getId(), isFollowed);
- // 增加存眷列表到 Redis
- if (count == 1) {
- addToRedisSet(dinerInfo.getId(), followUserId);
- }
- return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE,
- &#34;存眷胜利&#34;, path, &#34;存眷胜利&#34;);
- }
- return ResultInfoUtil.buildSuccess(path, &#34;操纵胜利&#34;);
- }
- /**
- * 增加存眷列表到 Redis
- *
- * @param dinerId
- * @param followUserId
- */
- private void addToRedisSet(Integer dinerId, Integer followUserId) {
- redisTemplate.opsForSet().add(RedisKeyConstant.following.getKey() + dinerId, followUserId);
- redisTemplate.opsForSet().add(RedisKeyConstant.followers.getKey() + followUserId, dinerId);
- }
- /**
- * 移除 Redis 存眷列表
- *
- * @param dinerId
- * @param followUserId
- */
- private void removeFromRedisSet(Integer dinerId, Integer followUserId) {
- redisTemplate.opsForSet().remove(RedisKeyConstant.following.getKey() + dinerId, followUserId);
- redisTemplate.opsForSet().remove(RedisKeyConstant.followers.getKey() + followUserId, dinerId);
- }
- /**
- * 获得登任命户疑息
- *
- * @param accessToken
- * @return
- */
- private SignInUserInfo loadSignInDinerInfo(String accessToken) {
- //必需 登录
- AssertUtil.mustLogin(accessToken);
- String url = oauthServerName + &#34;user/me?access_token={accessToken}&#34;;
- ResultInfo resultInfo = restTemplate.getForObject(url, ResultInfo.class, accessToken);
- if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
- throw new ParameterException(resultInfo.getMessage());
- }
- SignInUserInfo dinerInfo = BeanUtil.fillBeanWithMap((LinkedHashMap) resultInfo.getData(),
- new SignInUserInfo(), false);
- return dinerInfo;
- }
- }
复造代码 Controller完毕
- package com.zjq.seckill.controller;
- import com.zjq.co妹妹ons.model.domain.ResultInfo;
- import com.zjq.seckill.service.FollowService;
- import org.springframework.web.bind.annotation.*;
- import javax.annotation.Resource;
- import javax.servlet.http.HttpServletRequest;
- /**
- * 存眷/与闭掌握层
- * @author zjq
- */
- @RestController
- public class FollowController {
- @Resource
- private FollowService followService;
- @Resource
- private HttpServletRequest request;
- /**
- * 存眷/与闭
- *
- * @param followUserId 存眷的用户ID
- * @param isFollowed 可否存眷 1=存眷 0=打消
- * @param access_token 登任命户token
- * @return
- */
- @PostMapping(&#34;/{followUserId}&#34;)
- public ResultInfo follow(@PathVariable Integer followUserId,
- @RequestParam int isFollowed,
- String access_token) {
- ResultInfo resultInfo = followService.follow(followUserId,
- isFollowed, access_token, request.getServletPath());
- return resultInfo;
- }
- }
复造代码 网闭设置路由划定规矩
- spring:
- application:
- name: ms-gateway
- cloud:
- gateway:
- discovery:
- locator:
- enabled: true # 启开设置备案中间截至路由功用
- lower-case-service-id: true # 将效劳称呼转小写
- routes:
-
- # 密友功用微效劳
- - id: ms-follow
- uri: lb://ms-follow
- predicates:
- - Path=/follow/**
- filters:
- - StripPrefix=1
复造代码 测尝试证
顺次启用,备案中间、网闭、认证中间、密友功用微效劳。
尝试id为5的用户,存眷id为1的用户。
检查 redis能够瞅到有二个汇合,一个粉丝汇合,一个存眷汇合。
检察数据库,id为5的用户存眷了id为1的用户
让id即是7的用户存眷id即是1的用户,redis战数据库保存疑息以下:
配合存眷列表
- 从Redis中读与登任命户的存眷列表取检察用户的存眷列表,而后截至交加操纵,获得配合存眷的用户id
- 而后颠末用户效劳传进用户id数据获得用户根本疑息
Controller增加办法
- /**
- * 配合存眷列表
- *
- * @param userId
- * @param access_token
- * @return
- */
- @GetMapping(&#34;co妹妹ons/{userId}&#34;)
- public ResultInfo findCo妹妹onsFriends(@PathVariable Integer userId,
- String access_token) {
- return followService.findCo妹妹onsFriends(userId, access_token, request.getServletPath());
- }
复造代码 Service增加办法
- /**
- * 配合存眷列表
- *
- * @param userId
- * @param accessToken
- * @param path
- * @return
- */
- @Transactional(rollbackFor = Exception.class)
- public ResultInfo findCo妹妹onsFriends(Integer userId, String accessToken, String path) {
- // 可否挑选了检察工具
- AssertUtil.isTrue(userId == null || userId < 1,
- &#34;请挑选要检察的人&#34;);
- // 获得登任命户疑息
- SignInUserInfo userInfo = loadSignInuserInfo(accessToken);
- // 获得登任命户的存眷疑息
- String loginuserKey = RedisKeyConstant.following.getKey() + userInfo.getId();
- // 获得登任命户检察工具的存眷疑息
- String userKey = RedisKeyConstant.following.getKey() + userId;
- // 计较交加
- Set<Integer> userIds = redisTemplate.opsForSet().intersect(loginuserKey, userKey);
- // 不
- if (userIds == null || userIds.isEmpty()) {
- return ResultInfoUtil.buildSuccess(path, new ArrayList<ShortUserInfo>());
- }
- // 挪用门客效劳按照 ids 盘问门客疑息
- ResultInfo resultInfo = restTemplate.getForObject(usersServerName + &#34;findByIds?access_token={accessToken}&ids={ids}&#34;,
- ResultInfo.class, accessToken, StrUtil.join(&#34;,&#34;, userIds));
- if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
- resultInfo.setPath(path);
- return resultInfo;
- }
- //处置 成果散
- List<LinkedHashMap> dinnerInfoMaps = (ArrayList) resultInfo.getData();
- List<ShortUserInfo> userInfos = dinnerInfoMaps.stream()
- .map(user -> BeanUtil.fillBeanWithMap(user, new ShortUserInfo(), true))
- .collect(Collectors.toList());
- return ResultInfoUtil.buildSuccess(path, userInfos);
- }
复造代码 用户效劳新删按照ids盘问用户汇合
Controller:- /**
- *依据 ids 盘问用户疑息
- *
- * @param ids
- * @return
- */
- @GetMapping(&#34;findByIds&#34;)
- public ResultInfo<List<ShortUserInfo>> findByIds(String ids) {
- List<ShortUserInfo> dinerInfos = userService.findByIds(ids);
- return ResultInfoUtil.buildSuccess(request.getServletPath(), dinerInfos);
- }
复造代码 Service:- /**
- *依据 ids 盘问门客疑息
- *
- * @param ids 主键 id,多个以逗号分开,逗号之间不消空格
- * @return
- */
- public List<ShortUserInfo> findByIds(String ids) {
- AssertUtil.isNotEmpty(ids);
- String[] idArr = ids.split(&#34;,&#34;);
- List<ShortUserInfo> dinerInfos = usersMapper.findByIds(idArr);
- return dinerInfos;
- }
复造代码 Mapper:- /**
- *依据 ID 汇合盘问多个门客疑息
- * @param ids
- * @return
- */
- @Select(&#34;<script> &#34; +
- &#34; select id, nickname, avatar_url from t_diners &#34; +
- &#34; where is_valid = 1 and id in &#34; +
- &#34; <foreach item=\&#34;id\&#34; collection=\&#34;ids\&#34; open=\&#34;(\&#34; separator=\&#34;,\&#34; close=\&#34;)\&#34;> &#34; +
- &#34; #{id} &#34; +
- &#34; </foreach> &#34; +
- &#34; </script>&#34;)
- List<ShortUserInfo> findByIds(@Param(&#34;ids&#34;) String[] ids);
复造代码 上面尝试已经让id5战7的用户存眷了id为1的用户,咱们持续让id5的用户存眷id为3的用户,让id五、六、7的用户存眷了id为2的用户:
redis战数据库疑息以下:
测尝试证
盘问目前登任命户id为5战id为7的配合存眷疑息:
盘问目前登任命户id为6战id为7的配合存眷疑息:
能够瞅进去5战7配合存眷了1战2,6战7只配合存眷了2,契合预期。原文实质到此完毕了
若有收获 欢送面赞
您的鼓舞是尔最年夜的能源
若有毛病❌疑义欢送 列位指出 |