黑马点评-好友关注

 关注和取关

实现关注和取关会通过同一个接口实现/api/follow/2/true,2是被关注人的用户Id,true表示关注,false表示取关

再实现关注和取关之前是要实现一个判断是否关注用户的接口/follow/or/not/2

实现关注和取关的用户表有一下字段,注意id应该处理为自增长

!!为什么这里选择数据库表去处理关注和取关的功能,不选择redis中的set数据结构来存储?

Set是一个无序的集合,里面的元素是不重复的,支持交集、并集、差集等操作。每个用户可以用一个set来存储他们的关注列表,另一个set存储粉丝列表。例如,用户A关注用户B,那么可以在user:A:following这个set中添加用户B的ID,同时在user:B:followers这个set中添加用户A的ID。这样,关注和取关操作可以通过SADD和SREM命令来实现,非常快速,而且可以确保不重复。

1.在数据持久化存储和数据量方面:Redis默认是内存存储,虽然有持久化机制(如RDB和AOF),但在极端情况下可能会有数据丢失的风险。而数据库的数据持久化更可靠,事务支持也更完善。在大数据量的情况下,Redis的内存占用会变得很高,需要考虑成本问题。而数据库表存储在磁盘上,虽然查询可能慢一些,但存储成本相对较低。

2.除了关注和取关之外,还需要复杂的查询,比如共同关注、推荐关注等,Redis的set操作可以直接用SINTER来求交集,非常方便。而数据库可能需要复杂的JOIN操作或者多次查询才能实现,效率可能不如Redis。

创建两个方法接口

@RestController
@RequestMapping("/follow")
public class FollowController {

    @Resource
    private IFollowService followService;

    @PutMapping("/{id}/{isFollow}")
    public Result follow(@PathVariable("id") Long followUserId, @PathVariable("isFollow") Boolean isFollow) {
        return followService.follow(followUserId, isFollow);
    }

    @GetMapping("/or/not/{id}")
    public Result isFollow(@PathVariable("id") Long followUserId) {
        return followService.isFollow(followUserId);
    }
}

service层方法实现类

public interface IFollowService extends IService<Follow> {

    Result follow(Long followUserId, Boolean isFollow);

    Result isFollow(Long followUserId);
}

实现关注和取关

    @Override
    public Result follow(Long followUserId, Boolean isFollow) {
        //1.拿到登录用户id
        Long userId = UserHolder.getUser().getId();

        //2.判断关注还是取关
        if(isFollow){
            //关注就返回成功信息,并写入数据库表
            Follow follow = new Follow();
            follow.setUserId(userId);
            follow.setFollowUserId(followUserId);
            save(follow);
        }else{
            //取关就删除数据库表的数据, delete from tb_follow where user_id = ? and follow_user_Id = ?
            //表示删除时应该两个id等于传入的两个用户id
            remove(new QueryWrapper<Follow>().eq("user_id", userId).eq("follow_user_Id", followUserId));

        }
        return Result.ok();
    }

 实现查询是否关注

    @Override
    public Result isFollow(Long followUserId) {
        // 1.获取登录用户
        Long userId = UserHolder.getUser().getId();
        // 2.查询是否关注 select count(*) from tb_follow where user_id = ? and follow_user_id = ?
        //并不需要查出存在不存在,只需要用计数器即可,关注了就>0
        Integer count = query().eq("user_id", userId).eq("follow_user_id", followUserId).count();
        // 3.判断
        return Result.ok(count > 0);
    }

共同关注

必须要实现两个功能才能进入用户的主页,一个是用户的个人信息功能接口/user/{id},一个是查看当前用户的笔记功能接口/blog/of/user

    @GetMapping("/of/user")
    public Result queryBlogByUserId(
            @RequestParam(value = "current", defaultValue = "1") Integer current,
            @RequestParam("id") Long id) {
        // 根据用户查询
        Page<Blog> page = blogService.query()
                .eq("user_id", id).page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));
        // 获取当前页数据
        List<Blog> records = page.getRecords();
        return Result.ok(records);
    }
    @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);
    }

