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

广州网站建设找新际深圳物流公司大全排名

广州网站建设找新际,深圳物流公司大全排名,做医药行业找药的网站,官方网站建设方案2022黑马Redis跟学笔记.实战篇 六4.7.达人探店功能4.7.1.分享探店图文1. 达人探店-发布探店笔记2. 达人探店-查看探店笔记4.7.2.点赞功能4.7.3.基于List实现点赞用户列表TOP104.7.4.基于SortedSet实现点赞排行榜4.8.关注列表4.8.1.关注列表实现原理4.8.2.添加关注1. 好友关注-关… 2022黑马Redis跟学笔记.实战篇 六4.7.达人探店功能4.7.1.分享探店图文1. 达人探店-发布探店笔记2. 达人探店-查看探店笔记4.7.2.点赞功能4.7.3.基于List实现点赞用户列表TOP104.7.4.基于SortedSet实现点赞排行榜4.8.关注列表4.8.1.关注列表实现原理4.8.2.添加关注1. 好友关注-关注和取消关注4.8.3.共同关注列表4.8.4.取消关注1.好友关注-Feed流实现方案4.8.5.探店推送功能1. 好友关注-推送到粉丝收件箱2.好友关注-实现分页查询收邮箱4.7.达人探店功能 4.7.1.分享探店图文 1. 达人探店-发布探店笔记 发布探店笔记 探店笔记类似点评网站的评价往往是图文结合。对应的表有两个 tb_blog探店笔记表包含笔记中的标题、文字、图片等 tb_blog_comments其他用户对探店笔记的评价 具体发布流程 上传接口java Slf4j RestController RequestMapping(upload) public class UploadController {PostMapping(blog)public Result uploadImage(RequestParam(file) MultipartFile image) {try {// 获取原始文件名称String originalFilename image.getOriginalFilename();// 生成新文件名String fileName createNewFileName(originalFilename);// 保存文件image.transferTo(new File(SystemConstants.IMAGE_UPLOAD_DIR, fileName));// 返回结果log.debug(文件上传成功{}, fileName);return Result.ok(fileName);} catch (IOException e) {throw new RuntimeException(文件上传失败, e);}}}注意同学们在操作时需要修改SystemConstants.IMAGE_UPLOAD_DIR 自己图片所在的地址在实际开发中图片一般会放在nginx上或者是云存储上。 修改图片的上传路径到本地的地址 修改类SystemConstants.java public static final String IMAGE_UPLOAD_DIR F:\\javawebwork\\ty-comment-html\\nginx-1.18.0\\html\\hmdp\\imgs\\;BlogController java RestController RequestMapping(/blog) public class BlogController {Resourceprivate IBlogService blogService;PostMappingpublic Result saveBlog(RequestBody Blog blog) {//获取登录用户UserDTO user UserHolder.getUser();blog.setUpdateTime(user.getId());//保存探店博文blogService.saveBlog(blog);//返回idreturn Result.ok(blog.getId());} }点击加号上传图片 根据开发者工具看到目录 上传路径 点击发布 发布之后可以在首页看到相关信息。 再看一下数据库 2. 达人探店-查看探店笔记 实现查看发布探店笔记的接口 点击笔记详情发现报错了。 修改BlogController.java GetMapping(/hot)public Result queryHotBlog(RequestParam(value current, defaultValue 1) Integer current) {return blogService.queryHotBlog(current);}/*** param* return com.hmdp.dto.Result* description //查看笔记详情页面* param: id 笔记id* date 2023/2/17 22:03* author wty**/GetMapping(/{id})public Result queryBlogById(PathVariable(id) Long id) {return blogService.queryBlogById(id);}实现代码 修改接口IBlogService.java /*** p* 服务类* /p** author wty* since 2021-12-22*/ public interface IBlogService extends IServiceBlog {Result queryHotBlog(Integer current);/*** param* return com.hmdp.dto.Result* description //查看笔记详情页面* param: id 笔记id* date 2023/2/17 22:05* author wty**/Result queryBlogById(Long id); }修改BlogServiceImpl /*** p* 服务实现类* /p** author wty* since 2021-12-22*/ Service public class BlogServiceImpl extends ServiceImplBlogMapper, Blog implements IBlogService {Resourceprivate IUserService userService;Overridepublic Result queryHotBlog(Integer current) {// 根据用户查询PageBlog page query().orderByDesc(liked).page(new Page(current, SystemConstants.MAX_PAGE_SIZE));// 获取当前页数据ListBlog records page.getRecords();// 查询用户records.forEach(this::queryBlogUser);return Result.ok(records);}/*** param* return com.hmdp.dto.Result* description //查看笔记详情页面* param: id 笔记id* date 2023/2/17 22:07* author wty**/Overridepublic Result queryBlogById(Long id) {// 1.查询blogBlog blog getById(id);if (null blog) {return Result.fail(笔记不存在!);}// 2.查询blog相关的用户queryBlogUser(blog);return Result.ok(blog);}private void queryBlogUser(Blog blog) {Long userId blog.getUserId();User user userService.getById(userId);blog.setName(user.getNickName());blog.setIcon(user.getIcon());} }重启应用发现打开成功了详情页面 4.7.2.点赞功能 初始代码 GetMapping(/likes/{id}) public Result queryBlogLikes(PathVariable(id) Long id) {//修改点赞数量blogService.update().setSql(liked liked 1 ).eq(id,id).update();return Result.ok(); }问题分析这种方式会导致一个用户无限点赞明显是不合理的 造成这个问题的原因是我们现在的逻辑发起请求只是给数据库1所以才会出现这个问题。 完善点赞功能 需求 同一个用户只能点赞一次再次点击则取消点赞如果当前用户已经点赞则点赞按钮高亮显示前端已实现判断字段Blog类的isLike属性 实现步骤 给Blog类中添加一个isLike字段标示是否被当前用户点赞修改点赞功能利用Redis的set集合判断是否点赞过未点赞过则点赞数1已点赞过则点赞数-1修改根据id查询Blog的业务判断当前登录用户是否点赞过赋值给isLike字段修改分页查询Blog业务判断当前登录用户是否点赞过赋值给isLike字段 为什么采用set集合 因为我们的数据是不能重复的其次是一个集合。 具体步骤 1、在Blog 添加一个字段 TableField(exist false) private Boolean isLike;2、修改代码 BlogController.java PutMapping(/like/{id})public Result likeBlog(PathVariable(id) Long id) {// 修改点赞数量 update tb_blog set liked liked where id ?//blogService.update().setSql(liked liked 1).eq(id, id).update();return blogService.likeBlog(id);}修改IBlogService.java添加方法 /*** param* return com.hmdp.dto.Result* description //点赞* param: id* date 2023/2/17 22:32* author wty**/Result likeBlog(Long id);修改BlogServiceImpl.java Service public class BlogServiceImpl extends ServiceImplBlogMapper, Blog implements IBlogService {Resourceprivate IUserService userService;ResourceStringRedisTemplate stringRedisTemplate;Overridepublic Result queryHotBlog(Integer current) {// 根据用户查询PageBlog page query().orderByDesc(liked).page(new Page(current, SystemConstants.MAX_PAGE_SIZE));// 获取当前页数据ListBlog records page.getRecords();// 查询用户records.forEach(new ConsumerBlog() {Overridepublic void accept(Blog blog) {queryBlogUser(blog);isBlogLiked(blog);}});//records.forEach(this::queryBlogUser);return Result.ok(records);}/*** param* return com.hmdp.dto.Result* description //查看笔记详情页面* param: id 笔记id* date 2023/2/17 22:07* author wty**/Overridepublic Result queryBlogById(Long id) {// 1.查询blogBlog blog getById(id);if (null blog) {return Result.fail(笔记不存在!);}// 2.查询blog相关的用户queryBlogUser(blog);// 3.查询blog是否被点赞isBlogLiked(blog);return Result.ok(blog);}/*** param* return void* description //当前笔记是否被当前用户点赞* param: blog* date 2023/2/17 22:50* author wty**/private void isBlogLiked(Blog blog) {Long id blog.getId();// 1.获取当前登录用户Long userId UserHolder.getUser().getId();// 2.判断当前登录用户是否点赞 key blog:liked: idString key RedisConstants.BLOG_LIKED_KEY id;Boolean isMember stringRedisTemplate.opsForSet().isMember(key, userId.toString());boolean flag BooleanUtil.isTrue(isMember);blog.setIsLike(flag);}/*** param* return com.hmdp.dto.Result* description // 点赞* param: id* date 2023/2/17 22:32* author wty**/Overridepublic Result likeBlog(Long id) {// 1.获取当前登录用户Long userId UserHolder.getUser().getId();// 2.判断当前登录用户是否点赞 key blog:liked: idString key RedisConstants.BLOG_LIKED_KEY id;Boolean isMember stringRedisTemplate.opsForSet().isMember(key, userId.toString());boolean flag BooleanUtil.isTrue(isMember);if (!flag) {// 3.如果未点赞可以点赞// 3.1数据库点赞数1// 修改点赞数量 update tb_blog set liked liked where id ?boolean isSuccess update().setSql(liked liked 1).eq(id, id).update();if (isSuccess) {// 3.2保存用户到Redis的set集合stringRedisTemplate.opsForSet().add(key, userId.toString());}} else {// 4.如果已经点赞取消点赞// 4.1数据库点赞数 -1boolean isSuccess update().setSql(liked liked - 1).eq(id, id).update();if (isSuccess) {// 4.2把用户从Redis的set集合移除stringRedisTemplate.opsForSet().remove(key, userId.toString());}}return null;}private void queryBlogUser(Blog blog) {Long userId blog.getUserId();User user userService.getById(userId);blog.setName(user.getNickName());blog.setIcon(user.getIcon());} } 重启应用点赞后这里感觉前端代码有点问题点赞后还需要刷新一下页面才会显示高亮。 这里点赞和取消赞后前端会报错会的小伙伴帮忙解决一下。 4.7.3.基于List实现点赞用户列表TOP10 4.7.4.基于SortedSet实现点赞排行榜 在探店笔记的详情页面应该把给该笔记点赞的人显示出来比如最早点赞的TOP5形成点赞排行榜 之前的点赞是放到set集合但是set集合是不能排序的所以这个时候咱们可以采用一个可以排序的set集合就是咱们的sortedSet 我们接下来来对比一下这些集合的区别是什么 所有点赞的人需要是唯一的所以我们应当使用set或者是sortedSet 其次我们需要排序就可以直接锁定使用sortedSet啦 这里注意sortedSet中判断一个元素是否存在是没有像set集合那样的SISMEMBER的那我们怎么判断呢这里可以用ZSCORE获取sorted set中的指定元素的score值如果能获取到说明存在获取不到说明不存在。 修改代码 BlogServiceImpl 点赞逻辑代码 Service public class BlogServiceImpl extends ServiceImplBlogMapper, Blog implements IBlogService {Resourceprivate IUserService userService;ResourceStringRedisTemplate stringRedisTemplate;Overridepublic Result queryHotBlog(Integer current) {// 根据用户查询PageBlog page query().orderByDesc(liked).page(new Page(current, SystemConstants.MAX_PAGE_SIZE));// 获取当前页数据ListBlog records page.getRecords();// 查询用户records.forEach(new ConsumerBlog() {Overridepublic void accept(Blog blog) {queryBlogUser(blog);isBlogLiked(blog);}});//records.forEach(this::queryBlogUser);return Result.ok(records);}/*** param* return com.hmdp.dto.Result* description //查看笔记详情页面* param: id 笔记id* date 2023/2/17 22:07* author wty**/Overridepublic Result queryBlogById(Long id) {// 1.查询blogBlog blog getById(id);if (null blog) {return Result.fail(笔记不存在!);}// 2.查询blog相关的用户queryBlogUser(blog);// 3.查询blog是否被点赞isBlogLiked(blog);return Result.ok(blog);}/*** param* return void* description //当前笔记是否被当前用户点赞(Set)* param: blog* date 2023/2/17 22:50* author wty**/private void isBlogLikedSet(Blog blog) {Long id blog.getId();// 1.获取当前登录用户Long userId UserHolder.getUser().getId();if (null userId) {return;}// 2.判断当前登录用户是否点赞 key blog:liked: idString key RedisConstants.BLOG_LIKED_KEY id;Boolean isMember stringRedisTemplate.opsForSet().isMember(key, userId.toString());boolean flag BooleanUtil.isTrue(isMember);blog.setIsLike(flag);}/*** param* return void* description //当前笔记是否被当前用户点赞(SortedSet)* param: blog* date 2023/2/17 22:50* author wty**/private void isBlogLiked(Blog blog) {Long id blog.getId();// 1.获取当前登录用户Long userId UserHolder.getUser().getId();if (null userId) {return;}// 2.判断当前登录用户是否点赞 key blog:liked: idString key RedisConstants.BLOG_LIKED_KEY id;Double score stringRedisTemplate.opsForZSet().score(key, userId.toString());blog.setIsLike(null ! score);}/*** param* return com.hmdp.dto.Result* description // 点赞(Set集合)* param: id* date 2023/2/17 22:32* author wty**/public Result likeBlogSet(Long id) {// 1.获取当前登录用户Long userId UserHolder.getUser().getId();// 2.判断当前登录用户是否点赞 key blog:liked: idString key RedisConstants.BLOG_LIKED_KEY id;Boolean isMember stringRedisTemplate.opsForSet().isMember(key, userId.toString());boolean flag BooleanUtil.isTrue(isMember);if (!flag) {// 3.如果未点赞可以点赞// 3.1数据库点赞数1// 修改点赞数量 update tb_blog set liked liked where id ?boolean isSuccess update().setSql(liked liked 1).eq(id, id).update();if (isSuccess) {// 3.2保存用户到Redis的set集合stringRedisTemplate.opsForSet().add(key, userId.toString());}} else {// 4.如果已经点赞取消点赞// 4.1数据库点赞数 -1boolean isSuccess update().setSql(liked liked - 1).eq(id, id).update();if (isSuccess) {// 4.2把用户从Redis的set集合移除stringRedisTemplate.opsForSet().remove(key, userId.toString());}}return null;}/*** param* return com.hmdp.dto.Result* description // 点赞(SortedSet集合)* param: id* date 2023/2/17 22:32* author wty**/Overridepublic Result likeBlog(Long id) {// 1.获取当前登录用户Long userId UserHolder.getUser().getId();// 2.判断当前登录用户是否点赞 key blog:liked: idString key RedisConstants.BLOG_LIKED_KEY id;Double score stringRedisTemplate.opsForZSet().score(key, userId.toString());if (null score) {// 3.如果未点赞可以点赞// 3.1数据库点赞数1// 修改点赞数量 update tb_blog set liked liked where id ?boolean isSuccess update().setSql(liked liked 1).eq(id, id).update();if (isSuccess) {// 3.2保存用户到Redis的sortedset集合 zadd key score memberstringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());}} else {// 4.如果已经点赞取消点赞// 4.1数据库点赞数 -1boolean isSuccess update().setSql(liked liked - 1).eq(id, id).update();if (isSuccess) {// 4.2把用户从Redis的set集合移除stringRedisTemplate.opsForZSet().remove(key, userId.toString());}}return null;}private void queryBlogUser(Blog blog) {Long userId blog.getUserId();User user userService.getById(userId);blog.setName(user.getNickName());blog.setIcon(user.getIcon());} } 重启应用删除redis中的liked的元素然后点赞 看redis中存储成功 再点击一下取消点赞 看redis中也没有了。 点赞列表查询列表 修改BlogController GetMapping(/likes/{id})public Result queryBlogLikes(PathVariable(id) Long id) {return blogService.queryBlogLikes(id);}修改接口BlogServiceImpl.java /*** param* return com.hmdp.dto.Result* description //查询点赞的列表* param: id* date 2023/2/18 1:10* author wty**/Result queryBlogLikes(Long id);修改BlogService /*** param* return com.hmdp.dto.Result* description //查询点赞列表* param: id* date 2023/2/18 1:11* author wty**/Overridepublic Result queryBlogLikes(Long id) {String key RedisConstants.BLOG_LIKED_KEY id;// 1.查询top5的点赞用户 zrange key 0 4SetString top5 stringRedisTemplate.opsForZSet().range(key, 0, 4);if (null top5 || top5.isEmpty()) {return Result.ok(Collections.emptyList());}// 2.解析出其中的用户idListLong ids top5.stream().map(Long::valueOf).collect(Collectors.toList());// 3.根据用户id查询用户ListUserDTO userDTOS userService.listByIds(ids).stream().map(user - BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());// 4.返回return Result.ok(userDTOS);}重启应用 加载出列表 再用另一个号点赞发现后来的人点赞顺序竟然是第一个这明显是顺序反了 原因是sql语句 我们放到sqlyog中查询一下发现顺序确实会重排 sql如下即可解决 SELECT * FROM tb_user WHERE id IN (5,1) ORDER BY FIELD(id,5,1);调整代码BlogServiceImpl.java // 3.根据用户id查询用户//ListUserDTO userDTOS userService.listByIds(ids).stream().map(user - BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());ListUserDTO userDTOS userService.query()// SELECT * FROM tb_user WHERE id IN (5,1) ORDER BY FIELD(id,5,1);.in(id, ids).last(order by field (id, idsStr ) ).list().stream().map(user - BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());重启应用发现点赞顺序已经调整了 4.8.关注列表 4.8.1.关注列表实现原理 4.8.2.添加关注 1. 好友关注-关注和取消关注 针对用户的操作可以对用户进行关注和取消关注功能。 实现思路 需求基于该表数据结构实现两个接口 关注和取关接口判断是否关注的接口 关注是User之间的关系是博主与粉丝的关系数据库中有一张tb_follow表来标示 注意: 这里需要把主键修改为自增长简化开发。 修改FollowController RestController RequestMapping(/follow) public class FollowController {Autowiredprivate IFollowService iFollowService;/*** param* return com.hmdp.dto.Result* description //关注博主* param: followUserId* param: isFollow* date 2023/2/18 10:54* author wty**/PutMapping(/{id}/{isFollow})public Result follow(PathVariable(id) Long followUserId, PathVariable(isFollow) boolean isFollow) {return iFollowService.follow(followUserId, isFollow);}/*** param* return com.hmdp.dto.Result* description //判断是否关注* param: followUserId* date 2023/2/18 10:54* author wty**/GetMapping(/or/not/{id})public Result isFollow(PathVariable(id) Long followUserId) {return iFollowService.isFollow(followUserId);} }修改接口IFollowService.java public interface IFollowService extends IServiceFollow {Result follow(Long followUserId, boolean isFollow);Result isFollow(Long followUserId); }修改实现类FollowServiceImpl.java Service public class FollowServiceImpl extends ServiceImplFollowMapper, Follow implements IFollowService {/*** param* return com.hmdp.dto.Result* description //关注* param: id* param: isFollow* date 2023/2/18 10:58* author wty**/Overridepublic Result follow(Long followUserId, boolean isFollow) {Long userId UserHolder.getUser().getId();// 1.判断是关注还是取关if (isFollow) {// 2.关注新增数据Follow follow new Follow();follow.setFollowUserId(followUserId);follow.setUserId(userId);// insert into tb_follow values()save(follow);} else {// 3.取关删除// delete from follow where user_id ? and follow_user_id ?LambdaQueryWrapperFollow wrapper new LambdaQueryWrapper();remove(wrapper.eq(Follow::getUserId, userId).eq(Follow::getFollowUserId, followUserId));}return Result.ok();}/*** param* return com.hmdp.dto.Result* description //判断是否关注* param: id* param: followUserId* date 2023/2/18 10:58* author wty**/Overridepublic Result isFollow(Long followUserId) {// 1.获取用户idLong userId UserHolder.getUser().getId();// 2.查询是否关注 select * from tb_follow where user_id ? and follow_user_id ?Integer count query().eq(user_id, userId).eq(follow_user_id, followUserId).count();return Result.ok(count 0);} } 重启应用点击关注 查看数据库 4.8.3.共同关注列表 想要去看共同关注的好友需要首先进入到这个页面这个页面会发起两个请求 1、去查询用户的详情 2、去查询用户的笔记 以上两个功能和共同关注没有什么关系大家可以自行将笔记中的代码拷贝到idea中就可以实现这两个功能了我们的重点在于共同关注功能。 // UserController 根据id查询用户 GetMapping(/{id}) public Result queryUserById(PathVariable(id) Long userId){// 查询详情User user userService.getById(userId);if (user null) {return Result.ok();}UserDTO userDTO BeanUtil.copyProperties(user, UserDTO.class);// 返回return Result.ok(userDTO); }// BlogController 根据id查询博主的探店笔记 GetMapping(/of/user) public Result queryBlogByUserId(RequestParam(value current, defaultValue 1) Integer current,RequestParam(id) Long id) {// 根据用户查询PageBlog page blogService.query().eq(user_id, id).page(new Page(current, SystemConstants.MAX_PAGE_SIZE));// 获取当前页数据ListBlog records page.getRecords();return Result.ok(records); }完成后重启应用点击头像 进入后可以查看详情 点击共同关注报错 接下来我们来看看共同关注如何实现 需求利用Redis中恰当的数据结构实现共同关注功能。在博主个人页面展示出当前用户与博主的共同关注呢。 当然是使用我们之前学习过的set集合咯在set集合中有交集并集补集的api我们可以把两人的关注的人分别放入到一个set集合中然后再通过api去查看这两个set集合中的交集数据。 我们先来改造当前的关注列表 改造原因是因为我们需要在用户关注了某位用户后需要将数据放入到set集合中方便后续进行共同关注同时当取消关注时也需要从set集合中进行删除 将关注的博主放入redis的set列表中 FollowServiceImpl /*** param* return com.hmdp.dto.Result* description //关注* param: id* param: isFollow* date 2023/2/18 10:58* author wty**/Overridepublic Result follow(Long followUserId, boolean isFollow) {Long userId UserHolder.getUser().getId();String key RedisConstants.FOLLOW_USER_LIST userId;// 1.判断是关注还是取关if (isFollow) {// 2.关注新增数据Follow follow new Follow();follow.setFollowUserId(followUserId);follow.setUserId(userId);// insert into tb_follow values()boolean isSuccess save(follow);// 加入Redis,实现共同关注if (isSuccess) {// 把关注用户的id加入redis的set集合 sadd userId followUserIdstringRedisTemplate.opsForSet().add(key, followUserId.toString());}} else {// 3.取关删除// delete from follow where user_id ? and follow_user_id ?LambdaQueryWrapperFollow wrapper new LambdaQueryWrapper();boolean isSuccess remove(wrapper.eq(Follow::getUserId, userId).eq(Follow::getFollowUserId, followUserId));if (isSuccess) {// 从Redis中移除,实现取关// 把关注用户的id从redis的set集合中移除 srem userId followUserIdstringRedisTemplate.opsForSet().remove(key, followUserId.toString());}}return Result.ok();}重启应用取消关注后重新关注 查看redis 查看数据库 再登录一个用户去关注前2个账号 关注完后看redis保存成功 看数据库也保存成功 下面实现共同关注的功能 具体的关注代码 修改FollowController.java /*** param* return com.hmdp.dto.Result* description //查询共同关注* param: id* date 2023/2/18 13:12* author wty**/GetMapping(/common/{id})public Result followCommons(PathVariable(id) Long id) {return iFollowService.followCommons(id);}修改接口 Result followCommons(Long id);修改实现类FollowServiceImpl.java /*** param* return com.hmdp.dto.Result* description //共同关注* param: id* date 2023/2/18 13:13* author wty**/Overridepublic Result followCommons(Long id) {// 1.获取当前用户Long userId UserHolder.getUser().getId();String key RedisConstants.FOLLOW_USER_LIST userId;// 目标用户String keyFollow RedisConstants.FOLLOW_USER_LIST id;// 2.求交集SetString intersect stringRedisTemplate.opsForSet().intersect(key, keyFollow);if (null intersect || intersect.isEmpty()) {return Result.ok(Collections.emptyList());}// 3.解析id集合ListLong ids intersect.stream().map(Long::valueOf).collect(Collectors.toList());ListUserDTO userDTOS userService.listByIds(ids).stream().map(user - BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());// 查询用户return Result.ok(userDTOS);}重启应用此时已经可以查看共同关注了。 查看redis也存储成功了。 4.8.4.取消关注 1.好友关注-Feed流实现方案 当我们关注了用户后这个用户发了动态那么我们应该把这些数据推送给用户这个需求其实我们又把他叫做Feed流关注推送也叫做Feed流直译为投喂。为用户持续的提供“沉浸式”的体验通过无限下拉刷新获取新的信息。 对于传统的模式的内容解锁我们是需要用户去通过搜索引擎或者是其他的方式去解锁想要看的内容 对于新型的Feed流的的效果不需要我们用户再去推送信息而是系统分析用户到底想要什么然后直接把内容推送给用户从而使用户能够更加的节约时间不用主动去寻找。 Feed流的实现有两种模式 Feed流产品有两种常见模式 Timeline不做内容筛选简单的按照内容发布时间排序常用于好友或关注。例如朋友圈 优点信息全面不会有缺失。并且实现也相对简单缺点信息噪音较多用户不一定感兴趣内容获取效率低 智能排序利用智能算法屏蔽掉违规的、用户不感兴趣的内容。推送用户感兴趣信息来吸引用户 优点投喂用户感兴趣信息用户粘度很高容易沉迷缺点如果算法不精准可能起到反作用 本例中的个人页面是基于关注的好友来做Feed流因此采用Timeline的模式。该模式的实现方案有三种 我们本次针对好友的操作采用的就是Timeline的方式只需要拿到我们关注用户的信息然后按照时间排序即可 因此采用Timeline的模式。该模式的实现方案有三种 拉模式推模式推拉结合 拉模式也叫做读扩散 该模式的核心含义就是当张三和李四和王五发了消息后都会保存在自己的邮箱中假设赵六要读取信息那么他会从读取他自己的收件箱此时系统会从他关注的人群中把他关注人的信息全部都进行拉取然后在进行排序 优点比较节约空间因为赵六在读信息时并没有重复读取而且读取完之后可以把他的收件箱进行清楚。 缺点比较延迟当用户读取数据时才去关注的人里边去读取数据假设用户关注了大量的用户那么此时就会拉取海量的内容对服务器压力巨大。 推模式也叫做写扩散。 推模式是没有写邮箱的当张三写了一个内容此时会主动的把张三写的内容发送到他的粉丝收件箱中去假设此时李四再来读取就不用再去临时拉取了 优点时效快不用临时拉取 缺点内存压力大假设一个大V写信息很多人关注他 就会写很多分数据到粉丝那边去 推拉结合模式也叫做读写混合兼具推和拉两种模式的优点。 推拉模式是一个折中的方案站在发件人这一段如果是个普通的人那么我们采用写扩散的方式直接把数据写入到他的粉丝中去因为普通的人他的粉丝关注量比较小所以这样做没有压力如果是大V那么他是直接将数据先写入到一份到发件箱里边去然后再直接写一份到活跃粉丝收件箱里边去现在站在收件人这端来看如果是活跃粉丝那么大V和普通的人发的都会直接写入到自己收件箱里边来而如果是普通的粉丝由于他们上线不是很频繁所以等他们上线时再从发件箱里边去拉信息。 4.8.5.探店推送功能 1. 好友关注-推送到粉丝收件箱 需求 1.修改新增探店笔记的业务在保存blog到数据库的同时推送到粉丝的收件箱2.收件箱满足可以根据时间戳排序必须用Redis的数据结构实现3.查询收件箱数据时可以实现分页查询 Feed流中的数据会不断更新所以数据的角标也在变化因此不能采用传统的分页模式。 传统的分页在feed流是不适用的因为我们的数据会随时发生变化。 假设在t1 时刻我们去读取第一页此时page 1 size 5 那么我们拿到的就是10 ~ 6 这几条记录假设现在t2时候又发布了一条记录此时t3 时刻我们来读取第二页读取第二页传入的参数是page2 size5 那么此时读取到的第二页实际上是从6 开始然后是6~2 那么我们就读取到了重复的数据所以feed流的分页不能采用原始方案来做。 Feed流的滚动分页 我们需要记录每次操作的最后一条然后从这个位置开始去读取数据 举个例子我们从t1时刻开始拿第一页数据拿到了10~6然后记录下当前最后一次拿取的记录就是6t2时刻发布了新的记录此时这个11放到最顶上但是不会影响我们之前记录的6此时t3时刻来拿第二页第二页这个时候拿数据还是从6后一点的5去拿就拿到了5-1的记录。我们这个地方可以采用sortedSet来做可以进行范围查询并且还可以记录当前获取数据时间戳最小值就可以实现滚动分页了 核心的意思就是我们在保存完探店笔记后获得到当前笔记的粉丝然后把数据推送到粉丝的redis中去。 修改BlogController.java PostMappingpublic Result saveBlog(RequestBody Blog blog) {return blogService.saveBlog(blog);}修改接口IBlogService.java Result saveBlog(Blog blog);接口实现类BlogServiceImpl.java /*** param* return com.hmdp.dto.Result* description //使用push模式* param: blog* date 2023/2/18 13:54* author wty**/Overridepublic Result saveBlog(Blog blog) {// 1.获取登录用户UserDTO user UserHolder.getUser();blog.setUserId(user.getId());// 2.保存探店博文boolean isSuccess save(blog);if (!isSuccess) {return Result.fail(新增笔记失败,请重新发布!);}// 3.查询笔记作者的所有粉丝// select user_id from tb_follow where follow_user_id ?LambdaQueryWrapperFollow wrapper new LambdaQueryWrapper();wrapper.eq(Follow::getFollowUserId, user.getId());ListFollow follows followService.list(wrapper);// 4.推送笔记id给所有粉丝for (Follow follow : follows) {// 4.1获取粉丝idLong fansId follow.getUserId();// 4.2推送给粉丝String key RedisConstants.FEED_KEY fansId;stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMillis());}// 5.返回idreturn Result.ok(blog.getId());}重启应用 用可可登录发布博客 发布成功 看redis,可可的粉丝收到了消息 2.好友关注-实现分页查询收邮箱 需求在个人主页的“关注”卡片中查询并展示推送的Blog信息 具体操作如下 1、每次查询完成后我们要分析出查询出数据的最小时间戳这个值会作为下一次查询的条件 2、我们需要找到与上一次查询相同的查询个数作为偏移量下次查询时跳过这些查询过的数据拿到我们需要的数据 综上我们的请求参数中就需要携带 lastId上一次查询的最小时间戳 和偏移量这两个参数。 这两个参数第一次会由前端来指定以后的查询就根据后台结果作为条件再次传递到后台。 先来熟悉一下sortedset的指令 打开命令窗口 zadd z1 1 m1 2 m2 3 m3 4 m4 5 m5 6 m6 zadd z1 7 m7 8 m8 9 m9 ZRANGEBYSCORE z1 0 8存放了9个元素 假如现在我们想倒叙排列 ZREVRANGE z1 0 9如果还想带上分数呢 ZREVRANGE z1 0 9 WITHSCORES按照分数查询,模拟分页 比如以score 4为分水岭分割成2页 ZREVRANGEBYSCORE z1 1000 0 WITHSCORES LIMIT 0 3此时来插入了一条数据 ZADD z1 10 m10输入以下指令 注意这里的7是上次查询的最小额score ZREVRANGEBYSCORE z1 7 0 WITHSCORES LIMIT 1 3现在我们把redis中2个值更改为一样的看看会怎么样。 打开命令窗口运行以下命令 ZREVRANGEBYSCORE z1 1000 0 WITHSCORES LIMIT 0 5接着查询下一页 ZREVRANGEBYSCORE z1 6 0 WITHSCORES LIMIT 1 5那正确应该写为 ZREVRANGEBYSCORE z1 6 0 WITHSCORES LIMIT 2 5最后总结规律 滚动分页 -- 第一次 ZREVRANGEBYSCORE key 设定一个最大值 0 WITHSCORES LIMIT 0 每页展示几条 -- 之后 ZREVRANGEBYSCORE key 第一条最小的角标 0 WITHSCORES LIMIT 第一页中与最小值相等的元素的个数 每页展示几条一、定义出来具体的返回值实体类 新增实体类ScrollResult Data public class ScrollResult {private List? list;private Long minTime;private Integer offset; }BlogController 注意RequestParam 表示接受url地址栏传参的注解当方法上参数的名称和url地址栏不相同时可以通过RequestParam 来进行指定 GetMapping(/of/follow) public Result queryBlogOfFollow(RequestParam(lastId) Long max, RequestParam(value offset, defaultValue 0) Integer offset){return blogService.queryBlogOfFollow(max, offset); }接口IBlogService.java中添加 /*** param* return com.hmdp.dto.Result* description //滚动分页查询关注列表* param: max* param: offset* date 2023/2/18 16:53* author wty**/Result queryBlogOfFollow(Long max, Integer offset);BlogServiceImpl /*** param* return com.hmdp.dto.Result* description //滚动分页查询关注列表* param: max* param: offset* date 2023/2/18 16:54* author wty**/Overridepublic Result queryBlogOfFollow(Long max, Integer offset) {// 1.查询当前用户Long userId UserHolder.getUser().getId();// 2.找到收件箱String key RedisConstants.FEED_KEY userId;// 滚动分页查询(第一次查询)//ZREVRANGEBYSCORE z1 1000 0 WITHSCORES LIMIT 0 5SetZSetOperations.TypedTupleString set stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 2L);// 3.非空判断if (null set || set.isEmpty()) {return Result.ok();}// 4.解析收件箱的数据:blogId,minTime(时间戳),offsetArrayListLong ids new ArrayList(set.size());long minTime Long.MAX_VALUE;int off 1;for (ZSetOperations.TypedTupleString tuple : set) {// 4.1获取idids.add(Long.valueOf(tuple.getValue()));// 4.2 获取分数(时间戳)long time tuple.getScore().longValue();if (time minTime) {off;} else {minTime minTime tuple.getScore().longValue() ? minTime : time;off 1;}}//ListBlog blogs listByIds(ids);// select * from tb_blog where id in () order by field(id,max,min)String joinStr StrUtil.join(,, ids);// 5.根据blogId查询blogListBlog blogs query().in(id, ids).last(order by field(id, joinStr )).list();for (Blog blog : blogs) {// 2.查询blog相关的用户queryBlogUser(blog);// 3.查询blog是否被点赞isBlogLiked(blog);}// 6.封装并返回ScrollResult result new ScrollResult();result.setList(blogs);result.setOffset(off);result.setMinTime(minTime);return Result.ok(result);}重启应用看到可可发的动态了 再换一个人关注发个动态一样也查询到了 滚动分页也实现了
文章转载自:
http://www.morning.nqwz.cn.gov.cn.nqwz.cn
http://www.morning.fxjnn.cn.gov.cn.fxjnn.cn
http://www.morning.xdttq.cn.gov.cn.xdttq.cn
http://www.morning.nkyqh.cn.gov.cn.nkyqh.cn
http://www.morning.mfbcs.cn.gov.cn.mfbcs.cn
http://www.morning.chzbq.cn.gov.cn.chzbq.cn
http://www.morning.dxtxk.cn.gov.cn.dxtxk.cn
http://www.morning.dnhdp.cn.gov.cn.dnhdp.cn
http://www.morning.wnkjb.cn.gov.cn.wnkjb.cn
http://www.morning.xqqcq.cn.gov.cn.xqqcq.cn
http://www.morning.hlppp.cn.gov.cn.hlppp.cn
http://www.morning.nppml.cn.gov.cn.nppml.cn
http://www.morning.tktcr.cn.gov.cn.tktcr.cn
http://www.morning.mgfnt.cn.gov.cn.mgfnt.cn
http://www.morning.crkmm.cn.gov.cn.crkmm.cn
http://www.morning.jgcyn.cn.gov.cn.jgcyn.cn
http://www.morning.fwzjs.cn.gov.cn.fwzjs.cn
http://www.morning.nqrdx.cn.gov.cn.nqrdx.cn
http://www.morning.qnwyf.cn.gov.cn.qnwyf.cn
http://www.morning.brwp.cn.gov.cn.brwp.cn
http://www.morning.wrtxk.cn.gov.cn.wrtxk.cn
http://www.morning.clbsd.cn.gov.cn.clbsd.cn
http://www.morning.ntgrn.cn.gov.cn.ntgrn.cn
http://www.morning.cpfbg.cn.gov.cn.cpfbg.cn
http://www.morning.jbgzy.cn.gov.cn.jbgzy.cn
http://www.morning.sbrjj.cn.gov.cn.sbrjj.cn
http://www.morning.jlktz.cn.gov.cn.jlktz.cn
http://www.morning.jgttx.cn.gov.cn.jgttx.cn
http://www.morning.ryfpx.cn.gov.cn.ryfpx.cn
http://www.morning.fjscr.cn.gov.cn.fjscr.cn
http://www.morning.gfnsh.cn.gov.cn.gfnsh.cn
http://www.morning.lbxcc.cn.gov.cn.lbxcc.cn
http://www.morning.bklhx.cn.gov.cn.bklhx.cn
http://www.morning.rwjtf.cn.gov.cn.rwjtf.cn
http://www.morning.srjgz.cn.gov.cn.srjgz.cn
http://www.morning.mxnrl.cn.gov.cn.mxnrl.cn
http://www.morning.wckrl.cn.gov.cn.wckrl.cn
http://www.morning.mhnxs.cn.gov.cn.mhnxs.cn
http://www.morning.cfnsn.cn.gov.cn.cfnsn.cn
http://www.morning.thrtt.cn.gov.cn.thrtt.cn
http://www.morning.trmpj.cn.gov.cn.trmpj.cn
http://www.morning.nfzzf.cn.gov.cn.nfzzf.cn
http://www.morning.dnbkz.cn.gov.cn.dnbkz.cn
http://www.morning.wjwfj.cn.gov.cn.wjwfj.cn
http://www.morning.qsmch.cn.gov.cn.qsmch.cn
http://www.morning.gydth.cn.gov.cn.gydth.cn
http://www.morning.mdmxf.cn.gov.cn.mdmxf.cn
http://www.morning.pmdnx.cn.gov.cn.pmdnx.cn
http://www.morning.rqqkc.cn.gov.cn.rqqkc.cn
http://www.morning.jrgxx.cn.gov.cn.jrgxx.cn
http://www.morning.rgwrl.cn.gov.cn.rgwrl.cn
http://www.morning.jwtwf.cn.gov.cn.jwtwf.cn
http://www.morning.tkrwm.cn.gov.cn.tkrwm.cn
http://www.morning.srndk.cn.gov.cn.srndk.cn
http://www.morning.swimstaracademy.cn.gov.cn.swimstaracademy.cn
http://www.morning.plnry.cn.gov.cn.plnry.cn
http://www.morning.wqngt.cn.gov.cn.wqngt.cn
http://www.morning.trjdr.cn.gov.cn.trjdr.cn
http://www.morning.yngtl.cn.gov.cn.yngtl.cn
http://www.morning.yesidu.com.gov.cn.yesidu.com
http://www.morning.dmwjl.cn.gov.cn.dmwjl.cn
http://www.morning.gglhj.cn.gov.cn.gglhj.cn
http://www.morning.sqfnx.cn.gov.cn.sqfnx.cn
http://www.morning.bdsyu.cn.gov.cn.bdsyu.cn
http://www.morning.hwlmy.cn.gov.cn.hwlmy.cn
http://www.morning.wknjy.cn.gov.cn.wknjy.cn
http://www.morning.fthcq.cn.gov.cn.fthcq.cn
http://www.morning.rnyhx.cn.gov.cn.rnyhx.cn
http://www.morning.qcwck.cn.gov.cn.qcwck.cn
http://www.morning.wslpk.cn.gov.cn.wslpk.cn
http://www.morning.btlsb.cn.gov.cn.btlsb.cn
http://www.morning.srsln.cn.gov.cn.srsln.cn
http://www.morning.npqps.cn.gov.cn.npqps.cn
http://www.morning.srrzb.cn.gov.cn.srrzb.cn
http://www.morning.rmfwh.cn.gov.cn.rmfwh.cn
http://www.morning.jmbgl.cn.gov.cn.jmbgl.cn
http://www.morning.wdprz.cn.gov.cn.wdprz.cn
http://www.morning.hqrkq.cn.gov.cn.hqrkq.cn
http://www.morning.iqcge.com.gov.cn.iqcge.com
http://www.morning.mcjrf.cn.gov.cn.mcjrf.cn
http://www.tj-hxxt.cn/news/277176.html

相关文章:

  • 阿里云个人网站制作兰州网络营销策划公司排名
  • 工业信息化部网站备案做网站需要哪些条件
  • 朔州seo网站建设百度热词指数
  • 网站和后台怎么做网站流量竞品分析
  • ps模板素材网站一般购物网站怎么做推广
  • 中国建设银行互联网网站seo网站做推广
  • 上海市城市建设管理局网站网站建设论文 优帮云
  • 搭建网站软件做原创视频网站
  • 服装网站建设运营规划太仓有专门做网站的地方吗
  • 上哪儿找做网站的客户互联网营销型网站
  • 哪家做网站响应式网站报价
  • 只用html5可以做网站吗西安网页制作工作室
  • 门户网站 销售建设二手商品网站总结
  • 中山手机网站建设费用商城网站建设net2006
  • 黄页推广引流网站洛阳市做网站的
  • ui设计的网站有哪些斗鱼网站开发是用什么语言
  • 网站架设工具wordpress不能翻页
  • 如果是创建的网站在线购物商城系统
  • 网站开发建设价格怎么做免费网站教程
  • ps怎么做网站的广告条网店营销策划书
  • 网站优化外包找谁镇江网站设计多少钱
  • 广州建设行业信息网站虹桥网站建设
  • 济南槐荫网站开发公司网站内容管理系统下载
  • 新手初做网站樱桃企业网站管理系统v1.1-cms
  • 大型门户网站是这样炼成的源代码网页制作与设计类课程
  • 邓州微网站建设昆网站在哪里
  • 网站 html 作用wordpress给图片加链接
  • 服装公司 网站怎么做资讯网站做app
  • 定制网站建设功能报价表模板购物咨询主题WordPress
  • 网站下载的app删除了怎么找到做网站全屏尺寸是多少钱