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 @@ -8,7 +8,7 @@
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

//@Component // Redis 방식 비활성화
@Component
@RequiredArgsConstructor
public class ChatRedisPublisher {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Component;

//@Component // Redis 방식 비활성화
@Component
@RequiredArgsConstructor
public class ChatRedisSubscriber implements MessageListener {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;

//@Configuration // Redis 방식 비활성화
@Configuration
@RequiredArgsConstructor
public class RedisPubSubConfig {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,43 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;

@Tag(name = "Chat History", description = "채팅 히스토리 조회 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/chat-rooms")
public class ChatHistoryController {

private final ChatHistoryService chatHistoryService;

/** 최신 30개 */
@Operation(summary = "최신 채팅 메시지 조회", description = "채팅방의 최신 메시지들을 지정된 개수만큼 조회합니다")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "메시지 조회 성공"),
@ApiResponse(responseCode = "404", description = "채팅방을 찾을 수 없음")
})
@GetMapping("/{roomId}/messages")
public ResponseEntity<ChatHistoryPage> latest(
@PathVariable UUID roomId,
@RequestParam(defaultValue = "30") int limit) {
@Parameter(description = "채팅방 ID", required = true) @PathVariable UUID roomId,
@Parameter(description = "조회할 메시지 개수 (기본값: 30)") @RequestParam(defaultValue = "30") int limit) {
return ResponseEntity.ok(chatHistoryService.loadLatest(roomId, limit));
}

/** 이전 페이지(커서 기반) */
@Operation(summary = "이전 채팅 메시지 조회", description = "커서 기반으로 이전 메시지들을 조회합니다 (무한스크롤용)")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "메시지 조회 성공"),
@ApiResponse(responseCode = "400", description = "잘못된 커서 값"),
@ApiResponse(responseCode = "404", description = "채팅방을 찾을 수 없음")
})
Comment on lines +36 to +41
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

400(잘못된 커서 값) 응답 문서와 실제 동작 불일치 — 컨트롤러에서 변환 예외 매핑 필요

  • Service의 decodeCursor는 잘못된 형식일 때 IllegalArgumentException이 날 수 있고, 현재 컨트롤러에서 이를 400으로 변환하지 않아 500이 될 여지가 있습니다.

다음과 같이 400 매핑을 추가해 문서와 일치시키는 것을 권장합니다:

   @GetMapping("/{roomId}/messages/before")
   public ResponseEntity<ChatHistoryPage> before(
       @Parameter(description = "채팅방 ID", required = true) @PathVariable UUID roomId,
-      @Parameter(description = "페이지 커서 (이전 조회의 nextCursor 값)", required = true) @RequestParam String cursor,
-      @Parameter(description = "조회할 메시지 개수 (기본값: 30)") @RequestParam(defaultValue = "30") int limit) {
-
-    return ResponseEntity.ok(chatHistoryService.loadBefore(roomId, cursor, limit));
+      @Parameter(description = "페이지 커서 (이전 조회의 nextCursor 값)", required = true) @RequestParam String cursor,
+      @Parameter(description = "조회할 메시지 개수 (1-100, 기본값: 30)") @RequestParam(defaultValue = "30") int limit) {
+    try {
+      return ResponseEntity.ok(chatHistoryService.loadBefore(roomId, cursor, limit));
+    } catch (IllegalArgumentException ex) {
+      return ResponseEntity.badRequest().build();
+    }
   }
🤖 Prompt for AI Agents
In
backendProject/src/main/java/likelion/mlb/backendProject/domain/chat/controller/ChatHistoryController.java
around lines 36-41, the controller currently lets IllegalArgumentException from
service.decodeCursor bubble up (causing 500) while the API docs promise a 400
for invalid cursors; fix by mapping that exception to HTTP 400 — either wrap the
decodeCursor call in a try/catch that throws a
ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid cursor") on
IllegalArgumentException, or add an
@ExceptionHandler(IllegalArgumentException.class) in this controller (or a
controller advice) that returns a 400 response with an appropriate message so
runtime behavior matches the documented 400 response.

