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
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependencies {
// Spring
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-websocket")
implementation("org.springframework.boot:spring-boot-starter-validation")

// Database & JPA
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.back.domain.studyroom.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "studyroom")
public class StudyRoomProperties {

private Heartbeat heartbeat = new Heartbeat();
private Default defaultSettings = new Default();

@Getter
@Setter
public static class Heartbeat {
private int timeoutMinutes = 5;
}

@Getter
@Setter
public static class Default {
private int maxParticipants = 10;
private boolean allowCamera = true;
private boolean allowAudio = true;
private boolean allowScreenShare = true;
}
}
376 changes: 165 additions & 211 deletions src/main/java/com/back/domain/studyroom/controller/RoomController.java

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions src/main/java/com/back/domain/studyroom/dto/CreateRoomRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.back.domain.studyroom.dto;

import jakarta.validation.constraints.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class CreateRoomRequest {
@NotBlank(message = "방 제목은 필수입니다")
@Size(max = 100, message = "방 제목은 100자를 초과할 수 없습니다")
private String title;

@Size(max = 500, message = "방 설명은 500자를 초과할 수 없습니다")
private String description;

private Boolean isPrivate = false;

private String password;

@Min(value = 2, message = "최소 2명 이상이어야 합니다")
@Max(value = 100, message = "최대 100명까지 가능합니다")
private Integer maxParticipants = 10;
}
12 changes: 12 additions & 0 deletions src/main/java/com/back/domain/studyroom/dto/JoinRoomRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.back.domain.studyroom.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class JoinRoomRequest {
private String password;
}
26 changes: 26 additions & 0 deletions src/main/java/com/back/domain/studyroom/dto/JoinRoomResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.back.domain.studyroom.dto;

import com.back.domain.studyroom.entity.RoomMember;
import com.back.domain.studyroom.entity.RoomRole;
import lombok.Builder;
import lombok.Getter;

import java.time.LocalDateTime;

@Getter
@Builder
public class JoinRoomResponse {
private Long roomId;
private Long userId;
private RoomRole role;
private LocalDateTime joinedAt;

public static JoinRoomResponse from(RoomMember member) {
return JoinRoomResponse.builder()
.roomId(member.getRoom().getId())
.userId(member.getUser().getId())
.role(member.getRole())
.joinedAt(member.getJoinedAt())
.build();
}
}
35 changes: 35 additions & 0 deletions src/main/java/com/back/domain/studyroom/dto/MyRoomResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.back.domain.studyroom.dto;

import com.back.domain.studyroom.entity.Room;
import com.back.domain.studyroom.entity.RoomRole;
import com.back.domain.studyroom.entity.RoomStatus;
import lombok.Builder;
import lombok.Getter;

import java.time.LocalDateTime;

