Redis实现好友功能

发布时间:2024年01月19日

好友功能是目前社交场景的必备功能之一,一般好友相关的功能包含有:关注/取关、我(他)的关注、我(他)的粉丝、共同关注等这样一些功能。

1.关注和取关

1.设计思路

总体思路我们采用MySQL + Redis的方式结合完成。MySQL主要是保存落地数据,而利用Redis的Sets进行集合操作。Sets拥有去重(我们不能多次关注同一用户)功能**。一个用户我们存贮两个集合:一个是保存用户关注的人 另一个是保存关注用户的人.**

1.1 用到的命令
  • SADD 添加成员;命令格式: SADD key member [member …] ----- 关注
  • SREM 移除某个成员;命令格式: SREM key member [member …] -------取关
  • SCARD 统计集合内的成员数;命令格式: SCARD key -------关注/粉丝个数
  • SISMEMBER 判断是否是集合成员;命令格式:SISMEMBER key member ---------判断是否关注(如果关注那么只可以点击取关)
  • SMEMBERS 查询集合内的成员;命令格式: SMEMBERS key -------列表使用(关注列表和粉丝列表)
  • SINTER 查询集合的交集;命令格式: SINTER key [key …] --------共同关注、我关注的人关注了他

2.关注表结构

CREATE TABLE `t_follow` (
`id`  int(11) NOT NULL AUTO_INCREMENT ,
`diner_id`  int(11) NULL DEFAULT NULL COMMENT '用户id' ,
`follow_diner_id`  int(11) NULL DEFAULT NULL COMMENT '用户关注的人id' ,
`is_valid`  tinyint(1) NULL DEFAULT NULL COMMENT '是否关注,0未关注/取消关注,1已关注',
`create_date`  datetime NULL DEFAULT NULL ,
`update_date`  datetime NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
AUTO_INCREMENT=6
ROW_FORMAT=COMPACT;

为什么t_follow要使用is_valid标识符?

答:当用户A第一次关注用户B时,会往t_follow表添加一条数据,此时 is_valid值为1,当A取消对B的关注时,不需要删除刚刚插入的数据,只需要更改is_valid变成0即可。当然也可以不用is_valid字段,当A取消对B的关注时,物理删除对应的这条数据,但公司一般删除不会做物理删除。

3.关注/取关业务逻辑



@Service
public class FollowService {
    
    @Value("${service.name.ms-oauth-server}")
    private String oauthServerName;
    @Resource
    private RestTemplate restTemplate;
    @Resource
    private FollowMapper followMapper;
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 关注/取关
     *
     * @param followDinerId 关注的食客ID
     * @param isFollowed    是否关注 1=关注 0=取消
     * @param accessToken   登录用户token
     * @param path          访问地址
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public ResultInfo follow(Integer followDinerId, int isFollowed,
                             String accessToken, String path) {

        // 是否选择了关注对象
        AssertUtil.isTrue(followDinerId == null || followDinerId < 1, "请选择要关注的人");
        // 获取登录用户信息
        SignInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
        // 去关注表中查询看有没有对应数据
        Follow follow = followMapper.selectFollow(dinerInfo.getId(), followDinerId);
        // 如果没有关注信息,且要进行关注操作
        if (follow == null && isFollowed == 1) {
            // 添加关注信息
            int count = followMapper.save(dinerInfo.getId(), followDinerId);
            // 添加关注列表到 Redis
            if (count == 1) {
                addToRedisSet(dinerInfo.getId(), followDinerId);
            }
            return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE,
                    "关注成功", path, "关注成功");
        }

        // isValid值为0,那么是取关,如果isValid=1那么就是关注中
        // 如果有关注信息,且目前处于取关状态,且要进行关注操作
        if (follow != null && follow.getIsValid() == 0 && isFollowed == 1) {
            // 重新关注
            int count = followMapper.update(follow.getId(), isFollowed);
            // 添加关注列表
            if (count == 1) {
                addToRedisSet(dinerInfo.getId(), followDinerId);
            }
            return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE,
                    "关注成功", path, "关注成功");
        }

        // 如果有关注信息,且目前处于关注中状态,且要进行取关操作
        if (follow != null && follow.getIsValid() == 1 && isFollowed == 0) {
            // 取关
            int count = followMapper.update(follow.getId(), isFollowed);
            // 移除 Redis 关注列表
            removeFromRedisSet(dinerInfo.getId(), followDinerId);
            return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE,
                    "成功取关", path, "成功取关");
        }

        return ResultInfoUtil.buildSuccess(path, "操作成功");
    }


/**
 * 添加关注列表到 Redis
 *
 * @param dinerId
 * @param followDinerId
 */
private void addToRedisSet(Integer dinerId, Integer followDinerId) {
    redisTemplate.opsForSet().add(RedisKeyConstant.following.getKey() + dinerId, followDinerId);
    redisTemplate.opsForSet().add(RedisKeyConstant.followers.getKey() + followDinerId, dinerId);
}

/**
 * 移除 Redis 关注列表
 *
 * @param dinerId
 * @param followDinerId
 */
private void removeFromRedisSet(Integer dinerId, Integer followDinerId) {
    redisTemplate.opsForSet().remove(RedisKeyConstant.following.getKey() + dinerId, followDinerId);
    redisTemplate.opsForSet().remove(RedisKeyConstant.followers.getKey() + followDinerId, dinerId);
}

}

2.共同关注列表

  • 从Redis中读取登录用户的关注列表与查看用户的关注列表,然后进行交集操作,获取共同关注的用户id
  • 然后通过用户id数据获取用户基本信息

1.查询共同好友代码



/**
 * 共同好友
 *
 * @param dinerId
 * @param accessToken
 * @param path
 * @return
 */
public ResultInfo findCommonsFriends(Integer dinerId, String accessToken, String path) {
    // 是否选择了关注对象
    AssertUtil.isTrue(dinerId == null || dinerId < 1, "请选择要查看的人");
    // 获取登录用户信息
    SignInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
    // 登录用户的关注信息
    String loginDinerKey = RedisKeyConstant.following.getKey() + dinerInfo.getId();
    // 登录用户关注的对象的关注信息
    String dinerKey = RedisKeyConstant.following.getKey() + dinerId;
    // 计算交集
    Set<Integer> followingDinerIds = redisTemplate.opsForSet().intersect(loginDinerKey, dinerKey);
    // 没有
    if (followingDinerIds == null || followingDinerIds.isEmpty()) {
        return ResultInfoUtil.buildSuccess(path, new ArrayList<ShortDinerInfo>());
    }
    // 根据 ids 查询食客信息
    ResultInfo resultInfo = restTemplate.getForObject(dinersServerName + "findByIds?access_token=${accessToken}&ids={ids}",
            ResultInfo.class, accessToken, StrUtil.join(",", followingDinerIds));
    if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
        resultInfo.setPath(path);
        return resultInfo;
    }
    // 处理结果集
    List<LinkedHashMap> dinerInfoMaps = (ArrayList) resultInfo.getData();
    List<ShortDinerInfo> dinerInfos = dinerInfoMaps.stream()
            .map(diner -> fillBeanWithMap(diner, new ShortDinerInfo(), true))
            .collect(Collectors.toList());

    return ResultInfoUtil.buildSuccess(path, dinerInfos);
}

文章来源:https://blog.csdn.net/weixin_42528855/article/details/135696462
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。