diff --git a/pom.xml b/pom.xml
index bbdda38..f1c2606 100644
--- a/pom.xml
+++ b/pom.xml
@@ -68,6 +68,15 @@
jackson-core
3.2.0
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ software.amazon.awssdk
+ s3
+ 2.25.0
+
diff --git a/src/main/java/net/hackyourfuture/hyfshop/product/AppConfig.java b/src/main/java/net/hackyourfuture/hyfshop/product/AppConfig.java
new file mode 100644
index 0000000..d2ad17b
--- /dev/null
+++ b/src/main/java/net/hackyourfuture/hyfshop/product/AppConfig.java
@@ -0,0 +1,14 @@
+package net.hackyourfuture.hyfshop.product;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class AppConfig {
+
+ @Bean
+ public ObjectMapper objectMapper(){
+ return new ObjectMapper();
+ }
+}
diff --git a/src/main/java/net/hackyourfuture/hyfshop/product/B2Config.java b/src/main/java/net/hackyourfuture/hyfshop/product/B2Config.java
new file mode 100644
index 0000000..71e3d48
--- /dev/null
+++ b/src/main/java/net/hackyourfuture/hyfshop/product/B2Config.java
@@ -0,0 +1,41 @@
+package net.hackyourfuture.hyfshop.product;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.s3.S3Client;
+import software.amazon.awssdk.services.s3.presigner.S3Presigner;
+
+import java.net.URI;
+
+@Configuration
+public class B2Config {
+
+ @Value("${b2.endpoint}") private String endpoint;
+ @Value("${b2.region}") private String region;
+ @Value("${b2.access-key}") private String accessKey;
+ @Value("${b2.secret-key}") private String secretKey;
+
+ @Bean
+ public S3Client s3Client() {
+ return S3Client.builder()
+ .credentialsProvider(StaticCredentialsProvider.create(
+ AwsBasicCredentials.create(accessKey, secretKey)))
+ .endpointOverride(URI.create(endpoint))
+ .region(Region.of(region))
+ .build();
+ }
+
+ @Bean
+ public S3Presigner s3Presigner() {
+ return S3Presigner.builder()
+ .credentialsProvider(StaticCredentialsProvider.create(
+ AwsBasicCredentials.create(accessKey, secretKey)))
+ .endpointOverride(URI.create(endpoint))
+ .region(Region.of(region))
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/hackyourfuture/hyfshop/product/FileService.java b/src/main/java/net/hackyourfuture/hyfshop/product/FileService.java
new file mode 100644
index 0000000..d82c6e2
--- /dev/null
+++ b/src/main/java/net/hackyourfuture/hyfshop/product/FileService.java
@@ -0,0 +1,50 @@
+package net.hackyourfuture.hyfshop.product;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+import software.amazon.awssdk.core.sync.RequestBody;
+import software.amazon.awssdk.services.s3.S3Client;
+import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
+import software.amazon.awssdk.services.s3.model.PutObjectRequest;
+import software.amazon.awssdk.services.s3.presigner.S3Presigner;
+
+import java.util.UUID;
+
+@Service
+
+public class FileService {
+
+ private final S3Client s3Client;
+ private final S3Presigner s3Presigner;
+
+ @Value("${b2.bucket}")
+ private String bucket;
+
+ @Value("${b2.endpoint}")
+ private String endpoint;
+
+ public FileService(S3Client s3Client, S3Presigner s3Presigner) {
+ this.s3Client = s3Client;
+ this.s3Presigner = s3Presigner;
+ }
+
+ public String upload(MultipartFile file) throws Exception {
+ String key = "uploads/" + UUID.randomUUID() + "-" + file.getOriginalFilename();
+ s3Client.putObject(
+ PutObjectRequest.builder()
+ .bucket(bucket).key(key)
+ .contentType(file.getContentType()).build(),
+ RequestBody.fromInputStream(file.getInputStream(), file.getSize())
+ );
+ return endpoint + "/" + bucket + "/" + key;
+ }
+
+ public void delete(String imageUrl) {
+ String key = imageUrl.replace(endpoint + "/" + bucket + "/", "");
+ s3Client.deleteObject(
+ DeleteObjectRequest.builder()
+ .bucket(bucket).key(key).build()
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/hackyourfuture/hyfshop/product/Product.java b/src/main/java/net/hackyourfuture/hyfshop/product/Product.java
index ccc6204..a3108b9 100644
--- a/src/main/java/net/hackyourfuture/hyfshop/product/Product.java
+++ b/src/main/java/net/hackyourfuture/hyfshop/product/Product.java
@@ -6,6 +6,7 @@
import lombok.Setter;
import java.math.BigDecimal;
+import java.util.Map;
@AllArgsConstructor
@NoArgsConstructor
@@ -17,4 +18,5 @@ public class Product {
private BigDecimal price;
private String category;
private String imageUrl;
+ private Map details;
}
diff --git a/src/main/java/net/hackyourfuture/hyfshop/product/ProductController.java b/src/main/java/net/hackyourfuture/hyfshop/product/ProductController.java
index 9e6c162..78feccf 100644
--- a/src/main/java/net/hackyourfuture/hyfshop/product/ProductController.java
+++ b/src/main/java/net/hackyourfuture/hyfshop/product/ProductController.java
@@ -20,8 +20,13 @@ public List getProducts() {
return productService.getAllProducts();
}
- @GetMapping("/search")
- public List searchProducts(@Nullable @RequestParam("color") String color) {
+ @GetMapping("/{id}")
+ public ProductResponse getProductById(@PathVariable int id){
+ return productService.getProductById(id);
+ }
+
+ @GetMapping(params = "color")
+ public List searchProducts(@Nullable @RequestParam String color) {
if (color == null) {
return productService.getAllProducts();
}
@@ -34,12 +39,13 @@ public ProductResponse setProductSize(@PathVariable int id, @RequestBody SetSize
}
@PutMapping("/{id}/image")
- public ProductResponse setProductImage(@PathVariable int id, @RequestBody MultipartFile file) {
+ public ProductResponse setProductImage(@PathVariable int id, @RequestParam("file") MultipartFile file) throws Exception {
return productService.setProductImage(id, file);
}
@DeleteMapping("/{id}/image")
public ProductResponse deleteProductImage(@PathVariable int id) {
+
return productService.deleteProductImage(id);
}
}
diff --git a/src/main/java/net/hackyourfuture/hyfshop/product/ProductRepository.java b/src/main/java/net/hackyourfuture/hyfshop/product/ProductRepository.java
index 2766734..3e0f0e4 100644
--- a/src/main/java/net/hackyourfuture/hyfshop/product/ProductRepository.java
+++ b/src/main/java/net/hackyourfuture/hyfshop/product/ProductRepository.java
@@ -1,29 +1,46 @@
package net.hackyourfuture.hyfshop.product;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
+import net.hackyourfuture.hyfshop.product.dto.ProductResponse;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.stereotype.Repository;
import java.util.List;
+import java.util.Map;
@Repository
@AllArgsConstructor
public class ProductRepository {
private final JdbcClient jdbcClient;
+ private final ObjectMapper objectMapper = new ObjectMapper();
- public static final RowMapper PRODUCT_ROW_MAPPER = (rs, _) -> {
+ public final RowMapper PRODUCT_ROW_MAPPER = (rs, _) -> {
var product = new Product();
product.setId(rs.getInt("id"));
product.setTitle(rs.getString("title"));
product.setPrice(rs.getBigDecimal("price"));
product.setCategory(rs.getString("category"));
product.setImageUrl(rs.getString("image_url"));
+
+ try{
+ String json = rs.getString("details");
+ if(json != null){
+ product.setDetails(objectMapper.readValue(json,
+ new TypeReference