-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #43 from Move-Log/develop
Merge develop to main
- Loading branch information
Showing
18 changed files
with
405 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
src/main/java/com/movelog/domain/gpt/application/GptService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package com.movelog.domain.gpt.application; | ||
|
||
import com.fasterxml.jackson.databind.JsonNode; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.web.reactive.function.client.WebClient; | ||
import reactor.core.publisher.Mono; | ||
|
||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
@Slf4j | ||
public class GptService { | ||
|
||
private final WebClient gptWebClient; // @Qualifier 제거하고 빈 이름에 맞는 생성자 주입 | ||
@Value("${chatgpt.api-key}") | ||
private String apiKey; | ||
@Value("${chatgpt.model}") | ||
private String model; | ||
@Value("${chatgpt.max-tokens}") | ||
private Integer maxTokens; | ||
@Value("${chatgpt.temperature}") | ||
private Double temperature; | ||
@Value("${chatgpt.top-p}") | ||
private Double topP; | ||
|
||
public Mono<JsonNode> callChatGpt(String prompt) { | ||
return gptWebClient.post() | ||
.uri("/chat/completions") // 경로에 대한 수정 | ||
.header("Authorization", "Bearer " + apiKey) | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.bodyValue(buildRequestBody(prompt)) | ||
.retrieve() | ||
.bodyToMono(JsonNode.class) | ||
.doOnSuccess(response -> log.info("GPT API 응답 성공: {}", response)) | ||
.doOnError(error -> log.error("GPT API 호출 중 오류 발생: {}", error.getMessage())) | ||
.onErrorResume(error -> { | ||
log.error("GPT API 호출 중 예외 처리 발생: {}", error.getMessage()); | ||
return Mono.error(new RuntimeException("GPT API 호출 실패", error)); | ||
}); | ||
} | ||
|
||
private Map<String, Object> buildRequestBody(String prompt) { | ||
Map<String, Object> bodyMap = new HashMap<>(); | ||
bodyMap.put("model", model); | ||
bodyMap.put("max_tokens", maxTokens); | ||
bodyMap.put("temperature", temperature); | ||
bodyMap.put("top_p", topP); | ||
bodyMap.put("messages", List.of( | ||
Map.of( | ||
"role", "user", | ||
"content", prompt | ||
) | ||
)); | ||
return bodyMap; | ||
} | ||
} |
85 changes: 85 additions & 0 deletions
85
src/main/java/com/movelog/domain/news/application/HeadlineGeneratorService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package com.movelog.domain.news.application; | ||
|
||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.movelog.domain.gpt.application.GptService; | ||
import com.movelog.domain.news.dto.response.HeadLineRes; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.stereotype.Service; | ||
import reactor.core.publisher.Mono; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
@Slf4j | ||
public class HeadlineGeneratorService { | ||
|
||
private final GptService gptService; | ||
|
||
public List<HeadLineRes> generateHeadLine(String option, String verb, String noun) { | ||
// 프롬프트 생성 | ||
String prompt = generatePrompt(option, verb, noun); | ||
|
||
// 비동기 방식으로 GPT API 호출 | ||
return gptService.callChatGpt(prompt) | ||
.flatMap(response -> { | ||
if (response == null) { | ||
return Mono.error(new RuntimeException("GPT API 응답이 없습니다.")); | ||
} | ||
|
||
// 응답 파싱하여 List<CreatePracticeRes> 반환 | ||
List<HeadLineRes> practiceResList = parseResponse(response); | ||
return Mono.just(practiceResList); | ||
}) | ||
.doOnError(error -> log.error("문제 생성 중 오류 발생: ", error)).block(); | ||
} | ||
|
||
|
||
// 프롬프트 생성 | ||
private String generatePrompt(String option, String verb, String noun) { | ||
return String.format( | ||
"다음 내용을 바탕으로 뉴스 헤드라인을 생성해주세요: %s\n" + | ||
"당신은 뉴스 헤드라인을 작성하는 역할이며, 다음 형식을 엄격히 따라 뉴스 헤드라인을 제공해 주세요:\n\n" + | ||
"이 뉴스 헤드라인의 목적은 사용자의 기록을 기반으로 약간의 재미 요소가 들어간 헤드라인을 제공하는 것입니다.\n" + | ||
"각 뉴스 헤드라인은 반드시 격식있는 어체로 구성되어야 하며, 반드시 올바른 어법을 준수해야 합니다.\n" + | ||
"각 헤드라인의 내용은 주어진 옵션, 동사, 명사를 기반으로 생성되어야 하며, 헤드라인 내용에 직접적인 내용이 들어가지 않아도 됩니다." + | ||
"뉴스 헤드라인은 반드시 3개의 후보를 제공해야 하며, 각 후보는 1개의 줄바꿈으로 구분됩니다.\n" + | ||
"각 헤드라인 내용의 구조는 반드시 쉼표로 나뉘어야 하며, 쉼표 이전의 내용에는 헤드라인 강조 옵션에 대한 내용이 포함되어야 합니다." + | ||
"쉼표 앞 뒤의 내용에 대한 글자 수는 각각 16자 이하여야 하며, 쉼표는 반드시 한글자로만 구성되어야 합니다.\n\n" + | ||
|
||
"다음은 제공된 정보를 통해 출력해야 하는 뉴스 헤드라인의 출력 예시입니다:\n\n" + | ||
"꾸준한 노력 끝에 완성한 마라톤 기록, 도전의 의미는?\n오랜만의 첫 도전, 무엇이 그를 움직이게 했나?\n드디어 끊어낸 술, 운동으로 극복해내" + | ||
|
||
"다음은 제공된 정보를 통해 출력해야 하는 뉴스 헤드라인의 출력 형식입니다:\n\n" + | ||
"첫번째 헤드라인 후보 내용\n두번째 헤드라인 후보 내용\n세번째 헤드라인 후보 내용\n" + | ||
|
||
"반드시 뉴스 헤드라인 형식을 준수해 주세요. 정해진 형식을 따르지 않으면 응답을 처리할 수 없습니다." + | ||
"만약 결과 헤드라인 내용이 위 형식과 다르다면, 다시 요청하여 주세요.", | ||
option, verb, noun | ||
); | ||
} | ||
|
||
// GPT 응답 파싱 | ||
private List<HeadLineRes> parseResponse(JsonNode response) { | ||
List<HeadLineRes> headLineResList = new ArrayList<>(); | ||
|
||
// GPT 응답에서 질문, 답변, 해설 추출 | ||
String textResponse = response.path("choices").get(0).path("message").path("content").asText().trim(); | ||
log.info("Full Text Response: " + textResponse); // 전체 응답 확인 | ||
|
||
String[] headLines = textResponse.split("\n"); | ||
// 헤드라인 생성 결과가 3개가 아닌 경우 예외 처리 | ||
if (headLines.length != 3) { | ||
throw new RuntimeException("생성된 헤드라인이 3개가 아닙니다."); | ||
} | ||
|
||
// 헤드라인 후보 파싱 결과를 리스트에 저장 후 응답 리턴 | ||
for (String headLine : headLines) { | ||
headLineResList.add(HeadLineRes.builder().headLine(headLine).build()); | ||
} | ||
|
||
return headLineResList; | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
src/main/java/com/movelog/domain/news/application/NewsService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package com.movelog.domain.news.application; | ||
|
||
import com.movelog.domain.news.dto.request.NewsHeadLineReq; | ||
import com.movelog.domain.news.dto.response.HeadLineRes; | ||
import com.movelog.domain.user.application.UserService; | ||
import com.movelog.domain.user.domain.User; | ||
import com.movelog.domain.user.domain.repository.UserRepository; | ||
import com.movelog.domain.user.exception.UserNotFoundException; | ||
import com.movelog.global.DefaultAssert; | ||
import com.movelog.global.config.security.token.UserPrincipal; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
@Transactional(readOnly = true) | ||
public class NewsService { | ||
private final HeadlineGeneratorService headlineGeneratorService; | ||
private final UserService userService; | ||
private final UserRepository userRepository; | ||
|
||
public List<HeadLineRes> createHeadLine(UserPrincipal userPrincipal, NewsHeadLineReq newsHeadLineReq) { | ||
User user = validateUser(userPrincipal); | ||
// id가 5인 유저 정보(테스트용) | ||
// User user = userRepository.findById(5L).orElseThrow(UserNotFoundException::new); | ||
String option = newsHeadLineReq.getOption(); | ||
String verb = newsHeadLineReq.getVerb(); | ||
String noun = newsHeadLineReq.getNoun(); | ||
return headlineGeneratorService.generateHeadLine(option, verb, noun); | ||
} | ||
|
||
|
||
private User validateUser(UserPrincipal userPrincipal) { | ||
Optional<User> userOptional = userService.findById(userPrincipal.getId()); | ||
DefaultAssert.isOptionalPresent(userOptional); | ||
return userOptional.get(); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
src/main/java/com/movelog/domain/news/dto/request/NewsHeadLineReq.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.movelog.domain.news.dto.request; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Builder | ||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
@Getter | ||
public class NewsHeadLineReq { | ||
@Schema( type = "String", example ="첫 도전, 오랜만에 다시, 꾸준히 이어온 기록, 끊어낸 습관 중 택 1", description="뉴스 헤드라인 고정 옵션입니다.") | ||
private String option; | ||
|
||
@Schema( type = "String", example ="했어요, 먹었어요, 갔어요 중 택 1", description="사용자가 선택한 동사 정보입니다.") | ||
private String verb; | ||
|
||
@Schema( type = "String", example ="클라이밍", description="사용자가 선택한 명사 정보입니다.") | ||
private String noun; | ||
|
||
|
||
} |
16 changes: 16 additions & 0 deletions
16
src/main/java/com/movelog/domain/news/dto/response/HeadLineRes.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.movelog.domain.news.dto.response; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Builder | ||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
@Getter | ||
public class HeadLineRes { | ||
@Schema( type = "String", example ="5년 만의 첫 도전, 무엇이 그를 움직이게 했나?", description="뉴스 헤드라인 추천 내용입니다.") | ||
private String headLine; | ||
} |
46 changes: 46 additions & 0 deletions
46
src/main/java/com/movelog/domain/news/presentation/NewsController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package com.movelog.domain.news.presentation; | ||
|
||
import com.movelog.domain.news.application.NewsService; | ||
import com.movelog.domain.news.dto.request.NewsHeadLineReq; | ||
import com.movelog.domain.news.dto.response.HeadLineRes; | ||
import com.movelog.global.config.security.token.CurrentUser; | ||
import com.movelog.global.config.security.token.UserPrincipal; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.Parameter; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import java.util.List; | ||
|
||
@Slf4j | ||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/v1/news") | ||
@Tag(name = "News", description = "뉴스 관련 API입니다.") | ||
public class NewsController { | ||
|
||
private final NewsService newsService; | ||
|
||
@Operation(summary = "뉴스 헤드라인 생성 API", description = "뉴스 헤드라인을 생성하는 API입니다.") | ||
@ApiResponses(value = { | ||
@ApiResponse(responseCode = "200", description = "뉴스 헤드라인 생성 성공"), | ||
@ApiResponse(responseCode = "400", description = "뉴스 헤드라인 생성 실패") | ||
}) | ||
@PostMapping("/headline") | ||
public List<HeadLineRes> createHeadLine( | ||
@Parameter(description = "Access Token을 입력해주세요.", required = true) @CurrentUser UserPrincipal userPrincipal, | ||
@Parameter(description = "뉴스 헤드라인 생성 요청", required = true) @RequestBody NewsHeadLineReq newsHeadLineReq | ||
) { | ||
return newsService.createHeadLine(userPrincipal, newsHeadLineReq); | ||
} | ||
|
||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
src/main/java/com/movelog/domain/record/domain/VerbType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.movelog.domain.record.domain; | ||
|
||
import lombok.Getter; | ||
|
||
@Getter | ||
public enum VerbType { | ||
// 했어요, 먹었어요, 갔어요 | ||
DO("했어요"), EAT("먹었어요"), GO("갔어요"); | ||
|
||
private final String verbType; | ||
|
||
VerbType(String verbType) { | ||
this.verbType = verbType; | ||
} | ||
|
||
// Enum 매핑 메서드 추가 | ||
public static VerbType fromValue(String value) { | ||
for (VerbType type : values()) { | ||
if (type.getVerbType().equals(value)) { | ||
return type; | ||
} | ||
} | ||
throw new IllegalArgumentException("No enum constant for value: " + value); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 1 addition & 2 deletions
3
src/main/java/com/movelog/domain/record/presentation/RecordController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.