You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Spring Boot+Java的动漫社交网站添加好友功能实现咨询

好友功能两种实现方案分析与通用思路

方案一:在User类中维护好友列表

  • 可行性:这种方案在小型训练营项目/快速原型里完全能用,代码结构简单,不用额外加模块,适合快速搭出功能。但有几个坑要注意:
    • 数据库层面:用JPA的话会自动生成中间关联表(比如user_friends),但这种方式存不了好友关系的额外信息——比如你们哪天加的好友、你给对方的备注,要是后面想加这些功能,改起来会很麻烦。
    • 性能问题:用户好友多了之后,查用户资料的时候会自动把整个好友列表都加载出来,容易拖慢接口,得手动加@ManyToMany(fetch = FetchType.LAZY)改成懒加载。
  • 核心代码示例:
@Entity
public class UserProfile {
    @Id
    private UUID userId;
    private String displayName;
    private Date birthday;
    private String aboutMe;

    // 懒加载好友列表,关联中间表
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
        name = "user_friends",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "friend_id")
    )
    private List<UserProfile> friendsList = new ArrayList<>();

    // 添加好友:要维护双向关联
    public void addFriend(UserProfile friend) {
        if (!friendsList.contains(friend)) {
            friendsList.add(friend);
            friend.getFriendsList().add(this);
        }
    }

    // 移除好友:同样要删两边
    public void removeFriend(UserProfile friend) {
        friendsList.remove(friend);
        friend.getFriendsList().remove(this);
    }

    // 省略getter/setter
}
  • 好友请求的补充:这个方案本身处理不了“待确认”的请求状态,得单独加个FriendRequest实体类,存发起方、接收方、请求状态(待确认/同意/拒绝),不然没法实现“加好友需要对方同意”的逻辑。

方案二:独立好友关系模块

  • 可行性:这是需要扩展功能的项目的首选,灵活性拉满。把好友关系做成独立实体,能存各种元数据(添加时间、备注、拉黑状态),而且分层清晰,符合Spring Boot的MVC架构,后续加功能也方便。
  • 核心实体设计:
@Entity
public class FriendRelation {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID relationId;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private UserProfile user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "friend_id")
    private UserProfile friend;

    private LocalDateTime createTime;
    private String friendNote; // 用户给好友的备注
    private RelationStatus status; // 关系状态:待确认/已同意/已拉黑

    // 省略getter/setter
}

// 状态枚举
public enum RelationStatus {
    PENDING, ACCEPTED, BLOCKED
}
  • 分层实现示例:
    • Repository层:做基础查询
      public interface FriendRelationRepository extends JpaRepository<FriendRelation, UUID> {
          // 查某用户对另一用户的特定状态关系
          Optional<FriendRelation> findByUserUserIdAndFriendUserIdAndStatus(UUID userId, UUID friendId, RelationStatus status);
          // 查用户所有已同意的好友
          List<FriendRelation> findByUserUserIdAndStatus(UUID userId, RelationStatus.ACCEPTED);
      }
      
    • Service层:封装业务逻辑
      @Service
      public class FriendService {
          @Autowired
          private FriendRelationRepository relationRepo;
          @Autowired
          private UserProfileRepository userRepo;
      
          // 发送好友请求
          public void sendFriendRequest(UUID senderId, UUID receiverId) {
              UserProfile sender = userRepo.findById(senderId).orElseThrow(() -> new RuntimeException("发起方用户不存在"));
              UserProfile receiver = userRepo.findById(receiverId).orElseThrow(() -> new RuntimeException("接收方用户不存在"));
      
              // 检查是否已有待确认请求
              if (relationRepo.findByUserUserIdAndFriendUserIdAndStatus(senderId, receiverId, RelationStatus.PENDING).isPresent()) {
                  throw new RuntimeException("已经发过好友请求了");
              }
      
              FriendRelation request = new FriendRelation();
              request.setUser(sender);
              request.setFriend(receiver);
              request.setCreateTime(LocalDateTime.now());
              request.setStatus(RelationStatus.PENDING);
              relationRepo.save(request);
          }
      
          // 同意好友请求
          public void acceptFriendRequest(UUID receiverId, UUID senderId) {
              // 找到发起方发的待确认请求
              FriendRelation request = relationRepo.findByUserUserIdAndFriendUserIdAndStatus(senderId, receiverId, RelationStatus.PENDING)
                  .orElseThrow(() -> new RuntimeException("没有待确认的好友请求"));
              
              // 把请求改成已同意
              request.setStatus(RelationStatus.ACCEPTED);
              // 同时创建反向关系,方便双向查询好友列表
              FriendRelation reverseRelation = new FriendRelation();
              reverseRelation.setUser(request.getFriend());
              reverseRelation.setFriend(request.getUser());
              reverseRelation.setCreateTime(LocalDateTime.now());
              reverseRelation.setStatus(RelationStatus.ACCEPTED);
              
              relationRepo.saveAll(List.of(request, reverseRelation));
          }
      
          // 移除好友
          public void removeFriend(UUID userId, UUID friendId) {
              // 删除双向的已同意关系
              relationRepo.deleteByUserUserIdAndFriendUserIdAndStatus(userId, friendId, RelationStatus.ACCEPTED);
              relationRepo.deleteByUserUserIdAndFriendUserIdAndStatus(friendId, userId, RelationStatus.ACCEPTED);
          }
      }
      
    • Controller层:暴露接口
      @RestController
      @RequestMapping("/api/friends")
      public class FriendController {
          @Autowired
          private FriendService friendService;
      
          // 发送好友请求
          @PostMapping("/request")
          public ResponseEntity<Void> sendRequest(@RequestParam UUID senderId, @RequestParam UUID receiverId) {
              friendService.sendFriendRequest(senderId, receiverId);
              return ResponseEntity.ok().build();
          }
      
          // 同意好友请求
          @PostMapping("/accept")
          public ResponseEntity<Void> acceptRequest(@RequestParam UUID receiverId, @RequestParam UUID senderId) {
              friendService.acceptFriendRequest(receiverId, senderId);
              return ResponseEntity.ok().build();
          }
      
          // 移除好友
          @DeleteMapping("/{friendId}")
          public ResponseEntity<Void> removeFriend(@RequestParam UUID userId, @PathVariable UUID friendId) {
              friendService.removeFriend(userId, friendId);
              return ResponseEntity.ok().build();
          }
      }
      

通用实现要点总结

  1. 先定需求范围:如果只是训练营的demo,方案一够快;如果要做更完整的社交功能,直接选方案二。
  2. 必须处理请求状态:不管用哪种方案,都不能直接加好友,得先通过“请求-确认”的流程,不然没法实现双向同意的逻辑。
  3. 数据库优化:给FriendRelation的user_id、friend_id、status加联合索引,查询会快很多;所有关联查询都用懒加载,避免加载不必要的数据。
  4. 业务逻辑要严谨:
    • 防止重复发送请求或添加好友;
    • 移除好友时一定要处理双向关系,不然会出现“我删了对方,但对方列表里还有我”的情况;
    • 加权限控制,比如只有请求接收方才能同意请求,不能让别人代操作。

内容的提问来源于stack exchange,提问作者juviscript

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 13:17:49