diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/repository/FairytaleRepository.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/repository/FairytaleRepository.java index 11b3b8a..eae3a05 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/repository/FairytaleRepository.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/repository/FairytaleRepository.java @@ -19,9 +19,9 @@ public interface FairytaleRepository extends JpaRepository { countQuery = "SELECT COUNT(f) FROM Fairytale f WHERE f.user.id = :userId AND f.background IN :backgrounds") Page findByUserIdAndBackgroundIn(@Param("userId") Long userId, @Param("backgrounds") List backgrounds, Pageable pageable); - @Query(value = "SELECT f FROM Fairytale f JOIN FETCH f.user WHERE f.user.id <> :userId AND f.background IN :backgrounds", - countQuery = "SELECT COUNT(f) FROM Fairytale f WHERE f.user.id <> :userId AND f.background IN :backgrounds") - Page findByUserIdNotAndBackgroundIn(@Param("userId") Long userId, @Param("backgrounds") List backgrounds, Pageable pageable); + @Query(value = "SELECT f FROM Fairytale f JOIN FETCH f.user WHERE f.user.id <> :userId ORDER BY f.createdAt DESC", + countQuery = "SELECT COUNT(f) FROM Fairytale f WHERE f.user.id <> :userId") + Page findSharedFairytales(@Param("userId") Long userId, Pageable pageable); @Query("SELECT f FROM Fairytale f JOIN FETCH f.user WHERE f.id = :id") Optional findByIdWithUser(@Param("id") Long id); diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleService.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleService.java index a9099e7..b237d70 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleService.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleService.java @@ -10,7 +10,7 @@ public interface FairytaleService { Page getMyFairytales(Long userId, Island island, Pageable pageable); - Page getSharedFairytales(Long userId, Island island, Pageable pageable); + Page getSharedFairytales(Long userId, Pageable pageable); FairytaleDetailRes getFairytaleDetail(Long fairytaleId); } diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleServiceImpl.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleServiceImpl.java index 942cc4f..1024dcb 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleServiceImpl.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleServiceImpl.java @@ -8,6 +8,8 @@ import com.capstone.kkumteul.domain.fairytale.web.dto.FairytaleDetailRes; import com.capstone.kkumteul.domain.fairytale.web.dto.FairytaleListRes; import com.capstone.kkumteul.domain.fairytale.web.dto.ParagraphRes; +import com.capstone.kkumteul.domain.vocab.repository.WordEntryRepository; +import com.capstone.kkumteul.domain.vocab.web.dto.WordEntryRes; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -23,6 +25,7 @@ public class FairytaleServiceImpl implements FairytaleService { private final FairytaleRepository fairytaleRepository; private final ParagraphRepository paragraphRepository; + private final WordEntryRepository wordEntryRepository; @Override public Page getMyFairytales(Long userId, Island island, Pageable pageable) { @@ -31,8 +34,8 @@ public Page getMyFairytales(Long userId, Island island, Pageab } @Override - public Page getSharedFairytales(Long userId, Island island, Pageable pageable) { - return fairytaleRepository.findByUserIdNotAndBackgroundIn(userId, island.getBackgrounds(), pageable) + public Page getSharedFairytales(Long userId, Pageable pageable) { + return fairytaleRepository.findSharedFairytales(userId, pageable) .map(FairytaleListRes::from); } @@ -46,6 +49,9 @@ public FairytaleDetailRes getFairytaleDetail(Long fairytaleId) { .map(ParagraphRes::from) .toList(); - return FairytaleDetailRes.of(fairytale, paragraphs); + List vocab = WordEntryRes.listOf( + wordEntryRepository.findByFairytaleIdOrderByPageNoAsc(fairytaleId)); + + return FairytaleDetailRes.of(fairytale, paragraphs, vocab); } } diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/web/controller/FairytaleController.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/web/controller/FairytaleController.java index 87195a3..9805c52 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/web/controller/FairytaleController.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/web/controller/FairytaleController.java @@ -55,10 +55,9 @@ public ResponseEntity>> getMyFairytales( @GetMapping("/shared") public ResponseEntity>> getSharedFairytales( @AuthUser User user, - @RequestParam Island island, @PageableDefault(size = 6) Pageable pageable ) { - Page res = fairytaleService.getSharedFairytales(user.getId(), island, pageable); + Page res = fairytaleService.getSharedFairytales(user.getId(), pageable); return ResponseEntity.status(HttpStatus.OK).body(SuccessResponse.ok(res)); } diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/web/dto/FairytaleDetailRes.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/web/dto/FairytaleDetailRes.java index bce7cae..4021602 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/web/dto/FairytaleDetailRes.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/web/dto/FairytaleDetailRes.java @@ -1,6 +1,7 @@ package com.capstone.kkumteul.domain.fairytale.web.dto; import com.capstone.kkumteul.domain.fairytale.entity.Fairytale; +import com.capstone.kkumteul.domain.vocab.web.dto.WordEntryRes; import java.util.List; @@ -11,9 +12,10 @@ public record FairytaleDetailRes( String morality, String charSpecies, String background, - List paragraphs + List paragraphs, + List vocab ) { - public static FairytaleDetailRes of(Fairytale fairytale, List paragraphs) { + public static FairytaleDetailRes of(Fairytale fairytale, List paragraphs, List vocab) { return new FairytaleDetailRes( fairytale.getId(), fairytale.getTitle(), @@ -21,7 +23,8 @@ public static FairytaleDetailRes of(Fairytale fairytale, List para fairytale.getMorality().name(), fairytale.getCharSpecies().name(), fairytale.getBackground().name(), - paragraphs + paragraphs, + vocab ); } } diff --git a/src/main/java/com/capstone/kkumteul/domain/vocab/exception/VocabErrorCode.java b/src/main/java/com/capstone/kkumteul/domain/vocab/exception/VocabErrorCode.java index f518fbe..cebda4a 100644 --- a/src/main/java/com/capstone/kkumteul/domain/vocab/exception/VocabErrorCode.java +++ b/src/main/java/com/capstone/kkumteul/domain/vocab/exception/VocabErrorCode.java @@ -11,7 +11,6 @@ public enum VocabErrorCode implements BaseResponseCode { PARAGRAPH_NOT_FOUND_FOR_VOCAB("VOCAB_404_1", NOT_FOUND, "해당 페이지의 본문을 찾을 수 없습니다."), - VOCAB_FORBIDDEN("VOCAB_403_1", FORBIDDEN, "본인 동화의 단어장만 조회할 수 있습니다."), VOCAB_EXTRACT_FAILED("VOCAB_500_1", INTERNAL_SERVER_ERROR, "단어장 추출에 실패했습니다."); private final String code; diff --git a/src/main/java/com/capstone/kkumteul/domain/vocab/exception/VocabForbiddenException.java b/src/main/java/com/capstone/kkumteul/domain/vocab/exception/VocabForbiddenException.java deleted file mode 100644 index 679cd6c..0000000 --- a/src/main/java/com/capstone/kkumteul/domain/vocab/exception/VocabForbiddenException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.capstone.kkumteul.domain.vocab.exception; - -import com.capstone.kkumteul.global.exception.BaseException; - -public class VocabForbiddenException extends BaseException { - public VocabForbiddenException() { - super(VocabErrorCode.VOCAB_FORBIDDEN); - } -} diff --git a/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabService.java b/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabService.java index d01de3a..c88e0a7 100644 --- a/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabService.java +++ b/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabService.java @@ -2,12 +2,11 @@ import com.capstone.kkumteul.domain.kafka.dto.VocabExtractedMessage; import com.capstone.kkumteul.domain.vocab.service.dto.VocabExtractionResult; -import com.capstone.kkumteul.domain.vocab.web.dto.WordEntryRes; import java.util.List; /** - * 단어장 추출/조회 서비스. + * 단어장 추출 서비스. 조회는 동화 상세(FairytaleService)에 포함됨. */ public interface VocabService { @@ -26,12 +25,4 @@ public interface VocabService { * word가 null/blank이면 NO_DIFFICULT_WORD 처리. 모든 종착 분기에서 markVocabDone 호출 (SSE guarantee). */ VocabExtractionResult processExtractedWord(VocabExtractedMessage message); - - /** - * 본인 동화의 누적 단어장 조회. 페이지 번호 오름차순. - * - * @param userId 요청자 (소유권 검증용) - * @param fairytaleId 동화 ID - */ - List getVocab(Long userId, Long fairytaleId); } diff --git a/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabServiceImpl.java b/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabServiceImpl.java index 04f5ba9..65764f3 100644 --- a/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabServiceImpl.java +++ b/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabServiceImpl.java @@ -6,10 +6,8 @@ import com.capstone.kkumteul.domain.fairytale.service.FairytaleCheckService; import com.capstone.kkumteul.domain.kafka.dto.VocabExtractedMessage; import com.capstone.kkumteul.domain.vocab.entity.WordEntry; -import com.capstone.kkumteul.domain.vocab.exception.VocabForbiddenException; import com.capstone.kkumteul.domain.vocab.repository.WordEntryRepository; import com.capstone.kkumteul.domain.vocab.service.dto.VocabExtractionResult; -import com.capstone.kkumteul.domain.vocab.web.dto.WordEntryRes; import com.capstone.kkumteul.global.client.VocabExtractClient; import com.capstone.kkumteul.global.client.dto.VocabExtractResponse; import lombok.RequiredArgsConstructor; @@ -19,7 +17,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; -import java.util.Objects; import java.util.Optional; @Slf4j @@ -133,18 +130,4 @@ public VocabExtractionResult processExtractedWord(VocabExtractedMessage message) } } - /** - * 본인 동화 누적 단어장 조회. - * 동화 소유권 검증 후 페이지 순서로 반환. - */ - @Override - public List getVocab(Long userId, Long fairytaleId) { - Fairytale fairytale = fairytaleRepository.findById(fairytaleId) - .orElseThrow(FairytaleNotFoundException::new); - Objects.requireNonNull(fairytale.getUser(), "Fairytale.user는 null이 될 수 없음"); - if (!fairytale.getUser().getId().equals(userId)) { - throw new VocabForbiddenException(); - } - return WordEntryRes.listOf(wordEntryRepository.findByFairytaleIdOrderByPageNoAsc(fairytaleId)); - } } diff --git a/src/main/java/com/capstone/kkumteul/domain/vocab/web/controller/VocabController.java b/src/main/java/com/capstone/kkumteul/domain/vocab/web/controller/VocabController.java deleted file mode 100644 index c007ed4..0000000 --- a/src/main/java/com/capstone/kkumteul/domain/vocab/web/controller/VocabController.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.capstone.kkumteul.domain.vocab.web.controller; - -import com.capstone.kkumteul.domain.user.entity.User; -import com.capstone.kkumteul.domain.vocab.service.VocabService; -import com.capstone.kkumteul.domain.vocab.web.dto.WordEntryRes; -import com.capstone.kkumteul.global.response.SuccessResponse; -import com.capstone.kkumteul.global.security.AuthUser; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/fairytales") -public class VocabController { - - private final VocabService vocabService; - - /** 본인 동화의 누적 단어장 조회 (페이지 순). */ - @GetMapping("/{fairytaleId}/vocab") - public ResponseEntity>> getVocab( - @AuthUser User user, - @PathVariable Long fairytaleId - ) { - List entries = vocabService.getVocab(user.getId(), fairytaleId); - return ResponseEntity.status(HttpStatus.OK) - .body(SuccessResponse.ok(entries)); - } -} diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties deleted file mode 100644 index b034870..0000000 --- a/src/main/resources/application-dev.properties +++ /dev/null @@ -1,6 +0,0 @@ -# dev profile — Kafka 미가용 환경에서 vocab만 시연. -# 활성화는 -Dspring.profiles.active=dev 또는 SPRING_PROFILES_ACTIVE=dev 환경변수로. -# spring.profiles.active=dev 는 부트스트랩 패러독스 방지 위해 여기에 작성하지 않는다. - -fastapi.base-url=http://localhost:8000 -logging.level.com.capstone.kkumteul=DEBUG