Skip to content
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 @@ -11,8 +11,17 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws/chat") // 클라이언트 WebSocket 연결 지점
.setAllowedOriginPatterns("http://localhost:63342")
.setAllowedOriginPatterns(
"http://localhost:63342",
"http://localhost:5173", // Vite 기본
"http://localhost:3000", // CRA/Next 기본
"http://127.0.0.1:*",
"http://192.168.*.*:*") // 같은 LAN의 실제 기기 테스트용
.withSockJS(); // fallback for old browsers

// ✅ 모바일/안드로이드용 (네이티브 WebSocket)
registry.addEndpoint("/ws/chat-native")
.setAllowedOriginPatterns("*"); // wss 사용 시 TLS 세팅
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@
import com.assu.server.domain.chat.dto.ChatResponseDTO;
import com.assu.server.domain.chat.service.ChatService;
import com.assu.server.global.apiPayload.code.status.SuccessStatus;
import com.assu.server.global.util.PrincipalDetails;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import com.assu.server.global.apiPayload.BaseResponse;
import org.springframework.messaging.simp.SimpMessagingTemplate;

import java.util.List;

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/chat")
Expand All @@ -22,64 +26,89 @@ public class ChatController {
private final SimpMessagingTemplate simpMessagingTemplate;

@Operation(
summary = "채팅방 목록 조회 API",
description = "Request Header에 User id를 입력해 주세요."
summary = "채팅방을 생성하는 API",
description = "# [v1.0 (2025-08-05)](https://clumsy-seeder-416.notion.site/2241197c19ed80c38871ec77deced713) 채팅방을 생성합니다.\n"+
"- storeId: Request Body, Long\n" +
"- partnerId: Request Body, Long\n"
)
@GetMapping("/rooms")
public BaseResponse<List<com.assu.server.domain.chat.dto.ChatRoomListResultDTO>> getChatRoomList() {
return BaseResponse.onSuccess(SuccessStatus._OK, chatService.getChatRoomList());
@PostMapping("/rooms")
public BaseResponse<ChatResponseDTO.CreateChatRoomResponseDTO> createChatRoom(
@AuthenticationPrincipal PrincipalDetails pd,
@RequestBody ChatRequestDTO.CreateChatRoomRequestDTO request) {
Long memberId = pd.getMember().getId();
return BaseResponse.onSuccess(SuccessStatus._OK, chatService.createChatRoom(request, memberId));
}

@Operation(
summary = "채팅방 생성 API",
description = "상대방의 id를 request body에 입력해 주세요"
summary = "채팅방 목록을 조회하는 API",
description = "# [v1.0 (2025-08-05)](https://clumsy-seeder-416.notion.site/API-1d71197c19ed819f8f70fb437e9ce62b?p=2241197c19ed816993c3c5ae17d6f099&pm=s) 채팅방 목록을 조회합니다.\n"
)
@PostMapping("/create/rooms")
public BaseResponse<ChatResponseDTO.CreateChatRoomResponseDTO> createChatRoom(@RequestBody ChatRequestDTO.CreateChatRoomRequestDTO request) {
return BaseResponse.onSuccess(SuccessStatus._OK, chatService.createChatRoom(request));
@GetMapping("/rooms")
public BaseResponse<List<com.assu.server.domain.chat.dto.ChatRoomListResultDTO>> getChatRoomList(
@AuthenticationPrincipal PrincipalDetails pd
) {
Long memberId = pd.getMember().getId();
return BaseResponse.onSuccess(SuccessStatus._OK, chatService.getChatRoomList(memberId));
}

@Operation(
summary = "채팅 API",
description = "roomId, senderId, message를 입력해 주세요"
description = "# [v1.0 (2025-08-05)](https://clumsy-seeder-416.notion.site/2241197c19ed800eab45c35073761c97?v=2241197c19ed8134b64f000cc26c5d31&p=2371197c19ed80968342e2bc8fe88cee&pm=s) 메시지를 전송합니다.\n"+
"- roomId: Request Body, Long\n" +
"- senderId: Request Body, Long\n"+
"- receiverId: Request Body, Long\n" +
"- message: Request Body, String\n"
)
@MessageMapping("/send")
public void handleMessage(@Payload ChatRequestDTO.ChatMessageRequestDTO request) {
log.info("[WS] handleMessage IN: {}", request); // ★ 호출 여부 확인
ChatResponseDTO.SendMessageResponseDTO response = chatService.handleMessage(request);

log.info("[WS] handleMessage SAVED id={}", response.messageId()); // 저장 확인용
simpMessagingTemplate.convertAndSend("/sub/chat/" + request.roomId(), response);
}

@Operation(
summary = "메시지 읽음 처리 API",
description = "roomId를 입력해 주세요."
description = "# [v1.0 (2025-08-05)](https://clumsy-seeder-416.notion.site/2241197c19ed800eab45c35073761c97?v=2241197c19ed8134b64f000cc26c5d31&p=2241197c19ed81ffa771cb18ab157b54&pm=s) 메시지를 읽음처리합니다.\n"+
"- roomId: Path Variable, Long\n"
)
@PatchMapping("rooms/{roomId}/read")
public BaseResponse<ChatResponseDTO.ReadMessageResponseDTO> readMessage(
@PathVariable Long roomId) {
ChatResponseDTO.ReadMessageResponseDTO response = chatService.readMessage(roomId);
@AuthenticationPrincipal PrincipalDetails pd,
@PathVariable Long roomId
) {
Long memberId = pd.getMember().getId();
ChatResponseDTO.ReadMessageResponseDTO response = chatService.readMessage(roomId, memberId);
return BaseResponse.onSuccess(SuccessStatus._OK, response);
}

@Operation(
summary = "채팅방 상세 조회 API",
description = "roomId를 입력해 주세요."
description = "# [v1.0 (2025-08-05)](https://clumsy-seeder-416.notion.site/2241197c19ed800eab45c35073761c97?v=2241197c19ed8134b64f000cc26c5d31&p=2241197c19ed81399395fd66f73730af&pm=s) 채팅방을 클릭했을 때 메시지를 조회합니다.\n"+
"- roomId: Path Variable, Long\n"
)
@GetMapping("rooms/{roomId}/messages")
public BaseResponse<ChatResponseDTO.ChatHistoryResponseDTO> getChatHistory(@PathVariable Long roomId) {
ChatResponseDTO.ChatHistoryResponseDTO response = chatService.readHistory(roomId);
public BaseResponse<ChatResponseDTO.ChatHistoryResponseDTO> getChatHistory(
@AuthenticationPrincipal PrincipalDetails pd,
@PathVariable Long roomId
) {
Long memberId = pd.getMember().getId();
ChatResponseDTO.ChatHistoryResponseDTO response = chatService.readHistory(roomId, memberId);
return BaseResponse.onSuccess(SuccessStatus._OK, response);
}

@Operation(
summary = "채팅방 나가기 API" +
" (참여자가 2명이면 채팅방이 살아있지만, 이미 한 명이 나갔다면 채팅방이 삭제됩니다.)",
description = "roomId를 입력해 주세요."
summary = "채팅방을 나가는 API" +
"참여자가 2명이면 채팅방이 살아있지만, 이미 한 명이 나갔다면 채팅방이 삭제됩니다.",
description = "# [v1.0 (2025-08-05)](https://clumsy-seeder-416.notion.site/2241197c19ed800eab45c35073761c97?v=2241197c19ed8134b64f000cc26c5d31&p=2371197c19ed8079a6e1c2331cb4f534&pm=s) 채팅방을 나갑니다.\n"+
"- roomId: Path Variable, Long\n"
)
@DeleteMapping("rooms/{roomId}/leave")
public BaseResponse<ChatResponseDTO.LeaveChattingRoomResponseDTO> leaveChattingRoom(
@AuthenticationPrincipal PrincipalDetails pd,
@PathVariable Long roomId
) {
return BaseResponse.onSuccess(SuccessStatus._OK, chatService.leaveChattingRoom(roomId));
Long memberId = pd.getMember().getId();
return BaseResponse.onSuccess(SuccessStatus._OK, chatService.leaveChattingRoom(roomId, memberId));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ public static ChattingRoom toCreateChattingRoom(Admin admin, Partner partner) {
}

public static ChatResponseDTO.CreateChatRoomResponseDTO toCreateChatRoomIdDTO(ChattingRoom room) {
return new ChatResponseDTO.CreateChatRoomResponseDTO(room.getId());
return ChatResponseDTO.CreateChatRoomResponseDTO.builder()
.roomId(room.getId())
.adminViewName(room.getPartner().getName())
.partnerViewName(room.getAdmin().getName())
.build();

}

public static Message toMessageEntity(ChatRequestDTO.ChatMessageRequestDTO request, ChattingRoom room, Member sender, Member receiver) {
Expand All @@ -60,8 +65,10 @@ public static ChatResponseDTO.SendMessageResponseDTO toSendMessageDTO(Message me
return ChatResponseDTO.SendMessageResponseDTO.builder()
.roomId(message.getChattingRoom().getId())
.senderId(message.getSender().getId())
.receiverId(message.getReceiver().getId())
.message(message.getMessage())
.sentAt(message.getCreatedAt())
.messageType(message.getType())
.build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.assu.server.domain.chat.dto;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -21,6 +22,9 @@ public class ChatMessageDTO {
private String message;
private LocalDateTime sendTime;

@JsonProperty("isRead")
private boolean isRead;

@JsonProperty("isMyMessage")
private boolean isMyMessage;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
public class ChatRequestDTO {
@Getter
public static class CreateChatRoomRequestDTO {
private Long adminId;
private Long storeId;
private Long partnerId;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.assu.server.domain.chat.dto;

import com.assu.server.domain.chat.entity.enums.MessageType;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.google.protobuf.Enum;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -16,21 +19,30 @@ public class ChatResponseDTO {
@Builder
public static class CreateChatRoomResponseDTO {
private Long roomId;
private String adminViewName;
private String partnerViewName;
}

// 메시지 전송
@Builder
public record SendMessageResponseDTO(
Long messageId,
Long roomId,
Long senderId,
Long receiverId,
String message,
MessageType messageType,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
LocalDateTime sentAt
) {}

// 메시지 읽음 처리
public record ReadMessageResponseDTO(
Long roomId,
int readCount
Long readerId,
List<Long> readMessagesId,
int readCount,
boolean isRead
) {}

// 채팅방 들어갔을 때 조회
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
import java.util.List;

public interface ChatService {
List<ChatRoomListResultDTO> getChatRoomList();
ChatResponseDTO.CreateChatRoomResponseDTO createChatRoom(ChatRequestDTO.CreateChatRoomRequestDTO request);
List<ChatRoomListResultDTO> getChatRoomList(Long memberId);
ChatResponseDTO.CreateChatRoomResponseDTO createChatRoom(ChatRequestDTO.CreateChatRoomRequestDTO request, Long memberId);
ChatResponseDTO.SendMessageResponseDTO handleMessage(ChatRequestDTO.ChatMessageRequestDTO request);
ChatResponseDTO.ReadMessageResponseDTO readMessage(Long roomId);
ChatResponseDTO.ChatHistoryResponseDTO readHistory(Long roomId);
ChatResponseDTO.LeaveChattingRoomResponseDTO leaveChattingRoom(Long roomId);
ChatResponseDTO.ReadMessageResponseDTO readMessage(Long roomId, Long memberId);
ChatResponseDTO.ChatHistoryResponseDTO readHistory(Long roomId, Long memberId);
ChatResponseDTO.LeaveChattingRoomResponseDTO leaveChattingRoom(Long roomId, Long memberId);
}
Loading