From 5abede3e3d82638d7bab99d202d15899f52e52ae Mon Sep 17 00:00:00 2001 From: kms Date: Thu, 9 Nov 2023 10:41:15 +0900 Subject: [PATCH 1/4] :hammer: fix: add set properties in updateFile #40 --- src/main/java/com/smart/watchboard/service/FileService.java | 4 ++++ .../java/com/smart/watchboard/service/RequestService.java | 5 +---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/smart/watchboard/service/FileService.java b/src/main/java/com/smart/watchboard/service/FileService.java index a6e5673..bd6fe6f 100644 --- a/src/main/java/com/smart/watchboard/service/FileService.java +++ b/src/main/java/com/smart/watchboard/service/FileService.java @@ -52,13 +52,17 @@ public void createFile(FileDto fileDto) { public void updateFile(FileDto fileDto) { Document document = whiteboardService.findDoc(fileDto.getDocumentId()); File updatedFile = fileRepository.findByDocument(document); + String key = fileDto.getFileType() + "/" + fileDto.getDocumentId() + "." + fileDto.getFile().getOriginalFilename(); //Optional file = findFile(fileDto.getFileId()); //File updatedFile = file.get(); updatedFile.setFileName(fileDto.getFile().getOriginalFilename()); + updatedFile.setObjectKey(key); updatedFile.setPath(fileDto.getPath()); + updatedFile.setFileType(fileDto.getFileType()); updatedFile.setSize(fileDto.getFile().getSize()); updatedFile.setCreatedAt(Instant.now()); updatedFile.setModifiedAt(Instant.now()); + updatedFile.setDocument(document); fileRepository.save(updatedFile); } diff --git a/src/main/java/com/smart/watchboard/service/RequestService.java b/src/main/java/com/smart/watchboard/service/RequestService.java index cb3f322..b18779c 100644 --- a/src/main/java/com/smart/watchboard/service/RequestService.java +++ b/src/main/java/com/smart/watchboard/service/RequestService.java @@ -7,10 +7,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; +import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; From 261490b0aac7224d1a519647a28a9b74ebe3ec5c Mon Sep 17 00:00:00 2001 From: kms Date: Thu, 9 Nov 2023 10:54:20 +0900 Subject: [PATCH 2/4] :sparkles: feat: add api to subscribe sse #40 --- .../controller/GraphController.java | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/smart/watchboard/controller/GraphController.java b/src/main/java/com/smart/watchboard/controller/GraphController.java index 3de504d..a7ad299 100644 --- a/src/main/java/com/smart/watchboard/controller/GraphController.java +++ b/src/main/java/com/smart/watchboard/controller/GraphController.java @@ -1,6 +1,7 @@ package com.smart.watchboard.controller; import com.fasterxml.jackson.core.JsonProcessingException; +import com.smart.watchboard.dto.KeywordsBodyDto; import com.smart.watchboard.dto.KeywordsDto; import com.smart.watchboard.dto.MindmapDto; import com.smart.watchboard.service.*; @@ -9,8 +10,10 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import javax.sound.sampled.UnsupportedAudioFileException; import java.io.IOException; @@ -27,20 +30,22 @@ public class GraphController { private final RequestService requestService; private final FileService fileService; private final WhiteboardService whiteboardService; + private final SseService sseService; @PostMapping("/graph/{documentID}") @Operation(summary = "마인드맵 생성", description = "ai 서버에 마인드맵 요청한다.") - public ResponseEntity createMindmap(@PathVariable(value = "documentID") long documentId, @RequestBody List keywords, @RequestHeader("Authorization") String accessToken) throws JsonProcessingException { - if (whiteboardService.isPdfType(documentId)) { - String path = fileService.getPdfUrl(documentId); - ResponseEntity body = requestService.requestPdfMindmap(path, documentId, keywords); - return new ResponseEntity<>(body, HttpStatus.OK); - } else if (whiteboardService.isAudioType(documentId)) { - String text = lectureNoteService.getText(documentId); - ResponseEntity body = requestService.requestSTTMindmap(text, documentId, keywords); - return new ResponseEntity<>(body, HttpStatus.OK); - } - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + public void createMindmap(@PathVariable(value = "documentID") long documentId, @RequestBody KeywordsBodyDto keywordsBodyDto, @RequestHeader("Authorization") String accessToken) throws JsonProcessingException { + sseService.notify(documentId, keywordsBodyDto.getKeywords()); +// if (whiteboardService.isPdfType(documentId)) { +// String path = fileService.getPdfUrl(documentId); +// ResponseEntity body = requestService.requestPdfMindmap(path, documentId, keywords); +// return new ResponseEntity<>(body, HttpStatus.OK); +// } else if (whiteboardService.isAudioType(documentId)) { +// String text = lectureNoteService.getText(documentId); +// ResponseEntity body = requestService.requestSTTMindmap(text, documentId, keywords); +// return new ResponseEntity<>(body, HttpStatus.OK); +// } +// return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } @GetMapping("/documents/{documentID}/mindmap") @@ -68,6 +73,16 @@ public ResponseEntity getAnswer(@PathVariable(value = "documentID") long docu return new ResponseEntity<>(responseEntity, HttpStatus.OK); } + @GetMapping(value = "/subscribe/{documentID}", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public SseEmitter subscribe(@PathVariable(value = "documentID") long documentId) { + return sseService.subscribe(documentId); + } + + @PostMapping("/send-data/{documentID}") + public void sendData(@PathVariable(value = "documentID") long documentId, @RequestBody KeywordsBodyDto keywordsBodyDto) { + sseService.notify(documentId, keywordsBodyDto.getKeywords()); + } + @PostMapping("/abc/{documentID}") public ResponseEntity test(@PathVariable(value = "documentID") long documentId) throws UnsupportedAudioFileException, IOException { System.out.println(documentId); From 9e71a42b108dcc0bf2fc642dcbd6bd6337bfdaab Mon Sep 17 00:00:00 2001 From: kms Date: Thu, 9 Nov 2023 10:54:49 +0900 Subject: [PATCH 3/4] :sparkles: feat: create sse repository #40 --- .../repository/EmitterRepository.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/main/java/com/smart/watchboard/repository/EmitterRepository.java diff --git a/src/main/java/com/smart/watchboard/repository/EmitterRepository.java b/src/main/java/com/smart/watchboard/repository/EmitterRepository.java new file mode 100644 index 0000000..c98bffd --- /dev/null +++ b/src/main/java/com/smart/watchboard/repository/EmitterRepository.java @@ -0,0 +1,26 @@ +package com.smart.watchboard.repository; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Repository +@RequiredArgsConstructor +public class EmitterRepository { + private final Map emitters = new ConcurrentHashMap<>(); + + public void save(Long id, SseEmitter emitter) { + emitters.put(id, emitter); + } + + public void deleteById(Long id) { + emitters.remove(id); + } + + public SseEmitter get(Long id) { + return emitters.get(id); + } +} From 2823a29c351be2c9e5005dc51446fda76ba74244 Mon Sep 17 00:00:00 2001 From: kms Date: Thu, 9 Nov 2023 10:55:20 +0900 Subject: [PATCH 4/4] :sparkles: feat: create sse service #40 --- .../smart/watchboard/service/SseService.java | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/main/java/com/smart/watchboard/service/SseService.java diff --git a/src/main/java/com/smart/watchboard/service/SseService.java b/src/main/java/com/smart/watchboard/service/SseService.java new file mode 100644 index 0000000..42b9c51 --- /dev/null +++ b/src/main/java/com/smart/watchboard/service/SseService.java @@ -0,0 +1,77 @@ +package com.smart.watchboard.service; + +import com.smart.watchboard.repository.EmitterRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class SseService { + private static final Long DEFAULT_TIMEOUT = 60L * 1000 * 60; + private final EmitterRepository emitterRepository; + private final FileService fileService; + private final WhiteboardService whiteboardService; + private final RequestService requestService; + private final LectureNoteService lectureNoteService; + private final STTService sttService; + + public SseEmitter subscribe(Long documentId) { + SseEmitter emitter = createEmitter(documentId); + + sendToClientFirst(documentId, "EventStream Created. [documentId=" + documentId + "]"); + return emitter; + } + + public void notify(Long documentId, List keywords) { + sendToClient(documentId, keywords); + } + + private void sendToClientFirst(Long documentId, Object data) { + SseEmitter emitter = emitterRepository.get(documentId); + if (emitter != null) { + try { + emitter.send(SseEmitter.event().id(String.valueOf(documentId)).name("sse").data(data)); + } catch (IOException exception) { + emitterRepository.deleteById(documentId); + emitter.completeWithError(exception); + } + } + } + + private void sendToClient(Long documentId, List keywords) { + SseEmitter emitter = emitterRepository.get(documentId); + if (emitter != null) { + try { + if (whiteboardService.isPdfType(documentId)) { + String path = fileService.getPdfUrl(documentId); + ResponseEntity body = requestService.requestPdfMindmap(path, documentId, keywords); + emitter.send(SseEmitter.event().id(String.valueOf(documentId)).name("sse").data(body.getBody())); + } else if (whiteboardService.isAudioType(documentId)) { + String text = lectureNoteService.getText(documentId); + ResponseEntity body = requestService.requestSTTMindmap(text, documentId, keywords); + emitter.send(SseEmitter.event().id(String.valueOf(documentId)).name("sse").data(body.getBody())); + } + } catch (IOException exception) { + emitterRepository.deleteById(documentId); + emitter.completeWithError(exception); + } + } + } + + private SseEmitter createEmitter(Long documentId) { + SseEmitter emitter = new SseEmitter(DEFAULT_TIMEOUT); + emitterRepository.save(documentId, emitter); + + // Emitter가 완료될 때(모든 데이터가 성공적으로 전송된 상태) Emitter를 삭제한다. + emitter.onCompletion(() -> emitterRepository.deleteById(documentId)); + // Emitter가 타임아웃 되었을 때(지정된 시간동안 어떠한 이벤트도 전송되지 않았을 때) Emitter를 삭제한다. + emitter.onTimeout(() -> emitterRepository.deleteById(documentId)); + + return emitter; + } +}