Skip to content

Commit

Permalink
Merge pull request #32 from Alpha-mon/31-feat-swagger-문서-정리-및-코드-수정
Browse files Browse the repository at this point in the history
31 feat swagger 문서 정리 및 코드 수정
  • Loading branch information
megymj committed Oct 19, 2023
2 parents 47a3e21 + 7a82553 commit dc60288
Show file tree
Hide file tree
Showing 18 changed files with 281 additions and 147 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.ai.roboadvisor.domain.chat.dto.request.ClearRequest;
import org.ai.roboadvisor.domain.chat.dto.request.MessageClearRequest;
import org.ai.roboadvisor.domain.chat.dto.request.MessageRequest;
import org.ai.roboadvisor.domain.chat.dto.response.ChatResponse;
import org.ai.roboadvisor.domain.chat.dto.response.ChatResult;
Expand Down Expand Up @@ -32,9 +32,11 @@ public class ChatController {
private final ChatService chatService;

@Operation(summary = "채팅 서비스 입장", description = """
사용자가 채팅 서비스 입장 시, 기존의 대화 내용을 불러온다.
사용자가 채팅 서비스에 입장할 때 요청되는 함수
기존에 대화 내용이 존재하지 않는다면(처음 입장하는 경우), 초기 메시지(Welcome Message)를 보내준다.
case 1: 처음 입장하는 경우 - 서버에서 챗봇 초기 메시지(Welcome Message)를 보내준다.
case 2: 기존에 대화 내용이 존재하는 경우 - 기존에 존재하는 대화 메시지 전체를 불러온다.
order : 대화 내용을 불러오는 경우에 숫자가 클수록 최근에 한 대화이다.
Expand All @@ -44,8 +46,8 @@ public class ChatController {
@getAllChats_CREATED
@ApiResponse_Internal_Server_Error
@GetMapping(value = "/{nickname}")
public ResponseEntity<SuccessApiResponse<ChatResult>> getAllChats(@PathVariable("nickname") String nickname) {
ChatResult chatResult = chatService.getAllChats(nickname);
public ResponseEntity<SuccessApiResponse<ChatResult>> enter(@PathVariable("nickname") String nickname) {
ChatResult chatResult = chatService.enter(nickname);
if (checkIfChatOrderResponseIsPresent(chatResult)) {
// case LOAD_CHAT_SUCCESS
return ResponseEntity.status(HttpStatus.OK)
Expand All @@ -57,32 +59,41 @@ public ResponseEntity<SuccessApiResponse<ChatResult>> getAllChats(@PathVariable(
}
}

@Operation(summary = "채팅 메시지 전송", description = "클라이언트로부터 사용자의 메시지를 받아서, ChatGPT로 보내고, 응답 결과를 받아 클라이언트로 전달한다")
@Operation(summary = "채팅 메시지 전송", description = """
사용자가 입력한 채팅 메시지를 전달한다.
프론트에서는 채팅 메시지를 서버로 보냄과 동시에 화면에 사용자가 보낸 채팅 메시지를 직접 띄우는 식으로 구현되어 있다.
서버에서는 사용자가 입력한 메시지를 ChatGPT API를 사용하여 전달한 다음, 응답 결과를 받아 반환한다.
Swagger 문서 하단의 Schemas 중 RequestBody로 'MessageRequest' 를 사용한다.
""")
@sendMessage_CREATED
@sendMessage_BAD_REQUEST
@ApiResponse_Internal_Server_Error
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SuccessApiResponse<ChatResult>> sendMessage(@RequestBody MessageRequest messageRequest) {
chatService.save(messageRequest);

ChatResponse result = chatService.getMessageFromApi(messageRequest.getNickname(), messageRequest.getContent());

return ResponseEntity.status(HttpStatus.CREATED)
.body(SuccessApiResponse.success(SuccessCode.CHAT_CREATED_SUCCESS, new ChatResult(result)));
}

@Operation(summary = "대화 내용 삭제", description = """
요청을 보내면, 기존 대화 내용이 전부 삭제된다.
기존 대화 내용이 전부 삭제된다.
대화 내용이 삭제된 이후, data에 초기 메시지(Welcome Message)를 리스트 형식으로 담아서 다시 보내준다.
Swagger 문서 하단의 Schemas 중 RequestBody로 'MessageClearRequest' 를 사용한다.
""")
@clear_CREATED
@ApiResponse_Internal_Server_Error
@PostMapping(value = "/clear")
public ResponseEntity<SuccessApiResponse<ChatResult>> clear(@RequestBody ClearRequest clearRequest) {
String userNickname = clearRequest.getNickname();
public ResponseEntity<SuccessApiResponse<ChatResult>> clear(@RequestBody MessageClearRequest messageClearRequest) {
return ResponseEntity.status(HttpStatus.CREATED)
.body(SuccessApiResponse.success(SuccessCode.CHAT_DELETED_SUCCESS,
chatService.clear(userNickname)));
chatService.clear(messageClearRequest)));
}

protected boolean checkIfChatOrderResponseIsPresent(ChatResult chatResult) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "채팅 대화 내역 삭제를 요청할 때 사용하는 JSON 요청 예시")
public class ClearRequest {
public class MessageClearRequest {

@Schema(description = "사용자 정보: 닉네임", example = "testUser")
@NotBlank
private String nickname;

public static ClearRequest of(String nickname) {
return new ClearRequest(nickname);
public static MessageClearRequest of(String nickname) {
return new MessageClearRequest(nickname);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
import lombok.extern.slf4j.Slf4j;
import org.ai.roboadvisor.domain.chat.dto.Message;
import org.ai.roboadvisor.domain.chat.dto.request.ChatGptRequest;
import org.ai.roboadvisor.domain.chat.dto.request.MessageClearRequest;
import org.ai.roboadvisor.domain.chat.dto.request.MessageRequest;
import org.ai.roboadvisor.domain.chat.dto.response.ChatGptResponse;
import org.ai.roboadvisor.domain.chat.dto.response.ChatOrderResponse;
import org.ai.roboadvisor.domain.chat.dto.response.ChatResponse;
import org.ai.roboadvisor.domain.chat.dto.response.ChatResult;
import org.ai.roboadvisor.domain.chat.entity.Chat;
import org.ai.roboadvisor.domain.chat.repository.ChatRepository;
import org.ai.roboadvisor.domain.user.repository.UserRepository;
import org.ai.roboadvisor.global.exception.CustomException;
import org.ai.roboadvisor.global.exception.ErrorCode;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -34,6 +36,7 @@
public class ChatService {

private final ChatGPTService chatGPTService;
private final UserRepository userRepository;
private final ChatRepository chatRepository;
private final MongoTemplate mongoTemplate;
private final String ROLE_USER = "user";
Expand All @@ -45,7 +48,7 @@ public class ChatService {
private String OPEN_AI_MODEL;

@Transactional
public ChatResult getAllChats(String nickname) {
public ChatResult enter(String nickname) {
if (!checkIfChatExistsInDb(nickname)) {
return new ChatResult(createAndSaveWelcomeMessage(nickname));
} else {
Expand All @@ -71,7 +74,14 @@ public ChatResult getAllChats(String nickname) {
}
}

private boolean checkIfChatExistsInDb(String email) {
return chatRepository.existsChatByNickname(email);
}

public void save(MessageRequest messageRequest) {
// 사용자 닉네임 검증
checkUserIsExisted(messageRequest.getNickname());

// MessageRequest time format 검증
LocalDateTime dateTime = parseDateTime(messageRequest.getTime())
.orElseThrow(() -> new CustomException(ErrorCode.TIME_INPUT_INVALID));
Expand All @@ -80,6 +90,7 @@ public void save(MessageRequest messageRequest) {
saveChat(userChat);
}


public ChatResponse getMessageFromApi(String nickname, String message) {
ChatGptRequest chatGptRequest = ChatGptRequest
.builder()
Expand All @@ -106,13 +117,7 @@ public ChatResponse getMessageFromApi(String nickname, String message) {
.message(responseMessage)
.time(now)
.build();

try {
chat.setTimeZone(chat.getTime(), KST_TO_UTC); // KST -> UTC
chatRepository.save(chat);
} catch (RuntimeException e) {
throw new CustomException(ErrorCode.INTERNAL_SERVER_ERROR);
}
saveChat(chat);

// 2. Return Message DTO to client
return ChatResponse.builder()
Expand All @@ -123,7 +128,10 @@ public ChatResponse getMessageFromApi(String nickname, String message) {
}
}

public ChatResult clear(String nickname) {
public ChatResult clear(MessageClearRequest clearRequest) {
String nickname = clearRequest.getNickname();
checkUserIsExisted(nickname);

// Clear All data
try {
chatRepository.deleteAllByNickname(nickname);
Expand All @@ -134,7 +142,7 @@ public ChatResult clear(String nickname) {
}

public ChatResponse createAndSaveWelcomeMessage(String nickname) {
String WELCOME_MESSAGE = "안녕하세요, 저는 AI로보어드바이저의 ChatGPT 서비스에요! 궁금한 점을 입력해주세요";
String WELCOME_MESSAGE = "안녕하세요, 저는 알파몬의 ChatGPT 서비스에요! 궁금한 점을 입력해주세요";

// 1. Create Chat Entity and Save
LocalDateTime now = LocalDateTime.now().withNano(0); // ignore milliseconds
Expand All @@ -144,7 +152,6 @@ public ChatResponse createAndSaveWelcomeMessage(String nickname) {
.message(WELCOME_MESSAGE)
.time(now)
.build();

saveChat(chat);

// 2. Entity -> DTO and Return Welcome Message
Expand All @@ -171,7 +178,10 @@ private Optional<LocalDateTime> parseDateTime(String timeString) {
}
}

private boolean checkIfChatExistsInDb(String email) {
return chatRepository.existsChatByNickname(email);
private void checkUserIsExisted(String nickname) {
userRepository.findUserByNickname(nickname)
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_EXISTED));
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,26 @@
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@ApiResponse(responseCode = "201", description = "사용자가 채팅방에 처음 입장한 경우, 챗봇의 Welcome Message를 보낸다.",
@ApiResponse(responseCode = "201", description = """
case 1: 처음 입장하는 경우 - 서버에서 챗봇 초기 메시지(Welcome Message)를 보내준다.
이 경우는 data 안에 chatResponse 객체를 반환한다.
""",
content = @Content(schema = @Schema(implementation = SuccessApiResponse.class),
examples = @ExampleObject(name = "example",
description = "처음 입장한 경우 응답 예시",
value = """
{
"code": 201,
"message": "채팅방 입장에 성공하셨습니다",
"data": {
"chatResponse": {
"role": "assistant",
"content": "안녕하세요, 저는 AI로보어드바이저의 ChatGPT 서비스에요! 궁금한 점을 입력해주세요",
"time": "2023-09-22 14:32:58"
}
}
}
"code": 201,
"message": "채팅방 입장에 성공하셨습니다",
"data": {
"chatResponse": {
"role": "assistant",
"content": "안녕하세요, 저는 알파몬의 ChatGPT 서비스에요! 궁금한 점을 입력해주세요",
"time": "2023-10-19 00:53:00"
}
}
}
"""
)))
public @interface getAllChats_CREATED {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,17 @@
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@ApiResponse(responseCode = "200", description = """
사용자가 채팅방에 처음 입장한 경우, 기존의 대화 내용을 보낸다.
case 2: 기존에 대화 내용이 존재하는 경우 - 기존에 존재하는 대화 메시지 전체를 불러온다.
이 경우는 data 안에 chatOrderResponse 객체를 반환한다.
chatOrderResponse Array 객체 안에, order, role, content, time 정보가 각각 담긴다.
order 값이 높을 수록, 나중에 작성된 메시지이다. 따라서 order 값을 기준으로 정렬하면 되며,
role: assistant(ChatGPT 답변 내용), role: user(사용자가 입력한 대화)
혹은 time 값을 DateTime 타입으로 바꿔서 정렬도 가능하다.
order 값이 높을 수록, 최근에 작성된 메시지이다. 따라서 order 값을 기준으로 정렬하면 되며, 혹은 time 값을 DateTime 타입으로 바꿔서 정렬도 가능하다.
'현재는 Pagination을 따로 사용하지 않고, 전체 데이터를 한 번에 모두 보내도록 구현되어 있음'
'현재는 Pagination을 따로 사용하지 않고, 전체 데이터를 모두 보내도록 구현되어 있음'
""",
content = @Content(schema = @Schema(implementation = SuccessApiResponse.class),
examples = @ExampleObject(name = "example",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,23 @@
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@ApiResponse(responseCode = "400", description = """
시간 형식을 잘못 입력한 경우
1. 사용자의 닉네임이 DB에 존재하지 않는 경우 -> example1
e.g) 2023-03-03 11:11:11 (사이에 띄어쓰기가 두 칸인 경우), 2023-03-03T11:11:11 (사이에 문자가 끼워져 있는 경우), ...
2. 시간 형식을 잘못 입력한 경우 -> example2
e.g) 2023-03-03 11:11:11 (사이에 띄어쓰기가 두 칸인 경우), 2023-03-03T11:11:11 (사이에 문자가 끼워져 있는 경우), etc..
""",
content = @Content(schema = @Schema(implementation = SuccessApiResponse.class),
examples = @ExampleObject(name = "example",
examples = {@ExampleObject(name = "example1",
description = "사용자의 닉네임이 DB에 존재하지 않는 경우 예시",
value = """
{
"code": 400,
"message": "가입된 사용자의 정보가 존재하지 않습니다",
"data": null
}
"""
), @ExampleObject(name = "example2",
description = "시간 형식을 잘못 입력한 경우 예시",
value = """
{
Expand All @@ -26,6 +37,7 @@
"data": null
}
"""
)))
)}
))
public @interface sendMessage_BAD_REQUEST {
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
"data": {
"chatResponse": {
"role": "assistant",
"content": "주식에서 선물거래는 미래에 일정한 날짜에 주식을 사거나 팔 수 있는 계약을 말합니다. 이러한 계약은 특정 주식의 가격을 현재의 가격에서 미리 확정하고 미래에 거래가 이루어지도록 하는 것이 목적입니다. 주식 선물거래는 투자자들이 주식 시장의 가격 변동에 대해 원하는 방향으로 수익을 얻거나 손실을 최소화하기 위해 사용됩니다. 예를 들어, 주식 가격이 상승할 것으로 예상되는 투자자는 주식 선물계약을 매수하여 주식을 미리 구매하는 것으로서, 가격이 상승하면 이익을 얻을 수 있습니다. 반대로, 주식 가격이 하락할 것으로 예상되는 투자자는 주식 선물계약을 매도하여 주식을 미리 판매하는 것으로서, 가격이 하락하면 이익을 얻을 수 있습니다. 선물거래는 주식 가격 변동에 대한 투자자의 예측을 기반으로 이루어지므로, 주식 시장에서 높은 위험성을 가지고 있습니다.",
"time": "2023-09-23 13:24:10"
"content": "주식에서 선물거래란 특정 기간 동안 미리 약정된 가격으로 주식을 매매하는 거래 방식을 의미합니다. 즉, 주식 선물거래는 약정된 가격으로 미래의 특정 시점에 주식을 사거나 팔 수 있는 계약을 말합니다. 이러한 선물거래는 투자자들이 주식 가격의 변동성으로부터 보호받을 수 있고, 투자 전략을 구성할 때 사용할 수 있는 도구로 이용될 수 있습니다. 주식 선물거래는 주식시장의 투기성과 리스크를 관리하고자 하는 투자자들에게 유용한 도구로 활용됩니다.",
"time": "2023-10-19 00:56:53"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ private Comment findExistingCommentById(Long postId, Long commentId) {
Post existPost = postRepository.findPostById(postId)
.orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_EXISTED));
return commentRepository.findCommentByIdAndPost(commentId, existPost)
.orElseThrow(() -> new CustomException(ErrorCode.INTERNAL_SERVER_ERROR));
.orElseThrow(() -> new CustomException(ErrorCode.COMMENT_NOT_EXISTED));
}

private void validateUserHasAuthority(String requestNickname, Comment existingComment) {
Expand Down
Loading

0 comments on commit dc60288

Please sign in to comment.