From e676cf68284118ac2ebe2e616d57f29f7eb612fb Mon Sep 17 00:00:00 2001 From: inswal843 Date: Wed, 5 Feb 2025 14:50:12 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=EC=B5=9C=EA=B7=BC=20=EB=B3=B8=20=EC=83=81?= =?UTF-8?q?=ED=92=88=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84(=EC=BF=A0?= =?UTF-8?q?=ED=82=A4=20=EC=9D=B4=EC=9A=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ProductRecentController.java | 95 +++++++++++++++++++ .../product/dto/ProductRecentDto.java | 16 ++++ .../product/service/ProductRecentService.java | 10 ++ .../service/ProductRecentServiceImpl.java | 66 +++++++++++++ .../product/service/ProductServiceImpl.java | 4 +- 5 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 src/main/java/kw/zeropick/product/controller/ProductRecentController.java create mode 100644 src/main/java/kw/zeropick/product/dto/ProductRecentDto.java create mode 100644 src/main/java/kw/zeropick/product/service/ProductRecentService.java create mode 100644 src/main/java/kw/zeropick/product/service/ProductRecentServiceImpl.java diff --git a/src/main/java/kw/zeropick/product/controller/ProductRecentController.java b/src/main/java/kw/zeropick/product/controller/ProductRecentController.java new file mode 100644 index 0000000..672a990 --- /dev/null +++ b/src/main/java/kw/zeropick/product/controller/ProductRecentController.java @@ -0,0 +1,95 @@ +package kw.zeropick.product.controller; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; +import kw.zeropick.payload.ApiResponse; +import kw.zeropick.product.dto.ProductDto; +import kw.zeropick.product.dto.ProductRecentDto; +import kw.zeropick.product.service.ProductRecentService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/recent") +@RequiredArgsConstructor +public class ProductRecentController { + private final ProductRecentService recentService; + + @PostMapping("/{productId}") + public ResponseEntity addRecentProduct( + @PathVariable Long productId, + HttpServletRequest request, + HttpServletResponse response + ) { + try { + // 쿠키 값 가져오기 + String cookieValue = getCookieValue(request, "recentProducts"); + + // 서비스 호출 + String updatedCookieValue = recentService.addRecentProduct(productId, cookieValue); + + // 쿠키 저장 + Cookie cookie = new Cookie("recentProducts", updatedCookieValue); + cookie.setPath("/"); + cookie.setHttpOnly(true); + cookie.setMaxAge(7 * 24 * 60 * 60); // 7일 + response.addCookie(cookie); + + return ResponseEntity.ok( + ApiResponse.builder() + .check(true) + .information("상품이 최근 본 목록에 추가되었습니다.") + .build() + ); + } catch (Exception e) { + return ResponseEntity.internalServerError().body( + ApiResponse.builder() + .check(false) + .information(e.getMessage()) + .build() + ); + } + } + + @GetMapping + public ResponseEntity getRecentProducts(HttpServletRequest request) { + try { + String cookieValue = getCookieValue(request, "recentProducts"); + + // 서비스 호출 + List recentProducts = recentService.getRecentProducts(cookieValue); + + return ResponseEntity.ok( + ApiResponse.builder() + .check(true) + .information(recentProducts) + .build() + ); + } catch (Exception e) { + return ResponseEntity.internalServerError().body( + ApiResponse.builder() + .check(false) + .information(e.getMessage()) + .build() + ); + } + } + + private String getCookieValue(HttpServletRequest request, String cookieName) { + if (request.getCookies() != null) { + for (Cookie cookie : request.getCookies()) { + if (cookieName.equals(cookie.getName())) { + return cookie.getValue(); + } + } + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/kw/zeropick/product/dto/ProductRecentDto.java b/src/main/java/kw/zeropick/product/dto/ProductRecentDto.java new file mode 100644 index 0000000..5d400f6 --- /dev/null +++ b/src/main/java/kw/zeropick/product/dto/ProductRecentDto.java @@ -0,0 +1,16 @@ +package kw.zeropick.product.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@AllArgsConstructor +@Builder +public class ProductRecentDto { + private Long id; + + private String productName; + + private String imageUrl; +} diff --git a/src/main/java/kw/zeropick/product/service/ProductRecentService.java b/src/main/java/kw/zeropick/product/service/ProductRecentService.java new file mode 100644 index 0000000..7dc1d12 --- /dev/null +++ b/src/main/java/kw/zeropick/product/service/ProductRecentService.java @@ -0,0 +1,10 @@ +package kw.zeropick.product.service; + +import java.util.List; +import kw.zeropick.product.dto.ProductDto; +import kw.zeropick.product.dto.ProductRecentDto; + +public interface ProductRecentService { + List getRecentProducts(String cookieValue) throws Exception; + String addRecentProduct(Long productId, String cookieValue) throws Exception; +} diff --git a/src/main/java/kw/zeropick/product/service/ProductRecentServiceImpl.java b/src/main/java/kw/zeropick/product/service/ProductRecentServiceImpl.java new file mode 100644 index 0000000..daa6174 --- /dev/null +++ b/src/main/java/kw/zeropick/product/service/ProductRecentServiceImpl.java @@ -0,0 +1,66 @@ +package kw.zeropick.product.service; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import kw.zeropick.product.domain.Product; +import kw.zeropick.product.dto.ProductDto; +import kw.zeropick.product.dto.ProductRecentDto; +import kw.zeropick.product.repository.ProductJpaRepository; +import kw.zeropick.product.service.ProductRecentService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class ProductRecentServiceImpl implements ProductRecentService { + private final ProductJpaRepository productJpaRepository; + private final ObjectMapper objectMapper; + private final ProductService productService; + + @Override + public List getRecentProducts(String cookieValue) throws Exception { + if (cookieValue == null || cookieValue.isEmpty()) { + return new ArrayList<>(); + } + String decodedValue = URLDecoder.decode(cookieValue, StandardCharsets.UTF_8); + return objectMapper.readValue(decodedValue, new TypeReference<>() {}); + } + + @Override + public String addRecentProduct(Long productId, String cookieValue) throws Exception { + // 현재 상품 정보 가져오기 + ProductRecentDto productDto = toProductDto(productService.productDetail(productId)); + + // 기존 쿠키 값 디코딩 + List recentProducts = getRecentProducts(cookieValue); + + // 기존 상품 제거 후 추가 + recentProducts.removeIf(product -> product.getId().equals(productId)); + recentProducts.add(productDto); + + // 최대 3개 유지 + if (recentProducts.size() > 3) { + recentProducts.remove(0); + } + + // JSON 문자열로 변환 및 인코딩 + String jsonValue = objectMapper.writeValueAsString(recentProducts); + return URLEncoder.encode(jsonValue, StandardCharsets.UTF_8); + } + + private ProductRecentDto toProductDto(ProductDto product) { + return ProductRecentDto.builder() + .id(product.getId()) + .productName(product.getProductName()) + .imageUrl(product.getImageUrl()) + .build(); + } +} diff --git a/src/main/java/kw/zeropick/product/service/ProductServiceImpl.java b/src/main/java/kw/zeropick/product/service/ProductServiceImpl.java index 96c6a1a..5b6bfa4 100644 --- a/src/main/java/kw/zeropick/product/service/ProductServiceImpl.java +++ b/src/main/java/kw/zeropick/product/service/ProductServiceImpl.java @@ -53,7 +53,7 @@ public ProductDto productDetail(Long productId) { ProductDto productDto = toProductDto(product, null); List artificialSweetDtos = new ArrayList<>(); - if(product.getIngredient().getAllulose() != 0){ + if(product.getIngredient().getAllulose() != null && product.getIngredient().getAllulose() != 0){ ArtificialSweetDto artificialSweetDto = ArtificialSweetDto.builder() .sweetName("알룰로오스") .sweetDetail("알룰로오스 설명") @@ -62,7 +62,7 @@ public ProductDto productDetail(Long productId) { .build(); artificialSweetDtos.add(artificialSweetDto); } - if(product.getIngredient().getErythritol() != 0){ + if(product.getIngredient().getErythritol() != null && product.getIngredient().getErythritol() != 0){ ArtificialSweetDto artificialSweetDto = ArtificialSweetDto.builder() .sweetName("에리트리톨") .sweetDetail("에리트리톨 설명")