Skip to content

Commit 01d980a

Browse files
authored
Merge pull request #45 from Decodeat/feat/42-users-behavior
feat: UserBehavior 상세조회, 등록 추가 / @OptionalUser 어노테이션 추가
2 parents 543c29c + c329cad commit 01d980a

File tree

10 files changed

+138
-5
lines changed

10 files changed

+138
-5
lines changed

src/main/java/com/DecodEat/domain/products/controller/ProductController.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.DecodEat.domain.users.entity.User;
88
import com.DecodEat.global.apiPayload.ApiResponse;
99
import com.DecodEat.global.common.annotation.CurrentUser;
10+
import com.DecodEat.global.common.annotation.OptionalUser;
1011
import com.DecodEat.global.dto.PageResponseDto;
1112
import io.swagger.v3.oas.annotations.Operation;
1213
import io.swagger.v3.oas.annotations.Parameter;
@@ -34,8 +35,9 @@ public class ProductController {
3435
summary = "제품 조회",
3536
description = "단일 제품 상세 정보 조회")
3637
@GetMapping("/{id}")
37-
public ApiResponse<ProductDetailDto> getProduct(@PathVariable Long id) {
38-
return ApiResponse.onSuccess(productService.getDetail(id));
38+
public ApiResponse<ProductDetailDto> getProduct(@PathVariable Long id,
39+
@OptionalUser User user) {
40+
return ApiResponse.onSuccess(productService.getDetail(id,user));
3941
}
4042

4143
@Operation(

src/main/java/com/DecodEat/domain/products/service/ProductService.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
import com.DecodEat.domain.products.repository.ProductRepository;
1919
import com.DecodEat.domain.products.repository.RawMaterialRepository;
2020
import com.DecodEat.domain.products.repository.ProductSpecification;
21+
import com.DecodEat.domain.users.entity.Behavior;
2122
import com.DecodEat.domain.users.entity.User;
23+
import com.DecodEat.domain.users.service.UserBehaviorService;
2224
import com.DecodEat.global.aws.s3.AmazonS3Manager;
2325
import com.DecodEat.global.dto.PageResponseDto;
2426
import com.DecodEat.global.exception.GeneralException;
@@ -52,13 +54,17 @@ public class ProductService {
5254
private final ProductRawMaterialRepository productRawMaterialRepository;
5355
private final AmazonS3Manager amazonS3Manager;
5456
private final PythonAnalysisClient pythonAnalysisClient;
57+
private final UserBehaviorService userBehaviorService;
5558

5659

5760
private static final int PAGE_SIZE = 12;
5861

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

65+
if(user != null)
66+
userBehaviorService.saveUserBehavior(user,product, Behavior.VIEW);
67+
6268
List<ProductInfoImage> images = productImageRepository.findByProduct(product);
6369
List<String> imageUrls = images.stream().map(ProductInfoImage::getImageUrl).toList();
6470

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

115+
userBehaviorService.saveUserBehavior(user,savedProduct,Behavior.REGISTER); // todo: 만약에 분석 실패?
116+
109117
return ProductConverter.toProductRegisterDto(savedProduct, productInfoImageUrls);
110118
}
111119

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

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

122131
Specification<Product> spec = Specification.where(ProductSpecification.isCompleted());
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.DecodEat.domain.users.entity;
2+
3+
public enum Behavior {
4+
VIEW,LIKE,REGISTER,SEARCH
5+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.DecodEat.domain.users.entity;
2+
3+
import com.DecodEat.domain.products.entity.Product;
4+
import com.DecodEat.global.common.BaseEntity;
5+
import jakarta.persistence.*;
6+
import lombok.*;
7+
8+
@Entity
9+
@Getter
10+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
11+
@Table
12+
@Builder
13+
@AllArgsConstructor
14+
public class UserBehavior extends BaseEntity {
15+
16+
@Id
17+
@GeneratedValue(strategy = GenerationType.IDENTITY)
18+
private Long id;
19+
20+
@ManyToOne(fetch = FetchType.LAZY)
21+
@JoinColumn(name = "user_id", nullable = false)
22+
private User user;
23+
24+
@ManyToOne(fetch = FetchType.LAZY)
25+
@JoinColumn(name = "product_id", nullable = false)
26+
private Product product;
27+
28+
@Enumerated(EnumType.STRING)
29+
@Column(name = "behavior", nullable = false)
30+
private Behavior behavior;
31+
32+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.DecodEat.domain.users.repository;
2+
3+
import com.DecodEat.domain.users.entity.UserBehavior;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
6+
public interface UserBehaviorRepository extends JpaRepository<UserBehavior, Long> {
7+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.DecodEat.domain.users.service;
2+
3+
import com.DecodEat.domain.products.entity.Product;
4+
import com.DecodEat.domain.users.entity.Behavior;
5+
import com.DecodEat.domain.users.entity.User;
6+
import com.DecodEat.domain.users.entity.UserBehavior;
7+
import com.DecodEat.domain.users.repository.UserBehaviorRepository;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.stereotype.Service;
10+
import org.springframework.transaction.annotation.Transactional;
11+
12+
@Service
13+
@RequiredArgsConstructor
14+
public class UserBehaviorService {
15+
private final UserBehaviorRepository userBehaviorRepository;
16+
@Transactional
17+
public void saveUserBehavior(User user, Product product, Behavior behavior) {
18+
UserBehavior userBehavior = UserBehavior.builder()
19+
.user(user)
20+
.product(product)
21+
.behavior(behavior)
22+
.build();
23+
userBehaviorRepository.save(userBehavior);
24+
}
25+
}

src/main/java/com/DecodEat/global/config/CurrentUserArgumentResolver.java renamed to src/main/java/com/DecodEat/global/common/annotation/CurrentUserArgumentResolver.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
package com.DecodEat.global.config;
1+
package com.DecodEat.global.common.annotation;
22

33
import com.DecodEat.domain.users.service.UserService;
44
import com.DecodEat.global.apiPayload.code.status.ErrorStatus;
5-
import com.DecodEat.global.common.annotation.CurrentUser;
65
import com.DecodEat.global.exception.GeneralException;
76
import lombok.RequiredArgsConstructor;
87
import org.springframework.core.MethodParameter;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.DecodEat.global.common.annotation;
2+
3+
import io.swagger.v3.oas.annotations.Parameter;
4+
5+
import java.lang.annotation.ElementType;
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.RetentionPolicy;
8+
import java.lang.annotation.Target;
9+
10+
@Target(ElementType.PARAMETER)
11+
@Retention(RetentionPolicy.RUNTIME)
12+
@Parameter(hidden = true)
13+
public @interface OptionalUser {
14+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.DecodEat.global.common.annotation;
2+
3+
import com.DecodEat.domain.users.entity.User;
4+
import com.DecodEat.domain.users.service.UserService;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.core.MethodParameter;
7+
import org.springframework.security.core.Authentication;
8+
import org.springframework.security.core.context.SecurityContextHolder;
9+
import org.springframework.security.core.userdetails.UserDetails;
10+
import org.springframework.stereotype.Component;
11+
import org.springframework.web.bind.support.WebDataBinderFactory;
12+
import org.springframework.web.context.request.NativeWebRequest;
13+
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
14+
import org.springframework.web.method.support.ModelAndViewContainer;
15+
16+
@Component
17+
@RequiredArgsConstructor
18+
public class OptionalUserArgumentResolver implements HandlerMethodArgumentResolver {
19+
private final UserService userService;
20+
21+
@Override
22+
public boolean supportsParameter(MethodParameter parameter) {
23+
return parameter.getParameterAnnotation(OptionalUser.class) != null
24+
&& parameter.getParameterType().equals(User.class);
25+
}
26+
27+
@Override
28+
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
29+
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
30+
31+
if (principal instanceof org.springframework.security.core.userdetails.User springUser) {
32+
Long userId = Long.valueOf(springUser.getUsername());
33+
return userService.findById(userId);
34+
}
35+
36+
// 예외를 던지는 대신 null을 반환!
37+
return null;
38+
}
39+
}

src/main/java/com/DecodEat/global/config/WebConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.DecodEat.global.config;
22

3+
import com.DecodEat.global.common.annotation.CurrentUserArgumentResolver;
34
import lombok.RequiredArgsConstructor;
45
import org.springframework.context.annotation.Configuration;
56
import org.springframework.web.method.support.HandlerMethodArgumentResolver;

0 commit comments

Comments
 (0)