Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat/201] 친구 검색 기능 구현 및 친구 목록 조회 페이징 추가 #205

Merged
merged 5 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 28 additions & 25 deletions src/main/java/com/gamegoo/apiPayload/code/status/ErrorStatus.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public enum ErrorStatus implements BaseErrorCode {

// 페이징 관련 에러
PAGE_INVALID(HttpStatus.BAD_REQUEST, "PAGE401", "페이지 값은 1 이상이어야 합니다."),
CURSOR_INVALID(HttpStatus.BAD_REQUEST, "PAGE402", "커서 값은 1 이상이어야 합니다."),

// Member 관련 에러
PASSWORD_INVALID(HttpStatus.BAD_REQUEST, "MEMBER400", "비밀번호가 불일치합니다."),
Expand Down Expand Up @@ -58,14 +59,14 @@ public enum ErrorStatus implements BaseErrorCode {
// Riot 관련 에러
RIOT_NOT_FOUND(HttpStatus.NOT_FOUND, "RIOT404", "해당 Riot 계정이 존재하지 않습니다."),
RIOT_MATCH_NOT_FOUND(HttpStatus.NOT_FOUND, "RIOTMATCH404",
"해당 Riot 계정의 매칭을 불러오는 도중 에러가 발생했습니다. 최근 100판 이내 이벤트 매칭 제외, 일반 매칭(일반게임,랭크게임,칼바람)을 많이 한 계정으로 다시 시도하세요."),
"해당 Riot 계정의 매칭을 불러오는 도중 에러가 발생했습니다. 최근 100판 이내 이벤트 매칭 제외, 일반 매칭(일반게임,랭크게임,칼바람)을 많이 한 계정으로 다시 시도하세요."),
RIOT_PREFER_CHAMPION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "RIOTCHAMPION500",
"선호 챔피언을 연동하는 도중 에러가 발생했습니다"),
"선호 챔피언을 연동하는 도중 에러가 발생했습니다"),
CHAMPION_NOT_FOUND(HttpStatus.NOT_FOUND, "CHAMPION404", "해당 챔피언이 존재하지 않습니다."),
RIOT_MEMBER_CONFLICT(HttpStatus.CONFLICT, "RIOT409", "해당 이메일 계정은 이미 다른 RIOT 계정과 연동되었습니다."),
RIOT_ACCOUNT_CONFLICT(HttpStatus.CONFLICT, "RIOT409", "해당 RIOT 계정은 이미 다른 이메일과 연동되어있습니다."),
RIOT_INSUFFICIENT_MATCHES(HttpStatus.NOT_FOUND, "RIOT404",
"해당 RIOT 계정은 최근 100판 이내에 솔로랭크, 자유랭크, 일반게임, 칼바람을 플레이한 적이 없기 때문에 선호하는 챔피언 3명을 정할 수 없습니다."),
"해당 RIOT 계정은 최근 100판 이내에 솔로랭크, 자유랭크, 일반게임, 칼바람을 플레이한 적이 없기 때문에 선호하는 챔피언 3명을 정할 수 없습니다."),
RIOT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "RIOT500", "RIOT API 연동 중 에러가 발생했습니다."),

