diff --git a/src/main/java/com/webservice/algorithmchef/client/FoodSafetyApiClient.java b/src/main/java/com/webservice/algorithmchef/client/FoodSafetyApiClient.java new file mode 100644 index 0000000..4efb67b --- /dev/null +++ b/src/main/java/com/webservice/algorithmchef/client/FoodSafetyApiClient.java @@ -0,0 +1,71 @@ +package com.webservice.algorithmchef.client; + +import com.webservice.algorithmchef.dto.recipe.CookRcpResponse; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +@Service +public class FoodSafetyApiClient { + + private final RestTemplate restTemplate; + + public FoodSafetyApiClient(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + @Value("${foodsafety.cookrcp.base-url}") + private String baseUrl; + + @Value("${foodsafety.cookrcp.service-key}") + private String serviceKey; + + @Value("${foodsafety.cookrcp.svc-no}") + private String svcNo; + + public int getTotalCount() { + String url = String.format("%s/%s/%s/json/1/1", + baseUrl, serviceKey, svcNo); + + CookRcpResponse res = restTemplate.getForObject(url, CookRcpResponse.class); + + if (res == null || res.getCOOKRCP01() == null) return 0; + + try { + return Integer.parseInt(res.getCOOKRCP01().getTotalCount()); + } catch (Exception e) { + return 0; + } + } + + public List fetchRange(int start, int end) { + String url = String.format("%s/%s/%s/json/%d/%d", + baseUrl, serviceKey, svcNo, start, end); + + CookRcpResponse res = restTemplate.getForObject(url, CookRcpResponse.class); + + return Optional.ofNullable(res) + .map(CookRcpResponse::getCOOKRCP01) + .map(CookRcpResponse.Body::getRow) + .orElse(List.of()); + } + + public List fetchAll() { + int total = getTotalCount(); + if (total <= 0) return List.of(); + + int pageSize = 200; + List all = new ArrayList<>(total); + + for (int start = 1; start <= total; start += pageSize) { + int end = Math.min(start + pageSize - 1, total); + all.addAll(fetchRange(start, end)); + } + + return all; + } +} diff --git a/src/main/java/com/webservice/algorithmchef/config/JwtAuthenticationFilter.java b/src/main/java/com/webservice/algorithmchef/config/JwtAuthenticationFilter.java index 53dadd0..9f5b5d9 100644 --- a/src/main/java/com/webservice/algorithmchef/config/JwtAuthenticationFilter.java +++ b/src/main/java/com/webservice/algorithmchef/config/JwtAuthenticationFilter.java @@ -13,7 +13,7 @@ import org.springframework.web.filter.OncePerRequestFilter; // (⭐수정) algorithmchef 프로젝트의 의존성으로 변경 -import com.webservice.algorithmchef.service.UserService; +import com.webservice.algorithmchef.service.UserService; import com.webservice.algorithmchef.util.JwtUtil; import io.jsonwebtoken.Claims; @@ -60,13 +60,13 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse claims = jwtUtil.getClaims(token); } catch (Exception e) { log.warn("유효하지 않은 JWT 토큰: {}", e.getMessage()); - // (참고) 여기서 401 Unauthorized를 바로 반환할 수도 있으나, + // (참고) 여기서 401 Unauthorized를 바로 반환할 수도 있으나, // SecurityConfig의 exceptionHandling에서 처리하도록 위임하는 것이 일반적입니다. } // 3. 토큰이 유효하고, 아직 SecurityContext에 인증 정보가 없는 경우 if (claims != null && SecurityContextHolder.getContext().getAuthentication() == null) { - + // claims.getSubject()는 User 엔티티의 "userLoginId" (로그인 ID)여야 합니다. String userLoginId = claims.getSubject(); String status = claims.get("status", String.class); // "status" 클레임 추출 @@ -84,11 +84,11 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse return; // 필터 체인 중단 } } - + // 6. (정상 사용자) 또는 (임시 사용자가 허용된 URL에 접근한 경우) // UserDetails를 DB에서 조회하여 인증 토큰 생성 UserDetails userDetails = userService.loadUserByUsername(userLoginId); - + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); @@ -97,7 +97,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse SecurityContextHolder.getContext().setAuthentication(authenticationToken); log.info("SecurityContext에 인증 정보 저장 완료: {}", userLoginId); } - + // 8. 다음 필터로 체인 넘김 filterChain.doFilter(request, response); } diff --git a/src/main/java/com/webservice/algorithmchef/config/RestTemplateConfig.java b/src/main/java/com/webservice/algorithmchef/config/RestTemplateConfig.java new file mode 100644 index 0000000..d765585 --- /dev/null +++ b/src/main/java/com/webservice/algorithmchef/config/RestTemplateConfig.java @@ -0,0 +1,14 @@ +package com.webservice.algorithmchef.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/src/main/java/com/webservice/algorithmchef/config/SecurityConfig.java b/src/main/java/com/webservice/algorithmchef/config/SecurityConfig.java index a1bb528..40ae68f 100644 --- a/src/main/java/com/webservice/algorithmchef/config/SecurityConfig.java +++ b/src/main/java/com/webservice/algorithmchef/config/SecurityConfig.java @@ -17,8 +17,8 @@ @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { - - private final JwtAuthenticationFilter jwtAuthenticationFilter; + + private final JwtAuthenticationFilter jwtAuthenticationFilter; @Bean @@ -34,8 +34,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti http .authorizeHttpRequests(authorize -> authorize - .requestMatchers("/auth/login", "/auth/signUp", - "/auth/findPassword", "/auth/findUserId").permitAll() + .requestMatchers("/auth/login", "/auth/signUp", + "/auth/findPassword", "/auth/findUserId").permitAll() .anyRequest().authenticated()); http diff --git a/src/main/java/com/webservice/algorithmchef/controller/RecipeReviewController.java b/src/main/java/com/webservice/algorithmchef/controller/RecipeReviewController.java index 6a99396..f4c5b2e 100644 --- a/src/main/java/com/webservice/algorithmchef/controller/RecipeReviewController.java +++ b/src/main/java/com/webservice/algorithmchef/controller/RecipeReviewController.java @@ -17,19 +17,19 @@ @RestController @RequiredArgsConstructor public class RecipeReviewController { - - private final RecipeReviewService rService; - - @PostMapping("/recipe/review") - public ResponseEntity makeReview(@RequestBody RecipeReviewRequest request, - @AuthenticationPrincipal UserDetails userDetails){ - try { - String userId = userDetails.getUsername(); - RecipeReviewResponse response = rService.makeReview(request, userId); - return ResponseEntity.status(HttpStatus.CREATED).body(response); - }catch (IllegalArgumentException e) { - return ResponseEntity.badRequest().body(e.getMessage()); - } - } + + private final RecipeReviewService rService; + + @PostMapping("/recipe/review") + public ResponseEntity makeReview(@RequestBody RecipeReviewRequest request, + @AuthenticationPrincipal UserDetails userDetails){ + try { + String userId = userDetails.getUsername(); + RecipeReviewResponse response = rService.makeReview(request, userId); + return ResponseEntity.status(HttpStatus.CREATED).body(response); + }catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body(e.getMessage()); + } + } } diff --git a/src/main/java/com/webservice/algorithmchef/controller/RecipeSearchController.java b/src/main/java/com/webservice/algorithmchef/controller/RecipeSearchController.java new file mode 100644 index 0000000..c37816c --- /dev/null +++ b/src/main/java/com/webservice/algorithmchef/controller/RecipeSearchController.java @@ -0,0 +1,50 @@ +package com.webservice.algorithmchef.controller; + +import com.webservice.algorithmchef.model.Recipe; +import com.webservice.algorithmchef.service.RecipeService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@RestController +@RequestMapping("/api/recipes") +@CrossOrigin(origins = "http://localhost:3000") +public class RecipeSearchController { + + private final RecipeService recipeService; + + public RecipeSearchController(RecipeService recipeService) { + this.recipeService = recipeService; + } + + @GetMapping("/search") + public ResponseEntity getRandomRecipe(@RequestParam String ingredient) { + + Recipe recipe = recipeService.getRandomRecipeByIngredient(ingredient); + + if (recipe == null) { + return ResponseEntity.ok("검색된 레시피가 없습니다."); + } + + return ResponseEntity.ok(recipe); + } + + @GetMapping("/search-multi") + public ResponseEntity getRandomRecipeMultiple(@RequestParam String ingredients) { + + List ingList = + Arrays.stream(ingredients.split(",")) + .map(String::trim) + .toList(); + + Recipe recipe = recipeService.getRandomRecipeByIngredients(ingList); + + if (recipe == null) { + return ResponseEntity.ok("해당 재료들을 모두 포함한 레시피가 없습니다."); + } + + return ResponseEntity.ok(recipe); + } +} diff --git a/src/main/java/com/webservice/algorithmchef/dto/recipe/CookRcpResponse.java b/src/main/java/com/webservice/algorithmchef/dto/recipe/CookRcpResponse.java new file mode 100644 index 0000000..7ec4340 --- /dev/null +++ b/src/main/java/com/webservice/algorithmchef/dto/recipe/CookRcpResponse.java @@ -0,0 +1,168 @@ +package com.webservice.algorithmchef.dto.recipe; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class CookRcpResponse { + + @JsonProperty("COOKRCP01") + private Body cookrcp01; + + public Body getCOOKRCP01() { + return cookrcp01; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Body { + + @JsonProperty("total_count") + private String totalCount; + + @JsonProperty("row") + private List row; + + public String getTotalCount() { + return totalCount; + } + + public List getRow() { + // NPE 방지용 + return row == null ? List.of() : row; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Item { + + @JsonProperty("RCP_NM") + private String name; + + @JsonProperty("RCP_SEQ") + private String recipeIdRaw; + + + @JsonProperty("RCP_PARTS_DTLS") + private String parts; + + @JsonProperty("ATT_FILE_NO_MAIN") + private String imageUrl; + + @JsonProperty("INFO_ENG") + private String infoEng; + + @JsonProperty("HASH_TAG") + private String hashTag; + + @JsonProperty("RCP_WAY2") + private String way; + + @JsonProperty("RCP_PAT2") + private String category; + + @JsonProperty("MANUAL01") private String manual01; + @JsonProperty("MANUAL02") private String manual02; + @JsonProperty("MANUAL03") private String manual03; + @JsonProperty("MANUAL04") private String manual04; + @JsonProperty("MANUAL05") private String manual05; + @JsonProperty("MANUAL06") private String manual06; + @JsonProperty("MANUAL07") private String manual07; + @JsonProperty("MANUAL08") private String manual08; + @JsonProperty("MANUAL09") private String manual09; + @JsonProperty("MANUAL10") private String manual10; + @JsonProperty("MANUAL11") private String manual11; + @JsonProperty("MANUAL12") private String manual12; + @JsonProperty("MANUAL13") private String manual13; + @JsonProperty("MANUAL14") private String manual14; + @JsonProperty("MANUAL15") private String manual15; + @JsonProperty("MANUAL16") private String manual16; + @JsonProperty("MANUAL17") private String manual17; + @JsonProperty("MANUAL18") private String manual18; + @JsonProperty("MANUAL19") private String manual19; + @JsonProperty("MANUAL20") private String manual20; + + @JsonProperty("RCP_NA_TIP") + private String naTip; + + + public String getName() { + return name; + } + + public String getParts() { + return parts; + } + + public String getImageUrl() { + return imageUrl; + } + + public String getInfoEng() { + return infoEng; + } + + public Double getKcal() { + if (infoEng == null) return null; + try { + String n = infoEng.replaceAll("[^0-9.]", ""); + return n.isEmpty() ? null : Double.valueOf(n); + } catch (Exception e) { + return null; + } + } + + public Double getKcalOrNull() { + return getKcal(); + } + + public String getHashTag() { + return hashTag; + } + + public String getWay() { + return way; + } + + public String getCategory() { + return category; + } + + public String getType() { + if (category != null && !category.isBlank()) return category; + if (way != null && !way.isBlank()) return way; + return "기타"; + } + + public String getInstructions() { + List steps = new ArrayList<>(); + + for (String s : List.of( + manual01, manual02, manual03, manual04, manual05, + manual06, manual07, manual08, manual09, manual10, + manual11, manual12, manual13, manual14, manual15, + manual16, manual17, manual18, manual19, manual20 + )) { + if (s == null) continue; + + String t = s + .replace("\r\n", "\n") + .replace("\\n", "\n") + .replaceAll("[ \t]+", " ") + .trim(); + + if (!t.isEmpty()) { + steps.add(t); + } + } + + return String.join("\n", steps); + } + public String getTip() { + return naTip; + } + } +} diff --git a/src/main/java/com/webservice/algorithmchef/dto/recipereview/RecipeReviewRequest.java b/src/main/java/com/webservice/algorithmchef/dto/recipereview/RecipeReviewRequest.java index f04a4f6..92a6699 100644 --- a/src/main/java/com/webservice/algorithmchef/dto/recipereview/RecipeReviewRequest.java +++ b/src/main/java/com/webservice/algorithmchef/dto/recipereview/RecipeReviewRequest.java @@ -7,7 +7,7 @@ @NoArgsConstructor public class RecipeReviewRequest { - private String name; - private float rating; - private String content; + private String name; + private float rating; + private String content; } diff --git a/src/main/java/com/webservice/algorithmchef/dto/recipereview/RecipeReviewResponse.java b/src/main/java/com/webservice/algorithmchef/dto/recipereview/RecipeReviewResponse.java index 2333038..21840a1 100644 --- a/src/main/java/com/webservice/algorithmchef/dto/recipereview/RecipeReviewResponse.java +++ b/src/main/java/com/webservice/algorithmchef/dto/recipereview/RecipeReviewResponse.java @@ -9,13 +9,13 @@ @NoArgsConstructor public class RecipeReviewResponse { - private String name; - private String userId; - private float rating; - - public RecipeReviewResponse(RecipeReview review) { - this.name = review.getRecipe().getName(); - this.userId = review.getUser().getUserId(); - this.rating = review.getRating(); - } + private String name; + private String userId; + private float rating; + + public RecipeReviewResponse(RecipeReview review) { + this.name = review.getRecipe().getName(); + this.userId = review.getUser().getUserId(); + this.rating = review.getRating(); + } } diff --git a/src/main/java/com/webservice/algorithmchef/model/Recipe.java b/src/main/java/com/webservice/algorithmchef/model/Recipe.java index c63007a..531deb4 100644 --- a/src/main/java/com/webservice/algorithmchef/model/Recipe.java +++ b/src/main/java/com/webservice/algorithmchef/model/Recipe.java @@ -25,46 +25,46 @@ @ToString public class Recipe { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name="recipe_id") - private Long id; - - @Column(nullable = false) - private String name; - - @Lob - private String description; - - @Lob - @Column(nullable = false) - private String instructions; - - private String imageUrl; - - @Column(nullable = false) - private String type; - - @Column(nullable = false) - @Lob - private String neededIngredients; - - @Column(nullable = false) - private double kcal; - - @Column - private String tag; - - @Column - private String portions; - - @Column - private String time; - - @Column - private String tip; - - @OneToMany(mappedBy = "recipe", orphanRemoval = true) - private List recipeReviews; - + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name="recipe_id") + private Long id; + + @Column(nullable = false) + private String name; + + @Lob + private String description; + + @Lob + @Column(nullable = false, columnDefinition = "LONGTEXT") + private String instructions; + + private String imageUrl; + + @Column(nullable = false) + private String type; + + @Lob + @Column(name = "needed_ingredients", columnDefinition = "TEXT") + private String neededIngredients; + + @Column(nullable = false) + private double kcal; + + @Column + private String tag; + + @Column + private String portions; + + @Column + private String time; + + @Column + private String tip; + + @OneToMany(mappedBy = "recipe", orphanRemoval = true) + private List recipeReviews; + } diff --git a/src/main/java/com/webservice/algorithmchef/model/RecipeReview.java b/src/main/java/com/webservice/algorithmchef/model/RecipeReview.java index dab0b96..dc4b454 100644 --- a/src/main/java/com/webservice/algorithmchef/model/RecipeReview.java +++ b/src/main/java/com/webservice/algorithmchef/model/RecipeReview.java @@ -29,27 +29,27 @@ @ToString public class RecipeReview { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name="review_id") - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name="user_id") - private User user; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name="recipe_id") - private Recipe recipe; - - @Column(nullable = false) - private float rating; - - @Column - @Lob - private String content; - - @CreationTimestamp - private LocalDateTime createdAt; - + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name="review_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name="user_id") + private User user; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name="recipe_id") + private Recipe recipe; + + @Column(nullable = false) + private float rating; + + @Column + @Lob + private String content; + + @CreationTimestamp + private LocalDateTime createdAt; + } diff --git a/src/main/java/com/webservice/algorithmchef/repository/RecipeRepository.java b/src/main/java/com/webservice/algorithmchef/repository/RecipeRepository.java index 915d827..915f68f 100644 --- a/src/main/java/com/webservice/algorithmchef/repository/RecipeRepository.java +++ b/src/main/java/com/webservice/algorithmchef/repository/RecipeRepository.java @@ -1,12 +1,36 @@ package com.webservice.algorithmchef.repository; +import com.webservice.algorithmchef.model.Recipe; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + import java.util.Optional; +import java.util.List; -import org.springframework.data.jpa.repository.JpaRepository; +public interface RecipeRepository extends JpaRepository { -import com.webservice.algorithmchef.model.Recipe; + Optional findByName(String name); + + boolean existsByName(String name); -public interface RecipeRepository extends JpaRepository{ + @Query(""" + SELECT r FROM Recipe r + WHERE (:i1 = '' OR r.neededIngredients LIKE %:i1%) + AND (:i2 = '' OR r.neededIngredients LIKE %:i2%) + AND (:i3 = '' OR r.neededIngredients LIKE %:i3%) + AND (:i4 = '' OR r.neededIngredients LIKE %:i4%) + AND (:i5 = '' OR r.neededIngredients LIKE %:i5%) + """) + List findByIngredientsMulti( + @Param("i1") String i1, + @Param("i2") String i2, + @Param("i3") String i3, + @Param("i4") String i4, + @Param("i5") String i5 + ); - public Optional findByName(String name); + @Query("SELECT r FROM Recipe r " + + "WHERE r.neededIngredients LIKE %:ingredient%") + List findByIngredientKeyword(@Param("ingredient") String ingredient); } diff --git a/src/main/java/com/webservice/algorithmchef/repository/RecipeReviewRepository.java b/src/main/java/com/webservice/algorithmchef/repository/RecipeReviewRepository.java index fafd31e..4e705d1 100644 --- a/src/main/java/com/webservice/algorithmchef/repository/RecipeReviewRepository.java +++ b/src/main/java/com/webservice/algorithmchef/repository/RecipeReviewRepository.java @@ -8,5 +8,5 @@ public interface RecipeReviewRepository extends JpaRepository { - boolean existsByUserAndRecipe(User user, Recipe recipe); + boolean existsByUserAndRecipe(User user, Recipe recipe); } diff --git a/src/main/java/com/webservice/algorithmchef/service/RecipeDataInitializer.java b/src/main/java/com/webservice/algorithmchef/service/RecipeDataInitializer.java new file mode 100644 index 0000000..3aad28b --- /dev/null +++ b/src/main/java/com/webservice/algorithmchef/service/RecipeDataInitializer.java @@ -0,0 +1,21 @@ +package com.webservice.algorithmchef.service; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +@Component +public class RecipeDataInitializer implements CommandLineRunner { + + private final RecipeImportService importService; + + public RecipeDataInitializer(RecipeImportService importService) { + this.importService = importService; + } + + @Override + public void run(String... args) throws Exception { + System.out.println("[INIT] import start"); + importService.importAllFromFoodSafety(); + System.out.println("[INIT] import complete"); + } +} diff --git a/src/main/java/com/webservice/algorithmchef/service/RecipeImportService.java b/src/main/java/com/webservice/algorithmchef/service/RecipeImportService.java new file mode 100644 index 0000000..761dea8 --- /dev/null +++ b/src/main/java/com/webservice/algorithmchef/service/RecipeImportService.java @@ -0,0 +1,96 @@ +package com.webservice.algorithmchef.service; + +import com.webservice.algorithmchef.client.FoodSafetyApiClient; +import com.webservice.algorithmchef.dto.recipe.CookRcpResponse; +import com.webservice.algorithmchef.model.Recipe; +import com.webservice.algorithmchef.repository.RecipeRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +public class RecipeImportService { + + private final FoodSafetyApiClient apiClient; + private final RecipeRepository recipeRepository; + + public RecipeImportService(FoodSafetyApiClient apiClient, + RecipeRepository recipeRepository) { + this.apiClient = apiClient; + this.recipeRepository = recipeRepository; + } + + @Transactional + public void importAllFromFoodSafety() { + + System.out.println("[IMPORT] foodsafetycountry total recipe api start"); + + final int BATCH_SIZE = 100; + int start = 1; + int totalSaved = 0; + + while (true) { + int end = start + BATCH_SIZE - 1; + + System.out.println("[IMPORT] fetchRange(" + start + ", " + end + ")"); + + List items; + try { + items = apiClient.fetchRange(start, end); + } catch (Exception e) { + System.out.println("[IMPORT] fetchRange exception, import stop"); + e.printStackTrace(); + break; + } + + if (items == null || items.isEmpty()) { + System.out.println("[IMPORT] " + start + " ~ " + end + " this panel No recipe. import stop"); + break; + } + + System.out.println("[IMPORT] received count = " + items.size()); + + for (CookRcpResponse.Item item : items) { + + String name = item.getName(); + System.out.println(" - API recipe name = " + name); + + if (name == null || name.isBlank()) { + System.out.println(" -> No name, skip"); + continue; + } + + if (recipeRepository.existsByName(name)) { + System.out.println(" -> exist recipe, skip"); + continue; + } + + Recipe r = new Recipe(); + r.setName(name); + r.setDescription(null); + r.setInstructions(item.getInstructions()); + r.setImageUrl(item.getImageUrl()); + Double kcal = item.getKcalOrNull(); + r.setKcal(kcal != null ? kcal : 0.0); + + String originalType = item.getType(); + if (originalType == null || originalType.isBlank()) { + originalType = "기타"; + } + r.setTag(originalType); + r.setType("식품안전나라"); + r.setTime(null); + r.setTip(item.getTip()); + r.setNeededIngredients(item.getParts()); + recipeRepository.save(r); + totalSaved++; + System.out.println(" -> DB save complete (current new save: " + totalSaved + ")"); + } + + start += BATCH_SIZE; + } + + System.out.println("[IMPORT] every import complete. A total " + totalSaved + " new saved"); + } +} diff --git a/src/main/java/com/webservice/algorithmchef/service/RecipeReviewService.java b/src/main/java/com/webservice/algorithmchef/service/RecipeReviewService.java index a7ee780..95f7801 100644 --- a/src/main/java/com/webservice/algorithmchef/service/RecipeReviewService.java +++ b/src/main/java/com/webservice/algorithmchef/service/RecipeReviewService.java @@ -17,34 +17,34 @@ @Service @RequiredArgsConstructor public class RecipeReviewService { - - private final RecipeRepository recipeRepository; - private final RecipeReviewRepository recipeReviewRepository; - private final UserRepository userRepository; - - - @Transactional - public RecipeReviewResponse makeReview(RecipeReviewRequest reviewRequest, String userId) { - User user = userRepository.findByUserId(userId) - .orElseThrow(()-> new IllegalArgumentException("해당하는 아이디의 사용작 없습니다.")); - String recipeName = reviewRequest.getName(); - float rating = reviewRequest.getRating(); - String content = reviewRequest.getContent(); - Recipe recipe = recipeRepository.findByName(recipeName) - .orElseThrow(()-> new IllegalArgumentException("해당하는 이름의 recipe가 존재하지 않습니다.")); - if (recipeReviewRepository.existsByUserAndRecipe(user, recipe)) { - throw new IllegalStateException("이미 리뷰를 작성한 레시피입니다."); - } - RecipeReview review = RecipeReview.builder() - .content(content) - .rating(rating) - .user(user) - .recipe(recipe) - .build(); - - RecipeReview recipeReview = recipeReviewRepository.save(review); - return new RecipeReviewResponse(recipeReview); - - } + + private final RecipeRepository recipeRepository; + private final RecipeReviewRepository recipeReviewRepository; + private final UserRepository userRepository; + + + @Transactional + public RecipeReviewResponse makeReview(RecipeReviewRequest reviewRequest, String userId) { + User user = userRepository.findByUserId(userId) + .orElseThrow(()-> new IllegalArgumentException("해당하는 아이디의 사용작 없습니다.")); + String recipeName = reviewRequest.getName(); + float rating = reviewRequest.getRating(); + String content = reviewRequest.getContent(); + Recipe recipe = recipeRepository.findByName(recipeName) + .orElseThrow(()-> new IllegalArgumentException("해당하는 이름의 recipe가 존재하지 않습니다.")); + if (recipeReviewRepository.existsByUserAndRecipe(user, recipe)) { + throw new IllegalStateException("이미 리뷰를 작성한 레시피입니다."); + } + RecipeReview review = RecipeReview.builder() + .content(content) + .rating(rating) + .user(user) + .recipe(recipe) + .build(); + + RecipeReview recipeReview = recipeReviewRepository.save(review); + return new RecipeReviewResponse(recipeReview); + + } } diff --git a/src/main/java/com/webservice/algorithmchef/service/RecipeService.java b/src/main/java/com/webservice/algorithmchef/service/RecipeService.java new file mode 100644 index 0000000..abb8baa --- /dev/null +++ b/src/main/java/com/webservice/algorithmchef/service/RecipeService.java @@ -0,0 +1,54 @@ +package com.webservice.algorithmchef.service; + +import com.webservice.algorithmchef.model.Recipe; +import com.webservice.algorithmchef.repository.RecipeRepository; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Random; + +@Service +public class RecipeService { + + private final RecipeRepository recipeRepository; + private final Random random = new Random(); + + public RecipeService(RecipeRepository recipeRepository) { + this.recipeRepository = recipeRepository; + } + + public Recipe getRandomRecipeByIngredient(String ingredient) { + List list = recipeRepository.findByIngredientKeyword(ingredient); + + if (list.isEmpty()) { + return null; + } + + int idx = (int) (Math.random() * list.size()); + return list.get(idx); + } + + public Recipe getRandomRecipeByIngredients(List ingredients) { + + if (ingredients == null || ingredients.isEmpty()) { + throw new IllegalArgumentException("재료 리스트가 필요합니다."); + } + + String i1 = ingredients.size() > 0 ? ingredients.get(0) : ""; + String i2 = ingredients.size() > 1 ? ingredients.get(1) : ""; + String i3 = ingredients.size() > 2 ? ingredients.get(2) : ""; + String i4 = ingredients.size() > 3 ? ingredients.get(3) : ""; + String i5 = ingredients.size() > 4 ? ingredients.get(4) : ""; + + List list = + recipeRepository.findByIngredientsMulti(i1, i2, i3, i4, i5); + + if (list.isEmpty()) { + return null; + } + + return list.get(random.nextInt(list.size())); + } +}