diff --git a/refrigerator/src/main/java/moja/refrigerator/aggregate/user/Follow.java b/refrigerator/src/main/java/moja/refrigerator/aggregate/user/Follow.java new file mode 100644 index 0000000..80eaa90 --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/aggregate/user/Follow.java @@ -0,0 +1,22 @@ +package moja.refrigerator.aggregate.user; + +import jakarta.persistence.*; +import lombok.Data; + +@Entity +@Table(name = "tbl_follow") +@Data +public class Follow { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "follow_pk") + private long followPk; + + @ManyToOne + @JoinColumn(name = "follower") + private User follower; + + @ManyToOne + @JoinColumn(name = "following") + private User following; +} diff --git a/refrigerator/src/main/java/moja/refrigerator/controller/user/UserController.java b/refrigerator/src/main/java/moja/refrigerator/controller/user/UserController.java index 3dd637c..c841068 100644 --- a/refrigerator/src/main/java/moja/refrigerator/controller/user/UserController.java +++ b/refrigerator/src/main/java/moja/refrigerator/controller/user/UserController.java @@ -1,10 +1,10 @@ package moja.refrigerator.controller.user; -import jakarta.servlet.http.HttpServletRequest; import moja.refrigerator.dto.user.request.PasswordResetRequest; import moja.refrigerator.dto.user.request.PasswordUpdateRequest; import moja.refrigerator.dto.user.request.UserCreateRequest; import moja.refrigerator.dto.user.request.UserUpdateRequest; +import moja.refrigerator.service.user.FollowService; import moja.refrigerator.service.user.UserService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -13,9 +13,11 @@ @RestController public class UserController { private final UserService userService; + private final FollowService followService; - public UserController(UserService userService) { + public UserController(UserService userService, FollowService followService) { this.userService = userService; + this.followService = followService; } // 토큰 검증 로직 확인용 @@ -51,4 +53,11 @@ public ResponseEntity updatePassword(@RequestBody PasswordUpdateRequest reque userService.updatePassword(request); return ResponseEntity.ok().body("비밀번호가 변경되었습니다."); } + + // 팔로우 및 언팔로우 + @PostMapping("/follow/{userPk}") + public ResponseEntity toggleFollow(@PathVariable Long userPk) { + followService.toggleFollow(userPk); + return ResponseEntity.ok().body("팔로우 상태가 변경되었습니다."); + } } diff --git a/refrigerator/src/main/java/moja/refrigerator/repository/user/FollowRepository.java b/refrigerator/src/main/java/moja/refrigerator/repository/user/FollowRepository.java new file mode 100644 index 0000000..a568240 --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/repository/user/FollowRepository.java @@ -0,0 +1,12 @@ +package moja.refrigerator.repository.user; + +import moja.refrigerator.aggregate.user.Follow; +import moja.refrigerator.aggregate.user.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface FollowRepository extends JpaRepository { + boolean existsByFollowerAndFollowing(User follower, User following); + void deleteByFollowerAndFollowing(User follower, User following); +} diff --git a/refrigerator/src/main/java/moja/refrigerator/service/user/FollowService.java b/refrigerator/src/main/java/moja/refrigerator/service/user/FollowService.java new file mode 100644 index 0000000..e213f57 --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/service/user/FollowService.java @@ -0,0 +1,5 @@ +package moja.refrigerator.service.user; + +public interface FollowService { + void toggleFollow(Long targetUserPk); +} diff --git a/refrigerator/src/main/java/moja/refrigerator/service/user/FollowServiceImpl.java b/refrigerator/src/main/java/moja/refrigerator/service/user/FollowServiceImpl.java new file mode 100644 index 0000000..14e077b --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/service/user/FollowServiceImpl.java @@ -0,0 +1,50 @@ +package moja.refrigerator.service.user; + +import moja.refrigerator.aggregate.user.Follow; +import moja.refrigerator.aggregate.user.User; +import moja.refrigerator.repository.user.FollowRepository; +import moja.refrigerator.repository.user.UserRepository; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class FollowServiceImpl implements FollowService { + private final FollowRepository followRepository; + private final UserRepository userRepository; + + public FollowServiceImpl(FollowRepository followRepository, UserRepository userRepository) { + this.followRepository = followRepository; + this.userRepository = userRepository; + } + + @Override + @Transactional + public void toggleFollow(Long targetUserPk) { + // 현재 로그인한 사용자 찾기 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + User follower = userRepository.findByUserId(authentication.getName()) + .orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다.")); + + // 팔로우 대상 사용자 찾기 + User following = userRepository.findByUserPk(targetUserPk) + .orElseThrow(() -> new UsernameNotFoundException("팔로우 하려는 사용자를 찾을 수 없습니다.")); + + // 자기 자신 팔로우 방지 + if (follower.getUserPk() == following.getUserPk()) { + throw new IllegalArgumentException("자기 자신을 팔로우할 수 없습니다."); + } + + // 이미 팔로우 중이면 언팔로우, 아니면 팔로우 + if (followRepository.existsByFollowerAndFollowing(follower, following)) { + followRepository.deleteByFollowerAndFollowing(follower, following); + } else { + Follow follow = new Follow(); + follow.setFollower(follower); + follow.setFollowing(following); + followRepository.save(follow); + } + } +}