Skip to content

Commit

Permalink
feat : 질문 업데이트 API 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
1000kkannoo committed Aug 28, 2024
1 parent 2070e5f commit a746acc
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ public CustomResponseEntity<Void> deleteAccountInsurance(
}

/**
*
* @param accountId
* @param request
* @apiNote 내가 가입한 보험 수정 API
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ public CustomResponseEntity<Page<QnaBoardResponse.ReadQuestion>> readQuestions(
}

/**
* @apiNote 질문 게시판 상세 조회 API
* @param accountId
* @param qnaBoardId
* @return
* @apiNote 질문 게시판 상세 조회 API
*/
@GetMapping("/question/detail")
public CustomResponseEntity<QnaBoardResponse.ReadQuestion> readQuestion(
Expand All @@ -73,4 +73,17 @@ public CustomResponseEntity<QnaBoardResponse.ReadQuestion> readQuestion(
) {
return CustomResponseEntity.success(qnaBoardService.readQuestion(accountId, qnaBoardId));
}

/**
* @param accountId
* @param request
* @apiNote 질문 업데이트 API
*/
@PatchMapping("/question")
public CustomResponseEntity<QnaBoardResponse.Input> updateQuesion(
@AuthenticationPrincipal final Long accountId,
@RequestBody @Valid final QnaBoardRequest.Update request
) {
return CustomResponseEntity.success(qnaBoardService.updateQuesion(accountId, request));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,13 @@ public static class Input {
private Boolean isShare;
private InsuranceType insuranceType;
}

@AllArgsConstructor
@NoArgsConstructor
@Getter
@ToString
public static class Update {
private Long quesionId;
private String quesion;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,9 @@ public class QnaBoard {

// 추천된 Link를 관리하는 String(복잡한 연관관계 및 Join을 최적화하기 위한 String 처리)
private String insuranceLinks;

public void updateQnaBoard(String answer, String insuranceLinks) {
this.answer = answer;
this.insuranceLinks = insuranceLinks;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cmc15.backend.domain.qnaboard.repository;

import cmc15.backend.domain.account.entity.Account;
import cmc15.backend.domain.account.entity.InsuranceType;
import cmc15.backend.domain.qnaboard.entity.QnaBoard;
import org.springframework.data.domain.Page;
Expand All @@ -8,11 +9,12 @@
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
public interface QnaBoardRepository extends JpaRepository<QnaBoard, Long> {
List<QnaBoard> findByAccount_AccountId(Long accountId);
Page<QnaBoard> findByInsuranceTypeAndIsShare(InsuranceType insuranceType, Pageable pageable, boolean isShare);

Page<QnaBoard> findAllByIsShare(Pageable pageable, boolean isShare);
Optional<QnaBoard> findByQnaBoardIdAndAccount(Long qnaBoardId, Account account);
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@
@RequiredArgsConstructor
public class QnaBoardService {

public static final String PROMPT = "너는 보험설계사야. 너에게 보험에 대한 질문을 할거야. 질문에 대해서 300자 이내로 설명해주고, 한국에서 사용할 수 있는 실제 보험 상품을 무조건 3개 추천해줘. 보험 추천 방식은 \n" +
"ObjectMapper로 파싱할 수 있는 JSON 형태로 답변을 해줄거야, 아래는 예시야.\n" +
"{\n" +
"\"context\" : \"질문에 대한 답변 300자 이내\",\n" +
"\"links\" : [\n" +
"{\"insuranceCompany\" : \"추천된 보험회사 이름\", \"link\" : \"추천된 보험회사 사이트 주소\"}, ...]\n" +
"}\n\n" +
"질문 : ";

private final QnaBoardValidator qnaBoardValidator;
private final OpenAiChatModel openAiChatModel;
private final ObjectMapper objectMapper;
Expand All @@ -44,16 +53,7 @@ public QnaBoardResponse.Input inputQuesion(final Long accountId, final QnaBoardR
qnaBoardValidator.validateInputQuesion(request.getQuesion());
Account account = accountRepository.findById(accountId).orElseThrow(() -> new CustomException(NOT_FOUND_USER));

String prompt = "너는 보험설계사야. 너에게 보험에 대한 질문을 할거야. 질문에 대해서 300자 이내로 설명해주고, 한국에서 사용할 수 있는 실제 보험 상품을 무조건 3개 추천해줘. 보험 추천 방식은 \n" +
"ObjectMapper로 파싱할 수 있는 JSON 형태로 답변을 해줄거야, 아래는 예시야.\n" +
"{\n" +
"\"context\" : \"질문에 대한 답변 300자 이내\",\n" +
"\"links\" : [\n" +
"{\"insuranceCompany\" : \"추천된 보험회사 이름\", \"link\" : \"추천된 보험회사 사이트 주소\"}, ...]\n" +
"}\n\n" +
"질문 : ";

String call = openAiChatModel.call(prompt + request.getQuesion());
String call = openAiChatModel.call(PROMPT + request.getQuesion());
InsuranceCallResponse insuranceCallResponse = callParsingJson(call);
List<InsuranceCallResponse.InsuranceLink> links = insuranceCallResponse.getLinks();
return QnaBoardResponse.Input.to(qnaBoardRepository.save(QnaBoard.builder()
Expand Down Expand Up @@ -150,4 +150,23 @@ public QnaBoardResponse.ReadQuestion readQuestion(final Long accountId, final Lo

return QnaBoardResponse.ReadQuestion.to(qnaBoard, links);
}

/**
* @param accountId
* @param request
* @apiNote 질문 업데이트 API
*/
@Transactional
public QnaBoardResponse.Input updateQuesion(Long accountId, QnaBoardRequest.Update request) {
Account account = accountRepository.findById(accountId).orElseThrow(() -> new CustomException(NOT_FOUND_USER));
QnaBoard qnaBoard = qnaBoardRepository.findByQnaBoardIdAndAccount(request.getQuesionId(), account).orElseThrow(() -> new CustomException(NOT_FOUND_QNA_BOARD));

qnaBoardValidator.validateInputQuesion(request.getQuesion());
String call = openAiChatModel.call(PROMPT + request.getQuesion());
InsuranceCallResponse insuranceCallResponse = callParsingJson(call);
List<InsuranceCallResponse.InsuranceLink> links = insuranceCallResponse.getLinks();

qnaBoard.updateQnaBoard(insuranceCallResponse.getContext(), getInsuranceLinks(links));
return QnaBoardResponse.Input.to(qnaBoard, links);
}
}
60 changes: 60 additions & 0 deletions src/test/java/cmc15/backend/docs/QnaBoardControllerDocsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.restdocs.payload.JsonFieldType.*;
Expand Down Expand Up @@ -298,4 +299,63 @@ protected Object initController() {
.andExpect(status().isOk())
.andDo(document);
}

@DisplayName("질문 업데이트 API")
@Test
void 질문_업데이트_API() throws Exception{
// given
QnaBoardRequest.Update request = new QnaBoardRequest.Update(1L, "질문");

// InsuranceLink 객체 생성
InsuranceCallResponse.InsuranceLink link1 = new InsuranceCallResponse.InsuranceLink("삼성화재", "https://www.samsungfire.com");
InsuranceCallResponse.InsuranceLink link2 = new InsuranceCallResponse.InsuranceLink("현대해상", "https://www.hi.co.kr");
InsuranceCallResponse.InsuranceLink link3 = new InsuranceCallResponse.InsuranceLink("DB손해보험", "https://www.idbins.com");

// InsuranceLink 리스트 생성
List<InsuranceCallResponse.InsuranceLink> links = new ArrayList<>();
links.add(link1);
links.add(link2);
links.add(link3);

given(qnaBoardService.updateQuesion(any(), any(QnaBoardRequest.Update.class)))
.willReturn(QnaBoardResponse.Input.builder()
.qnaBoardId(1L)
.quesion("보험을 알아보기 쉬운 애플리케이션은 뭘까?")
.answer("보험을 알아보기 쉬운 애플리케이션을 찾고 계시는 군요! ...")
.isShare(true)
.insuranceType("하나손해보험")
.links(links)
.build()
);

ResourceSnippetParameters resources = ResourceSnippetParameters.builder()
.tag("질문 게시판/AI")
.summary("질문 업데이트(AI) API")
.requestHeaders(headerWithName("Authorization")
.description("Swagger 요청시 해당 입력칸이 아닌 우측 상단 자물쇠 또는 Authorize 버튼을 이용해 토큰을 넣어주세요"))
.requestFields(
fieldWithPath("quesionId").type(NUMBER).description("질문 ID"),
fieldWithPath("quesion").type(STRING).description("명령 메시지"))
.responseFields(
fieldWithPath("code").type(NUMBER).description("상태 코드"),
fieldWithPath("message").type(STRING).description("상태 메세지"),
fieldWithPath("data.qnaBoardId").type(NUMBER).description("질문 Id"),
fieldWithPath("data.quesion").type(STRING).description("질문"),
fieldWithPath("data.answer").type(STRING).description("AI 대답"),
fieldWithPath("data.isShare").type(BOOLEAN).description("전체 게시판 질문 공유 여부"),
fieldWithPath("data.insuranceType").type(STRING).description("보험 유형"),
fieldWithPath("data.links[]").type(ARRAY).description("보험 추천 사이트 리스트"),
fieldWithPath("data.links[].insuranceCompany").type(STRING).description("보험 추천된 회사 이름"),
fieldWithPath("data.links[].link").type(STRING).description("보험 추천된 회사 사이트 주소"))
.build();

RestDocumentationResultHandler document = documentHandler("update-quesion", prettyPrint(), prettyPrint(), resources);

// when // then
mockMvc.perform(RestDocumentationRequestBuilders.patch("/api/question")
.header("Authorization", "Bearer AccessToken")
.content(objectMapper.writeValueAsString(request))
.contentType(APPLICATION_JSON))
.andDo(document);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import cmc15.backend.domain.qnaboard.dto.request.QnaBoardRequest;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;

import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

Expand All @@ -23,7 +23,7 @@ class QnaBoardControllerTest extends ControllerTestSupport {
mockMvc.perform(RestDocumentationRequestBuilders.post("/api/quesion")
.header("Authorization", "Bearer AccessToken")
.content(objectMapper.writeValueAsString(request))
.contentType(MediaType.APPLICATION_JSON))
.contentType(APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk());
}
Expand Down Expand Up @@ -67,4 +67,17 @@ class QnaBoardControllerTest extends ControllerTestSupport {
.andDo(print())
.andExpect(status().isOk());
}

@DisplayName("질문 업데이트 API")
@Test
void 질문_업데이트_API() throws Exception{
// given
QnaBoardRequest.Update request = new QnaBoardRequest.Update(1L, "질문");

// when // then
mockMvc.perform(RestDocumentationRequestBuilders.patch("/api/question")
.header("Authorization", "Bearer AccessToken")
.content(objectMapper.writeValueAsString(request))
.contentType(APPLICATION_JSON));
}
}

0 comments on commit a746acc

Please sign in to comment.