@Getter
@Builder
public class MyRoomResponse {
private Long roomId;
private String title;
private String description;
private int currentParticipants;
private int maxParticipants;
private RoomStatus status;
private RoomRole myRole;
private LocalDateTime createdAt;

public static MyRoomResponse of(Room room, RoomRole myRole) {
return MyRoomResponse.builder()
.roomId(room.getId())
.title(room.getTitle())
.description(room.getDescription() != null ? room.getDescription() : "")
.currentParticipants(room.getCurrentParticipants())
.maxParticipants(room.getMaxParticipants())
.status(room.getStatus())
.myRole(myRole)
.createdAt(room.getCreatedAt())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.back.domain.studyroom.dto;

import com.back.domain.studyroom.entity.Room;
import com.back.domain.studyroom.entity.RoomStatus;
import lombok.Builder;
import lombok.Getter;

import java.time.LocalDateTime;
import java.util.List;

@Getter
@Builder
public class RoomDetailResponse {
private Long roomId;
private String title;
private String description;
private boolean isPrivate;
private int maxParticipants;
private int currentParticipants;
private RoomStatus status;
private boolean allowCamera;
private boolean allowAudio;
private boolean allowScreenShare;
private String createdBy;
private LocalDateTime createdAt;
private List<RoomMemberResponse> members;

public static RoomDetailResponse of(Room room, List<RoomMemberResponse> members) {
return RoomDetailResponse.builder()
.roomId(room.getId())
.title(room.getTitle())
.description(room.getDescription() != null ? room.getDescription() : "")
.isPrivate(room.isPrivate())
.maxParticipants(room.getMaxParticipants())
.currentParticipants(room.getCurrentParticipants())
.status(room.getStatus())
.allowCamera(room.isAllowCamera())
.allowAudio(room.isAllowAudio())
.allowScreenShare(room.isAllowScreenShare())
.createdBy(room.getCreatedBy().getNickname())
.createdAt(room.getCreatedAt())
.members(members)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.back.domain.studyroom.dto;

import com.back.domain.studyroom.entity.RoomMember;
import com.back.domain.studyroom.entity.RoomRole;
import lombok.Builder;
import lombok.Getter;

import java.time.LocalDateTime;

@Getter
@Builder
public class RoomMemberResponse {
private Long userId;
private String nickname;
private RoomRole role;
private boolean isOnline;
private LocalDateTime joinedAt;
private LocalDateTime lastActiveAt;

public static RoomMemberResponse from(RoomMember member) {
return RoomMemberResponse.builder()
.userId(member.getUser().getId())
.nickname(member.getUser().getNickname())
.role(member.getRole())
.isOnline(member.isOnline())
.joinedAt(member.getJoinedAt())
.lastActiveAt(member.getLastActiveAt() != null ? member.getLastActiveAt() : member.getJoinedAt())
.build();
}
}
34 changes: 34 additions & 0 deletions src/main/java/com/back/domain/studyroom/dto/RoomResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.back.domain.studyroom.dto;

import com.back.domain.studyroom.entity.Room;
import com.back.domain.studyroom.entity.RoomStatus;
import lombok.Builder;
import lombok.Getter;

import java.time.LocalDateTime;

@Getter
@Builder
public class RoomResponse {
private Long roomId;
private String title;
private String description;
private int currentParticipants;
private int maxParticipants;
private RoomStatus status;
private String createdBy;
private LocalDateTime createdAt;

public static RoomResponse from(Room room) {
return RoomResponse.builder()
.roomId(room.getId())
.title(room.getTitle())
.description(room.getDescription() != null ? room.getDescription() : "")
.currentParticipants(room.getCurrentParticipants())
.maxParticipants(room.getMaxParticipants())
.status(room.getStatus())
.createdBy(room.getCreatedBy().getNickname())
.createdAt(room.getCreatedAt())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.back.domain.studyroom.dto;

import jakarta.validation.constraints.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class UpdateRoomSettingsRequest {
@NotBlank(message = "방 제목은 필수입니다")
@Size(max = 100, message = "방 제목은 100자를 초과할 수 없습니다")
private String title;

@Size(max = 500, message = "방 설명은 500자를 초과할 수 없습니다")
private String description;

@Min(value = 2, message = "최소 2명 이상이어야 합니다")
@Max(value = 100, message = "최대 100명까지 가능합니다")
private Integer maxParticipants;

private Boolean allowCamera = true;
private Boolean allowAudio = true;
private Boolean allowScreenShare = true;
}
12 changes: 12 additions & 0 deletions src/main/java/com/back/domain/studyroom/entity/Room.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ public boolean needsPassword() {
방장이 스터디를 시작할 때 또는 대기 중인 방을 활성화할 때
*/
public void activate() {
if (this.status == RoomStatus.TERMINATED) {
throw new IllegalStateException("종료된 방은 활성화할 수 없습니다.");
}
this.status = RoomStatus.ACTIVE;
this.isActive = true;
}
Expand All @@ -132,6 +135,12 @@ public void activate() {
* 방을 일시 정지 상태로 변경
*/
public void pause() {
if (this.status == RoomStatus.TERMINATED) {
throw new IllegalStateException("종료된 방은 일시정지할 수 없습니다.");
}
if (this.status != RoomStatus.ACTIVE) {
throw new IllegalStateException("활성화된 방만 일시정지할 수 있습니다.");
}
this.status = RoomStatus.PAUSED;
}

Expand All @@ -140,6 +149,9 @@ public void pause() {
방장이 스터디를 완전히 끝내거나, 빈 방을 자동 정리할 때 (종료 처리를 어떻게 뺄지에 따라 변경 될 예정)
*/
public void terminate() {
if (this.status == RoomStatus.TERMINATED) {
return; // 이미 종료된 방은 무시
}
this.status = RoomStatus.TERMINATED;
this.isActive = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@ public boolean isMember() {
/**
* 현재 활성 상태인지 확인
온라인 멤버 목록 표시, 비활성 사용자 정리 등
온라인 상태이고 최근 5분 이내에 heartbeat가 있었던 경우
온라인 상태이고 최근 설정된 시간 이내에 heartbeat가 있었던 경우
*/
public boolean isActive() {
public boolean isActive(int timeoutMinutes) {
return isOnline && lastHeartbeat != null &&
lastHeartbeat.isAfter(LocalDateTime.now().minusMinutes(5));
lastHeartbeat.isAfter(LocalDateTime.now().minusMinutes(timeoutMinutes));
}


Expand Down
Loading