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] 채팅목록 조회 구현 #88

Merged
merged 5 commits into from
Dec 20, 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
Expand Up @@ -2,6 +2,7 @@

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jboss.logging.MDC;
Expand All @@ -11,9 +12,11 @@
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import site.billbill.apiserver.api.chat.dto.request.ChatRequest;
import site.billbill.apiserver.api.chat.dto.response.ChatResponse;
import site.billbill.apiserver.api.chat.dto.response.ChatResponse.ViewChatInfoResponse;
import site.billbill.apiserver.api.chat.service.ChatService;
import site.billbill.apiserver.common.response.BaseResponse;
import site.billbill.apiserver.common.utils.jwt.JWTUtil;
Expand All @@ -29,24 +32,28 @@ public class ChatController {
@Operation(summary = "채팅방 나가기", description = "채팅방 나가기 API")
@PatchMapping("/{channelId}")
public BaseResponse<String> leaveChatChannel(@PathVariable(value = "channelId") String channelId) {
log.info("api 호출 정상적~");
String userId = MDC.get(JWTUtil.MDC_USER_ID).toString();
return new BaseResponse<>(chatService.leaveChatChannel(channelId,userId));
}

@Operation(summary = "채팅방 생성 및 id 조회", description = "빌리기 버튼 누를 때 api")
@PostMapping("")
public BaseResponse<String> startChannel(@RequestBody ChatRequest.borrowInfo request) {
log.info("api 호출 정상적~");
String userId = MDC.get(JWTUtil.MDC_USER_ID).toString();
return new BaseResponse<>(chatService.startChannel(request, userId));
}

@Operation(summary = "채팅방 info 조회", description = "채팅방 info 조회 API")
@GetMapping("/{channelId}")
public BaseResponse<ChatResponse.ViewChannelInfoResponse> getInfoChannel(@PathVariable(value = "channelId") String channelId) {
log.info("api 호출 정상적~");
String userId = MDC.get(JWTUtil.MDC_USER_ID).toString();
return new BaseResponse<>(chatService.getInfoChannel(channelId,userId));
return new BaseResponse<>(chatService.getInfoChannel(channelId, userId));
}

@Operation(summary = "채팅목록 조회", description = "채팅목록 조회 API")
@GetMapping("/list")
public BaseResponse<List<ViewChatInfoResponse>> getChatList(@RequestParam(required = false) String beforeTimestamp) {
String userId = MDC.get(JWTUtil.MDC_USER_ID).toString();
return new BaseResponse<>(chatService.getChatList(beforeTimestamp, userId));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package site.billbill.apiserver.api.chat.converter;

import java.time.LocalDate;
import site.billbill.apiserver.api.chat.dto.request.WebhookRequest.ChatInfo;
import site.billbill.apiserver.api.chat.dto.response.ChatResponse;
import site.billbill.apiserver.model.chat.ChatChannelJpaEntity;
import site.billbill.apiserver.model.post.ItemsJpaEntity;
Expand All @@ -19,7 +20,9 @@ public static ChatChannelJpaEntity toChatChannel(String channelId, ItemsJpaEntit
.build();
}

public static ChatResponse.ViewChannelInfoResponse toViewChannelInfo(ChatChannelJpaEntity channel, UserJpaEntity opponent, ItemsJpaEntity item, int totalPrice,
public static ChatResponse.ViewChannelInfoResponse toViewChannelInfo(ChatChannelJpaEntity channel,
UserJpaEntity opponent, ItemsJpaEntity item,
int totalPrice,
String status, String userId) {

return ChatResponse.ViewChannelInfoResponse.builder()
Expand All @@ -36,4 +39,26 @@ public static ChatResponse.ViewChannelInfoResponse toViewChannelInfo(ChatChannel
.myId(userId)
.build();
}

public static ChatResponse.ViewChatInfoResponse toViewChatInfo(ChatInfo chatInfo,
String userId, UserJpaEntity opponent,
ItemsJpaEntity item) {
int unReadCount = chatInfo.getUnreadCount();

if (chatInfo.getLastSender().equals(userId)) {
unReadCount = 0;
}

return ChatResponse.ViewChatInfoResponse.builder()
.channelId(chatInfo.getChannelId())
.lastChat(chatInfo.getLastChat())
.lastSender(chatInfo.getLastSender())
.updatedAt(chatInfo.getUpdatedAt())
.unreadCount(unReadCount)
.opponentId(opponent.getUserId())
.opponentProfileUrl(opponent.getProfile())
.opponentNickname(opponent.getNickname())
.itemFirstUrl(item.getImages().get(0))
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package site.billbill.apiserver.api.chat.dto.request;

import java.time.LocalDateTime;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

public class WebhookRequest {
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class ChatInfo {
private String channelId;
private int unreadCount;
private String lastChat;
private String lastSender;
private LocalDateTime updatedAt;
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class ChatInfoList {
private List<ChatInfo> chatInfoList;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import lombok.Builder;
import lombok.Getter;

Expand All @@ -23,4 +25,18 @@ public static class ViewChannelInfoResponse {
@JsonFormat(pattern = "yyyy-MM-dd")
LocalDate endedAt;
}

@Getter
@Builder
public static class ViewChatInfoResponse {
private String channelId;
private int unreadCount;
private String lastChat;
private String lastSender;
private LocalDateTime updatedAt;
private String opponentId;
private String opponentNickname;
private String opponentProfileUrl;
private String itemFirstUrl;
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package site.billbill.apiserver.api.chat.service;

import java.util.List;
import site.billbill.apiserver.api.chat.dto.request.ChatRequest;
import site.billbill.apiserver.api.chat.dto.response.ChatResponse.ViewChannelInfoResponse;
import site.billbill.apiserver.api.chat.dto.response.ChatResponse.ViewChatInfoResponse;

public interface ChatService {
String leaveChatChannel(String postId, String userId);

ViewChannelInfoResponse getInfoChannel(String channelId, String userId);

String startChannel(ChatRequest.borrowInfo request, String userId);

List<ViewChatInfoResponse> getChatList(String beforeTimestamp, String userId);
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package site.billbill.apiserver.api.chat.service;

import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import site.billbill.apiserver.api.chat.converter.ChatConverter;
import site.billbill.apiserver.api.chat.dto.request.ChatRequest;
import site.billbill.apiserver.api.chat.dto.request.WebhookRequest.ChatInfo;
import site.billbill.apiserver.api.chat.dto.request.WebhookRequest.ChatInfoList;
import site.billbill.apiserver.api.chat.dto.response.ChatResponse;
import site.billbill.apiserver.api.chat.dto.response.ChatResponse.ViewChatInfoResponse;
import site.billbill.apiserver.common.enums.exception.ErrorCode;
import site.billbill.apiserver.common.utils.ULID.ULIDUtil;
import site.billbill.apiserver.exception.CustomException;
Expand All @@ -27,7 +30,6 @@
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ChatServiceImpl implements ChatService {
private static final Logger log = LoggerFactory.getLogger(ChatServiceImpl.class);
private final ChatRepository chatRepository;
private final UserRepository userRepository;
private final ItemsRepository itemsRepository;
Expand Down Expand Up @@ -89,4 +91,25 @@ public ChatResponse.ViewChannelInfoResponse getInfoChannel(String channelId, Str

return ChatConverter.toViewChannelInfo(chatChannel, opponent, item, totalPrice, status, userId);
}

public List<ViewChatInfoResponse> getChatList(String beforeTimestamp, String userId) {
userRepository.findById(userId)
.orElseThrow(() -> new CustomException(ErrorCode.NotFound, "회원을 찾을 수 없습니다.", HttpStatus.NOT_FOUND));

List<String> activeChatIdsByUserId = chatRepository.findActiveChatIdsByUserId(userId);

if (activeChatIdsByUserId == null || activeChatIdsByUserId.isEmpty()) {
return Collections.emptyList();
}

ChatInfoList webhookResult = webhookService.sendWebhookForChatList(activeChatIdsByUserId, beforeTimestamp);
List<ChatInfo> chatInfoList = webhookResult.getChatInfoList();

return chatInfoList.stream().map(chatInfo -> {
ChatChannelJpaEntity chatChannel = chatRepository.findById(chatInfo.getChannelId())
.orElseThrow(() -> new CustomException(ErrorCode.NotFound, "채널을 찾을 수 없습니다.", HttpStatus.NOT_FOUND));
UserJpaEntity opponent = chatChannel.getOpponent(userId);
return ChatConverter.toViewChatInfo(chatInfo, userId, opponent, chatChannel.getItem());
}).collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package site.billbill.apiserver.api.chat.service;

import java.util.List;
import site.billbill.apiserver.api.chat.dto.request.WebhookRequest;

public interface WebhookService {
void sendWebhookForChatRoomCreate(String channelId, String contact, String owner);
WebhookRequest.ChatInfoList sendWebhookForChatList(List<String> chatRoomIds, String beforeTimestamp);
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
package site.billbill.apiserver.api.chat.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import site.billbill.apiserver.api.chat.dto.request.WebhookRequest;
import site.billbill.apiserver.common.enums.exception.ErrorCode;
import site.billbill.apiserver.exception.CustomException;

@Slf4j
@Service
public class WebhookServiceImpl {
public class WebhookServiceImpl implements WebhookService {
private final WebClient webClient;
private final ObjectMapper objectMapper;

@Value("${webhook.url}")
private String webhookUrl;

@Autowired
public WebhookServiceImpl(WebClient.Builder webClientBuilder) {
public WebhookServiceImpl(WebClient.Builder webClientBuilder, ObjectMapper objectMapper) {
this.webClient = webClientBuilder.baseUrl(webhookUrl).build();
this.objectMapper = objectMapper;
}

public void sendWebhookForChatRoomCreate(String channelId, String contact, String owner) {
Expand All @@ -27,12 +36,33 @@ public void sendWebhookForChatRoomCreate(String channelId, String contact, Strin
payload.put("contactId", contact);
payload.put("ownerId", owner);

webClient.post()
.uri("")
.bodyValue(payload)
try {
webClient.post()
.uri("/channel")
.bodyValue(payload)
.retrieve()
.bodyToMono(Void.class)
.block();
} catch (Exception e) {
log.error("Webhook 호출 실패: {}", e.getMessage());
throw new CustomException(ErrorCode.ServerError, "Webhook 호출 실패", HttpStatus.BAD_GATEWAY);
}
}

public WebhookRequest.ChatInfoList sendWebhookForChatList(List<String> chatRoomIds, String beforeTimestamp) {
String jsonResponse = webClient.post()
.uri("/chat/list")
.bodyValue(Map.of(
"chatRoomIds", chatRoomIds,
"beforeTimestamp", beforeTimestamp
))
.retrieve()
.bodyToMono(Void.class)
.doOnError(error -> log.error("Webhook 호출 실패: {}", error.getMessage()))
.subscribe();
.bodyToMono(String.class)
.block();
try {
return objectMapper.readValue(jsonResponse, WebhookRequest.ChatInfoList.class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

public enum ChannelState {
PRE, // 거래전
ACCEPTED, // 거래수락
CONFIRMED, // 거래확정
CANCELLED // 거래취소
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import site.billbill.apiserver.model.chat.ChatChannelJpaEntity;
import site.billbill.apiserver.model.post.ItemsJpaEntity;
import site.billbill.apiserver.model.user.UserJpaEntity;
Expand All @@ -25,4 +26,10 @@ public interface ChatRepository extends JpaRepository<ChatChannelJpaEntity,Strin
"And c.contact = :user "
)
List<ChatChannelJpaEntity> findAllByItemAndContactUser(ItemsJpaEntity item, UserJpaEntity user);

@Query("SELECT c.channelId " +
"FROM ChatChannelJpaEntity c " +
"WHERE (c.owner.userId = :userId AND c.ownerLeft = false) " +
" OR (c.contact.userId = :userId AND c.contactLeft = false)")
List<String> findActiveChatIdsByUserId(String userId);
}
Loading