Skip to content

Commit a2048dd

Browse files
✨ feat : 방 게임 시작 API 구현 + SSE 이벤트 퍼블리싱 (#51)
* ✨ feat : 정답없이 문제 조회 * ✨ feat : 방 게임 시작 구현 * chore: Java 스타일 수정 * 🔧 chore : 시큐리티 필터 임시 허용 해제 * ♻️ refactor : GameService 분리 * chore: Java 스타일 수정 * 🔧 chore : merge 잘못 한 거 수정 * chore: Java 스타일 수정 * ✨ 게임 시작 상태 SSE로 이벤트 퍼블리시 * chore: Java 스타일 수정 * ♻️ refactor : PR 리뷰 반영(stream().allMatch, gameSetting 내부 메서드) * chore: Java 스타일 수정 --------- Co-authored-by: github-actions <>
1 parent 693240f commit a2048dd

File tree

13 files changed

+168
-6
lines changed

13 files changed

+168
-6
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package io.f1.backend.domain.game.app;
2+
3+
import io.f1.backend.domain.game.dto.GameStartData;
4+
import io.f1.backend.domain.game.dto.response.GameStartResponse;
5+
import io.f1.backend.domain.game.event.RoomUpdatedEvent;
6+
import io.f1.backend.domain.game.model.GameSetting;
7+
import io.f1.backend.domain.game.model.Player;
8+
import io.f1.backend.domain.game.model.Room;
9+
import io.f1.backend.domain.game.model.RoomState;
10+
import io.f1.backend.domain.game.store.RoomRepository;
11+
import io.f1.backend.domain.quiz.app.QuizService;
12+
import io.f1.backend.domain.quiz.entity.Quiz;
13+
14+
import lombok.RequiredArgsConstructor;
15+
16+
import org.springframework.context.ApplicationEventPublisher;
17+
import org.springframework.stereotype.Service;
18+
19+
import java.util.Map;
20+
21+
@Service
22+
@RequiredArgsConstructor
23+
public class GameService {
24+
25+
private final QuizService quizService;
26+
private final RoomRepository roomRepository;
27+
private final ApplicationEventPublisher eventPublisher;
28+
29+
public GameStartData gameStart(Long roomId, Long quizId) {
30+
31+
Room room =
32+
roomRepository
33+
.findRoom(roomId)
34+
.orElseThrow(() -> new IllegalArgumentException("404 존재하지 않는 방입니다."));
35+
36+
if (!validateReadyStatus(room)) {
37+
throw new IllegalArgumentException("E403004 : 레디 상태가 아닙니다.");
38+
}
39+
40+
// 방의 gameSetting에 설정된 퀴즈랑 요청 퀴즈랑 같은지 체크 후 GameSetting에서 라운드 가져오기
41+
Integer round = checkGameSetting(room, quizId);
42+
43+
Quiz quiz = quizService.getQuizWithQuestionsById(quizId);
44+
45+
// 라운드 수만큼 랜덤 Question 추출
46+
GameStartResponse questions = quizService.getRandomQuestionsWithoutAnswer(quizId, round);
47+
48+
// 방 정보 게임 중으로 변경
49+
room.updateRoomState(RoomState.PLAYING);
50+
51+
eventPublisher.publishEvent(new RoomUpdatedEvent(room, quiz));
52+
53+
return new GameStartData(getDestination(roomId), questions);
54+
}
55+
56+
private Integer checkGameSetting(Room room, Long quizId) {
57+
58+
GameSetting gameSetting = room.getGameSetting();
59+
60+
if (!gameSetting.checkQuizId(quizId)) {
61+
throw new IllegalArgumentException("E409002 : 게임 설정이 다릅니다. (게임을 시작할 수 없습니다.)");
62+
}
63+
64+
return gameSetting.getRound();
65+
}
66+
67+
private boolean validateReadyStatus(Room room) {
68+
69+
Map<String, Player> playerSessionMap = room.getPlayerSessionMap();
70+
71+
return playerSessionMap.values().stream().allMatch(Player::isReady);
72+
}
73+
74+
private static String getDestination(Long roomId) {
75+
return "/sub/room/" + roomId;
76+
}
77+
}

backend/src/main/java/io/f1/backend/domain/game/app/RoomService.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public class RoomService {
6060
public RoomCreateResponse saveRoom(RoomCreateRequest request) {
6161

6262
Long quizMinId = quizService.getQuizMinId();
63-
Quiz quiz = quizService.getQuizById(quizMinId);
63+
Quiz quiz = quizService.getQuizWithQuestionsById(quizMinId);
6464

6565
GameSetting gameSetting = toGameSetting(quiz);
6666

@@ -132,7 +132,7 @@ public RoomInitialData initializeRoomSocket(Long roomId, String sessionId) {
132132
RoomSettingResponse roomSettingResponse = toRoomSettingResponse(room);
133133

134134
Long quizId = room.getGameSetting().getQuizId();
135-
Quiz quiz = quizService.getQuizById(quizId);
135+
Quiz quiz = quizService.getQuizWithQuestionsById(quizId);
136136

137137
GameSettingResponse gameSettingResponse =
138138
toGameSettingResponse(room.getGameSetting(), quiz);
@@ -189,7 +189,7 @@ public RoomListResponse getAllRooms() {
189189
.map(
190190
room -> {
191191
Long quizId = room.getGameSetting().getQuizId();
192-
Quiz quiz = quizService.getQuizById(quizId);
192+
Quiz quiz = quizService.getQuizWithQuestionsById(quizId);
193193

194194
return toRoomResponse(room, quiz);
195195
})
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package io.f1.backend.domain.game.dto;
2+
3+
import io.f1.backend.domain.game.dto.response.GameStartResponse;
4+
5+
public record GameStartData(String destination, GameStartResponse gameStartResponse) {}

backend/src/main/java/io/f1/backend/domain/game/dto/MessageType.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ public enum MessageType {
55
GAME_SETTING,
66
PLAYER_LIST,
77
SYSTEM_NOTICE,
8+
GAME_START,
89
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package io.f1.backend.domain.game.dto.request;
2+
3+
public record GameStartRequest(Long quizId) {}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.f1.backend.domain.game.dto.response;
2+
3+
import io.f1.backend.domain.quiz.dto.GameQuestionResponse;
4+
5+
import java.util.List;
6+
7+
public record GameStartResponse(List<GameQuestionResponse> questions) {}

backend/src/main/java/io/f1/backend/domain/game/model/GameSetting.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,11 @@ public class GameSetting {
1010
private Long quizId;
1111
private Integer round; // 게임 변경 시 해당 게임의 총 문제 수로 설정
1212
private int timeLimit = 60;
13+
14+
public boolean checkQuizId(Long quizId) {
15+
if (this.quizId != null && this.quizId.equals(quizId)) {
16+
return false;
17+
}
18+
return true;
19+
}
1320
}

backend/src/main/java/io/f1/backend/domain/game/model/Room.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ public void updateHost(Player nextHost) {
4646
this.host = nextHost;
4747
}
4848

49+
public void updateRoomState(RoomState newState) {
50+
this.state = newState;
51+
}
52+
4953
public void removeUserId(Long id) {
5054
this.userIdSessionMap.remove(id);
5155
}

backend/src/main/java/io/f1/backend/domain/game/websocket/GameSocketController.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package io.f1.backend.domain.game.websocket;
22

3+
import io.f1.backend.domain.game.app.GameService;
34
import io.f1.backend.domain.game.app.RoomService;
5+
import io.f1.backend.domain.game.dto.GameStartData;
46
import io.f1.backend.domain.game.dto.MessageType;
57
import io.f1.backend.domain.game.dto.RoomExitData;
68
import io.f1.backend.domain.game.dto.RoomInitialData;
9+
import io.f1.backend.domain.game.dto.request.GameStartRequest;
710

811
import lombok.RequiredArgsConstructor;
912

@@ -19,6 +22,7 @@ public class GameSocketController {
1922

2023
private final MessageSender messageSender;
2124
private final RoomService roomService;
25+
private final GameService gameService;
2226

2327
@MessageMapping("/room/initializeRoomSocket/{roomId}")
2428
public void initializeRoomSocket(@DestinationVariable Long roomId, Message<?> message) {
@@ -56,6 +60,18 @@ public void exitRoom(@DestinationVariable Long roomId, Message<?> message) {
5660
}
5761
}
5862

63+
@MessageMapping("/room/start/{roomId}")
64+
public void gameStart(@DestinationVariable Long roomId, Message<GameStartRequest> message) {
65+
66+
Long quizId = message.getPayload().quizId();
67+
68+
GameStartData gameStartData = gameService.gameStart(roomId, quizId);
69+
70+
String destination = gameStartData.destination();
71+
72+
messageSender.send(destination, MessageType.GAME_START, gameStartData.gameStartResponse());
73+
}
74+
5975
private static String getSessionId(Message<?> message) {
6076
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
6177
return accessor.getSessionId();

backend/src/main/java/io/f1/backend/domain/quiz/app/QuizService.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
import static java.nio.file.Files.deleteIfExists;
66

7+
import io.f1.backend.domain.game.dto.response.GameStartResponse;
78
import io.f1.backend.domain.question.app.QuestionService;
89
import io.f1.backend.domain.question.dto.QuestionRequest;
10+
import io.f1.backend.domain.question.entity.Question;
911
import io.f1.backend.domain.quiz.dao.QuizRepository;
1012
import io.f1.backend.domain.quiz.dto.QuizCreateRequest;
1113
import io.f1.backend.domain.quiz.dto.QuizCreateResponse;
@@ -214,12 +216,11 @@ public QuizListPageResponse getQuizzes(String title, String creator, Pageable pa
214216
}
215217

216218
@Transactional(readOnly = true)
217-
public Quiz getQuizById(Long quizId) {
219+
public Quiz getQuizWithQuestionsById(Long quizId) {
218220
Quiz quiz =
219221
quizRepository
220-
.findById(quizId)
222+
.findQuizWithQuestionsById(quizId)
221223
.orElseThrow(() -> new RuntimeException("E404002: 존재하지 않는 퀴즈입니다."));
222-
quiz.getQuestions().size();
223224
return quiz;
224225
}
225226

@@ -228,6 +229,7 @@ public Long getQuizMinId() {
228229
return quizRepository.getQuizMinId();
229230
}
230231

232+
@Transactional(readOnly = true)
231233
public QuizQuestionListResponse getQuizWithQuestions(Long quizId) {
232234
Quiz quiz =
233235
quizRepository
@@ -236,4 +238,15 @@ public QuizQuestionListResponse getQuizWithQuestions(Long quizId) {
236238

237239
return quizToQuizQuestionListResponse(quiz);
238240
}
241+
242+
@Transactional(readOnly = true)
243+
public GameStartResponse getRandomQuestionsWithoutAnswer(Long quizId, Integer round) {
244+
quizRepository
245+
.findById(quizId)
246+
.orElseThrow(() -> new NoSuchElementException("존재하지 않는 퀴즈입니다."));
247+
248+
List<Question> randomQuestions = quizRepository.findRandQuestionsByQuizId(quizId, round);
249+
250+
return toGameStartResponse(randomQuestions);
251+
}
239252
}

0 commit comments

Comments
 (0)