Skip to content

Commit

Permalink
feat: 친구 로직 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
GitJIHO committed Nov 26, 2024
1 parent 09802eb commit fa1c182
Show file tree
Hide file tree
Showing 8 changed files with 303 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/main/java/com/software/ott/friend/FriendStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.software.ott.friend;

public enum FriendStatus {
PENDING, ACCEPTED, DECLINED
}
Original file line number Diff line number Diff line change
@@ -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<StringTypeMessageResponse> 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<List<FriendResponse>> getAllFriends(@RequestAttribute("memberId") Long memberId) {
List<FriendResponse> friendResponses = friendService.getAllFriend(memberId);
return ResponseEntity.ok().body(friendResponses);
}

@Operation(summary = "대기중인 친구 요청들 확인", description = "본인에게 온 친구 요청 대기 목록을 확인합니다.")
@GetMapping("/pending")
public ResponseEntity<List<FriendRequestResponse>> getAllPendingFriendRequest(@RequestAttribute("memberId") Long memberId) {
List<FriendRequestResponse> friendRequestResponses = friendService.getAllFriendRequests(memberId);
return ResponseEntity.ok().body(friendRequestResponses);
}

@Operation(summary = "거절한 친구 요청들 확인", description = "본인에게 온 친구 요청 중 거절한 요청 목록을 확인합니다.")
@GetMapping("/declined")
public ResponseEntity<List<FriendRequestResponse>> getAllDeclinedFriendRequest(@RequestAttribute("memberId") Long memberId) {
List<FriendRequestResponse> friendRequestResponses = friendService.getAllDeclinedFriends(memberId);
return ResponseEntity.ok().body(friendRequestResponses);
}

@Operation(summary = "친구 요청 수락", description = "친구 요청을 수락합니다.")
@PutMapping("/accept/{friendRequestId}")
public ResponseEntity<StringTypeMessageResponse> 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<StringTypeMessageResponse> 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<StringTypeMessageResponse> deleteFriend(@RequestAttribute("memberId") Long memberId, @PathVariable Long friendRequestId) {
friendService.deleteFriend(memberId, friendRequestId);
return ResponseEntity.ok().body(new StringTypeMessageResponse("친구가 삭제되었습니다."));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.software.ott.friend.dto;

public record FriendEmailRequest(
String email
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.software.ott.friend.dto;

public record FriendRequestResponse(
Long friendRequestId,
String requesterName,
String requesterEmail,
String accepterName,
String accepterEmail
) {
}
8 changes: 8 additions & 0 deletions src/main/java/com/software/ott/friend/dto/FriendResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.software.ott.friend.dto;

public record FriendResponse(
Long friendRequestId,
String friendName,
String friendEmail
) {
}
50 changes: 50 additions & 0 deletions src/main/java/com/software/ott/friend/entity/Friend.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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<Friend, Long> {
List<Friend> findAllByAccepterIdAndStatusOrRequesterIdAndStatus(Long accepterId, FriendStatus status1, Long requesterId, FriendStatus status2);

boolean existsByRequesterIdOrAccepterId(Long requesterId, Long accepterId);
}
136 changes: 136 additions & 0 deletions src/main/java/com/software/ott/friend/service/FriendService.java
Original file line number Diff line number Diff line change
@@ -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<FriendResponse> 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<FriendRequestResponse> 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<FriendRequestResponse> 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);
}
}

0 comments on commit fa1c182

Please sign in to comment.