From 6383e6a3d9daeff8bc9c71394f51b3be2cfcc713 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Wed, 17 Apr 2024 15:51:08 +0900 Subject: [PATCH 01/29] =?UTF-8?q?[feat]=20s3=20config=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/comma/global/config/S3Config.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/main/java/com/example/comma/global/config/S3Config.java diff --git a/src/main/java/com/example/comma/global/config/S3Config.java b/src/main/java/com/example/comma/global/config/S3Config.java new file mode 100644 index 0000000..d4c6d67 --- /dev/null +++ b/src/main/java/com/example/comma/global/config/S3Config.java @@ -0,0 +1,33 @@ +package com.example.comma.global.config; + +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class S3Config { + @Value("${cloud.aws.credentials.access-key}") + private String accessKey; + + @Value("${cloud.aws.credentials.secret-key}") + private String secretKey; + + @Value("${cloud.aws.region.static}") + private String region; + + @Bean + public AmazonS3 amazonS3Client() { + AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey); + + return AmazonS3ClientBuilder + .standard() + .withCredentials(new AWSStaticCredentialsProvider(credentials)) + .withRegion(region) + .build(); + } +} \ No newline at end of file From 0c5e93ec48c424a5b5b099c509a805f537f10447 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Wed, 17 Apr 2024 15:51:27 +0900 Subject: [PATCH 02/29] =?UTF-8?q?[feat]=20jsoup=20aws=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index b0c003d..cb8b2d3 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' + // lombok compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' @@ -41,7 +42,6 @@ dependencies { implementation group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.2' implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2' - //oauth2 implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' @@ -55,7 +55,11 @@ dependencies { //implementation 'org.springframework.boot:spring-boot-starter-data-redis' // aws - //implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.4.1' + implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.4.1' + + //jsoup + implementation 'org.jsoup:jsoup:1.15.3' + } tasks.named('test') { From 06b365fe0c8b16760745229303395b3919e10bfa Mon Sep 17 00:00:00 2001 From: hysong4u Date: Wed, 17 Apr 2024 15:51:41 +0900 Subject: [PATCH 03/29] =?UTF-8?q?[feat]=20=EC=88=98=ED=99=94=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=ED=81=AC=EB=A1=A4=EB=A7=81=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/card/controller/CardController.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/com/example/comma/domain/card/controller/CardController.java b/src/main/java/com/example/comma/domain/card/controller/CardController.java index dea7b2e..995a7ef 100644 --- a/src/main/java/com/example/comma/domain/card/controller/CardController.java +++ b/src/main/java/com/example/comma/domain/card/controller/CardController.java @@ -2,12 +2,14 @@ import com.example.comma.domain.card.dto.response.*; import com.example.comma.domain.card.service.CardService; +import com.example.comma.domain.external.ImageCrawler; import com.example.comma.global.common.SuccessResponse; import com.example.comma.global.config.auth.UserId; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -16,6 +18,19 @@ @RestController public class CardController { private final CardService cardService; + private final ImageCrawler imageCrawler; + + + @GetMapping("/search") + public ResponseEntity> getCardList(@RequestParam(name = "searchWord") String searchWord) throws IOException { + List signImageUrls = imageCrawler.crawlImageUrls(searchWord); + byte[] mergeImages = imageCrawler.mergeImages(signImageUrls); + String url = imageCrawler.uploadFile(mergeImages, "merged_image.jpg"); + + return SuccessResponse.ok(url); + } + + @GetMapping("/{name}") public ResponseEntity> getWord(@PathVariable(name = "name") String name) { From f04be3f3b79a5263c82f0980fe5060b6dab2639c Mon Sep 17 00:00:00 2001 From: hysong4u Date: Wed, 17 Apr 2024 15:51:57 +0900 Subject: [PATCH 04/29] =?UTF-8?q?[feat]=20=EC=88=98=ED=99=94=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=ED=81=AC=EB=A1=A4=EB=A7=81=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comma/domain/external/ImageCrawler.java | 158 ++++++++++++++++++ .../global/config/auth/SecurityConfig.java | 2 + 2 files changed, 160 insertions(+) create mode 100644 src/main/java/com/example/comma/domain/external/ImageCrawler.java diff --git a/src/main/java/com/example/comma/domain/external/ImageCrawler.java b/src/main/java/com/example/comma/domain/external/ImageCrawler.java new file mode 100644 index 0000000..2257808 --- /dev/null +++ b/src/main/java/com/example/comma/domain/external/ImageCrawler.java @@ -0,0 +1,158 @@ +package com.example.comma.domain.external; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; +import com.example.comma.global.error.exception.EntityNotFoundException; +import lombok.RequiredArgsConstructor; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import org.springframework.beans.factory.annotation.Value; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.*; +import java.net.URL; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.example.comma.global.error.ErrorCode.SIGNLANGUAGE_NOT_FOUND; + +@RequiredArgsConstructor +@Service +@Transactional +public class ImageCrawler { + + private final AmazonS3 amazonS3; + + @Value("${cloud.aws.s3.bucket}") + private String bucketName; + + public List crawlImageUrls(String searchWord) { + try { + // 검색 페이지 URL + String searchUrl = "https://sldict.korean.go.kr/front/search/searchAllList.do?searchKeyword=" + searchWord; + + // 검색 페이지에서 첫 번째 결과 페이지로 이동 + Document doc = Jsoup.connect(searchUrl).get(); + Elements spanElements = doc.select("span[class=tit]"); + Element spanElement = spanElements.first(); + assert spanElement != null; + Element aElement = spanElement.selectFirst("a"); + assert aElement != null; + + String aElementText = aElement.text().trim(); + + if (!searchWord.equals(aElementText)) { + throw new EntityNotFoundException(SIGNLANGUAGE_NOT_FOUND); + } + + String href = aElement.attr("href"); + + // fnSearchContentsView 인자 추출 + String[] argsArray = href.split("'"); + String originNo = argsArray[1]; + String topCategory = argsArray[2]; + + // URL 생성 + String url = "https://sldict.korean.go.kr/front/sign/signContentsView.do" + + "?origin_no=" + originNo + + "&top_category=" + topCategory + + "&category=" + + "&searchKeyword=" + URLEncoder.encode(searchWord, "UTF-8") + + "&searchCondition=" + + "&search_gubun=" + + "&museum_type=00" + + "¤t_pos_index=0"; + + // 상세 페이지로 이동하여 이미지 URL 추출 + Document detailDoc = Jsoup.connect(url).get(); + Elements imgElements = detailDoc.select("img[alt=수어동작 이미지]"); + + List imageUrls = new ArrayList<>(); + + for (Element imgElement : imgElements) { + String imageUrl = imgElement.attr("src"); + if (imageUrl.startsWith("http://")) { + imageUrl = imageUrl.replace("http://", "https://"); + } + imageUrls.add(imageUrl); + } + + return imageUrls; + + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + + //이미지 S3 업로드 + public String uploadFile(byte[] fileData, String fileName) throws IOException { + String directory = "mergedImg/"; + String contentType = "image/jpg"; + + ByteArrayInputStream inputStream = new ByteArrayInputStream(fileData); + + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentType(contentType); + metadata.setContentLength(fileData.length); + + amazonS3.putObject(new PutObjectRequest(bucketName, directory + fileName, inputStream, metadata)); + + return "https://" + bucketName + ".s3.amazonaws.com/" + directory + fileName; + } + + + public static byte[] mergeImages(List imageUrls) throws IOException { + BufferedImage[] images = new BufferedImage[imageUrls.size()]; + int totalWidth = 0; + int maxHeight = 0; + + for (int i = 0; i < imageUrls.size(); i++) { + URL imageUrl = new URL(imageUrls.get(i)); + BufferedImage image = ImageIO.read(imageUrl); + images[i] = image; + totalWidth += image.getWidth(); + maxHeight = Math.max(maxHeight, image.getHeight()); + } + + BufferedImage mergedImage = new BufferedImage(totalWidth, maxHeight, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = mergedImage.createGraphics(); + + int x = 0; + for (BufferedImage image : images) { + g2d.drawImage(image, x, 0, null); + x += image.getWidth(); + } + g2d.dispose(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(mergedImage, "jpg", baos); + baos.flush(); + byte[] bytes = baos.toByteArray(); + baos.close(); + + return bytes; + } + + + + + +} + + + + diff --git a/src/main/java/com/example/comma/global/config/auth/SecurityConfig.java b/src/main/java/com/example/comma/global/config/auth/SecurityConfig.java index 4e0a891..1084adc 100644 --- a/src/main/java/com/example/comma/global/config/auth/SecurityConfig.java +++ b/src/main/java/com/example/comma/global/config/auth/SecurityConfig.java @@ -27,6 +27,8 @@ public class SecurityConfig { private static final String[] whiteList = {"/", "api/user/token/**", "login/oauth2/code/google/**", + "/api/card/search/**", + "upload/**", }; @Bean From 1ad546ed4013d0a8b7ca172c64a296f5b3ab53d8 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Wed, 17 Apr 2024 15:52:04 +0900 Subject: [PATCH 05/29] =?UTF-8?q?[feat]=20=EC=88=98=ED=99=94=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=ED=81=AC=EB=A1=A4=EB=A7=81=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=EC=B2=98=EB=A6=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/comma/global/error/ErrorCode.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/example/comma/global/error/ErrorCode.java b/src/main/java/com/example/comma/global/error/ErrorCode.java index 0114201..82904b7 100644 --- a/src/main/java/com/example/comma/global/error/ErrorCode.java +++ b/src/main/java/com/example/comma/global/error/ErrorCode.java @@ -37,6 +37,7 @@ public enum ErrorCode { USER_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 유저를 찾을 수 없습니다."), USER_CARD_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 유저가 등록한 카드를 찾을 수 없습니다."), FAIRYTALE_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 동화를 찾을 수 없습니다."), + SIGNLANGUAGE_NOT_FOUND(HttpStatus.NOT_FOUND, "일치하는 수화 카드를 찾을 수 없습니다."), /** * 405 Method Not Allowed */ From 9437828d6adbf75265a4bd74b59e6ce15c086867 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Fri, 19 Apr 2024 00:35:12 +0900 Subject: [PATCH 06/29] =?UTF-8?q?[feat]=20gemini=20api=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../card/controller/CardController.java | 3 +- .../service/controller/GeminniController.java | 88 +++++++++++++++++++ .../dto/request/request/GeminiRequest.java | 9 ++ .../service/dto/response/GeminiResponse.java | 9 ++ .../{ => service/service}/ImageCrawler.java | 4 +- .../global/config/auth/SecurityConfig.java | 4 +- 6 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/example/comma/domain/external/service/controller/GeminniController.java create mode 100644 src/main/java/com/example/comma/domain/external/service/dto/request/request/GeminiRequest.java create mode 100644 src/main/java/com/example/comma/domain/external/service/dto/response/GeminiResponse.java rename src/main/java/com/example/comma/domain/external/{ => service/service}/ImageCrawler.java (97%) diff --git a/src/main/java/com/example/comma/domain/card/controller/CardController.java b/src/main/java/com/example/comma/domain/card/controller/CardController.java index 995a7ef..f3421b7 100644 --- a/src/main/java/com/example/comma/domain/card/controller/CardController.java +++ b/src/main/java/com/example/comma/domain/card/controller/CardController.java @@ -2,7 +2,7 @@ import com.example.comma.domain.card.dto.response.*; import com.example.comma.domain.card.service.CardService; -import com.example.comma.domain.external.ImageCrawler; +import com.example.comma.domain.external.service.service.ImageCrawler; import com.example.comma.global.common.SuccessResponse; import com.example.comma.global.config.auth.UserId; import lombok.RequiredArgsConstructor; @@ -10,7 +10,6 @@ import org.springframework.web.bind.annotation.*; import java.io.IOException; -import java.util.ArrayList; import java.util.List; @RequiredArgsConstructor diff --git a/src/main/java/com/example/comma/domain/external/service/controller/GeminniController.java b/src/main/java/com/example/comma/domain/external/service/controller/GeminniController.java new file mode 100644 index 0000000..11a5e3b --- /dev/null +++ b/src/main/java/com/example/comma/domain/external/service/controller/GeminniController.java @@ -0,0 +1,88 @@ +package com.example.comma.domain.external.service.controller; + +import com.example.comma.domain.external.service.service.ImageCrawler; +import com.example.comma.global.common.SuccessResponse; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import org.springframework.http.*; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.io.IOException; +import java.util.Base64; + +@RequiredArgsConstructor +@RestController +public class GeminniController { + + private final ImageCrawler imageCrawler; + private static final String API_ENDPOINT_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent"; + private static final String API_KEY = "AIzaSyCgt_fuEZ2fU4z_t1KoHaNIxur_ML7kjgY"; + + private final RestTemplate restTemplate = new RestTemplate(); + private final ObjectMapper objectMapper = new ObjectMapper(); + + @PostMapping("/geminni") + public ResponseEntity generateImage(@RequestParam(name = "text") String text) { + String requestBody = "{\"contents\": [{\"parts\": [{\"text\": \"" + text + "\"}]}]}"; + + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(API_ENDPOINT_URL) + .queryParam("key", API_KEY); + + ResponseEntity responseEntity = restTemplate.postForEntity(builder.toUriString(), requestBody, String.class); + + if (responseEntity.getStatusCode() == HttpStatus.OK) { + String responseBody = responseEntity.getBody(); + String imageBase64 = extractImage(responseBody); + + if (!StringUtils.isEmpty(imageBase64)) { + byte[] imageBytes = Base64.getDecoder().decode(imageBase64); + return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG).body(imageBytes); + } + } + + return ResponseEntity.notFound().build(); + } + + private String extractImage(String responseBody) { + try { + JsonNode rootNode = objectMapper.readTree(responseBody); + JsonNode candidatesNode = rootNode.get("candidates"); + + if (candidatesNode.isArray()) { + for (JsonNode candidateNode : candidatesNode) { + JsonNode contentNode = candidateNode.get("content"); + JsonNode partsNode = contentNode.get("parts"); + + if (partsNode.isArray()) { + for (JsonNode partNode : partsNode) { + String partText = partNode.get("text").asText(); + if (isImage(partText)) { + return partText; + } + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); // 오류 발생 시 로그에 출력 + } + return null; // 이미지가 발견되지 않았을 경우 null 반환 + } + + + private boolean isImage(String text) { + try { + Base64.getDecoder().decode(text); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + +} diff --git a/src/main/java/com/example/comma/domain/external/service/dto/request/request/GeminiRequest.java b/src/main/java/com/example/comma/domain/external/service/dto/request/request/GeminiRequest.java new file mode 100644 index 0000000..6a0a778 --- /dev/null +++ b/src/main/java/com/example/comma/domain/external/service/dto/request/request/GeminiRequest.java @@ -0,0 +1,9 @@ +package com.example.comma.domain.external.service.dto.request.request; + +public class GeminiRequest { + private String text; + + public GeminiRequest(String text) { + this.text = text; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/comma/domain/external/service/dto/response/GeminiResponse.java b/src/main/java/com/example/comma/domain/external/service/dto/response/GeminiResponse.java new file mode 100644 index 0000000..dbd3118 --- /dev/null +++ b/src/main/java/com/example/comma/domain/external/service/dto/response/GeminiResponse.java @@ -0,0 +1,9 @@ +package com.example.comma.domain.external.service.dto.response; + +import lombok.Data; + +@Data +public class GeminiResponse { + private boolean success; + private byte[] imageData; +} diff --git a/src/main/java/com/example/comma/domain/external/ImageCrawler.java b/src/main/java/com/example/comma/domain/external/service/service/ImageCrawler.java similarity index 97% rename from src/main/java/com/example/comma/domain/external/ImageCrawler.java rename to src/main/java/com/example/comma/domain/external/service/service/ImageCrawler.java index 2257808..138d4ed 100644 --- a/src/main/java/com/example/comma/domain/external/ImageCrawler.java +++ b/src/main/java/com/example/comma/domain/external/service/service/ImageCrawler.java @@ -1,4 +1,4 @@ -package com.example.comma.domain.external; +package com.example.comma.domain.external.service.service; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.ObjectMetadata; @@ -53,7 +53,7 @@ public List crawlImageUrls(String searchWord) { String aElementText = aElement.text().trim(); - if (!searchWord.equals(aElementText)) { + if (!aElementText.contains(searchWord)) { throw new EntityNotFoundException(SIGNLANGUAGE_NOT_FOUND); } diff --git a/src/main/java/com/example/comma/global/config/auth/SecurityConfig.java b/src/main/java/com/example/comma/global/config/auth/SecurityConfig.java index 1084adc..4121548 100644 --- a/src/main/java/com/example/comma/global/config/auth/SecurityConfig.java +++ b/src/main/java/com/example/comma/global/config/auth/SecurityConfig.java @@ -27,8 +27,8 @@ public class SecurityConfig { private static final String[] whiteList = {"/", "api/user/token/**", "login/oauth2/code/google/**", - "/api/card/search/**", - "upload/**", + "/api/card/search/**", //임시 + "/geminni" //임시 }; @Bean From 016d7b589bd105bdf150ce1f62081d99f1f8e7ed Mon Sep 17 00:00:00 2001 From: hysong4u Date: Sat, 20 Apr 2024 02:18:02 +0900 Subject: [PATCH 07/29] =?UTF-8?q?[feat]=20=EC=9D=98=EC=A1=B4=EC=84=B1=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index cb8b2d3..605a534 100644 --- a/build.gradle +++ b/build.gradle @@ -60,6 +60,8 @@ dependencies { //jsoup implementation 'org.jsoup:jsoup:1.15.3' + implementation 'io.github.flashvayne:chatgpt-spring-boot-starter:1.0.4' + } tasks.named('test') { From 04e6badbdf6bf09ba326bce632b5379ded843dd1 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Sat, 20 Apr 2024 02:19:32 +0900 Subject: [PATCH 08/29] =?UTF-8?q?[feat]=20=EC=88=98=ED=99=94=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=20=EA=B2=80=EC=83=89=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../card/controller/CardController.java | 9 +++++-- .../dto/response/SearchListResponseDto.java | 6 +++++ .../service/service/ImageCrawler.java | 26 ++++++++++++++----- .../global/config/auth/SecurityConfig.java | 3 ++- 4 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/example/comma/domain/card/dto/response/SearchListResponseDto.java diff --git a/src/main/java/com/example/comma/domain/card/controller/CardController.java b/src/main/java/com/example/comma/domain/card/controller/CardController.java index f3421b7..dc592f1 100644 --- a/src/main/java/com/example/comma/domain/card/controller/CardController.java +++ b/src/main/java/com/example/comma/domain/card/controller/CardController.java @@ -11,6 +11,7 @@ import java.io.IOException; import java.util.List; +import java.util.Map; @RequiredArgsConstructor @RequestMapping("/api/card") @@ -21,6 +22,12 @@ public class CardController { @GetMapping("/search") + public ResponseEntity> getSearchList(@RequestParam(name = "searchWord") String searchWord) { + List searchResults = imageCrawler.crawlSearchList(searchWord); + return SuccessResponse.ok(searchResults); + } + + @GetMapping("/image") public ResponseEntity> getCardList(@RequestParam(name = "searchWord") String searchWord) throws IOException { List signImageUrls = imageCrawler.crawlImageUrls(searchWord); byte[] mergeImages = imageCrawler.mergeImages(signImageUrls); @@ -29,8 +36,6 @@ public ResponseEntity> getCardList(@RequestParam(name = "sear return SuccessResponse.ok(url); } - - @GetMapping("/{name}") public ResponseEntity> getWord(@PathVariable(name = "name") String name) { CardImageResponseDto CardImage = cardService.getCardImage(name); diff --git a/src/main/java/com/example/comma/domain/card/dto/response/SearchListResponseDto.java b/src/main/java/com/example/comma/domain/card/dto/response/SearchListResponseDto.java new file mode 100644 index 0000000..a7e6586 --- /dev/null +++ b/src/main/java/com/example/comma/domain/card/dto/response/SearchListResponseDto.java @@ -0,0 +1,6 @@ +package com.example.comma.domain.card.dto.response; + +public record SearchListResponseDto( + String word +) { +} diff --git a/src/main/java/com/example/comma/domain/external/service/service/ImageCrawler.java b/src/main/java/com/example/comma/domain/external/service/service/ImageCrawler.java index 138d4ed..6a16c58 100644 --- a/src/main/java/com/example/comma/domain/external/service/service/ImageCrawler.java +++ b/src/main/java/com/example/comma/domain/external/service/service/ImageCrawler.java @@ -3,8 +3,10 @@ import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.PutObjectRequest; +import com.example.comma.domain.card.dto.response.SearchListResponseDto; import com.example.comma.global.error.exception.EntityNotFoundException; import lombok.RequiredArgsConstructor; +import org.jsoup.Connection; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; @@ -22,8 +24,7 @@ import java.io.*; import java.net.URL; import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.Arrays; +import java.util.*; import java.util.List; import static com.example.comma.global.error.ErrorCode.SIGNLANGUAGE_NOT_FOUND; @@ -97,7 +98,6 @@ public List crawlImageUrls(String searchWord) { } - //이미지 S3 업로드 public String uploadFile(byte[] fileData, String fileName) throws IOException { String directory = "mergedImg/"; @@ -148,11 +148,25 @@ public static byte[] mergeImages(List imageUrls) throws IOException { } - - - + public List crawlSearchList(String searchWord) { + List searchResults = new ArrayList<>(); + try { + String searchUrl = "https://sldict.korean.go.kr/front/search/searchAllList.do?searchKeyword=" + searchWord; + Document doc = Jsoup.connect(searchUrl).get(); + Elements aElements = doc.select("span[class=tit] a"); + for (Element aElement : aElements) { + String text = aElement.text(); + SearchListResponseDto result = new SearchListResponseDto(text); + searchResults.add(result); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + return searchResults; + } } + diff --git a/src/main/java/com/example/comma/global/config/auth/SecurityConfig.java b/src/main/java/com/example/comma/global/config/auth/SecurityConfig.java index 4121548..0aa85de 100644 --- a/src/main/java/com/example/comma/global/config/auth/SecurityConfig.java +++ b/src/main/java/com/example/comma/global/config/auth/SecurityConfig.java @@ -28,7 +28,8 @@ public class SecurityConfig { "api/user/token/**", "login/oauth2/code/google/**", "/api/card/search/**", //임시 - "/geminni" //임시 + "/gemini" ,//임시 + "/generate-image" //임시 }; @Bean From aee5ba2f7869c765fb2c5c27007c883c0272887f Mon Sep 17 00:00:00 2001 From: hysong4u Date: Sat, 20 Apr 2024 02:22:14 +0900 Subject: [PATCH 09/29] =?UTF-8?q?[fix]=20=EC=83=9D=EC=84=B1=ED=98=95=20ai?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/controller/DalleController.java | 40 +++++++++++++++++++ ...iController.java => GeminiController.java} | 20 +++++----- 2 files changed, 49 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/example/comma/domain/external/service/controller/DalleController.java rename src/main/java/com/example/comma/domain/external/service/controller/{GeminniController.java => GeminiController.java} (87%) diff --git a/src/main/java/com/example/comma/domain/external/service/controller/DalleController.java b/src/main/java/com/example/comma/domain/external/service/controller/DalleController.java new file mode 100644 index 0000000..4664560 --- /dev/null +++ b/src/main/java/com/example/comma/domain/external/service/controller/DalleController.java @@ -0,0 +1,40 @@ +package com.example.comma.domain.external.service.controller; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.*; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; + +@RestController +public class DalleController { + + @Value("${dalle.api.key}") + private String apiKey; + + private final RestTemplate restTemplate = new RestTemplate(); + + @PostMapping("/dalle") + public ResponseEntity generateImage(@RequestParam("text") String text) { + // DALL-E API 엔드포인트 + String apiUrl = "https://api.openai.com/v1/davinci/generate"; + + // API 헤더 설정 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Authorization", "Bearer " + apiKey); + + // API 요청 바디 생성 + String requestBody = "{\"prompt\": \"" + text + "\", \"max_tokens\": 50}"; + + // API 요청 보내기 + HttpEntity requestEntity = new HttpEntity<>(requestBody, headers); + ResponseEntity responseEntity = restTemplate.exchange(apiUrl, HttpMethod.POST, requestEntity, byte[].class); + + // API 응답 반환 + return ResponseEntity.status(responseEntity.getStatusCode()).contentType(MediaType.IMAGE_JPEG).body(responseEntity.getBody()); + } +} diff --git a/src/main/java/com/example/comma/domain/external/service/controller/GeminniController.java b/src/main/java/com/example/comma/domain/external/service/controller/GeminiController.java similarity index 87% rename from src/main/java/com/example/comma/domain/external/service/controller/GeminniController.java rename to src/main/java/com/example/comma/domain/external/service/controller/GeminiController.java index 11a5e3b..5d0a556 100644 --- a/src/main/java/com/example/comma/domain/external/service/controller/GeminniController.java +++ b/src/main/java/com/example/comma/domain/external/service/controller/GeminiController.java @@ -5,29 +5,29 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; -import net.minidev.json.JSONArray; -import net.minidev.json.JSONObject; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; -import java.io.IOException; import java.util.Base64; @RequiredArgsConstructor @RestController -public class GeminniController { +public class GeminiController { private final ImageCrawler imageCrawler; private static final String API_ENDPOINT_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent"; - private static final String API_KEY = "AIzaSyCgt_fuEZ2fU4z_t1KoHaNIxur_ML7kjgY"; + + @Value("${gemini.api.key}") + private String API_KEY; private final RestTemplate restTemplate = new RestTemplate(); private final ObjectMapper objectMapper = new ObjectMapper(); - @PostMapping("/geminni") + @GetMapping("/gemini") public ResponseEntity generateImage(@RequestParam(name = "text") String text) { String requestBody = "{\"contents\": [{\"parts\": [{\"text\": \"" + text + "\"}]}]}"; @@ -70,19 +70,17 @@ private String extractImage(String responseBody) { } } } catch (Exception e) { - e.printStackTrace(); // 오류 발생 시 로그에 출력 + e.printStackTrace(); } - return null; // 이미지가 발견되지 않았을 경우 null 반환 + return null; } - private boolean isImage(String text) { try { - Base64.getDecoder().decode(text); + Base64.getDecoder().decode(text); return true; } catch (IllegalArgumentException e) { return false; } } - } From 54b418e4321850d60b11a7594c356d958eefe409 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Sat, 27 Apr 2024 21:20:10 +0900 Subject: [PATCH 10/29] =?UTF-8?q?[feat]=20=EC=9D=98=EC=A1=B4=EC=84=B1=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ build.gradle | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index b3db3e2..b63174a 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,5 @@ out/ .vscode/ application.yml +comma_firebase_key.json + diff --git a/build.gradle b/build.gradle index 605a534..3589a3a 100644 --- a/build.gradle +++ b/build.gradle @@ -60,8 +60,12 @@ dependencies { //jsoup implementation 'org.jsoup:jsoup:1.15.3' + //chatgpt implementation 'io.github.flashvayne:chatgpt-spring-boot-starter:1.0.4' + //firebase + implementation 'com.google.firebase:firebase-admin:9.2.0' + implementation 'com.squareup.okhttp3:okhttp:4.11.0' } tasks.named('test') { From 86763422959d5828786d097f753e3cdcf4a95ef7 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Sat, 27 Apr 2024 21:21:33 +0900 Subject: [PATCH 11/29] =?UTF-8?q?[feat]=20DALLE=20=EB=8B=A8=EC=96=B4?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../card/controller/CardController.java | 12 ++--- .../service/{service => }/ImageCrawler.java | 54 ++++++++++++++++--- .../service/controller/DalleController.java | 40 -------------- 3 files changed, 53 insertions(+), 53 deletions(-) rename src/main/java/com/example/comma/domain/external/service/{service => }/ImageCrawler.java (75%) delete mode 100644 src/main/java/com/example/comma/domain/external/service/controller/DalleController.java diff --git a/src/main/java/com/example/comma/domain/card/controller/CardController.java b/src/main/java/com/example/comma/domain/card/controller/CardController.java index dc592f1..63cc8e8 100644 --- a/src/main/java/com/example/comma/domain/card/controller/CardController.java +++ b/src/main/java/com/example/comma/domain/card/controller/CardController.java @@ -2,7 +2,7 @@ import com.example.comma.domain.card.dto.response.*; import com.example.comma.domain.card.service.CardService; -import com.example.comma.domain.external.service.service.ImageCrawler; +import com.example.comma.domain.external.service.ImageCrawler; import com.example.comma.global.common.SuccessResponse; import com.example.comma.global.config.auth.UserId; import lombok.RequiredArgsConstructor; @@ -11,7 +11,6 @@ import java.io.IOException; import java.util.List; -import java.util.Map; @RequiredArgsConstructor @RequestMapping("/api/card") @@ -22,7 +21,7 @@ public class CardController { @GetMapping("/search") - public ResponseEntity> getSearchList(@RequestParam(name = "searchWord") String searchWord) { + public ResponseEntity> getSearchList(@RequestParam(name = "searchWord") String searchWord) throws IOException { List searchResults = imageCrawler.crawlSearchList(searchWord); return SuccessResponse.ok(searchResults); } @@ -31,9 +30,10 @@ public ResponseEntity> getSearchList(@RequestParam(name = "se public ResponseEntity> getCardList(@RequestParam(name = "searchWord") String searchWord) throws IOException { List signImageUrls = imageCrawler.crawlImageUrls(searchWord); byte[] mergeImages = imageCrawler.mergeImages(signImageUrls); - String url = imageCrawler.uploadFile(mergeImages, "merged_image.jpg"); - - return SuccessResponse.ok(url); + String signImageUrl = imageCrawler.uploadFile(mergeImages, "merged_image.jpg"); + String generatedImageUrl = imageCrawler.generateImage(searchWord); + SearchCardResponseDto generatedUrl = new SearchCardResponseDto(generatedImageUrl, signImageUrl); + return SuccessResponse.ok(generatedUrl); } @GetMapping("/{name}") diff --git a/src/main/java/com/example/comma/domain/external/service/service/ImageCrawler.java b/src/main/java/com/example/comma/domain/external/service/ImageCrawler.java similarity index 75% rename from src/main/java/com/example/comma/domain/external/service/service/ImageCrawler.java rename to src/main/java/com/example/comma/domain/external/service/ImageCrawler.java index 6a16c58..cf29425 100644 --- a/src/main/java/com/example/comma/domain/external/service/service/ImageCrawler.java +++ b/src/main/java/com/example/comma/domain/external/service/ImageCrawler.java @@ -1,30 +1,34 @@ -package com.example.comma.domain.external.service.service; +package com.example.comma.domain.external.service; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.PutObjectRequest; import com.example.comma.domain.card.dto.response.SearchListResponseDto; import com.example.comma.global.error.exception.EntityNotFoundException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; -import org.jsoup.Connection; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; - import org.springframework.beans.factory.annotation.Value; - +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; - +import org.springframework.web.client.RestTemplate; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.net.URL; import java.net.URLEncoder; -import java.util.*; +import java.util.ArrayList; import java.util.List; import static com.example.comma.global.error.ErrorCode.SIGNLANGUAGE_NOT_FOUND; @@ -39,6 +43,12 @@ public class ImageCrawler { @Value("${cloud.aws.s3.bucket}") private String bucketName; + @Value("${dalle.api.key}") + private String openaiApiKey; + + private final RestTemplate restTemplate = new RestTemplate(); + + // 이미지 크롤링 public List crawlImageUrls(String searchWord) { try { // 검색 페이지 URL @@ -115,6 +125,7 @@ public String uploadFile(byte[] fileData, String fileName) throws IOException { } + //이미지 병합 public static byte[] mergeImages(List imageUrls) throws IOException { BufferedImage[] images = new BufferedImage[imageUrls.size()]; int totalWidth = 0; @@ -164,6 +175,35 @@ public List crawlSearchList(String searchWord) { } return searchResults; } + + //달리 이미지 생성 + public String generateImage(String searchWord) throws IOException { + String apiUrl = "https://api.openai.com/v1/images/generations"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Authorization", "Bearer " + openaiApiKey); + + String requestBody = "{\"model\": \"dall-e-3\", \"prompt\": \"" + "[" +searchWord + "]"+ + " [](대괄호) 안에 들어가는 단어에 대해 사실적인 이미지를 그대로 일러스트 화한 느낌으로, 아기자기하고 부드러운 그림체로 그려줘" + + "\", \"n\": 1, \"size\": \"1024x1024\"}"; + + HttpEntity requestEntity = new HttpEntity<>(requestBody, headers); + String imageURL = restTemplate.postForObject(apiUrl, requestEntity, String.class); + + return extractImageUrl(imageURL); + } + + //json 파싱 + public String extractImageUrl(String json) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + JsonNode rootNode = mapper.readTree(json); + + JsonNode dataNode = rootNode.get("data").get(0); + String imageUrl = dataNode.get("url").asText(); + + return imageUrl; + } } diff --git a/src/main/java/com/example/comma/domain/external/service/controller/DalleController.java b/src/main/java/com/example/comma/domain/external/service/controller/DalleController.java deleted file mode 100644 index 4664560..0000000 --- a/src/main/java/com/example/comma/domain/external/service/controller/DalleController.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.example.comma.domain.external.service.controller; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.*; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.multipart.MultipartFile; -import java.io.IOException; - -@RestController -public class DalleController { - - @Value("${dalle.api.key}") - private String apiKey; - - private final RestTemplate restTemplate = new RestTemplate(); - - @PostMapping("/dalle") - public ResponseEntity generateImage(@RequestParam("text") String text) { - // DALL-E API 엔드포인트 - String apiUrl = "https://api.openai.com/v1/davinci/generate"; - - // API 헤더 설정 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("Authorization", "Bearer " + apiKey); - - // API 요청 바디 생성 - String requestBody = "{\"prompt\": \"" + text + "\", \"max_tokens\": 50}"; - - // API 요청 보내기 - HttpEntity requestEntity = new HttpEntity<>(requestBody, headers); - ResponseEntity responseEntity = restTemplate.exchange(apiUrl, HttpMethod.POST, requestEntity, byte[].class); - - // API 응답 반환 - return ResponseEntity.status(responseEntity.getStatusCode()).contentType(MediaType.IMAGE_JPEG).body(responseEntity.getBody()); - } -} From 2aff6c9d5f314efd575b89d87ab054691099d774 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Sat, 27 Apr 2024 21:22:08 +0900 Subject: [PATCH 12/29] =?UTF-8?q?[feat]=20DALLE=20=EB=8B=A8=EC=96=B4?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20API=20Dto=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/card/dto/response/SearchCardResponseDto.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/com/example/comma/domain/card/dto/response/SearchCardResponseDto.java diff --git a/src/main/java/com/example/comma/domain/card/dto/response/SearchCardResponseDto.java b/src/main/java/com/example/comma/domain/card/dto/response/SearchCardResponseDto.java new file mode 100644 index 0000000..328ae9c --- /dev/null +++ b/src/main/java/com/example/comma/domain/card/dto/response/SearchCardResponseDto.java @@ -0,0 +1,7 @@ +package com.example.comma.domain.card.dto.response; + +public record SearchCardResponseDto( + String generatedImageUrl, + String signImageUrl +) { +} From a9b99403fd9d7afb2e84f164800138555935ad8c Mon Sep 17 00:00:00 2001 From: hysong4u Date: Sun, 28 Apr 2024 01:39:57 +0900 Subject: [PATCH 13/29] =?UTF-8?q?[fix]=20=EB=8B=A8=EC=96=B4=EC=9D=B8?= =?UTF-8?q?=EC=8B=9D=20api=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comma/domain/card/controller/CardController.java | 10 ++++++++-- .../com/example/comma/domain/card/entity/Card.java | 4 ++-- .../com/example/comma/domain/card/entity/UserCard.java | 2 ++ .../example/comma/domain/card/service/CardService.java | 8 +++++--- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/example/comma/domain/card/controller/CardController.java b/src/main/java/com/example/comma/domain/card/controller/CardController.java index 63cc8e8..20409dc 100644 --- a/src/main/java/com/example/comma/domain/card/controller/CardController.java +++ b/src/main/java/com/example/comma/domain/card/controller/CardController.java @@ -37,8 +37,14 @@ public ResponseEntity> getCardList(@RequestParam(name = "sear } @GetMapping("/{name}") - public ResponseEntity> getWord(@PathVariable(name = "name") String name) { - CardImageResponseDto CardImage = cardService.getCardImage(name); + public ResponseEntity> getWord(@PathVariable(name = "name") String name) throws IOException { + + Long cardId = cardService.getCardId(name); + List signImageUrls = imageCrawler.crawlImageUrls(name); + byte[] mergeImages = imageCrawler.mergeImages(signImageUrls); + String signImageUrl = imageCrawler.uploadFile(mergeImages, "merged_image.jpg"); + String generatedImageUrl = imageCrawler.generateImage(name); + CardImageResponseDto CardImage = new CardImageResponseDto(cardId, generatedImageUrl, signImageUrl); return SuccessResponse.ok(CardImage); } diff --git a/src/main/java/com/example/comma/domain/card/entity/Card.java b/src/main/java/com/example/comma/domain/card/entity/Card.java index 6ca6377..6169466 100644 --- a/src/main/java/com/example/comma/domain/card/entity/Card.java +++ b/src/main/java/com/example/comma/domain/card/entity/Card.java @@ -20,9 +20,9 @@ public class Card { private String name; - private String CardImageUrl; + private String cardImageUrl; - private String SignImageUrl; + private String signImageUrl; @OneToMany(mappedBy = "card") diff --git a/src/main/java/com/example/comma/domain/card/entity/UserCard.java b/src/main/java/com/example/comma/domain/card/entity/UserCard.java index 0035d57..9d99f37 100644 --- a/src/main/java/com/example/comma/domain/card/entity/UserCard.java +++ b/src/main/java/com/example/comma/domain/card/entity/UserCard.java @@ -29,6 +29,8 @@ public class UserCard extends BaseTimeEntity { private Boolean cardRegistration; + private String CardImageUrl; + public UserCard(User user, Card card, Boolean quizParticipation, Boolean cardRegistration) { this.user = user; this.card = card; diff --git a/src/main/java/com/example/comma/domain/card/service/CardService.java b/src/main/java/com/example/comma/domain/card/service/CardService.java index f9cae11..ae9c0a0 100644 --- a/src/main/java/com/example/comma/domain/card/service/CardService.java +++ b/src/main/java/com/example/comma/domain/card/service/CardService.java @@ -8,6 +8,7 @@ import com.example.comma.domain.card.entity.UserCard; import com.example.comma.domain.card.repository.CardRepository; import com.example.comma.domain.card.repository.UserCardRepository; +import com.example.comma.domain.external.service.ImageCrawler; import com.example.comma.domain.user.entity.User; import com.example.comma.domain.user.repository.UserRepository; import com.example.comma.global.error.ErrorCode; @@ -19,6 +20,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.io.IOException; import java.util.List; import java.util.Random; import java.util.stream.Collectors; @@ -32,12 +34,12 @@ public class CardService { private final CardRepository cardRepository; private final UserRepository userRepository; private final UserCardRepository userCardRepository; + private final ImageCrawler imageCrawler; - public CardImageResponseDto getCardImage(String name) { - System.out.println("name = " + name); + public Long getCardId(String name) { Card card = cardRepository.findByName(name) .orElseThrow(() -> new EntityNotFoundException(ErrorCode.CARD_NOT_FOUND)); - return new CardImageResponseDto(card.getId(), card.getCardImageUrl(), card.getSignImageUrl()); + return card.getId(); } From 2d87793ed847ea5e23a0514b38bf97f1d0e67980 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Sun, 28 Apr 2024 01:40:23 +0900 Subject: [PATCH 14/29] =?UTF-8?q?[fix]=20dto=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/dto/request/request/GeminiRequest.java | 9 --------- .../external/service/dto/response/GeminiResponse.java | 9 --------- 2 files changed, 18 deletions(-) delete mode 100644 src/main/java/com/example/comma/domain/external/service/dto/request/request/GeminiRequest.java delete mode 100644 src/main/java/com/example/comma/domain/external/service/dto/response/GeminiResponse.java diff --git a/src/main/java/com/example/comma/domain/external/service/dto/request/request/GeminiRequest.java b/src/main/java/com/example/comma/domain/external/service/dto/request/request/GeminiRequest.java deleted file mode 100644 index 6a0a778..0000000 --- a/src/main/java/com/example/comma/domain/external/service/dto/request/request/GeminiRequest.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.example.comma.domain.external.service.dto.request.request; - -public class GeminiRequest { - private String text; - - public GeminiRequest(String text) { - this.text = text; - } -} \ No newline at end of file diff --git a/src/main/java/com/example/comma/domain/external/service/dto/response/GeminiResponse.java b/src/main/java/com/example/comma/domain/external/service/dto/response/GeminiResponse.java deleted file mode 100644 index dbd3118..0000000 --- a/src/main/java/com/example/comma/domain/external/service/dto/response/GeminiResponse.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.example.comma.domain.external.service.dto.response; - -import lombok.Data; - -@Data -public class GeminiResponse { - private boolean success; - private byte[] imageData; -} From 3ddc9f62d478165db011c5623ed7b26c6b3bdeb8 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Sun, 28 Apr 2024 01:40:42 +0900 Subject: [PATCH 15/29] =?UTF-8?q?[fix]=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20api=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/comma/domain/external/service/ImageCrawler.java | 2 +- .../com/example/comma/global/config/auth/SecurityConfig.java | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/example/comma/domain/external/service/ImageCrawler.java b/src/main/java/com/example/comma/domain/external/service/ImageCrawler.java index cf29425..720f429 100644 --- a/src/main/java/com/example/comma/domain/external/service/ImageCrawler.java +++ b/src/main/java/com/example/comma/domain/external/service/ImageCrawler.java @@ -185,7 +185,7 @@ public String generateImage(String searchWord) throws IOException { headers.set("Authorization", "Bearer " + openaiApiKey); String requestBody = "{\"model\": \"dall-e-3\", \"prompt\": \"" + "[" +searchWord + "]"+ - " [](대괄호) 안에 들어가는 단어에 대해 사실적인 이미지를 그대로 일러스트 화한 느낌으로, 아기자기하고 부드러운 그림체로 그려줘" + + " [](대괄호) 안에 들어가는 단어에 대해 사실적인 이미지를 그대로 일러스트 화한 느낌으로, 부드러운 그림체로 그려줘" + "\", \"n\": 1, \"size\": \"1024x1024\"}"; HttpEntity requestEntity = new HttpEntity<>(requestBody, headers); diff --git a/src/main/java/com/example/comma/global/config/auth/SecurityConfig.java b/src/main/java/com/example/comma/global/config/auth/SecurityConfig.java index 0aa85de..7798b26 100644 --- a/src/main/java/com/example/comma/global/config/auth/SecurityConfig.java +++ b/src/main/java/com/example/comma/global/config/auth/SecurityConfig.java @@ -27,9 +27,8 @@ public class SecurityConfig { private static final String[] whiteList = {"/", "api/user/token/**", "login/oauth2/code/google/**", - "/api/card/search/**", //임시 - "/gemini" ,//임시 - "/generate-image" //임시 + "/gemini",//임시 + "/push-notification" //임시 }; @Bean From eddbeaedfc9224a5b96813f2cbf1860201591c04 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Wed, 1 May 2024 04:34:11 +0900 Subject: [PATCH 16/29] =?UTF-8?q?[feat]=20gemini=20=EC=88=98=ED=98=95=20?= =?UTF-8?q?=EC=82=AC=EC=A7=84=20=EC=83=81=EC=84=B8=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=B6=94=EC=B6=9C=20api=20controller=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 + .../external/controller/GeminiController.java | 28 ++++++ .../service/controller/GeminiController.java | 86 ------------------- 3 files changed, 30 insertions(+), 86 deletions(-) create mode 100644 src/main/java/com/example/comma/domain/external/controller/GeminiController.java delete mode 100644 src/main/java/com/example/comma/domain/external/service/controller/GeminiController.java diff --git a/build.gradle b/build.gradle index 3589a3a..42b6855 100644 --- a/build.gradle +++ b/build.gradle @@ -66,6 +66,8 @@ dependencies { //firebase implementation 'com.google.firebase:firebase-admin:9.2.0' implementation 'com.squareup.okhttp3:okhttp:4.11.0' + + implementation 'com.google.cloud:google-cloud-vertex-ai:1.2.0' } tasks.named('test') { diff --git a/src/main/java/com/example/comma/domain/external/controller/GeminiController.java b/src/main/java/com/example/comma/domain/external/controller/GeminiController.java new file mode 100644 index 0000000..b62b978 --- /dev/null +++ b/src/main/java/com/example/comma/domain/external/controller/GeminiController.java @@ -0,0 +1,28 @@ +package com.example.comma.domain.external.controller; + +import com.example.comma.domain.external.service.GeminiService; +import com.example.comma.domain.external.service.ImageCrawler; +import com.example.comma.global.common.SuccessResponse; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.*; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.util.Base64; + +@RequiredArgsConstructor +@RestController +public class GeminiController { + private final GeminiService geminiService; + @GetMapping("/gemini") + public ResponseEntity> generateResponse(@RequestParam(name = "text") String text) { + String response = geminiService.generateResponse(text); + return SuccessResponse.ok(response); + } + +} diff --git a/src/main/java/com/example/comma/domain/external/service/controller/GeminiController.java b/src/main/java/com/example/comma/domain/external/service/controller/GeminiController.java deleted file mode 100644 index 5d0a556..0000000 --- a/src/main/java/com/example/comma/domain/external/service/controller/GeminiController.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.example.comma.domain.external.service.controller; - -import com.example.comma.domain.external.service.service.ImageCrawler; -import com.example.comma.global.common.SuccessResponse; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.*; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - -import java.util.Base64; - -@RequiredArgsConstructor -@RestController -public class GeminiController { - - private final ImageCrawler imageCrawler; - private static final String API_ENDPOINT_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent"; - - @Value("${gemini.api.key}") - private String API_KEY; - - private final RestTemplate restTemplate = new RestTemplate(); - private final ObjectMapper objectMapper = new ObjectMapper(); - - @GetMapping("/gemini") - public ResponseEntity generateImage(@RequestParam(name = "text") String text) { - String requestBody = "{\"contents\": [{\"parts\": [{\"text\": \"" + text + "\"}]}]}"; - - UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(API_ENDPOINT_URL) - .queryParam("key", API_KEY); - - ResponseEntity responseEntity = restTemplate.postForEntity(builder.toUriString(), requestBody, String.class); - - if (responseEntity.getStatusCode() == HttpStatus.OK) { - String responseBody = responseEntity.getBody(); - String imageBase64 = extractImage(responseBody); - - if (!StringUtils.isEmpty(imageBase64)) { - byte[] imageBytes = Base64.getDecoder().decode(imageBase64); - return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG).body(imageBytes); - } - } - - return ResponseEntity.notFound().build(); - } - - private String extractImage(String responseBody) { - try { - JsonNode rootNode = objectMapper.readTree(responseBody); - JsonNode candidatesNode = rootNode.get("candidates"); - - if (candidatesNode.isArray()) { - for (JsonNode candidateNode : candidatesNode) { - JsonNode contentNode = candidateNode.get("content"); - JsonNode partsNode = contentNode.get("parts"); - - if (partsNode.isArray()) { - for (JsonNode partNode : partsNode) { - String partText = partNode.get("text").asText(); - if (isImage(partText)) { - return partText; - } - } - } - } - } - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - private boolean isImage(String text) { - try { - Base64.getDecoder().decode(text); - return true; - } catch (IllegalArgumentException e) { - return false; - } - } -} From 2bc593e263163914773d1657e032661e1310aa5a Mon Sep 17 00:00:00 2001 From: hysong4u Date: Wed, 1 May 2024 04:34:23 +0900 Subject: [PATCH 17/29] =?UTF-8?q?[feat]=20gemini=20=EC=88=98=ED=98=95=20?= =?UTF-8?q?=EC=82=AC=EC=A7=84=20=EC=83=81=EC=84=B8=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=B6=94=EC=B6=9C=20api=20service=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../external/service/GeminiService.java | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/main/java/com/example/comma/domain/external/service/GeminiService.java diff --git a/src/main/java/com/example/comma/domain/external/service/GeminiService.java b/src/main/java/com/example/comma/domain/external/service/GeminiService.java new file mode 100644 index 0000000..7f8e09e --- /dev/null +++ b/src/main/java/com/example/comma/domain/external/service/GeminiService.java @@ -0,0 +1,64 @@ +package com.example.comma.domain.external.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.util.HashMap; +import java.util.Map; + +@RequiredArgsConstructor +@Service +public class GeminiService { + private final RestTemplate restTemplate = new RestTemplate(); + + @Value("${gemini.api.key}") + private String apiKey; + + public String generateResponse(String text) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + text ="한국 수화 단어 중 " + text + "에 대한 수형 설명해줘"; + String requestBody = "{\"contents\": [{\"parts\":[{\"text\":\"" + text + "\"}]}]}"; + + UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent") + .queryParam("key", apiKey); + + HttpEntity requestEntity = new HttpEntity<>(requestBody, headers); + + ResponseEntity responseEntity = restTemplate.exchange( + builder.toUriString(), + HttpMethod.POST, + requestEntity, + String.class + ); + + if (responseEntity.getStatusCode() == HttpStatus.OK) { + String responseBody = responseEntity.getBody(); + String textResponse = extractTextFromResponse(responseBody); + return textResponse; + } else { + return "Failed to generate content. Status code: " + responseEntity.getStatusCodeValue(); + } + } + + private String extractTextFromResponse(String responseBody) { + int startIndex = responseBody.indexOf("\"text\":") + "\"text\":".length(); + int endIndex = responseBody.indexOf("\"role\""); + String text = responseBody.substring(startIndex, endIndex); + String cleanedText = text + .replaceAll("[^가-힣a-zA-Z0-9.\\s]", "") + .replaceAll("nn", "\n\n") + .replaceAll("n", " ") + .replaceAll("\\s+", " "); + + + return cleanedText; + } + +} \ No newline at end of file From ccec50178d8d768e1ab852ad66b44c3d8be408fd Mon Sep 17 00:00:00 2001 From: hysong4u Date: Wed, 1 May 2024 04:35:37 +0900 Subject: [PATCH 18/29] =?UTF-8?q?[fix]=20=EB=8B=A8=EC=96=B4=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20api=20request=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../card/controller/CardController.java | 16 ++++++-- .../comma/domain/card/entity/Card.java | 7 +++- .../comma/domain/card/entity/UserCard.java | 3 +- .../domain/card/service/CardService.java | 38 ++++++++++++++++--- .../domain/external/service/ImageCrawler.java | 2 +- 5 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/example/comma/domain/card/controller/CardController.java b/src/main/java/com/example/comma/domain/card/controller/CardController.java index 20409dc..4184800 100644 --- a/src/main/java/com/example/comma/domain/card/controller/CardController.java +++ b/src/main/java/com/example/comma/domain/card/controller/CardController.java @@ -30,9 +30,11 @@ public ResponseEntity> getSearchList(@RequestParam(name = "se public ResponseEntity> getCardList(@RequestParam(name = "searchWord") String searchWord) throws IOException { List signImageUrls = imageCrawler.crawlImageUrls(searchWord); byte[] mergeImages = imageCrawler.mergeImages(signImageUrls); - String signImageUrl = imageCrawler.uploadFile(mergeImages, "merged_image.jpg"); + String signImageUrl = imageCrawler.uploadFile(mergeImages, searchWord + ".jpg"); String generatedImageUrl = imageCrawler.generateImage(searchWord); SearchCardResponseDto generatedUrl = new SearchCardResponseDto(generatedImageUrl, signImageUrl); + + cardService.registerCard(searchWord, signImageUrl); return SuccessResponse.ok(generatedUrl); } @@ -40,17 +42,23 @@ public ResponseEntity> getCardList(@RequestParam(name = "sear public ResponseEntity> getWord(@PathVariable(name = "name") String name) throws IOException { Long cardId = cardService.getCardId(name); + List signImageUrls = imageCrawler.crawlImageUrls(name); byte[] mergeImages = imageCrawler.mergeImages(signImageUrls); - String signImageUrl = imageCrawler.uploadFile(mergeImages, "merged_image.jpg"); + String signImageUrl = imageCrawler.uploadFile(mergeImages, name + ".jpg"); String generatedImageUrl = imageCrawler.generateImage(name); + CardImageResponseDto CardImage = new CardImageResponseDto(cardId, generatedImageUrl, signImageUrl); + cardService.registerCard(name, signImageUrl); + return SuccessResponse.ok(CardImage); + } @PostMapping("/{cardId}") - public ResponseEntity> createCard(@UserId Long userId, @PathVariable(name = "cardId") Long cardId) { - cardService.createCard(userId, cardId); + public ResponseEntity> createCard(@UserId Long userId, @PathVariable(name = "cardId") Long cardId, @RequestBody String cardImageUrl) { + // cardService.createCard(userId, cardId); + cardService.saveCard(userId, cardId, cardImageUrl); return SuccessResponse.created(null); } diff --git a/src/main/java/com/example/comma/domain/card/entity/Card.java b/src/main/java/com/example/comma/domain/card/entity/Card.java index 6169466..77e09f2 100644 --- a/src/main/java/com/example/comma/domain/card/entity/Card.java +++ b/src/main/java/com/example/comma/domain/card/entity/Card.java @@ -20,12 +20,15 @@ public class Card { private String name; - private String cardImageUrl; - private String signImageUrl; @OneToMany(mappedBy = "card") private List userCardList; + public Card(String name, String signImageUrl) { + this.name = name; + this.signImageUrl = signImageUrl; + } + } diff --git a/src/main/java/com/example/comma/domain/card/entity/UserCard.java b/src/main/java/com/example/comma/domain/card/entity/UserCard.java index 9d99f37..4c1bf8a 100644 --- a/src/main/java/com/example/comma/domain/card/entity/UserCard.java +++ b/src/main/java/com/example/comma/domain/card/entity/UserCard.java @@ -31,11 +31,12 @@ public class UserCard extends BaseTimeEntity { private String CardImageUrl; - public UserCard(User user, Card card, Boolean quizParticipation, Boolean cardRegistration) { + public UserCard(User user, Card card, Boolean quizParticipation, Boolean cardRegistration, String CardImageUrl) { this.user = user; this.card = card; this.quizParticipation = quizParticipation; this.cardRegistration = cardRegistration; + this.CardImageUrl = CardImageUrl; } public void setQuizParticipation(Boolean quizParticipation) { diff --git a/src/main/java/com/example/comma/domain/card/service/CardService.java b/src/main/java/com/example/comma/domain/card/service/CardService.java index ae9c0a0..7054990 100644 --- a/src/main/java/com/example/comma/domain/card/service/CardService.java +++ b/src/main/java/com/example/comma/domain/card/service/CardService.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.util.List; +import java.util.Optional; import java.util.Random; import java.util.stream.Collectors; @@ -42,7 +43,17 @@ public Long getCardId(String name) { return card.getId(); } + public void registerCard(String name, String signImageUrl) { + Optional existingCardOptional = cardRepository.findByName(name); + if (existingCardOptional.isPresent()) { + return; + } + Card newCard = new Card(name, signImageUrl); + cardRepository.save(newCard); + } + + /* public void createCard(Long userId, Long cardId) { User user = userRepository.findById(userId) @@ -60,6 +71,24 @@ public void createCard(Long userId, Long cardId) { userCardRepository.save(userCard); } +*/ + public void saveCard(Long userId, Long cardId, String cardImageUrl) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException(ErrorCode.USER_NOT_FOUND)); + + Card card = cardRepository.findById(cardId) + .orElseThrow(() -> new EntityNotFoundException(ErrorCode.CARD_NOT_FOUND)); + + if (userCardRepository.existsByUserAndCard(user, card)) { + throw new ConflictException(ErrorCode.USER_CARD_ALREADY_EXISTS); + } + + UserCard userCard = new UserCard(user, card, false, true, cardImageUrl); + + userCardRepository.save(userCard); + } + + @Transactional @Scheduled(cron = "0 0 0 * * ?") public void resetCardRegistration() { @@ -84,7 +113,7 @@ private List convertToCardResponseDtos(List userCards return userCards.stream() .map(userCard -> { Card card = userCard.getCard(); - return new CardResponseDto(userCard.getId(), card.getName(), card.getCardImageUrl(), card.getSignImageUrl()); + return new CardResponseDto(userCard.getId(), card.getName(), userCard.getCardImageUrl(), card.getSignImageUrl()); }) .collect(Collectors.toList()); } @@ -104,13 +133,12 @@ public CardResponseDto getCardDetail(Long userCardId) { .orElseThrow(() -> new EntityNotFoundException(ErrorCode.USER_CARD_NOT_FOUND)); Card card = userCard.getCard(); - return new CardResponseDto(card.getId(), card.getName(), card.getCardImageUrl(), card.getSignImageUrl()); + return new CardResponseDto(card.getId(), card.getName(), userCard.getCardImageUrl(), card.getSignImageUrl()); } public WrongCardResponseDto getRandomQuizCard(Long userCardId) { UserCard userCard = userCardRepository.findById(userCardId) .orElseThrow(() -> new EntityNotFoundException(ErrorCode.USER_CARD_NOT_FOUND)); - List remainUserCards = userCardRepository.findUserCardByUserIdAndCardIdNot(userCard.getUser().getId(), userCardId); if (remainUserCards.size() <= 1) { @@ -127,7 +155,7 @@ public WrongCardResponseDto getRandomQuizCard(Long userCardId) { randomCard = randomUserCard.getCard(); - return new WrongCardResponseDto(randomCard.getName(), randomCard.getCardImageUrl(), randomCard.getSignImageUrl()); + return new WrongCardResponseDto(randomCard.getName(), randomUserCard.getCardImageUrl(), randomCard.getSignImageUrl()); } @@ -136,7 +164,7 @@ public CorrectCardResponseDto getQuizCard(Long userCardId) { .orElseThrow(() -> new EntityNotFoundException(ErrorCode.USER_CARD_NOT_FOUND)); Card card = userCard.getCard(); - return new CorrectCardResponseDto(card.getName(), card.getCardImageUrl(), card.getSignImageUrl()); + return new CorrectCardResponseDto(card.getName(), userCard.getCardImageUrl(), card.getSignImageUrl()); } @Transactional diff --git a/src/main/java/com/example/comma/domain/external/service/ImageCrawler.java b/src/main/java/com/example/comma/domain/external/service/ImageCrawler.java index 720f429..166d2fc 100644 --- a/src/main/java/com/example/comma/domain/external/service/ImageCrawler.java +++ b/src/main/java/com/example/comma/domain/external/service/ImageCrawler.java @@ -63,7 +63,6 @@ public List crawlImageUrls(String searchWord) { assert aElement != null; String aElementText = aElement.text().trim(); - if (!aElementText.contains(searchWord)) { throw new EntityNotFoundException(SIGNLANGUAGE_NOT_FOUND); } @@ -108,6 +107,7 @@ public List crawlImageUrls(String searchWord) { } + //이미지 S3 업로드 public String uploadFile(byte[] fileData, String fileName) throws IOException { String directory = "mergedImg/"; From 75f6dffd9d37977b1b6d3e5c936b87f68ccf1663 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Thu, 2 May 2024 17:34:47 +0900 Subject: [PATCH 19/29] =?UTF-8?q?[fix]=20=EB=8B=A8=EC=96=B4=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20api=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../card/controller/CardController.java | 51 +++++++++++++------ .../dto/response/DescriptionResponseDto.java | 8 +++ .../dto/response/SearchListResponseDto.java | 6 --- .../domain/card/service/CardService.java | 6 +-- .../external/controller/GeminiController.java | 28 ---------- .../external/service/GeminiService.java | 47 ++++++++++++++--- ...eCrawler.java => ImageCrawlerService.java} | 10 ++-- 7 files changed, 90 insertions(+), 66 deletions(-) create mode 100644 src/main/java/com/example/comma/domain/card/dto/response/DescriptionResponseDto.java delete mode 100644 src/main/java/com/example/comma/domain/card/dto/response/SearchListResponseDto.java delete mode 100644 src/main/java/com/example/comma/domain/external/controller/GeminiController.java rename src/main/java/com/example/comma/domain/external/service/{ImageCrawler.java => ImageCrawlerService.java} (95%) diff --git a/src/main/java/com/example/comma/domain/card/controller/CardController.java b/src/main/java/com/example/comma/domain/card/controller/CardController.java index 4184800..ff80923 100644 --- a/src/main/java/com/example/comma/domain/card/controller/CardController.java +++ b/src/main/java/com/example/comma/domain/card/controller/CardController.java @@ -2,7 +2,8 @@ import com.example.comma.domain.card.dto.response.*; import com.example.comma.domain.card.service.CardService; -import com.example.comma.domain.external.service.ImageCrawler; +import com.example.comma.domain.external.service.GeminiService; +import com.example.comma.domain.external.service.ImageCrawlerService; import com.example.comma.global.common.SuccessResponse; import com.example.comma.global.config.auth.UserId; import lombok.RequiredArgsConstructor; @@ -10,6 +11,7 @@ import org.springframework.web.bind.annotation.*; import java.io.IOException; +import java.util.Collections; import java.util.List; @RequiredArgsConstructor @@ -17,25 +19,42 @@ @RestController public class CardController { private final CardService cardService; - private final ImageCrawler imageCrawler; + private final GeminiService geminiService; + private final ImageCrawlerService imageCrawlerService; - @GetMapping("/search") + //단어 검색 + @GetMapping("/search-word") public ResponseEntity> getSearchList(@RequestParam(name = "searchWord") String searchWord) throws IOException { - List searchResults = imageCrawler.crawlSearchList(searchWord); - return SuccessResponse.ok(searchResults); + List searchResults = imageCrawlerService.crawlSearchList(searchWord); + List descriptionResponse = geminiService.generateDescriptionList(searchResults); + return SuccessResponse.ok(descriptionResponse); } - @GetMapping("/image") - public ResponseEntity> getCardList(@RequestParam(name = "searchWord") String searchWord) throws IOException { - List signImageUrls = imageCrawler.crawlImageUrls(searchWord); - byte[] mergeImages = imageCrawler.mergeImages(signImageUrls); - String signImageUrl = imageCrawler.uploadFile(mergeImages, searchWord + ".jpg"); - String generatedImageUrl = imageCrawler.generateImage(searchWord); + //수형 설명 검색 + @GetMapping("/search-details") + public ResponseEntity> generateResponse(@RequestParam(name = "searchWord") String searchWord) throws IOException { + + //수형, 단어 이미지 생성 + List signImageUrls = imageCrawlerService.crawlImageUrls(searchWord); + byte[] mergeImages = imageCrawlerService.mergeImages(signImageUrls); + String signImageUrl = imageCrawlerService.uploadFile(mergeImages, searchWord + ".jpg"); + String generatedImageUrl = imageCrawlerService.generateImage(searchWord); SearchCardResponseDto generatedUrl = new SearchCardResponseDto(generatedImageUrl, signImageUrl); + //cardId 생성 cardService.registerCard(searchWord, signImageUrl); - return SuccessResponse.ok(generatedUrl); + Long cardId = cardService.getCardId(searchWord); + + //단어 사전 정의 생성 + List descriptionResponse = geminiService.generateDescriptionList(Collections.singletonList(searchWord)); + + //수형 동작 설명 + String generatesignLanguageDescription= geminiService.generateResponse(searchWord); + + WordDatailsResponseDto wordDatailsResponse = new WordDatailsResponseDto(cardId, searchWord, descriptionResponse.get(0).description(),descriptionResponse.get(0).partsOfSeech(), generatedImageUrl, signImageUrl,generatesignLanguageDescription ); + + return SuccessResponse.ok(wordDatailsResponse); } @GetMapping("/{name}") @@ -43,10 +62,10 @@ public ResponseEntity> getWord(@PathVariable(name = "name") S Long cardId = cardService.getCardId(name); - List signImageUrls = imageCrawler.crawlImageUrls(name); - byte[] mergeImages = imageCrawler.mergeImages(signImageUrls); - String signImageUrl = imageCrawler.uploadFile(mergeImages, name + ".jpg"); - String generatedImageUrl = imageCrawler.generateImage(name); + List signImageUrls = imageCrawlerService.crawlImageUrls(name); + byte[] mergeImages = imageCrawlerService.mergeImages(signImageUrls); + String signImageUrl = imageCrawlerService.uploadFile(mergeImages, name + ".jpg"); + String generatedImageUrl = imageCrawlerService.generateImage(name); CardImageResponseDto CardImage = new CardImageResponseDto(cardId, generatedImageUrl, signImageUrl); cardService.registerCard(name, signImageUrl); diff --git a/src/main/java/com/example/comma/domain/card/dto/response/DescriptionResponseDto.java b/src/main/java/com/example/comma/domain/card/dto/response/DescriptionResponseDto.java new file mode 100644 index 0000000..4772f25 --- /dev/null +++ b/src/main/java/com/example/comma/domain/card/dto/response/DescriptionResponseDto.java @@ -0,0 +1,8 @@ +package com.example.comma.domain.card.dto.response; + +public record DescriptionResponseDto( + String word, + String description, + String partsOfSeech +) { +} diff --git a/src/main/java/com/example/comma/domain/card/dto/response/SearchListResponseDto.java b/src/main/java/com/example/comma/domain/card/dto/response/SearchListResponseDto.java deleted file mode 100644 index a7e6586..0000000 --- a/src/main/java/com/example/comma/domain/card/dto/response/SearchListResponseDto.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.example.comma.domain.card.dto.response; - -public record SearchListResponseDto( - String word -) { -} diff --git a/src/main/java/com/example/comma/domain/card/service/CardService.java b/src/main/java/com/example/comma/domain/card/service/CardService.java index 7054990..7f9ca76 100644 --- a/src/main/java/com/example/comma/domain/card/service/CardService.java +++ b/src/main/java/com/example/comma/domain/card/service/CardService.java @@ -1,6 +1,5 @@ package com.example.comma.domain.card.service; -import com.example.comma.domain.card.dto.response.CardImageResponseDto; import com.example.comma.domain.card.dto.response.CardResponseDto; import com.example.comma.domain.card.dto.response.CorrectCardResponseDto; import com.example.comma.domain.card.dto.response.WrongCardResponseDto; @@ -8,7 +7,7 @@ import com.example.comma.domain.card.entity.UserCard; import com.example.comma.domain.card.repository.CardRepository; import com.example.comma.domain.card.repository.UserCardRepository; -import com.example.comma.domain.external.service.ImageCrawler; +import com.example.comma.domain.external.service.ImageCrawlerService; import com.example.comma.domain.user.entity.User; import com.example.comma.domain.user.repository.UserRepository; import com.example.comma.global.error.ErrorCode; @@ -20,7 +19,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.io.IOException; import java.util.List; import java.util.Optional; import java.util.Random; @@ -35,7 +33,7 @@ public class CardService { private final CardRepository cardRepository; private final UserRepository userRepository; private final UserCardRepository userCardRepository; - private final ImageCrawler imageCrawler; + private final ImageCrawlerService imageCrawlerService; public Long getCardId(String name) { Card card = cardRepository.findByName(name) diff --git a/src/main/java/com/example/comma/domain/external/controller/GeminiController.java b/src/main/java/com/example/comma/domain/external/controller/GeminiController.java deleted file mode 100644 index b62b978..0000000 --- a/src/main/java/com/example/comma/domain/external/controller/GeminiController.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.example.comma.domain.external.controller; - -import com.example.comma.domain.external.service.GeminiService; -import com.example.comma.domain.external.service.ImageCrawler; -import com.example.comma.global.common.SuccessResponse; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.*; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - -import java.util.Base64; - -@RequiredArgsConstructor -@RestController -public class GeminiController { - private final GeminiService geminiService; - @GetMapping("/gemini") - public ResponseEntity> generateResponse(@RequestParam(name = "text") String text) { - String response = geminiService.generateResponse(text); - return SuccessResponse.ok(response); - } - -} diff --git a/src/main/java/com/example/comma/domain/external/service/GeminiService.java b/src/main/java/com/example/comma/domain/external/service/GeminiService.java index 7f8e09e..c580a8e 100644 --- a/src/main/java/com/example/comma/domain/external/service/GeminiService.java +++ b/src/main/java/com/example/comma/domain/external/service/GeminiService.java @@ -1,15 +1,15 @@ package com.example.comma.domain.external.service; +import com.example.comma.domain.card.dto.response.DescriptionResponseDto; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; -import java.util.HashMap; -import java.util.Map; +import java.util.ArrayList; +import java.util.List; @RequiredArgsConstructor @Service @@ -19,13 +19,46 @@ public class GeminiService { @Value("${gemini.api.key}") private String apiKey; + //수형 설명 검색 + public String generateResponse(String text) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); - text ="한국 수화 단어 중 " + text + "에 대한 수형 설명해줘"; + text = "한국 수화 단어 중 " + text + "에 대한 수형 설명해줘"; String requestBody = "{\"contents\": [{\"parts\":[{\"text\":\"" + text + "\"}]}]}"; + return sendGeminiResponse(requestBody); + } + + + //단어 설명 검색 + public List generateDescriptionList(List words) { + List responses = new ArrayList<>(); + + for (String text : words) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + //단어 설명 + String requestBody1 = "{\"contents\": [{\"parts\":[{\"text\":\"1." + text + "단어에 대한 사전 정의 => 20자 이내로 답변은 하나씩만 작성\"}]}]}"; + // 품사 + String requestBody2 = "{\"contents\": [{\"parts\":[{\"text\":\"2." + text + "단어에 대한 품사 => 5자 이내로 답변은 하나씩만 작성\"}]}]}"; + + String response1 = sendGeminiResponse(requestBody1); + String response2 = sendGeminiResponse(requestBody2); + + responses.add(new DescriptionResponseDto(text, response1, response2)); + } + + return responses; + } + + //Gemini API 호출 + private String sendGeminiResponse(String requestBody) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent") .queryParam("key", apiKey); @@ -40,8 +73,7 @@ public String generateResponse(String text) { if (responseEntity.getStatusCode() == HttpStatus.OK) { String responseBody = responseEntity.getBody(); - String textResponse = extractTextFromResponse(responseBody); - return textResponse; + return extractTextFromResponse(responseBody); } else { return "Failed to generate content. Status code: " + responseEntity.getStatusCodeValue(); } @@ -61,4 +93,7 @@ private String extractTextFromResponse(String responseBody) { return cleanedText; } + + + } \ No newline at end of file diff --git a/src/main/java/com/example/comma/domain/external/service/ImageCrawler.java b/src/main/java/com/example/comma/domain/external/service/ImageCrawlerService.java similarity index 95% rename from src/main/java/com/example/comma/domain/external/service/ImageCrawler.java rename to src/main/java/com/example/comma/domain/external/service/ImageCrawlerService.java index 166d2fc..513afc6 100644 --- a/src/main/java/com/example/comma/domain/external/service/ImageCrawler.java +++ b/src/main/java/com/example/comma/domain/external/service/ImageCrawlerService.java @@ -3,7 +3,6 @@ import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.PutObjectRequest; -import com.example.comma.domain.card.dto.response.SearchListResponseDto; import com.example.comma.global.error.exception.EntityNotFoundException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -36,7 +35,7 @@ @RequiredArgsConstructor @Service @Transactional -public class ImageCrawler { +public class ImageCrawlerService { private final AmazonS3 amazonS3; @@ -159,16 +158,15 @@ public static byte[] mergeImages(List imageUrls) throws IOException { } - public List crawlSearchList(String searchWord) { - List searchResults = new ArrayList<>(); + public List crawlSearchList(String searchWord) { + List searchResults = new ArrayList<>(); try { String searchUrl = "https://sldict.korean.go.kr/front/search/searchAllList.do?searchKeyword=" + searchWord; Document doc = Jsoup.connect(searchUrl).get(); Elements aElements = doc.select("span[class=tit] a"); for (Element aElement : aElements) { String text = aElement.text(); - SearchListResponseDto result = new SearchListResponseDto(text); - searchResults.add(result); + searchResults.add(text); } } catch (IOException e) { throw new RuntimeException(e); From cee04c1cc0786c6b00da8c652bb70b4fbe641ed3 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Thu, 2 May 2024 17:35:53 +0900 Subject: [PATCH 20/29] =?UTF-8?q?[fix]=20=EB=8B=A8=EC=96=B4=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EA=B2=80=EC=83=89=20api=20Dto=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../card/dto/response/WordDatailsResponseDto.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/java/com/example/comma/domain/card/dto/response/WordDatailsResponseDto.java diff --git a/src/main/java/com/example/comma/domain/card/dto/response/WordDatailsResponseDto.java b/src/main/java/com/example/comma/domain/card/dto/response/WordDatailsResponseDto.java new file mode 100644 index 0000000..892cdc6 --- /dev/null +++ b/src/main/java/com/example/comma/domain/card/dto/response/WordDatailsResponseDto.java @@ -0,0 +1,13 @@ +package com.example.comma.domain.card.dto.response; + +public record WordDatailsResponseDto( + Long cardId, + String word, + String description, + String partsOfSeech, + String cardImageUrl, + String signImageUrl, + String signLanguageDescription + +) { +} From c782b77178c17ca441a3efb504dcfc9525c90759 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Thu, 2 May 2024 18:29:57 +0900 Subject: [PATCH 21/29] =?UTF-8?q?[fix]=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=20=EC=A0=80=EC=9E=A5=20api=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comma/domain/card/controller/CardController.java | 12 +++++++----- .../domain/card/dto/request/CardImageRequest.java | 6 ++++++ 2 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/example/comma/domain/card/dto/request/CardImageRequest.java diff --git a/src/main/java/com/example/comma/domain/card/controller/CardController.java b/src/main/java/com/example/comma/domain/card/controller/CardController.java index ff80923..502fd58 100644 --- a/src/main/java/com/example/comma/domain/card/controller/CardController.java +++ b/src/main/java/com/example/comma/domain/card/controller/CardController.java @@ -1,5 +1,6 @@ package com.example.comma.domain.card.controller; +import com.example.comma.domain.card.dto.request.CardImageRequest; import com.example.comma.domain.card.dto.response.*; import com.example.comma.domain.card.service.CardService; import com.example.comma.domain.external.service.GeminiService; @@ -23,7 +24,7 @@ public class CardController { private final ImageCrawlerService imageCrawlerService; - //단어 검색 + //단어 리스트 검색 @GetMapping("/search-word") public ResponseEntity> getSearchList(@RequestParam(name = "searchWord") String searchWord) throws IOException { List searchResults = imageCrawlerService.crawlSearchList(searchWord); @@ -31,7 +32,7 @@ public ResponseEntity> getSearchList(@RequestParam(name = "se return SuccessResponse.ok(descriptionResponse); } - //수형 설명 검색 + //단어 상세 정보 조회 @GetMapping("/search-details") public ResponseEntity> generateResponse(@RequestParam(name = "searchWord") String searchWord) throws IOException { @@ -57,6 +58,7 @@ public ResponseEntity> generateResponse(@RequestParam(name = return SuccessResponse.ok(wordDatailsResponse); } + //단어 인식으로 단어 정보 가져오기 @GetMapping("/{name}") public ResponseEntity> getWord(@PathVariable(name = "name") String name) throws IOException { @@ -74,10 +76,10 @@ public ResponseEntity> getWord(@PathVariable(name = "name") S } + //UserCard 단어 카드 저장 @PostMapping("/{cardId}") - public ResponseEntity> createCard(@UserId Long userId, @PathVariable(name = "cardId") Long cardId, @RequestBody String cardImageUrl) { - // cardService.createCard(userId, cardId); - cardService.saveCard(userId, cardId, cardImageUrl); + public ResponseEntity> createCard(@UserId Long userId, @PathVariable(name = "cardId") Long cardId, @RequestBody CardImageRequest cardImageRequest) { + cardService.saveCard(userId, cardId, cardImageRequest.cardImageUrl()); return SuccessResponse.created(null); } diff --git a/src/main/java/com/example/comma/domain/card/dto/request/CardImageRequest.java b/src/main/java/com/example/comma/domain/card/dto/request/CardImageRequest.java new file mode 100644 index 0000000..5b4963f --- /dev/null +++ b/src/main/java/com/example/comma/domain/card/dto/request/CardImageRequest.java @@ -0,0 +1,6 @@ +package com.example.comma.domain.card.dto.request; + +public record CardImageRequest( + String cardImageUrl +) { +} From 7b5b6c48e927467aa0dfe8d14000943cb30a9f07 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Thu, 2 May 2024 18:32:23 +0900 Subject: [PATCH 22/29] =?UTF-8?q?[delete]=20=ED=91=B8=EC=8B=9C=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EA=B4=80=EB=A0=A8=20=EA=B8=B0=EB=8A=A5=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/external/controller/FirebaseMessagingController.java | 0 .../domain/external/dto/request/PushNotificationRequest.java | 0 .../domain/external/dto/response/PushNotificationResponse.java | 0 .../comma/domain/external/service/FirebaseMessagingService.java | 0 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/java/com/example/comma/domain/external/controller/FirebaseMessagingController.java create mode 100644 src/main/java/com/example/comma/domain/external/dto/request/PushNotificationRequest.java create mode 100644 src/main/java/com/example/comma/domain/external/dto/response/PushNotificationResponse.java create mode 100644 src/main/java/com/example/comma/domain/external/service/FirebaseMessagingService.java diff --git a/src/main/java/com/example/comma/domain/external/controller/FirebaseMessagingController.java b/src/main/java/com/example/comma/domain/external/controller/FirebaseMessagingController.java new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/com/example/comma/domain/external/dto/request/PushNotificationRequest.java b/src/main/java/com/example/comma/domain/external/dto/request/PushNotificationRequest.java new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/com/example/comma/domain/external/dto/response/PushNotificationResponse.java b/src/main/java/com/example/comma/domain/external/dto/response/PushNotificationResponse.java new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/com/example/comma/domain/external/service/FirebaseMessagingService.java b/src/main/java/com/example/comma/domain/external/service/FirebaseMessagingService.java new file mode 100644 index 0000000..e69de29 From 655ebcd1276c1beb4cb8ca55aafa35bbe8735d3b Mon Sep 17 00:00:00 2001 From: hysong4u Date: Thu, 2 May 2024 18:38:51 +0900 Subject: [PATCH 23/29] =?UTF-8?q?[remove]=20=EB=8B=A8=EC=96=B4=20=EC=9D=B8?= =?UTF-8?q?=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20api=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../card/controller/CardController.java | 18 ------------------ .../domain/card/service/CardService.java | 19 ------------------- 2 files changed, 37 deletions(-) diff --git a/src/main/java/com/example/comma/domain/card/controller/CardController.java b/src/main/java/com/example/comma/domain/card/controller/CardController.java index 502fd58..598ed43 100644 --- a/src/main/java/com/example/comma/domain/card/controller/CardController.java +++ b/src/main/java/com/example/comma/domain/card/controller/CardController.java @@ -58,24 +58,6 @@ public ResponseEntity> generateResponse(@RequestParam(name = return SuccessResponse.ok(wordDatailsResponse); } - //단어 인식으로 단어 정보 가져오기 - @GetMapping("/{name}") - public ResponseEntity> getWord(@PathVariable(name = "name") String name) throws IOException { - - Long cardId = cardService.getCardId(name); - - List signImageUrls = imageCrawlerService.crawlImageUrls(name); - byte[] mergeImages = imageCrawlerService.mergeImages(signImageUrls); - String signImageUrl = imageCrawlerService.uploadFile(mergeImages, name + ".jpg"); - String generatedImageUrl = imageCrawlerService.generateImage(name); - - CardImageResponseDto CardImage = new CardImageResponseDto(cardId, generatedImageUrl, signImageUrl); - cardService.registerCard(name, signImageUrl); - - return SuccessResponse.ok(CardImage); - - } - //UserCard 단어 카드 저장 @PostMapping("/{cardId}") public ResponseEntity> createCard(@UserId Long userId, @PathVariable(name = "cardId") Long cardId, @RequestBody CardImageRequest cardImageRequest) { diff --git a/src/main/java/com/example/comma/domain/card/service/CardService.java b/src/main/java/com/example/comma/domain/card/service/CardService.java index 7f9ca76..37581b2 100644 --- a/src/main/java/com/example/comma/domain/card/service/CardService.java +++ b/src/main/java/com/example/comma/domain/card/service/CardService.java @@ -51,25 +51,6 @@ public void registerCard(String name, String signImageUrl) { } - /* - public void createCard(Long userId, Long cardId) { - - User user = userRepository.findById(userId) - .orElseThrow(() -> new EntityNotFoundException(ErrorCode.USER_NOT_FOUND)); - - Card card = cardRepository.findById(cardId) - .orElseThrow(() -> new EntityNotFoundException(ErrorCode.CARD_NOT_FOUND)); - - if (userCardRepository.existsByUserAndCard(user, card)) { - throw new ConflictException(ErrorCode.USER_CARD_ALREADY_EXISTS); - } - - UserCard userCard = new UserCard(user, card, false, true); - - userCardRepository.save(userCard); - } - -*/ public void saveCard(Long userId, Long cardId, String cardImageUrl) { User user = userRepository.findById(userId) .orElseThrow(() -> new EntityNotFoundException(ErrorCode.USER_NOT_FOUND)); From b0543da62148c2f2506d9814d3b1a3d086bae354 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Thu, 2 May 2024 18:52:03 +0900 Subject: [PATCH 24/29] =?UTF-8?q?[fix]=20gemini=20=EB=AA=85=EB=A0=B9?= =?UTF-8?q?=EC=96=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/comma/domain/card/controller/CardController.java | 5 ++--- .../example/comma/domain/external/service/GeminiService.java | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/example/comma/domain/card/controller/CardController.java b/src/main/java/com/example/comma/domain/card/controller/CardController.java index 598ed43..b6036ae 100644 --- a/src/main/java/com/example/comma/domain/card/controller/CardController.java +++ b/src/main/java/com/example/comma/domain/card/controller/CardController.java @@ -40,8 +40,7 @@ public ResponseEntity> generateResponse(@RequestParam(name = List signImageUrls = imageCrawlerService.crawlImageUrls(searchWord); byte[] mergeImages = imageCrawlerService.mergeImages(signImageUrls); String signImageUrl = imageCrawlerService.uploadFile(mergeImages, searchWord + ".jpg"); - String generatedImageUrl = imageCrawlerService.generateImage(searchWord); - SearchCardResponseDto generatedUrl = new SearchCardResponseDto(generatedImageUrl, signImageUrl); + String generatedImageUrl = imageCrawlerService.generateImage(searchWord);; //cardId 생성 cardService.registerCard(searchWord, signImageUrl); @@ -51,7 +50,7 @@ public ResponseEntity> generateResponse(@RequestParam(name = List descriptionResponse = geminiService.generateDescriptionList(Collections.singletonList(searchWord)); //수형 동작 설명 - String generatesignLanguageDescription= geminiService.generateResponse(searchWord); + String generatesignLanguageDescription= geminiService.generateSignDescription(searchWord); WordDatailsResponseDto wordDatailsResponse = new WordDatailsResponseDto(cardId, searchWord, descriptionResponse.get(0).description(),descriptionResponse.get(0).partsOfSeech(), generatedImageUrl, signImageUrl,generatesignLanguageDescription ); diff --git a/src/main/java/com/example/comma/domain/external/service/GeminiService.java b/src/main/java/com/example/comma/domain/external/service/GeminiService.java index c580a8e..c1eb8d5 100644 --- a/src/main/java/com/example/comma/domain/external/service/GeminiService.java +++ b/src/main/java/com/example/comma/domain/external/service/GeminiService.java @@ -21,11 +21,11 @@ public class GeminiService { //수형 설명 검색 - public String generateResponse(String text) { + public String generateSignDescription(String text) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); - text = "한국 수화 단어 중 " + text + "에 대한 수형 설명해줘"; + text = "한국 수화 단어 중 " + text + "에 대한 수화 동작 방법만 작성"; String requestBody = "{\"contents\": [{\"parts\":[{\"text\":\"" + text + "\"}]}]}"; return sendGeminiResponse(requestBody); From 4428156ee10bf72787de40bb0bf5ea9d71b9d565 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Thu, 2 May 2024 21:04:42 +0900 Subject: [PATCH 25/29] =?UTF-8?q?[feat]=20=EB=82=B4=20=EB=8B=A8=EC=96=B4?= =?UTF-8?q?=20=EC=B9=B4=EB=93=9C=20=EC=A1=B0=ED=9A=8C=20api=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/card/controller/CardController.java | 14 +++++++++++--- .../domain/card/dto/request/CardImageRequest.java | 6 ------ .../domain/card/dto/request/CardInfoRequest.java | 7 +++++++ .../card/dto/response/MyCardResponseDto.java | 9 +++++++++ .../com/example/comma/domain/card/entity/Card.java | 1 - .../example/comma/domain/card/entity/UserCard.java | 8 +++++--- .../comma/domain/card/service/CardService.java | 14 ++++++++++++-- 7 files changed, 44 insertions(+), 15 deletions(-) delete mode 100644 src/main/java/com/example/comma/domain/card/dto/request/CardImageRequest.java create mode 100644 src/main/java/com/example/comma/domain/card/dto/request/CardInfoRequest.java create mode 100644 src/main/java/com/example/comma/domain/card/dto/response/MyCardResponseDto.java diff --git a/src/main/java/com/example/comma/domain/card/controller/CardController.java b/src/main/java/com/example/comma/domain/card/controller/CardController.java index b6036ae..5434cf4 100644 --- a/src/main/java/com/example/comma/domain/card/controller/CardController.java +++ b/src/main/java/com/example/comma/domain/card/controller/CardController.java @@ -1,6 +1,6 @@ package com.example.comma.domain.card.controller; -import com.example.comma.domain.card.dto.request.CardImageRequest; +import com.example.comma.domain.card.dto.request.CardInfoRequest; import com.example.comma.domain.card.dto.response.*; import com.example.comma.domain.card.service.CardService; import com.example.comma.domain.external.service.GeminiService; @@ -57,13 +57,21 @@ public ResponseEntity> generateResponse(@RequestParam(name = return SuccessResponse.ok(wordDatailsResponse); } + //UserCard 단어 카드 저장 @PostMapping("/{cardId}") - public ResponseEntity> createCard(@UserId Long userId, @PathVariable(name = "cardId") Long cardId, @RequestBody CardImageRequest cardImageRequest) { - cardService.saveCard(userId, cardId, cardImageRequest.cardImageUrl()); + public ResponseEntity> saveCard(@UserId Long userId, @PathVariable(name = "cardId") Long cardId, @RequestBody CardInfoRequest cardInfoRequest) { + cardService.saveCard(userId, cardId, cardInfoRequest); return SuccessResponse.created(null); } + //UserCard 개별 정보 조회 + @GetMapping("/my-card/{userCardId}") + public ResponseEntity> getMyCard(@PathVariable(name = "userCardId") Long userCardId) { + MyCardResponseDto myCard = cardService.getMyCard(userCardId); + return SuccessResponse.ok(myCard); + } + @GetMapping("/lastest") public ResponseEntity> getLastestCard(@UserId Long userId) { List CardImage = cardService.getLatestCard(userId); diff --git a/src/main/java/com/example/comma/domain/card/dto/request/CardImageRequest.java b/src/main/java/com/example/comma/domain/card/dto/request/CardImageRequest.java deleted file mode 100644 index 5b4963f..0000000 --- a/src/main/java/com/example/comma/domain/card/dto/request/CardImageRequest.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.example.comma.domain.card.dto.request; - -public record CardImageRequest( - String cardImageUrl -) { -} diff --git a/src/main/java/com/example/comma/domain/card/dto/request/CardInfoRequest.java b/src/main/java/com/example/comma/domain/card/dto/request/CardInfoRequest.java new file mode 100644 index 0000000..50ce7b9 --- /dev/null +++ b/src/main/java/com/example/comma/domain/card/dto/request/CardInfoRequest.java @@ -0,0 +1,7 @@ +package com.example.comma.domain.card.dto.request; + +public record CardInfoRequest( + String cardImageUrl, + String signLanguageDescription +) { +} diff --git a/src/main/java/com/example/comma/domain/card/dto/response/MyCardResponseDto.java b/src/main/java/com/example/comma/domain/card/dto/response/MyCardResponseDto.java new file mode 100644 index 0000000..8225bea --- /dev/null +++ b/src/main/java/com/example/comma/domain/card/dto/response/MyCardResponseDto.java @@ -0,0 +1,9 @@ +package com.example.comma.domain.card.dto.response; + +public record MyCardResponseDto( + String name, + String cardImageUrl, + String signImageUrl, + String signLanguageDescription +) { +} diff --git a/src/main/java/com/example/comma/domain/card/entity/Card.java b/src/main/java/com/example/comma/domain/card/entity/Card.java index 77e09f2..eafd27b 100644 --- a/src/main/java/com/example/comma/domain/card/entity/Card.java +++ b/src/main/java/com/example/comma/domain/card/entity/Card.java @@ -22,7 +22,6 @@ public class Card { private String signImageUrl; - @OneToMany(mappedBy = "card") private List userCardList; diff --git a/src/main/java/com/example/comma/domain/card/entity/UserCard.java b/src/main/java/com/example/comma/domain/card/entity/UserCard.java index 4c1bf8a..90e1046 100644 --- a/src/main/java/com/example/comma/domain/card/entity/UserCard.java +++ b/src/main/java/com/example/comma/domain/card/entity/UserCard.java @@ -29,14 +29,16 @@ public class UserCard extends BaseTimeEntity { private Boolean cardRegistration; - private String CardImageUrl; + private String cardImageUrl; + private String signLanguageDescription; - public UserCard(User user, Card card, Boolean quizParticipation, Boolean cardRegistration, String CardImageUrl) { + public UserCard(User user, Card card, Boolean quizParticipation, Boolean cardRegistration, String cardImageUrl, String signLanguageDescription) { this.user = user; this.card = card; this.quizParticipation = quizParticipation; this.cardRegistration = cardRegistration; - this.CardImageUrl = CardImageUrl; + this.cardImageUrl = cardImageUrl; + this.signLanguageDescription = signLanguageDescription; } public void setQuizParticipation(Boolean quizParticipation) { diff --git a/src/main/java/com/example/comma/domain/card/service/CardService.java b/src/main/java/com/example/comma/domain/card/service/CardService.java index 37581b2..3e43f37 100644 --- a/src/main/java/com/example/comma/domain/card/service/CardService.java +++ b/src/main/java/com/example/comma/domain/card/service/CardService.java @@ -1,7 +1,9 @@ package com.example.comma.domain.card.service; +import com.example.comma.domain.card.dto.request.CardInfoRequest; import com.example.comma.domain.card.dto.response.CardResponseDto; import com.example.comma.domain.card.dto.response.CorrectCardResponseDto; +import com.example.comma.domain.card.dto.response.MyCardResponseDto; import com.example.comma.domain.card.dto.response.WrongCardResponseDto; import com.example.comma.domain.card.entity.Card; import com.example.comma.domain.card.entity.UserCard; @@ -51,7 +53,7 @@ public void registerCard(String name, String signImageUrl) { } - public void saveCard(Long userId, Long cardId, String cardImageUrl) { + public void saveCard(Long userId, Long cardId, CardInfoRequest cardInfoRequest) { User user = userRepository.findById(userId) .orElseThrow(() -> new EntityNotFoundException(ErrorCode.USER_NOT_FOUND)); @@ -62,7 +64,7 @@ public void saveCard(Long userId, Long cardId, String cardImageUrl) { throw new ConflictException(ErrorCode.USER_CARD_ALREADY_EXISTS); } - UserCard userCard = new UserCard(user, card, false, true, cardImageUrl); + UserCard userCard = new UserCard(user, card, false, true, cardInfoRequest.cardImageUrl(), cardInfoRequest.signLanguageDescription()); userCardRepository.save(userCard); } @@ -158,4 +160,12 @@ public List getTop5Cards(Long userId) { List userCards = userCardRepository.findTop5ByUserIdOrderByCreateDateDesc(userId); return convertToCardResponseDtos(userCards); } + + public MyCardResponseDto getMyCard(Long userCardId) { + UserCard userCard = userCardRepository.findById(userCardId) + .orElseThrow(() -> new EntityNotFoundException(ErrorCode.USER_CARD_NOT_FOUND)); + + Card card = userCard.getCard(); + return new MyCardResponseDto(card.getName(), userCard.getCardImageUrl(),card.getSignImageUrl(), userCard.getSignLanguageDescription()); + } } From 9f3326faaa8d83b1af47afae8088c54e02104715 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Thu, 2 May 2024 21:04:55 +0900 Subject: [PATCH 26/29] =?UTF-8?q?[feat]=20=EA=B0=90=EC=A0=95=ED=91=9C?= =?UTF-8?q?=ED=98=84=20enum=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comma/domain/user/entity/Emotion.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/java/com/example/comma/domain/user/entity/Emotion.java diff --git a/src/main/java/com/example/comma/domain/user/entity/Emotion.java b/src/main/java/com/example/comma/domain/user/entity/Emotion.java new file mode 100644 index 0000000..7487660 --- /dev/null +++ b/src/main/java/com/example/comma/domain/user/entity/Emotion.java @@ -0,0 +1,19 @@ +package com.example.comma.domain.user.entity; + +public enum Emotion { + SOSO("그저 그래요"), + ANGRY("화나요"), + SAD("슬퍼요"), + PEACEFUL("평온해요"), + DESPRESS("우울해요"), + HAPPY("행복해요"), + FULLFIILED("뿌듯해요"), + ANXIOUS("불안해요"), + NONE("모르겠어요"); + + private final String emotion; + + Emotion(String emotion) { + this.emotion = emotion; + } +} From 20392d757331492e6783a809179e05b404c41daf Mon Sep 17 00:00:00 2001 From: hysong4u Date: Thu, 2 May 2024 22:37:00 +0900 Subject: [PATCH 27/29] =?UTF-8?q?[feat]=20=EA=B0=90=EC=A0=95=ED=91=9C?= =?UTF-8?q?=ED=98=84=20enum=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/comma/domain/user/entity/Emotion.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/example/comma/domain/user/entity/Emotion.java b/src/main/java/com/example/comma/domain/user/entity/Emotion.java index 7487660..b780679 100644 --- a/src/main/java/com/example/comma/domain/user/entity/Emotion.java +++ b/src/main/java/com/example/comma/domain/user/entity/Emotion.java @@ -11,9 +11,13 @@ public enum Emotion { ANXIOUS("불안해요"), NONE("모르겠어요"); - private final String emotion; + private final String koreanEmotion; - Emotion(String emotion) { - this.emotion = emotion; + Emotion(String koreanEmotion) { + this.koreanEmotion = koreanEmotion; + } + + public String getKoreanEmotion() { + return koreanEmotion; } } From dbed0425fbac743a42904d5bf9a8fbb236a450a5 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Thu, 2 May 2024 22:37:20 +0900 Subject: [PATCH 28/29] =?UTF-8?q?[feat]=20=EA=B0=90=EC=A0=95=ED=91=9C?= =?UTF-8?q?=ED=98=84=20=EB=93=B1=EB=A1=9D=20=EB=B0=8F=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20api=20dto=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/dto/request/EmotionRequest.java | 9 +++++++++ .../domain/user/dto/response/EmotionResponse.java | 12 ++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 src/main/java/com/example/comma/domain/user/dto/request/EmotionRequest.java create mode 100644 src/main/java/com/example/comma/domain/user/dto/response/EmotionResponse.java diff --git a/src/main/java/com/example/comma/domain/user/dto/request/EmotionRequest.java b/src/main/java/com/example/comma/domain/user/dto/request/EmotionRequest.java new file mode 100644 index 0000000..d0aa602 --- /dev/null +++ b/src/main/java/com/example/comma/domain/user/dto/request/EmotionRequest.java @@ -0,0 +1,9 @@ +package com.example.comma.domain.user.dto.request; + +import com.example.comma.domain.user.entity.Emotion; + +public record EmotionRequest( + Emotion parentEmotion, + Emotion childEmotion +) { +} diff --git a/src/main/java/com/example/comma/domain/user/dto/response/EmotionResponse.java b/src/main/java/com/example/comma/domain/user/dto/response/EmotionResponse.java new file mode 100644 index 0000000..b2ce27a --- /dev/null +++ b/src/main/java/com/example/comma/domain/user/dto/response/EmotionResponse.java @@ -0,0 +1,12 @@ +package com.example.comma.domain.user.dto.response; + +import com.example.comma.domain.user.entity.Emotion; +import lombok.Builder; + +@Builder +public record EmotionResponse( + String parentEmotion, + String childEmotion +) { + +} From 76320e9c2e9d447a7330a09a76b065fcfbff5532 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Thu, 2 May 2024 22:37:32 +0900 Subject: [PATCH 29/29] =?UTF-8?q?[feat]=20=EA=B0=90=EC=A0=95=ED=91=9C?= =?UTF-8?q?=ED=98=84=20=EB=93=B1=EB=A1=9D=20=EB=B0=8F=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserController.java | 15 +++++++- .../comma/domain/user/entity/User.java | 16 +++++++++ .../domain/user/service/UserService.java | 35 +++++++++++++++++-- 3 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/example/comma/domain/user/controller/UserController.java b/src/main/java/com/example/comma/domain/user/controller/UserController.java index 5c6fe06..abd1528 100644 --- a/src/main/java/com/example/comma/domain/user/controller/UserController.java +++ b/src/main/java/com/example/comma/domain/user/controller/UserController.java @@ -1,10 +1,11 @@ package com.example.comma.domain.user.controller; -import ch.qos.logback.core.net.HardenedObjectInputStream; import com.example.comma.domain.card.dto.response.CardResponseDto; import com.example.comma.domain.card.service.CardService; import com.example.comma.domain.fairytale.dto.Top2FairytaleResponseDto; import com.example.comma.domain.fairytale.service.FairytaleService; +import com.example.comma.domain.user.dto.request.EmotionRequest; +import com.example.comma.domain.user.dto.response.EmotionResponse; import com.example.comma.domain.user.dto.response.HomepageResponseDto; import com.example.comma.domain.user.service.UserService; import com.example.comma.global.common.SuccessResponse; @@ -53,6 +54,18 @@ public ResponseEntity> getHome(@UserId Long userId) { return SuccessResponse.ok(responseData); } + @PostMapping("/emotion") + public ResponseEntity> registerEmotion(@UserId Long userId, @RequestBody EmotionRequest emotionRequest){ + userService.registerEmotion(userId, emotionRequest); + return SuccessResponse.created(null); + } + + @GetMapping("/emotion") + public ResponseEntity> getEmotion(@UserId Long userId){ + EmotionResponse emotionResponse = userService.getEmotion(userId); + return SuccessResponse.ok(emotionResponse); + } + } diff --git a/src/main/java/com/example/comma/domain/user/entity/User.java b/src/main/java/com/example/comma/domain/user/entity/User.java index 2a6fc61..22f3957 100644 --- a/src/main/java/com/example/comma/domain/user/entity/User.java +++ b/src/main/java/com/example/comma/domain/user/entity/User.java @@ -30,6 +30,14 @@ public class User extends BaseTimeEntity { private String profileImage; + @Enumerated(EnumType.STRING) + @Column(name = "parent_emotion") + private Emotion parentEmotion; + + @Enumerated(EnumType.STRING) + @Column(name = "child_emotion") + private Emotion childEmotion; + @Column(nullable = false) private String socialId; @@ -50,5 +58,13 @@ public User(String socialId, String name, String email, String profileImage) { public void setNickname(String nickname) { this.nickname = nickname; } + + public void setParentEmotion(Emotion parentEmotion) { + this.parentEmotion = parentEmotion; + } + + public void setChildEmotion(Emotion childEmotion) { + this.childEmotion = childEmotion; + } } diff --git a/src/main/java/com/example/comma/domain/user/service/UserService.java b/src/main/java/com/example/comma/domain/user/service/UserService.java index d1b8f0e..9f6b161 100644 --- a/src/main/java/com/example/comma/domain/user/service/UserService.java +++ b/src/main/java/com/example/comma/domain/user/service/UserService.java @@ -1,19 +1,21 @@ package com.example.comma.domain.user.service; -import com.example.comma.domain.card.dto.response.CardResponseDto; import com.example.comma.domain.card.entity.UserCard; import com.example.comma.domain.card.repository.UserCardRepository; -import com.example.comma.domain.card.service.CardService; import com.example.comma.domain.fairytale.entity.UserFairytale; import com.example.comma.domain.fairytale.repository.UserFairytaleRepository; +import com.example.comma.domain.user.dto.request.EmotionRequest; +import com.example.comma.domain.user.dto.response.EmotionResponse; import com.example.comma.domain.user.dto.response.HomepageResponseDto; import com.example.comma.domain.user.dto.response.UserTokenResponseDto; +import com.example.comma.domain.user.entity.Emotion; import com.example.comma.domain.user.entity.User; import com.example.comma.domain.user.repository.UserRepository; import com.example.comma.global.config.auth.jwt.JwtProvider; import com.example.comma.global.error.ErrorCode; import com.example.comma.global.error.exception.EntityNotFoundException; import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -103,7 +105,36 @@ private static String getRandomElement(String[] array) { return array[randomIndex]; } + @Scheduled(cron = "0 0 0 * * ?") + public void initEmotion() { + userRepository.findAll().forEach(user -> { + user.setParentEmotion(null); + user.setChildEmotion(null); + userRepository.save(user); + }); + } + + + public void registerEmotion(Long userId, EmotionRequest emotionRequest) { + System.out.println(emotionRequest); + User user = userRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException(ErrorCode.USER_NOT_FOUND)); + user.setParentEmotion(emotionRequest.parentEmotion()); + user.setChildEmotion(emotionRequest.childEmotion()); + } + public EmotionResponse getEmotion(Long userId) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException(ErrorCode.USER_NOT_FOUND)); + + Emotion parentEmotion = user.getParentEmotion(); + Emotion childEmotion = user.getChildEmotion(); + + return EmotionResponse.builder() + .parentEmotion(parentEmotion.getKoreanEmotion()) + .childEmotion(childEmotion.getKoreanEmotion()) + .build(); + } }