Skip to content

Commit

Permalink
Merge pull request #69 from kakao-tech-campus-2nd-step3/weekly
Browse files Browse the repository at this point in the history
Weekly 정기 병합
  • Loading branch information
yooonwodyd authored Nov 2, 2024
2 parents 6dd292f + 4a31669 commit a6df80c
Show file tree
Hide file tree
Showing 48 changed files with 1,017 additions and 158 deletions.
7 changes: 7 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,14 @@ dependencies {

//S3
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

//Test
testImplementation 'org.testcontainers:testcontainers:1.20.2'
testImplementation 'org.testcontainers:junit-jupiter:1.20.2'
testImplementation 'org.testcontainers:mysql:1.20.2'

}

tasks.named('test') {
useJUnitPlatform()
}
2 changes: 2 additions & 0 deletions src/main/java/com/helpmeCookies/Step3Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 Step3Application {

public static void main(String[] args) {
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/com/helpmeCookies/global/entity/BaseTimeEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.helpmeCookies.global.entity;

import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import java.time.LocalDateTime;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Getter
public abstract class BaseTimeEntity {

@CreatedDate
private LocalDateTime createdDate;

@LastModifiedDate
private LocalDateTime modifiedDate;
}
32 changes: 13 additions & 19 deletions src/main/java/com/helpmeCookies/global/utils/AwsS3FileUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.helpmeCookies.product.dto.FileUploadResponse;
import com.helpmeCookies.product.dto.ImageUpload;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
Expand All @@ -14,7 +14,6 @@

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
Expand All @@ -28,26 +27,21 @@ public class AwsS3FileUtils {
private String bucket;

//다중파일 업로드후 url 반환
public List<FileUploadResponse> uploadMultiImages(List<MultipartFile> multipartFiles) {
List<FileUploadResponse> fileList = new ArrayList<>();
public ImageUpload uploadMultiImages(MultipartFile multipartFile) {

multipartFiles.forEach(file -> {
String fileName = createFileName(file.getOriginalFilename()); //파일 이름 난수화
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(file.getSize());
objectMetadata.setContentType(file.getContentType());
String fileName = createFileName(multipartFile.getOriginalFilename()); //파일 이름 난수화
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(multipartFile.getSize());
objectMetadata.setContentType(multipartFile.getContentType());

try (InputStream inputStream = file.getInputStream()) {
amazonS3.putObject(new PutObjectRequest(bucket, fileName, inputStream, objectMetadata)
.withCannedAcl(CannedAccessControlList.PublicRead));
} catch (IOException e) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "파일 업로드 실패" + fileName);
}

fileList.add(new FileUploadResponse(amazonS3.getUrl(bucket,fileName).toString(),fileName));
});
try (InputStream inputStream = multipartFile.getInputStream()) {
amazonS3.putObject(new PutObjectRequest(bucket, fileName, inputStream, objectMetadata)
.withCannedAcl(CannedAccessControlList.PublicRead));
} catch (IOException e) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "파일 업로드 실패" + fileName);
}

return fileList;
return new ImageUpload(amazonS3.getUrl(bucket,fileName).toString());
}

public String createFileName(String fileName) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,51 @@
package com.helpmeCookies.product.controller;

import com.helpmeCookies.product.dto.FileUploadResponse;
import com.helpmeCookies.product.dto.ImageUpload;
import static com.helpmeCookies.product.util.SortUtil.convertProductSort;

import com.helpmeCookies.product.controller.docs.ProductApiDocs;
import com.helpmeCookies.product.dto.ProductImageResponse;
import com.helpmeCookies.product.dto.ProductPage;
import com.helpmeCookies.product.dto.ProductRequest;
import com.helpmeCookies.product.dto.ProductResponse;
import com.helpmeCookies.product.entity.Product;
import com.helpmeCookies.product.service.ProductImageService;
import com.helpmeCookies.product.service.ProductService;
import com.helpmeCookies.product.util.ProductSort;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;

