From 2417b62b8544b72471c4eddd32bd2d7d894e8664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=EC=9C=A0=EC=A7=84?= Date: Fri, 5 Jul 2024 04:34:45 +0900 Subject: [PATCH 1/3] feature: add image #5 --- .../exception/ArticleNotFoundException.java | 7 ++++ .../{domain => article}/Article.java | 7 ++-- .../TimeLapse/article/ArticleController.java | 10 ++--- .../TimeLapse/article/ArticleConverter.java | 2 - .../TimeLapse/article/ArticleRepository.java | 1 - .../TimeLapse/article/ArticleRequestDTO.java | 3 -- .../TimeLapse/article/ArticleService.java | 5 --- .../TimeLapse/article/ImageRepository.java | 7 ---- .../{domain => }/common/BaseEntity.java | 2 +- .../TimeLapse/{domain => image}/Image.java | 8 ++-- .../TimeLapse/image/ImageController.java | 25 +++++++++++++ .../TimeLapse/image/ImageConverter.java | 15 ++++++++ .../TimeLapse/image/ImageRepository.java | 7 ++++ .../TimeLapse/image/ImageRequestDTO.java | 14 +++++++ .../TimeLapse/image/ImageResponseDTO.java | 20 ++++++++++ .../TimeLapse/image/ImageService.java | 37 +++++++++++++++++++ .../TimeLapse/{domain => member}/Member.java | 13 ++++--- .../TimeLapse/member/MemberRepository.java | 4 +- .../hackathon/TimeLapse/s3/S3Controller.java | 7 ++++ .../com/hackathon/TimeLapse/s3/S3Service.java | 21 +++++++++++ 20 files changed, 174 insertions(+), 41 deletions(-) create mode 100644 src/main/java/com/hackathon/TimeLapse/apiPayload/exception/ArticleNotFoundException.java rename src/main/java/com/hackathon/TimeLapse/{domain => article}/Article.java (83%) delete mode 100644 src/main/java/com/hackathon/TimeLapse/article/ImageRepository.java rename src/main/java/com/hackathon/TimeLapse/{domain => }/common/BaseEntity.java (93%) rename src/main/java/com/hackathon/TimeLapse/{domain => image}/Image.java (71%) create mode 100644 src/main/java/com/hackathon/TimeLapse/image/ImageController.java create mode 100644 src/main/java/com/hackathon/TimeLapse/image/ImageConverter.java create mode 100644 src/main/java/com/hackathon/TimeLapse/image/ImageRepository.java create mode 100644 src/main/java/com/hackathon/TimeLapse/image/ImageRequestDTO.java create mode 100644 src/main/java/com/hackathon/TimeLapse/image/ImageResponseDTO.java create mode 100644 src/main/java/com/hackathon/TimeLapse/image/ImageService.java rename src/main/java/com/hackathon/TimeLapse/{domain => member}/Member.java (79%) diff --git a/src/main/java/com/hackathon/TimeLapse/apiPayload/exception/ArticleNotFoundException.java b/src/main/java/com/hackathon/TimeLapse/apiPayload/exception/ArticleNotFoundException.java new file mode 100644 index 0000000..5bce804 --- /dev/null +++ b/src/main/java/com/hackathon/TimeLapse/apiPayload/exception/ArticleNotFoundException.java @@ -0,0 +1,7 @@ +package com.hackathon.TimeLapse.apiPayload.exception; + +public class ArticleNotFoundException extends RuntimeException { + public ArticleNotFoundException(String message){ + super(message); + } +} diff --git a/src/main/java/com/hackathon/TimeLapse/domain/Article.java b/src/main/java/com/hackathon/TimeLapse/article/Article.java similarity index 83% rename from src/main/java/com/hackathon/TimeLapse/domain/Article.java rename to src/main/java/com/hackathon/TimeLapse/article/Article.java index efde9fd..f7d7dc6 100644 --- a/src/main/java/com/hackathon/TimeLapse/domain/Article.java +++ b/src/main/java/com/hackathon/TimeLapse/article/Article.java @@ -1,9 +1,10 @@ -package com.hackathon.TimeLapse.domain; +package com.hackathon.TimeLapse.article; -import com.hackathon.TimeLapse.domain.common.BaseEntity; +import com.hackathon.TimeLapse.common.BaseEntity; +import com.hackathon.TimeLapse.image.Image; +import com.hackathon.TimeLapse.member.Member; import jakarta.persistence.*; import lombok.*; -import org.springframework.data.jpa.domain.support.AuditingEntityListener; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/com/hackathon/TimeLapse/article/ArticleController.java b/src/main/java/com/hackathon/TimeLapse/article/ArticleController.java index 956eb77..007cde9 100644 --- a/src/main/java/com/hackathon/TimeLapse/article/ArticleController.java +++ b/src/main/java/com/hackathon/TimeLapse/article/ArticleController.java @@ -1,27 +1,23 @@ package com.hackathon.TimeLapse.article; import com.hackathon.TimeLapse.apiPayload.ApiResponse; -import com.hackathon.TimeLapse.domain.Article; -import com.hackathon.TimeLapse.s3.S3Service; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @RestController @RequiredArgsConstructor @Validated -@RequestMapping("/api") +@RequestMapping("/api/article") public class ArticleController { private final ArticleService articleService; - private final S3Service s3Service; - - @PostMapping("/article") + @PostMapping("/") public ApiResponse createArticle(@RequestBody @Valid ArticleRequestDTO.createArticleDTO request, @RequestParam(name = "memberId") Long memberId) { Article article = articleService.createArticle(memberId, request); return ApiResponse.onSuccess(ArticleConverter.toCreateReviewResultDTO(article)); } + } diff --git a/src/main/java/com/hackathon/TimeLapse/article/ArticleConverter.java b/src/main/java/com/hackathon/TimeLapse/article/ArticleConverter.java index 8ef7e3e..d075c5d 100644 --- a/src/main/java/com/hackathon/TimeLapse/article/ArticleConverter.java +++ b/src/main/java/com/hackathon/TimeLapse/article/ArticleConverter.java @@ -1,7 +1,5 @@ package com.hackathon.TimeLapse.article; -import com.hackathon.TimeLapse.domain.Article; - import java.time.LocalDateTime; public class ArticleConverter { diff --git a/src/main/java/com/hackathon/TimeLapse/article/ArticleRepository.java b/src/main/java/com/hackathon/TimeLapse/article/ArticleRepository.java index 36e9a3d..269bdd5 100644 --- a/src/main/java/com/hackathon/TimeLapse/article/ArticleRepository.java +++ b/src/main/java/com/hackathon/TimeLapse/article/ArticleRepository.java @@ -1,6 +1,5 @@ package com.hackathon.TimeLapse.article; -import com.hackathon.TimeLapse.domain.Article; import org.springframework.data.jpa.repository.JpaRepository; public interface ArticleRepository extends JpaRepository { diff --git a/src/main/java/com/hackathon/TimeLapse/article/ArticleRequestDTO.java b/src/main/java/com/hackathon/TimeLapse/article/ArticleRequestDTO.java index f64b561..a818356 100644 --- a/src/main/java/com/hackathon/TimeLapse/article/ArticleRequestDTO.java +++ b/src/main/java/com/hackathon/TimeLapse/article/ArticleRequestDTO.java @@ -4,9 +4,6 @@ import jakarta.validation.constraints.NotNull; import lombok.Getter; -import java.util.List; - - public class ArticleRequestDTO { @Getter diff --git a/src/main/java/com/hackathon/TimeLapse/article/ArticleService.java b/src/main/java/com/hackathon/TimeLapse/article/ArticleService.java index ee6c5e8..18b215a 100644 --- a/src/main/java/com/hackathon/TimeLapse/article/ArticleService.java +++ b/src/main/java/com/hackathon/TimeLapse/article/ArticleService.java @@ -1,14 +1,9 @@ package com.hackathon.TimeLapse.article; import com.hackathon.TimeLapse.apiPayload.exception.MemberNotFoundException; -import com.hackathon.TimeLapse.domain.Image; import com.hackathon.TimeLapse.member.MemberRepository; -import com.hackathon.TimeLapse.domain.Article; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; - @Service public class ArticleService { private final ArticleRepository articleRepository; diff --git a/src/main/java/com/hackathon/TimeLapse/article/ImageRepository.java b/src/main/java/com/hackathon/TimeLapse/article/ImageRepository.java deleted file mode 100644 index 2fff0eb..0000000 --- a/src/main/java/com/hackathon/TimeLapse/article/ImageRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.hackathon.TimeLapse.article; - -import com.hackathon.TimeLapse.domain.Article; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface ImageRepository extends JpaRepository { -} \ No newline at end of file diff --git a/src/main/java/com/hackathon/TimeLapse/domain/common/BaseEntity.java b/src/main/java/com/hackathon/TimeLapse/common/BaseEntity.java similarity index 93% rename from src/main/java/com/hackathon/TimeLapse/domain/common/BaseEntity.java rename to src/main/java/com/hackathon/TimeLapse/common/BaseEntity.java index c196b9a..5c96583 100644 --- a/src/main/java/com/hackathon/TimeLapse/domain/common/BaseEntity.java +++ b/src/main/java/com/hackathon/TimeLapse/common/BaseEntity.java @@ -1,4 +1,4 @@ -package com.hackathon.TimeLapse.domain.common; +package com.hackathon.TimeLapse.common; import jakarta.persistence.EntityListeners; import jakarta.persistence.MappedSuperclass; diff --git a/src/main/java/com/hackathon/TimeLapse/domain/Image.java b/src/main/java/com/hackathon/TimeLapse/image/Image.java similarity index 71% rename from src/main/java/com/hackathon/TimeLapse/domain/Image.java rename to src/main/java/com/hackathon/TimeLapse/image/Image.java index 627d125..cd5d0a0 100644 --- a/src/main/java/com/hackathon/TimeLapse/domain/Image.java +++ b/src/main/java/com/hackathon/TimeLapse/image/Image.java @@ -1,9 +1,9 @@ -package com.hackathon.TimeLapse.domain; +package com.hackathon.TimeLapse.image; -import com.hackathon.TimeLapse.domain.common.BaseEntity; +import com.hackathon.TimeLapse.article.Article; +import com.hackathon.TimeLapse.common.BaseEntity; import jakarta.persistence.*; import lombok.*; -import org.springframework.data.jpa.domain.support.AuditingEntityListener; @Entity @Getter @@ -16,7 +16,7 @@ public class Image extends BaseEntity { @GeneratedValue( strategy = GenerationType.IDENTITY ) - private long id; + private Long id; @Column( nullable = false, diff --git a/src/main/java/com/hackathon/TimeLapse/image/ImageController.java b/src/main/java/com/hackathon/TimeLapse/image/ImageController.java new file mode 100644 index 0000000..1db6fab --- /dev/null +++ b/src/main/java/com/hackathon/TimeLapse/image/ImageController.java @@ -0,0 +1,25 @@ +package com.hackathon.TimeLapse.image; + +import com.hackathon.TimeLapse.apiPayload.ApiResponse; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +@RestController +@RequiredArgsConstructor +@Validated +@RequestMapping("/api/images") +public class ImageController { + private final ImageService imageService; + + @PostMapping("/") + public ApiResponse addImages(@RequestBody @Valid ImageRequestDTO.addImagesDTO request, + @RequestHeader("articleId") Long articleId) { + List images = imageService.addImages(articleId, request); + return ApiResponse.onSuccess(ImageConverter.toAddImageResultDTO(images)); + } +} diff --git a/src/main/java/com/hackathon/TimeLapse/image/ImageConverter.java b/src/main/java/com/hackathon/TimeLapse/image/ImageConverter.java new file mode 100644 index 0000000..8f662c5 --- /dev/null +++ b/src/main/java/com/hackathon/TimeLapse/image/ImageConverter.java @@ -0,0 +1,15 @@ +package com.hackathon.TimeLapse.image; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +public class ImageConverter { + + public static ImageResponseDTO.addImageResultDTO toAddImageResultDTO(List images){ + List imageUrlList = images.stream() + .map(Image::getImage_url) + .collect(Collectors.toList()); + return new ImageResponseDTO.addImageResultDTO(imageUrlList); + } +} diff --git a/src/main/java/com/hackathon/TimeLapse/image/ImageRepository.java b/src/main/java/com/hackathon/TimeLapse/image/ImageRepository.java new file mode 100644 index 0000000..95b8c58 --- /dev/null +++ b/src/main/java/com/hackathon/TimeLapse/image/ImageRepository.java @@ -0,0 +1,7 @@ +package com.hackathon.TimeLapse.image; + +import com.hackathon.TimeLapse.article.Article; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ImageRepository extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/com/hackathon/TimeLapse/image/ImageRequestDTO.java b/src/main/java/com/hackathon/TimeLapse/image/ImageRequestDTO.java new file mode 100644 index 0000000..5fd6838 --- /dev/null +++ b/src/main/java/com/hackathon/TimeLapse/image/ImageRequestDTO.java @@ -0,0 +1,14 @@ +package com.hackathon.TimeLapse.image; + +import jakarta.validation.constraints.NotNull; +import lombok.Getter; + +import java.util.List; + +public class ImageRequestDTO { + @Getter + public static class addImagesDTO { + + private List imageUrlList; + } +} diff --git a/src/main/java/com/hackathon/TimeLapse/image/ImageResponseDTO.java b/src/main/java/com/hackathon/TimeLapse/image/ImageResponseDTO.java new file mode 100644 index 0000000..6a6af01 --- /dev/null +++ b/src/main/java/com/hackathon/TimeLapse/image/ImageResponseDTO.java @@ -0,0 +1,20 @@ +package com.hackathon.TimeLapse.image; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +public class ImageResponseDTO { + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class addImageResultDTO{ + private List imageUrlList; + } + +} diff --git a/src/main/java/com/hackathon/TimeLapse/image/ImageService.java b/src/main/java/com/hackathon/TimeLapse/image/ImageService.java new file mode 100644 index 0000000..6f32548 --- /dev/null +++ b/src/main/java/com/hackathon/TimeLapse/image/ImageService.java @@ -0,0 +1,37 @@ +package com.hackathon.TimeLapse.image; +import com.hackathon.TimeLapse.apiPayload.exception.ArticleNotFoundException; +import com.hackathon.TimeLapse.article.Article; +import com.hackathon.TimeLapse.article.ArticleRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class ImageService { + private final ImageRepository imageRepository; + private final ArticleRepository articleRepository; + + public ImageService(ImageRepository imageRepository, ArticleRepository articleRepository) { + this.imageRepository = imageRepository; + this.articleRepository = articleRepository; + } + + @Transactional + public List addImages(Long articleId, ImageRequestDTO.addImagesDTO request) { + Article article = articleRepository.findById(articleId) + .orElseThrow(() -> new ArticleNotFoundException("Article not found with id: " + articleId)); + + List images = new ArrayList<>(); + for (String imageUrl : request.getImageUrlList()) { + Image image = Image.builder() + .image_url(imageUrl) + .article(article) + .build(); + images.add(image); + } + + return imageRepository.saveAll(images); + } +} \ No newline at end of file diff --git a/src/main/java/com/hackathon/TimeLapse/domain/Member.java b/src/main/java/com/hackathon/TimeLapse/member/Member.java similarity index 79% rename from src/main/java/com/hackathon/TimeLapse/domain/Member.java rename to src/main/java/com/hackathon/TimeLapse/member/Member.java index 120493a..04345f4 100644 --- a/src/main/java/com/hackathon/TimeLapse/domain/Member.java +++ b/src/main/java/com/hackathon/TimeLapse/member/Member.java @@ -1,9 +1,10 @@ -package com.hackathon.TimeLapse.domain; +package com.hackathon.TimeLapse.member; import java.util.ArrayList; import java.util.List; -import com.hackathon.TimeLapse.domain.common.BaseEntity; +import com.hackathon.TimeLapse.article.Article; +import com.hackathon.TimeLapse.common.BaseEntity; import com.hackathon.TimeLapse.oauth.OAuthProvider; import jakarta.persistence.CascadeType; @@ -27,7 +28,7 @@ public class Member extends BaseEntity { @Id @GeneratedValue( - strategy = GenerationType.IDENTITY + strategy = GenerationType.IDENTITY ) private Long id; @@ -39,9 +40,9 @@ public class Member extends BaseEntity { private OAuthProvider oAuthProvider; @OneToMany( - mappedBy = "member", - cascade = {CascadeType.ALL}, - fetch = FetchType.EAGER + mappedBy = "member", + cascade = {CascadeType.ALL}, + fetch = FetchType.EAGER ) private List
articleList = new ArrayList<>(); diff --git a/src/main/java/com/hackathon/TimeLapse/member/MemberRepository.java b/src/main/java/com/hackathon/TimeLapse/member/MemberRepository.java index c6e3a26..7621f98 100644 --- a/src/main/java/com/hackathon/TimeLapse/member/MemberRepository.java +++ b/src/main/java/com/hackathon/TimeLapse/member/MemberRepository.java @@ -4,8 +4,8 @@ import org.springframework.data.jpa.repository.JpaRepository; -import com.hackathon.TimeLapse.domain.Member; +import com.hackathon.TimeLapse.member.Member; public interface MemberRepository extends JpaRepository { - Optional findByEmail(String email); + Optional findByEmail(String email); } diff --git a/src/main/java/com/hackathon/TimeLapse/s3/S3Controller.java b/src/main/java/com/hackathon/TimeLapse/s3/S3Controller.java index 499e700..b81a58a 100644 --- a/src/main/java/com/hackathon/TimeLapse/s3/S3Controller.java +++ b/src/main/java/com/hackathon/TimeLapse/s3/S3Controller.java @@ -8,6 +8,8 @@ import lombok.RequiredArgsConstructor; +import java.util.List; + @RestController @RequestMapping("/api/files") @RequiredArgsConstructor @@ -20,6 +22,11 @@ public ResponseEntity uploadFile(@RequestParam("file") MultipartFile fil return s3Service.uploadFile(file); } + @PostMapping(value = "/uploadMultiple", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ResponseEntity> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) { + return s3Service.uploadMultipleFiles(files); + } + @GetMapping("/url") public String getFileUrl(@RequestParam("fileName") String fileName) { return s3Service.getFileUrl(fileName); diff --git a/src/main/java/com/hackathon/TimeLapse/s3/S3Service.java b/src/main/java/com/hackathon/TimeLapse/s3/S3Service.java index d85380f..a5405d2 100644 --- a/src/main/java/com/hackathon/TimeLapse/s3/S3Service.java +++ b/src/main/java/com/hackathon/TimeLapse/s3/S3Service.java @@ -4,7 +4,9 @@ import java.net.URL; import java.time.LocalDateTime; import java.time.ZoneOffset; +import java.util.ArrayList; import java.util.Date; +import java.util.List; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; @@ -44,6 +46,25 @@ public ResponseEntity uploadFile(@RequestParam("file") MultipartFile fil } } + public ResponseEntity> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) { + List fileUrls = new ArrayList<>(); + for (MultipartFile file : files) { + try { + String fileName = file.getOriginalFilename(); + String fileUrl = "https://" + bucket + "/test" + fileName; + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentType(file.getContentType()); + metadata.setContentLength(file.getSize()); + amazonS3Client.putObject(bucket, fileName, file.getInputStream(), metadata); + fileUrls.add(fileUrl); + } catch (IOException e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } + return ResponseEntity.ok(fileUrls); + } + public void deleteFile(String fileName) { amazonS3Client.deleteObject(bucket, fileName); } From aa9a60f81c71dfb1a35ab52186df19d76f06577f Mon Sep 17 00:00:00 2001 From: oxdjww Date: Fri, 5 Jul 2024 04:52:19 +0900 Subject: [PATCH 2/3] fix: enable updateAt createdAt --- .../TimeLapse/TimeLapseApplication.java | 2 ++ .../TimeLapse/common/BaseEntity.java | 33 ++++++++++--------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/hackathon/TimeLapse/TimeLapseApplication.java b/src/main/java/com/hackathon/TimeLapse/TimeLapseApplication.java index 7b29feb..66ca12d 100644 --- a/src/main/java/com/hackathon/TimeLapse/TimeLapseApplication.java +++ b/src/main/java/com/hackathon/TimeLapse/TimeLapseApplication.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication +@EnableJpaAuditing public class TimeLapseApplication { public static void main(String[] args) { diff --git a/src/main/java/com/hackathon/TimeLapse/common/BaseEntity.java b/src/main/java/com/hackathon/TimeLapse/common/BaseEntity.java index 5c96583..9c32cad 100644 --- a/src/main/java/com/hackathon/TimeLapse/common/BaseEntity.java +++ b/src/main/java/com/hackathon/TimeLapse/common/BaseEntity.java @@ -1,27 +1,30 @@ package com.hackathon.TimeLapse.common; -import jakarta.persistence.EntityListeners; -import jakarta.persistence.MappedSuperclass; import java.time.LocalDateTime; + import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; @MappedSuperclass -@EntityListeners({AbstractMethodError.class}) +@EntityListeners(AuditingEntityListener.class) public abstract class BaseEntity { - @CreatedDate - private LocalDateTime createdAt; - @LastModifiedDate - private LocalDateTime updatedAt; + @CreatedDate + private LocalDateTime createdAt; + @LastModifiedDate + private LocalDateTime updatedAt; - public BaseEntity() { - } + public BaseEntity() { + } - public LocalDateTime getCreatedAt() { - return this.createdAt; - } + public LocalDateTime getCreatedAt() { + return this.createdAt; + } - public LocalDateTime getUpdatedAt() { - return this.updatedAt; - } + public LocalDateTime getUpdatedAt() { + return this.updatedAt; + } } From 02d21ac7ae22b76e55891881c0c66da64f458028 Mon Sep 17 00:00:00 2001 From: oxdjww Date: Fri, 5 Jul 2024 05:43:47 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20=EA=B8=B0=EB=8A=A5=EC=9D=84=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 --- .../TimeLapse/article/ArticleController.java | 58 +++++++++++++++---- .../TimeLapse/article/ArticleRequestDTO.java | 6 +- .../TimeLapse/article/ArticleService.java | 8 ++- .../TimeLapse/image/ImageRequestDTO.java | 3 + .../TimeLapse/oauth/AuthTokensGenerator.java | 2 +- .../TimeLapse/oauth/MemberController.java | 2 +- .../TimeLapse/oauth/OAuthLoginService.java | 2 +- .../hackathon/TimeLapse/s3/S3Controller.java | 12 +++- .../com/hackathon/TimeLapse/s3/S3Service.java | 23 +++----- 9 files changed, 83 insertions(+), 33 deletions(-) diff --git a/src/main/java/com/hackathon/TimeLapse/article/ArticleController.java b/src/main/java/com/hackathon/TimeLapse/article/ArticleController.java index 007cde9..26e8250 100644 --- a/src/main/java/com/hackathon/TimeLapse/article/ArticleController.java +++ b/src/main/java/com/hackathon/TimeLapse/article/ArticleController.java @@ -1,23 +1,61 @@ package com.hackathon.TimeLapse.article; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.http.MediaType; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + import com.hackathon.TimeLapse.apiPayload.ApiResponse; +import com.hackathon.TimeLapse.image.Image; +import com.hackathon.TimeLapse.image.ImageRequestDTO; +import com.hackathon.TimeLapse.image.ImageService; +import com.hackathon.TimeLapse.s3.S3Service; + import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; +import lombok.extern.slf4j.Slf4j; @RestController @RequiredArgsConstructor @Validated +@Slf4j @RequestMapping("/api/article") public class ArticleController { - private final ArticleService articleService; - - @PostMapping("/") - public ApiResponse createArticle(@RequestBody @Valid ArticleRequestDTO.createArticleDTO request, - @RequestParam(name = "memberId") Long memberId) { - Article article = articleService.createArticle(memberId, request); - return ApiResponse.onSuccess(ArticleConverter.toCreateReviewResultDTO(article)); - } + + private final ArticleService articleService; + private final S3Service s3Service; + private final ImageService imageService; + + @PostMapping(value = "", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE}) + public ApiResponse createArticle( + @RequestPart @Valid ArticleRequestDTO.createArticleDTO request, + @RequestPart List files + ) throws IOException { + long memberId = Long.parseLong(SecurityContextHolder.getContext().getAuthentication().getName()); + log.info("[*] memberId: " + memberId); + System.out.println(files.get(0)); + List uploadedFilesLinks = s3Service.uploadMultipleFiles(files); + Article article = articleService.createArticle(memberId, request, uploadedFilesLinks); + + List images = new ArrayList<>(); + for (String url : uploadedFilesLinks) { + Image image = Image.builder() + .image_url(url) + .article(article) + .build(); + images.add(image); + } + imageService.addImages(article.getId(), new ImageRequestDTO.addImagesDTO(uploadedFilesLinks)); + + return ApiResponse.onSuccess(ArticleConverter.toCreateReviewResultDTO(article)); + } } diff --git a/src/main/java/com/hackathon/TimeLapse/article/ArticleRequestDTO.java b/src/main/java/com/hackathon/TimeLapse/article/ArticleRequestDTO.java index a818356..8b15192 100644 --- a/src/main/java/com/hackathon/TimeLapse/article/ArticleRequestDTO.java +++ b/src/main/java/com/hackathon/TimeLapse/article/ArticleRequestDTO.java @@ -1,5 +1,9 @@ package com.hackathon.TimeLapse.article; +import java.util.List; + +import org.springframework.web.multipart.MultipartFile; + import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Getter; @@ -18,7 +22,5 @@ public static class createArticleDTO { private String description; @NotNull private Long status; - @NotNull - private Long memberId; } } diff --git a/src/main/java/com/hackathon/TimeLapse/article/ArticleService.java b/src/main/java/com/hackathon/TimeLapse/article/ArticleService.java index 18b215a..bce11e2 100644 --- a/src/main/java/com/hackathon/TimeLapse/article/ArticleService.java +++ b/src/main/java/com/hackathon/TimeLapse/article/ArticleService.java @@ -1,5 +1,8 @@ package com.hackathon.TimeLapse.article; +import java.util.List; + import com.hackathon.TimeLapse.apiPayload.exception.MemberNotFoundException; +import com.hackathon.TimeLapse.image.ImageService; import com.hackathon.TimeLapse.member.MemberRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -15,7 +18,8 @@ public ArticleService(ArticleRepository articleRepository, MemberRepository memb } @Transactional - public Article createArticle(Long memberId, ArticleRequestDTO.createArticleDTO request) { + public Article createArticle(Long memberId, ArticleRequestDTO.createArticleDTO request, + List uploadedFilesLinks) { Article article = ArticleConverter.toArticle(request); article.setMember(memberRepository.findById(memberId) @@ -23,4 +27,4 @@ public Article createArticle(Long memberId, ArticleRequestDTO.createArticleDTO r return articleRepository.save(article); } -} \ No newline at end of file +} diff --git a/src/main/java/com/hackathon/TimeLapse/image/ImageRequestDTO.java b/src/main/java/com/hackathon/TimeLapse/image/ImageRequestDTO.java index 5fd6838..4deae9e 100644 --- a/src/main/java/com/hackathon/TimeLapse/image/ImageRequestDTO.java +++ b/src/main/java/com/hackathon/TimeLapse/image/ImageRequestDTO.java @@ -1,12 +1,15 @@ package com.hackathon.TimeLapse.image; import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import java.util.List; public class ImageRequestDTO { @Getter + @AllArgsConstructor public static class addImagesDTO { private List imageUrlList; diff --git a/src/main/java/com/hackathon/TimeLapse/oauth/AuthTokensGenerator.java b/src/main/java/com/hackathon/TimeLapse/oauth/AuthTokensGenerator.java index c654012..59c94ab 100644 --- a/src/main/java/com/hackathon/TimeLapse/oauth/AuthTokensGenerator.java +++ b/src/main/java/com/hackathon/TimeLapse/oauth/AuthTokensGenerator.java @@ -10,7 +10,7 @@ @RequiredArgsConstructor public class AuthTokensGenerator { private static final String BEARER_TYPE = "Bearer"; - private static final long ACCESS_TOKEN_EXPIRE_TIME = 1000 * 60 * 30; // 30분 + private static final long ACCESS_TOKEN_EXPIRE_TIME = 1000 * 60 * 30 * 24 * 7; // 30분 private static final long REFRESH_TOKEN_EXPIRE_TIME = 1000 * 60 * 60 * 24 * 7; // 7일 private final JwtTokenProvider jwtTokenProvider; diff --git a/src/main/java/com/hackathon/TimeLapse/oauth/MemberController.java b/src/main/java/com/hackathon/TimeLapse/oauth/MemberController.java index cd24ca4..f16dfb4 100644 --- a/src/main/java/com/hackathon/TimeLapse/oauth/MemberController.java +++ b/src/main/java/com/hackathon/TimeLapse/oauth/MemberController.java @@ -8,7 +8,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.hackathon.TimeLapse.domain.Member; +import com.hackathon.TimeLapse.member.Member; import com.hackathon.TimeLapse.member.MemberRepository; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/hackathon/TimeLapse/oauth/OAuthLoginService.java b/src/main/java/com/hackathon/TimeLapse/oauth/OAuthLoginService.java index 9ea96f4..a61a30d 100644 --- a/src/main/java/com/hackathon/TimeLapse/oauth/OAuthLoginService.java +++ b/src/main/java/com/hackathon/TimeLapse/oauth/OAuthLoginService.java @@ -2,7 +2,7 @@ import org.springframework.stereotype.Service; -import com.hackathon.TimeLapse.domain.Member; +import com.hackathon.TimeLapse.member.Member; import com.hackathon.TimeLapse.member.MemberRepository; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/hackathon/TimeLapse/s3/S3Controller.java b/src/main/java/com/hackathon/TimeLapse/s3/S3Controller.java index b81a58a..48fa98d 100644 --- a/src/main/java/com/hackathon/TimeLapse/s3/S3Controller.java +++ b/src/main/java/com/hackathon/TimeLapse/s3/S3Controller.java @@ -1,6 +1,7 @@ package com.hackathon.TimeLapse.s3; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -8,6 +9,7 @@ import lombok.RequiredArgsConstructor; +import java.io.IOException; import java.util.List; @RestController @@ -23,8 +25,14 @@ public ResponseEntity uploadFile(@RequestParam("file") MultipartFile fil } @PostMapping(value = "/uploadMultiple", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public ResponseEntity> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) { - return s3Service.uploadMultipleFiles(files); + public ResponseEntity> uploadMultipleFiles(@RequestParam("files") List files) { + try{ + return ResponseEntity.ok(s3Service.uploadMultipleFiles(files)); + } + catch (IOException e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } } @GetMapping("/url") diff --git a/src/main/java/com/hackathon/TimeLapse/s3/S3Service.java b/src/main/java/com/hackathon/TimeLapse/s3/S3Service.java index a5405d2..053fc03 100644 --- a/src/main/java/com/hackathon/TimeLapse/s3/S3Service.java +++ b/src/main/java/com/hackathon/TimeLapse/s3/S3Service.java @@ -46,23 +46,18 @@ public ResponseEntity uploadFile(@RequestParam("file") MultipartFile fil } } - public ResponseEntity> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) { + public List uploadMultipleFiles(@RequestParam("files") List files) throws IOException { List fileUrls = new ArrayList<>(); for (MultipartFile file : files) { - try { - String fileName = file.getOriginalFilename(); - String fileUrl = "https://" + bucket + "/test" + fileName; - ObjectMetadata metadata = new ObjectMetadata(); - metadata.setContentType(file.getContentType()); - metadata.setContentLength(file.getSize()); - amazonS3Client.putObject(bucket, fileName, file.getInputStream(), metadata); - fileUrls.add(fileUrl); - } catch (IOException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); - } + String fileName = file.getOriginalFilename(); + String fileUrl = "https://" + bucket + "/test" + fileName; + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentType(file.getContentType()); + metadata.setContentLength(file.getSize()); + amazonS3Client.putObject(bucket, fileName, file.getInputStream(), metadata); + fileUrls.add(fileUrl); } - return ResponseEntity.ok(fileUrls); + return fileUrls; } public void deleteFile(String fileName) {