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 #140] 채팅 요청 목록 조회 API #141

Merged
merged 14 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.dnd.gongmuin.chat.controller;

import java.util.List;

import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
Expand All @@ -10,12 +8,12 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.dnd.gongmuin.chat.dto.request.CreateChatRoomRequest;
import com.dnd.gongmuin.chat.dto.response.AcceptChatResponse;
import com.dnd.gongmuin.chat.dto.response.ChatMessageResponse;
import com.dnd.gongmuin.chat.dto.response.ChatProposalResponse;
import com.dnd.gongmuin.chat.dto.response.ChatRoomDetailResponse;
import com.dnd.gongmuin.chat.dto.response.ChatRoomSimpleResponse;
import com.dnd.gongmuin.chat.dto.response.CreateChatRoomResponse;
Expand Down Expand Up @@ -57,15 +55,25 @@ public ResponseEntity<CreateChatRoomResponse> createChatRoom(
return ResponseEntity.ok(response);
}

@Operation(summary = "채팅방 목록 조회 API", description = "회원의 채팅방 목록을 조회한다.")
@Operation(summary = "채팅방 활성화 목록 조회 API", description = "회원의 채팅방 목록을 조회한다.")
@GetMapping("/api/chat-rooms")
public ResponseEntity<PageResponse<ChatRoomSimpleResponse>> getChatRoomsByMember(
@RequestParam("statuses") List<String> statuses,
@AuthenticationPrincipal Member member,
Pageable pageable
) {
PageResponse<ChatRoomSimpleResponse> response = chatRoomService.getChatRoomsByMember(member, statuses,
pageable);
PageResponse<ChatRoomSimpleResponse> response
= chatRoomService.getChatRoomsByMember(member, pageable);
return ResponseEntity.ok(response);
}

@Operation(summary = "채팅방 요청 목록 조회 API", description = "회원의 채팅방 목록을 조회한다.")
@GetMapping("/api/chat-rooms/proposals")
public ResponseEntity<PageResponse<ChatProposalResponse>> getChatProposalsByMember(
@AuthenticationPrincipal Member member,
Pageable pageable
) {
PageResponse<ChatProposalResponse> response
= chatRoomService.getChatProposalsByMember(member, pageable);
return ResponseEntity.ok(response);
}

Expand Down
10 changes: 0 additions & 10 deletions src/main/java/com/dnd/gongmuin/chat/domain/ChatStatus.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.dnd.gongmuin.chat.domain;

import java.util.Arrays;
import java.util.List;

import com.dnd.gongmuin.chat.exception.ChatErrorCode;
import com.dnd.gongmuin.common.exception.runtime.ValidationException;
Expand All @@ -26,15 +25,6 @@ public static ChatStatus from(String input) {
.orElseThrow(() -> new ValidationException(ChatErrorCode.NOT_FOUND_CHAT_STATUS));
}

public static List<ChatStatus> from(List<String> inputs) {
return inputs.stream()
.map(input -> Arrays.stream(ChatStatus.values())
.filter(status -> status.isEqual(input))
.findAny()
.orElseThrow(() -> new ValidationException(ChatErrorCode.NOT_FOUND_CHAT_STATUS)))
.toList();
}

