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,19 @@
package com.capstone.kkumteul.domain.fairytale.extern;

import com.capstone.kkumteul.domain.fairytale.exception.ParagraphNotFoundException;
import com.capstone.kkumteul.domain.fairytale.repository.ParagraphRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class ParagraphAdapter implements ParagraphPort {

private final ParagraphRepository paragraphRepository;

@Override
public int getPageNoByParagraphId(Long paragraphId) {
return paragraphRepository.findById(paragraphId)
.orElseThrow(ParagraphNotFoundException::new).getPage();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.capstone.kkumteul.domain.fairytale.extern;

public interface ParagraphPort {

int getPageNoByParagraphId(Long paragraphId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ public interface FairytaleCheckService {
boolean isBothDone(Long fairytaleId, int page);

void markTotalPages(Long fairytaleId, int totalPages);

void markTtsFileDone(Long fairytaleId, int pageNo, String ttsUrl);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.capstone.kkumteul.domain.fairytale.web.dto.SseEventRes;
import com.capstone.kkumteul.domain.vocab.entity.WordEntry;
import com.capstone.kkumteul.domain.vocab.repository.WordEntryRepository;
import com.capstone.kkumteul.domain.voice.repository.TtsHistoryRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -26,12 +27,14 @@ public class FairytaleCheckServiceImpl implements FairytaleCheckService {
private final SseService sseService;
private final WordEntryRepository wordEntryRepository;
private final ParagraphRepository paragraphRepository;
private final TtsHistoryRepository ttsHistoryRepository;

@Value("${vocab.fallback-threshold-seconds:300}")
private long vocabFallbackThresholdSeconds;

private static final String VOCAB_KEY = "vocab:%d:%d";
private static final String IMAGE_KEY = "image:%d:%d";
private static final String TTS_KEY = "tts:%d:%d";
private static final String TOTAL_KEY = "total:%d";
private static final String SENT_KEY = "sent:%d";
private static final String DONE = "done";
Expand All @@ -51,6 +54,13 @@ public void markImageDone(Long fairytaleId, int page) {
checkAndSend(fairytaleId, page);
}

@Override
public void markTtsFileDone(Long fairytaleId, int page, String ttsUrl) {
redisTemplate.opsForValue().set(String.format(TTS_KEY, fairytaleId, page), DONE.concat("@" + ttsUrl));
log.info("[TTS DONE] fairytaleId={}, page={}", fairytaleId, page);
checkAndSend(fairytaleId, page);
}

/**
* image done μ‹œμ μ— vocab λ§ˆμ»€κ°€ μ—†κ³  paragraph 생성 ν›„ μž„κ³„ 초과면 빈 vocab으둜 κ°•μ œ mark.
* AI Producerκ°€ vocab_extractedλ₯Ό λˆ„λ½ν•œ 경우의 SSE hang을 λ°©μ§€ν•œλ‹€.
Expand All @@ -71,11 +81,15 @@ private void forceVocabIfStale(Long fairytaleId, int page) {
redisTemplate.opsForValue().set(vocabKey, DONE);
}

// FIXME isBothDone -> isAllDone
@Override
public boolean isBothDone(Long fairytaleId, int page) {
String vocabStatus = redisTemplate.opsForValue().get(String.format(VOCAB_KEY, fairytaleId, page));
String imageStatus = redisTemplate.opsForValue().get(String.format(IMAGE_KEY, fairytaleId, page));
return DONE.equals(vocabStatus) && DONE.equals(imageStatus);

String ttsStatus = redisTemplate.opsForValue().get(String.format(TTS_KEY, fairytaleId, page));
return DONE.equals(vocabStatus) && DONE.equals(imageStatus) &&
ttsStatus != null && DONE.equals(ttsStatus.split("@")[0]);
}

//sse전솑
Expand All @@ -85,6 +99,7 @@ private void checkAndSend(Long fairytaleId, int page) {
if (!both) return;

Optional<WordEntry> wordEntry = wordEntryRepository.findByFairytaleIdAndPageNo(fairytaleId, page);
String ttsUrl = redisTemplate.opsForValue().get(String.format(TTS_KEY, fairytaleId, page)).split("@")[1];
List<Paragraph> paragraphs = paragraphRepository.findByFairytaleIdAndPage(fairytaleId, page);

if (paragraphs.isEmpty()) {
Expand All @@ -104,14 +119,16 @@ private void checkAndSend(Long fairytaleId, int page) {
page,
sentences,
vocab,
paragraph.getImageUrl()
paragraph.getImageUrl(),
ttsUrl
);

log.info("[PAGE_CONTENT SEND] fairytaleId={}, page={}", fairytaleId, page);
sseService.sendToClient(fairytaleId, "page_content", event);

redisTemplate.delete(String.format(VOCAB_KEY, fairytaleId, page));
redisTemplate.delete(String.format(IMAGE_KEY, fairytaleId, page));
redisTemplate.delete(String.format(TTS_KEY, fairytaleId, page));

Long sent = redisTemplate.opsForValue().increment(String.format(SENT_KEY, fairytaleId));
log.info("[SENT COUNT] fairytaleId={}, sent={}", fairytaleId, sent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ public interface FairytaleService {

Page<FairytaleListRes> getSharedFairytales(Long userId, Pageable pageable);

FairytaleDetailRes getFairytaleDetail(Long fairytaleId);
FairytaleDetailRes getFairytaleDetail(Long fairytaleId, Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
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 com.capstone.kkumteul.domain.voice.repository.TtsHistoryRepository;
import com.capstone.kkumteul.domain.voice.web.dto.TtsResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Expand All @@ -26,6 +28,7 @@ public class FairytaleServiceImpl implements FairytaleService {
private final FairytaleRepository fairytaleRepository;
private final ParagraphRepository paragraphRepository;
private final WordEntryRepository wordEntryRepository;
private final TtsHistoryRepository ttsHistoryRepository;

@Override
public Page<FairytaleListRes> getMyFairytales(Long userId, Island island, Pageable pageable) {
Expand All @@ -40,13 +43,19 @@ public Page<FairytaleListRes> getSharedFairytales(Long userId, Pageable pageable
}

@Override
public FairytaleDetailRes getFairytaleDetail(Long fairytaleId) {
public FairytaleDetailRes getFairytaleDetail(Long fairytaleId, Long userId) {
Fairytale fairytale = fairytaleRepository.findByIdWithUser(fairytaleId)
.orElseThrow(FairytaleNotFoundException::new);

List<ParagraphRes> paragraphs = paragraphRepository.findByFairytaleIdOrderByPageAsc(fairytaleId)
.stream()
.map(ParagraphRes::from)
.map(p -> {
String ttsUrl = ttsHistoryRepository
.findTtsUrlByFairytaleIdAndUserIdAndPageNo(fairytaleId, userId, p.getPage())
.map(TtsResponse::ttsUrl)
.orElse(null);
return ParagraphRes.from(p, ttsUrl);
})
.toList();

List<WordEntryRes> vocab = WordEntryRes.listOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,10 @@ public ResponseEntity<SuccessResponse<Page<FairytaleListRes>>> getSharedFairytal

@GetMapping("/{fairytaleId}")
public ResponseEntity<SuccessResponse<FairytaleDetailRes>> getFairytaleDetail(
@AuthUser User user,
@PathVariable Long fairytaleId
) {
FairytaleDetailRes res = fairytaleService.getFairytaleDetail(fairytaleId);
FairytaleDetailRes res = fairytaleService.getFairytaleDetail(fairytaleId, user.getId());
return ResponseEntity.status(HttpStatus.OK).body(SuccessResponse.ok(res));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
public record ParagraphRes(
int page,
List<String> sentences,
String imageUrl
String imageUrl,
String ttsUrl
) {
public static ParagraphRes from(Paragraph paragraph) {
public static ParagraphRes from(Paragraph paragraph, String ttsUrl) {
return new ParagraphRes(
paragraph.getPage(),
List.of(paragraph.getText().split("\n")),
paragraph.getImageUrl()
paragraph.getImageUrl(),
ttsUrl
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ public record SseEventRes(
int pageNo,
List<String> text,
Vocabulary vocab,
String imageUrl
String imageUrl,
String ttsUrl
) { public record Vocabulary(
String word,
String meaning
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,13 @@

import com.capstone.kkumteul.domain.fairytale.entity.Fairytale;
import com.capstone.kkumteul.domain.fairytale.exception.FairytaleNotFoundException;
import com.capstone.kkumteul.domain.game.entity.EdgeChoice;
import com.capstone.kkumteul.domain.game.entity.GameResult;
import com.capstone.kkumteul.domain.game.entity.GraphEdge;
import com.capstone.kkumteul.domain.game.entity.GraphNode;
import com.capstone.kkumteul.domain.game.entity.NodeCategory;
import com.capstone.kkumteul.domain.game.exception.AlreadyAnsweredException;
import com.capstone.kkumteul.domain.game.exception.EdgeNotFoundException;
import com.capstone.kkumteul.domain.game.exception.GameAlreadyCompletedException;
import com.capstone.kkumteul.domain.game.exception.GameForbiddenException;
import com.capstone.kkumteul.domain.game.exception.GameNotCompletedException;
import com.capstone.kkumteul.domain.game.exception.GraphNotFoundException;
import com.capstone.kkumteul.domain.game.exception.InvalidEdgeException;
import com.capstone.kkumteul.domain.game.exception.QuizNotFoundException;
import com.capstone.kkumteul.domain.game.entity.*;
import com.capstone.kkumteul.domain.game.exception.*;
import com.capstone.kkumteul.domain.game.repository.EdgeChoiceRepository;
import com.capstone.kkumteul.domain.game.repository.GameResultRepository;
import com.capstone.kkumteul.domain.game.repository.GraphEdgeRepository;
import com.capstone.kkumteul.domain.game.repository.GraphNodeRepository;
import com.capstone.kkumteul.domain.game.web.dto.ClassifyRes;
import com.capstone.kkumteul.domain.game.web.dto.EdgeDetailRes;
import com.capstone.kkumteul.domain.game.web.dto.GameStartRes;
import com.capstone.kkumteul.domain.game.web.dto.GameStatusRes;
import com.capstone.kkumteul.domain.game.web.dto.GraphDetailRes;
import com.capstone.kkumteul.domain.game.web.dto.QuizAnswerRes;
import com.capstone.kkumteul.domain.game.web.dto.QuizRes;
import com.capstone.kkumteul.domain.game.web.dto.*;
import com.capstone.kkumteul.domain.user.entity.User;
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,7 @@
import lombok.Getter;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class GameSession {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.capstone.kkumteul.domain.kafka.consumer;

import com.capstone.kkumteul.domain.fairytale.entity.Paragraph;
import com.capstone.kkumteul.domain.fairytale.extern.ParagraphPort;
import com.capstone.kkumteul.domain.fairytale.repository.ParagraphRepository;
import com.capstone.kkumteul.domain.fairytale.service.FairytaleCheckService;
import com.capstone.kkumteul.domain.kafka.dto.FairytaleCompletedMessage;
import com.capstone.kkumteul.domain.kafka.dto.ImageMessage;
import com.capstone.kkumteul.domain.kafka.dto.TtsFileDoneMessage;
import com.capstone.kkumteul.global.client.GraphExtractTrigger;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -22,8 +25,20 @@ public class FairytaleKafkaConsumer {
private final ParagraphRepository paragraphRepository;
private final FairytaleCheckService fairytaleCheckService;
private final GraphExtractTrigger graphExtractTrigger;
private final ParagraphPort paragraphPort;
private final ObjectMapper objectMapper;

@KafkaListener(topics = "tts_done", groupId = "kkumteul-group")
public void consumeTtsFileDone(String message) {
try {
TtsFileDoneMessage msg = objectMapper.readValue(message, TtsFileDoneMessage.class);
int pageNo = paragraphPort.getPageNoByParagraphId(msg.getParagraphId());
fairytaleCheckService.markTtsFileDone(msg.getFairytaleId(), pageNo, msg.getTtsUrl());
} catch (JsonProcessingException e) {
log.error("tts_done 처리 μ‹€νŒ¨ message={}", message, e);
}
}

@KafkaListener(topics = "fairytale_done", groupId = "kkumteul-group")
public void consumeDone(String message) {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.capstone.kkumteul.domain.kafka.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class TtsFileDoneMessage {
@JsonProperty("userId")
private Long userId;

@JsonProperty("fairytaleId")
private Long fairytaleId;

@JsonProperty("paragraphId")
private Long paragraphId;

@JsonProperty("ttsHistoryId")
private Long ttsHistoryId;

@JsonProperty("ttsUrl")
private String ttsUrl;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import lombok.AllArgsConstructor;
import lombok.Getter;

import static com.capstone.kkumteul.global.constant.StaticValue.*;
import static com.capstone.kkumteul.global.constant.StaticValue.INTERNAL_SERVER_ERROR;
import static com.capstone.kkumteul.global.constant.StaticValue.NOT_FOUND;

@Getter
@AllArgsConstructor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public enum VoiceErrorCode implements BaseResponseCode {

INVALID_FILE_EXCEPTION("INVALID_FILE_400", 400, "μš”μ²­κ°’μœΌλ‘œ μ „λ‹¬ν•œ 파일의 양식이 잘λͺ»μ—ˆμŠ΅λ‹ˆλ‹€."),
FILE_NOT_CREATED("FILE_NOT_FOUND_404", 404, "TTS μŒμ„± νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€."),
FILE_UPLOAD_FAIL("FILE_UPLOAD_FAIL_500", 500, "νŒŒμΌμ„ λ³€ν™˜ν•˜κ³ , S3에 μ—…λ‘œλ“œν•˜λŠ” 것에 μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.");
FILE_UPLOAD_FAIL("FILE_UPLOAD_FAIL_500", 500, "νŒŒμΌμ„ λ³€ν™˜ν•˜κ³ , S3에 μ—…λ‘œλ“œν•˜λŠ” 것에 μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€."),;

private final String code;
private final int httpStatus;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.capstone.kkumteul.domain.voice.repository;

import com.capstone.kkumteul.domain.voice.entity.TtsHistory;
import com.capstone.kkumteul.domain.voice.web.dto.TtsResponse;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
Expand All @@ -20,4 +21,21 @@ public interface TtsHistoryRepository extends CrudRepository<TtsHistory, Long> {
Optional<TtsHistory> findByParagraphIdAndUserId(
@Param("paragraphId") Long paragraphId,
@Param("userId") Long userId);


@Query("""
select new com.capstone.kkumteul.domain.voice.web.dto.TtsResponse(
t.ttsUrl
)
from TtsHistory t join
t.paragraph p
where p.fairytale.id = :fairytaleId
and p.page = :pageNo
and t.user.id = :userId
""")
Optional<TtsResponse> findTtsUrlByFairytaleIdAndUserIdAndPageNo(
@Param("fairytaleId") Long fairytaleId,
@Param("userId") Long userId,
@Param("pageNo") int pageNo
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.capstone.kkumteul.domain.user.entity.User;
import com.capstone.kkumteul.domain.voice.web.dto.TtsFileResponse;
import org.springframework.web.multipart.MultipartFile;

public interface VoiceService {
Void saveWav(byte[] wavFile, String originalFilename, User user);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.Optional;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.capstone.kkumteul.domain.voice.web.dto;

public record TtsResponse (
String ttsUrl
) {
}
Loading