Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat : 마이페이지 기능을 위한 저장기능을 구현한다 #30

Merged
merged 5 commits into from
Jan 22, 2025
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
Expand Up @@ -49,4 +49,14 @@ public ResponseEntity<ApiResponse<?>> completeConcept(
conceptService.completeConceptLearning(currentUser.getId(), learningSetId, level);
return ResponseEntity.status(HttpStatus.OK).body(ApiResponse.EMPTY_RESPONSE);
}

@Operation(summary = "개념 학습 저장", description = "개념 학습을 저장합니다.")
@PostMapping("/learning/{conceptId}/scrap")
public ResponseEntity<ApiResponse<?>> scrapConcept(
final @AuthenticationPrincipal CustomUserDetails currentUser,
final @PathVariable("conceptId") long conceptId) {

conceptService.scrapConcept(currentUser.getId(), conceptId);
return ResponseEntity.status(HttpStatus.OK).body(ApiResponse.EMPTY_RESPONSE);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.ripple.BE.learning.controller;

import com.ripple.BE.global.dto.response.ApiResponse;
import com.ripple.BE.learning.dto.QuizDTO;
import com.ripple.BE.learning.dto.QuizListDTO;
import com.ripple.BE.learning.dto.QuizResultDTO;
import com.ripple.BE.learning.dto.request.SubmitAnswerRequest;
import com.ripple.BE.learning.dto.response.QuizListResponse;
import com.ripple.BE.learning.dto.response.QuizResponse;
import com.ripple.BE.learning.dto.response.QuizResultResponse;
import com.ripple.BE.learning.service.quiz.QuizService;
import com.ripple.BE.user.domain.CustomUserDetails;
Expand All @@ -16,6 +18,7 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand Down Expand Up @@ -70,4 +73,25 @@ public ResponseEntity<ApiResponse<Object>> finishQuiz(

return ResponseEntity.status(HttpStatus.OK).body(ApiResponse.from(ApiResponse.EMPTY_RESPONSE));
}

@Operation(summary = "퀴즈 저장", description = "퀴즈를 저장합니다.")
@PostMapping("/learning/quiz/{quizId}/scrap")
public ResponseEntity<ApiResponse<?>> scrapQuiz(
final @AuthenticationPrincipal CustomUserDetails currentUser,
final @PathVariable("quizId") long quizId) {

quizService.scrapQuiz(currentUser.getId(), quizId);
return ResponseEntity.status(HttpStatus.OK).body(ApiResponse.EMPTY_RESPONSE);
}

@Operation(summary = "개별 퀴즈 조회", description = "개별 퀴즈를 조회합니다.")
@GetMapping("/learning/quiz/{quizId}")
public ResponseEntity<ApiResponse<Object>> getSingleQuiz(
final @PathVariable("quizId") long quizId) {

QuizDTO quizDTO = quizService.getSingleQuiz(quizId);

return ResponseEntity.status(HttpStatus.OK)
.body(ApiResponse.from(QuizResponse.toQuizResponse(quizDTO)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.ripple.BE.learning.domain.concept;

import com.ripple.BE.user.domain.User;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Table(name = "concept_scraps")
@Getter
@Builder
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class ConceptScrap {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "concept_id")
private Concept concept;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.ripple.BE.learning.repository;

import com.ripple.BE.learning.domain.concept.ConceptScrap;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ConceptScrapRepository extends JpaRepository<ConceptScrap, Long> {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.ripple.BE.learning.repository;

import com.ripple.BE.learning.domain.quiz.QuizScrap;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface QuizScrapRepository extends JpaRepository<QuizScrap, Long> {}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.ripple.BE.learning.service.concept;

import com.ripple.BE.learning.domain.concept.Concept;
import com.ripple.BE.learning.domain.concept.ConceptScrap;
import com.ripple.BE.learning.domain.learningset.LearningSet;
import com.ripple.BE.learning.domain.learningset.UserLearningSet;
import com.ripple.BE.learning.dto.ConceptListDTO;
import com.ripple.BE.learning.exception.LearningException;
import com.ripple.BE.learning.exception.errorcode.LearningErrorCode;
import com.ripple.BE.learning.repository.ConceptRepository;
import com.ripple.BE.learning.repository.ConceptScrapRepository;
import com.ripple.BE.learning.repository.UserLearningSetRepository;
import com.ripple.BE.learning.service.learningset.LearningSetService;
import com.ripple.BE.user.domain.User;
Expand All @@ -28,6 +30,7 @@ public class ConceptService {

private final UserLearningSetRepository userLearningSetRepository;
private final ConceptRepository conceptRepository;
private final ConceptScrapRepository conceptScrapRepository;

/**
* 학습 세트의 개념 목록을 조회
Expand Down Expand Up @@ -66,4 +69,21 @@ public void completeConceptLearning(
userService.updateCompletedCountByLevel(user, level);
}
}

/**
* 개념 스크랩
*
* @param userId
* @param conceptId
*/
@Transactional
public void scrapConcept(final long userId, final long conceptId) {
User user = userService.findUserById(userId);
Concept concept =
conceptRepository
.findById(conceptId)
.orElseThrow(() -> new LearningException(LearningErrorCode.CONCEPT_NOT_FOUND));

conceptScrapRepository.save(ConceptScrap.builder().user(user).concept(concept).build());
}
}
54 changes: 47 additions & 7 deletions src/main/java/com/ripple/BE/learning/service/quiz/QuizService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
import com.ripple.BE.learning.domain.learningset.UserLearningSet;
import com.ripple.BE.learning.domain.quiz.FailQuiz;
import com.ripple.BE.learning.domain.quiz.Quiz;
import com.ripple.BE.learning.domain.quiz.QuizScrap;
import com.ripple.BE.learning.domain.type.Type;
import com.ripple.BE.learning.dto.QuizDTO;
import com.ripple.BE.learning.dto.QuizListDTO;
import com.ripple.BE.learning.dto.QuizResultDTO;
import com.ripple.BE.learning.exception.LearningException;
import com.ripple.BE.learning.exception.QuizException;
import com.ripple.BE.learning.exception.errorcode.LearningErrorCode;
import com.ripple.BE.learning.repository.QuizRepository;
import com.ripple.BE.learning.repository.QuizScrapRepository;
import com.ripple.BE.learning.repository.UserLearningSetRepository;
import com.ripple.BE.learning.service.learningset.LearningSetService;
import com.ripple.BE.user.domain.User;
Expand All @@ -28,10 +31,12 @@
@RequiredArgsConstructor
@Service
@Slf4j
@Transactional(readOnly = true)
public class QuizService {

private final UserLearningSetRepository userLearningSetRepository;
private final QuizRepository quizRepository;
private final QuizScrapRepository quizScrapRepository;

private final QuizRedisService quizRedisService;
private final LearningSetService learningSetService;
Expand Down Expand Up @@ -83,17 +88,17 @@ public QuizResultDTO submitAnswer(final long userId, final long quizId, final in

QuizListDTO quizListDTO =
quizRedisService.fetchFromRedis(userId, QUESTION_TYPE, QuizListDTO.class);

// 마이페이지의 저장한 퀴즈에서 다시풀기 진행 시 (퀴즈 진행 redis 데이터가 없을 경우)
Copy link
Member

Choose a reason for hiding this comment

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

오 이 부분은 생각 못했는데 추가해주셔서 감사합니다

if (quizListDTO == null) {
throw new LearningException(QUIZ_PROGRESS_NOT_FOUND);
Quiz quiz = getQuizById(quizId);
boolean isCorrect = isCorrectAnswer(QuizDTO.toQuizDTO(quiz), answerIndex);

return QuizResultDTO.toQuizResultDTO(isCorrect, quiz.getExplanation());
}

QuizDTO quizDTO = getQuizDTO(quizListDTO, quizId); // 퀴즈 정보 가져오기

boolean isCorrect =
quizDTO
.answer()
.trim()
.equals(quizDTO.choiceList().choices().get(answerIndex).content().trim());
boolean isCorrect = isCorrectAnswer(quizDTO, answerIndex);

if (!isCorrect) {
quizRedisService.saveToRedisList(userId, WRONG_ANSWER_TYPE, quizId); // 오답 리스트에 추가
Expand Down Expand Up @@ -148,4 +153,39 @@ private QuizDTO getQuizDTO(final QuizListDTO quizListDTO, final long quizId) {
.findFirst()
.orElseThrow(() -> new LearningException(QUIZ_PROGRESS_NOT_FOUND));
}

/**
* 퀴즈 스크랩
*
* @param userId
* @param quizId
*/
@Transactional
public void scrapQuiz(final long userId, final long quizId) {

User user = userService.findUserById(userId);
Quiz quiz =
quizRepository.findById(quizId).orElseThrow(() -> new QuizException(QUIZ_NOT_FOUND));

quizScrapRepository.save(QuizScrap.builder().user(user).quiz(quiz).build());
}

/**
* 개별 퀴즈 조회
*
* @param quizId
* @return 퀴즈 목록 반환
*/
public QuizDTO getSingleQuiz(final long quizId) {
Quiz quiz = getQuizById(quizId);
return QuizDTO.toQuizDTO(quiz);
}

// 정답 여부 확인
private boolean isCorrectAnswer(final QuizDTO quizDTO, final int answerIndex) {
return quizDTO
.answer()
.trim()
.equals(quizDTO.choiceList().choices().get(answerIndex).content().trim());
}
Copy link
Member

Choose a reason for hiding this comment

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

메소드로 따로 뺀 부분 좋은거 같아요

}