Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@
import io.f1.backend.domain.game.dto.response.RoomListResponse;
import io.f1.backend.domain.game.dto.response.RoomResponse;
import io.f1.backend.domain.game.dto.response.RoomSettingResponse;
import io.f1.backend.domain.game.event.RoomCreatedEvent;
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.RoomSetting;
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 io.f1.backend.domain.user.entity.User;

import lombok.RequiredArgsConstructor;

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

import java.util.List;
Expand All @@ -33,8 +35,10 @@
@RequiredArgsConstructor
public class RoomService {

private final QuizService quizService;
private final RoomRepository roomRepository;
private final AtomicLong roomIdGenerator = new AtomicLong(0);
private final ApplicationEventPublisher eventPublisher;

public RoomCreateResponse saveRoom(RoomCreateRequest request, Map<String, Object> loginUser) {

Expand All @@ -46,7 +50,14 @@ public RoomCreateResponse saveRoom(RoomCreateRequest request, Map<String, Object

Long newId = roomIdGenerator.incrementAndGet();

roomRepository.saveRoom(new Room(newId, roomSetting, gameSetting, host));
Room room = new Room(newId, roomSetting, gameSetting, host);

roomRepository.saveRoom(room);

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

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

return new RoomCreateResponse(newId);
}
Expand Down Expand Up @@ -103,22 +114,14 @@ public RoomInitialData enterRoom(Long roomId, String sessionId) {
destination, roomSettingResponse, gameSettingResponse, playerListResponse);
}

// todo quizService에서 퀴즈 조회 메서드로 변경
public RoomListResponse getAllRooms() {
List<Room> rooms = roomRepository.findAll();
List<RoomResponse> roomResponses =
rooms.stream()
.map(
room -> {
User user = new User(); // 임시 유저 객체
user.setNickname("임시 유저 닉네임");

Quiz quiz = new Quiz(); // 임시 퀴즈 객체
quiz.setTitle("임시 퀴즈 제목");
quiz.setDescription("임시 퀴즈 설명");
quiz.setThumbnailUrl("임시 이미지");
quiz.setQuestions(List.of());
quiz.setCreator(user);
Long quizId = room.getGameSetting().getQuizId();
Quiz quiz = quizService.getQuizById(quizId);

return toRoomResponse(room, quiz);
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.f1.backend.domain.game.event;

import io.f1.backend.domain.game.model.Room;
import io.f1.backend.domain.quiz.entity.Quiz;

public record RoomCreatedEvent(Room room, Quiz quiz) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package io.f1.backend.domain.game.event;

public record RoomDeletedEvent(Long roomId) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.f1.backend.domain.game.event;

import io.f1.backend.domain.game.model.Room;
import io.f1.backend.domain.quiz.entity.Quiz;

public record RoomUpdatedEvent(Room room, Quiz quiz) {}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.f1.backend.domain.game.sse.app;

import io.f1.backend.domain.game.sse.dto.LobbySseEvent;
import io.f1.backend.domain.game.sse.store.SseEmitterRepository;

import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -28,4 +29,15 @@ public SseEmitter subscribe() {
}
return emitter;
}

// 로비로 SSE 메시지를 쏘기위한 메서드
public <T> void notifyLobbyUpdate(LobbySseEvent<T> event) {
for (SseEmitter emitter : emitterRepository.getAll()) {
try {
emitter.send(SseEmitter.event().name(event.type()).data(event));
} catch (IOException e) {
emitterRepository.remove(emitter);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package io.f1.backend.domain.game.sse.dto;

public record LobbySseEvent<T>(String type, T payload) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.f1.backend.domain.game.sse.dto;

public record RoomCreatedPayload(
Long roomId,
String roomName,
int maxUserCount,
int currentUserCount,
boolean locked,
String roomState,
String quizTitle,
String description,
String creator,
int numberOfQuestion,
String thumbnailUrl) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package io.f1.backend.domain.game.sse.dto;

public record RoomDeletedPayload(Long roomId) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.f1.backend.domain.game.sse.dto;

public record RoomUpdatedPayload(
Long roomId,
int currentUserCount,
String roomState,
String quizTitle,
String description,
String creator,
int numberOfQuestion,
String thumbnailUrl) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.f1.backend.domain.game.sse.dto;

public enum SseEventType {
CREATE,
UPDATE,
DELETE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.f1.backend.domain.game.sse.listener;

import static io.f1.backend.domain.game.sse.mapper.SseMapper.*;

import io.f1.backend.domain.game.event.RoomCreatedEvent;
import io.f1.backend.domain.game.sse.app.SseService;
import io.f1.backend.domain.game.sse.dto.LobbySseEvent;
import io.f1.backend.domain.game.sse.dto.RoomCreatedPayload;

import lombok.RequiredArgsConstructor;

import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class RoomCreatedEventListener {

private final SseService sseService;

@Async
@EventListener
public void roomCreate(RoomCreatedEvent event) {
LobbySseEvent<RoomCreatedPayload> sseEvent = fromRoomCreated(event);
sseService.notifyLobbyUpdate(sseEvent);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.f1.backend.domain.game.sse.listener;

import static io.f1.backend.domain.game.sse.mapper.SseMapper.*;

import io.f1.backend.domain.game.event.RoomDeletedEvent;
import io.f1.backend.domain.game.sse.app.SseService;
import io.f1.backend.domain.game.sse.dto.LobbySseEvent;
import io.f1.backend.domain.game.sse.dto.RoomDeletedPayload;

import lombok.RequiredArgsConstructor;

import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;

@RequiredArgsConstructor
public class RoomDeletedEventListener {

private final SseService sseService;

@Async
@EventListener
public void roomDelete(RoomDeletedEvent event) {
LobbySseEvent<RoomDeletedPayload> sseEvent = fromRoomDeleted(event);
sseService.notifyLobbyUpdate(sseEvent);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.f1.backend.domain.game.sse.listener;

import io.f1.backend.domain.game.event.RoomUpdatedEvent;
import io.f1.backend.domain.game.sse.app.SseService;
import io.f1.backend.domain.game.sse.dto.LobbySseEvent;
import io.f1.backend.domain.game.sse.dto.RoomUpdatedPayload;
import io.f1.backend.domain.game.sse.mapper.SseMapper;

import lombok.RequiredArgsConstructor;

import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;

@RequiredArgsConstructor
public class RoomUpdatedEventListener {

private final SseService sseService;

@Async
@EventListener
public void roomUpdate(RoomUpdatedEvent event) {
LobbySseEvent<RoomUpdatedPayload> sseEvent = SseMapper.fromRoomUpdated(event);
sseService.notifyLobbyUpdate(sseEvent);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.f1.backend.domain.game.sse.mapper;

import io.f1.backend.domain.game.event.RoomCreatedEvent;
import io.f1.backend.domain.game.event.RoomDeletedEvent;
import io.f1.backend.domain.game.event.RoomUpdatedEvent;
import io.f1.backend.domain.game.model.Room;
import io.f1.backend.domain.game.sse.dto.LobbySseEvent;
import io.f1.backend.domain.game.sse.dto.RoomCreatedPayload;
import io.f1.backend.domain.game.sse.dto.RoomDeletedPayload;
import io.f1.backend.domain.game.sse.dto.RoomUpdatedPayload;
import io.f1.backend.domain.game.sse.dto.SseEventType;
import io.f1.backend.domain.quiz.entity.Quiz;

public class SseMapper {

public static LobbySseEvent<RoomCreatedPayload> fromRoomCreated(RoomCreatedEvent event) {
Room room = event.room();
Quiz quiz = event.quiz();
RoomCreatedPayload payload =
new RoomCreatedPayload(
room.getId(),
room.getRoomSetting().roomName(),
room.getRoomSetting().maxUserCount(),
room.getPlayerSessionMap().size(),
room.getRoomSetting().locked(),
room.getState().name(),
quiz.getTitle(),
quiz.getDescription(),
quiz.getCreator().getNickname(),
quiz.getQuestions().size(),
quiz.getThumbnailUrl());
return new LobbySseEvent<>(SseEventType.CREATE.name(), payload);
}

public static LobbySseEvent<RoomUpdatedPayload> fromRoomUpdated(RoomUpdatedEvent event) {
Room room = event.room();
Quiz quiz = event.quiz();
RoomUpdatedPayload payload =
new RoomUpdatedPayload(
room.getId(),
room.getPlayerSessionMap().size(),
room.getState().name(),
quiz.getTitle(),
quiz.getDescription(),
quiz.getCreator().getNickname(),
quiz.getQuestions().size(),
quiz.getThumbnailUrl());
return new LobbySseEvent<>(SseEventType.UPDATE.name(), payload);
}

public static LobbySseEvent<RoomDeletedPayload> fromRoomDeleted(RoomDeletedEvent event) {
Long roomId = event.roomId();
RoomDeletedPayload payload = new RoomDeletedPayload(roomId);
return new LobbySseEvent<>(SseEventType.DELETE.name(), payload);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ public class SseEmitterRepository {
public void save(SseEmitter emitter) {
emitters.add(emitter);
// 연결종료 객체정리
emitter.onCompletion(
() -> {
emitters.remove(emitter);
});
emitter.onCompletion(() -> emitters.remove(emitter));
emitter.onTimeout(() -> emitters.remove(emitter));
emitter.onError(error -> emitters.remove(emitter));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,10 @@ private String convertToThumbnailPath(MultipartFile thumbnailFile) throws IOExce
private String getExtension(String filename) {
return filename.substring(filename.lastIndexOf(".") + 1);
}

public Quiz getQuizById(Long quizId) {
return quizRepository
.findById(quizId)
.orElseThrow(() -> new RuntimeException("E404002: 존재하지 않는 퀴즈입니다."));
}
}