diff --git a/src/backend/user-server/src/main/java/com/asyncgate/user_server/controller/FriendController.java b/src/backend/user-server/src/main/java/com/asyncgate/user_server/controller/FriendController.java index afe13508..9289c05c 100644 --- a/src/backend/user-server/src/main/java/com/asyncgate/user_server/controller/FriendController.java +++ b/src/backend/user-server/src/main/java/com/asyncgate/user_server/controller/FriendController.java @@ -5,7 +5,7 @@ import com.asyncgate.user_server.domain.Member; import com.asyncgate.user_server.dto.response.FriendResponse; import com.asyncgate.user_server.dto.response.FriendsResponse; -import com.asyncgate.user_server.dto.response.MemberResponse; +import com.asyncgate.user_server.dto.response.UserClientInfoResponses.UserClientInfoResponse; import com.asyncgate.user_server.security.annotation.MemberID; import com.asyncgate.user_server.support.response.SuccessResponse; import com.asyncgate.user_server.usecase.FriendUseCase; @@ -24,10 +24,10 @@ public class FriendController implements FriendControllerDocs { */ @Override @GetMapping - public SuccessResponse searchTarget(final @RequestParam String email) { + public SuccessResponse searchTarget(final @RequestParam String email) { Member findMember = friendUseCase.getByEmail(email); return SuccessResponse.ok( - MemberResponse.from(findMember) + UserClientInfoResponse.from(findMember) ); } diff --git a/src/backend/user-server/src/main/java/com/asyncgate/user_server/controller/docs/FriendControllerDocs.java b/src/backend/user-server/src/main/java/com/asyncgate/user_server/controller/docs/FriendControllerDocs.java index e6b4bbae..f2ff76a8 100644 --- a/src/backend/user-server/src/main/java/com/asyncgate/user_server/controller/docs/FriendControllerDocs.java +++ b/src/backend/user-server/src/main/java/com/asyncgate/user_server/controller/docs/FriendControllerDocs.java @@ -2,7 +2,7 @@ import com.asyncgate.user_server.dto.response.FriendResponse; import com.asyncgate.user_server.dto.response.FriendsResponse; -import com.asyncgate.user_server.dto.response.MemberResponse; +import com.asyncgate.user_server.dto.response.UserClientInfoResponses; import com.asyncgate.user_server.security.annotation.MemberID; import com.asyncgate.user_server.support.response.SuccessResponse; import io.swagger.v3.oas.annotations.Operation; @@ -18,7 +18,7 @@ public interface FriendControllerDocs { @ApiResponse(responseCode = "200", description = "정상적으로 조회되었습니다.") }) @GetMapping - SuccessResponse searchTarget( + SuccessResponse searchTarget( @Parameter(description = "검색할 회원의 이메일", required = true) @RequestParam String email ); diff --git a/src/backend/user-server/src/main/java/com/asyncgate/user_server/domain/Friend.java b/src/backend/user-server/src/main/java/com/asyncgate/user_server/domain/Friend.java index e96f0364..20176497 100644 --- a/src/backend/user-server/src/main/java/com/asyncgate/user_server/domain/Friend.java +++ b/src/backend/user-server/src/main/java/com/asyncgate/user_server/domain/Friend.java @@ -21,18 +21,25 @@ private Friend(String id, String userId1, String userId2, String requestedBy, Fr } public static Friend create(String requestUserId, String toUserId) { - // 두 사용자 ID를 사전 순으로 정렬하여 저장 - if (requestUserId.compareTo(toUserId) < 0) { - return new Friend(UUID.randomUUID().toString(), requestUserId, toUserId, requestUserId, FriendStatus.PENDING); - } else { - return new Friend(UUID.randomUUID().toString(), toUserId, requestUserId, requestUserId, FriendStatus.PENDING); - } + String lowerUserId = getLowerUserId(requestUserId, toUserId); + String higherUserId = getHigherUserId(requestUserId, toUserId); + // 요청을 보낸 쪽의 ID는 요청자(requestedBy)로 설정합니다. + return new Friend(UUID.randomUUID().toString(), lowerUserId, higherUserId, requestUserId, FriendStatus.PENDING); } public static Friend of(String id, String userId1, String userId2, String requestedBy, FriendStatus status) { return new Friend(id, userId1, userId2, requestedBy, status); } + public static String getLowerUserId(String userId1, String userId2) { + return userId1.compareTo(userId2) < 0 ? userId1 : userId2; + } + + public static String getHigherUserId(String userId1, String userId2) { + return userId1.compareTo(userId2) < 0 ? userId2 : userId1; + } + + public void accept() { this.status = FriendStatus.ACCEPTED; } diff --git a/src/backend/user-server/src/main/java/com/asyncgate/user_server/domain/FriendStatus.java b/src/backend/user-server/src/main/java/com/asyncgate/user_server/domain/FriendStatus.java index d88c05b6..265a5950 100644 --- a/src/backend/user-server/src/main/java/com/asyncgate/user_server/domain/FriendStatus.java +++ b/src/backend/user-server/src/main/java/com/asyncgate/user_server/domain/FriendStatus.java @@ -3,5 +3,8 @@ public enum FriendStatus { PENDING, ACCEPTED, - REJECTED + REJECTED, + + // 실제 데이터 미사용 x 프론트 반환 전용 + RECEIVED, } diff --git a/src/backend/user-server/src/main/java/com/asyncgate/user_server/dto/response/MemberResponse.java b/src/backend/user-server/src/main/java/com/asyncgate/user_server/dto/response/MemberResponse.java deleted file mode 100644 index dc3864bf..00000000 --- a/src/backend/user-server/src/main/java/com/asyncgate/user_server/dto/response/MemberResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.asyncgate.user_server.dto.response; - -import com.asyncgate.user_server.domain.Member; - -import java.time.LocalDate; - -public record MemberResponse(String email, String name, String nickname, String profileImgUrl, LocalDate birth) { - - public static MemberResponse from(Member member) { - return new MemberResponse(member.getEmail(), member.getName(), member.getNickname(), member.getProfileImgUrl(), member.getBirth()); - } - -} diff --git a/src/backend/user-server/src/main/java/com/asyncgate/user_server/entity/FriendEntity.java b/src/backend/user-server/src/main/java/com/asyncgate/user_server/entity/FriendEntity.java index 21e5c995..7e39ac99 100644 --- a/src/backend/user-server/src/main/java/com/asyncgate/user_server/entity/FriendEntity.java +++ b/src/backend/user-server/src/main/java/com/asyncgate/user_server/entity/FriendEntity.java @@ -31,7 +31,7 @@ public class FriendEntity extends BaseEntity { @Enumerated(EnumType.STRING) @Column(name = "status", nullable = false) - private FriendStatus status; + private FriendStatus status = FriendStatus.PENDING; @Builder public FriendEntity(String id, String userId1, String userId2, String requestedBy, FriendStatus status) { diff --git a/src/backend/user-server/src/main/java/com/asyncgate/user_server/exception/FailType.java b/src/backend/user-server/src/main/java/com/asyncgate/user_server/exception/FailType.java index a109780f..13f014fa 100644 --- a/src/backend/user-server/src/main/java/com/asyncgate/user_server/exception/FailType.java +++ b/src/backend/user-server/src/main/java/com/asyncgate/user_server/exception/FailType.java @@ -52,8 +52,16 @@ public enum FailType { // Friend FRIEND_NOT_FOUND(HttpStatus.NOT_FOUND, "Friend_4001", "친구 정보가 존재하지 않습니다."), - UNAUTHORIZED_ACCESS(HttpStatus.FORBIDDEN, "Friend_40300", "현재 사용자는 해당 친구 관계에 대한 권한이 없습니다."), - UNAUTHORIZED_ACTION(HttpStatus.FORBIDDEN, "Friend_40301", "본인의 친구 요청에 대해서는 해당 작업을 수행할 수 없습니다.");; + FRIEND_PENDING_NOT_FOUND(HttpStatus.NOT_FOUND, "Friend_4002", "친구 요청한 기록이 없습니다."), + UNAUTHORIZED_ACCESS(HttpStatus.FORBIDDEN, "Friend_4030", "현재 사용자는 해당 친구 관계에 대한 권한이 없습니다."), + UNAUTHORIZED_ACTION(HttpStatus.FORBIDDEN, "Friend_4031", "본인의 친구 요청에 대해서는 해당 작업을 수행할 수 없습니다."), + + FRIEND_ALREADY_PENDING_EXISTS(HttpStatus.BAD_REQUEST, "Friend_4003", "이미 친구요청을 한 상태입니다."), + FRIEND_IS_NOT_PENDING(HttpStatus.BAD_REQUEST, "Friend_4003", "친구 상태가 친구 요청 상태가 아닙니다."), + FRIEND_ALREADY_ACCEPTED_EXISTS(HttpStatus.BAD_REQUEST, "Friend_4004", "이미 친구인 관계입니다."), + FRIEND_ALREADY_REJECTED_EXISTS(HttpStatus.BAD_REQUEST, "Friend_4005", "이미 친구 신청을 거절당했습니다."), + + ; private final HttpStatus status; private final String errorCode; diff --git a/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/FriendQueryDslRepository.java b/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/FriendQueryDslRepository.java index 66cf8f3e..9a32683b 100644 --- a/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/FriendQueryDslRepository.java +++ b/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/FriendQueryDslRepository.java @@ -1,13 +1,17 @@ package com.asyncgate.user_server.repository; +import com.asyncgate.user_server.domain.Friend; import com.asyncgate.user_server.domain.FriendStatus; import com.asyncgate.user_server.entity.FriendEntity; import com.asyncgate.user_server.entity.QFriendEntity; +import com.asyncgate.user_server.exception.FailType; +import com.asyncgate.user_server.exception.UserServerException; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; @Repository @RequiredArgsConstructor @@ -75,4 +79,54 @@ public List findFriendsIdByUserId(final String userId) { return transFriendIds(userId, friendEntities); } + + public void validNotExists(final String userId1, final String userId2) { + String lowerUserId = Friend.getLowerUserId(userId1, userId2); + String higherUserId = Friend.getHigherUserId(userId1, userId2); + + Optional optionalFindEntity = Optional.ofNullable( + jpaQueryFactory.select(friendEntity) + .from(friendEntity) + .where( + friendEntity.userId1.eq(lowerUserId), + friendEntity.userId2.eq(higherUserId), + friendEntity.deleted.isFalse() + ) + .fetchOne() + ); + + if (optionalFindEntity.isEmpty()) { + return; + } + + FriendEntity findFriendEntity = optionalFindEntity.get(); + + if (findFriendEntity.getStatus().equals(FriendStatus.ACCEPTED)) { + throw new UserServerException(FailType.FRIEND_ALREADY_ACCEPTED_EXISTS); + } + if (findFriendEntity.getStatus().equals(FriendStatus.PENDING)) { + throw new UserServerException(FailType.FRIEND_ALREADY_PENDING_EXISTS); + } + if (findFriendEntity.getStatus().equals(FriendStatus.REJECTED)) { + throw new UserServerException(FailType.FRIEND_ALREADY_REJECTED_EXISTS); + } + } + + public FriendEntity findByIdAndPending(final String friendId) { + FriendEntity findFriendEntity = Optional.ofNullable( + jpaQueryFactory.select(friendEntity) + .from(friendEntity) + .where( + friendEntity.id.eq(friendId), + friendEntity.status.eq(FriendStatus.PENDING), + friendEntity.deleted.isFalse() + ) + .fetchOne() + ).orElseThrow(() -> new UserServerException(FailType.FRIEND_PENDING_NOT_FOUND)); + + if (!findFriendEntity.getStatus().equals(FriendStatus.PENDING)) { + throw new UserServerException(FailType.FRIEND_IS_NOT_PENDING); + } + return findFriendEntity; + } } diff --git a/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/FriendRepository.java b/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/FriendRepository.java index 2e5079d5..24bcbbe5 100644 --- a/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/FriendRepository.java +++ b/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/FriendRepository.java @@ -16,4 +16,8 @@ public interface FriendRepository { List findReceivedFriendRequests(String userId); List findFriendIdsByUserId(String userId); + + void validNotExists(String userId1, String userId2); + + Friend findIdAndPending(String friendId); } diff --git a/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/FriendRepositoryImpl.java b/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/FriendRepositoryImpl.java index c69ee87c..b6daddb5 100644 --- a/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/FriendRepositoryImpl.java +++ b/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/FriendRepositoryImpl.java @@ -55,4 +55,16 @@ public List findReceivedFriendRequests(final String userId) { public List findFriendIdsByUserId(final String userId) { return queryDslRepository.findFriendsIdByUserId(userId); } + + @Override + public void validNotExists(final String userId1, final String userId2) { + queryDslRepository.validNotExists(userId1, userId2); + } + + @Override + public Friend findIdAndPending(final String friendId) { + return DomainUtil.FriendMapper.toDomain( + queryDslRepository.findByIdAndPending(friendId) + ); + } } diff --git a/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/MemberQueryDslRepository.java b/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/MemberQueryDslRepository.java index c19ee8f2..e81d0b3f 100644 --- a/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/MemberQueryDslRepository.java +++ b/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/MemberQueryDslRepository.java @@ -1,6 +1,5 @@ package com.asyncgate.user_server.repository; -import com.asyncgate.user_server.domain.Member; import com.asyncgate.user_server.entity.MemberEntity; import com.asyncgate.user_server.entity.QMemberEntity; import com.querydsl.jpa.impl.JPAQueryFactory; diff --git a/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/MemberRepositoryImpl.java b/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/MemberRepositoryImpl.java index 2b77fb57..8940c933 100644 --- a/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/MemberRepositoryImpl.java +++ b/src/backend/user-server/src/main/java/com/asyncgate/user_server/repository/MemberRepositoryImpl.java @@ -1,9 +1,6 @@ package com.asyncgate.user_server.repository; import com.asyncgate.user_server.domain.Member; -import com.asyncgate.user_server.entity.MemberEntity; -import com.asyncgate.user_server.exception.FailType; -import com.asyncgate.user_server.exception.UserServerException; import com.asyncgate.user_server.support.utility.DomainUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -21,9 +18,9 @@ public class MemberRepositoryImpl implements MemberRepository { @Override public void save(final Member member) { - MemberEntity memberEntity = DomainUtil.MemberMapper.toEntity(member); - - memberJpaRepository.save(memberEntity); + memberJpaRepository.save( + DomainUtil.MemberMapper.toEntity(member) + ); } @Override @@ -33,7 +30,7 @@ public void softDeleteById(final String id) { } @Override - public List getByMemberIds(List memberIds) { + public List getByMemberIds(final List memberIds) { return queryDslRepository.getByMemberIds(memberIds).stream() .map(DomainUtil.MemberMapper::toDomain) .toList(); @@ -60,14 +57,4 @@ public boolean isExistByEmail(final String email) { public boolean isExistById(final String id) { return memberJpaRepository.findById(id).isPresent(); } - - private MemberEntity findMemberEntityByEmail(final String email) { - return memberJpaRepository.findByNotDeletedEmail(email) - .orElseThrow(() -> new UserServerException(FailType.MEMBER_NOT_EXIST_EMAIL)); - } - - private MemberEntity findMemberEntityById(final String id) { - return memberJpaRepository.findByNotDeletedId(id) - .orElseThrow(() -> new UserServerException(FailType.MEMBER_NOT_FOUND)); - } } diff --git a/src/backend/user-server/src/main/java/com/asyncgate/user_server/service/FriendService.java b/src/backend/user-server/src/main/java/com/asyncgate/user_server/service/FriendService.java index 7002e433..8e2e53f9 100644 --- a/src/backend/user-server/src/main/java/com/asyncgate/user_server/service/FriendService.java +++ b/src/backend/user-server/src/main/java/com/asyncgate/user_server/service/FriendService.java @@ -32,6 +32,7 @@ public Member getByEmail(final String email) { @Override @Transactional public Friend registerFriend(final String requestUserId, final String toUserId) { + friendRepository.validNotExists(requestUserId, toUserId); Friend friend = Friend.create(requestUserId, toUserId); friendRepository.save(friend); return friend; @@ -40,7 +41,7 @@ public Friend registerFriend(final String requestUserId, final String toUserId) @Override @Transactional public Friend acceptFriend(final String userId, final String friendId) { - Friend friend = friendRepository.findById(friendId); + Friend friend = friendRepository.findIdAndPending(friendId); validEditPermission(userId, friend); friend.accept(); friendRepository.save(friend); @@ -65,7 +66,7 @@ private void validDeletePermission(String userId, Friend friend) { @Override @Transactional public Friend rejectFriend(final String userId, final String friendId) { - Friend friend = friendRepository.findById(friendId); + Friend friend = friendRepository.findIdAndPending(friendId); validEditPermission(userId, friend); friend.reject(); friendRepository.save(friend); @@ -92,7 +93,7 @@ public FriendsResponse getSentFriendRequests(final String userId) { public FriendsResponse getReceivedFriendRequests(final String userId) { List receivedFriendIds = friendRepository.findReceivedFriendRequests(userId); return FriendsResponse.of( - memberRepository.getByMemberIds(receivedFriendIds), FriendStatus.PENDING + memberRepository.getByMemberIds(receivedFriendIds), FriendStatus.RECEIVED ); }