Skip to content

Commit

Permalink
[feat #140] 채팅 요청 목록 조회 API (#141)
Browse files Browse the repository at this point in the history
* [feat] : 채팅방 목록 조회 repository 함수 분리

* [test] : 채팅방 목록 조회 repository 함수 분리 테스트 반영

* [feat] : 채팅 목록 조회 분리에 따른 파라미터 제거

* [test] : 채팅 목록 조회 분리에 따른 파라미터 제거

* [feat] : 채팅 요청 목록 조회 repository 함수 추가

* [feat] : 채팅 요청 목록 조회 관련 DTO 생성

* [feat] : 채팅 목록 조회 DTO 불필요한 필드 삭제

* [feat] : 채팅 요청 목록 DTO mapper 추가

* [feat] : 채팅 요청 목록 조회 비즈니스 로직 추가

* [feat] : 채팅 요청 목록 조회 API 추가

* [test] : 채팅 목록, 채팅 요청 목록 API 분리 반영

* [test] : 채팅 요청 목록 조회 repository 테스트

* [test] : 채팅 요청 목록 조회 비즈니스 로직 테스트

* [test] : 채팅 요청 목록 조회 통합 테스트
  • Loading branch information
hyun2371 authored Nov 15, 2024
1 parent 2db3183 commit 96c5266
Show file tree
Hide file tree
Showing 14 changed files with 323 additions and 48 deletions.
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
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);

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

0 comments on commit 96c5266

Please sign in to comment.