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 : 스터디룸 탈퇴 API 구현 #27

Merged
merged 9 commits into from
Jul 15, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.linkode.api_server.common.exception;

import com.linkode.api_server.common.response.status.ResponseStatus;
import lombok.Getter;

@Getter
public class LeaveStudyroomExeption extends RuntimeException{

private final ResponseStatus exceptionStatus;

public LeaveStudyroomExeption(ResponseStatus exceptionStatus) {
super(exceptionStatus.getMessage());
this.exceptionStatus = exceptionStatus;
}

public LeaveStudyroomExeption(ResponseStatus exceptionStatus, String message) {
super(message);
this.exceptionStatus = exceptionStatus;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public enum BaseExceptionResponseStatus implements ResponseStatus{
*/
NOT_FOUND_MEMBER_STUDYROOM(6000, HttpStatus.OK.value(), "조건에 맞는 멤버_스터디룸을 찾을 수 없습니다."),
NOT_FOUND_CREW(6001, HttpStatus.OK.value(), "해당 스터디룸의 CREW 를 찾을 수 없습니다."),
CANNOT_LEAVE_STUDYROOM(6002, HttpStatus.OK.value(), "방장은 탈퇴할 수 없습니다."),


/**
* color 관련 code : 7000대
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@

import com.linkode.api_server.common.response.BaseResponse;
import com.linkode.api_server.common.response.status.BaseExceptionResponseStatus;
import com.linkode.api_server.dto.studyroom.CreateStudyroomRequest;
import com.linkode.api_server.dto.studyroom.CreateStudyroomResponse;
import com.linkode.api_server.dto.studyroom.DetailStudyroomResponse;
import com.linkode.api_server.dto.studyroom.PatchStudyroomRequest;
import com.linkode.api_server.dto.studyroom.*;
import com.linkode.api_server.service.MemberStudyroomService;
import com.linkode.api_server.service.StudyroomService;
import com.linkode.api_server.util.JwtProvider;
Expand All @@ -27,6 +24,9 @@ public class StudyroomController {
@Autowired
JwtProvider jwtProvider;

/**
* 스터디룸 삭제
*/
@PatchMapping("/removal")
public BaseResponse<BaseExceptionResponseStatus> deleteStudyroom(@RequestHeader("Authorization") String authorization, @RequestParam long studyroomId){

Expand All @@ -37,7 +37,21 @@ public BaseResponse<BaseExceptionResponseStatus> deleteStudyroom(@RequestHeader(
return new BaseResponse<>(responseStatus);
} else {
return new BaseResponse<>(responseStatus, responseStatus);
} }
}
}

/**
* 스터디룸 탈퇴
* */
@PatchMapping("/leave")
public BaseResponse<MemberStudyroomListResponse> leaveStudyroom(@RequestHeader("Authorization") String authorization, @RequestParam long studyroomId){

long memberId = jwtProvider.extractIdFromHeader(authorization);
BaseExceptionResponseStatus responseStatus = memberStudyroomService.leaveStudyroom(studyroomId,memberId);
MemberStudyroomListResponse latestStudyroomList = memberStudyroomService.getMemberStudyroomList(memberId);
log.info("Run leaveStudyroom API ");
Copy link
Member

Choose a reason for hiding this comment

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

엇, log 가 아래에 있어서 위의 쿼리문들이 전부 실행된 이후에 로그가 찍히는데 혹시 의도하신건가요?? 로그찍는 스타일은 사람마다 다르니까 이거는 정말 궁금해서 물어봅니더!

Copy link
Member Author

Choose a reason for hiding this comment

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

넵! 기본적으로 메서드 로그는 제공되기때문에
저는 정상적으로 서비스단 메소드가 실행되어야
컨트롤러가 돈다고 판단하기위해 디버깅용 로그를 찍어두었습니다!

return new BaseResponse<>(responseStatus,latestStudyroomList);
}

@PostMapping("/generation")
public CreateStudyroomResponse createStudyroom(@RequestHeader("Authorization") String authorization, @RequestBody CreateStudyroomRequest request){
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.linkode.api_server.dto.studyroom;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.List;

@Getter @Setter
@AllArgsConstructor
@NoArgsConstructor
public class MemberStudyroomListResponse {

private List<MemberStudyroomListResponse.Studyroom> studyroomList;

@Getter @Setter
@AllArgsConstructor
public static class Studyroom{
Long studyroomId;
String studyroomProfile;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public void afterConnectionEstablished(WebSocketSession session) throws Exceptio
log.info("WebSocket connection established for studyroomId: {}, userId: {}", studyroomId, userId);

studyroomSessions.computeIfAbsent(studyroomId, k -> ConcurrentHashMap.newKeySet()).add(session);
userSessions.put(userId, session);
userSessions.put(userId + "_" + studyroomId, session); // 유저 ID와 스터디룸 ID를 키로 사용

session.sendMessage(new TextMessage("Connection Success! for studyroomId : " + studyroomId));
broadcastMessage(studyroomId, userId, "User " + userId + " has joined the room.");
Expand All @@ -61,7 +61,7 @@ protected void handleTextMessage(WebSocketSession session, TextMessage message)
String directMessage = payload.substring(index + 1);

log.info("direct call from {}: {}", targetUserId, directMessage);
sendMessageToUser(targetUserId, "[" + userId + " (call)] : " + directMessage);
sendMessageToUser(targetUserId + "_" + studyroomId, "[" + userId + " (call)] : " + directMessage);
return;
}
}
Expand Down Expand Up @@ -90,7 +90,7 @@ public void afterConnectionClosed(WebSocketSession session, CloseStatus status)
studyroomSessions.remove(studyroomId);
}
}
userSessions.remove(userId);
userSessions.remove(userId + "_" + studyroomId);

broadcastMessage(studyroomId, userId, "User " + userId + " has left the room.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,13 @@ public interface MemberstudyroomRepository extends JpaRepository<MemberStudyroom
"AND ms.member.memberId = :memberId " +
"AND ms.status = :status")
Optional<MemberStudyroom> getStudyroomDetail(long studyroomId,long memberId, BaseStatus status);


/**
* findByMember_MemberIdAndStatus와 동일하나 N+1 문제 해결버전
* */
@Query("SELECT ms FROM MemberStudyroom ms JOIN FETCH ms.studyroom WHERE ms.member.memberId = :memberId AND ms.status = :status")
Optional<List<MemberStudyroom>> findByMemberIdAndStatus(Long memberId, BaseStatus status);


}
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package com.linkode.api_server.service;

