From 05a29737c7467c36f54359570858a688739ed4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A4=80=EC=8D=A8=ED=81=AC?= Date: Tue, 14 Nov 2023 19:02:16 +0900 Subject: [PATCH 1/7] =?UTF-8?q?chore:=20s3=20dependency=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 71e4ec5..a58b4dc 100644 --- a/build.gradle +++ b/build.gradle @@ -30,9 +30,11 @@ dependencies { annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' - // swagger - implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0") + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' + + // s3 + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' } tasks.named('test') { From 4f61942bec7e00531481a25d1aee6defc8e251d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A4=80=EC=8D=A8=ED=81=AC?= Date: Tue, 14 Nov 2023 19:02:28 +0900 Subject: [PATCH 2/7] =?UTF-8?q?chore:=20s3config=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flory/global/config/S3Config.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/main/java/zerobibim/flory/global/config/S3Config.java diff --git a/src/main/java/zerobibim/flory/global/config/S3Config.java b/src/main/java/zerobibim/flory/global/config/S3Config.java new file mode 100644 index 0000000..e7bac4e --- /dev/null +++ b/src/main/java/zerobibim/flory/global/config/S3Config.java @@ -0,0 +1,28 @@ +package zerobibim.flory.global.config; + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3Client; +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 AmazonS3Client amazonS3Client() { + BasicAWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey); + return (AmazonS3Client) AmazonS3ClientBuilder.standard() + .withRegion(region) + .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)) + .build(); + } +} From adf9b1a4ba5bc060d39eb775a931863861e36230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A4=80=EC=8D=A8=ED=81=AC?= Date: Tue, 14 Nov 2023 19:40:27 +0900 Subject: [PATCH 3/7] =?UTF-8?q?chore:=20Image=20entity=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flory/domain/Image/entity/Image.java | 31 +++++++++++++++++++ .../flory/domain/flower/entity/Flower.java | 5 +++ 2 files changed, 36 insertions(+) create mode 100644 src/main/java/zerobibim/flory/domain/Image/entity/Image.java diff --git a/src/main/java/zerobibim/flory/domain/Image/entity/Image.java b/src/main/java/zerobibim/flory/domain/Image/entity/Image.java new file mode 100644 index 0000000..ee9038a --- /dev/null +++ b/src/main/java/zerobibim/flory/domain/Image/entity/Image.java @@ -0,0 +1,31 @@ +package zerobibim.flory.domain.Image.entity; + +import jakarta.persistence.*; +import lombok.*; +import org.hibernate.annotations.Where; +import zerobibim.flory.global.common.BaseTime; + +@Entity +@Getter +@Where(clause = "deleted_at is null") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class Image extends BaseTime { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String url; + + private Long senderId; + + private Boolean isNFT; + + @Builder + public Image(String url) { + this.url = url; + this.isNFT = Boolean.FALSE; + } +} diff --git a/src/main/java/zerobibim/flory/domain/flower/entity/Flower.java b/src/main/java/zerobibim/flory/domain/flower/entity/Flower.java index d7c7764..bfbab47 100644 --- a/src/main/java/zerobibim/flory/domain/flower/entity/Flower.java +++ b/src/main/java/zerobibim/flory/domain/flower/entity/Flower.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import lombok.*; import org.hibernate.annotations.Where; +import zerobibim.flory.domain.Image.entity.Image; import zerobibim.flory.global.common.BaseTime; @Entity @@ -24,6 +25,10 @@ public class Flower extends BaseTime { @Column(nullable = false) private Long price; + @OneToOne + @JoinColumn + private Image image; + @Builder public Flower(String name, String description, Long price) { this.name = name; From 5ab045e64362d378d5dae45645c5b9c5ee37353a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A4=80=EC=8D=A8=ED=81=AC?= Date: Tue, 14 Nov 2023 19:40:44 +0900 Subject: [PATCH 4/7] =?UTF-8?q?chore:=20S3ImageComponent=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flory/utils/S3ImageComponent.java | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/main/java/zerobibim/flory/utils/S3ImageComponent.java diff --git a/src/main/java/zerobibim/flory/utils/S3ImageComponent.java b/src/main/java/zerobibim/flory/utils/S3ImageComponent.java new file mode 100644 index 0000000..c827841 --- /dev/null +++ b/src/main/java/zerobibim/flory/utils/S3ImageComponent.java @@ -0,0 +1,64 @@ +package zerobibim.flory.utils; + +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.amazonaws.services.s3.model.DeleteObjectRequest; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.Objects; +import java.util.UUID; + +@RequiredArgsConstructor +@Component +public class S3ImageComponent { + private final AmazonS3Client amazonS3Client; + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + public String uploadImage(String category, MultipartFile multipartFile) { + // 파일명 + String fileName = createFileName(category, Objects.requireNonNull(multipartFile.getOriginalFilename())); + + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentType(multipartFile.getContentType()); + + // S3에 업로드 + try { + amazonS3Client.putObject(new PutObjectRequest(bucket, fileName, multipartFile.getInputStream(), objectMetadata) + .withCannedAcl(CannedAccessControlList.PublicRead)); + } catch (IOException ignored) { + } + + return amazonS3Client.getUrl(bucket, fileName).toString(); + } + + /** + * 파일명 생성 + * @param category + * @param originalFileName + * @return 작명된 파일 이름 + */ + public String createFileName(String category, String originalFileName) { + int fileExtensionIndex = originalFileName.lastIndexOf("."); + String fileExtension = originalFileName.substring(fileExtensionIndex); + String fileName = originalFileName.substring(0, fileExtensionIndex); + String random = String.valueOf(UUID.randomUUID()); + + return category + "/" + fileName + "_" + random + fileExtension; + } + + /** + * 이미지 삭제 + * @param fileUrl + */ + public void deleteImage(String fileUrl) { + String[] deleteUrl = fileUrl.split("/", 4); + amazonS3Client.deleteObject(new DeleteObjectRequest(bucket, deleteUrl[3])); + } +} From 304b94ee8a32c891259f0f4f7ba6187ffcd12f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A4=80=EC=8D=A8=ED=81=AC?= Date: Tue, 14 Nov 2023 20:37:00 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20create=20image=20api=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Image/controller/ImageConroller.java | 24 ++++++++++++ .../Image/dto/response/ImageIdResponse.java | 12 ++++++ .../flory/domain/Image/entity/Image.java | 1 + .../domain/Image/mapper/ImageMapper.java | 13 +++++++ .../Image/repository/ImageRepository.java | 8 ++++ .../Image/repository/JpaImageRepository.java | 7 ++++ .../domain/Image/service/ImageService.java | 38 +++++++++++++++++++ .../flory/domain/flower/entity/Flower.java | 4 ++ .../ApiPayload/code/status/ErrorStatus.java | 3 ++ 9 files changed, 110 insertions(+) create mode 100644 src/main/java/zerobibim/flory/domain/Image/controller/ImageConroller.java create mode 100644 src/main/java/zerobibim/flory/domain/Image/dto/response/ImageIdResponse.java create mode 100644 src/main/java/zerobibim/flory/domain/Image/mapper/ImageMapper.java create mode 100644 src/main/java/zerobibim/flory/domain/Image/repository/ImageRepository.java create mode 100644 src/main/java/zerobibim/flory/domain/Image/repository/JpaImageRepository.java create mode 100644 src/main/java/zerobibim/flory/domain/Image/service/ImageService.java diff --git a/src/main/java/zerobibim/flory/domain/Image/controller/ImageConroller.java b/src/main/java/zerobibim/flory/domain/Image/controller/ImageConroller.java new file mode 100644 index 0000000..bffd71b --- /dev/null +++ b/src/main/java/zerobibim/flory/domain/Image/controller/ImageConroller.java @@ -0,0 +1,24 @@ +package zerobibim.flory.domain.Image.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import zerobibim.flory.domain.Image.dto.response.ImageIdResponse; +import zerobibim.flory.domain.Image.service.ImageService; +import zerobibim.flory.global.common.ApiPayload.ApiResponse; + +@Tag(name = "Image API", description = "이미지 관련 API") +@RestController +@RequiredArgsConstructor +@RequestMapping("/image") +public class ImageConroller { + private final ImageService imageService; + + @PostMapping + @Operation(summary = "이미지 생성 API") + public ApiResponse createImage(@RequestPart("flower_image") MultipartFile flowerImage) { + return ApiResponse.onSuccess(imageService.createImage(flowerImage)); + } +} diff --git a/src/main/java/zerobibim/flory/domain/Image/dto/response/ImageIdResponse.java b/src/main/java/zerobibim/flory/domain/Image/dto/response/ImageIdResponse.java new file mode 100644 index 0000000..0db756c --- /dev/null +++ b/src/main/java/zerobibim/flory/domain/Image/dto/response/ImageIdResponse.java @@ -0,0 +1,12 @@ +package zerobibim.flory.domain.Image.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class ImageIdResponse { + private Long ImageId; +} diff --git a/src/main/java/zerobibim/flory/domain/Image/entity/Image.java b/src/main/java/zerobibim/flory/domain/Image/entity/Image.java index ee9038a..0b9a226 100644 --- a/src/main/java/zerobibim/flory/domain/Image/entity/Image.java +++ b/src/main/java/zerobibim/flory/domain/Image/entity/Image.java @@ -26,6 +26,7 @@ public class Image extends BaseTime { @Builder public Image(String url) { this.url = url; + this.senderId = null; this.isNFT = Boolean.FALSE; } } diff --git a/src/main/java/zerobibim/flory/domain/Image/mapper/ImageMapper.java b/src/main/java/zerobibim/flory/domain/Image/mapper/ImageMapper.java new file mode 100644 index 0000000..92a4478 --- /dev/null +++ b/src/main/java/zerobibim/flory/domain/Image/mapper/ImageMapper.java @@ -0,0 +1,13 @@ +package zerobibim.flory.domain.Image.mapper; + +import org.springframework.stereotype.Component; +import zerobibim.flory.domain.Image.entity.Image; + +@Component +public class ImageMapper { + public Image toEntity(String url) { + return Image.builder() + .url(url) + .build(); + } +} diff --git a/src/main/java/zerobibim/flory/domain/Image/repository/ImageRepository.java b/src/main/java/zerobibim/flory/domain/Image/repository/ImageRepository.java new file mode 100644 index 0000000..1778833 --- /dev/null +++ b/src/main/java/zerobibim/flory/domain/Image/repository/ImageRepository.java @@ -0,0 +1,8 @@ +package zerobibim.flory.domain.Image.repository; + +import zerobibim.flory.domain.Image.entity.Image; + +public interface ImageRepository { + + Image save(Image image); +} diff --git a/src/main/java/zerobibim/flory/domain/Image/repository/JpaImageRepository.java b/src/main/java/zerobibim/flory/domain/Image/repository/JpaImageRepository.java new file mode 100644 index 0000000..6d1bef3 --- /dev/null +++ b/src/main/java/zerobibim/flory/domain/Image/repository/JpaImageRepository.java @@ -0,0 +1,7 @@ +package zerobibim.flory.domain.Image.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import zerobibim.flory.domain.Image.entity.Image; + +public interface JpaImageRepository extends JpaRepository, ImageRepository { +} diff --git a/src/main/java/zerobibim/flory/domain/Image/service/ImageService.java b/src/main/java/zerobibim/flory/domain/Image/service/ImageService.java new file mode 100644 index 0000000..7d10462 --- /dev/null +++ b/src/main/java/zerobibim/flory/domain/Image/service/ImageService.java @@ -0,0 +1,38 @@ +package zerobibim.flory.domain.Image.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import zerobibim.flory.domain.Image.dto.response.ImageIdResponse; +import zerobibim.flory.domain.Image.entity.Image; +import zerobibim.flory.domain.Image.mapper.ImageMapper; +import zerobibim.flory.domain.Image.repository.ImageRepository; +import zerobibim.flory.global.common.ApiPayload.code.status.ErrorStatus; +import zerobibim.flory.global.common.ExceptionHandler; +import zerobibim.flory.utils.S3ImageComponent; + +@Service +@RequiredArgsConstructor +public class ImageService { + + private final ImageMapper imageMapper; + private final ImageRepository imageRepository; + private final S3ImageComponent s3ImageComponent; + + @Transactional + public ImageIdResponse createImage(final MultipartFile flowerImage) { + String imageUrl = uploadImage(flowerImage); + + Image newImage = imageRepository.save( + imageMapper.toEntity(imageUrl)); + return new ImageIdResponse(newImage.getId()); + } + + private String uploadImage(final MultipartFile flowerImage) { + if(flowerImage.isEmpty()) { + throw new ExceptionHandler(ErrorStatus.IMAGE_BLANK); + } + return s3ImageComponent.uploadImage("flower-image", flowerImage); + } +} diff --git a/src/main/java/zerobibim/flory/domain/flower/entity/Flower.java b/src/main/java/zerobibim/flory/domain/flower/entity/Flower.java index bfbab47..e05ebaa 100644 --- a/src/main/java/zerobibim/flory/domain/flower/entity/Flower.java +++ b/src/main/java/zerobibim/flory/domain/flower/entity/Flower.java @@ -40,4 +40,8 @@ public void update(String description, Long price) { this.description = description; this.price = price; } + + public void updateFlowerImage(Image image) { + this.image = image; + } } diff --git a/src/main/java/zerobibim/flory/global/common/ApiPayload/code/status/ErrorStatus.java b/src/main/java/zerobibim/flory/global/common/ApiPayload/code/status/ErrorStatus.java index bd946f5..f63bcaf 100644 --- a/src/main/java/zerobibim/flory/global/common/ApiPayload/code/status/ErrorStatus.java +++ b/src/main/java/zerobibim/flory/global/common/ApiPayload/code/status/ErrorStatus.java @@ -33,6 +33,9 @@ public enum ErrorStatus implements BaseErrorCode { // 테스트 관련 응답 TEST_EXCEPTION(HttpStatus.BAD_REQUEST, "TEST4001", "테스트를 위한 에러 코드"), + + // 이미지 관련 응답 + IMAGE_BLANK(HttpStatus.BAD_REQUEST, "IMAGE4001", "이미지 파일이 없습니다."), ; From 34a3107ded2dc096ba05ae6b3caf6bbab9dea500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A4=80=EC=8D=A8=ED=81=AC?= Date: Tue, 14 Nov 2023 22:09:20 +0900 Subject: [PATCH 6/7] =?UTF-8?q?feat:=20=EA=BD=83=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20API=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/Image/repository/ImageRepository.java | 4 ++++ .../domain/flower/controller/FlowerController.java | 12 ++++++++++++ .../dto/request/FlowerInsertImageRequest.java | 13 +++++++++++++ .../flory/domain/flower/service/FlowerService.java | 13 +++++++++++++ .../common/ApiPayload/code/status/ErrorStatus.java | 2 ++ 5 files changed, 44 insertions(+) create mode 100644 src/main/java/zerobibim/flory/domain/flower/dto/request/FlowerInsertImageRequest.java diff --git a/src/main/java/zerobibim/flory/domain/Image/repository/ImageRepository.java b/src/main/java/zerobibim/flory/domain/Image/repository/ImageRepository.java index 1778833..00b554b 100644 --- a/src/main/java/zerobibim/flory/domain/Image/repository/ImageRepository.java +++ b/src/main/java/zerobibim/flory/domain/Image/repository/ImageRepository.java @@ -2,7 +2,11 @@ import zerobibim.flory.domain.Image.entity.Image; +import java.util.Optional; + public interface ImageRepository { Image save(Image image); + + Optional findImageById(Long id); } diff --git a/src/main/java/zerobibim/flory/domain/flower/controller/FlowerController.java b/src/main/java/zerobibim/flory/domain/flower/controller/FlowerController.java index 9f97b2c..60ddefd 100644 --- a/src/main/java/zerobibim/flory/domain/flower/controller/FlowerController.java +++ b/src/main/java/zerobibim/flory/domain/flower/controller/FlowerController.java @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; +import zerobibim.flory.domain.flower.dto.request.FlowerInsertImageRequest; import zerobibim.flory.domain.flower.dto.request.FlowerCreateReqeust; import zerobibim.flory.domain.flower.dto.request.FlowerUpdateRequest; import zerobibim.flory.domain.flower.dto.response.FlowerDetailResponse; @@ -42,6 +43,17 @@ public ApiResponse updateFlower(@RequestBody FlowerUpdateReque return ApiResponse.onSuccess(flowerService.updateFlower(request)); } + /** + * 꽃 이미지를 업데이트합니다. + * @param request 업데이트 할 꽃과 이미지에 대한 DTO입니다. + * @return 업데이트된 꽃의 id가 반환됩니다. + */ + @PostMapping("/image") + @Operation(summary = "꽃 이미지 생성 API") + public ApiResponse insertImage(@RequestBody FlowerInsertImageRequest request) { + return ApiResponse.onSuccess(flowerService.insertImage(request)); + } + /** * 꽃 전체 정보를 조회합니다. * @return 꽃 전체 정보를 리스트로 반환합니다. diff --git a/src/main/java/zerobibim/flory/domain/flower/dto/request/FlowerInsertImageRequest.java b/src/main/java/zerobibim/flory/domain/flower/dto/request/FlowerInsertImageRequest.java new file mode 100644 index 0000000..6367dab --- /dev/null +++ b/src/main/java/zerobibim/flory/domain/flower/dto/request/FlowerInsertImageRequest.java @@ -0,0 +1,13 @@ +package zerobibim.flory.domain.flower.dto.request; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class FlowerInsertImageRequest { + private Long flowerId; + private Long imageId; +} diff --git a/src/main/java/zerobibim/flory/domain/flower/service/FlowerService.java b/src/main/java/zerobibim/flory/domain/flower/service/FlowerService.java index 5c12485..19f0a35 100644 --- a/src/main/java/zerobibim/flory/domain/flower/service/FlowerService.java +++ b/src/main/java/zerobibim/flory/domain/flower/service/FlowerService.java @@ -3,6 +3,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import zerobibim.flory.domain.Image.entity.Image; +import zerobibim.flory.domain.Image.service.ImageService; +import zerobibim.flory.domain.flower.dto.request.FlowerInsertImageRequest; import zerobibim.flory.domain.flower.dto.request.FlowerCreateReqeust; import zerobibim.flory.domain.flower.dto.request.FlowerUpdateRequest; import zerobibim.flory.domain.flower.dto.response.FlowerDetailResponse; @@ -22,6 +25,7 @@ public class FlowerService implements EntityLoader { private final FlowerRepository flowerRepository; private final FlowerMapper flowerMapper; + private final ImageService imageService; public FlowerIdResponse createFlower(FlowerCreateReqeust reqeust) { // 중복 꽃 여부 확인 @@ -45,6 +49,15 @@ public FlowerIdResponse updateFlower(FlowerUpdateRequest request) { return new FlowerIdResponse(flower.getId()); } + @Transactional + public FlowerIdResponse insertImage(FlowerInsertImageRequest request) { + Flower flower = loadEntity(request.getFlowerId()); + Image image = imageService.loadEntity(request.getImageId()); + + flower.updateFlowerImage(image); + return new FlowerIdResponse(flower.getId()); + } + public List findAllFlower() { return flowerRepository.findAll().stream().map(flowerMapper::toResponse).toList(); } diff --git a/src/main/java/zerobibim/flory/global/common/ApiPayload/code/status/ErrorStatus.java b/src/main/java/zerobibim/flory/global/common/ApiPayload/code/status/ErrorStatus.java index f63bcaf..49ab6f9 100644 --- a/src/main/java/zerobibim/flory/global/common/ApiPayload/code/status/ErrorStatus.java +++ b/src/main/java/zerobibim/flory/global/common/ApiPayload/code/status/ErrorStatus.java @@ -25,6 +25,7 @@ public enum ErrorStatus implements BaseErrorCode { // 꽃 관련 응답 FLOWER_NOT_FOUND(HttpStatus.BAD_REQUEST, "FLOWER4001", "존재하지 않는 꽃입니다."), FLOWER_EXISTED(HttpStatus.BAD_REQUEST, "FLOWER4002","이미 존재하는 꽃입니다."), + NO_IMAGE_IN_FLOWER(HttpStatus.BAD_REQUEST, "FLOWER4003", "꽃에 이미지가 존재하지 않습니다."), // 기념일 관련 응답 // 구매 관련 응답 @@ -36,6 +37,7 @@ public enum ErrorStatus implements BaseErrorCode { // 이미지 관련 응답 IMAGE_BLANK(HttpStatus.BAD_REQUEST, "IMAGE4001", "이미지 파일이 없습니다."), + IMAGE_NOT_FOUND(HttpStatus.BAD_REQUEST, "IMAGE4002", "존재하지 않는 이미지입니다."), ; From 2016a5e313b5f9a9b32654ee87f296f981a2efad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A4=80=EC=8D=A8=ED=81=AC?= Date: Tue, 14 Nov 2023 22:10:03 +0900 Subject: [PATCH 7/7] =?UTF-8?q?feat:=20=EA=B5=AC=EB=A7=A4=20=20API=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flory/domain/Image/entity/Image.java | 9 +++++++++ .../domain/Image/service/ImageService.java | 18 +++++++++++++++++- .../dto/request/PurchaseCreateRequest.java | 1 - .../flory/domain/purchase/entity/Purchase.java | 5 +---- .../domain/purchase/mapper/PurchaseMapper.java | 3 +-- .../purchase/service/PurchaseService.java | 6 +++++- 6 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/main/java/zerobibim/flory/domain/Image/entity/Image.java b/src/main/java/zerobibim/flory/domain/Image/entity/Image.java index 0b9a226..6ae4ddc 100644 --- a/src/main/java/zerobibim/flory/domain/Image/entity/Image.java +++ b/src/main/java/zerobibim/flory/domain/Image/entity/Image.java @@ -21,12 +21,21 @@ public class Image extends BaseTime { private Long senderId; + private Long receiverId; + private Boolean isNFT; @Builder public Image(String url) { this.url = url; this.senderId = null; + this.receiverId = null; this.isNFT = Boolean.FALSE; } + + public void updateImage(Long senderId, Long receiverId) { + this.senderId = senderId; + this.receiverId = receiverId; + this.isNFT = Boolean.TRUE; + } } diff --git a/src/main/java/zerobibim/flory/domain/Image/service/ImageService.java b/src/main/java/zerobibim/flory/domain/Image/service/ImageService.java index 7d10462..e645fc4 100644 --- a/src/main/java/zerobibim/flory/domain/Image/service/ImageService.java +++ b/src/main/java/zerobibim/flory/domain/Image/service/ImageService.java @@ -9,12 +9,15 @@ import zerobibim.flory.domain.Image.mapper.ImageMapper; import zerobibim.flory.domain.Image.repository.ImageRepository; import zerobibim.flory.global.common.ApiPayload.code.status.ErrorStatus; +import zerobibim.flory.global.common.EntityLoader; import zerobibim.flory.global.common.ExceptionHandler; import zerobibim.flory.utils.S3ImageComponent; +import java.util.Optional; + @Service @RequiredArgsConstructor -public class ImageService { +public class ImageService implements EntityLoader { private final ImageMapper imageMapper; private final ImageRepository imageRepository; @@ -35,4 +38,17 @@ private String uploadImage(final MultipartFile flowerImage) { } return s3ImageComponent.uploadImage("flower-image", flowerImage); } + + @Transactional + public void makeNft(Long imageId, Long senderId, Long receiverId) { + Image image = loadEntity(imageId); + image.updateImage(senderId, receiverId); + } + + @Override + public Image loadEntity(Long id) { + Optional image = imageRepository.findImageById(id); + if(image.isEmpty()) throw new ExceptionHandler(ErrorStatus.IMAGE_NOT_FOUND); + return image.get(); + } } diff --git a/src/main/java/zerobibim/flory/domain/purchase/dto/request/PurchaseCreateRequest.java b/src/main/java/zerobibim/flory/domain/purchase/dto/request/PurchaseCreateRequest.java index e9f54ad..e176e78 100644 --- a/src/main/java/zerobibim/flory/domain/purchase/dto/request/PurchaseCreateRequest.java +++ b/src/main/java/zerobibim/flory/domain/purchase/dto/request/PurchaseCreateRequest.java @@ -14,7 +14,6 @@ public class PurchaseCreateRequest { private String receiverNickname; private Long flowerId; private int flowerQuentity; - private String nftComment; private LocalDate receiveDate; private int deliveryTip; private int totalPrice; diff --git a/src/main/java/zerobibim/flory/domain/purchase/entity/Purchase.java b/src/main/java/zerobibim/flory/domain/purchase/entity/Purchase.java index e27ff05..94888da 100644 --- a/src/main/java/zerobibim/flory/domain/purchase/entity/Purchase.java +++ b/src/main/java/zerobibim/flory/domain/purchase/entity/Purchase.java @@ -39,8 +39,6 @@ public class Purchase extends BaseTime { private int flowerCnt; - private String nftComment; - private int deliveryTip; private int totalPrice; @@ -50,7 +48,7 @@ public class Purchase extends BaseTime { @Builder public Purchase(Member sender, Member receiver, LocalDate receiveDate, Flower flower, String receiverName, String receiverAddress, - int flowerCnt, String nftComment, int deliveryTip, int totalPrice) { + int flowerCnt, int deliveryTip, int totalPrice) { this.sender = sender; this.receiver = receiver; this.receiveDate = receiveDate; @@ -58,7 +56,6 @@ public Purchase(Member sender, Member receiver, LocalDate receiveDate, this.receiverAddress = receiverAddress; this.flower = flower; this.flowerCnt = flowerCnt; - this.nftComment = nftComment; this.deliveryTip = deliveryTip; this.totalPrice = totalPrice; this.isDelivered = Boolean.FALSE; diff --git a/src/main/java/zerobibim/flory/domain/purchase/mapper/PurchaseMapper.java b/src/main/java/zerobibim/flory/domain/purchase/mapper/PurchaseMapper.java index 5d6eec4..e0d8804 100644 --- a/src/main/java/zerobibim/flory/domain/purchase/mapper/PurchaseMapper.java +++ b/src/main/java/zerobibim/flory/domain/purchase/mapper/PurchaseMapper.java @@ -11,7 +11,7 @@ public class PurchaseMapper { public Purchase toEntity(Member sender, Member receiver, LocalDate receiveDate, Flower flower, String receiverName, String receiverAddress, - int flowerCnt, String nftComment, int deliveryTip, int totalPrice) { + int flowerCnt, int deliveryTip, int totalPrice) { return Purchase.builder() .sender(sender) .receiver(receiver) @@ -20,7 +20,6 @@ public Purchase toEntity(Member sender, Member receiver, LocalDate receiveDate, .receiverName(receiverName) .receiverAddress(receiverAddress) .flowerCnt(flowerCnt) - .nftComment(nftComment) .deliveryTip(deliveryTip) .totalPrice(totalPrice) .build(); diff --git a/src/main/java/zerobibim/flory/domain/purchase/service/PurchaseService.java b/src/main/java/zerobibim/flory/domain/purchase/service/PurchaseService.java index 03e290f..da86319 100644 --- a/src/main/java/zerobibim/flory/domain/purchase/service/PurchaseService.java +++ b/src/main/java/zerobibim/flory/domain/purchase/service/PurchaseService.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import zerobibim.flory.domain.Image.service.ImageService; import zerobibim.flory.domain.flower.entity.Flower; import zerobibim.flory.domain.flower.service.FlowerService; import zerobibim.flory.domain.member.entity.Member; @@ -24,16 +25,19 @@ public class PurchaseService implements EntityLoader { private final PurchaseMapper purchaseMapper; private final MemberService memberService; private final FlowerService flowerService; + private final ImageService imageService; public PurchaseIdResponse createPurchase(PurchaseCreateRequest request) { Member sender = memberService.loadEntity(request.getMemberId()); Member receiver = memberService.findMemberByNickname(request.getReceiverNickname()); Flower flower = flowerService.loadEntity(request.getFlowerId()); + if(flower.getImage() == null) throw new ExceptionHandler(ErrorStatus.NO_IMAGE_IN_FLOWER); + imageService.makeNft(flower.getImage().getId(), sender.getId(), receiver.getId()); Purchase newPurchase = purchaseRepository.save( purchaseMapper.toEntity( sender, receiver, request.getReceiveDate(), flower, request.getReceiverName(), request.getReceiverAddress(), - request.getFlowerQuentity(), request.getNftComment(), request.getDeliveryTip(), request.getTotalPrice() + request.getFlowerQuentity(), request.getDeliveryTip(), request.getTotalPrice() )); return new PurchaseIdResponse(newPurchase.getId());