|
8 | 8 |
|
9 | 9 | import java.time.LocalDateTime; |
10 | 10 |
|
11 | | -/* |
12 | | - RoomMember 엔티티 - 방과 사용자 간의 멤버십 관계를 나타냄 |
13 | | - 연관관계 : |
14 | | - - Room (1) : RoomMember (N) - 한 방에 여러 멤버가 있을 수 있음 |
15 | | - - User (1) : RoomMember (N) - 한 사용자가 여러 방의 멤버가 될 수 있음 |
16 | | - @JoinColumn vs @JoinTable 선택 이유: |
17 | | - - @JoinColumn: 외래키를 이용한 직접 관계 (현재 변경) |
18 | | - - @JoinTable: 별도의 연결 테이블을 만드는 관계 |
19 | | - RoomMember 테이블에서 그냥 room_id와 user_id 외래키로 직접 연결. |
| 11 | +/** |
| 12 | + * RoomMember 엔티티 - 방과 사용자 간의 영구적인 멤버십 관계를 나타냄 |
| 13 | + * room (1) : RoomMember (N) - 한 방에 여러 멤버 |
| 14 | + * user (1) : RoomMember (N) - 한 사용자가 여러 방의 멤버 |
20 | 15 | */ |
21 | 16 | @Entity |
22 | 17 | @NoArgsConstructor |
23 | 18 | @Getter |
24 | 19 | @Table(uniqueConstraints = @UniqueConstraint(columnNames = {"room_id", "user_id"})) |
25 | 20 | public class RoomMember extends BaseEntity { |
26 | 21 |
|
27 | | - // 방 정보 - 어떤 방의 멤버인지 |
| 22 | + // ==================== 영구 데이터 (DB에서 관리) ==================== |
28 | 23 | @ManyToOne(fetch = FetchType.LAZY) |
29 | | - @JoinColumn(name = "room_id") // room_member 테이블의 room_id 컬럼이 room 테이블의 id를 참조 |
| 24 | + @JoinColumn(name = "room_id", nullable = false) |
30 | 25 | private Room room; |
31 | 26 |
|
32 | | - // 사용자 정보 - 누가 이 방의 멤버인지 |
33 | 27 | @ManyToOne(fetch = FetchType.LAZY) |
34 | | - @JoinColumn(name = "user_id") // room_member 테이블의 user_id 컬럼이 users 테이블의 id를 참조 |
| 28 | + @JoinColumn(name = "user_id", nullable = false) |
35 | 29 | private User user; |
36 | 30 |
|
37 | | - // 방 내에서의 역할 (방장, 부방장, 멤버, 방문객) |
38 | 31 | @Enumerated(EnumType.STRING) |
39 | 32 | @Column(nullable = false) |
40 | 33 | private RoomRole role = RoomRole.VISITOR; |
41 | 34 |
|
42 | | - // 멤버십 기본 정보 |
43 | 35 | @Column(nullable = false) |
44 | | - private LocalDateTime joinedAt; // 방에 처음 입장한 시간 |
45 | | - private LocalDateTime lastActiveAt; // 마지막으로 활동한 시간 |
| 36 | + private LocalDateTime joinedAt; // 방에 처음 가입한 시간 (불변) |
46 | 37 |
|
47 | | - // 실시간 상태 관리 필드들 |
48 | | - @Column(nullable = false) |
49 | | - private boolean isOnline = false; // 현재 방에 온라인 상태인지 |
50 | | - |
51 | | - private String connectionId; // WebSocket 연결 ID (실시간 통신용) |
52 | | - |
53 | | - private LocalDateTime lastHeartbeat; // 마지막 heartbeat 시간 (연결 상태 확인용) |
| 38 | + private LocalDateTime lastActiveAt; // 마지막 활동 시간 (참고용, 정확성 낮음) |
54 | 39 |
|
55 | | - // 💡 권한 확인 메서드들 (RoomRole enum의 메서드를 위임) |
| 40 | + // ==================== 권한 확인 메서드 ==================== |
56 | 41 |
|
57 | 42 | /** |
58 | | - * 방 관리 권한이 있는지 확인 (방장, 부방장) |
59 | | - 방 설정 변경, 공지사항 작성 등의 권한이 필요할 때 |
| 43 | + * 방 관리 권한 확인 (방장, 부방장) |
| 44 | + * 사용: 방 설정 변경, 공지사항 작성 등 |
60 | 45 | */ |
61 | 46 | public boolean canManageRoom() { |
62 | 47 | return role.canManageRoom(); |
63 | 48 | } |
64 | 49 |
|
65 | 50 | /** |
66 | | - * 멤버 추방 권한이 있는지 확인 (방장, 부방장) |
67 | | - 다른 멤버를 추방하려고 할 때 |
| 51 | + * 멤버 추방 권한 확인 (방장, 부방장) |
| 52 | + * 사용: 다른 멤버를 추방할 때 |
68 | 53 | */ |
69 | 54 | public boolean canKickMember() { |
70 | 55 | return role.canKickMember(); |
71 | 56 | } |
72 | 57 |
|
73 | 58 | /** |
74 | | - * 공지사항 관리 권한이 있는지 확인 (방장, 부방장) |
75 | | - 공지사항을 작성하거나 삭제할 때 |
| 59 | + * 공지사항 관리 권한 확인 (방장, 부방장) |
| 60 | + * 사용: 공지사항 작성/삭제 |
76 | 61 | */ |
77 | 62 | public boolean canManageNotices() { |
78 | 63 | return role.canManageNotices(); |
79 | 64 | } |
80 | 65 |
|
81 | 66 | /** |
82 | | - * 방장인지 확인 |
83 | | - 방 소유자만 가능한 작업 (방 삭제, 호스트 권한 이양 등) |
| 67 | + * 방장 여부 확인 |
| 68 | + * 사용: 방 삭제, 호스트 권한 이양 등 |
84 | 69 | */ |
85 | 70 | public boolean isHost() { |
86 | 71 | return role.isHost(); |
87 | 72 | } |
88 | 73 |
|
89 | 74 | /** |
90 | | - * 정식 멤버인지 확인 (방문객이 아닌 멤버, 부방장, 방장) |
91 | | - 멤버만 접근 가능한 기능 (파일 업로드, 학습 기록 등) |
| 75 | + * 정식 멤버 여부 확인 (방문객이 아닌 멤버, 부방장, 방장) |
| 76 | + * 사용: 멤버만 접근 가능한 기능 |
92 | 77 | */ |
93 | 78 | public boolean isMember() { |
94 | 79 | return role.isMember(); |
95 | 80 | } |
96 | 81 |
|
97 | | - /** |
98 | | - * 현재 활성 상태인지 확인 |
99 | | - 온라인 멤버 목록 표시, 비활성 사용자 정리 등 |
100 | | - 온라인 상태이고 최근 설정된 시간 이내에 heartbeat가 있었던 경우 |
101 | | - */ |
102 | | - public boolean isActive(int timeoutMinutes) { |
103 | | - return isOnline && lastHeartbeat != null && |
104 | | - lastHeartbeat.isAfter(LocalDateTime.now().minusMinutes(timeoutMinutes)); |
105 | | - } |
106 | | - |
| 82 | + // ==================== 정적 팩토리 메서드 ==================== |
107 | 83 |
|
108 | 84 | /** |
109 | | - 기본 멤버 생성 메서드, 처음 입장 시 사용 |
| 85 | + * 기본 멤버 생성 |
110 | 86 | */ |
111 | | - public static RoomMember create(Room room, User user, RoomRole role) { |
| 87 | + private static RoomMember create(Room room, User user, RoomRole role) { |
112 | 88 | RoomMember member = new RoomMember(); |
113 | 89 | member.room = room; |
114 | 90 | member.user = user; |
115 | 91 | member.role = role; |
116 | 92 | member.joinedAt = LocalDateTime.now(); |
117 | 93 | member.lastActiveAt = LocalDateTime.now(); |
118 | | - member.isOnline = true; // 생성 시 온라인 상태 |
119 | | - member.lastHeartbeat = LocalDateTime.now(); |
120 | | - |
121 | 94 | return member; |
122 | 95 | } |
123 | 96 |
|
124 | | - // 방장 멤버 생성 -> 새로운 방을 생성할 때 방 생성자를 방장으로 등록 |
| 97 | + /** |
| 98 | + * 방장 멤버 생성 |
| 99 | + * 사용: 방 생성 시 생성자를 방장으로 등록 |
| 100 | + */ |
125 | 101 | public static RoomMember createHost(Room room, User user) { |
126 | 102 | return create(room, user, RoomRole.HOST); |
127 | 103 | } |
128 | 104 |
|
129 | 105 | /** |
130 | | - * 일반 멤버 생성, 권한 자동 변경 |
131 | | - - 비공개 방에서 초대받은 사용자를 정식 멤버로 등록할 때 (로직 검토 중) |
| 106 | + * 일반 멤버 생성 |
| 107 | + * 사용: 비공개 방에 초대된 사용자를 정식 멤버로 등록 |
132 | 108 | */ |
133 | 109 | public static RoomMember createMember(Room room, User user) { |
134 | 110 | return create(room, user, RoomRole.MEMBER); |
135 | 111 | } |
136 | 112 |
|
137 | 113 | /** |
138 | 114 | * 방문객 생성 |
139 | | - * 사용 상황: 공개 방에 처음 입장하는 사용자를 임시 방문객으로 등록 |
| 115 | + * 사용: 공개 방에 처음 입장하는 사용자를 임시 방문객으로 등록 |
140 | 116 | */ |
141 | 117 | public static RoomMember createVisitor(Room room, User user) { |
142 | 118 | return create(room, user, RoomRole.VISITOR); |
143 | 119 | } |
144 | 120 |
|
| 121 | + // ==================== 상태 변경 메서드 ==================== |
| 122 | + |
145 | 123 | /** |
146 | | - * 멤버의 역할 변경 |
147 | | - 방장이 멤버를 부방장으로 승격시키거나 강등시킬 때 |
| 124 | + * 멤버 역할 변경 |
| 125 | + * 사용: 방장이 멤버를 승격/강등시킬 때 |
148 | 126 | */ |
149 | 127 | public void updateRole(RoomRole newRole) { |
150 | 128 | this.role = newRole; |
151 | 129 | } |
152 | 130 |
|
153 | 131 | /** |
154 | | - * 온라인 상태 변경 |
155 | | - * 사용 상황: 멤버가 방에 입장하거나 퇴장할 때 |
156 | | - 활동 시간도 함께 업데이트, 온라인이 되면 heartbeat도 갱신 |
| 132 | + * 마지막 활동 시간 업데이트 |
| 133 | + * 참고용이며, 정확한 활동 추적은 Redis의 WebSocketSessionManager를 사용하세요. |
157 | 134 | */ |
158 | | - public void updateOnlineStatus(boolean online) { |
159 | | - this.isOnline = online; |
| 135 | + public void updateLastActivity() { |
160 | 136 | this.lastActiveAt = LocalDateTime.now(); |
161 | | - if (online) { |
162 | | - this.lastHeartbeat = LocalDateTime.now(); |
163 | | - } |
164 | | - } |
165 | | - |
166 | | - /** |
167 | | - * WebSocket 연결 ID 업데이트 |
168 | | - * 사용 상황: 멤버가 웹소켓으로 방에 연결될 때 |
169 | | - + heartbeat도 함께 갱신 |
170 | | - */ |
171 | | - public void updateConnectionId(String connectionId) { |
172 | | - this.connectionId = connectionId; |
173 | | - this.lastHeartbeat = LocalDateTime.now(); |
174 | | - } |
175 | | - |
176 | | - /** |
177 | | - * 사용 : 클라이언트에서 주기적으로 서버에 연결 상태를 알릴 때 |
178 | | - * 목적: 연결이 끊어진 멤버를 자동으로 감지하기 위해 사용, 별도의 다른 것으로 변경 가능 |
179 | | - */ |
180 | | - public void heartbeat() { |
181 | | - this.lastHeartbeat = LocalDateTime.now(); |
182 | | - this.lastActiveAt = LocalDateTime.now(); |
183 | | - this.isOnline = true; |
184 | | - } |
185 | | - |
186 | | - /** |
187 | | - * 방 퇴장 처리 (명시적 퇴장과 연결 끊김 상태 로직 분할 예정임.. 일단은 임시로 통합 상태) |
188 | | - 멤버가 방을 나가거나 연결이 끊어졌을 때, 오프라인 상태로 변경하고 연결 ID 제거 |
189 | | - */ |
190 | | - public void leave() { |
191 | | - this.isOnline = false; |
192 | | - this.connectionId = null; |
193 | 137 | } |
194 | 138 | } |
0 commit comments