@GetMapping("/{roomId}/messages/before")
public ResponseEntity<ChatHistoryPage> before(
@PathVariable UUID roomId,
@RequestParam String cursor,
@RequestParam(defaultValue = "30") int limit) {
@Parameter(description = "채팅방 ID", required = true) @PathVariable UUID roomId,
@Parameter(description = "페이지 커서 (이전 조회의 nextCursor 값)", required = true) @RequestParam String cursor,
@Parameter(description = "조회할 메시지 개수 (기본값: 30)") @RequestParam(defaultValue = "30") int limit) {
return ResponseEntity.ok(chatHistoryService.loadBefore(roomId, cursor, limit));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import java.security.Principal;
import java.util.Map;
import java.util.UUID;
//import likelion.mlb.backendProject.domain.chat.bus.ChatRedisPublisher;
import likelion.mlb.backendProject.domain.chat.bus.ChatRedisPublisher;
import likelion.mlb.backendProject.domain.chat.dto.ChatSendRequest;
import likelion.mlb.backendProject.domain.chat.repository.ChatMembershipRepository;
import lombok.RequiredArgsConstructor;
Expand All @@ -23,7 +23,7 @@ public class ChatMessagingController {
private final ChatMessageService chatMessageService;
private final SimpMessagingTemplate messagingTemplate;
private final ChatMembershipRepository membershipRepository;
//private final ChatRedisPublisher chatRedisPublisher;
private final ChatRedisPublisher chatRedisPublisher;


@MessageMapping("/chat/{roomId}/send")
Expand Down Expand Up @@ -55,11 +55,7 @@ public void send(@DestinationVariable UUID roomId,
"createdAt", saved.getCreatedAt().toString()
);

// Redis 대신 직접 WebSocket으로 전송 (즉시 전달)
String topic = "/topic/chat/" + roomId;
messagingTemplate.convertAndSend(topic, payload);

// Redis 방식 (주석처리 - 지연 발생)
//chatRedisPublisher.publishToRoom(roomId, new java.util.HashMap<>(payload));
// Redis pub/sub 방식으로 전송
chatRedisPublisher.publishToRoom(roomId, new java.util.HashMap<>(payload));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* */
import java.util.Map;
import java.util.UUID;
//import likelion.mlb.backendProject.domain.chat.bus.ChatRedisPublisher;
import likelion.mlb.backendProject.domain.chat.bus.ChatRedisPublisher;
import likelion.mlb.backendProject.domain.chat.service.ChatMessageService;
import lombok.Data;
import lombok.RequiredArgsConstructor;
Expand All @@ -14,34 +14,51 @@
import org.springframework.web.bind.annotation.*;
import likelion.mlb.backendProject.domain.chat.service.ChatNotificationService;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;

@Tag(name = "Chat Notification", description = "채팅 알림 및 테스트 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/notify")
public class ChatNotificationController {


private final SimpMessagingTemplate messagingTemplate;
//private final ChatRedisPublisher chatRedisPublisher; // Redis 방식 비활성화
private final ChatRedisPublisher chatRedisPublisher;
private final ChatNotificationService notificationService;
private final ChatMessageService chatMessageService; // 디버그용

@Operation(summary = "직접 알림 전송", description = "플레이어 이벤트 알림을 직접 전송합니다 (테스트용)")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "알림 전송 성공"),
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터")
})
@PostMapping("/raw")
public ResponseEntity<Void> raw(@RequestBody RawNotify req) {
public ResponseEntity<Void> raw(@Parameter(description = "알림 데이터") @RequestBody RawNotify req) {
notificationService.sendMatchAlert(
req.getPlayerId(), req.getFixtureId(), req.getEventType(),
req.getMinute(), req.getPoint(), req.getText()
);
return ResponseEntity.ok().build();
}

@Operation(summary = "채팅방 알림 전송", description = "특정 채팅방에 시스템 알림 메시지를 전송합니다 (테스트용)")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "알림 전송 성공"),
@ApiResponse(responseCode = "404", description = "채팅방을 찾을 수 없음")
})
@GetMapping("/room/{roomId}")
public ResponseEntity<Map<String, Object>> roomAlert(
@PathVariable UUID roomId,
@RequestParam String text) {
@Parameter(description = "채팅방 ID", required = true) @PathVariable UUID roomId,
@Parameter(description = "전송할 메시지 내용", required = true) @RequestParam String text) {

var saved = chatMessageService.saveSystemAlert(roomId, text); // DB 저장

// ✅ 직접 WebSocket으로 브로드캐스트 (즉시 전송)
// ✅ STOMP 브로드캐스트 + Redis fan-out
Map<String, Object> payload = Map.of(
"id", saved.getId().toString(),
"chatRoomId", roomId.toString(),
Expand All @@ -50,9 +67,7 @@ public ResponseEntity<Map<String, Object>> roomAlert(
"createdAt", saved.getCreatedAt().toString()
);
messagingTemplate.convertAndSend("/topic/chat/" + roomId, payload);

// Redis 방식 (주석처리 - 지연 발생)
//chatRedisPublisher.publishToRoom(roomId, new java.util.HashMap<>(payload));
chatRedisPublisher.publishToRoom(roomId, new java.util.HashMap<>(payload));

return ResponseEntity.ok(Map.of("ok", true, "id", saved.getId().toString()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@Tag(name = "Chat Read State", description = "채팅 만음 상태 관리 API")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

오타: "채팅 만음 상태 관리 API" → "채팅 읽음 상태 관리 API"

사용자 노출 문구의 오탈자입니다. 스웨거 UI 가독성을 위해 즉시 수정 권장.

적용 예:

-@Tag(name = "Chat Read State", description = "채팅 만음 상태 관리 API")
+@Tag(name = "Chat Read State", description = "채팅 읽음 상태 관리 API")
🤖 Prompt for AI Agents
In
backendProject/src/main/java/likelion/mlb/backendProject/domain/chat/controller/ChatReadController.java
around line 20, the Swagger @Tag description contains a typo "채팅 만음 상태 관리 API";
update the string to "채팅 읽음 상태 관리 API" to correct the user-facing label in the
Swagger UI, preserving the existing annotation and formatting.

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/chat-rooms")
Expand All @@ -20,8 +27,16 @@ public class ChatReadController {
private final ChatReadService chatReadService;
private final ChatMembershipRepository membershipRepository;

@Operation(summary = "릶음 상태 조회", description = "사용자의 마지막 읽은 메시지와 미읽 개수를 조회합니다")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "읽음 상태 조회 성공"),
@ApiResponse(responseCode = "401", description = "인증되지 않은 사용자"),
@ApiResponse(responseCode = "403", description = "채팅방 멤버가 아님"),
@ApiResponse(responseCode = "500", description = "내부 서버 오류")
})
Comment on lines +30 to +36
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

오타: "릶음 상태 조회" → "읽음 상태 조회"

스웨거 Operation 요약의 철자 오류입니다.

-  @Operation(summary = "릶음 상태 조회", description = "사용자의 마지막 읽은 메시지와 미읽 개수를 조회합니다")
+  @Operation(summary = "읽음 상태 조회", description = "사용자의 마지막 읽은 메시지와 미읽 개수를 조회합니다")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Operation(summary = "릶음 상태 조회", description = "사용자의 마지막 읽은 메시지와 미읽 개수를 조회합니다")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "읽음 상태 조회 성공"),
@ApiResponse(responseCode = "401", description = "인증되지 않은 사용자"),
@ApiResponse(responseCode = "403", description = "채팅방 멤버가 아님"),
@ApiResponse(responseCode = "500", description = "내부 서버 오류")
})
@Operation(summary = "읽음 상태 조회", description = "사용자의 마지막 읽은 메시지와 미읽 개수를 조회합니다")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "읽음 상태 조회 성공"),
@ApiResponse(responseCode = "401", description = "인증되지 않은 사용자"),
@ApiResponse(responseCode = "403", description = "채팅방 멤버가 아님"),
@ApiResponse(responseCode = "500", description = "내부 서버 오류")
})
🤖 Prompt for AI Agents
In
backendProject/src/main/java/likelion/mlb/backendProject/domain/chat/controller/ChatReadController.java
around lines 30 to 36, fix the typo in the Swagger @Operation summary: change
the Korean text "릶음 상태 조회" to the correct "읽음 상태 조회" so the API documentation
displays the proper summary; update only the summary string literal to the
corrected spelling.

