From fa1c182f969b00335b1c1ef70d9da70d0f2e3433 Mon Sep 17 00:00:00 2001 From: gitjiho Date: Wed, 27 Nov 2024 03:13:11 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/software/ott/friend/FriendStatus.java | 5 + .../friend/controller/FriendController.java | 73 ++++++++++ .../ott/friend/dto/FriendEmailRequest.java | 6 + .../ott/friend/dto/FriendRequestResponse.java | 10 ++ .../ott/friend/dto/FriendResponse.java | 8 ++ .../software/ott/friend/entity/Friend.java | 50 +++++++ .../friend/repository/FriendRepository.java | 15 ++ .../ott/friend/service/FriendService.java | 136 ++++++++++++++++++ 8 files changed, 303 insertions(+) create mode 100644 src/main/java/com/software/ott/friend/FriendStatus.java create mode 100644 src/main/java/com/software/ott/friend/controller/FriendController.java create mode 100644 src/main/java/com/software/ott/friend/dto/FriendEmailRequest.java create mode 100644 src/main/java/com/software/ott/friend/dto/FriendRequestResponse.java create mode 100644 src/main/java/com/software/ott/friend/dto/FriendResponse.java create mode 100644 src/main/java/com/software/ott/friend/entity/Friend.java create mode 100644 src/main/java/com/software/ott/friend/repository/FriendRepository.java create mode 100644 src/main/java/com/software/ott/friend/service/FriendService.java diff --git a/src/main/java/com/software/ott/friend/FriendStatus.java b/src/main/java/com/software/ott/friend/FriendStatus.java new file mode 100644 index 0000000..9405732 --- /dev/null +++ b/src/main/java/com/software/ott/friend/FriendStatus.java @@ -0,0 +1,5 @@ +package com.software.ott.friend; + +public enum FriendStatus { + PENDING, ACCEPTED, DECLINED +} diff --git a/src/main/java/com/software/ott/friend/controller/FriendController.java b/src/main/java/com/software/ott/friend/controller/FriendController.java new file mode 100644 index 0000000..89b1494 --- /dev/null +++ b/src/main/java/com/software/ott/friend/controller/FriendController.java @@ -0,0 +1,73 @@ +package com.software.ott.friend.controller; + +import com.software.ott.common.dto.StringTypeMessageResponse; +import com.software.ott.friend.dto.FriendEmailRequest; +import com.software.ott.friend.dto.FriendRequestResponse; +import com.software.ott.friend.dto.FriendResponse; +import com.software.ott.friend.service.FriendService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/friend") +@Tag(name = "친구", description = "친구 관련 API") +public class FriendController { + + private final FriendService friendService; + + @Operation(summary = "친구 요청 보내기", description = "친구의 email을 기반으로 친구요청합니다.") + @PostMapping + public ResponseEntity sendFriendRequest(@RequestAttribute("memberId") Long memberId, @RequestBody FriendEmailRequest friendEmailRequest) { + friendService.makeFriendRequest(memberId, friendEmailRequest); + return ResponseEntity.status(HttpStatus.CREATED).body(new StringTypeMessageResponse("친구 요청이 생성되었습니다.")); + } + + @Operation(summary = "친구 확인", description = "본인의 친구들을 확인합니다.") + @GetMapping + public ResponseEntity> getAllFriends(@RequestAttribute("memberId") Long memberId) { + List friendResponses = friendService.getAllFriend(memberId); + return ResponseEntity.ok().body(friendResponses); + } + + @Operation(summary = "대기중인 친구 요청들 확인", description = "본인에게 온 친구 요청 대기 목록을 확인합니다.") + @GetMapping("/pending") + public ResponseEntity> getAllPendingFriendRequest(@RequestAttribute("memberId") Long memberId) { + List friendRequestResponses = friendService.getAllFriendRequests(memberId); + return ResponseEntity.ok().body(friendRequestResponses); + } + + @Operation(summary = "거절한 친구 요청들 확인", description = "본인에게 온 친구 요청 중 거절한 요청 목록을 확인합니다.") + @GetMapping("/declined") + public ResponseEntity> getAllDeclinedFriendRequest(@RequestAttribute("memberId") Long memberId) { + List friendRequestResponses = friendService.getAllDeclinedFriends(memberId); + return ResponseEntity.ok().body(friendRequestResponses); + } + + @Operation(summary = "친구 요청 수락", description = "친구 요청을 수락합니다.") + @PutMapping("/accept/{friendRequestId}") + public ResponseEntity acceptFriendRequest(@RequestAttribute("memberId") Long memberId, @PathVariable Long friendRequestId) { + friendService.acceptFriendRequest(memberId, friendRequestId); + return ResponseEntity.ok().body(new StringTypeMessageResponse("친구 요청이 수락되었습니다.")); + } + + @Operation(summary = "친구 요청 거절", description = "친구 요청을 거절합니다.") + @PutMapping("/decline/{friendRequestId}") + public ResponseEntity declineFriendRequest(@RequestAttribute("memberId") Long memberId, @PathVariable Long friendRequestId) { + friendService.declineFriendRequest(memberId, friendRequestId); + return ResponseEntity.ok().body(new StringTypeMessageResponse("친구 요청이 거절되었습니다.")); + } + + @Operation(summary = "친구 또는 친구요청 삭제", description = "친구를 삭제합니다.") + @DeleteMapping("/{friendRequestId}") + public ResponseEntity deleteFriend(@RequestAttribute("memberId") Long memberId, @PathVariable Long friendRequestId) { + friendService.deleteFriend(memberId, friendRequestId); + return ResponseEntity.ok().body(new StringTypeMessageResponse("친구가 삭제되었습니다.")); + } +} diff --git a/src/main/java/com/software/ott/friend/dto/FriendEmailRequest.java b/src/main/java/com/software/ott/friend/dto/FriendEmailRequest.java new file mode 100644 index 0000000..46ddb0d --- /dev/null +++ b/src/main/java/com/software/ott/friend/dto/FriendEmailRequest.java @@ -0,0 +1,6 @@ +package com.software.ott.friend.dto; + +public record FriendEmailRequest( + String email +) { +} diff --git a/src/main/java/com/software/ott/friend/dto/FriendRequestResponse.java b/src/main/java/com/software/ott/friend/dto/FriendRequestResponse.java new file mode 100644 index 0000000..c85818d --- /dev/null +++ b/src/main/java/com/software/ott/friend/dto/FriendRequestResponse.java @@ -0,0 +1,10 @@ +package com.software.ott.friend.dto; + +public record FriendRequestResponse( + Long friendRequestId, + String requesterName, + String requesterEmail, + String accepterName, + String accepterEmail +) { +} diff --git a/src/main/java/com/software/ott/friend/dto/FriendResponse.java b/src/main/java/com/software/ott/friend/dto/FriendResponse.java new file mode 100644 index 0000000..665d3a6 --- /dev/null +++ b/src/main/java/com/software/ott/friend/dto/FriendResponse.java @@ -0,0 +1,8 @@ +package com.software.ott.friend.dto; + +public record FriendResponse( + Long friendRequestId, + String friendName, + String friendEmail +) { +} diff --git a/src/main/java/com/software/ott/friend/entity/Friend.java b/src/main/java/com/software/ott/friend/entity/Friend.java new file mode 100644 index 0000000..b589e38 --- /dev/null +++ b/src/main/java/com/software/ott/friend/entity/Friend.java @@ -0,0 +1,50 @@ +package com.software.ott.friend.entity; + +import com.software.ott.common.exception.BadRequestException; +import com.software.ott.friend.FriendStatus; +import com.software.ott.member.entity.Member; +import jakarta.persistence.*; +import lombok.*; + +@Builder +@Getter +@Setter +@Entity +@NoArgsConstructor +@AllArgsConstructor +public class Friend { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + @ManyToOne + @JoinColumn(name = "requester_id") + private Member requester; + @ManyToOne + @JoinColumn(name = "accepter_id") + private Member accepter; + @Enumerated(EnumType.STRING) + private FriendStatus status; + + public boolean checkMemberIsNotAccepter(Member member) { + return !this.accepter.equals(member); + } + + public boolean checkMemberIsNotRequester(Member member) { + return !this.requester.equals(member); + } + + public void acceptFriendRequest() { + if (!this.status.equals(FriendStatus.PENDING)) { + throw new BadRequestException("대기중인 친구 요청이 아닙니다."); + } + this.status = FriendStatus.ACCEPTED; + } + + public void declinedFriendRequest() { + if (!this.status.equals(FriendStatus.PENDING)) { + throw new BadRequestException("대기중인 친구 요청이 아닙니다."); + } + this.status = FriendStatus.DECLINED; + } +} diff --git a/src/main/java/com/software/ott/friend/repository/FriendRepository.java b/src/main/java/com/software/ott/friend/repository/FriendRepository.java new file mode 100644 index 0000000..3e850bc --- /dev/null +++ b/src/main/java/com/software/ott/friend/repository/FriendRepository.java @@ -0,0 +1,15 @@ +package com.software.ott.friend.repository; + +import com.software.ott.friend.FriendStatus; +import com.software.ott.friend.entity.Friend; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface FriendRepository extends JpaRepository { + List findAllByAccepterIdAndStatusOrRequesterIdAndStatus(Long accepterId, FriendStatus status1, Long requesterId, FriendStatus status2); + + boolean existsByRequesterIdOrAccepterId(Long requesterId, Long accepterId); +} diff --git a/src/main/java/com/software/ott/friend/service/FriendService.java b/src/main/java/com/software/ott/friend/service/FriendService.java new file mode 100644 index 0000000..daa8fed --- /dev/null +++ b/src/main/java/com/software/ott/friend/service/FriendService.java @@ -0,0 +1,136 @@ +package com.software.ott.friend.service; + +import com.software.ott.common.exception.BadRequestException; +import com.software.ott.common.exception.NotFoundException; +import com.software.ott.friend.FriendStatus; +import com.software.ott.friend.dto.FriendEmailRequest; +import com.software.ott.friend.dto.FriendRequestResponse; +import com.software.ott.friend.dto.FriendResponse; +import com.software.ott.friend.entity.Friend; +import com.software.ott.friend.repository.FriendRepository; +import com.software.ott.member.entity.Member; +import com.software.ott.member.repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class FriendService { + + private final FriendRepository friendRepository; + private final MemberRepository memberRepository; + + @Transactional + public void makeFriendRequest(Long memberId, FriendEmailRequest friendEmailRequest) { + Member requesterMember = memberRepository.findById(memberId) + .orElseThrow(() -> new NotFoundException("id에 해당하는 멤버가 없습니다.")); + Member accepterMember = memberRepository.findByEmail(friendEmailRequest.email()) + .orElseThrow(() -> new NotFoundException("email에 해당하는 멤버가 없습니다.")); + + if (friendRepository.existsByRequesterIdOrAccepterId(memberId, memberId)) { + throw new BadRequestException("이미 친구이거나, 친구요청이 존재합니다."); + } + + friendRepository.save(Friend.builder() + .requester(requesterMember) + .accepter(accepterMember) + .status(FriendStatus.PENDING) + .build()); + } + + @Transactional(readOnly = true) + public List getAllFriend(Long memberId) { + return friendRepository.findAllByAccepterIdAndStatusOrRequesterIdAndStatus(memberId, FriendStatus.ACCEPTED, memberId, FriendStatus.ACCEPTED) + .stream().map(friend -> { + if (friend.getRequester().getId().equals(memberId)) { + return new FriendResponse( + friend.getId(), + friend.getAccepter().getName(), + friend.getAccepter().getEmail() + ); + } else { + return new FriendResponse( + friend.getId(), + friend.getRequester().getName(), + friend.getRequester().getEmail() + ); + } + }) + .collect(Collectors.toList()); + } + + @Transactional(readOnly = true) + public List getAllDeclinedFriends(Long memberId) { + return friendRepository.findAllByAccepterIdAndStatusOrRequesterIdAndStatus(memberId, FriendStatus.DECLINED, memberId, FriendStatus.DECLINED) + .stream().map( + Friend -> new FriendRequestResponse( + Friend.getId(), + Friend.getRequester().getName(), + Friend.getRequester().getEmail(), + Friend.getAccepter().getName(), + Friend.getAccepter().getEmail())) + .collect(Collectors.toList()); + } + + @Transactional(readOnly = true) + public List getAllFriendRequests(Long memberId) { + return friendRepository.findAllByAccepterIdAndStatusOrRequesterIdAndStatus(memberId, FriendStatus.PENDING, memberId, FriendStatus.PENDING) + .stream().map( + Friend -> new FriendRequestResponse( + Friend.getId(), + Friend.getRequester().getName(), + Friend.getRequester().getEmail(), + Friend.getAccepter().getName(), + Friend.getAccepter().getEmail())) + .collect(Collectors.toList()); + } + + @Transactional + public void acceptFriendRequest(Long memberId, Long friendRequestId) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new NotFoundException("id에 해당하는 멤버가 없습니다.")); + + Friend friendRequest = friendRepository.findById(friendRequestId) + .orElseThrow(() -> new NotFoundException("friendRequestId에 해당하는 친구요청이 없습니다.")); + + if (friendRequest.checkMemberIsNotAccepter(member)) { + throw new BadRequestException("친구 요청을 수락할 권한이 없습니다. 요청받은 사람만 수락할 수 있습니다."); + } + + friendRequest.acceptFriendRequest(); + } + + @Transactional + public void declineFriendRequest(Long memberId, Long friendRequestId) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new NotFoundException("id에 해당하는 멤버가 없습니다.")); + + Friend friendRequest = friendRepository.findById(friendRequestId) + .orElseThrow(() -> new NotFoundException("friendRequestId에 해당하는 친구요청이 없습니다.")); + + if (friendRequest.checkMemberIsNotAccepter(member)) { + throw new BadRequestException("친구 요청을 수락할 권한이 없습니다. 요청받은 사람만 수락할 수 있습니다."); + } + + friendRequest.declinedFriendRequest(); + } + + @Transactional + public void deleteFriend(Long memberId, Long friendRequestId) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new NotFoundException("id에 해당하는 멤버가 없습니다.")); + + Friend friendRequest = friendRepository.findById(friendRequestId) + .orElseThrow(() -> new NotFoundException("friendRequestId에 해당하는 친구요청이 없습니다.")); + + if (friendRequest.checkMemberIsNotRequester(member) && friendRequest.checkMemberIsNotAccepter(member)) { + throw new BadRequestException("친구를 삭제할 권한이 없습니다. 당사자들만 삭제할 수 있습니다."); + } + + friendRepository.deleteById(friendRequestId); + } +}