-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into fe/refactor/#272
- Loading branch information
Showing
27 changed files
with
741 additions
and
527 deletions.
There are no files selected for viewing
61 changes: 42 additions & 19 deletions
61
...end/src/main/java/com/techeer/backend/api/aifeedback/controller/AIFeedbackController.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 |
---|---|---|
@@ -1,32 +1,55 @@ | ||
package com.techeer.backend.api.aifeedback.controller; | ||
|
||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import com.techeer.backend.api.aifeedback.converter.AIFeedbackConverter; | ||
import com.techeer.backend.api.aifeedback.domain.AIFeedback; | ||
import com.techeer.backend.api.aifeedback.dto.AIFeedbackResponse; | ||
import com.techeer.backend.api.aifeedback.service.AIFeedbackService; | ||
|
||
import com.techeer.backend.global.common.response.CommonResponse; | ||
import com.techeer.backend.global.success.SuccessCode; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
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.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@Tag(name = "AIFeedback", description = "AIFeedback 받기") | ||
@Tag(name = "AIFeedback", description = "AI 피드백 받기") | ||
@RestController | ||
@RequestMapping("/api/v1/aifeedbacks") | ||
public class AIFeedbackController { | ||
private final AIFeedbackService aifeedbackService; | ||
|
||
public AIFeedbackController(AIFeedbackService aifeedbackService) { | ||
this.aifeedbackService = aifeedbackService; | ||
} | ||
private final AIFeedbackService aifeedbackService; | ||
|
||
public AIFeedbackController(AIFeedbackService aifeedbackService) { | ||
this.aifeedbackService = aifeedbackService; | ||
} | ||
|
||
@Operation(summary = "AI 피드백 생성", description = "본인 이력서에 대한 AI 피드백을 진행합니다.") | ||
@PostMapping("/{resume_id}") | ||
public CommonResponse<AIFeedbackResponse> createFeedbackFromS3(@PathVariable("resume_id") Long resumeId) { | ||
AIFeedback feedback = aifeedbackService.generateAIFeedbackFromS3(resumeId); | ||
AIFeedbackResponse feedbackResponse = AIFeedbackConverter.toResponse(feedback); | ||
return CommonResponse.of(SuccessCode.CREATED, feedbackResponse); | ||
} | ||
|
||
@Operation(summary = "단일 피드백 조회", description = "피드백 ID를 통해 단일 AI 피드백을 조회합니다.") | ||
@GetMapping("/{aifeedback_id}") | ||
public CommonResponse<AIFeedbackResponse> getFeedbackById(@PathVariable("aifeedback_id") Long aifeedbackId) { | ||
AIFeedback aifeedback = aifeedbackService.getFeedbackById(aifeedbackId); | ||
AIFeedbackResponse response = AIFeedbackConverter.toResponse(aifeedback); | ||
return CommonResponse.of(SuccessCode.OK, response); | ||
} | ||
|
||
@Operation(summary = "AIFeedback 생성", description = "본인 이력서에 대한 ai 피드백을 진행합니다.") | ||
@PostMapping("/{resume_id}") | ||
public ResponseEntity<AIFeedbackResponse> createFeedbackFromS3(@PathVariable("resume_id") Long resumeId) { | ||
AIFeedbackResponse feedbackResponse = aifeedbackService.generateAIFeedbackFromS3(resumeId); | ||
return ResponseEntity.status(HttpStatus.CREATED).body(feedbackResponse); | ||
} | ||
@Operation(summary = "이력서별 피드백 목록 조회", description = "특정 이력서에 대한 모든 AI 피드백을 조회합니다.") | ||
@GetMapping("/resume/{resume_id}") | ||
public CommonResponse<List<AIFeedbackResponse>> getFeedbacksByResumeId(@PathVariable("resume_id") Long resumeId) { | ||
List<AIFeedback> feedbacks = aifeedbackService.getFeedbacksByResumeId(resumeId); | ||
List<AIFeedbackResponse> responses = feedbacks.stream() | ||
.map(feedback -> AIFeedbackConverter.toResponse(feedback)) | ||
.collect(Collectors.toList()); | ||
return CommonResponse.of(SuccessCode.OK, responses); | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
backend/src/main/java/com/techeer/backend/api/aifeedback/converter/AIFeedbackConverter.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,18 @@ | ||
package com.techeer.backend.api.aifeedback.converter; | ||
|
||
import com.techeer.backend.api.aifeedback.domain.AIFeedback; | ||
import com.techeer.backend.api.aifeedback.dto.AIFeedbackResponse; | ||
|
||
public class AIFeedbackConverter { | ||
public static AIFeedbackResponse toResponse(AIFeedback aifeedback) { | ||
if (aifeedback == null) { | ||
return null; | ||
} | ||
return AIFeedbackResponse.builder() | ||
.id(aifeedback.getId()) | ||
.resumeId(aifeedback.getResumeId()) | ||
.feedback(aifeedback.getFeedback()) | ||
.build(); | ||
} | ||
} | ||
|
6 changes: 5 additions & 1 deletion
6
...end/src/main/java/com/techeer/backend/api/aifeedback/repository/AIFeedbackRepository.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 |
---|---|---|
@@ -1,11 +1,15 @@ | ||
package com.techeer.backend.api.aifeedback.repository; | ||
|
||
import com.techeer.backend.api.aifeedback.domain.AIFeedback; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.stereotype.Repository; | ||
|
||
@Repository | ||
public interface AIFeedbackRepository extends JpaRepository<AIFeedback, Long> { | ||
Optional<AIFeedback> findByResumeId(Long resumeId); | ||
List<AIFeedback> findByResumeId(Long resumeId); | ||
|
||
Optional<AIFeedback> findById(Long feedbackId); | ||
|
||
} |
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
94 changes: 50 additions & 44 deletions
94
backend/src/main/java/com/techeer/backend/api/aifeedback/service/OpenAIService.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 |
---|---|---|
@@ -1,77 +1,83 @@ | ||
package com.techeer.backend.api.aifeedback.service; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import java.io.IOException; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
import org.apache.http.HttpResponse; | ||
import org.apache.http.client.config.RequestConfig; | ||
import org.apache.http.client.methods.HttpPost; | ||
import org.apache.http.entity.ContentType; | ||
import org.apache.http.entity.StringEntity; | ||
import org.apache.http.impl.client.CloseableHttpClient; | ||
import org.apache.http.impl.client.HttpClients; | ||
import org.apache.http.util.EntityUtils; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Service; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
|
||
@Service | ||
public class OpenAIService { | ||
|
||
@Value("${chatgpt.api.key}") | ||
private String apiKey; | ||
@Value("${chatgpt.api.key}") | ||
private String apiKey; | ||
|
||
@Value("${chatgpt.api.url}") | ||
private String apiUrl; | ||
|
||
@Value("${chatgpt.api.url}") | ||
private String apiUrl; | ||
private final CloseableHttpClient httpClient; | ||
private static final int TIMEOUT = 30000; | ||
|
||
private final CloseableHttpClient httpClient; | ||
private static final int TIMEOUT = 30000; | ||
private final ObjectMapper objectMapper = new ObjectMapper(); | ||
|
||
private final ObjectMapper objectMapper = new ObjectMapper(); | ||
public OpenAIService() { | ||
RequestConfig requestConfig = RequestConfig.custom() | ||
.setConnectTimeout(TIMEOUT) | ||
.setSocketTimeout(TIMEOUT) | ||
.build(); | ||
|
||
public OpenAIService() { | ||
RequestConfig requestConfig = RequestConfig.custom() | ||
.setConnectTimeout(TIMEOUT) | ||
.setSocketTimeout(TIMEOUT) | ||
.build(); | ||
this.httpClient = HttpClients.custom() | ||
.setDefaultRequestConfig(requestConfig) | ||
.build(); | ||
} | ||
|
||
this.httpClient = HttpClients.custom() | ||
.setDefaultRequestConfig(requestConfig) | ||
.build(); | ||
} | ||
public String getAIFeedback(String resumeText) throws IOException { | ||
HttpPost httpPost = new HttpPost(apiUrl); | ||
httpPost.setHeader("Content-Type", "application/json"); | ||
httpPost.setHeader("Authorization", "Bearer " + apiKey); | ||
|
||
public String getAIFeedback(String resumeText) throws IOException { | ||
HttpPost httpPost = new HttpPost(apiUrl); | ||
httpPost.setHeader("Content-Type", "application/json"); | ||
httpPost.setHeader("Authorization", "Bearer " + apiKey); | ||
String jsonBody = createRequestBody(resumeText); | ||
StringEntity entity = new StringEntity(jsonBody, ContentType.APPLICATION_JSON); | ||
httpPost.setEntity(entity); | ||
|
||
// JSON 요청 본문 생성 | ||
StringEntity entity = new StringEntity(createRequestBody(resumeText)); | ||
httpPost.setEntity(entity); | ||
HttpResponse response = httpClient.execute(httpPost); | ||
int statusCode = response.getStatusLine().getStatusCode(); | ||
String responseBody = EntityUtils.toString(response.getEntity()); | ||
|
||
HttpResponse response = httpClient.execute(httpPost); | ||
int statusCode = response.getStatusLine().getStatusCode(); | ||
if (statusCode != 200) { | ||
throw new IOException("OpenAI API 호출 실패: 상태 코드 " + statusCode + ", 응답: " + responseBody); | ||
} | ||
return responseBody; | ||
} | ||
|
||
if (statusCode != 200) { | ||
String errorMessage = EntityUtils.toString(response.getEntity()); | ||
throw new IOException("OpenAI API 호출 실패: 상태 코드 " + statusCode + ", 응답: " + errorMessage); | ||
} | ||
|
||
return EntityUtils.toString(response.getEntity()); | ||
} | ||
// ObjectMapper를 사용하여 JSON 요청 본문 생성 | ||
private String createRequestBody(String resumeText) throws IOException { | ||
Map<String, Object> requestBody = new HashMap<>(); | ||
requestBody.put("model", "gpt-4"); | ||
requestBody.put("temperature", 0.7); | ||
|
||
// ObjectMapper를 사용하여 JSON 요청 본문 생성 | ||
private String createRequestBody(String resumeText) throws IOException { | ||
Map<String, Object> requestBody = new HashMap<>(); | ||
requestBody.put("model", "gpt-4o"); | ||
Map<String, String> message = new HashMap<>(); | ||
message.put("role", "user"); | ||
// 불필요한 공백이나 개행이 문제를 일으킬 수 있으므로 trim()으로 정리합니다. | ||
message.put("content", ("이 이력서에서 잘 작성된 부분과 개선해야 할 부분을 구체적으로 지적해 주세요. " + | ||
"특히, 내용의 명확성, 경험 기술의 구체성, 그리고 부족한 스킬이나 프로젝트가 있는지에 대한 피드백을 제공해 주세요.\n" + resumeText).trim()); | ||
|
||
Map<String, String> message = new HashMap<>(); | ||
message.put("role", "user"); | ||
message.put("content", "\"이 이력서에서 잘 작성된 부분과 개선해야 할 부분을 구체적으로 지적해 주세요. 특히, 내용의 명확성, 경험 기술의 구체성, 그리고 부족한 스킬이나 프로젝트가 있는지에 대한 피드백을 제공해 주세요.\": \n" + resumeText); | ||
// messages는 List 또는 배열 형태여야 합니다. | ||
requestBody.put("messages", new Object[]{message}); | ||
|
||
requestBody.put("messages", new Object[] {message}); | ||
// JSON 문자열 생성 후 로그 출력 (디버깅용) | ||
String jsonBody = objectMapper.writeValueAsString(requestBody); | ||
return jsonBody; | ||
} | ||
|
||
return objectMapper.writeValueAsString(requestBody); | ||
} | ||
} |
Oops, something went wrong.