@GetMapping("/{roomId}/read-state")
public ResponseEntity<?> getReadState(@PathVariable UUID roomId,
public ResponseEntity<?> getReadState(
@Parameter(description = "채팅방 ID", required = true) @PathVariable UUID roomId,
@AuthenticationPrincipal CustomUserDetails cud) {
if (cud == null || cud.getUser() == null) {
log.debug("[read-state] 401: no principal");
Expand All @@ -46,9 +61,18 @@ public ResponseEntity<?> getReadState(@PathVariable UUID roomId,
}
}

@Operation(summary = "메시지 읽음 처리", description = "지정된 메시지까지 읽음으로 표시합니다")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "읽음 처리 성공"),
@ApiResponse(responseCode = "400", description = "잘못된 메시지 ID"),
@ApiResponse(responseCode = "401", description = "인증되지 않은 사용자"),
@ApiResponse(responseCode = "403", description = "채팅방 멤버가 아님"),
@ApiResponse(responseCode = "500", description = "내부 서버 오류")
})
@PostMapping("/{roomId}/read-state")
public ResponseEntity<?> markRead(@PathVariable UUID roomId,
@RequestBody(required = false) Map<String, String> body,
public ResponseEntity<?> markRead(
@Parameter(description = "채팅방 ID", required = true) @PathVariable UUID roomId,
@Parameter(description = "읽음 요청 데이터 (messageId 포함)") @RequestBody(required = false) Map<String, String> body,
@AuthenticationPrincipal CustomUserDetails cud) {
if (cud == null || cud.getUser() == null) {
log.debug("[mark-read] 401: no principal");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
import likelion.mlb.backendProject.domain.chat.service.ChatRoomQueryService;
import likelion.mlb.backendProject.domain.chat.service.ChatRoomService;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;

@Tag(name = "Chat Room", description = "채팅방 조회 및 관리 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/chat-rooms")
Expand All @@ -21,36 +28,46 @@ public class ChatRoomQueryController {
private final ChatRoomService chatRoomService;
private final ChatRoomRepository chatRoomRepository;

/**
* 방 스코어보드 (4명, 점수/랭크)
*/
@Operation(summary = "채팅방 스코어보드 조회", description = "채팅방 참가자들의 점수와 순위를 조회합니다")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "스코어보드 조회 성공"),
@ApiResponse(responseCode = "404", description = "채팅방을 찾을 수 없음")
})
@GetMapping("/{roomId}/scoreboard")
public List<ScoreboardItem> scoreboard(@PathVariable UUID roomId) {
public List<ScoreboardItem> scoreboard(@Parameter(description = "채팅방 ID", required = true) @PathVariable UUID roomId) {
return queryService.getScoreboard(roomId);
}

/**
* 특정 참가자의 로스터(11인) + 포메이션
*/
@Operation(summary = "참가자 로스터 조회", description = "특정 참가자의 11명 로스터와 포메이션 정보를 조회합니다")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "로스터 조회 성공"),
@ApiResponse(responseCode = "404", description = "참가자를 찾을 수 없음")
})
@GetMapping("/{roomId}/participants/{participantId}/roster")
public RosterResponse roster(@PathVariable UUID roomId, @PathVariable UUID participantId) {
public RosterResponse roster(
@Parameter(description = "채팅방 ID", required = true) @PathVariable UUID roomId,
@Parameter(description = "참가자 ID", required = true) @PathVariable UUID participantId) {
return queryService.getRoster(roomId, participantId);
}

/**
* 드래프트로 채팅방 조회
*/
@Operation(summary = "드래프트 채팅방 조회", description = "드래프트 ID로 해당 채팅방을 조회합니다")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "채팅방 조회 성공"),
@ApiResponse(responseCode = "404", description = "드래프트에 해당하는 채팅방을 찾을 수 없음")
})
@GetMapping("/by-draft/{draftId}")
public ChatRoom getChatRoomByDraft(@PathVariable UUID draftId) {
public ChatRoom getChatRoomByDraft(@Parameter(description = "드래프트 ID", required = true) @PathVariable UUID draftId) {
return chatRoomRepository.findByDraftId(draftId)
.orElseThrow(() -> new IllegalArgumentException("ChatRoom not found for draft " + draftId));
}
Comment on lines +59 to 62
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

문서(404)와 런타임 불일치: 404로 매핑되도록 예외 형식 변경 필요

현재는 IllegalArgumentException을 던져 500으로 번역될 수 있습니다. 문서대로 404를 보장하려면 ResponseStatusException(HttpStatus.NOT_FOUND, ...)로 변경하세요.

-    return chatRoomRepository.findByDraftId(draftId)
-        .orElseThrow(() -> new IllegalArgumentException("ChatRoom not found for draft " + draftId));
+    return chatRoomRepository.findByDraftId(draftId)
+        .orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
+            org.springframework.http.HttpStatus.NOT_FOUND, "ChatRoom not found for draft " + draftId));