import com.linkode.api_server.common.exception.LeaveStudyroomExeption;
import com.linkode.api_server.common.exception.MemberStudyroomException;
import com.linkode.api_server.common.response.BaseResponse;
import com.linkode.api_server.common.response.status.BaseExceptionResponseStatus;
import com.linkode.api_server.domain.Studyroom;
import com.linkode.api_server.domain.base.BaseStatus;
import com.linkode.api_server.domain.memberstudyroom.MemberRole;
import com.linkode.api_server.domain.memberstudyroom.MemberStudyroom;
import com.linkode.api_server.dto.studyroom.DetailStudyroomResponse;
import com.linkode.api_server.dto.studyroom.MemberStudyroomListResponse;
import com.linkode.api_server.repository.MemberstudyroomRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -18,8 +22,7 @@
import java.util.Set;
import java.util.stream.Collectors;

import static com.linkode.api_server.common.response.status.BaseExceptionResponseStatus.NOT_FOUND_CREW;
import static com.linkode.api_server.common.response.status.BaseExceptionResponseStatus.NOT_FOUND_MEMBER_STUDYROOM;
import static com.linkode.api_server.common.response.status.BaseExceptionResponseStatus.*;

@Slf4j
@Service
Expand Down Expand Up @@ -88,4 +91,55 @@ public DetailStudyroomResponse getStudyroomDetail(long studyroomId, long memberI
return response;
}

/**
* 스터디룸 탈퇴
*
* memberStudyroom에 대한 유효성 검증을 한후 적절하지 못한 맴버 스터디룸이면 예외를 던집니다.
* 방장이면 탈퇴할 수 없도록 조건문을 통해 방장인지 파악한 뒤 예외를 강제로 던집니다!
*
* */
@Transactional
public BaseExceptionResponseStatus leaveStudyroom(long studyroomId, long memberId){
try {
MemberStudyroom memberStudyroom = memberstudyroomRepository
.findByMember_MemberIdAndStudyroom_StudyroomIdAndStatus(memberId,studyroomId,BaseStatus.ACTIVE)
.orElseThrow(()-> new MemberStudyroomException(NOT_FOUND_MEMBER_STUDYROOM));
if(memberStudyroom.getRole()==MemberRole.CAPTAIN) throw new
LeaveStudyroomExeption(CANNOT_LEAVE_STUDYROOM);
memberStudyroom.updateMemberStudyroomStatus(BaseStatus.DELETE);
memberstudyroomRepository.save(memberStudyroom);
return BaseExceptionResponseStatus.SUCCESS;
}
catch (LeaveStudyroomExeption e) {
log.error("MemberStudyroomException! -> ", e);
return CANNOT_LEAVE_STUDYROOM;
}
catch (MemberStudyroomException e) {
log.error("MemberStudyroomException! -> ", e);
return NOT_FOUND_MEMBER_STUDYROOM;
}
catch (Exception e){
return FAILURE;
}
}

/**
* 유저의 스터디룸 리스트 조회
*
* 스트림 문법으로 매핑
* */
public MemberStudyroomListResponse getMemberStudyroomList(long memberId){
Copy link
Member

Choose a reason for hiding this comment

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

이 부분에서 셀렉트문이 하나 더 날라갈 것 같은데 방장이라면 기존 스터디룸과 달라질 부분이 없으니 CAPTAIN 이라면 그냥 null 로 반환하는 것은 어떨까요??? 근데 또 생각해보면 그 사이에 다른 스터디룸에서 업데이트 된 것을 여기서 채워넣을 수 있어서 좋은 방법인 것 같기두 합니다!

Copy link
Member Author

@Mouon Mouon Jul 15, 2024

Choose a reason for hiding this comment

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

엇 저도 그 부분에대해 고민하였었는데
방장일때 탈퇴가 이루어지지 않아 변경되지않은 리스트또한 상태또한 검증될필요가 있다고 판단하여 일관성있게 반환하도록 하였습니다!
또한 탈퇴 API가 일관되게 최신 스터디룸 리스트 조회기능을 하는게 나을것 같다고 생각했었습니다!


List<MemberStudyroomListResponse.Studyroom> studyroomList =
memberstudyroomRepository.findByMemberIdAndStatus(memberId,BaseStatus.ACTIVE)
.orElseThrow(()->new MemberStudyroomException(NOT_FOUND_MEMBER_STUDYROOM))
.stream()
.map(ms -> new MemberStudyroomListResponse.Studyroom(
ms.getStudyroom().getStudyroomId(),
ms.getStudyroom().getStudyroomProfile()
)).collect(Collectors.toList());

return new MemberStudyroomListResponse(studyroomList);
}

}
Loading