Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public void gameStart(Long roomId, UserPrincipal principal) {
public void onCorrectAnswer(GameCorrectAnswerEvent event) {

Room room = event.room();
log.debug("Correct Answer! " + room.getCurrentRound());
String sessionId = event.sessionId();
ChatMessage chatMessage = event.chatMessage();
String answer = event.answer();
Expand Down Expand Up @@ -135,6 +136,8 @@ public void onCorrectAnswer(GameCorrectAnswerEvent event) {
@EventListener
public void onTimeout(GameTimeoutEvent event) {
Room room = event.room();
log.debug("Timeout! " + room.getCurrentRound());

String destination = getDestination(room.getId());

messageSender.sendBroadcast(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,23 @@
import io.f1.backend.domain.game.model.Room;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

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

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RequiredArgsConstructor
public class TimerService {

private final ApplicationEventPublisher eventPublisher;

public void startTimer(Room room, int delaySec) {
log.debug("Starting timer : round " + room.getCurrentRound());
cancelTimer(room);

ScheduledFuture<?> timer =
Expand All @@ -43,6 +46,7 @@ public boolean validateCurrentRound(Room room) {

public boolean cancelTimer(Room room) {
// 정답 맞혔어요 ~ 타이머 캔슬 부탁
log.debug("Canceling timer : round " + room.getCurrentRound());
ScheduledFuture<?> timer = room.getTimer();
if (timer != null && !timer.isDone()) {
return timer.cancel(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package io.f1.backend.domain.question.api;

import io.f1.backend.domain.question.app.QuestionService;
import io.f1.backend.domain.question.dto.QuestionUpdateRequest;

import lombok.RequiredArgsConstructor;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -20,21 +17,6 @@ public class QuestionController {

private final QuestionService questionService;

@PutMapping("/{questionId}")
public ResponseEntity<Void> updateQuestion(
@PathVariable Long questionId, @RequestBody QuestionUpdateRequest request) {

if (request.content() != null) {
questionService.updateQuestionContent(questionId, request.content());
}

if (request.content() != null) {
questionService.updateQuestionAnswer(questionId, request.answer());
}

return ResponseEntity.noContent().build();
}

@DeleteMapping("/{questionId}")
public ResponseEntity<Void> deleteQuestion(@PathVariable Long questionId) {
questionService.deleteQuestion(questionId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,23 @@

import static io.f1.backend.domain.question.mapper.QuestionMapper.questionRequestToQuestion;
import static io.f1.backend.domain.question.mapper.TextQuestionMapper.questionRequestToTextQuestion;
import static io.f1.backend.domain.quiz.app.QuizService.verifyUserAuthority;

import io.f1.backend.domain.question.dao.QuestionRepository;
import io.f1.backend.domain.question.dao.TextQuestionRepository;
import io.f1.backend.domain.question.dto.QuestionRequest;
import io.f1.backend.domain.question.dto.QuestionUpdateRequest;
import io.f1.backend.domain.question.entity.Question;
import io.f1.backend.domain.question.entity.TextQuestion;
import io.f1.backend.domain.quiz.entity.Quiz;
import io.f1.backend.global.exception.CustomException;
import io.f1.backend.global.exception.errorcode.AuthErrorCode;
import io.f1.backend.global.exception.errorcode.QuestionErrorCode;
import io.f1.backend.global.security.enums.Role;
import io.f1.backend.global.util.SecurityUtils;

import lombok.RequiredArgsConstructor;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Objects;

@Service
@RequiredArgsConstructor
public class QuestionService {
Expand All @@ -41,46 +38,17 @@ public void saveQuestion(Quiz quiz, QuestionRequest request) {
question.addTextQuestion(textQuestion);
}

@Transactional
public void updateQuestionContent(Long questionId, String content) {

validateContent(content);
public void updateQuestions(QuestionUpdateRequest request) {

Question question =
questionRepository
.findById(questionId)
.findById(request.getId())
.orElseThrow(
() -> new CustomException(QuestionErrorCode.QUESTION_NOT_FOUND));

verifyUserAuthority(question.getQuiz());

TextQuestion textQuestion = question.getTextQuestion();
textQuestion.changeContent(content);
}

private static void verifyUserAuthority(Quiz quiz) {
if (SecurityUtils.getCurrentUserRole() == Role.ADMIN) {
return;
}
if (!Objects.equals(SecurityUtils.getCurrentUserId(), quiz.getCreator().getId())) {
throw new CustomException(AuthErrorCode.FORBIDDEN);
}
}

@Transactional
public void updateQuestionAnswer(Long questionId, String answer) {

validateAnswer(answer);

Question question =
questionRepository
.findById(questionId)
.orElseThrow(
() -> new CustomException(QuestionErrorCode.QUESTION_NOT_FOUND));

verifyUserAuthority(question.getQuiz());

question.changeAnswer(answer);
textQuestion.changeContent(request.getContent());
question.changeAnswer(request.getAnswer());
}

@Transactional
Expand All @@ -96,16 +64,4 @@ public void deleteQuestion(Long questionId) {

questionRepository.delete(question);
}

private void validateAnswer(String answer) {
if (answer.trim().length() < 5 || answer.trim().length() > 30) {
throw new CustomException(QuestionErrorCode.INVALID_ANSWER_LENGTH);
}
}

private void validateContent(String content) {
if (content.trim().length() < 5 || content.trim().length() > 30) {
throw new CustomException(QuestionErrorCode.INVALID_CONTENT_LENGTH);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
package io.f1.backend.domain.question.dto;

public record QuestionUpdateRequest(String content, String answer) {}
import io.f1.backend.global.validation.TrimmedSize;

import jakarta.validation.constraints.NotBlank;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class QuestionUpdateRequest {

private Long id;

@TrimmedSize(min = 5, max = 30)
@NotBlank(message = "문제를 입력해주세요.")
private String content;

@TrimmedSize(min = 1, max = 30)
@NotBlank(message = "정답을 입력해주세요.")
private String answer;
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,9 @@ public ResponseEntity<Void> deleteQuiz(@PathVariable Long quizId) {
public ResponseEntity<Void> updateQuiz(
@PathVariable Long quizId,
@RequestPart(required = false) MultipartFile thumbnailFile,
@RequestPart QuizUpdateRequest request) {
@Valid @RequestPart QuizUpdateRequest request) {

if (request.title() != null) {
quizService.updateQuizTitle(quizId, request.title());
}

if (request.description() != null) {
quizService.updateQuizDesc(quizId, request.description());
}
quizService.updateQuizAndQuestions(quizId, request);

if (thumbnailFile != null && !thumbnailFile.isEmpty()) {
quizService.updateThumbnail(quizId, thumbnailFile);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import io.f1.backend.domain.question.app.QuestionService;
import io.f1.backend.domain.question.dto.QuestionRequest;
import io.f1.backend.domain.question.dto.QuestionUpdateRequest;
import io.f1.backend.domain.question.entity.Question;
import io.f1.backend.domain.quiz.dao.QuizRepository;
import io.f1.backend.domain.quiz.dto.QuizCreateRequest;
Expand All @@ -14,6 +15,7 @@
import io.f1.backend.domain.quiz.dto.QuizListResponse;
import io.f1.backend.domain.quiz.dto.QuizMinData;
import io.f1.backend.domain.quiz.dto.QuizQuestionListResponse;
import io.f1.backend.domain.quiz.dto.QuizUpdateRequest;
import io.f1.backend.domain.quiz.entity.Quiz;
import io.f1.backend.domain.user.dao.UserRepository;
import io.f1.backend.domain.user.entity.User;
Expand Down Expand Up @@ -138,7 +140,7 @@ public void deleteQuiz(Long quizId) {
quizRepository.deleteById(quizId);
}

private static void verifyUserAuthority(Quiz quiz) {
public static void verifyUserAuthority(Quiz quiz) {
if (SecurityUtils.getCurrentUserRole() == Role.ADMIN) {
return;
}
Expand All @@ -148,30 +150,22 @@ private static void verifyUserAuthority(Quiz quiz) {
}

@Transactional
public void updateQuizTitle(Long quizId, String title) {
public void updateQuizAndQuestions(Long quizId, QuizUpdateRequest request) {
Quiz quiz =
quizRepository
.findById(quizId)
.orElseThrow(() -> new CustomException(QuizErrorCode.QUIZ_NOT_FOUND));

verifyUserAuthority(quiz);

validateTitle(title);
quiz.changeTitle(title);
}

@Transactional
public void updateQuizDesc(Long quizId, String description) {

Quiz quiz =
quizRepository
.findById(quizId)
.orElseThrow(() -> new CustomException(QuizErrorCode.QUIZ_NOT_FOUND));
quiz.changeTitle(request.getTitle());
quiz.changeDescription(request.getDescription());

verifyUserAuthority(quiz);
List<QuestionUpdateRequest> questionReqList = request.getQuestions();

validateDesc(description);
quiz.changeDescription(description);
for (QuestionUpdateRequest questionReq : questionReqList) {
questionService.updateQuestions(questionReq);
}
}

@Transactional
Expand All @@ -191,18 +185,6 @@ public void updateThumbnail(Long quizId, MultipartFile thumbnailFile) {
quiz.changeThumbnailUrl(newThumbnailPath);
}

private void validateDesc(String desc) {
if (desc.trim().length() < 10 || desc.trim().length() > 50) {
throw new CustomException(QuizErrorCode.INVALID_DESC_LENGTH);
}
}

private void validateTitle(String title) {
if (title.trim().length() < 2 || title.trim().length() > 30) {
throw new CustomException(QuizErrorCode.INVALID_TITLE_LENGTH);
}
}

private void deleteThumbnailFile(String oldFilename) {
if (oldFilename.contains(DEFAULT)) {
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
package io.f1.backend.domain.quiz.dto;

public record QuizUpdateRequest(String title, String description) {}
import io.f1.backend.domain.question.dto.QuestionUpdateRequest;
import io.f1.backend.global.validation.TrimmedSize;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class QuizUpdateRequest {

@TrimmedSize(min = 2, max = 30)
@NotBlank(message = "퀴즈 제목을 설정해주세요.")
private String title;

@TrimmedSize(min = 10, max = 50)
@NotBlank(message = "퀴즈 설명을 적어주세요.")
private String description;

@Size(min = 10, max = 80, message = "문제는 최소 10개, 최대 80개로 정해주세요.")
private List<QuestionUpdateRequest> questions;
}