// 차단 관련 에러
Expand All @@ -80,7 +81,7 @@ public enum ErrorStatus implements BaseErrorCode {

// 게시판 글 작성 관련 에러
BOARD_GAME_STYLE_BAD_REQUEST(HttpStatus.BAD_REQUEST, "BOARD400",
"게임 스타일 선택 개수(최대 3개)를 초과했습니다."),
"게임 스타일 선택 개수(최대 3개)를 초과했습니다."),
GAME_MODE_INVALID(HttpStatus.BAD_REQUEST, "BOARD401", "게임모드 값은 1~4만 가능합니다."),
MAIN_POSITION_INVALID(HttpStatus.BAD_REQUEST, "BOARD401", "주포지션 값은 0~5만 가능합니다."),
SUB_POSITION_INVALID(HttpStatus.BAD_REQUEST, "BOARD401", "부포지션 값은 0~5만 가능합니다."),
Expand All @@ -96,11 +97,11 @@ public enum ErrorStatus implements BaseErrorCode {
// 매너평가 관련 에러
MANNER_TARGET_MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "MANNER401", "매너 평가 대상 회원을 찾을 수 없습니다."),
BAD_MANNER_TARGET_MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "MANNER401",
"비매너 평가 대상 회원을 찾을 수 없습니다."),
"비매너 평가 대상 회원을 찾을 수 없습니다."),
MANNER_UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "MANNER401", "매너평가 작성자만 수정 가능합니다."),
MANNER_KEYWORD_TYPE_INVALID(HttpStatus.BAD_REQUEST, "MANNER401", "매너 키워드 유형은 1~6만 가능합니다."),
BAD_MANNER_KEYWORD_TYPE_INVALID(HttpStatus.BAD_REQUEST, "MANNER401",
"비매너 키워드 유형은 7~12만 가능합니다."),
"비매너 키워드 유형은 7~12만 가능합니다."),
MANNER_KEYWORD_NOT_FOUND(HttpStatus.NOT_FOUND, "MANNER404", "해당 매너 키워드를 찾을 수 없습니다."),
MANNER_NOT_FOUND(HttpStatus.NOT_FOUND, "MANNER404", "해당 매너평가를 찾을 수 없습니다."),
BAD_MANNER_NOT_FOUND(HttpStatus.NOT_FOUND, "MANNER404", "해당 비매너평가를 찾을 수 없습니다."),
Expand All @@ -114,43 +115,45 @@ public enum ErrorStatus implements BaseErrorCode {
CHATROOM_ACCESS_DENIED(HttpStatus.BAD_REQUEST, "CHAT403", "접근할 수 없는 채팅방 입니다."),
CHAT_MESSAGE_NOT_FOUND(HttpStatus.NOT_FOUND, "CHAT404", "해당 메시지를 찾을 수 없습니다"),
CHAT_TARGET_IS_BLOCKED_CHAT_START_FAILED(HttpStatus.FORBIDDEN, "CHAT405",
"채팅 상대 회원을 차단한 상태입니다. 채팅 시작이 불가능합니다."),
"채팅 상대 회원을 차단한 상태입니다. 채팅 시작이 불가능합니다."),
BLOCKED_BY_CHAT_TARGET_CHAT_START_FAILED(HttpStatus.FORBIDDEN, "CHAT406",
"채팅 상대 회원이 나를 차단했습니다. 채팅 시작이 불가능합니다."),
"채팅 상대 회원이 나를 차단했습니다. 채팅 시작이 불가능합니다."),
CHAT_TARGET_IS_BLOCKED_SEND_CHAT_FAILED(HttpStatus.FORBIDDEN, "CHAT407",
"채팅 상대 회원을 차단한 상태입니다. 채팅 메시지 전송이 불가능합니다."),
"채팅 상대 회원을 차단한 상태입니다. 채팅 메시지 전송이 불가능합니다."),
BLOCKED_BY_CHAT_TARGET_SEND_CHAT_FAILED(HttpStatus.FORBIDDEN, "CHAT408",
"채팅 상대 회원이 나를 차단했습니다. 채팅 메시지 전송이 불가능합니다."),
"채팅 상대 회원이 나를 차단했습니다. 채팅 메시지 전송이 불가능합니다."),
CHAT_TARGET_MEMBER_ID_INVALID(HttpStatus.BAD_REQUEST, "CHAT409", "채팅방 시작 대상 회원 id 값이 잘못되었습니다."),

// 친구 관련 에러
FRIEND_BAD_REQUEST(HttpStatus.BAD_REQUEST, "FRIEND401", "잘못된 친구 요청입니다."),
FRIEND_TARGET_IS_BLOCKED(HttpStatus.BAD_REQUEST, "FRIEND402",
"내가 차단한 회원입니다. 친구 요청을 보낼 수 없습니다."),
"내가 차단한 회원입니다. 친구 요청을 보낼 수 없습니다."),
BLOCKED_BY_FRIEND_TARGET(HttpStatus.BAD_REQUEST, "FRIEND403",
"나를 차단한 회원입니다. 친구 요청을 보낼 수 없습니다."),
"나를 차단한 회원입니다. 친구 요청을 보낼 수 없습니다."),
MY_PENDING_FRIEND_REQUEST_EXIST(HttpStatus.BAD_REQUEST, "FRIEND404",
"해당 회원에게 보낸 수락 대기 중인 친구 요청이 존재합니다. 친구 요청을 보낼 수 없습니다."),
"해당 회원에게 보낸 수락 대기 중인 친구 요청이 존재합니다. 친구 요청을 보낼 수 없습니다."),
TARGET_PENDING_FRIEND_REQUEST_EXIST(HttpStatus.BAD_REQUEST, "FRIEND405",
"해당 회원이 나에게 보낸 친구 요청이 수락 대기 중 입니다. 해당 요청을 수락 해주세요."),
"해당 회원이 나에게 보낸 친구 요청이 수락 대기 중 입니다. 해당 요청을 수락 해주세요."),
ALREADY_FRIEND(HttpStatus.BAD_REQUEST, "FRIEND406",
"두 회원은 이미 친구 관계 입니다. 친구 요청을 보낼 수 없습니다."),
PENDING_FRIEND_REQUEST_NOT_EXIST(HttpStatus.NOT_FOUND, "FRIEND407",
"취소/수락/거절할 친구 요청이 존재하지 않습니다."),
MEMBERS_NOT_FRIEND(HttpStatus.BAD_REQUEST, "FRIEND408", "두 회원은 친구 관계가 아닙니다."),
ALREADY_STAR_FRIEND(HttpStatus.BAD_REQUEST, "FRIEND409", "이미 즐겨찾기 되어 있는 친구입니다."),
NOT_STAR_FRIEND(HttpStatus.BAD_REQUEST, "FRIEND410", "즐겨찾기 되어 있는 친구가 아닙니다."),
FRIEND_SEARCH_QUERY_BAD_REQUEST(HttpStatus.BAD_REQUEST, "FRIEND411",
"친구 검색 쿼리는 100자 이하여야 합니다."),