@RestController
@RequestMapping("/api/v1/products")
@RequestMapping("/v1/products")
@RequiredArgsConstructor
public class ProductController {
public class ProductController implements ProductApiDocs {

private final ProductService productService;
private final ProductImageService productImageService;

@PostMapping
public ResponseEntity<Void> saveProduct(@RequestBody ProductRequest productRequest) {
productService.save(productRequest);
Product product = productService.save(productRequest);
productImageService.saveImages(product.getId(),productRequest.imageUrls());
return ResponseEntity.ok().build();
}

@PostMapping("/{productId}/images")
public ResponseEntity<ProductImageResponse> uploadImages(@PathVariable("productId") Long productId, List<MultipartFile> files) throws IOException {
List<FileUploadResponse> responses = productImageService.uploadMultiFiles(productId,files);
return ResponseEntity.ok(new ProductImageResponse(responses.stream().map(FileUploadResponse::photoUrl).toList()));
@PostMapping("/images")
public ResponseEntity<ProductImageResponse> uploadImages(List<MultipartFile> files) {
List<ImageUpload> responses = productImageService.uploadMultiFiles(files);
return ResponseEntity.ok(new ProductImageResponse(responses.stream().map(ImageUpload::photoUrl).toList()));
}

@GetMapping("/{productId}")
public ResponseEntity<ProductResponse> getProductInfo(@PathVariable("productId") Long productId) {
Product product = productService.find(productId);
return ResponseEntity.ok(ProductResponse.from(product));
List<String> urls = productImageService.getImages(productId);
return ResponseEntity.ok(ProductResponse.from(product,urls));
}

@PutMapping("/{productId}")
Expand All @@ -49,8 +56,11 @@ public ResponseEntity<Void> editProductInfo(@PathVariable("productId") Long prod
}

@PutMapping("/{productId}/images")
public ResponseEntity<Void> editImages(@PathVariable("productId") Long productId, List<MultipartFile> files) throws IOException {
public ResponseEntity<Void> editImages(@PathVariable("productId") Long productId, List<MultipartFile> files) {
productImageService.editImages(productId, files);
List<String> images = productImageService.uploadMultiFiles(files).stream()
.map(ImageUpload::photoUrl).toList();
productImageService.saveImages(productId,images);
return ResponseEntity.ok().build();
}

Expand All @@ -59,4 +69,17 @@ public ResponseEntity<Void> deleteProduct(@PathVariable("productId") Long produc
productService.delete(productId);
return ResponseEntity.noContent().build();
}

@GetMapping
public ResponseEntity<ProductPage.Paging> getProductsByPage(
@RequestParam("query") String query,
@RequestParam(name = "size", required = false, defaultValue = "20") int size,
@RequestParam("page") int page,
@RequestParam("sort") ProductSort productSort
) {
var sort = convertProductSort(productSort);
var pageable = PageRequest.of(page, size, sort);

return ResponseEntity.ok(productService.getProductsByPage(query, pageable));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.helpmeCookies.product.controller.docs;

import com.helpmeCookies.product.dto.ProductPage.Paging;
import com.helpmeCookies.product.util.ProductSort;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;

@Tag(name = "상품 관련 기능", description = "상품 관련 API")
public interface ProductApiDocs {

@Operation(summary = "상품 검색")
ResponseEntity<Paging> getProductsByPage(
String query,
@Parameter(description = "default value 20") int size,
int page,
ProductSort productSort
);

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

import com.helpmeCookies.product.entity.ProductImage;

public record FileUploadResponse(
String photoUrl,
String uuid
public record ImageUpload(
String photoUrl
) {
public ProductImage toEntity(Long productId) {
return ProductImage.builder()
.productId(productId)
.photoUrl(photoUrl)
.uuid(uuid)
.build();
}
}
46 changes: 46 additions & 0 deletions src/main/java/com/helpmeCookies/product/dto/ProductPage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.helpmeCookies.product.dto;

import com.helpmeCookies.product.repository.dto.ProductSearch;
import java.util.List;
import org.springframework.data.domain.Page;

public class ProductPage {

public record Info(
Long id,
String name,
String artist,
Long price,
String thumbnailUrl
) {
public static Info from(ProductSearch productSearch) {
return new Info(
productSearch.getId(),
productSearch.getName(),
productSearch.getArtist(),
productSearch.getPrice(),
productSearch.getThumbnailUrl()
);
}

public static List<Info> of(List<ProductSearch> content) {
return content.stream()
.map(Info::from)
.toList();
}
}

public record Paging (
boolean hasNext,
List<Info> products
) {

public static Paging from(Page<ProductSearch> productPage) {
return new Paging(
productPage.hasNext(),
Info.of(productPage.getContent())
);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ public record ProductRequest(
String description,
String preferredLocation,
List<HashTag> hashTags,
Long artistInfo
Long artistInfoId,
List<String> imageUrls

) {
public Product toEntity(ArtistInfo artistInfo) {
return Product.builder()
Expand Down
28 changes: 6 additions & 22 deletions src/main/java/com/helpmeCookies/product/dto/ProductResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,13 @@ public record ProductResponse(
String description,
String preferredLocation,
List<HashTag> hashTags,
ArtistInfo artistInfo
ArtistInfo artistInfo,
List<String> imageUrls
) {
public static class ArtistInfo {
private final Long artistId;
private final String name;

public ArtistInfo(Long artistId, String name) {
this.artistId = artistId;
this.name = name;
}

public Long getArtistId() {
return artistId;
}

public String getName() {
return name;
}
public record ArtistInfo(Long artistId, String artistName) {
}

public static ProductResponse from(Product product) {
//TODO artistInfo 코드 개발 이후 수정 예정
public static ProductResponse from(Product product, List<String> urls) {
return new ProductResponse(
product.getId(),
product.getName(),
Expand All @@ -45,9 +30,8 @@ public static ProductResponse from(Product product) {
product.getDescription(),
product.getPreferredLocation(),
product.getHashTags(),
new ArtistInfo(
1L, "임시"
)
new ArtistInfo(product.getArtistInfo().getId(),product.getArtistInfo().getNickname()),
urls
);
}
}
11 changes: 10 additions & 1 deletion src/main/java/com/helpmeCookies/product/entity/Product.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.helpmeCookies.product.entity;

import com.helpmeCookies.global.entity.BaseTimeEntity;
import jakarta.persistence.JoinColumn;
import java.util.List;

import com.helpmeCookies.user.entity.ArtistInfo;
Expand All @@ -17,7 +19,7 @@
import lombok.Builder;

@Entity
public class Product {
public class Product extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand All @@ -42,12 +44,15 @@ public class Product {
@Column(nullable = false)
private String preferredLocation;

private String thumbnailUrl;

@ElementCollection(targetClass = HashTag.class)
@CollectionTable(name = "product_hashtags")
@Enumerated(EnumType.STRING)
private List<HashTag> hashTags;

@ManyToOne
@JoinColumn(name = "artist_info_id")
private ArtistInfo artistInfo;

public Product() {}
Expand Down Expand Up @@ -110,4 +115,8 @@ public void update(String name, Category category, String size, Long price, Stri
this.hashTags = hashTags;
this.artistInfo = artistInfo;
}

public void updateThumbnail(String thumbnailUrl) {
this.thumbnailUrl = thumbnailUrl;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,9 @@ public ProductImage(String photoUrl, Long productId, String uuid) {
this.productId = productId;
this.uuid = uuid;
}

public String getPhotoUrl() {
return photoUrl;
}
}

Loading

0 comments on commit a6df80c

Please sign in to comment.