필요 시 import 추가:

+import org.springframework.http.HttpStatus;
+import org.springframework.web.server.ResponseStatusException;
🤖 Prompt for AI Agents
In
backendProject/src/main/java/likelion/mlb/backendProject/domain/chat/controller/ChatRoomQueryController.java
around lines 59 to 62, the method currently throws IllegalArgumentException
which can produce a 500 response; replace that with throwing a
ResponseStatusException(HttpStatus.NOT_FOUND, "ChatRoom not found for draft " +
draftId) so the framework returns a 404 as documented, and add the required
import(s) (org.springframework.web.server.ResponseStatusException and
org.springframework.http.HttpStatus) if they are not present.


/**
* 채팅방 생성 (드래프트용)
*/
@Operation(summary = "채팅방 생성", description = "드래프트를 위한 새 채팅방을 생성합니다")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "채팅방 생성 성공"),
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터")
})
@PostMapping
public ChatRoom createChatRoom(@RequestBody CreateChatRoomRequest request) {
public ChatRoom createChatRoom(@Parameter(description = "채팅방 생성 요청") @RequestBody CreateChatRoomRequest request) {
return chatRoomService.createForDraft(request.getDraftId());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,28 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;

@Tag(name = "Chat Search Management", description = "채팅 검색 인덱스 관리 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/chat-rooms")
public class ChatReindexController {
private final ChatMessageRepository repo;
private final ChatSearchIndexer indexer;

@Operation(summary = "채팅 메시지 재인덱싱", description = "지정된 채팅방의 모든 메시지를 Elasticsearch에 재인덱싱합니다")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "재인덱싱 성공"),
@ApiResponse(responseCode = "404", description = "채팅방을 찾을 수 없음"),
@ApiResponse(responseCode = "500", description = "내부 서버 오류")
})
@PostMapping("/{roomId}/reindex")
public void reindex(@PathVariable UUID roomId) {
public void reindex(@Parameter(description = "채팅방 ID", required = true) @PathVariable UUID roomId) {
// 대용량이면 Page로 분할 처리
repo.findAllByChatRoomIdOrderByCreatedAtAsc(roomId).forEach(indexer::index);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;

@Tag(name = "Chat Search", description = "채팅 메시지 검색 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/chat-rooms")
Expand All @@ -19,12 +26,18 @@ public class ChatSearchController {
private final ChatSearchService chatSearchService;
private final ChatMembershipRepository membershipRepository;

@Operation(summary = "채팅 메시지 검색", description = "지정된 채팅방에서 키워드로 메시지를 검색합니다")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "검색 성공"),
@ApiResponse(responseCode = "403", description = "채팅방 멤버가 아님"),
@ApiResponse(responseCode = "500", description = "내부 서버 오류")
})
@GetMapping("/{roomId}/search")
public ResponseEntity<ChatSearchResult> search(
@PathVariable UUID roomId,
@RequestParam(required = false, defaultValue = "") String q,
@RequestParam(required = false) String cursor,
@RequestParam(defaultValue = "20") int limit,
@Parameter(description = "채팅방 ID", required = true) @PathVariable UUID roomId,
@Parameter(description = "검색 키워드") @RequestParam(required = false, defaultValue = "") String q,
@Parameter(description = "페이지 커서 (이전 조회의 nextCursor 값)") @RequestParam(required = false) String cursor,
@Parameter(description = "조회할 메시지 개수 (1-50, 기본값: 20)") @RequestParam(defaultValue = "20") int limit,
Authentication authentication) {

// 권한 체크
Expand Down
Loading