// 알림 관련 에러
NOTIFICATION_TYPE_NOT_FOUND(HttpStatus.NOT_FOUND, "NOTI401", "해당 알림 타입 데이터를 찾을 수 없습니다."),
NOTIFICATION_METHOD_BAD_REQUEST(HttpStatus.BAD_REQUEST, "NOTI402", "알림 생성 메소드 호출이 잘못되었습니다."),
INVALID_NOTIFICATION_TYPE(HttpStatus.BAD_REQUEST, "NOTI403",
"잘못된 알림 조회 타입입니다. general과 friend 중 하나를 입력하세요."),
"잘못된 알림 조회 타입입니다. general과 friend 중 하나를 입력하세요."),
NOTIFICATION_NOT_FOUND(HttpStatus.NOT_FOUND, "NOTI404", "해당 알림 내역을 찾을 수 없습니다."),

// SOCKET 서버 API 호출 에러
SOCKET_API_RESPONSE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "SOCKET501",
"socket서버 api 요청에 실패했습니다.");
"socket서버 api 요청에 실패했습니다.");

private final HttpStatus httpStatus;
private final String code;
Expand All @@ -159,19 +162,19 @@ public enum ErrorStatus implements BaseErrorCode {
@Override
public ErrorReasonDTO getReason() {
return ErrorReasonDTO.builder()
.message(message)
.code(code)
.isSuccess(false)
.build();
.message(message)
.code(code)
.isSuccess(false)
.build();
}

@Override
public ErrorReasonDTO getReasonHttpStatus() {
return ErrorReasonDTO.builder()
.message(message)
.code(code)
.isSuccess(false)
.httpStatus(httpStatus)
.build();
.message(message)
.code(code)
.isSuccess(false)
.httpStatus(httpStatus)
.build();
}
}
36 changes: 29 additions & 7 deletions src/main/java/com/gamegoo/controller/member/FriendController.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.gamegoo.converter.MemberConverter;
import com.gamegoo.domain.friend.Friend;
import com.gamegoo.dto.member.MemberResponse;
import com.gamegoo.dto.member.MemberResponse.friendInfoDTO;
import com.gamegoo.dto.member.MemberResponse.friendRequestResultDTO;
import com.gamegoo.dto.member.MemberResponse.starFriendResultDTO;
import com.gamegoo.service.member.FriendService;
Expand All @@ -15,12 +16,14 @@
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Slice;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
Expand All @@ -32,18 +35,37 @@ public class FriendController {

private final FriendService friendService;

@Operation(summary = "친구 목록 조회 API", description = "해당 회원의 친구 목록을 조회하는 API 입니다. 이름 오름차순으로 정렬해 제공합니다.")
@Operation(summary = "친구 목록 조회 API", description =
"해당 회원의 친구 목록을 조회하는 API 입니다. 이름 오름차순(한글-영문-숫자 순)으로 정렬해 제공합니다.\n\n"
+ "cursor를 보내지 않으면 상위 10개 친구 목록을 조회합니다.")
@Parameter(name = "cursor", description = "페이징을 위한 커서, 이전 친구 목록 조회에서 응답받은 next_cursor를 보내주세요.")
@GetMapping
public ApiResponse<List<MemberResponse.friendInfoDTO>> getFriendList() {
public ApiResponse<MemberResponse.friendListDTO> getFriendList(
@RequestParam(name = "cursor", required = false) Long cursorId
) {
Long memberId = JWTUtil.getCurrentUserId();
List<Friend> friends = friendService.getFriends(memberId);

List<MemberResponse.friendInfoDTO> friendInfoDTOList = friends.stream()
.map(MemberConverter::toFriendInfoDto).collect(
Collectors.toList());
Slice<Friend> friends = friendService.getFriends(memberId, cursorId);

return ApiResponse.onSuccess(friendInfoDTOList);
return ApiResponse.onSuccess(MemberConverter.toFriendListDTO(friends));

}

@Operation(summary = "소환사명으로 친구 검색 API", description =
"해당 회원의 친구 중, query string으로 시작하는 소환사명을 가진 모든 친구 목록을 조회합니다.")
@Parameter(name = "query", description = "친구 목록 검색을 위한 소환사명 string으로, 100자 이하여야 합니다.")
@GetMapping("/search")
public ApiResponse<List<friendInfoDTO>> searchFriend(
@RequestParam(name = "query") String query
) {
Long memberId = JWTUtil.getCurrentUserId();

List<Friend> friends = friendService.searchFriendByQueryString(memberId, query);

List<friendInfoDTO> friendInfoDTOList = friends.stream()
.map(MemberConverter::toFriendInfoDto).collect(Collectors.toList());

return ApiResponse.onSuccess(friendInfoDTOList);
}

@Operation(summary = "친구 요청 전송 API", description = "대상 회원에게 친구 요청을 전송하는 API 입니다."
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/com/gamegoo/converter/MemberConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import com.gamegoo.domain.friend.Friend;
import com.gamegoo.domain.member.Member;
import com.gamegoo.dto.member.MemberResponse;
import com.gamegoo.dto.member.MemberResponse.friendInfoDTO;
import com.gamegoo.util.MemberUtils;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Slice;

public class MemberConverter {

Expand Down Expand Up @@ -124,12 +126,27 @@ public static MemberResponse.memberProfileDTO toMemberProfileDTO(Member member,

}

public static MemberResponse.friendListDTO toFriendListDTO(Slice<Friend> friends) {
List<friendInfoDTO> friendInfoDTOList = friends.stream()
.map(MemberConverter::toFriendInfoDto).collect(Collectors.toList());
return MemberResponse.friendListDTO.builder()
.friendInfoDTOList(friendInfoDTOList)
.list_size(friendInfoDTOList.size())
.has_next(friends.hasNext())
.next_cursor(
friends.hasNext() ? friends.getContent().get(friendInfoDTOList.size() - 1)
.getToMember().getId()
: null)
.build();
}

public static MemberResponse.friendInfoDTO toFriendInfoDto(Friend friend) {
return MemberResponse.friendInfoDTO.builder()
.memberId(friend.getToMember().getId())
.name(friend.getToMember().getGameName())
.memberProfileImg(friend.getToMember().getProfileImage())
.isLiked(friend.getIsLiked())
.isBlind(friend.getToMember().getBlind())
.build();
}

Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/gamegoo/dto/member/MemberResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,18 @@ public static class memberProfileDTO {
List<ChampionResponseDTO> championResponseDTOList;
}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class friendListDTO {

List<friendInfoDTO> friendInfoDTOList;
Integer list_size;
Boolean has_next;
Long next_cursor;
}

@Builder
@Getter
@NoArgsConstructor
Expand All @@ -143,6 +155,7 @@ public static class friendInfoDTO {
String name;
Integer memberProfileImg;
boolean isLiked;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

boolean -> Boolean 으로 통일해도 좋을 것 같아요.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 Boolean으로 수정했습니다!

boolean isBlind;
}

@Builder
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package com.gamegoo.repository.friend;

import com.gamegoo.domain.member.Member;
import com.gamegoo.domain.friend.Friend;

import com.gamegoo.domain.member.Member;
import java.util.List;
import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface FriendRepository extends JpaRepository<Friend, Long> {
public interface FriendRepository extends JpaRepository<Friend, Long>, FriendRepositoryCustom {

List<Friend> findAllByFromMemberId(Long memberId);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.gamegoo.repository.friend;

import com.gamegoo.domain.friend.Friend;
import java.util.List;
import org.springframework.data.domain.Slice;

interface FriendRepositoryCustom {

Slice<Friend> findFriendsByCursorAndOrdered(Long cursor, Long memberId, Integer pageSize);

List<Friend> findFriendsByQueryStringAndOrdered(String queryString, Long memberId);
}
Loading
Loading