diff --git a/build.gradle b/build.gradle index 027092f..cc446f0 100644 --- a/build.gradle +++ b/build.gradle @@ -66,6 +66,12 @@ dependencies { // JSON implementation 'com.googlecode.json-simple:json-simple:1.1.1' + + + + + implementation 'org.apache.httpcomponents:httpclient:4.5.13' + } tasks.named('bootBuildImage') { diff --git a/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java b/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java index eef97be..014c5f7 100644 --- a/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java +++ b/src/main/java/org/kau/kkoolbeeServer/KkoolbeeServerApplication.java @@ -16,7 +16,7 @@ public static void main(String[] args) { TimeZone.setDefault(TimeZone.getTimeZone("UTC")); TimeZone tz = TimeZone.getDefault(); - System.out.println("현재 시간대: " + tz.getID()); + } diff --git a/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java b/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java index 937707e..7e04be9 100644 --- a/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java +++ b/src/main/java/org/kau/kkoolbeeServer/S3/S3UploaderService.java @@ -10,6 +10,7 @@ import org.springframework.web.multipart.MultipartFile; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.MessageDigest; @@ -50,6 +51,18 @@ public String upload(MultipartFile file) throws IOException, NoSuchAlgorithmExce return amazonS3Client.getUrl(bucketName, fileName).toString(); } + public String uploadFile(File file) throws IOException, NoSuchAlgorithmException { + String fileHash = calculateFileHash(file); + String fileName = fileHash + "-" + file.getName(); + + if (!amazonS3Client.doesObjectExist(bucketName, fileName)) { + amazonS3Client.putObject(new PutObjectRequest(bucketName, fileName, file)); + } + + return amazonS3Client.getUrl(bucketName, fileName).toString(); + + } + private String calculateFileHash(MultipartFile file) throws IOException, NoSuchAlgorithmException { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] fileBytes = file.getBytes(); @@ -57,6 +70,17 @@ private String calculateFileHash(MultipartFile file) throws IOException, NoSuchA return Hex.encodeHexString(hash); } + private String calculateFileHash(File file) throws IOException, NoSuchAlgorithmException { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + try (FileInputStream fis = new FileInputStream(file)) { + byte[] fileBytes = new byte[(int) file.length()]; + fis.read(fileBytes); + byte[] hash = digest.digest(fileBytes); + return Hex.encodeHexString(hash); + } + } + + diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java index b823556..8aa8196 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/controller/DiaryController.java @@ -1,5 +1,7 @@ package org.kau.kkoolbeeServer.domain.diary.controller; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.validation.Valid; import org.kau.kkoolbeeServer.S3.S3UploaderService; import org.kau.kkoolbeeServer.domain.advice.dto.AdviceResponseDto; @@ -37,6 +39,7 @@ import java.util.Optional; import java.util.stream.Collectors; + @RestController public class DiaryController { private static final Logger logger = LoggerFactory.getLogger(DiaryController.class); @@ -59,7 +62,20 @@ public DiaryController(DiaryService diaryService,S3UploaderService s3UploaderSer @PostMapping("/api/diary/content") public ResponseEntity> getDiaryContents(@RequestHeader(value = "Authorization") String authHeader,@RequestBody DiaryContentRequestDto diaryContentRequestDto) { + try{ + Long memberId=extractMemberIdFromRequestHeader(authHeader); + + } + catch(IllegalArgumentException e){ + return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) + .body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); + } + catch(RuntimeException e){ + e.printStackTrace(); + return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) + .body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); + } Long diaryId = diaryContentRequestDto.getDiaryId(); Optional diaryOptional = diaryService.findDiaryById(diaryId); @@ -85,37 +101,17 @@ public ResponseEntity> getDiaryContents(@RequestHeader(value = "A return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseDto)); } else { return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) - .body(ApiResponse.error(ErrorType.REQUEST_VALIDATION_ERROR)); + .body(ApiResponse.error(ErrorType.NO_DIARY)); //diary 가 null 일 경우 요청이상함 반환 } } - /* @PostMapping("/api/diary/list/calendar") - public ResponseEntity> getDiariesByMonth(@RequestBody CurrentDateRequestDto requestDto){ - LocalDateTime currentDate = requestDto.getCurrentDate(); - - - Listdiaries=diaryService.findDiariesByMonth(currentDate); - if(diaries.isEmpty()){ - return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) - .body(ApiResponse.error(ErrorType.REQUEST_VALIDATION_ERROR, "해당 월에 대한 일기가 존재하지 않습니다.")); - } - - List diaryDtos=diaries.stream() - .map(diary -> new CalenderDiaryResponseDto(diary.getId(), diary.getTitle(), diary.getWritedAt())) - .collect(Collectors.toList()); - - Map> responseMap= Map.of("monthList",diaryDtos); - return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseMap));*/ @PostMapping("/api/diary/list/calendar") public ResponseEntity> getDiariesByMonth(@RequestHeader("Authorization") String authHeader,@RequestBody CurrentDateRequestDto requestDto){ - String accessToken = null; - if (authHeader != null && authHeader.startsWith("Bearer ")) { - accessToken = authHeader.substring(7); - } - Long memberId= jwtProvider.getUserFromJwt(accessToken); + + Long memberId= extractMemberIdFromRequestHeader(authHeader); LocalDateTime currentDate = requestDto.getCurrentDate(); @@ -134,45 +130,14 @@ public ResponseEntity> getDiariesByMonth(@RequestHeader("Authoriz return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseMap)); - - } - /*@PostMapping("/api/diary/list/feeling") - public ResponseEntity> getDiariesByFeeling( @RequestBody FeelingListRequestDto requestDto) { - - - List diaries = diaryService.findDiariesByFeeling(requestDto.getFeeling()); - - if (diaries.isEmpty()) { - return ResponseEntity.status(ErrorType.REQUEST_VALIDATION_ERROR.getHttpStatus()) - .body(ApiResponse.error(ErrorType.REQUEST_VALIDATION_ERROR, "해당 감정에 대한 일기가 존재하지 않습니다.")); - } - - List feelingList = diaries.stream() - .map(diary -> new FeelingListResponseDto(diary.getId(), diary.getWritedAt(), diary.getTitle())) - .collect(Collectors.toList()); - - Map> responseMap = Map.of("feelingList", feelingList); - return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED, responseMap)); - - }*/ - @PostMapping("/api/diary/list/feeling") public ResponseEntity> getDiariesByFeeling( @RequestHeader(value = "Authorization") String authHeader,@RequestBody FeelingListRequestDto requestDto) { - String accessToken = null; - if (authHeader != null && authHeader.startsWith("Bearer ")) { - accessToken = authHeader.substring(7); - } - else{ - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); - - } - - Long memberId= jwtProvider.getUserFromJwt(accessToken); + Long memberId= extractMemberIdFromRequestHeader(authHeader); Feeling feeling=Feeling.valueOf(requestDto.getFeeling()); List diaries = diaryService.findDiariesByMemberIdAndFeeling(memberId,feeling); @@ -216,25 +181,13 @@ public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = @RequestPart(value = "diaryContent") String diaryContent){ try { - String accessToken = null; - if (authHeader != null && authHeader.startsWith("Bearer ")) { - accessToken = authHeader.substring(7); - } - else{ - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); - - } - + Long memberId=extractMemberIdFromRequestHeader(authHeader); String imageUrl=null; - if(image!=null && !image.isEmpty() ){ imageUrl = s3UploaderService.upload(image); } - - - Long memberId= jwtProvider.getUserFromJwt(accessToken); Member member= memberService.findByIdOrThrow(memberId); Diary diary = new Diary(); diary.setTitle(diaryTitle); @@ -247,77 +200,19 @@ public ResponseEntity> createSlowTypeDiary(@RequestHeader(value = Diary savedDiary=diaryService.saveDiary(diary); - SlowTypeCreateResponseDto responseDto=new SlowTypeCreateResponseDto(diary.getId(),diary.getContent(),diary.getTitle(),diary.getImageurl()); + SlowTypeCreateResponseDto responseDto=new SlowTypeCreateResponseDto(diary.getId(),diary.getContent(),diary.getTitle(),diary.getImageurl(),diary.getWritedAt()); return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,responseDto)); - } catch (Exception e) { - e.printStackTrace(); - - - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR,e.getMessage())); - } - } - -/* @PatchMapping("/api/diary/update") - public ResponseEntity updateDiary( - @RequestHeader(value = "Authorization") String authHeader, - @RequestPart(value = "imageUrl", required = false) MultipartFile imageFile, - @RequestPart(value="diaryId") Long diaryId, - @RequestPart(value = "diaryTitle") String diaryTitle, - @RequestPart(value = "diaryContent") String diaryContent) { - - logger.info("Authorization Header: {}", authHeader); - logger.info("Received diaryId: {}", diaryId); - logger.info("Received diaryTitle: {}", diaryTitle); - logger.info("Received diaryContent: {}", diaryContent); - - - - try{ - - String accessToken = null; - if (authHeader != null && authHeader.startsWith("Bearer ")) { - accessToken = authHeader.substring(7); - Long memberID= jwtProvider.getUserFromJwt(accessToken); - Diary diary=diaryService.findDiaryById(diaryId).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); - if(diary.getMember().getId()!=memberID){ - - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.NOT_YOUR_DIARY)); + }catch (IllegalArgumentException e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); } - } - else{ - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); - - } - String imageUrl=null; - if (imageFile!=null && !imageFile.isEmpty()){ - imageUrl=s3UploaderService.upload(imageFile); - - UpdateDiaryResponseDto responseDto=diaryService.updateDiary(diaryId,diaryContent,diaryTitle,imageUrl); - return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,responseDto)); - - } - else{ - UpdateDiaryResponseDto responseDto=diaryService.updateDiaryWithoutImage(diaryId,diaryContent,diaryTitle); - return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,responseDto)); - - - } - - - } - catch (Exception e){ - - logger.error("An error occurred while updating diary", e); - e.printStackTrace(); - - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR,e.getMessage())); - } - - - + catch(Exception e){ + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR)); + } + } - }*/ @PatchMapping("/api/diary/update") public ResponseEntity updateDiary( @RequestHeader(value = "Authorization") String authHeader, @@ -327,24 +222,19 @@ public ResponseEntity updateDiary( String diaryTitle= requestDto.getDiaryTitle(); String diaryContent= requestDto.getDiaryContent(); - - try{ String accessToken = null; - if (authHeader != null && authHeader.startsWith("Bearer ")) { - accessToken = authHeader.substring(7); - Long memberID= jwtProvider.getUserFromJwt(accessToken); - Diary diary=diaryService.findDiaryById(requestDto.getDiaryId()).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); - if(diary.getMember().getId()!=memberID){ + Long memberId=extractMemberIdFromRequestHeader(authHeader); + accessToken = authHeader.substring(7); + + Diary diary=diaryService.findDiaryById(requestDto.getDiaryId()).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); + if(diary.getMember().getId()!=memberId){ return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.NOT_YOUR_DIARY)); - } } - else{ - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); - } + String imageUrl=null; if (imageFile!=null && !imageFile.isEmpty()){ @@ -361,7 +251,9 @@ public ResponseEntity updateDiary( } - + } + catch (IllegalArgumentException e){ + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); } catch (Exception e){ logger.error("An error occurred while updating diary", e); @@ -371,37 +263,61 @@ public ResponseEntity updateDiary( } - - } @DeleteMapping("api/diary/delete") - public ResponseEntitydeleteDiary(@RequestHeader(value = "Authorization") String authHeader,@RequestBody DiaryDeleteRequestDto diaryDeleteRequestDto){ - String accessToken = null; - if (authHeader != null && authHeader.startsWith("Bearer ")) { - accessToken = authHeader.substring(7); - Long memberID= jwtProvider.getUserFromJwt(accessToken); - Diary diary=diaryService.findDiaryById(diaryDeleteRequestDto.getDiaryId()).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); - if(diary.getMember().getId()!=memberID){ - + public ResponseEntitydeleteDiary(@RequestHeader(value = "Authorization") String authHeader,@RequestBody String rawBody){ + ObjectMapper objectMapper = new ObjectMapper(); + try { + JsonNode rootNode = objectMapper.readTree(rawBody); + JsonNode diaryIdNode = rootNode.get("diaryId"); + if (diaryIdNode == null) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Invalid request body"); + } + Long diaryId = diaryIdNode.asLong(); + Long memberId = extractMemberIdFromRequestHeader(authHeader); + Diary diary = diaryService.findDiaryById(diaryId) + .orElseThrow(() -> new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); + if (!diary.getMember().getId().equals(memberId)) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.NOT_YOUR_DIARY)); } - } - else{ - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); - - } - diaryService.deleteDiary(diaryDeleteRequestDto.getDiaryId()); - return ResponseEntity.status(HttpStatus.OK).body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED)); - - - + diaryService.deleteDiary(diaryId); + return ResponseEntity.status(HttpStatus.OK).body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED)); + } catch (IllegalArgumentException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error parsing request by"); + } +} + @PostMapping("/api/diary/image") + public ResponseEntity imageCreate(@RequestHeader("Authorization") String authHeader,@RequestBody ImageCreateRequestDto imageCreateRequestDto){ + try{ + Long memberId=extractMemberIdFromRequestHeader(authHeader); + ImageCreateResponseDto responseDto=diaryService.generateImageFromDiary(imageCreateRequestDto.getDiaryId()); + return ResponseEntity.ok().body(ApiResponse.success(SuccessType.PROCESS_SUCCESSED,responseDto)); + } + catch (IllegalArgumentException e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.error(ErrorType.INVALID_HTTP_REQUEST_ERROR)); + } + catch(Exception e){ + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR)); + } + } -} + public Long extractMemberIdFromRequestHeader(String authHeader) { + if (authHeader != null && authHeader.startsWith("Bearer ")) { + String accessToken = authHeader.substring(7); + return jwtProvider.getUserFromJwt(accessToken); + } else { + throw new IllegalArgumentException("Invalid Authorization header"); + } + } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/ImageCreateRequestDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/ImageCreateRequestDto.java new file mode 100644 index 0000000..7d152fa --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/ImageCreateRequestDto.java @@ -0,0 +1,14 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.request; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@Getter +@Setter +@NoArgsConstructor +public class ImageCreateRequestDto { + private Long diaryId; +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/OpenAiImageGenerationRequestDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/OpenAiImageGenerationRequestDto.java new file mode 100644 index 0000000..15ace21 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/request/OpenAiImageGenerationRequestDto.java @@ -0,0 +1,25 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.request; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class OpenAiImageGenerationRequestDto { + private String model; + private String prompt; + private int n; + + private String size; + + @Builder + public OpenAiImageGenerationRequestDto(String model,String prompt, int n, String size) { + this.model=model; + this.prompt = prompt; + this.n = n; + this.size = size; + } +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/ImageCreateResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/ImageCreateResponseDto.java new file mode 100644 index 0000000..c02ddc5 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/ImageCreateResponseDto.java @@ -0,0 +1,16 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +public class ImageCreateResponseDto { + + private Long diaryId; + private String image_url; + +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/OpenAiImageGenerationResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/OpenAiImageGenerationResponseDto.java new file mode 100644 index 0000000..3b90a90 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/OpenAiImageGenerationResponseDto.java @@ -0,0 +1,20 @@ +package org.kau.kkoolbeeServer.domain.diary.dto.response; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Getter +@NoArgsConstructor +public class OpenAiImageGenerationResponseDto { + + private Long created; + private List data; + + @Getter + @NoArgsConstructor + public static class ImageData { + private String url; + } +} diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/SlowTypeCreateResponseDto.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/SlowTypeCreateResponseDto.java index 1b4ec3e..2137771 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/SlowTypeCreateResponseDto.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/dto/response/SlowTypeCreateResponseDto.java @@ -5,6 +5,8 @@ import lombok.Getter; import lombok.Setter; +import java.time.LocalDateTime; + @Getter @Setter @AllArgsConstructor @@ -14,4 +16,7 @@ public class SlowTypeCreateResponseDto { private String diaryContent; private String diaryTitle; private String imageUrl; + private LocalDateTime createdDate; + + } diff --git a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java index 757a7b1..9d7aee9 100644 --- a/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java +++ b/src/main/java/org/kau/kkoolbeeServer/domain/diary/service/DiaryService.java @@ -3,14 +3,30 @@ import org.kau.kkoolbeeServer.S3.S3UploaderService; import org.kau.kkoolbeeServer.domain.diary.Diary; import org.kau.kkoolbeeServer.domain.diary.Feeling; +import org.kau.kkoolbeeServer.domain.diary.dto.request.OpenAiImageGenerationRequestDto; +import org.kau.kkoolbeeServer.domain.diary.dto.response.ImageCreateResponseDto; +import org.kau.kkoolbeeServer.domain.diary.dto.response.OpenAiImageGenerationResponseDto; import org.kau.kkoolbeeServer.domain.diary.dto.response.UpdateDiaryResponseDto; import org.kau.kkoolbeeServer.domain.diary.repository.DiaryRepository; +import org.kau.kkoolbeeServer.global.config.OpenAiConfig; import org.springframework.beans.factory.annotation.Autowired; +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.http.ResponseEntity; +import org.springframework.mock.web.MockMultipartFile; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; - -import javax.swing.text.html.Option; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.NoSuchAlgorithmException; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -20,14 +36,19 @@ @Service public class DiaryService { + @Value("${openai.api.key}") + private String apiKey; + private RestTemplate restTemplate; private DiaryRepository diaryRepository; private S3UploaderService s3UploaderService; @Autowired - public DiaryService(DiaryRepository diaryRepository,S3UploaderService s3UploaderService) { + public DiaryService(RestTemplate restTemplate, DiaryRepository diaryRepository,S3UploaderService s3UploaderService) { + this.restTemplate=restTemplate; this.diaryRepository = diaryRepository; this.s3UploaderService=s3UploaderService; + } public Optional findDiaryById(Long diary_id){ @@ -36,12 +57,6 @@ public Optional findDiaryById(Long diary_id){ } - /* public List findDiariesByMonth(LocalDateTime date) { - LocalDateTime startOfMonth = date.withDayOfMonth(1).toLocalDate().atStartOfDay(); - LocalDateTime endOfMonth = startOfMonth.plusMonths(1).minusSeconds(1); - return diaryRepository.findByWritedAtBetween(startOfMonth, endOfMonth); - }*/ - public List findDiariesByMonthAndMemberId(LocalDateTime date, Long memberId) { LocalDateTime startOfMonth = date.withDayOfMonth(1).toLocalDate().atStartOfDay(); LocalDateTime endOfMonth = startOfMonth.plusMonths(1).minusSeconds(1); @@ -50,10 +65,7 @@ public List findDiariesByMonthAndMemberId(LocalDateTime date, Long member - /* public List findDiariesByFeeling(String feeling) { - return diaryRepository.findByFeeling(Feeling.valueOf(feeling)); - } -*/ + public List findDiariesByMemberIdAndFeeling(Long memberId,Feeling feeling) { return diaryRepository.findByMemberIdAndFeeling(memberId,feeling); } @@ -123,5 +135,98 @@ public void deleteDiary(Long diaryId){ } + public ImageCreateResponseDto generateImageFromDiary(Long diaryId) throws IOException, NoSuchAlgorithmException{ + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.parseMediaType(OpenAiConfig.MEDIA_TYPE)); + httpHeaders.add(OpenAiConfig.AUTHORIZATION, OpenAiConfig.BEARER + apiKey); + Diary diary=findDiaryById(diaryId).orElseThrow(()->new NoSuchElementException("해당 ID의 일기를 찾을 수 없습니다.")); + String formattedPrompt=formatPromptForImageGeneration(diary.getContent()); + + OpenAiImageGenerationRequestDto requestDto= + OpenAiImageGenerationRequestDto.builder() + .model(OpenAiConfig.MODEL) + .prompt(formattedPrompt) + .n(OpenAiConfig.IMAGE_COUNT) + .size(OpenAiConfig.IMAGE_SIZE) + .build(); + + HttpEntity requestDtoHttpEntity=new HttpEntity<>(requestDto,httpHeaders); + + ResponseEntity responseEntity = + restTemplate.postForEntity(OpenAiConfig.IMAGE_URL, + requestDtoHttpEntity, + OpenAiImageGenerationResponseDto.class); + + OpenAiImageGenerationResponseDto responseBody = responseEntity.getBody(); + if (responseBody != null && !responseBody.getData().isEmpty()) { + String imageUrl = responseBody.getData().get(0).getUrl(); + File downloadedFile=downloadFileFromUrl(imageUrl); + String s3Url = s3UploaderService.uploadFile(downloadedFile); + diary.setImageurl(s3Url); + diaryRepository.save(diary); + downloadedFile.delete(); + return new ImageCreateResponseDto(diary.getId(), diary.getImageurl()); + + + } else { + throw new RuntimeException("이미지 생성에 실패했습니다."); + } + + + } + + private File downloadFileFromUrl(String fileUrl) throws IOException { + URL url = new URL(fileUrl); + InputStream inputStream = url.openStream(); + File tempFile = File.createTempFile("image-", ".png"); + + try (FileOutputStream outputStream = new FileOutputStream(tempFile)) { + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + } finally { + inputStream.close(); + } + return tempFile; + } + + private String formatPromptForImageGeneration(String diaryContent) { + + String formattedPrompt = String.format(""" +유저의 diary 내용 : %s + +유저의 diary 내용을 요약해주세요. 아래는 예시입니다. +예시 + { 내용 : 2024년 4월 1일, 친구들과 함께 제주도의 한라산을 등반했다. + 한라산은 한국에서 가장 높은 산으로, 그 정상에서 바라보는 제주도의 풍경은 정말로 인상적이였다. + 제주도의 파란 바다와 울창한 숲이 한눈에 들어왔고, 그 아름다움에 모두가 말을 잃었다. + 우리는 등반하면서 서로를 의지하며 많은 얘기를 나누었고, 함께 등반한 친구들과 나는 더욱 가까워진 것을 느꼈다. + 우리는 서로에 대해 더 많이 알게 되었고, 이 여행이 우리 사이의 우정을 더욱 깊게 만들었다는 것을 깨달았다. + 한라산 등반은 단순한 여행 이상의 의미가 있었다. + 이 경험은 나에게 자연의 아름다움을 다시 한번 일깨워 주었고, 친구들과의 관계를 더욱 소중히 여기게 만들었다. + 이번 등반을 통해 얻은 추억과 교훈은 앞으로도 오랫동안 내 마음속에 남아있을 것이다. + 요약 : 친구들과 함께 제주도의 한라산을 등반했다. 한라산 정상에서의 풍경은 인상적이었고, 함께한 친구들과의 우정도 더 깊어졌다. + } +다음 요약된 내용을 바탕으로 다음과 같은 이미지를 생성해주세요. +이미지는 평면적인 2D 일러스트 스타일로, 유저의 감정을 반영하여 다음과 같은 색상을 주로 사용해주세요. +- 행복한 감정: 밝은 노란색과 분홍색 +- 슬픈 감정: 파란색 계열 +- 안정된 감정: 녹색 계열 +- 걱정되는 감정: 보라색 +- 놀란 감정: 오렌지색 +- 화난 감정: 분홍색 +캐릭터는 꿀벌 옷을 입고있는 곰을 모티브로 한 일러스트로, 사용자의 diary 내용에 맞는 포즈와 표정을 취하도록 해주세요. +다양한 요소들을 추가하기보단 불필요한 것들을 빼고 단조로운 스타일로 생성해주세요 +""", diaryContent); + + // 여기에 프롬프트를 포맷팅하는 로직을 구현합니다. + // 예를 들어, 특정 키워드를 추가하거나, 내용을 요약하는 등의 작업을 할 수 있습니다. + return formattedPrompt; // 일단은 단순히 일기 내용을 그대로 반환하도록 합니다. 실제 구현시에는 수정해야 합니다. + } + + + } diff --git a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java index be0aaf0..65551b4 100644 --- a/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java +++ b/src/main/java/org/kau/kkoolbeeServer/global/common/dto/enums/ErrorType.java @@ -43,6 +43,7 @@ public enum ErrorType { ADVICE_NOT_FOUND(HttpStatus.NOT_FOUND, "요청하신 Diary에 맞는 Advice가 없습니다."), NOT_FOUND_REFRESH_TOKEN_ERROR(HttpStatus.NOT_FOUND, "존재하지 않는 리프레시 토큰입니다."), NOT_YOUR_DIARY(HttpStatus.NOT_FOUND,"해당 Diary에 접근할 수 있는 권한이 없습니다."), + NO_DIARY(HttpStatus.NOT_FOUND,"해당 Id에 맞는 Diary가 존재하지 않습니다."), /** * 500 INTERNAL SERVER ERROR diff --git a/src/main/java/org/kau/kkoolbeeServer/global/config/AppConfig.java b/src/main/java/org/kau/kkoolbeeServer/global/config/AppConfig.java new file mode 100644 index 0000000..83593e9 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/config/AppConfig.java @@ -0,0 +1,18 @@ +package org.kau.kkoolbeeServer.global.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class AppConfig { + + @Bean + public RestTemplate restTemplate() { + RestTemplate restTemplate=new RestTemplate(); + // JSON 메시지 컨버터 추가 + restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter()); + return new RestTemplate(); + } +} diff --git a/src/main/java/org/kau/kkoolbeeServer/global/config/OpenAiConfig.java b/src/main/java/org/kau/kkoolbeeServer/global/config/OpenAiConfig.java new file mode 100644 index 0000000..dea8c98 --- /dev/null +++ b/src/main/java/org/kau/kkoolbeeServer/global/config/OpenAiConfig.java @@ -0,0 +1,17 @@ +package org.kau.kkoolbeeServer.global.config; + +import org.springframework.context.annotation.Configuration; + +@Configuration +public class OpenAiConfig { + public static final String AUTHORIZATION = "Authorization"; + public static final String BEARER = "Bearer "; + public static final String MEDIA_TYPE = "application/json; charset=UTF-8"; + + public static final String MODEL= "dall-e-3"; + public static final String IMAGE_URL = "https://api.openai.com/v1/images/generations"; + public static final int IMAGE_COUNT = 1;// 1~10 + public static final String IMAGE_SIZE = "1024x1024"; // '256x256', '512x512', '1024x1024' + + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 34f7983..cc310f3 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -24,4 +24,6 @@ logging: com: amazonaws: util: - EC2MetadataUtils: error \ No newline at end of file + EC2MetadataUtils: error +openai: + api.key: ${API_KEY} \ No newline at end of file