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/110] 친구 요청 등록 API 구현 #119

Merged
merged 9 commits into from
Aug 8, 2024
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.gamegoo.domain;
package com.gamegoo.domain.friend;

import static com.querydsl.core.types.PathMetadataFactory.*;

Expand All @@ -16,7 +16,7 @@
@Generated("com.querydsl.codegen.DefaultEntitySerializer")
public class QFriend extends EntityPathBase<Friend> {

private static final long serialVersionUID = -762196062L;
private static final long serialVersionUID = -138403158L;

private static final PathInits INITS = PathInits.DIRECT2;

Expand All @@ -27,13 +27,13 @@ public class QFriend extends EntityPathBase<Friend> {
//inherited
public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt;

public final QMember fromMember;
public final com.gamegoo.domain.QMember fromMember;

public final NumberPath<Long> id = createNumber("id", Long.class);

public final BooleanPath isLiked = createBoolean("isLiked");

public final QMember toMember;
public final com.gamegoo.domain.QMember toMember;

//inherited
public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt;
Expand All @@ -56,8 +56,8 @@ public QFriend(PathMetadata metadata, PathInits inits) {

public QFriend(Class<? extends Friend> type, PathMetadata metadata, PathInits inits) {
super(type, metadata, inits);
this.fromMember = inits.isInitialized("fromMember") ? new QMember(forProperty("fromMember")) : null;
this.toMember = inits.isInitialized("toMember") ? new QMember(forProperty("toMember")) : null;
this.fromMember = inits.isInitialized("fromMember") ? new com.gamegoo.domain.QMember(forProperty("fromMember")) : null;
this.toMember = inits.isInitialized("toMember") ? new com.gamegoo.domain.QMember(forProperty("toMember")) : null;
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.gamegoo.domain;
package com.gamegoo.domain.friend;

import static com.querydsl.core.types.PathMetadataFactory.*;

Expand All @@ -16,7 +16,7 @@
@Generated("com.querydsl.codegen.DefaultEntitySerializer")
public class QFriendRequests extends EntityPathBase<FriendRequests> {

private static final long serialVersionUID = -955592314L;
private static final long serialVersionUID = -1336304498L;

private static final PathInits INITS = PathInits.DIRECT2;

Expand All @@ -27,13 +27,13 @@ public class QFriendRequests extends EntityPathBase<FriendRequests> {
//inherited
public final DateTimePath<java.time.LocalDateTime> createdAt = _super.createdAt;

public final QMember fromMember;
public final com.gamegoo.domain.QMember fromMember;

public final NumberPath<Long> id = createNumber("id", Long.class);

public final BooleanPath isApproved = createBoolean("isApproved");
public final EnumPath<FriendRequestStatus> status = createEnum("status", FriendRequestStatus.class);

public final QMember toMember;
public final com.gamegoo.domain.QMember toMember;

//inherited
public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt;
Expand All @@ -56,8 +56,8 @@ public QFriendRequests(PathMetadata metadata, PathInits inits) {

public QFriendRequests(Class<? extends FriendRequests> type, PathMetadata metadata, PathInits inits) {
super(type, metadata, inits);
this.fromMember = inits.isInitialized("fromMember") ? new QMember(forProperty("fromMember")) : null;
this.toMember = inits.isInitialized("toMember") ? new QMember(forProperty("toMember")) : null;
this.fromMember = inits.isInitialized("fromMember") ? new com.gamegoo.domain.QMember(forProperty("fromMember")) : null;
this.toMember = inits.isInitialized("toMember") ? new com.gamegoo.domain.QMember(forProperty("toMember")) : null;
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public class QMannerRating extends EntityPathBase<MannerRating> {

public final NumberPath<Long> id = createNumber("id", Long.class);

public final BooleanPath isPositive = createBoolean("isPositive");

public final ListPath<MannerRatingKeyword, QMannerRatingKeyword> mannerRatingKeywordList = this.<MannerRatingKeyword, QMannerRatingKeyword>createList("mannerRatingKeywordList", MannerRatingKeyword.class, QMannerRatingKeyword.class, PathInits.DIRECT2);

public final com.gamegoo.domain.QMember toMember;
Expand Down
56 changes: 36 additions & 20 deletions src/main/java/com/gamegoo/apiPayload/code/status/ErrorStatus.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public enum ErrorStatus implements BaseErrorCode {
POSITION_NOT_FOUND(HttpStatus.NOT_FOUND, "POSITION404", "해당 Position을 찾을 수 없습니다."),

// Profile_Image 관련 에러
PROFILE_IMAGE_BAD_REQUEST(HttpStatus.BAD_REQUEST, "PROFILE_IMAGE400", "profile_image가 30자를 초과했습니다."),
PROFILE_IMAGE_BAD_REQUEST(HttpStatus.BAD_REQUEST, "PROFILE_IMAGE400",
"profile_image가 30자를 초과했습니다."),

// Email 인증 관련 에러
EMAIL_SEND_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "EMAIL500", "이메일 전송 도중, 에러가 발생했습니다."),
Expand All @@ -50,32 +51,37 @@ public enum ErrorStatus implements BaseErrorCode {
EMAIL_INVALID_TIME(HttpStatus.BAD_REQUEST, "EMAIL400", "이메일 인증 시간이 3분 초과했습니다."),

// 매칭 관련 에러
MATCHING_STATUS_BAD_REQUEST(HttpStatus.BAD_REQUEST, "MATCH400", "status는 SUCCESS, QUIT 둘 중 하나로만 변경이 가능합니다."),
MATCHING_STATUS_BAD_REQUEST(HttpStatus.BAD_REQUEST, "MATCH400",
"status는 SUCCESS, QUIT 둘 중 하나로만 변경이 가능합니다."),
MATCHING_NOT_FOUND(HttpStatus.NOT_FOUND, "MATCH404", "해당 사용자의 매칭 정보가 없습니다."),


// Riot 관련 에러
RIOT_NOT_FOUND(HttpStatus.NOT_FOUND, "RIOT404", "해당 Riot 계정이 존재하지 않습니다."),
RIOT_MATCH_NOT_FOUND(HttpStatus.NOT_FOUND, "RIOTMATCH404", "해당 Riot 계정의 매칭을 불러오는 도중 에러가 발생했습니다. 최근 100판 이내 이벤트 매칭 제외, 일반 매칭(일반게임,랭크게임,칼바람)을 많이 한 계정으로 다시 시도하세요."),
RIOT_PREFER_CHAMPION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "RIOTCHAMPION500", "선호 챔피언을 연동하는 도중 에러가 발생했습니다"),
RIOT_MATCH_NOT_FOUND(HttpStatus.NOT_FOUND, "RIOTMATCH404",
"해당 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 연동 중 에러가 발생했습니다."),

// 차단 관련 에러
TARGET_MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "BLOCK401", "차단 대상 회원을 찾을 수 없습니다."),
ALREADY_BLOCKED(HttpStatus.BAD_REQUEST, "BLOCK402", "이미 차단한 회원입니다."),
TARGET_MEMBER_NOT_BLOCKED(HttpStatus.BAD_REQUEST, "BLOCK403", "차단 목록에 존재하지 않는 회원입니다."),
BLOCK_MEMBER_BAD_REQUEST(HttpStatus.BAD_REQUEST, "BLOCK404", "잘못된 친구 차단 요청입니다."),

// 신고 관련 에러
REPORT_TARGET_MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "REPORT401", "신고 대상 회원을 찾을 수 없습니다."),
MEMBER_AND_TARGET_MEMBER_SAME(HttpStatus.BAD_REQUEST, "REPORT402", "회원과 신고 대상 회원이 같습니다."),

// 게시판 글 작성 관련 에러
BOARD_GAME_STYLE_BAD_REQUEST(HttpStatus.BAD_REQUEST, "BOARD400", "게임 스타일 선택 개수(최대 3개)를 초과했습니다."),
BOARD_GAME_STYLE_BAD_REQUEST(HttpStatus.BAD_REQUEST, "BOARD400",
"게임 스타일 선택 개수(최대 3개)를 초과했습니다."),
GAME_MODE_INVALID(HttpStatus.BAD_REQUEST, "BOARD401", "게임모드 값은 1~4만 가능합니다."),
MAIN_POSITION_INVALID(HttpStatus.BAD_REQUEST, "BOARD401", "주포지션 값은 1~5만 가능합니다."),
SUB_POSITION_INVALID(HttpStatus.BAD_REQUEST, "BOARD401", "부포지션 값은 1~5만 가능합니다."),
Expand All @@ -91,16 +97,26 @@ public enum ErrorStatus implements BaseErrorCode {
// 매너평가 관련 에러
MANNER_TARGET_MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "MANNER401", "매너 평가 대상 회원을 찾을 수 없습니다."),
MANNER_KEYWORD_TYPE_INVALID(HttpStatus.BAD_REQUEST, "MANNER401", "매너 키워드 유형은 1~6만 가능합니다."),
BAD_MANNER_KEYWORD_TYPE_INVALID(HttpStatus.BAD_REQUEST, "MANNER401", "비매너 키워드 유형은 7~12만 가능합니다."),
BAD_MANNER_KEYWORD_TYPE_INVALID(HttpStatus.BAD_REQUEST, "MANNER401",
"비매너 키워드 유형은 7~12만 가능합니다."),
MANNER_KEYWORD_NOT_FOUND(HttpStatus.NOT_FOUND, "MANNER404", "해당 매너 키워드를 찾을 수 없습니다."),
MANNER_CONFLICT(HttpStatus.CONFLICT, "MANNER409", "매너 평가는 최초 1회만 가능합니다."),
BAD_MANNER_CONFLICT(HttpStatus.CONFLICT, "MANNER409", "비매너 평가는 최초 1회만 가능합니다."),

// 채팅 관련 에러
CHAT_TARGET_NOT_FOUND(HttpStatus.NOT_FOUND, "CHAT4001", "채팅 대상 회원을 찾을 수 없습니다."),
CHATROOM_NOT_EXIST(HttpStatus.NOT_FOUND, "CHAT4002", "채팅방을 찾을 수 없습니다."),
CHATROOM_ACCESS_DENIED(HttpStatus.BAD_REQUEST, "CHAT4003", "접근할 수 없는 채팅방 입니다."),
CHAT_MESSAGE_NOT_FOUND(HttpStatus.NOT_FOUND, "CHAT4004", "해당 메시지를 찾을 수 없습니다");
CHAT_TARGET_NOT_FOUND(HttpStatus.NOT_FOUND, "CHAT401", "채팅 대상 회원을 찾을 수 없습니다."),
CHATROOM_NOT_EXIST(HttpStatus.NOT_FOUND, "CHAT402", "채팅방을 찾을 수 없습니다."),
CHATROOM_ACCESS_DENIED(HttpStatus.BAD_REQUEST, "CHAT403", "접근할 수 없는 채팅방 입니다."),
CHAT_MESSAGE_NOT_FOUND(HttpStatus.NOT_FOUND, "CHAT404", "해당 메시지를 찾을 수 없습니다"),

// 친구 관련 에러
FRIEND_BAD_REQUEST(HttpStatus.BAD_REQUEST, "FRIEND401", "잘못된 친구 요청입니다."),
FRIEND_TARGET_IS_BLOCKED(HttpStatus.BAD_REQUEST, "FRIEND402",
"내가 차단한 회원입니다. 친구 요청을 보낼 수 없습니다."),
BLOCKED_BY_FRIEND_TARGET(HttpStatus.BAD_REQUEST, "FRIEND403",
"나를 차단한 회원입니다. 친구 요청을 보낼 수 없습니다."),

;

private final HttpStatus httpStatus;
private final String code;
Expand All @@ -109,19 +125,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();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.gamegoo.apiPayload.exception.handler;

import com.gamegoo.apiPayload.code.BaseErrorCode;
import com.gamegoo.apiPayload.exception.GeneralException;

public class FriendHandler extends GeneralException {

public FriendHandler(BaseErrorCode code) {
super(code);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@

import com.gamegoo.apiPayload.ApiResponse;
import com.gamegoo.converter.MemberConverter;
import com.gamegoo.domain.Friend;
import com.gamegoo.domain.Member;
import com.gamegoo.dto.member.MemberResponse;
import com.gamegoo.service.member.MemberService;
import com.gamegoo.service.member.BlockService;
import com.gamegoo.util.JWTUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
Expand All @@ -24,58 +21,45 @@
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@Tag(name = "Member", description = "회원 관련 API")
@Tag(name = "Block", description = "회원 차단 관련 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/v1/member")
public class MemberController {
@RequestMapping("/v1/member/block")
public class BlockController {

private final MemberService memberService;
private final BlockService blockService;


@Operation(summary = "회원 차단 API", description = "대상 회원을 차단하는 API 입니다.")
@Parameter(name = "memberId", description = "차단할 대상 회원의 id 입니다.")
@PostMapping("/block/{memberId}")
@PostMapping("/{memberId}")
public ApiResponse<String> blockMember(@PathVariable(name = "memberId") Long targetMemberId) {
Long memberId = JWTUtil.getCurrentUserId(); //헤더에 있는 jwt 토큰에서 id를 가져오는 코드
memberService.blockMember(memberId, targetMemberId);
blockService.blockMember(memberId, targetMemberId);

return ApiResponse.onSuccess("회원 차단 성공");
}

@Operation(summary = "차단한 회원 목록 조회 API", description = "내가 차단한 회원의 목록을 조회하는 API 입니다.")
@Parameter(name = "page", description = "페이지 번호, 1 이상의 숫자를 입력해 주세요.")
@GetMapping("/block")
@GetMapping
public ApiResponse<MemberResponse.blockListDTO> getBlockList(
@RequestParam(name = "page") Integer page) {
Long memberId = JWTUtil.getCurrentUserId();

Page<Member> blockList = memberService.getBlockList(memberId, page - 1);
Page<Member> blockList = blockService.getBlockList(memberId, page - 1);

return ApiResponse.onSuccess(MemberConverter.toBlockListDTO(blockList));
}

@Operation(summary = "회원 차단 해제 API", description = "해당 회원에 대한 차단을 해제하는 API 입니다.")
@Parameter(name = "memberId", description = "차단을 해제할 대상 회원의 id 입니다.")
@DeleteMapping("/block/{memberId}")
@DeleteMapping("/{memberId}")
public ApiResponse<String> unBlockMember(@PathVariable(name = "memberId") Long targetMemberId) {
Long memberId = JWTUtil.getCurrentUserId();
memberService.unBlockMember(memberId, targetMemberId);
blockService.unBlockMember(memberId, targetMemberId);

return ApiResponse.onSuccess("차단 해제 성공");
}

@Operation(summary = "친구 목록 조회 API", description = "해당 회원의 친구 목록을 조회하는 API 입니다.")
@GetMapping("/friends")
public ApiResponse<Object> getFriendList() {
Long memberId = JWTUtil.getCurrentUserId();
List<Friend> friends = memberService.getFriends(memberId);

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

return ApiResponse.onSuccess(friendInfoDTOList);

}
}
61 changes: 61 additions & 0 deletions src/main/java/com/gamegoo/controller/member/FriendController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.gamegoo.controller.member;

import com.gamegoo.apiPayload.ApiResponse;
import com.gamegoo.converter.MemberConverter;
import com.gamegoo.domain.friend.Friend;
import com.gamegoo.domain.friend.FriendRequests;
import com.gamegoo.dto.member.MemberResponse;
import com.gamegoo.service.member.FriendService;
import com.gamegoo.util.JWTUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
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.RestController;

@Slf4j
@Tag(name = "Friend", description = "친구 관련 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/v1/friends")
public class FriendController {

private final FriendService friendService;

@Operation(summary = "친구 목록 조회 API", description = "해당 회원의 친구 목록을 조회하는 API 입니다.")
@GetMapping
public ApiResponse<List<MemberResponse.friendInfoDTO>> getFriendList() {
Long memberId = JWTUtil.getCurrentUserId();
List<Friend> friends = friendService.getFriends(memberId);

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

return ApiResponse.onSuccess(friendInfoDTOList);

}

@Operation(summary = "친구 요청 전송 API", description = "대상 회원에게 친구 요청을 전송하는 API 입니다."
+ "대상 회원에게 친구 요청 알림을 전송하며, 대상 회원이 현재 접속 중인 경우 socket을 통해 실시간 알림을 전송합니다.")
@Parameter(name = "memberId", description = "친구 요청을 전송할 대상 회원의 id 입니다.")
@PostMapping("/send/{memberId}")
public ApiResponse<String> sendFriendRequest(
@PathVariable(name = "memberId") Long targetMemberId) {
Long memberId = JWTUtil.getCurrentUserId();

FriendRequests friendRequests = friendService.sendFriendRequest(memberId, targetMemberId);

return ApiResponse.onSuccess("친구 요청 전송 성공");

}


}
Loading
Loading