Skip to content
Merged
21 changes: 21 additions & 0 deletions src/main/java/gg/agit/konect/domain/chat/controller/ChatApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
import gg.agit.konect.domain.chat.dto.ChatMessageSendRequest;
import gg.agit.konect.domain.chat.dto.ChatMuteResponse;
import gg.agit.konect.domain.chat.dto.ChatRoomCreateRequest;
import gg.agit.konect.domain.chat.dto.ChatRoomMembersResponse;
import gg.agit.konect.domain.chat.dto.ChatRoomNameUpdateRequest;
import gg.agit.konect.domain.chat.dto.ChatRoomResponse;
import gg.agit.konect.domain.chat.dto.ChatRoomsSummaryResponse;
import gg.agit.konect.domain.chat.dto.ChatSearchResponse;
import gg.agit.konect.domain.chat.enums.ChatInviteSortBy;
import gg.agit.konect.global.auth.annotation.UserId;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
Expand Down Expand Up @@ -267,4 +269,23 @@ ResponseEntity<ChatRoomResponse> createGroupChatRoom(
@Valid @RequestBody ChatRoomCreateRequest.Group request,
@UserId Integer userId
);

@Operation(summary = "채팅방 멤버 목록 조회", description = """
## 설명
- 특정 채팅방의 모든 멤버 목록을 조회합니다.

## 로직
- 채팅방에 참여 중인 멤버만 조회할 수 있습니다.
- 나간 멤버(leftAt이 설정된 멤버)는 목록에 포함되지 않습니다.
- 각 멤버의 userId, 이름, 프로필 이미지, 방장 여부, 참여 시간을 반환합니다.

## 에러
- FORBIDDEN_CHAT_ROOM_ACCESS (403): 채팅방에 접근할 권한이 없습니다.
- NOT_FOUND_CHAT_ROOM (404): 채팅방을 찾을 수 없습니다.
""")
@GetMapping("/rooms/{chatRoomId}/members")
ResponseEntity<ChatRoomMembersResponse> getChatRoomMembers(
@Parameter(description = "채팅방 ID") @PathVariable Integer chatRoomId,
@UserId Integer userId
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
Expand All @@ -14,11 +15,13 @@
import gg.agit.konect.domain.chat.dto.ChatMessageSendRequest;
import gg.agit.konect.domain.chat.dto.ChatMuteResponse;
import gg.agit.konect.domain.chat.dto.ChatRoomCreateRequest;
import gg.agit.konect.domain.chat.dto.ChatRoomMembersResponse;
import gg.agit.konect.domain.chat.dto.ChatRoomNameUpdateRequest;
import gg.agit.konect.domain.chat.dto.ChatRoomResponse;
import gg.agit.konect.domain.chat.dto.ChatRoomsSummaryResponse;
import gg.agit.konect.domain.chat.dto.ChatSearchResponse;
import gg.agit.konect.domain.chat.enums.ChatInviteSortBy;
import gg.agit.konect.domain.chat.service.ChatRoomMembershipService;
import gg.agit.konect.domain.chat.service.ChatService;
import gg.agit.konect.global.auth.annotation.UserId;
import jakarta.validation.Valid;
Expand All @@ -31,6 +34,7 @@
public class ChatController implements ChatApi {

private final ChatService chatService;
private final ChatRoomMembershipService chatRoomMembershipService;

@Override
public ResponseEntity<ChatRoomResponse> createOrGetChatRoom(
Expand Down Expand Up @@ -147,4 +151,13 @@ public ResponseEntity<ChatRoomResponse> createGroupChatRoom(
ChatRoomResponse response = chatService.createGroupChatRoom(userId, request);
return ResponseEntity.ok(response);
}

@Override
@GetMapping("/rooms/{chatRoomId}/members")
public ResponseEntity<ChatRoomMembersResponse> getChatRoomMembers(
@PathVariable Integer chatRoomId,
@UserId Integer userId) {
ChatRoomMembersResponse response = chatRoomMembershipService.getChatRoomMembers(chatRoomId, userId);
return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package gg.agit.konect.domain.chat.dto;

import java.time.LocalDateTime;

public record ChatRoomMemberResponse(
Integer userId,
String name,
String profileImageUrl,
boolean isOwner,
LocalDateTime joinedAt
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package gg.agit.konect.domain.chat.dto;

import java.util.List;

public record ChatRoomMembersResponse(
List<ChatRoomMemberResponse> members
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,27 @@ List<RoomUnreadCountProjection> countUnreadByRoomIdsAndUserId(
@Param("userId") Integer userId
);

@Query("""
SELECT COUNT(crm) > 0
FROM ChatRoomMember crm
WHERE crm.id.chatRoomId = :chatRoomId
AND crm.id.userId = :userId
AND crm.leftAt IS NULL
""")
boolean existsActiveByChatRoomIdAndUserId(
@Param("chatRoomId") Integer chatRoomId,
@Param("userId") Integer userId
);

@Query("""
SELECT crm
FROM ChatRoomMember crm
JOIN FETCH crm.user
WHERE crm.id.chatRoomId = :chatRoomId
AND crm.leftAt IS NULL
""")
List<ChatRoomMember> findActiveMembersByChatRoomId(@Param("chatRoomId") Integer chatRoomId);

List<ChatRoomMember> saveAll(Iterable<ChatRoomMember> chatRoomMembers);

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static gg.agit.konect.global.code.ApiResponseCode.FORBIDDEN_CHAT_ROOM_ACCESS;
import static gg.agit.konect.global.code.ApiResponseCode.NOT_FOUND_CHAT_ROOM;
import static gg.agit.konect.global.code.ApiResponseCode.NOT_FOUND_USER;

import java.time.LocalDateTime;
import java.util.List;
Expand All @@ -15,6 +16,8 @@

import gg.agit.konect.domain.chat.model.ChatRoom;
import gg.agit.konect.domain.chat.model.ChatRoomMember;
import gg.agit.konect.domain.chat.dto.ChatRoomMemberResponse;
import gg.agit.konect.domain.chat.dto.ChatRoomMembersResponse;
import gg.agit.konect.domain.chat.repository.ChatRoomMemberRepository;
import gg.agit.konect.domain.chat.repository.ChatRoomRepository;
import gg.agit.konect.domain.club.model.Club;
Expand All @@ -39,6 +42,46 @@ public class ChatRoomMembershipService {
private final ClubMemberRepository clubMemberRepository;
private final UserRepository userRepository;

@Transactional(readOnly = true)
public ChatRoomMembersResponse getChatRoomMembers(Integer chatRoomId, Integer currentUserId) {
User currentUser = userRepository.findById(currentUserId)
.orElseThrow(() -> CustomException.of(NOT_FOUND_USER));

// 채팅방 존재 여부 먼저 확인
ChatRoom chatRoom = chatRoomRepository.findById(chatRoomId)
.orElseThrow(() -> CustomException.of(NOT_FOUND_CHAT_ROOM));

validateMembership(chatRoom, currentUser);

List<ChatRoomMember> members = chatRoomMemberRepository.findActiveMembersByChatRoomId(chatRoomId);

return new ChatRoomMembersResponse(members.stream()
.filter(member -> member.getUser().getDeletedAt() == null)
.map(this::toMemberResponse)
.toList());
}

private void validateMembership(ChatRoom chatRoom, User currentUser) {
// 어드민은 시스템 어드민 방의 멤버를 조회할 수 있음
if (currentUser.isAdmin() && isSystemAdminRoom(chatRoom.getId())) {
return;
}

if (!chatRoomMemberRepository.existsActiveByChatRoomIdAndUserId(chatRoom.getId(), currentUser.getId())) {
throw CustomException.of(FORBIDDEN_CHAT_ROOM_ACCESS);
}
}

private ChatRoomMemberResponse toMemberResponse(ChatRoomMember member) {
return new ChatRoomMemberResponse(
member.getUser().getId(),
member.getUser().getName(),
member.getUser().getImageUrl(),
member.isOwner(),
member.getCreatedAt()
);
}

@Transactional
public void addClubMember(ClubMember clubMember) {
LocalDateTime baseline = Objects.requireNonNull(clubMember.getCreatedAt());
Expand Down
Loading
Loading