Skip to content

Commit 5b9d733

Browse files
authored
Merge pull request #84 from Decodeat/feat/77-behavior-based-recommendation
feat : 사용자 행동기반 추천
2 parents 1b2a08c + a1d8f1f commit 5b9d733

File tree

6 files changed

+71
-1
lines changed

6 files changed

+71
-1
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,10 @@ public ApiResponse<List<ProductSearchResponseDto.ProductPrevDto>> getProductBase
121121
return ApiResponse.onSuccess(productService.getProductBasedRecommendation(productId, limit));
122122
}
123123

124+
@GetMapping("/recommendation/user-behavior-based")
125+
@Operation(summary = "상품 기반 추천", description = "사용자 행동 기반 추천")
126+
public ApiResponse<UserBasedRecommendationResponseDto> getUserBasedRecommendation(@CurrentUser User user) {
127+
return ApiResponse.onSuccess(productService.getUserBasedRecommendation(user));
128+
}
129+
124130
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.DecodEat.domain.products.dto.response;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
8+
import java.util.List;
9+
10+
@Getter
11+
@NoArgsConstructor
12+
@AllArgsConstructor
13+
@Builder
14+
public class UserBasedRecommendationResponseDto {
15+
private String message;
16+
private ProductSearchResponseDto.SearchResultPrevDto standardProduct;
17+
private List<ProductSearchResponseDto.ProductPrevDto> products;
18+
}

src/main/java/com/DecodEat/domain/products/repository/ProductRepository.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
import org.springframework.data.jpa.repository.JpaRepository;
1010
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
1111
import org.springframework.data.jpa.repository.Query;
12+
import com.DecodEat.domain.users.entity.Behavior;
1213
import org.springframework.data.repository.query.Param;
1314

1415
import java.util.List;
16+
import java.util.Optional;
1517

1618
public interface ProductRepository extends JpaRepository<Product, Long>, JpaSpecificationExecutor<Product> {
1719

@@ -26,4 +28,7 @@ Slice<Product> findCompletedProductsByCursor(@Param("cursorId") Long cursorId,
2628
void deleteByDecodeStatusIn(List<DecodeStatus> statuses);
2729

2830
Page<Product> findByUserId(Long userId, Pageable pageable);
31+
32+
@Query(value = "SELECT p.product_id FROM product p JOIN user_behavior ub ON p.product_id = ub.product_id WHERE ub.user_id = :userId AND ub.behavior = :behavior ORDER BY RAND() LIMIT 1", nativeQuery = true)
33+
Optional<Long> findRandomProductIdByUserIdAndBehavior(@Param("userId") Long userId, @Param("behavior") Behavior behavior);
2934
}

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import com.DecodEat.domain.products.repository.*;
1313
import com.DecodEat.domain.users.entity.Behavior;
1414
import com.DecodEat.domain.users.entity.User;
15+
import com.DecodEat.domain.users.entity.UserBehavior;
16+
import com.DecodEat.domain.users.repository.UserBehaviorRepository;
1517
import com.DecodEat.domain.users.repository.UserRepository;
1618
import com.DecodEat.domain.users.service.UserBehaviorService;
1719
import com.DecodEat.global.apiPayload.code.status.ErrorStatus;
@@ -31,6 +33,7 @@
3133

3234
import javax.swing.*;
3335
import java.util.*;
36+
import java.util.concurrent.ThreadLocalRandom;
3437
import java.util.stream.Collectors;
3538

3639
import static com.DecodEat.global.apiPayload.code.status.ErrorStatus.*;
@@ -51,6 +54,7 @@ public class ProductService {
5154
private static final int PAGE_SIZE = 12;
5255
private final UserRepository userRepository;
5356
private final ProductLikeRepository productLikeRepository;
57+
private final UserBehaviorRepository userBehaviorRepository;
5458

5559
public ProductDetailDto getDetail(Long id, User user) {
5660
Product product = productRepository.findById(id).orElseThrow(() -> new GeneralException(PRODUCT_NOT_EXISTED));
@@ -210,6 +214,40 @@ public List<ProductSearchResponseDto.ProductPrevDto> getProductBasedRecommendati
210214
return productList.stream().map(ProductConverter::toProductPrevDto).toList();
211215
}
212216

217+
public UserBasedRecommendationResponseDto getUserBasedRecommendation(User user) {
218+
Long userId = user.getId();
219+
int randomCase = ThreadLocalRandom.current().nextInt(3);
220+
Behavior selectedBehavior = null;
221+
String message = "";
222+
switch (randomCase) {
223+
case 0:
224+
selectedBehavior = Behavior.LIKE; // 0번은 좋아요 기반
225+
message = "내가 최근 좋아요한 상품과 관련된 상품";
226+
break;
227+
case 1:
228+
selectedBehavior = Behavior.REGISTER; // 1번 등록 기반
229+
message = "내가 최근 등록한 상품과 관련된 상품";
230+
break;
231+
case 2:
232+
selectedBehavior = Behavior.VIEW; // 2번 조회 기반
233+
message = "내가 최근 조회한 상품과 관련된 상품";
234+
break;
235+
236+
}
237+
238+
Long standardProductId = productRepository.findRandomProductIdByUserIdAndBehavior(userId,selectedBehavior)
239+
.orElseThrow(()-> new GeneralException(NO_USER_BEHAVIOR_EXISTED));
240+
Product standardProduct = productRepository.findById(standardProductId).orElseThrow(()->new GeneralException(NO_RESULT));
241+
242+
List<ProductSearchResponseDto.ProductPrevDto> products = getProductBasedRecommendation(standardProductId, 5);
243+
244+
return UserBasedRecommendationResponseDto.builder()
245+
.standardProduct(ProductConverter.toSearchResultPrevDto(standardProduct))
246+
.message(message)
247+
.products(products)
248+
.build();
249+
}
250+
213251

214252
@Async
215253
public void requestAnalysisAsync(Long productId, List<String> imageUrls) {

src/main/java/com/DecodEat/domain/users/repository/UserBehaviorRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
import com.DecodEat.domain.users.entity.UserBehavior;
77
import org.springframework.data.jpa.repository.JpaRepository;
88

9+
import java.util.Optional;
10+
911
public interface UserBehaviorRepository extends JpaRepository<UserBehavior, Long> {
1012

1113
void deleteByUserAndProductAndBehavior(User user, Product product, Behavior behavior);
1214

15+
1316
}

src/main/java/com/DecodEat/global/apiPayload/code/status/ErrorStatus.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public enum ErrorStatus implements BaseErrorCode {
2424

2525
// 추천
2626
NO_RECOMMENDATION_PRODUCT_BASED(HttpStatus.NOT_FOUND,"RECOMMENDATION_400","유사한 상품이 존재하지 않습니다."),
27-
27+
NO_USER_BEHAVIOR_EXISTED(HttpStatus.NOT_FOUND,"RECOMMENDATION_401","유저의 해당 메타 데이터가 존재하지 않습니다."),
2828
// 기본 에러
2929
_BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON_400", "잘못된 요청입니다."),
3030
_UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "COMMON_401", "인증이 필요합니다."),

0 commit comments

Comments
 (0)