private boolean isEqual(String input) {
return input.equals(this.label);
}
Expand Down
23 changes: 22 additions & 1 deletion src/main/java/com/dnd/gongmuin/chat/dto/ChatRoomMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.dnd.gongmuin.chat.domain.ChatRoom;
import com.dnd.gongmuin.chat.dto.response.AcceptChatResponse;
import com.dnd.gongmuin.chat.dto.response.ChatProposalInfo;
import com.dnd.gongmuin.chat.dto.response.ChatProposalResponse;
import com.dnd.gongmuin.chat.dto.response.ChatRoomDetailResponse;
import com.dnd.gongmuin.chat.dto.response.ChatRoomInfo;
import com.dnd.gongmuin.chat.dto.response.ChatRoomSimpleResponse;
Expand Down Expand Up @@ -90,7 +92,6 @@ public static ChatRoomSimpleResponse toChatRoomSimpleResponse(
) {
return new ChatRoomSimpleResponse(
chatRoomInfo.chatRoomId(),
chatRoomInfo.chatStatus(),
new MemberInfo(
chatRoomInfo.partnerId(),
chatRoomInfo.partnerNickname(),
Expand All @@ -103,4 +104,24 @@ public static ChatRoomSimpleResponse toChatRoomSimpleResponse(
);
}

public static ChatProposalResponse toChatProposalResponse(
ChatProposalInfo chatProposalInfo,
LatestChatMessage latestChatMessage
) {
return new ChatProposalResponse(
chatProposalInfo.chatRoomId(),
chatProposalInfo.chatStatus(),
chatProposalInfo.isInquirer(),
new MemberInfo(
chatProposalInfo.partnerId(),
chatProposalInfo.partnerNickname(),
chatProposalInfo.partnerJobGroup(),
chatProposalInfo.partnerProfileImageNo()
),
latestChatMessage.content(),
latestChatMessage.type(),
latestChatMessage.createdAt().toString()
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.dnd.gongmuin.chat.dto.response;

import com.dnd.gongmuin.chat.domain.ChatStatus;
import com.dnd.gongmuin.member.domain.JobGroup;
import com.querydsl.core.annotations.QueryProjection;

public record ChatProposalInfo(
Long chatRoomId,
String chatStatus,
boolean isInquirer,
Long partnerId,
String partnerNickname,
String partnerJobGroup,
int partnerProfileImageNo
) {
@QueryProjection
public ChatProposalInfo(
Long chatRoomId,
ChatStatus chatStatus,
boolean isInquirer,
Long partnerId,
String partnerNickname,
JobGroup partnerJobGroup,
int partnerProfileImageNo
) {
this(
chatRoomId,
chatStatus.getLabel(),
isInquirer,
partnerId,
partnerNickname,
partnerJobGroup.getLabel(),
partnerProfileImageNo
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.dnd.gongmuin.chat.dto.response;

import com.dnd.gongmuin.question_post.dto.response.MemberInfo;

public record ChatProposalResponse (
Long chatRoomId,
String chatStatus,
boolean isInquirer,
MemberInfo chatPartner,
String latestMessage,
String messageType,
String messageCreatedAt
){}
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package com.dnd.gongmuin.chat.dto.response;

import com.dnd.gongmuin.chat.domain.ChatStatus;
import com.dnd.gongmuin.member.domain.JobGroup;
import com.querydsl.core.annotations.QueryProjection;

public record ChatRoomInfo(
Long chatRoomId,
String chatStatus,
Long partnerId,
String partnerNickname,
String partnerJobGroup,
Expand All @@ -15,15 +13,13 @@ public record ChatRoomInfo(
@QueryProjection
public ChatRoomInfo(
Long chatRoomId,
ChatStatus chatStatus,
Long partnerId,
String partnerNickname,
JobGroup partnerJobGroup,
int partnerProfileImageNo
) {
this(
chatRoomId,
chatStatus.getLabel(),
partnerId,
partnerNickname,
partnerJobGroup.getLabel(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

public record ChatRoomSimpleResponse(
Long chatRoomId,
String chatStatus,
MemberInfo chatPartner,
String latestMessage,
String messageType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
import org.springframework.data.domain.Slice;

import com.dnd.gongmuin.chat.domain.ChatStatus;
import com.dnd.gongmuin.chat.dto.response.ChatProposalInfo;
import com.dnd.gongmuin.chat.dto.response.ChatRoomInfo;
import com.dnd.gongmuin.member.domain.Member;

public interface ChatRoomQueryRepository {

Slice<ChatRoomInfo> getChatRoomsByMember(Member member, List<ChatStatus> chatStatuses, Pageable pageable);
Slice<ChatRoomInfo> getChatRoomsByMember(Member member, Pageable pageable);
Slice<ChatProposalInfo> getChatProposalsByMember(Member member, Pageable pageable);

List<Long> getAutoRejectedInquirerIds();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.dnd.gongmuin.chat.repository;


import static com.dnd.gongmuin.chat.domain.QChatRoom.*;

import java.time.LocalDateTime;
Expand All @@ -10,7 +11,9 @@
import org.springframework.data.domain.SliceImpl;

import com.dnd.gongmuin.chat.domain.ChatStatus;
import com.dnd.gongmuin.chat.dto.response.ChatProposalInfo;
import com.dnd.gongmuin.chat.dto.response.ChatRoomInfo;
import com.dnd.gongmuin.chat.dto.response.QChatProposalInfo;
import com.dnd.gongmuin.chat.dto.response.QChatRoomInfo;
import com.dnd.gongmuin.member.domain.Member;
import com.querydsl.core.types.dsl.CaseBuilder;
Expand All @@ -25,17 +28,53 @@ public class ChatRoomQueryRepositoryImpl implements ChatRoomQueryRepository {

public Slice<ChatRoomInfo> getChatRoomsByMember(
Member member,
List<ChatStatus> chatStatuses,
Pageable pageable
) {
List<ChatRoomInfo> content = queryFactory
.select(new QChatRoomInfo(
chatRoom.id,
new CaseBuilder()
.when(chatRoom.inquirer.id.eq(member.getId()))
.then(chatRoom.answerer.id)
.otherwise(chatRoom.inquirer.id),

new CaseBuilder()
.when(chatRoom.inquirer.id.eq(member.getId()))
.then(chatRoom.answerer.nickname)
.otherwise(chatRoom.inquirer.nickname),
new CaseBuilder()
.when(chatRoom.inquirer.id.eq(member.getId()))
.then(chatRoom.answerer.jobGroup)
.otherwise(chatRoom.inquirer.jobGroup),
new CaseBuilder()
.when(chatRoom.inquirer.id.eq(member.getId()))
.then(chatRoom.answerer.profileImageNo)
.otherwise(chatRoom.inquirer.profileImageNo)
))
.from(chatRoom)
.where(chatRoom.inquirer.id.eq(member.getId())
.or(chatRoom.answerer.id.eq(member.getId()))
.and(chatRoom.status.eq(ChatStatus.ACCEPTED)))
.fetch();

boolean hasNext = hasNext(pageable.getPageSize(), content);
return new SliceImpl<>(content, pageable, hasNext);
}

public Slice<ChatProposalInfo> getChatProposalsByMember(Member member, Pageable pageable){
List<ChatProposalInfo> content = queryFactory
.select(new QChatProposalInfo(
chatRoom.id,
chatRoom.status,
new CaseBuilder()
.when(chatRoom.inquirer.id.eq(member.getId()))
.then(true)
.otherwise(false),
new CaseBuilder()
.when(chatRoom.inquirer.id.eq(member.getId()))
.then(chatRoom.answerer.id)
.otherwise(chatRoom.inquirer.id),

new CaseBuilder()
.when(chatRoom.inquirer.id.eq(member.getId()))
.then(chatRoom.answerer.nickname)
Expand All @@ -52,7 +91,7 @@ public Slice<ChatRoomInfo> getChatRoomsByMember(
.from(chatRoom)
.where(chatRoom.inquirer.id.eq(member.getId())
.or(chatRoom.answerer.id.eq(member.getId()))
.and(chatRoom.status.in(chatStatuses)))
.and(chatRoom.status.in(List.of(ChatStatus.REJECTED, ChatStatus.PENDING))))
.fetch();

boolean hasNext = hasNext(pageable.getPageSize(), content);
Expand Down
48 changes: 44 additions & 4 deletions src/main/java/com/dnd/gongmuin/chat/service/ChatRoomService.java
Copy link
Collaborator

Choose a reason for hiding this comment

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

getChatPartner()를 리팩토링해도 괜찮겠네요.

if문에서 바로 return을 던져주고 있기 때문에 else과 else if문을 사용을 줄여도 좋을 것 같아요

private Member getChatPartner(Long memberId, ChatRoom chatRoom) {
		if (Objects.equals(chatRoom.getAnswerer().getId(), memberId)) {
			return chatRoom.getInquirer();
		}
		if (Objects.equals(chatRoom.getInquirer().getId(), memberId)) {
			return chatRoom.getAnswerer();
		}
		throw new ValidationException(ChatErrorCode.UNAUTHORIZED_CHAT_ROOM);
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

아니면 더 좋게 바꿀 수 도 있을 것 같아요. Member 객체에서 memberId만 getter통해 가져오기 보다는 Member 객체가 스스로 일을 하도록 하는거죠.

public class Member {
          private Long memberId;
          ....

          public boolean isSameId(Long otherId) {
                     return Objects.equals(otherId);
          }
}

private Member getChatPartner(Member member, ChatRoom chatRoom) {
		if (member.isSameId(chatRoom.getAnswerer()) {
			return chatRoom.getInquirer();
		}
		if (member.isSameId(chatRoom.getInquirer()))) {
			return chatRoom.getAnswerer();
		}
		throw new ValidationException(ChatErrorCode.UNAUTHORIZED_CHAT_ROOM);
}

Copy link
Member Author

@hyun2371 hyun2371 Nov 15, 2024

Choose a reason for hiding this comment

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

넵. 제안주신 부분 다음 PR에 참고해서 반영하겠습니다! 감사합니다

Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
import org.springframework.transaction.annotation.Transactional;

import com.dnd.gongmuin.chat.domain.ChatRoom;
import com.dnd.gongmuin.chat.domain.ChatStatus;
import com.dnd.gongmuin.chat.dto.ChatMessageMapper;
import com.dnd.gongmuin.chat.dto.ChatRoomMapper;
import com.dnd.gongmuin.chat.dto.request.CreateChatRoomRequest;
import com.dnd.gongmuin.chat.dto.response.AcceptChatResponse;
import com.dnd.gongmuin.chat.dto.response.ChatMessageResponse;
import com.dnd.gongmuin.chat.dto.response.ChatProposalInfo;
import com.dnd.gongmuin.chat.dto.response.ChatProposalResponse;
import com.dnd.gongmuin.chat.dto.response.ChatRoomDetailResponse;
import com.dnd.gongmuin.chat.dto.response.ChatRoomInfo;
import com.dnd.gongmuin.chat.dto.response.ChatRoomSimpleResponse;
Expand Down Expand Up @@ -94,11 +95,10 @@ public CreateChatRoomResponse createChatRoom(CreateChatRoomRequest request, Memb
}

@Transactional(readOnly = true)
public PageResponse<ChatRoomSimpleResponse> getChatRoomsByMember(Member member, List<String> chatStatuses,
Pageable pageable) {
public PageResponse<ChatRoomSimpleResponse> getChatRoomsByMember(Member member, Pageable pageable) {
// 회원 채팅방 정보 가져오기
Slice<ChatRoomInfo> chatRoomInfos = chatRoomRepository.getChatRoomsByMember(
member, ChatStatus.from(chatStatuses), pageable
member, pageable
);

// chatRoomId 리스트 추출
Expand All @@ -118,6 +118,26 @@ public PageResponse<ChatRoomSimpleResponse> getChatRoomsByMember(Member member,
return new PageResponse<>(responses, responses.size(), chatRoomInfos.hasNext());
}

@Transactional(readOnly = true)
public PageResponse<ChatProposalResponse> getChatProposalsByMember(Member member, Pageable pageable) {
Slice<ChatProposalInfo> chatProposalInfos = chatRoomRepository.getChatProposalsByMember(
member, pageable
);

List<Long> chatRoomIds = chatProposalInfos.stream()
.map(ChatProposalInfo::chatRoomId)
.toList();

List<LatestChatMessage> latestChatMessages
= chatMessageQueryRepository.findLatestChatByChatRoomIds(chatRoomIds);
Copy link
Collaborator

Choose a reason for hiding this comment

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

�가장 최신의 채팅 메시지 목록을 가져오는 메서드는 분리를 통해 중복을 줄일 수 있을 것 같아요!


List<ChatProposalResponse> responses = getChatProposalResponse(latestChatMessages,
chatProposalInfos);

return new PageResponse<>(responses, responses.size(), chatProposalInfos.hasNext());
}


@Transactional(readOnly = true)
public ChatRoomDetailResponse getChatRoomById(Long chatRoomId, Member member) {
ChatRoom chatRoom = getChatRoomById(chatRoomId);
Expand Down Expand Up @@ -181,6 +201,26 @@ private List<ChatRoomSimpleResponse> getChatRoomSimpleResponses(List<LatestChatM
}).toList();
}

private List<ChatProposalResponse> getChatProposalResponse(List<LatestChatMessage> latestChatMessages,
Slice<ChatProposalInfo> chatProposalInfos) {
// <chatRoomId, LatestMessage> -> 순서 보장 x
Map<Long, LatestChatMessage> messageMap = latestChatMessages.stream()
.collect(Collectors.toMap(LatestChatMessage::chatRoomId, message -> message));

// 최신순 정렬 및 변환
return chatProposalInfos.stream()
.sorted(
Comparator.comparing(
(ChatProposalInfo info) -> messageMap.get(info.chatRoomId()).createdAt()
).reversed())
.map(chatProposalInfo -> {
LatestChatMessage latestMessage = messageMap.get(chatProposalInfo.chatRoomId());
return ChatRoomMapper.toChatProposalResponse(
chatProposalInfo, latestMessage
);
}).toList();
}

private ChatRoom getChatRoomById(Long id) {
return chatRoomRepository.findById(id)
.orElseThrow(() -> new NotFoundException(ChatErrorCode.NOT_FOUND_CHAT_ROOM));
Expand Down
Loading
Loading