Skip to content

Commit dd010c5

Browse files
committed
feat: 협업 방 참여 기능 및 실시간 목록 동기화 추가(#39)
1 parent b8fd5aa commit dd010c5

File tree

3 files changed

+50
-5
lines changed

3 files changed

+50
-5
lines changed

src/main/java/com/dmu/debug_visual/collab/domain/repository/RoomParticipantRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import com.dmu.debug_visual.collab.domain.entity.Room;
44
import com.dmu.debug_visual.collab.domain.entity.RoomParticipant;
5+
import com.dmu.debug_visual.user.User;
56
import org.springframework.data.jpa.repository.JpaRepository;
67

78
import java.util.Optional;
89

910
public interface RoomParticipantRepository extends JpaRepository<RoomParticipant, Long> {
1011
Optional<RoomParticipant> findByRoomAndUser_UserId(Room room, String userId);
12+
boolean existsByRoomAndUser(Room room, User user);
1113
}

src/main/java/com/dmu/debug_visual/collab/rest/RoomController.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@
2727
@RequiredArgsConstructor
2828
public class RoomController {
2929

30+
// ✨ Controller는 이제 Service에만 의존합니다. 훨씬 깔끔해졌죠!
3031
private final RoomService roomService;
3132

32-
// --- 방 관리 ---
33+
// --- 1. 방 관리 ---
3334
@Operation(summary = "새로운 협업 방 생성", description = "DB에 새로운 협업 방을 생성하고, 방장을 첫 참여자로 자동 등록합니다.")
3435
@ApiResponses({
3536
@ApiResponse(responseCode = "200", description = "방 생성 성공", content = @Content(schema = @Schema(implementation = RoomResponse.class))),
@@ -42,7 +43,7 @@ public ResponseEntity<RoomResponse> createRoom(@RequestBody CreateRoomRequest re
4243
return ResponseEntity.ok(response);
4344
}
4445

45-
@Operation(summary = "방에서 참가자 강퇴 (방장 전용)", description = "방장이 특정 참가자를 방에서 영구적으로 제외시킵니다. 강퇴된 참가자는 해당 방의 모든 세션에서도 제거됩니다.")
46+
@Operation(summary = "방에서 참가자 강퇴 (방장 전용)", description = "방장이 특정 참가자를 방에서 영구적으로 제외시킵니다.")
4647
@ApiResponses({
4748
@ApiResponse(responseCode = "200", description = "강퇴 성공"),
4849
@ApiResponse(responseCode = "401", description = "인증 실패"),
@@ -58,8 +59,22 @@ public ResponseEntity<Void> kickParticipant(
5859
return ResponseEntity.ok().build();
5960
}
6061

61-
// --- 세션 관리 ---
62-
@Operation(summary = "방 안에 새 코드 세션 생성 (방송 시작)", description = "기존 방 안에 독립적인 새 코드 편집 세션을 생성합니다. 생성자는 자동으로 쓰기 권한을 가집니다.")
62+
@Operation(summary = "협업 방에 참여자로 등록", description = "사용자가 특정 방에 참여자로 자신을 등록합니다. 웹소켓에 연결하기 전에 반드시 호출해야 합니다.")
63+
@ApiResponses({
64+
@ApiResponse(responseCode = "200", description = "참여 등록 성공"),
65+
@ApiResponse(responseCode = "401", description = "인증 실패"),
66+
@ApiResponse(responseCode = "404", description = "존재하지 않는 방")
67+
})
68+
@PostMapping("/rooms/{roomId}/participants")
69+
public ResponseEntity<Void> joinRoom(
70+
@Parameter(description = "참여할 방의 고유 ID") @PathVariable String roomId,
71+
@AuthenticationPrincipal CustomUserDetails userDetails) {
72+
roomService.joinRoom(roomId, userDetails.getUsername());
73+
return ResponseEntity.ok().build();
74+
}
75+
76+
// --- 2. 세션 관리 ---
77+
@Operation(summary = "방 안에 새 코드 세션 생성 (방송 시작)", description = "기존 방 안에 독립적인 새 코드 편집 세션을 생성합니다.")
6378
@ApiResponses({
6479
@ApiResponse(responseCode = "200", description = "세션 생성 성공", content = @Content(schema = @Schema(implementation = SessionResponse.class))),
6580
@ApiResponse(responseCode = "401", description = "인증 실패"),
@@ -91,7 +106,7 @@ public ResponseEntity<Void> updateSessionStatus(
91106
return ResponseEntity.ok().build();
92107
}
93108

94-
// --- 세션 권한 관리 ---
109+
// --- 3. 세션 권한 관리 ---
95110
@Operation(summary = "세션 내 쓰기 권한 부여 (세션 생성자 전용)", description = "세션 생성자가 특정 참가자에게 해당 세션의 쓰기 권한을 부여합니다.")
96111
@ApiResponses({
97112
@ApiResponse(responseCode = "200", description = "권한 부여 성공"),

src/main/java/com/dmu/debug_visual/collab/service/RoomService.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,32 @@ private CodeSession findSessionAndVerifyCreator(String sessionId, String request
220220
}
221221
return session;
222222
}
223+
224+
/**
225+
* 사용자를 특정 방의 참여자로 등록합니다.
226+
* @param roomId 참여할 방의 ID
227+
* @param userId 참여할 사용자의 ID
228+
*/
229+
@Transactional
230+
public void joinRoom(String roomId, String userId) {
231+
Room room = roomRepository.findByRoomId(roomId)
232+
.orElseThrow(() -> new EntityNotFoundException("Room not found: " + roomId));
233+
User user = userRepository.findByUserId(userId)
234+
.orElseThrow(() -> new EntityNotFoundException("User not found: " + userId));
235+
236+
// 💡 이미 참여자인지 확인하여 중복 등록을 방지합니다.
237+
boolean isAlreadyParticipant = roomParticipantRepository.existsByRoomAndUser(room, user);
238+
if (isAlreadyParticipant) {
239+
// 이미 참여자이면 아무것도 하지 않고 성공으로 간주
240+
return;
241+
}
242+
243+
// 새로운 참여자로 등록 (기본 권한은 READ_ONLY)
244+
RoomParticipant newParticipant = RoomParticipant.builder()
245+
.room(room)
246+
.user(user)
247+
.permission(RoomParticipant.Permission.READ_ONLY)
248+
.build();
249+
roomParticipantRepository.save(newParticipant);
250+
}
223251
}

0 commit comments

Comments
 (0)