Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package io.f1.backend.domain.game.app;

import io.f1.backend.domain.game.dto.GameStartData;
import io.f1.backend.domain.game.dto.response.GameStartResponse;
import io.f1.backend.domain.game.model.GameSetting;
import io.f1.backend.domain.game.model.Player;
import io.f1.backend.domain.game.model.Room;
import io.f1.backend.domain.game.model.RoomState;
import io.f1.backend.domain.game.store.RoomRepository;
import io.f1.backend.domain.quiz.app.QuizService;

import lombok.RequiredArgsConstructor;

import org.springframework.stereotype.Service;

import java.util.Map;

@Service
@RequiredArgsConstructor
public class GameService {

private final QuizService quizService;
private final RoomRepository roomRepository;

private Integer checkGameSetting(Room room, Long quizId) {

GameSetting gameSetting = room.getGameSetting();

Long roomQuizId = gameSetting.getQuizId();

if (!roomQuizId.equals(quizId)) {
Copy link
Collaborator

@jiwon1217 jiwon1217 Jul 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[L5-참고의견]

if(!gameSetting.getQuizId().eqauls(quizId)) 형태의 조건문을 GameSetting 내부에 위치시키는 것이 좋을 것 같습니다 !

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확인했습니다 ! 수정했습니다 !

throw new IllegalArgumentException("E409002 : 게임 설정이 다릅니다. (게임을 시작할 수 없습니다.)");
}

return gameSetting.getRound();
}

public GameStartData gameStart(Long roomId, Long quizId) {

Room room =
roomRepository
.findRoom(roomId)
.orElseThrow(() -> new IllegalArgumentException("404 존재하지 않는 방입니다."));

if (!validateReadyStatus(room)) {
throw new IllegalArgumentException("E403004 : 레디 상태가 아닙니다.");
}

// 방의 gameSetting에 설정된 퀴즈랑 요청 퀴즈랑 같은지 체크 후 GameSetting에서 라운드 가져오기
Integer round = checkGameSetting(room, quizId);

// 라운드 수만큼 랜덤 Question 추출
GameStartResponse questions = quizService.getRandomQuestionsWithoutAnswer(quizId, round);

// 방 정보 게임 중으로 변경
room.updateRoomState(RoomState.PLAYING);

return new GameStartData(getDestination(roomId), questions);
}

private boolean validateReadyStatus(Room room) {

Map<String, Player> playerSessionMap = room.getPlayerSessionMap();

for (Player player : playerSessionMap.values()) {
if (!player.isReady()) {
return false;
}
}
return true;
}

private static String getDestination(Long roomId) {
return "/sub/room/" + roomId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.f1.backend.domain.game.dto;

import io.f1.backend.domain.game.dto.response.GameStartResponse;

public record GameStartData(String destination, GameStartResponse gameStartResponse) {}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ public enum MessageType {
GAME_SETTING,
PLAYER_LIST,
SYSTEM_NOTICE,
GAME_START,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package io.f1.backend.domain.game.dto.request;

public record GameStartRequest(Long quizId) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.f1.backend.domain.game.dto.response;

import io.f1.backend.domain.quiz.dto.GameQuestionResponse;

import java.util.List;

public record GameStartResponse(List<GameQuestionResponse> questions) {}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ public void updateHost(Player nextHost) {
this.host = nextHost;
}

public void updateRoomState(RoomState newState) {
this.state = newState;
}

public void removeUserId(Long id) {
this.userIdSessionMap.remove(id);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package io.f1.backend.domain.game.websocket;

import io.f1.backend.domain.game.app.GameService;
import io.f1.backend.domain.game.app.RoomService;
import io.f1.backend.domain.game.dto.GameStartData;
import io.f1.backend.domain.game.dto.MessageType;
import io.f1.backend.domain.game.dto.RoomExitData;
import io.f1.backend.domain.game.dto.RoomInitialData;
import io.f1.backend.domain.game.dto.request.GameStartRequest;

import lombok.RequiredArgsConstructor;

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

private final MessageSender messageSender;
private final RoomService roomService;
private final GameService gameService;

@MessageMapping("/room/initializeRoomSocket/{roomId}")
public void initializeRoomSocket(@DestinationVariable Long roomId, Message<?> message) {
Expand Down Expand Up @@ -56,6 +60,18 @@ public void exitRoom(@DestinationVariable Long roomId, Message<?> message) {
}
}

@MessageMapping("/room/start/{roomId}")
public void gameStart(@DestinationVariable Long roomId, Message<GameStartRequest> message) {

Long quizId = message.getPayload().quizId();

GameStartData gameStartData = gameService.gameStart(roomId, quizId);

String destination = gameStartData.destination();

messageSender.send(destination, MessageType.GAME_START, gameStartData.gameStartResponse());
}

private static String getSessionId(Message<?> message) {
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
return accessor.getSessionId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

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

import io.f1.backend.domain.game.dto.response.GameStartResponse;
import io.f1.backend.domain.question.app.QuestionService;
import io.f1.backend.domain.question.dto.QuestionRequest;
import io.f1.backend.domain.question.entity.Question;
import io.f1.backend.domain.quiz.dao.QuizRepository;
import io.f1.backend.domain.quiz.dto.QuizCreateRequest;
import io.f1.backend.domain.quiz.dto.QuizCreateResponse;
Expand Down Expand Up @@ -228,6 +230,7 @@ public Long getQuizMinId() {
return quizRepository.getQuizMinId();
}

@Transactional(readOnly = true)
public QuizQuestionListResponse getQuizWithQuestions(Long quizId) {
Quiz quiz =
quizRepository
Expand All @@ -236,4 +239,15 @@ public QuizQuestionListResponse getQuizWithQuestions(Long quizId) {

return quizToQuizQuestionListResponse(quiz);
}

@Transactional(readOnly = true)
public GameStartResponse getRandomQuestionsWithoutAnswer(Long quizId, Integer round) {
quizRepository
.findById(quizId)
.orElseThrow(() -> new NoSuchElementException("존재하지 않는 퀴즈입니다."));

List<Question> randomQuestions = quizRepository.findRandQuestionsByQuizId(quizId, round);

return toGameStartResponse(randomQuestions);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package io.f1.backend.domain.quiz.dao;

import io.f1.backend.domain.question.entity.Question;
import io.f1.backend.domain.quiz.entity.Quiz;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

public interface QuizRepository extends JpaRepository<Quiz, Long> {

Page<Quiz> findQuizzesByTitleContaining(String title, Pageable pageable);
Expand All @@ -15,4 +18,9 @@ public interface QuizRepository extends JpaRepository<Quiz, Long> {

@Query("SELECT MIN(q.id) FROM Quiz q")
Long getQuizMinId();

@Query(
value = "SELECT * FROM question WHERE quiz_id = :quizId ORDER BY RAND() LIMIT :round",
nativeQuery = true)
List<Question> findRandQuestionsByQuizId(Long quizId, Integer round);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package io.f1.backend.domain.quiz.dto;

public record GameQuestionResponse(Long id, String question) {}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.f1.backend.domain.quiz.mapper;

import io.f1.backend.domain.game.dto.response.GameStartResponse;
import io.f1.backend.domain.question.dto.QuestionResponse;
import io.f1.backend.domain.question.entity.Question;
import io.f1.backend.domain.quiz.dto.GameQuestionResponse;
import io.f1.backend.domain.quiz.dto.QuizCreateRequest;
import io.f1.backend.domain.quiz.dto.QuizCreateResponse;
import io.f1.backend.domain.quiz.dto.QuizListPageResponse;
Expand Down Expand Up @@ -83,4 +85,16 @@ public static QuizQuestionListResponse quizToQuizQuestionListResponse(Quiz quiz)
quiz.getQuestions().size(),
questionsToQuestionResponses(quiz.getQuestions()));
}

public static List<GameQuestionResponse> toGameQuestionResponseList(List<Question> questions) {
return questions.stream().map(QuizMapper::toGameQuestionResponse).toList();
}

public static GameQuestionResponse toGameQuestionResponse(Question question) {
return new GameQuestionResponse(question.getId(), question.getTextQuestion().getContent());
}

public static GameStartResponse toGameStartResponse(List<Question> questions) {
return new GameStartResponse(toGameQuestionResponseList(questions));
}
}