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
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
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.event.RoomUpdatedEvent;
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 io.f1.backend.domain.quiz.entity.Quiz;

import lombok.RequiredArgsConstructor;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
@RequiredArgsConstructor
public class GameService {

private final QuizService quizService;
private final RoomRepository roomRepository;
private final ApplicationEventPublisher eventPublisher;

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);

Quiz quiz = quizService.getQuizWithQuestionsById(quizId);

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

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

eventPublisher.publishEvent(new RoomUpdatedEvent(room, quiz));

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

private Integer checkGameSetting(Room room, Long quizId) {

GameSetting gameSetting = room.getGameSetting();

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

return gameSetting.getRound();
}

private boolean validateReadyStatus(Room room) {

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

return playerSessionMap.values().stream().allMatch(Player::isReady);
}

private static String getDestination(Long roomId) {
return "/sub/room/" + roomId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public class RoomService {
public RoomCreateResponse saveRoom(RoomCreateRequest request) {

Long quizMinId = quizService.getQuizMinId();
Quiz quiz = quizService.getQuizById(quizMinId);
Quiz quiz = quizService.getQuizWithQuestionsById(quizMinId);

GameSetting gameSetting = toGameSetting(quiz);

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

Long quizId = room.getGameSetting().getQuizId();
Quiz quiz = quizService.getQuizById(quizId);
Quiz quiz = quizService.getQuizWithQuestionsById(quizId);

GameSettingResponse gameSettingResponse =
toGameSettingResponse(room.getGameSetting(), quiz);
Expand Down Expand Up @@ -189,7 +189,7 @@ public RoomListResponse getAllRooms() {
.map(
room -> {
Long quizId = room.getGameSetting().getQuizId();
Quiz quiz = quizService.getQuizById(quizId);
Quiz quiz = quizService.getQuizWithQuestionsById(quizId);

return toRoomResponse(room, quiz);
})
Expand Down
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 @@ -10,4 +10,11 @@ public class GameSetting {
private Long quizId;
private Integer round; // 게임 변경 시 해당 게임의 총 문제 수로 설정
private int timeLimit = 60;

public boolean checkQuizId(Long quizId) {
if (this.quizId != null && this.quizId.equals(quizId)) {
return false;
}
return true;
}
}
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 @@ -214,12 +216,11 @@ public QuizListPageResponse getQuizzes(String title, String creator, Pageable pa
}

@Transactional(readOnly = true)
public Quiz getQuizById(Long quizId) {
public Quiz getQuizWithQuestionsById(Long quizId) {
Quiz quiz =
quizRepository
.findById(quizId)
.findQuizWithQuestionsById(quizId)
.orElseThrow(() -> new RuntimeException("E404002: 존재하지 않는 퀴즈입니다."));
quiz.getQuestions().size();
return quiz;
}

Expand All @@ -228,6 +229,7 @@ public Long getQuizMinId() {
return quizRepository.getQuizMinId();
}

@Transactional(readOnly = true)
public QuizQuestionListResponse getQuizWithQuestions(Long quizId) {
Quiz quiz =
quizRepository
Expand All @@ -236,4 +238,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,18 +1,30 @@
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;
import java.util.Optional;

public interface QuizRepository extends JpaRepository<Quiz, Long> {

Page<Quiz> findQuizzesByTitleContaining(String title, Pageable pageable);

Page<Quiz> findQuizzesByCreator_NicknameContaining(String creator, Pageable pageable);

@Query("SELECT q FROM Quiz q LEFT JOIN FETCH q.questions WHERE q.id = :quizId")
Optional<Quiz> findQuizWithQuestionsById(Long quizId);

@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));
}
}