说明:
(1)本篇博客开发的内容:【查看是否已关注某用户,接口】、【关注,接口】、【取关,接口】;
(2)本篇博客就一个点:对于那些需要频繁写的小数据,也可以存到Redis中,直接在Redis中增改;
目录
一:粉丝功能,技术选择:利用Redis的"累加"、"累减"来统计用户的关注数和粉丝数;(从而消除使用聚合函数操作数据,给数据库带来的压力)
二:开发【假如当前登录用户是A,当A查看B的主页时,查询A是否已经关注B,接口】;
2.开发【查询当前登录用户,是否已经关注当前查看页面的作家,接口】;
(1)在【api】接口工程中,创建MyFansControllerApi接口,并定义【查询当前登录用户,是否已经关注当前查看页面的作家,接口】;
(2)在【user】用户服务中,创建MyFansController类,去实现【查询当前登录用户,是否已经关注当前查看页面的作家,接口】;
(3)在【user】用户服务中,创建MyFansService接口,定义一个查询当前登录用户是否关注的方法;然后,创建MyFansServiceImpl实现类,去实现这个方法;
三:开发【A用户关注B用户,A的关注数+1,B的粉丝数+1,接口】;
1.在【api】接口工程的MyFansControllerApi接口,定义【A用户关注B用户,A的关注数+1,B的粉丝数+1,接口】;
2.在【user】用户服务的MyFansController类中,去实现【A用户关注B用户,A的关注数数+1,B的粉丝数+1,接口】;
3.在【user】用户服务的MyFansService接口,定义一个方法;然后,在MyFansServiceImpl实现类,去实现这个方法;
四:开发【A用户取关B用户,A的关注数-1,B的粉丝数-1,接口】;
1.在【api】接口工程的MyFansControllerApi接口,定义【A用户取关B用户,A的关注数-1,B的粉丝数-1,接口】;
2.在【user】用户服务的MyFansController类中,去实现【A用户取关B用户,A的关注数-1,B的粉丝数-1,接口】;
3.在【user】用户服务的MyFansService接口,定义一个方法;然后,在MyFansServiceImpl实现类,去实现这个方法;
附加:【关注,接口】和【取关,接口】都需要,“用户登录、并且用户是激活状态”才能操作;所以,在【api】接口工程的InterceptorConfig类中,对其进行配置;
一:粉丝功能,技术选择:利用Redis的"累加"、"累减"来统计用户的关注数和粉丝数;(从而消除使用聚合函数操作数据,给数据库带来的压力)
已知,在MariaDB中有一个fans表;
(1)统计某个用户的关注数和粉丝数,直接的想法是count()的方式去查询fans表;
(2)但是,由于前台门户端的并发量将会很高;也将会有很多人查看别人的个人展示页;那么,就会频繁的count()查询fans表;
(3)而这是不好的(尤其是数据量很大的时候),数据库的压力会很大;(在【附加:数据的聚合函数,会给数据库造成很大压力;】中,对此进行了介绍)
(4)所以,针对这个具体的业务而言,我们会利用Redis来优化,减轻数据库压力;
(1)对于Redis来说,前面我们使用Redis存放过热点数据;即,都是redis这款nosql数据库,作为缓存来使用;
(2)但是,Redis本身就是一款nosql;我们,自然可以把其当成数据库来使用;
(3)某个用户的【关注数】、【粉丝数】的累加和类减,可以通过Redis来实现;(Redis具体处理任务的时候,是单线程的,所以其实安全的)
(4)Redis中,底层有INCR(对某个值加1)和DECR(对某个值减1)两个命令;
● 比如用户1001,可以在Redis中创建一个【1001:fans,fansNum】数据,记录用户的粉丝数;
● 如果有人关注了1001,那么这个值就加1;
● 如果有人取关了1001,那么这个值就减1;
● 用户的关注数,同理;
二:开发【假如当前登录用户是A,当A查看B的主页时,查询A是否已经关注B,接口】;
1.该部分,内容说明;
而【我是慕课网】是否关注【慕小健】,是需要去数据库表中查询的;
2.开发【查询当前登录用户,是否已经关注当前查看页面的作家,接口】;
(1)在【api】接口工程中,创建MyFansControllerApi接口,并定义【查询当前登录用户,是否已经关注当前查看页面的作家,接口】;
package com.imooc.api.controller.user; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @Api(value = "粉丝管理",tags = {"粉丝管理功能的controller"}) @RequestMapping("fans") //设置路由,这个是需要前后端约定好的; public interface MyFansControllerApi { /** * 假如当前登录用户是A,当A查看B的主页时,查询A是否已经关注B; * @param writerId * @param fanId * @return */ @ApiOperation(value = "查询当前登录用户,是否已经关注当前查看页面的作家", notes = "查询当前登录用户,是否已经关注当前查看页面的作家", httpMethod = "POST") @PostMapping("/isMeFollowThisWriter") public GraceJSONResult hello(@RequestParam String writerId, @RequestParam String fanId); }
说明:
(1)这个接口的url、请求方式、参数需要前后端保持一致;
(2)在【user】用户服务中,创建MyFansController类,去实现【查询当前登录用户,是否已经关注当前查看页面的作家,接口】;
package com.imooc.user.controller; import com.imooc.api.BaseController; import com.imooc.api.controller.user.MyFansControllerApi; import com.imooc.grace.result.GraceJSONResult; import com.imooc.user.service.MyFansService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RestController; @RestController public class MyFansController extends BaseController implements MyFansControllerApi { final static Logger logger = LoggerFactory.getLogger(MyFansController.class); @Autowired private MyFansService myFansService; /** * 假如当前登录用户是A,当A查看B的主页时,查询A是否已经关注B; * @param writerId * @param fanId * @return */ @Override public GraceJSONResult isMeFollowThisWriter(String writerId, String fanId) { //这儿需要加一个writerId和fanId判空的处理 boolean isFollow = myFansService.isMeFollowThisWriter(writerId, fanId); return GraceJSONResult.ok(isFollow); } }
(3)在【user】用户服务中,创建MyFansService接口,定义一个查询当前登录用户是否关注的方法;然后,创建MyFansServiceImpl实现类,去实现这个方法;
package com.imooc.user.service; public interface MyFansService { /** * 假如当前登录用户是A,当A查看B的主页时,查询A是否已经关注B; */ public boolean isMeFollowThisWriter(String writerId, String fanId); }
package com.imooc.user.service.impl; import com.imooc.api.service.BaseService; import com.imooc.bo.UpdateUserInfoBO; import com.imooc.enums.Sex; import com.imooc.enums.UserStatus; import com.imooc.exception.GraceException; import com.imooc.grace.result.ResponseStatusEnum; import com.imooc.pojo.AppUser; import com.imooc.pojo.Fans; import com.imooc.user.mapper.AppUserMapper; import com.imooc.user.mapper.FansMapper; import com.imooc.user.service.MyFansService; import com.imooc.user.service.UserService; import com.imooc.utils.DateUtil; import com.imooc.utils.DesensitizationUtil; import com.imooc.utils.JsonUtils; import com.imooc.utils.RedisOperator; import org.n3r.idworker.Sid; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import tk.mybatis.mapper.entity.Example; import java.util.Date; @Service public class MyFansServiceImpl extends BaseService implements MyFansService { @Autowired FansMapper fansMapper; /** * 假如当前登录用户是A,当A查看B的主页时,查询A是否已经关注B; */ @Override public boolean isMeFollowThisWriter(String writerId, String fanId) { //根据传过来的参数,构建一个Fans对象; Fans fans = new Fans(); fans.setFanId(fanId); fans.setWriterId(writerId); //然后,去数据库的fans表中,查查看是否有满足这种条件的记录; int count = fansMapper.selectCount(fans); return count > 0; } }
(1)记得使用【15:第二章:架构后端项目:11:使用【mybatis-generator-core】依赖生成实体类、mapper接口、mapper.xml等逆向文件;(我们使用tkmybatis来帮助提升开发效率)】这个工程,针对fans表生成对应的mapper接口、xml、实体类;
(2)这儿我们查询的时候,使用了selectCount()方法,即这儿底层其实也是使用了SQL的count函数;即,这儿是不好的;;;;后面,我们使用elasticsearch的时候,我们会把粉丝的数据保存到elasticsearch中,所以后面这儿我们会优化;
(3)fans表的结构;这个表其实是有点冗余的,但是这个冗余是为了后面方便的,所以这个冗余是有价值的;
3.效果;
(1)先install一下整个项目;(2)记得使用SwitchHost开启虚拟域名映射;(3)使用Tomcat启动前端项目;(4)然后,启动后端项目;
三:开发【A用户关注B用户,A的关注数+1,B的粉丝数+1,接口】;
PS:按理说A用户是不能关注自己的;;;但是,这儿我们因为懒,就没有对这一点做控制;;;
1.在【api】接口工程的MyFansControllerApi接口,定义【A用户关注B用户,A的关注数+1,B的粉丝数+1,接口】;
/** * A用户关注B用户,A的粉丝数+1; * @param writerId * @param fanId * @return */ @ApiOperation(value = "A用户关注B用户,A的粉丝数+1", notes = "A用户关注B用户,A的粉丝数+1", httpMethod = "POST") @PostMapping("/follow") public GraceJSONResult follow(@RequestParam String writerId, @RequestParam String fanId);
说明:
(1)
2.在【user】用户服务的MyFansController类中,去实现【A用户关注B用户,A的关注数数+1,B的粉丝数+1,接口】;
/** * A用户关注B用户,A的关注数数+1,B的粉丝数+1; * @param writerId * @param fanId * @return */ @Override public GraceJSONResult follow(String writerId, String fanId) { //这儿需要加一个writerId和fanId判空的处理 myFansService.follow(writerId, fanId); return GraceJSONResult.ok(); }
3.在【user】用户服务的MyFansService接口,定义一个方法;然后,在MyFansServiceImpl实现类,去实现这个方法;
/** * A用户关注B用户,A的关注数数+1,B的粉丝数+1; * @param writerId * @param fanId * @return */ public void follow(String writerId, String fanId);
……………………………………………………
/** * A用户关注B用户,A的关注数数+1,B的粉丝数+1; * * @param writerId * @param fanId * @return */ @Transactional @Override public void follow(String writerId, String fanId) { Fans fans = new Fans(); AppUser appUser = userService.getUser(fanId);//根据粉丝id,去查询该粉丝的详细信息 String fanPkId = sid.nextShort();//通过雪花算法,得到一个fans表记录的id //然后,根据fans表的要求,去设置信息 fans.setId(fanPkId); fans.setFanId(fanId); fans.setWriterId(writerId); fans.setFace(appUser.getFace()); fans.setFanNickname(appUser.getNickname()); fans.setProvince(appUser.getProvince()); fans.setSex(appUser.getSex()); fansMapper.insert(fans);//插入数据库 //然后,A的关注数需要+1,B的粉丝数要+1; //B的粉丝数+1;(如果这个key不存在,就去创建) redisOperator.increment(REDIS_WRITER_FANS_COUNT + ":" + writerId, 1); //A的关注数+1; redisOperator.increment(REDIS_MY_FOLLOW_COUNT + ":" + fanId, 1); }
说明:
(0)关注数和粉丝数,这种既需要频繁查询,又需要频繁增改的小数据;我们也使用了Redis;
(1)我们在【api】接口工程的BaseService类中,定义了两个常量,让其作为向Redis中存放数据时,key的前半部分;
4.效果;
(1)先install一下整个项目;(2)记得使用SwitchHost开启虚拟域名映射;(3)使用Tomcat启动前端项目;(4)然后,启动后端项目;
四:开发【A用户取关B用户,A的关注数-1,B的粉丝数-1,接口】;
1.在【api】接口工程的MyFansControllerApi接口,定义【A用户取关B用户,A的关注数-1,B的粉丝数-1,接口】;
/** * A用户取关B用户,A的关注数数-1,B的粉丝数-1; * @param writerId:当前被查看主页的用户;(换句话说,将要被取关的用户) * @param fanId:当前登录的用户;(换句话说,将要取关别人的,用户) * @return */ @ApiOperation(value = "A用户取关B用户,A的关注数数-1,B的粉丝数-1", notes = "A用户取关B用户,A的关注数数-1,B的粉丝数-1", httpMethod = "POST") @PostMapping("/follow") public GraceJSONResult unFollow(@RequestParam String writerId, @RequestParam String fanId);
说明:
(1)
2.在【user】用户服务的MyFansController类中,去实现【A用户取关B用户,A的关注数-1,B的粉丝数-1,接口】;
/** * A用户取关B用户,A的关注数数-1,B的粉丝数-1; * @param writerId:当前被查看主页的用户;(换句话说,将要被取关的用户) * @param fanId:当前登录的用户;(换句话说,将要取关别人的,用户) * @return */ @Override public GraceJSONResult unFollow(String writerId, String fanId) { //这儿需要加一个writerId和fanId判空的处理 myFansService.unFollow(writerId, fanId); return GraceJSONResult.ok(); }
3.在【user】用户服务的MyFansService接口,定义一个方法;然后,在MyFansServiceImpl实现类,去实现这个方法;
/** * A用户取关B用户,A的关注数数-1,B的粉丝数-1; * @param writerId * @param fanId * @return */ public void unFollow(String writerId, String fanId);
……………………………………………………
/** * A用户取关B用户,A的关注数数-1,B的粉丝数-1; * * @param writerId * @param fanId * @return */ @Transactional @Override public void unFollow(String writerId, String fanId) { Fans fans = new Fans(); fans.setWriterId(writerId); fans.setFanId(fanId); fansMapper.delete(fans);//删除fans表中的记录 //然后,A的关注数需要-1,B的粉丝数要-1; //B的粉丝数-1;(如果这个key不存在,就去创建) redisOperator.decrement(REDIS_WRITER_FANS_COUNT + ":" + writerId, 1); //A的关注数-1; redisOperator.decrement(REDIS_MY_FOLLOW_COUNT + ":" + fanId, 1); }
4.效果;
(1)先install一下整个项目;(2)记得使用SwitchHost开启虚拟域名映射;(3)使用Tomcat启动前端项目;(4)然后,启动后端项目;
同时,在Redis中的数据,也是OK的;