共同关注的功能接口是/follow/common/{id},即你当前查看用户id的共同关注 

使用set数据结构可以实现交集、并集等功能,而共同关注即两个用户关注的交集部分

首先,修改关注和取关的逻辑,采取redis中set数据结构,在实现写入数据库的同时,将被关注用户Id放入set集合中,key采用关注用户id

    @Override
    public Result follow(Long followUserId, Boolean isFollow) {
        // 1.获取登录用户
        Long userId = UserHolder.getUser().getId();
        String key = "follows:" + userId;
        // 1.判断到底是关注还是取关
        if (isFollow) {
            // 2.关注,新增数据
            Follow follow = new Follow();
            follow.setUserId(userId);
            follow.setFollowUserId(followUserId);
            boolean isSuccess = save(follow);
            //若写入数据库成功的同时,将id放入redis的set集合
            if (isSuccess) {
                // 把关注用户的id,放入redis的set集合 sadd userId followerUserId
                stringRedisTemplate.opsForSet().add(key, followUserId.toString());
            }
        } else {
            // 3.取关,删除 delete from tb_follow where user_id = ? and follow_user_id = ?
            boolean isSuccess = remove(new QueryWrapper<Follow>()
                    .eq("user_id", userId).eq("follow_user_id", followUserId));
            if (isSuccess) {
                // 把关注用户的id从Redis集合中移除
                stringRedisTemplate.opsForSet().remove(key, followUserId.toString());
            }
        }
        return Result.ok();
    }

共同关注功能

@Override
    public Result followCommons(Long id) {
        //传递的参数是目标用户
        // 1.获取当前用户
        Long userId = UserHolder.getUser().getId();
        //当前用户key
        String key = "follows:" + userId;
        //目标用户key
        String key2 = "follows:" + id;
        // 2.求交集

        Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key, key2);
        if (intersect == null || intersect.isEmpty()) {
            // 无交集
            return Result.ok(Collections.emptyList());
        }
        // 3.解析id集合
        List<Long> ids = intersect.stream().map(Long::valueOf).collect(Collectors.toList());
        // 4.查询用户
        List<UserDTO> users = userService.listByIds(ids)
                .stream()
                .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
                .collect(Collectors.toList());
        return Result.ok(users);
    }

关注推送 

也叫Feed流,范围为投喂,通过无线下拉刷新获取新的信息

Feed流有两种模式:Timeline智能排序

 Timeline有三种实现方式:拉模式、推模式和推垃结合

拉模式:也叫读扩散,即每次赵六读取的时候,都会从它关注的人的发件箱里拉取消息并且按照时间排序,缺点就是延迟高(为什么延迟高?因此要去发件箱里读取数据,所以时间长)

推模式:也叫写扩散,即有人发消息会直接推送到粉丝的收件箱中按照时间排序,优点延迟低,缺点:占用内存高,一次写可能写n份

推拉结合:也叫读写混合,具有推拉两种模式的有优点,即将根据身份的不同赋予不同的权力,针对少量活跃粉丝采取推模式,延迟低但耗点内存,针对大量普通粉丝采取拉模式,省内存但延迟高

Feed总结

案例:基于推模式的关注推送

需求:

1.修改新增探店笔记的业务,将笔记内容保存到数据库的同时(相对来说,数据库持久且安全),只需要将用户id推到粉丝的收件箱中即可,然后按照用户id再去查询笔记

2.收件箱需要满足按照时间戳进行排序,使用redis数据结构实现(LIst和sortedset都满足排序和利用角标分页查询的功能,但list不支持滚动分页,sortedset支持滚动分页

3.查询收件箱用户id时,再进行分页查询(因为feed流中的数据会不断更新,数据的角标也在随之变化,若采取传统的分页查询会导致出现内容重复 ,所以要采取滚动分页

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值