From b3f330e86102559d713bcbf64a3da8ed9d905d04 Mon Sep 17 00:00:00 2001 From: Admin Date: Wed, 24 Sep 2025 19:45:03 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EB=82=B4=20=EC=B1=84=ED=8C=85?= =?UTF-8?q?=EB=B0=A9=20=EB=AA=A9=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/chat/controller/ChatController.java | 10 +++++-- .../server/api/chat/dto/MyChatPageResDto.java | 27 +++++++++++++++++++ .../repository/ChatMessageRepository.java | 9 +++++++ .../chat/repository/ChatRoomRepository.java | 17 ++++++++++++ .../server/api/chat/service/ChatService.java | 26 +++++++++++++----- 5 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/campick/server/api/chat/dto/MyChatPageResDto.java diff --git a/src/main/java/com/campick/server/api/chat/controller/ChatController.java b/src/main/java/com/campick/server/api/chat/controller/ChatController.java index 0590a19..c69d84b 100644 --- a/src/main/java/com/campick/server/api/chat/controller/ChatController.java +++ b/src/main/java/com/campick/server/api/chat/controller/ChatController.java @@ -5,10 +5,13 @@ import com.campick.server.api.chat.entity.ChatRoom; import com.campick.server.api.chat.service.ChatService; import com.campick.server.common.config.security.SecurityMember; +import com.campick.server.common.dto.PageResponseDto; import com.campick.server.common.response.ApiResponse; import com.campick.server.common.response.SuccessStatus; import lombok.RequiredArgsConstructor; import org.checkerframework.checker.units.qual.A; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; @@ -45,10 +48,13 @@ public ResponseEntity> readChatRoom(@PathVariable Long chatRoo } @GetMapping("/my") - public ResponseEntity> getMyChatRoom(@AuthenticationPrincipal SecurityMember securityMember) { + public ResponseEntity>> getMyChatRoom(@RequestParam(defaultValue = "0") Integer page, + @RequestParam(defaultValue = "10") Integer size, + @AuthenticationPrincipal SecurityMember securityMember) { + Pageable pageable = PageRequest.of(page, size); Long memberId = securityMember.getId(); - return ApiResponse.success(SuccessStatus.SEND_MY_CHATROOMS, chatService.getMyChatRooms(memberId)); + return ApiResponse.success(SuccessStatus.SEND_MY_CHATROOMS, chatService.getMyChatRooms(memberId, pageable)); } @GetMapping("/totalUnreadMessage") diff --git a/src/main/java/com/campick/server/api/chat/dto/MyChatPageResDto.java b/src/main/java/com/campick/server/api/chat/dto/MyChatPageResDto.java new file mode 100644 index 0000000..2ee02fd --- /dev/null +++ b/src/main/java/com/campick/server/api/chat/dto/MyChatPageResDto.java @@ -0,0 +1,27 @@ +package com.campick.server.api.chat.dto; + +import lombok.Getter; +import org.springframework.data.domain.Page; + +import java.util.List; + +@Getter +public class MyChatPageResDto { + private final long totalElements; // 잔체 요소 수 + private final int totalPages; // 전체 페이지 수 + private final int page; // 현재 페이지(0부터 시작) + private final int size; // 요청한 페이지 사이즈 + private final boolean isLast; // 마지막 페이지 여부 + private final int totalUnreadMessage; + private final List content; + + public MyChatPageResDto(Page page, int totalUnreadMessage) { + this.content = page.getContent(); + this.page = page.getNumber(); + this.size = page.getSize(); + this.totalElements = page.getTotalElements(); + this.totalPages = page.getTotalPages(); + this.isLast = page.isLast(); + this.totalUnreadMessage = totalUnreadMessage; + } +} diff --git a/src/main/java/com/campick/server/api/chat/repository/ChatMessageRepository.java b/src/main/java/com/campick/server/api/chat/repository/ChatMessageRepository.java index e7ffc2f..36f5b81 100644 --- a/src/main/java/com/campick/server/api/chat/repository/ChatMessageRepository.java +++ b/src/main/java/com/campick/server/api/chat/repository/ChatMessageRepository.java @@ -30,6 +30,15 @@ Integer markMessagesAsRead(@Param("chatRoomId") Long chatRoomId, "ORDER BY m.createdAt DESC LIMIT 1") ChatMessage findLastMessageByChatRoomId(@Param("chatRoomId") Long chatRoomId); + @Query(""" + SELECT m FROM ChatMessage m + WHERE m.chatRoom.id IN :chatRoomIds + AND m.createdAt IN ( + SELECT MAX(m2.createdAt) FROM ChatMessage m2 WHERE m2.chatRoom.id = m.chatRoom.id + ) + """) + List findLastMessages(@Param("chatRoomIds") List chatRoomIds); + @Query("SELECT COUNT(m) FROM ChatMessage m " + "WHERE m.chatRoom.id = :chatRoomId " + "AND m.isRead = false " + diff --git a/src/main/java/com/campick/server/api/chat/repository/ChatRoomRepository.java b/src/main/java/com/campick/server/api/chat/repository/ChatRoomRepository.java index 3ba29d2..3a594c6 100644 --- a/src/main/java/com/campick/server/api/chat/repository/ChatRoomRepository.java +++ b/src/main/java/com/campick/server/api/chat/repository/ChatRoomRepository.java @@ -3,6 +3,8 @@ import com.campick.server.api.chat.entity.ChatRoom; import com.campick.server.api.member.entity.Member; import com.campick.server.api.product.entity.Product; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -28,5 +30,20 @@ public interface ChatRoomRepository extends JpaRepository { " OR (b.id = :memberId AND cr.isBuyerOut = false))") List findAllByMemberId(@Param("memberId") Long memberId); + @Query(""" + SELECT c FROM ChatRoom c + WHERE c.seller.id = :memberId OR c.buyer.id = :memberId + ORDER BY ( + SELECT MAX(m.createdAt) FROM ChatMessage m WHERE m.chatRoom = c + ) DESC + """) + Page findByMemberIdOrderByLastMessageDesc(@Param("memberId") Long memberId, Pageable pageable); + Optional findByProductAndSellerAndBuyer(Product product, Member seller, Member buyer); + + @Query(""" + SELECT COUNT(c) FROM ChatRoom c + WHERE c.seller.id = :memberId OR c.buyer.id = :memberId + """) + long countChatRoomByMemberId(@Param("memberId") Long memberId); } diff --git a/src/main/java/com/campick/server/api/chat/service/ChatService.java b/src/main/java/com/campick/server/api/chat/service/ChatService.java index 0489fff..5fc1997 100644 --- a/src/main/java/com/campick/server/api/chat/service/ChatService.java +++ b/src/main/java/com/campick/server/api/chat/service/ChatService.java @@ -10,6 +10,7 @@ import com.campick.server.api.product.entity.Product; import com.campick.server.api.product.entity.ProductImage; import com.campick.server.api.product.repository.ProductRepository; +import com.campick.server.common.dto.PageResponseDto; import com.campick.server.common.exception.NotFoundException; import com.campick.server.common.response.ErrorStatus; import com.campick.server.common.util.TimeUtil; @@ -20,12 +21,17 @@ import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import java.io.IOException; import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -121,8 +127,14 @@ public void readChatRoom(Long chatRoomId, Long memberId) { Integer readMessageCount = chatMessageRepository.markMessagesAsRead(chatRoomId, memberId); } - public MyChatResDto getMyChatRooms(Long memberId) { - List myChatRooms = chatRoomRepository.findAllByMemberId(memberId); + public MyChatPageResDto getMyChatRooms(Long memberId, Pageable pageable) { + Page myChatRooms = chatRoomRepository.findByMemberIdOrderByLastMessageDesc(memberId, pageable); + List chatRoomIds = myChatRooms.stream().map(ChatRoom::getId).toList(); + Map lastMessageMap = chatMessageRepository.findLastMessages(chatRoomIds) + .stream() + .collect(Collectors.toMap(m -> m.getChatRoom().getId(), Function.identity())); + long total = chatRoomRepository.countChatRoomByMemberId(memberId); + List chatListDtos = myChatRooms.stream().map( chatRoom -> { String thumbnailUrl = chatRoom.getProduct().getImages().stream() @@ -131,7 +143,7 @@ public MyChatResDto getMyChatRooms(Long memberId) { .findFirst() .orElse(null); - ChatMessage lastChatMessage = chatMessageRepository.findLastMessageByChatRoomId(chatRoom.getId()); + ChatMessage lastChatMessage = lastMessageMap.get(chatRoom.getId()); Integer unreadMessageCount = chatMessageRepository.countUnreadMessages(chatRoom.getId(), memberId); return ChatListDto.builder() @@ -146,10 +158,10 @@ public MyChatResDto getMyChatRooms(Long memberId) { .build(); }).toList(); - return MyChatResDto.builder() - .chatRoom(chatListDtos) - .totalUnreadMessage(chatMessageRepository.countAllUnreadMessages(memberId)) - .build(); + Integer totalUnreadMessage = chatMessageRepository.countAllUnreadMessages(memberId); + Page pageImpl = new PageImpl<>(chatListDtos, pageable, total); + + return new MyChatPageResDto<>(pageImpl, totalUnreadMessage); } public Integer getTotalUnreadMessage(Long memberId) { From 234d093a0f39ac1bd18f40a0d132c8499142462a Mon Sep 17 00:00:00 2001 From: Admin Date: Wed, 24 Sep 2025 21:10:26 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EB=82=B4=EC=97=AD=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/chat/controller/ChatController.java | 7 ++- .../api/chat/dto/ChatRoomPageResDto.java | 40 +++++++++++++++++ .../repository/ChatMessageRepository.java | 4 ++ .../server/api/chat/service/ChatService.java | 43 ++++++++----------- 4 files changed, 68 insertions(+), 26 deletions(-) create mode 100644 src/main/java/com/campick/server/api/chat/dto/ChatRoomPageResDto.java diff --git a/src/main/java/com/campick/server/api/chat/controller/ChatController.java b/src/main/java/com/campick/server/api/chat/controller/ChatController.java index c69d84b..c465aab 100644 --- a/src/main/java/com/campick/server/api/chat/controller/ChatController.java +++ b/src/main/java/com/campick/server/api/chat/controller/ChatController.java @@ -35,8 +35,11 @@ public ResponseEntity> startChat(@RequestBody ChatRoomReqDto c } @GetMapping("/{chatRoomId}") - public ResponseEntity> getChatRoom(@PathVariable Long chatRoomId) { - return ApiResponse.success(SuccessStatus.SEND_LOAD_CHATROOM, chatService.getChatRoom(chatRoomId)); + public ResponseEntity>> getChatRoom(@PathVariable Long chatRoomId, + @RequestParam(defaultValue = "0") Integer page, + @RequestParam(defaultValue = "20") Integer size) { + Pageable pageable = PageRequest.of(page, size); + return ApiResponse.success(SuccessStatus.SEND_LOAD_CHATROOM, chatService.getChatRoom(chatRoomId, pageable)); } @PatchMapping("/{chatRoomId}") diff --git a/src/main/java/com/campick/server/api/chat/dto/ChatRoomPageResDto.java b/src/main/java/com/campick/server/api/chat/dto/ChatRoomPageResDto.java new file mode 100644 index 0000000..e9107ee --- /dev/null +++ b/src/main/java/com/campick/server/api/chat/dto/ChatRoomPageResDto.java @@ -0,0 +1,40 @@ +package com.campick.server.api.chat.dto; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.data.domain.Page; + +import java.util.List; + +@Getter @Setter +public class ChatRoomPageResDto { + private final long totalElements; // 잔체 요소 수 + private final int totalPages; // 전체 페이지 수 + private final int page; // 현재 페이지(0부터 시작) + private final int size; // 요청한 페이지 사이즈 + private final boolean isLast; // 마지막 페이지 여부 + + private Long sellerId; + private Long buyerId; + private String sellerNickname; + private String buyerNickname; + private String sellerProfileImage; + private String sellerPhoneNumber; + private Long productId; + private String productTitle; + private String productImage; + private String productStatus; + private String productPrice; + private Boolean isActive; + + private final List chatData; + + public ChatRoomPageResDto(Page page) { + this.chatData = page.getContent(); + this.page = page.getNumber(); + this.size = page.getSize(); + this.totalElements = page.getTotalElements(); + this.totalPages = page.getTotalPages(); + this.isLast = page.isLast(); + } +} diff --git a/src/main/java/com/campick/server/api/chat/repository/ChatMessageRepository.java b/src/main/java/com/campick/server/api/chat/repository/ChatMessageRepository.java index 36f5b81..f06eb87 100644 --- a/src/main/java/com/campick/server/api/chat/repository/ChatMessageRepository.java +++ b/src/main/java/com/campick/server/api/chat/repository/ChatMessageRepository.java @@ -1,6 +1,8 @@ package com.campick.server.api.chat.repository; import com.campick.server.api.chat.entity.ChatMessage; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -52,4 +54,6 @@ Integer countUnreadMessages(@Param("chatRoomId") Long chatRoomId, "AND cm.isRead = false " + "AND cm.member.id <> :memberId") Integer countAllUnreadMessages(@Param("memberId") Long memberId); + + Page findByChatRoomIdOrderByCreatedAtDesc(Long chatRoomId, Pageable pageable); } diff --git a/src/main/java/com/campick/server/api/chat/service/ChatService.java b/src/main/java/com/campick/server/api/chat/service/ChatService.java index 5fc1997..9dbbbdb 100644 --- a/src/main/java/com/campick/server/api/chat/service/ChatService.java +++ b/src/main/java/com/campick/server/api/chat/service/ChatService.java @@ -10,7 +10,6 @@ import com.campick.server.api.product.entity.Product; import com.campick.server.api.product.entity.ProductImage; import com.campick.server.api.product.repository.ProductRepository; -import com.campick.server.common.dto.PageResponseDto; import com.campick.server.common.exception.NotFoundException; import com.campick.server.common.response.ErrorStatus; import com.campick.server.common.util.TimeUtil; @@ -114,11 +113,11 @@ public Long startChatRoom(ChatRoomReqDto chatRoomReqDto, Long memberId) { return chatRoom.getId(); } - public ChatRoomResDto getChatRoom(Long chatRoomId) { + public ChatRoomPageResDto getChatRoom(Long chatRoomId, Pageable pageable) { ChatRoom chatRoom = chatRoomRepository.findDetailById(chatRoomId).orElseThrow( () -> new NotFoundException(ErrorStatus.CHAT_NOT_FOUND.getMessage()) ); - List chatMessages = chatMessageRepository.findMessagesByChatRoomId(chatRoomId); + Page chatMessages = chatMessageRepository.findByChatRoomIdOrderByCreatedAtDesc(chatRoomId, pageable); return convertToChatRoomResDto(chatRoom, chatMessages); } @@ -259,33 +258,29 @@ public void broadcastSoldEvent(WebSocketSession session, JsonNode data) { } } - private ChatRoomResDto convertToChatRoomResDto(ChatRoom chatRoom, List chatMessages) { + private ChatRoomPageResDto convertToChatRoomResDto(ChatRoom chatRoom, Page chatMessages) { String thumbnailImage = chatRoom.getProduct().getImages().stream() .filter(ProductImage::getIsThumbnail) .map(ProductImage::getImageUrl) .findFirst() .orElse(null); - ChatRoomResDto chatRoomResDto = new ChatRoomResDto(); - chatRoomResDto.setSellerId(chatRoom.getSeller().getId()); - chatRoomResDto.setSellerNickname(chatRoom.getSeller().getNickname()); - chatRoomResDto.setBuyerId(chatRoom.getBuyer().getId()); - chatRoomResDto.setBuyerNickname(chatRoom.getBuyer().getNickname()); - chatRoomResDto.setSellerProfileImage(chatRoom.getSeller().getProfileImageUrl()); - chatRoomResDto.setSellerPhoneNumber(chatRoom.getSeller().getMobileNumber()); - chatRoomResDto.setProductId(chatRoom.getProduct().getId()); - chatRoomResDto.setProductTitle(chatRoom.getProduct().getTitle()); - chatRoomResDto.setProductImage(thumbnailImage); - chatRoomResDto.setProductStatus(chatRoom.getProduct().getStatus().toString()); - chatRoomResDto.setProductPrice(chatRoom.getProduct().getCost().toString()); - chatRoomResDto.setIsActive(!chatRoom.getIsSellerOut() && !chatRoom.getIsBuyerOut()); - - List chatMessageResDto = chatMessages.stream() - .map(this::convertToChatMessageResDto - ).toList(); - chatRoomResDto.setChatData(chatMessageResDto); - - return chatRoomResDto; + ChatRoomPageResDto chatRoomPageResDto = new ChatRoomPageResDto<>(chatMessages); + + chatRoomPageResDto.setSellerId(chatRoom.getSeller().getId()); + chatRoomPageResDto.setSellerNickname(chatRoom.getSeller().getNickname()); + chatRoomPageResDto.setBuyerId(chatRoom.getBuyer().getId()); + chatRoomPageResDto.setBuyerNickname(chatRoom.getBuyer().getNickname()); + chatRoomPageResDto.setSellerProfileImage(chatRoom.getSeller().getProfileImageUrl()); + chatRoomPageResDto.setSellerPhoneNumber(chatRoom.getSeller().getMobileNumber()); + chatRoomPageResDto.setProductId(chatRoom.getProduct().getId()); + chatRoomPageResDto.setProductTitle(chatRoom.getProduct().getTitle()); + chatRoomPageResDto.setProductImage(thumbnailImage); + chatRoomPageResDto.setProductStatus(chatRoom.getProduct().getStatus().toString()); + chatRoomPageResDto.setProductPrice(chatRoom.getProduct().getCost().toString()); + chatRoomPageResDto.setIsActive(!chatRoom.getIsSellerOut() && !chatRoom.getIsBuyerOut()); + + return chatRoomPageResDto; } public void removeFromChatRoomMap(Long memberId, WebSocketSession session) {