Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.DecodEat.domain.users.entity.User;
import com.DecodEat.global.apiPayload.ApiResponse;
import com.DecodEat.global.common.annotation.CurrentUser;
import com.DecodEat.global.common.annotation.OptionalUser;
import com.DecodEat.global.dto.PageResponseDto;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand Down Expand Up @@ -34,8 +35,9 @@ public class ProductController {
summary = "제품 조회",
description = "단일 제품 상세 정보 조회")
@GetMapping("/{id}")
public ApiResponse<ProductDetailDto> getProduct(@PathVariable Long id) {
return ApiResponse.onSuccess(productService.getDetail(id));
public ApiResponse<ProductDetailDto> getProduct(@PathVariable Long id,
@OptionalUser User user) {
return ApiResponse.onSuccess(productService.getDetail(id,user));
}

@Operation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
import com.DecodEat.domain.products.repository.ProductRepository;
import com.DecodEat.domain.products.repository.RawMaterialRepository;
import com.DecodEat.domain.products.repository.ProductSpecification;
import com.DecodEat.domain.users.entity.Behavior;
import com.DecodEat.domain.users.entity.User;
import com.DecodEat.domain.users.service.UserBehaviorService;
import com.DecodEat.global.aws.s3.AmazonS3Manager;
import com.DecodEat.global.dto.PageResponseDto;
import com.DecodEat.global.exception.GeneralException;
Expand Down Expand Up @@ -52,13 +54,17 @@ public class ProductService {
private final ProductRawMaterialRepository productRawMaterialRepository;
private final AmazonS3Manager amazonS3Manager;
private final PythonAnalysisClient pythonAnalysisClient;
private final UserBehaviorService userBehaviorService;


private static final int PAGE_SIZE = 12;

public ProductDetailDto getDetail(Long id) {
public ProductDetailDto getDetail(Long id, User user) {
Product product = productRepository.findById(id).orElseThrow(() -> new GeneralException(PRODUCT_NOT_EXISTED));

if(user != null)
userBehaviorService.saveUserBehavior(user,product, Behavior.VIEW);

List<ProductInfoImage> images = productImageRepository.findByProduct(product);
List<String> imageUrls = images.stream().map(ProductInfoImage::getImageUrl).toList();

Expand Down Expand Up @@ -106,6 +112,8 @@ public ProductRegisterResponseDto addProduct(User user, ProductRegisterRequestDt
// 파이썬 서버에 비동기로 분석 요청
requestAnalysisAsync(savedProduct.getProductId(), productInfoImageUrls);

userBehaviorService.saveUserBehavior(user,savedProduct,Behavior.REGISTER); // todo: 만약에 분석 실패?

return ProductConverter.toProductRegisterDto(savedProduct, productInfoImageUrls);
}

Expand All @@ -117,6 +125,7 @@ public ProductResponseDTO.ProductListResultDTO getProducts(Long cursorId) {
return ProductConverter.toProductListResultDTO(slice);
}

// todo: 검색은 상품 엔티티와 1:1 매핑 불가능 -> userbehavior 어떻게?
public List<ProductSearchResponseDto.SearchResultPrevDto> searchProducts(String productName) {

Specification<Product> spec = Specification.where(ProductSpecification.isCompleted());
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/DecodEat/domain/users/entity/Behavior.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.DecodEat.domain.users.entity;

public enum Behavior {
VIEW,LIKE,REGISTER,SEARCH
}
32 changes: 32 additions & 0 deletions src/main/java/com/DecodEat/domain/users/entity/UserBehavior.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.DecodEat.domain.users.entity;

import com.DecodEat.domain.products.entity.Product;
import com.DecodEat.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table
@Builder
@AllArgsConstructor
public class UserBehavior extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "product_id", nullable = false)
private Product product;

@Enumerated(EnumType.STRING)
@Column(name = "behavior", nullable = false)
private Behavior behavior;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.DecodEat.domain.users.repository;

import com.DecodEat.domain.users.entity.UserBehavior;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserBehaviorRepository extends JpaRepository<UserBehavior, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.DecodEat.domain.users.service;

import com.DecodEat.domain.products.entity.Product;
import com.DecodEat.domain.users.entity.Behavior;
import com.DecodEat.domain.users.entity.User;
import com.DecodEat.domain.users.entity.UserBehavior;
import com.DecodEat.domain.users.repository.UserBehaviorRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class UserBehaviorService {
private final UserBehaviorRepository userBehaviorRepository;
@Transactional
public void saveUserBehavior(User user, Product product, Behavior behavior) {
UserBehavior userBehavior = UserBehavior.builder()
.user(user)
.product(product)
.behavior(behavior)
.build();
userBehaviorRepository.save(userBehavior);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package com.DecodEat.global.config;
package com.DecodEat.global.common.annotation;

import com.DecodEat.domain.users.service.UserService;
import com.DecodEat.global.apiPayload.code.status.ErrorStatus;
import com.DecodEat.global.common.annotation.CurrentUser;
import com.DecodEat.global.exception.GeneralException;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.DecodEat.global.common.annotation;

import io.swagger.v3.oas.annotations.Parameter;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Parameter(hidden = true)
public @interface OptionalUser {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.DecodEat.global.common.annotation;

import com.DecodEat.domain.users.entity.User;
import com.DecodEat.domain.users.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

@Component
@RequiredArgsConstructor
public class OptionalUserArgumentResolver implements HandlerMethodArgumentResolver {
private final UserService userService;

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterAnnotation(OptionalUser.class) != null
&& parameter.getParameterType().equals(User.class);
}

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

if (principal instanceof org.springframework.security.core.userdetails.User springUser) {
Long userId = Long.valueOf(springUser.getUsername());
return userService.findById(userId);
}

// 예외를 던지는 대신 null을 반환!
return null;
}
}
1 change: 1 addition & 0 deletions src/main/java/com/DecodEat/global/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.DecodEat.global.config;

import com.DecodEat.global.common.annotation.CurrentUserArgumentResolver;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
Expand Down
Loading